regenerated
[moon.git] / src / media.cpp
blobfa80c93a46138361cd7ce9fc3a146c739e3723f6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * media.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <math.h>
22 #include "writeablebitmap.h"
23 #include "bitmapimage.h"
24 #include "uri.h"
25 #include "runtime.h"
26 #include "media.h"
27 #include "error.h"
28 #include "downloader.h"
29 #include "geometry.h"
30 #include "timeline.h"
31 #include "debug.h"
32 #include "deployment.h"
35 * MediaBase
38 MediaBase::MediaBase ()
40 SetObjectType (Type::MEDIABASE);
42 source.downloader = NULL;
43 source.part_name = NULL;
44 source.queued = false;
45 downloader = NULL;
46 part_name = NULL;
47 allow_downloads = false;
48 source_changed = false;
51 MediaBase::~MediaBase ()
53 DownloaderAbort ();
56 void
57 MediaBase::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
59 MediaBase *media = (MediaBase *) closure;
61 media->DownloaderComplete ();
64 void
65 MediaBase::downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
67 MediaBase *media = (MediaBase *) closure;
69 media->DownloaderFailed (calldata);
72 void
73 MediaBase::DownloaderComplete ()
75 // Nothing for MediaBase to do...
78 void
79 MediaBase::DownloaderFailed (EventArgs *args)
81 // Nothing for MediaBase to do...
84 void
85 MediaBase::DownloaderAbort ()
87 if (downloader) {
88 downloader->RemoveHandler (Downloader::DownloadFailedEvent, downloader_failed, this);
89 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
90 downloader->SetStreamFunctions (NULL, NULL, NULL);
91 downloader->Abort ();
92 downloader->unref ();
93 g_free (part_name);
94 downloader = NULL;
95 part_name = NULL;
99 void
100 MediaBase::SetAllowDownloads (bool allow)
102 const char *uri;
103 Downloader *dl;
105 if ((allow_downloads && allow) || (!allow_downloads && !allow))
106 return;
108 if (allow && IsAttached () && source_changed) {
109 source_changed = false;
111 if ((uri = GetSource ()) && *uri) {
112 if (!(dl = GetDeployment ()->GetSurface ()->CreateDownloader ())) {
113 // we're shutting down
114 return;
117 dl->Open ("GET", uri, GetDownloaderPolicy (uri));
118 SetSource (dl, "");
119 dl->unref ();
123 allow_downloads = allow;
126 void
127 MediaBase::OnLoaded ()
129 FrameworkElement::OnLoaded ();
130 SetAllowDownloads (true);
133 void
134 MediaBase::SetSourceAsyncCallback ()
136 Downloader *downloader;
137 char *part_name;
139 DownloaderAbort ();
141 downloader = source.downloader;
142 part_name = source.part_name;
144 source.queued = false;
145 source.downloader = NULL;
146 source.part_name = NULL;
148 if (!IsAttached ())
149 return;
151 SetSourceInternal (downloader, part_name);
153 if (downloader)
154 downloader->unref ();
157 void
158 MediaBase::SetSourceInternal (Downloader *downloader, char *PartName)
160 this->downloader = downloader;
161 part_name = PartName;
163 if (downloader)
164 downloader->ref ();
167 void
168 MediaBase::set_source_async (EventObject *user_data)
170 MediaBase *media = (MediaBase *) user_data;
172 media->SetSourceAsyncCallback ();
175 void
176 MediaBase::SetSource (Downloader *downloader, const char *PartName)
178 source_changed = false;
180 if (source.queued) {
181 if (source.downloader)
182 source.downloader->unref ();
184 g_free (source.part_name);
185 source.downloader = NULL;
186 source.part_name = NULL;
189 source.part_name = g_strdup (PartName);
190 source.downloader = downloader;
192 if (downloader)
193 downloader->ref ();
195 if (source.downloader && source.downloader->Completed ()) {
196 SetSourceInternal (source.downloader, source.part_name);
197 source.downloader->unref ();
198 } else if (!source.queued) {
199 AddTickCall (MediaBase::set_source_async);
200 source.queued = true;
204 void
205 MediaBase::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
207 if (args->GetId () == MediaBase::SourceProperty) {
208 const char *uri = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
210 if (IsAttached () && AllowDownloads ()) {
211 if (uri && *uri) {
212 Downloader *dl;
213 if ((dl = GetDeployment ()->GetSurface ()->CreateDownloader ())) {
214 dl->Open ("GET", uri, GetDownloaderPolicy (uri));
215 SetSource (dl, "");
216 dl->unref ();
217 } else {
218 // we're shutting down
220 } else {
221 SetSource (NULL, NULL);
223 } else {
224 source_changed = true;
228 if (args->GetProperty ()->GetOwnerType() != Type::MEDIABASE) {
229 FrameworkElement::OnPropertyChanged (args, error);
230 return;
233 NotifyListenersOfPropertyChange (args, error);
237 // Image
239 Image::Image ()
241 SetObjectType (Type::IMAGE);
244 Image::~Image ()
246 BitmapSource *source = (BitmapSource*)GetSource ();
248 if (source)
249 source->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
252 void
253 Image::download_progress (EventObject *sender, EventArgs *calldata, gpointer closure)
255 Image *media = (Image *) closure;
257 media->DownloadProgress ();
260 void
261 Image::image_opened (EventObject *sender, EventArgs *calldata, gpointer closure)
263 Image *media = (Image *) closure;
265 media->ImageOpened ((RoutedEventArgs*)calldata);
268 void
269 Image::image_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
271 Image *media = (Image *) closure;
273 media->ImageFailed ((ImageErrorEventArgs*)calldata);
276 void
277 Image::source_pixel_data_changed (EventObject *sender, EventArgs *calldata, gpointer closure)
279 Image *media = (Image *) closure;
281 media->SourcePixelDataChanged ();
284 void
285 Image::DownloadProgress ()
287 BitmapImage *source = (BitmapImage *) GetSource ();
289 SetDownloadProgress (source->GetProgress ());
290 Emit (DownloadProgressChangedEvent);
293 void
294 Image::ImageOpened (RoutedEventArgs *args)
296 BitmapSource *source = (BitmapSource*)GetSource ();
298 if (source->Is (Type::BITMAPIMAGE)) {
299 source->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
300 source->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
301 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
304 InvalidateArrange ();
305 InvalidateMeasure ();
306 UpdateBounds ();
307 Invalidate ();
309 args->ref (); // to counter the unref in Emit
310 Emit (ImageOpenedEvent, args);
313 void
314 Image::ImageFailed (ImageErrorEventArgs *args)
316 BitmapSource *source = (BitmapSource*) GetSource ();
318 if (source->Is (Type::BITMAPIMAGE)) {
319 source->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
320 source->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
321 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
323 source->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
326 InvalidateArrange ();
327 InvalidateMeasure ();
328 UpdateBounds ();
329 Invalidate ();
331 args->ref (); // to counter the unref in Emit
332 Emit (ImageFailedEvent, args);
335 void
336 Image::SourcePixelDataChanged ()
338 Invalidate();
341 void
342 Image::SetSourceInternal (Downloader *downloader, char *PartName)
344 BitmapImage *source = (BitmapImage *) GetSource ();
346 MediaBase::SetSourceInternal (downloader, PartName);
348 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
349 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
350 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
352 source->SetDownloader (downloader, NULL, PartName);
355 void
356 Image::SetSource (Downloader *downloader, const char *PartName)
358 MediaBase::SetSource (downloader, PartName);
361 void
362 Image::Render (cairo_t *cr, Region *region, bool path_only)
364 ImageSource *source = GetSource ();
365 cairo_pattern_t *pattern = NULL;
366 cairo_matrix_t matrix;
368 if (!source)
369 return;
371 source->Lock ();
373 cairo_save (cr);
374 cairo_set_matrix (cr, &absolute_xform);
376 Size specified (GetActualWidth (), GetActualHeight ());
377 Size stretched = ApplySizeConstraints (specified);
378 bool adjust = specified != GetRenderSize ();
380 if (GetStretch () != StretchUniformToFill)
381 specified = specified.Min (stretched);
383 Rect paint = Rect (0, 0, specified.width, specified.height);
385 if (!path_only) {
386 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
388 if (GetStretch () == StretchNone)
389 paint = paint.Union (image);
391 if (image.width == 0.0 && image.height == 0.0)
392 return;
394 pattern = cairo_pattern_create_for_surface (source->GetSurface (cr));
395 image_brush_compute_pattern_matrix (&matrix, paint.width, paint.height,
396 image.width, image.height,
397 GetStretch (),
398 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
400 cairo_pattern_set_matrix (pattern, &matrix);
401 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
402 if (cairo_pattern_status (pattern) == CAIRO_STATUS_SUCCESS) {
403 cairo_set_source (cr, pattern);
405 cairo_pattern_destroy (pattern);
408 if (adjust) {
409 specified = MeasureOverride (specified);
410 paint = Rect ((stretched.width - specified.width) * 0.5, (stretched.height - specified.height) * 0.5, specified.width, specified.height);
413 if (!path_only)
414 RenderLayoutClip (cr);
416 paint = paint.Intersection (Rect (0, 0, stretched.width, stretched.height));
417 paint.Draw (cr);
419 if (!path_only)
420 cairo_fill (cr);
422 cairo_restore (cr);
424 source->Unlock ();
427 Size
428 Image::ComputeActualSize ()
430 Size result = MediaBase::ComputeActualSize ();
431 UIElement *parent = GetVisualParent ();
432 ImageSource *source = GetSource ();
434 if (parent && !parent->Is (Type::CANVAS))
435 if (LayoutInformation::GetLayoutSlot (this))
436 return result;
438 if (source && source->GetSurface (NULL)) {
439 Size available = Size (INFINITY, INFINITY);
440 available = ApplySizeConstraints (available);
441 result = MeasureOverride (available);
442 result = ApplySizeConstraints (result);
445 return result;
448 Size
449 Image::MeasureOverride (Size availableSize)
451 Size desired = availableSize;
452 Rect shape_bounds = Rect ();
453 ImageSource *source = GetSource ();
454 double sx = 0.0;
455 double sy = 0.0;
457 if (source)
458 shape_bounds = Rect (0,0,source->GetPixelWidth (),source->GetPixelHeight ());
460 /* don't stretch to infinite size */
461 if (isinf (desired.width))
462 desired.width = shape_bounds.width;
463 if (isinf (desired.height))
464 desired.height = shape_bounds.height;
466 /* compute the scaling */
467 if (shape_bounds.width > 0)
468 sx = desired.width / shape_bounds.width;
469 if (shape_bounds.height > 0)
470 sy = desired.height / shape_bounds.height;
472 /* don't use infinite dimensions as constraints */
473 if (isinf (availableSize.width))
474 sx = sy;
475 if (isinf (availableSize.height))
476 sy = sx;
478 switch (GetStretch ()) {
479 case StretchUniform:
480 sx = sy = MIN (sx, sy);
481 break;
482 case StretchUniformToFill:
483 sx = sy = MAX (sx, sy);
484 break;
485 case StretchFill:
486 if (isinf (availableSize.width))
487 sx = sy;
488 if (isinf (availableSize.height))
489 sy = sx;
490 break;
491 case StretchNone:
492 sx = sy = 1.0;
493 break;
496 desired = Size (shape_bounds.width * sx, shape_bounds.height * sy);
498 return desired;
501 Size
502 Image::ArrangeOverride (Size finalSize)
504 Size arranged = finalSize;
505 Rect shape_bounds = Rect ();
506 ImageSource *source = GetSource ();
507 double sx = 1.0;
508 double sy = 1.0;
511 if (source)
512 shape_bounds = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
514 /* compute the scaling */
515 if (shape_bounds.width == 0)
516 shape_bounds.width = arranged.width;
517 if (shape_bounds.height == 0)
518 shape_bounds.height = arranged.height;
520 if (shape_bounds.width != arranged.width)
521 sx = arranged.width / shape_bounds.width;
522 if (shape_bounds.height != arranged.height)
523 sy = arranged.height / shape_bounds.height;
525 switch (GetStretch ()) {
526 case StretchUniform:
527 sx = sy = MIN (sx, sy);
528 break;
529 case StretchUniformToFill:
530 sx = sy = MAX (sx, sy);
531 break;
532 case StretchNone:
533 sx = sy = 1.0;
534 default:
535 break;
538 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
540 return arranged;
543 Rect
544 Image::GetCoverageBounds ()
546 Stretch stretch = GetStretch ();
547 ImageSource *source = GetSource ();
549 if (!source || source->GetPixelFormat () == PixelFormatPbgra32)
550 return Rect ();
552 if (stretch == StretchFill || stretch == StretchUniformToFill)
553 return bounds;
555 cairo_matrix_t matrix;
556 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
557 Rect paint = Rect (0, 0, GetActualWidth (), GetActualHeight ());
559 image_brush_compute_pattern_matrix (&matrix,
560 paint.width, paint.height,
561 image.width, image.height, stretch,
562 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
564 cairo_matrix_invert (&matrix);
565 cairo_matrix_multiply (&matrix, &matrix, &absolute_xform);
567 image = image.Transform (&matrix);
568 image = image.Intersection (bounds);
570 return image;
573 void
574 Image::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
576 if (prop && (prop->GetId () == Image::SourceProperty
577 || prop->GetId () == MediaBase::SourceProperty)) {
578 InvalidateMeasure ();
579 Invalidate ();
580 return;
583 MediaBase::OnSubPropertyChanged (prop, obj, subobj_args);
585 void
586 Image::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
588 if (args->GetId () == Image::SourceProperty) {
589 ImageSource *source = args->GetNewValue () ? args->GetNewValue ()->AsImageSource () : NULL;
590 ImageSource *old = args->GetOldValue () ? args->GetOldValue ()->AsImageSource () : NULL;
592 if (old && old->Is(Type::BITMAPSOURCE)) {
593 old->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
595 if (source && source->Is(Type::BITMAPSOURCE)) {
596 source->AddHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
599 if (old && old->Is(Type::BITMAPIMAGE)) {
600 old->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
601 old->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
602 old->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
604 if (source && source->Is(Type::BITMAPIMAGE)) {
605 BitmapImage *bitmap = (BitmapImage *) source;
606 Uri *uri = bitmap->GetUriSource ();
608 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
609 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
610 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
612 if (bitmap->GetPixelWidth () > 0 && bitmap->GetPixelHeight () > 0) {
613 RoutedEventArgs *args = new RoutedEventArgs ();
614 ImageOpened (args);
615 args->unref ();
618 // can uri ever be null?
619 if (IsBeingParsed () && uri && IsAttached ()) {
620 ImageErrorEventArgs *args = NULL;
622 if (uri->IsInvalidPath ()) {
623 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri"));
624 } else if (!bitmap->ValidateDownloadPolicy ()) {
625 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation"));
628 if (args != NULL) {
629 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
630 GetDeployment ()->GetSurface ()->EmitError (args);
634 InvalidateMeasure ();
637 if (args->GetProperty ()->GetOwnerType() != Type::IMAGE) {
638 MediaBase::OnPropertyChanged (args, error);
639 return;
642 // we need to notify attachees if our DownloadProgress changed.
643 NotifyListenersOfPropertyChange (args, error);
646 bool
647 Image::InsideObject (cairo_t *cr, double x, double y)
649 if (!FrameworkElement::InsideObject (cr, x, y))
650 return false;
652 cairo_save (cr);
653 cairo_new_path (cr);
654 cairo_set_matrix (cr, &absolute_xform);
656 double nx = x;
657 double ny = y;
659 TransformPoint (&nx, &ny);
661 Render (cr, NULL, true);
662 bool inside = cairo_in_fill (cr, nx, ny);
663 cairo_restore (cr);
665 if (inside)
666 inside = InsideLayoutClip (x, y);
668 if (inside)
669 inside = InsideClip (cr, x, y);
671 return inside;
674 Value *
675 Image::CreateDefaultImageSource (DependencyObject *instance, DependencyProperty *property)
677 return Value::CreateUnrefPtr (new BitmapImage ());
681 // MediaAttributeCollection
684 MediaAttribute *
685 MediaAttributeCollection::GetItemByName (const char *name)
687 MediaAttribute *attr;
688 const char *value;
690 for (guint i = 0; i < array->len; i++) {
691 attr = ((Value *) array->pdata[i])->AsMediaAttribute ();
692 if (!(value = attr->GetName ()))
693 continue;
695 if (!g_ascii_strcasecmp (value, name))
696 return attr;
699 return NULL;
704 // TimelineMarkerCollection
708 TimelineMarkerCollection::AddWithError (Value *value, MoonError *error)
710 TimelineMarker *marker, *cur;
712 marker = value->AsTimelineMarker ();
714 for (guint i = 0; i < array->len; i++) {
715 cur = ((Value *) array->pdata[i])->AsTimelineMarker ();
716 if (cur->GetTime () >= marker->GetTime ()) {
717 DependencyObjectCollection::InsertWithError (i, value, error);
718 return i;
722 return DependencyObjectCollection::InsertWithError (array->len, value, error) ? array->len - 1 : -1;
725 bool
726 TimelineMarkerCollection::InsertWithError (int index, Value *value, MoonError *error)
728 return AddWithError (value, error) != -1;