2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / media.cpp
bloba46c513aefd276523afcbdb750f5029e7e70a0c5
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"
34 * MediaBase
37 MediaBase::MediaBase ()
39 SetObjectType (Type::MEDIABASE);
41 source.downloader = NULL;
42 source.part_name = NULL;
43 source.queued = false;
44 downloader = NULL;
45 part_name = NULL;
46 allow_downloads = false;
47 source_changed = false;
50 MediaBase::~MediaBase ()
52 DownloaderAbort ();
55 void
56 MediaBase::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
58 MediaBase *media = (MediaBase *) closure;
60 media->DownloaderComplete ();
63 void
64 MediaBase::downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
66 MediaBase *media = (MediaBase *) closure;
68 media->DownloaderFailed (calldata);
71 void
72 MediaBase::DownloaderComplete ()
74 // Nothing for MediaBase to do...
77 void
78 MediaBase::DownloaderFailed (EventArgs *args)
80 // Nothing for MediaBase to do...
83 void
84 MediaBase::DownloaderAbort ()
86 if (downloader) {
87 downloader->RemoveHandler (Downloader::DownloadFailedEvent, downloader_failed, this);
88 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
89 downloader->SetStreamFunctions (NULL, NULL, NULL);
90 downloader->Abort ();
91 downloader->unref ();
92 g_free (part_name);
93 downloader = NULL;
94 part_name = NULL;
98 void
99 MediaBase::SetAllowDownloads (bool allow)
101 Surface *surface = GetSurface ();
102 const char *uri;
103 Downloader *dl;
105 if ((allow_downloads && allow) || (!allow_downloads && !allow))
106 return;
108 if (allow && surface && source_changed) {
109 source_changed = false;
111 if ((uri = GetSource ()) && *uri) {
112 if (!(dl = surface->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 (GetSurface () == NULL)
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;
209 Surface *surface = GetSurface ();
211 if (surface && AllowDownloads ()) {
212 if (uri && *uri) {
213 Downloader *dl;
214 if ((dl = surface->CreateDownloader ())) {
215 dl->Open ("GET", uri, GetDownloaderPolicy (uri));
216 SetSource (dl, "");
217 dl->unref ();
218 } else {
219 // we're shutting down
221 } else {
222 SetSource (NULL, NULL);
224 } else {
225 source_changed = true;
229 if (args->GetProperty ()->GetOwnerType() != Type::MEDIABASE) {
230 FrameworkElement::OnPropertyChanged (args, error);
231 return;
234 NotifyListenersOfPropertyChange (args, error);
238 // Image
240 Image::Image ()
242 SetObjectType (Type::IMAGE);
245 Image::~Image ()
247 BitmapSource *source = (BitmapSource*)GetSource ();
249 if (source)
250 source->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
253 void
254 Image::download_progress (EventObject *sender, EventArgs *calldata, gpointer closure)
256 Image *media = (Image *) closure;
258 media->DownloadProgress ();
261 void
262 Image::image_opened (EventObject *sender, EventArgs *calldata, gpointer closure)
264 Image *media = (Image *) closure;
266 media->ImageOpened ();
269 void
270 Image::image_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
272 Image *media = (Image *) closure;
274 media->ImageFailed ((ImageErrorEventArgs*)calldata);
277 void
278 Image::source_pixel_data_changed (EventObject *sender, EventArgs *calldata, gpointer closure)
280 Image *media = (Image *) closure;
282 media->SourcePixelDataChanged ();
285 void
286 Image::DownloadProgress ()
288 BitmapImage *source = (BitmapImage *) GetSource ();
290 SetDownloadProgress (source->GetProgress ());
291 Emit (DownloadProgressChangedEvent);
294 void
295 Image::ImageOpened ()
297 BitmapSource *source = (BitmapSource*)GetSource ();
299 if (source->Is (Type::BITMAPIMAGE)) {
300 source->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
301 source->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
302 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
305 InvalidateArrange ();
306 InvalidateMeasure ();
307 UpdateBounds ();
308 Invalidate ();
311 void
312 Image::ImageFailed (ImageErrorEventArgs *args)
314 BitmapSource *source = (BitmapSource*) GetSource ();
316 if (source->Is (Type::BITMAPIMAGE)) {
317 source->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
318 source->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
319 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
321 source->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
324 InvalidateArrange ();
325 InvalidateMeasure ();
326 UpdateBounds ();
327 Invalidate ();
329 args->ref (); // to counter the unref in Emit
330 Emit (ImageFailedEvent, args);
333 void
334 Image::SourcePixelDataChanged ()
336 Invalidate();
339 void
340 Image::SetSourceInternal (Downloader *downloader, char *PartName)
342 BitmapImage *source = (BitmapImage *) GetSource ();
344 MediaBase::SetSourceInternal (downloader, PartName);
346 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
347 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
348 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
350 source->SetDownloader (downloader, NULL, PartName);
353 void
354 Image::SetSource (Downloader *downloader, const char *PartName)
356 MediaBase::SetSource (downloader, PartName);
359 void
360 Image::Render (cairo_t *cr, Region *region, bool path_only)
362 ImageSource *source = GetSource ();
363 cairo_pattern_t *pattern = NULL;
364 cairo_matrix_t matrix;
365 Rect image;
366 Rect paint;
368 if (!source)
369 return;
371 source->Lock ();
373 cairo_save (cr);
375 Size specified (GetActualWidth (), GetActualHeight ());
376 Size stretched = ApplySizeConstraints (specified);
378 if (GetStretch () != StretchUniformToFill)
379 specified = specified.Min(stretched);
381 paint = Rect (0, 0, specified.width, specified.height);
383 if (!path_only) {
384 image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
386 if (GetStretch () == StretchNone)
387 paint = paint.Union (image);
389 if (image.width == 0.0 && image.height == 0.0)
390 return;
392 pattern = cairo_pattern_create_for_surface (source->GetSurface (cr));
393 image_brush_compute_pattern_matrix (&matrix, paint.width, paint.height,
394 image.width, image.height,
395 GetStretch (),
396 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
398 cairo_pattern_set_matrix (pattern, &matrix);
399 cairo_set_source (cr, pattern);
402 cairo_set_matrix (cr, &absolute_xform);
404 if (!path_only)
405 RenderLayoutClip (cr);
407 paint = paint.Intersection (Rect (0,0,stretched.width,stretched.height));
408 paint.Draw (cr);
410 if (!path_only)
411 cairo_fill (cr);
413 cairo_restore (cr);
415 if (pattern)
416 cairo_pattern_destroy (pattern);
418 source->Unlock ();
421 Size
422 Image::ComputeActualSize ()
424 Size result = MediaBase::ComputeActualSize ();
425 UIElement *parent = GetVisualParent ();
426 ImageSource *source = GetSource ();
428 if (parent && !parent->Is (Type::CANVAS))
429 if (LayoutInformation::GetLayoutSlot (this))
430 return result;
432 if (source && source->GetSurface (NULL)) {
433 Size available = Size (INFINITY, INFINITY);
434 available = ApplySizeConstraints (available);
435 result = MeasureOverride (available);
436 result = ApplySizeConstraints (result);
439 return result;
442 Size
443 Image::MeasureOverride (Size availableSize)
445 Size desired = availableSize;
446 Rect shape_bounds = Rect ();
447 ImageSource *source = GetSource ();
448 double sx = 0.0;
449 double sy = 0.0;
451 if (source)
452 shape_bounds = Rect (0,0,source->GetPixelWidth (),source->GetPixelHeight ());
454 /* don't stretch to infinite size */
455 if (isinf (desired.width))
456 desired.width = shape_bounds.width;
457 if (isinf (desired.height))
458 desired.height = shape_bounds.height;
460 /* compute the scaling */
461 if (shape_bounds.width > 0)
462 sx = desired.width / shape_bounds.width;
463 if (shape_bounds.height > 0)
464 sy = desired.height / shape_bounds.height;
466 /* don't use infinite dimensions as constraints */
467 if (isinf (availableSize.width))
468 sx = sy;
469 if (isinf (availableSize.height))
470 sy = sx;
472 switch (GetStretch ()) {
473 case StretchUniform:
474 sx = sy = MIN (sx, sy);
475 break;
476 case StretchUniformToFill:
477 sx = sy = MAX (sx, sy);
478 break;
479 case StretchFill:
480 if (isinf (availableSize.width))
481 sx = sy;
482 if (isinf (availableSize.height))
483 sy = sx;
484 break;
485 case StretchNone:
486 sx = sy = 1.0;
487 break;
490 desired = Size (shape_bounds.width * sx, shape_bounds.height * sy);
492 return desired;
495 Size
496 Image::ArrangeOverride (Size finalSize)
498 Size arranged = finalSize;
499 Rect shape_bounds = Rect ();
500 ImageSource *source = GetSource ();
501 double sx = 1.0;
502 double sy = 1.0;
505 if (source)
506 shape_bounds = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
508 /* compute the scaling */
509 if (shape_bounds.width == 0)
510 shape_bounds.width = arranged.width;
511 if (shape_bounds.height == 0)
512 shape_bounds.height = arranged.height;
514 if (shape_bounds.width != arranged.width)
515 sx = arranged.width / shape_bounds.width;
516 if (shape_bounds.height != arranged.height)
517 sy = arranged.height / shape_bounds.height;
519 switch (GetStretch ()) {
520 case StretchUniform:
521 sx = sy = MIN (sx, sy);
522 break;
523 case StretchUniformToFill:
524 sx = sy = MAX (sx, sy);
525 break;
526 case StretchNone:
527 sx = sy = 1.0;
528 default:
529 break;
532 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
534 return arranged;
537 Rect
538 Image::GetCoverageBounds ()
540 Stretch stretch = GetStretch ();
541 ImageSource *source = GetSource ();
543 if (!source || source->GetPixelFormat () == PixelFormatPbgra32)
544 return Rect ();
546 if (stretch == StretchFill || stretch == StretchUniformToFill)
547 return bounds;
549 cairo_matrix_t matrix;
550 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
551 Rect paint = Rect (0, 0, GetActualWidth (), GetActualHeight ());
553 image_brush_compute_pattern_matrix (&matrix,
554 paint.width, paint.height,
555 image.width, image.height, stretch,
556 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
558 cairo_matrix_invert (&matrix);
559 cairo_matrix_multiply (&matrix, &matrix, &absolute_xform);
561 image = image.Transform (&matrix);
562 image = image.Intersection (bounds);
564 return image;
567 void
568 Image::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
570 if (prop && (prop->GetId () == Image::SourceProperty
571 || prop->GetId () == MediaBase::SourceProperty)) {
572 InvalidateMeasure ();
573 Invalidate ();
574 return;
577 MediaBase::OnSubPropertyChanged (prop, obj, subobj_args);
579 void
580 Image::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
582 if (args->GetId () == Image::SourceProperty) {
583 ImageSource *source = args->GetNewValue () ? args->GetNewValue ()->AsImageSource () : NULL;
584 ImageSource *old = args->GetOldValue () ? args->GetOldValue ()->AsImageSource () : NULL;
586 if (old && old->Is(Type::BITMAPSOURCE)) {
587 old->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
589 if (source && source->Is(Type::BITMAPSOURCE)) {
590 source->AddHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
593 if (old && old->Is(Type::BITMAPIMAGE)) {
594 old->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
595 old->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
596 old->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
598 if (source && source->Is(Type::BITMAPIMAGE)) {
599 BitmapImage *bitmap = (BitmapImage *) source;
600 Uri *uri = bitmap->GetUriSource ();
602 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
603 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
604 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
606 if (bitmap->GetPixelWidth () > 0 && bitmap->GetPixelHeight () > 0) {
607 ImageOpened ();
610 // can uri ever be null?
611 if (IsBeingParsed () && uri && GetSurface ()) {
612 ImageErrorEventArgs *args = NULL;
614 if (uri->IsInvalidPath ()) {
615 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri"));
616 } else if (!bitmap->ValidateDownloadPolicy ()) {
617 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation"));
620 if (args != NULL) {
621 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
622 GetSurface ()->EmitError (args);
626 InvalidateMeasure ();
629 if (args->GetProperty ()->GetOwnerType() != Type::IMAGE) {
630 MediaBase::OnPropertyChanged (args, error);
631 return;
634 // we need to notify attachees if our DownloadProgress changed.
635 NotifyListenersOfPropertyChange (args, error);
638 bool
639 Image::InsideObject (cairo_t *cr, double x, double y)
641 if (!FrameworkElement::InsideObject (cr, x, y))
642 return false;
644 cairo_save (cr);
645 cairo_new_path (cr);
646 cairo_set_matrix (cr, &absolute_xform);
648 double nx = x;
649 double ny = y;
651 TransformPoint (&nx, &ny);
653 Render (cr, NULL, true);
654 bool inside = cairo_in_fill (cr, nx, ny);
655 cairo_restore (cr);
657 if (inside)
658 inside = InsideLayoutClip (x, y);
660 if (inside)
661 inside = InsideClip (cr, x, y);
663 return inside;
666 Value *
667 Image::CreateDefaultImageSource (DependencyObject *instance, DependencyProperty *property)
669 return Value::CreateUnrefPtr (new BitmapImage ());
673 // MediaAttributeCollection
676 MediaAttribute *
677 MediaAttributeCollection::GetItemByName (const char *name)
679 MediaAttribute *attr;
680 const char *value;
682 for (guint i = 0; i < array->len; i++) {
683 attr = ((Value *) array->pdata[i])->AsMediaAttribute ();
684 if (!(value = attr->GetName ()))
685 continue;
687 if (!g_ascii_strcasecmp (value, name))
688 return attr;
691 return NULL;
696 // TimelineMarkerCollection
700 TimelineMarkerCollection::AddWithError (Value *value, MoonError *error)
702 TimelineMarker *marker, *cur;
704 marker = value->AsTimelineMarker ();
706 for (guint i = 0; i < array->len; i++) {
707 cur = ((Value *) array->pdata[i])->AsTimelineMarker ();
708 if (cur->GetTime () >= marker->GetTime ()) {
709 DependencyObjectCollection::InsertWithError (i, value, error);
710 return i;
714 return DependencyObjectCollection::InsertWithError (array->len, value, error) ? array->len - 1 : -1;
717 bool
718 TimelineMarkerCollection::InsertWithError (int index, Value *value, MoonError *error)
720 return AddWithError (value, error) != -1;
725 // MarkerReachedEventArgs
728 MarkerReachedEventArgs::MarkerReachedEventArgs (TimelineMarker *marker)
730 SetObjectType (Type::MARKERREACHEDEVENTARGS);
731 this->marker = marker;
732 marker->ref ();
735 MarkerReachedEventArgs::~MarkerReachedEventArgs ()
737 marker->unref ();