2009-12-03 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / media.cpp
blobb28c7589693fb4a96609d82ff096630a682140db
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);
379 if (GetStretch () != StretchUniformToFill)
380 specified = specified.Min (stretched);
382 Rect paint = Rect (0, 0, specified.width, specified.height);
384 if (!path_only) {
385 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
387 if (GetStretch () == StretchNone)
388 paint = paint.Union (image);
390 if (image.width == 0.0 && image.height == 0.0)
391 return;
393 pattern = cairo_pattern_create_for_surface (source->GetSurface (cr));
394 image_brush_compute_pattern_matrix (&matrix, paint.width, paint.height,
395 image.width, image.height,
396 GetStretch (),
397 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
399 cairo_pattern_set_matrix (pattern, &matrix);
400 if (cairo_pattern_status (pattern) == CAIRO_STATUS_SUCCESS) {
401 cairo_set_source (cr, pattern);
403 cairo_pattern_destroy (pattern);
406 if (!path_only)
407 RenderLayoutClip (cr);
409 paint = paint.Intersection (Rect (0, 0, stretched.width, stretched.height));
410 paint.Draw (cr);
412 if (!path_only)
413 cairo_fill (cr);
415 cairo_restore (cr);
417 source->Unlock ();
420 Size
421 Image::ComputeActualSize ()
423 Size result = MediaBase::ComputeActualSize ();
424 UIElement *parent = GetVisualParent ();
425 ImageSource *source = GetSource ();
427 if (parent && !parent->Is (Type::CANVAS))
428 if (LayoutInformation::GetLayoutSlot (this))
429 return result;
431 if (source && source->GetSurface (NULL)) {
432 Size available = Size (INFINITY, INFINITY);
433 available = ApplySizeConstraints (available);
434 result = MeasureOverride (available);
435 result = ApplySizeConstraints (result);
438 return result;
441 Size
442 Image::MeasureOverride (Size availableSize)
444 Size desired = availableSize;
445 Rect shape_bounds = Rect ();
446 ImageSource *source = GetSource ();
447 double sx = 0.0;
448 double sy = 0.0;
450 if (source)
451 shape_bounds = Rect (0,0,source->GetPixelWidth (),source->GetPixelHeight ());
453 /* don't stretch to infinite size */
454 if (isinf (desired.width))
455 desired.width = shape_bounds.width;
456 if (isinf (desired.height))
457 desired.height = shape_bounds.height;
459 /* compute the scaling */
460 if (shape_bounds.width > 0)
461 sx = desired.width / shape_bounds.width;
462 if (shape_bounds.height > 0)
463 sy = desired.height / shape_bounds.height;
465 /* don't use infinite dimensions as constraints */
466 if (isinf (availableSize.width))
467 sx = sy;
468 if (isinf (availableSize.height))
469 sy = sx;
471 switch (GetStretch ()) {
472 case StretchUniform:
473 sx = sy = MIN (sx, sy);
474 break;
475 case StretchUniformToFill:
476 sx = sy = MAX (sx, sy);
477 break;
478 case StretchFill:
479 if (isinf (availableSize.width))
480 sx = sy;
481 if (isinf (availableSize.height))
482 sy = sx;
483 break;
484 case StretchNone:
485 sx = sy = 1.0;
486 break;
489 desired = Size (shape_bounds.width * sx, shape_bounds.height * sy);
491 return desired;
494 Size
495 Image::ArrangeOverride (Size finalSize)
497 Size arranged = finalSize;
498 Rect shape_bounds = Rect ();
499 ImageSource *source = GetSource ();
500 double sx = 1.0;
501 double sy = 1.0;
504 if (source)
505 shape_bounds = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
507 /* compute the scaling */
508 if (shape_bounds.width == 0)
509 shape_bounds.width = arranged.width;
510 if (shape_bounds.height == 0)
511 shape_bounds.height = arranged.height;
513 if (shape_bounds.width != arranged.width)
514 sx = arranged.width / shape_bounds.width;
515 if (shape_bounds.height != arranged.height)
516 sy = arranged.height / shape_bounds.height;
518 switch (GetStretch ()) {
519 case StretchUniform:
520 sx = sy = MIN (sx, sy);
521 break;
522 case StretchUniformToFill:
523 sx = sy = MAX (sx, sy);
524 break;
525 case StretchNone:
526 sx = sy = 1.0;
527 default:
528 break;
531 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
533 return arranged;
536 Rect
537 Image::GetCoverageBounds ()
539 Stretch stretch = GetStretch ();
540 ImageSource *source = GetSource ();
542 if (!source || source->GetPixelFormat () == PixelFormatPbgra32)
543 return Rect ();
545 if (stretch == StretchFill || stretch == StretchUniformToFill)
546 return bounds;
548 cairo_matrix_t matrix;
549 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
550 Rect paint = Rect (0, 0, GetActualWidth (), GetActualHeight ());
552 image_brush_compute_pattern_matrix (&matrix,
553 paint.width, paint.height,
554 image.width, image.height, stretch,
555 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
557 cairo_matrix_invert (&matrix);
558 cairo_matrix_multiply (&matrix, &matrix, &absolute_xform);
560 image = image.Transform (&matrix);
561 image = image.Intersection (bounds);
563 return image;
566 void
567 Image::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
569 if (prop && (prop->GetId () == Image::SourceProperty
570 || prop->GetId () == MediaBase::SourceProperty)) {
571 InvalidateMeasure ();
572 Invalidate ();
573 return;
576 MediaBase::OnSubPropertyChanged (prop, obj, subobj_args);
578 void
579 Image::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
581 if (args->GetId () == Image::SourceProperty) {
582 ImageSource *source = args->GetNewValue () ? args->GetNewValue ()->AsImageSource () : NULL;
583 ImageSource *old = args->GetOldValue () ? args->GetOldValue ()->AsImageSource () : NULL;
585 if (old && old->Is(Type::BITMAPSOURCE)) {
586 old->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
588 if (source && source->Is(Type::BITMAPSOURCE)) {
589 source->AddHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
592 if (old && old->Is(Type::BITMAPIMAGE)) {
593 old->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
594 old->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
595 old->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
597 if (source && source->Is(Type::BITMAPIMAGE)) {
598 BitmapImage *bitmap = (BitmapImage *) source;
599 Uri *uri = bitmap->GetUriSource ();
601 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
602 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
603 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
605 if (bitmap->GetPixelWidth () > 0 && bitmap->GetPixelHeight () > 0) {
606 RoutedEventArgs *args = new RoutedEventArgs ();
607 ImageOpened (args);
608 args->unref ();
611 // can uri ever be null?
612 if (IsBeingParsed () && uri && IsAttached ()) {
613 ImageErrorEventArgs *args = NULL;
615 if (uri->IsInvalidPath ()) {
616 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri"));
617 } else if (!bitmap->ValidateDownloadPolicy ()) {
618 args = new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation"));
621 if (args != NULL) {
622 source->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
623 GetDeployment ()->GetSurface ()->EmitError (args);
627 InvalidateMeasure ();
630 if (args->GetProperty ()->GetOwnerType() != Type::IMAGE) {
631 MediaBase::OnPropertyChanged (args, error);
632 return;
635 // we need to notify attachees if our DownloadProgress changed.
636 NotifyListenersOfPropertyChange (args, error);
639 bool
640 Image::InsideObject (cairo_t *cr, double x, double y)
642 if (!FrameworkElement::InsideObject (cr, x, y))
643 return false;
645 cairo_save (cr);
646 cairo_new_path (cr);
647 cairo_set_matrix (cr, &absolute_xform);
649 double nx = x;
650 double ny = y;
652 TransformPoint (&nx, &ny);
654 Render (cr, NULL, true);
655 bool inside = cairo_in_fill (cr, nx, ny);
656 cairo_restore (cr);
658 if (inside)
659 inside = InsideLayoutClip (x, y);
661 if (inside)
662 inside = InsideClip (cr, x, y);
664 return inside;
667 Value *
668 Image::CreateDefaultImageSource (DependencyObject *instance, DependencyProperty *property)
670 return Value::CreateUnrefPtr (new BitmapImage ());
674 // MediaAttributeCollection
677 MediaAttribute *
678 MediaAttributeCollection::GetItemByName (const char *name)
680 MediaAttribute *attr;
681 const char *value;
683 for (guint i = 0; i < array->len; i++) {
684 attr = ((Value *) array->pdata[i])->AsMediaAttribute ();
685 if (!(value = attr->GetName ()))
686 continue;
688 if (!g_ascii_strcasecmp (value, name))
689 return attr;
692 return NULL;
697 // TimelineMarkerCollection
701 TimelineMarkerCollection::AddWithError (Value *value, MoonError *error)
703 TimelineMarker *marker, *cur;
705 marker = value->AsTimelineMarker ();
707 for (guint i = 0; i < array->len; i++) {
708 cur = ((Value *) array->pdata[i])->AsTimelineMarker ();
709 if (cur->GetTime () >= marker->GetTime ()) {
710 DependencyObjectCollection::InsertWithError (i, value, error);
711 return i;
715 return DependencyObjectCollection::InsertWithError (array->len, value, error) ? array->len - 1 : -1;
718 bool
719 TimelineMarkerCollection::InsertWithError (int index, Value *value, MoonError *error)
721 return AddWithError (value, error) != -1;