2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / media.cpp
blob1d15b0f2678d51388b312667f31c6d2de18c69d9
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);
323 InvalidateArrange ();
324 InvalidateMeasure ();
325 UpdateBounds ();
326 Invalidate ();
328 args->ref (); // to counter the unref in Emit
329 Emit (ImageFailedEvent, args);
332 void
333 Image::SourcePixelDataChanged ()
335 Invalidate();
338 void
339 Image::SetSourceInternal (Downloader *downloader, char *PartName)
341 BitmapImage *source = (BitmapImage *) GetSource ();
343 MediaBase::SetSourceInternal (downloader, PartName);
345 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
346 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
347 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
349 source->SetDownloader (downloader, NULL, PartName);
352 void
353 Image::SetSource (Downloader *downloader, const char *PartName)
355 MediaBase::SetSource (downloader, PartName);
358 void
359 Image::Render (cairo_t *cr, Region *region, bool path_only)
361 ImageSource *source = GetSource ();
362 cairo_surface_t *cairo_surface;
363 cairo_pattern_t *pattern;
364 cairo_matrix_t matrix;
365 Rect image;
366 Rect paint;
368 if (!source)
369 return;
371 source->Lock ();
373 cairo_surface = source->GetSurface (cr);
375 if (GetActualWidth () == 0.0 && GetActualHeight () == 0.0)
376 return;
377 if (source->GetPixelWidth () == 0.0 && source->GetPixelWidth () == 0.0)
378 return;
380 cairo_save (cr);
382 image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
383 Size specified (GetActualWidth (), GetActualHeight ());
385 if (GetStretch () != StretchNone)
386 specified = ApplySizeConstraints (specified);
388 paint = Rect (0, 0, specified.width, specified.height);
389 pattern = cairo_pattern_create_for_surface (cairo_surface);
391 image_brush_compute_pattern_matrix (&matrix, paint.width, paint.height, image.width, image.height, GetStretch (),
392 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
394 cairo_pattern_set_matrix (pattern, &matrix);
395 cairo_set_source (cr, pattern);
397 cairo_set_matrix (cr, &absolute_xform);
399 if (!path_only)
400 RenderLayoutClip (cr);
402 paint.Draw (cr);
403 cairo_fill (cr);
405 cairo_restore (cr);
406 cairo_pattern_destroy (pattern);
408 source->Unlock ();
411 Size
412 Image::ComputeActualSize ()
414 Size result = MediaBase::ComputeActualSize ();
415 Size specified = Size (GetWidth (), GetHeight ());
417 if (GetSource () && GetSource ()->GetSurface (NULL)) {
418 Size available = Size (INFINITY, INFINITY);
419 available = available.Min (specified);
420 result = MeasureOverride (available);
424 return result;
427 Size
428 Image::MeasureOverride (Size availableSize)
430 Size desired = availableSize;
431 Rect shape_bounds = Rect ();
432 ImageSource *source = GetSource ();
433 double sx = 0.0;
434 double sy = 0.0;
436 if (source)
437 shape_bounds = Rect (0,0,source->GetPixelWidth (),source->GetPixelHeight ());
439 if (GetStretch () == StretchNone)
440 return desired.Min (shape_bounds.width, shape_bounds.height);
442 /* don't stretch to infinite size */
443 if (isinf (desired.width))
444 desired.width = shape_bounds.width;
445 if (isinf (desired.height))
446 desired.height = shape_bounds.height;
448 /* compute the scaling */
449 if (shape_bounds.width > 0)
450 sx = desired.width / shape_bounds.width;
451 if (shape_bounds.height > 0)
452 sy = desired.height / shape_bounds.height;
454 switch (GetStretch ()) {
455 case StretchUniform:
456 sx = sy = MIN (sx, sy);
457 break;
458 case StretchUniformToFill:
459 sx = sy = MAX (sx, sy);
460 break;
461 default:
462 break;
465 desired = desired.Min (shape_bounds.width * sx, shape_bounds.height * sy);
467 return desired;
470 Size
471 Image::ArrangeOverride (Size finalSize)
473 Size arranged = finalSize;
474 Rect shape_bounds = Rect ();
475 ImageSource *source = GetSource ();
476 double sx = 1.0;
477 double sy = 1.0;
480 if (source)
481 shape_bounds = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
483 if (GetStretch () == StretchNone) {
484 arranged = Size (shape_bounds.x + shape_bounds.width,
485 shape_bounds.y + shape_bounds.height);
487 arranged = arranged.Max (finalSize);
489 return arranged;
492 /* compute the scaling */
493 if (shape_bounds.width == 0)
494 shape_bounds.width = arranged.width;
495 if (shape_bounds.height == 0)
496 shape_bounds.height = arranged.height;
498 if (shape_bounds.width != arranged.width)
499 sx = arranged.width / shape_bounds.width;
500 if (shape_bounds.height != arranged.height)
501 sy = arranged.height / shape_bounds.height;
503 switch (GetStretch ()) {
504 case StretchUniform:
505 sx = sy = MIN (sx, sy);
506 break;
507 case StretchUniformToFill:
508 sx = sy = MAX (sx, sy);
509 break;
510 default:
511 break;
514 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
516 return arranged;
519 Rect
520 Image::GetCoverageBounds ()
522 Stretch stretch = GetStretch ();
523 ImageSource *source = GetSource ();
525 if (!source || source->GetPixelFormat () == PixelFormatPbgra32)
526 return Rect ();
528 if (stretch == StretchFill || stretch == StretchUniformToFill)
529 return bounds;
531 cairo_matrix_t matrix;
532 Rect image = Rect (0, 0, source->GetPixelWidth (), source->GetPixelHeight ());
533 Rect paint = Rect (0, 0, GetActualWidth (), GetActualHeight ());
535 image_brush_compute_pattern_matrix (&matrix,
536 paint.width, paint.height,
537 image.width, image.height, stretch,
538 AlignmentXCenter, AlignmentYCenter, NULL, NULL);
540 cairo_matrix_invert (&matrix);
541 cairo_matrix_multiply (&matrix, &matrix, &absolute_xform);
543 image = image.Transform (&matrix);
544 image = image.Intersection (bounds);
546 return image;
549 void
550 Image::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
552 if (args->GetId () == Image::SourceProperty) {
553 ImageSource *source = args->GetNewValue () ? args->GetNewValue ()->AsImageSource () : NULL;
554 ImageSource *old = args->GetOldValue () ? args->GetOldValue ()->AsImageSource () : NULL;
556 if (old && old->Is(Type::BITMAPSOURCE)) {
557 old->RemoveHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
559 if (source && source->Is(Type::BITMAPSOURCE)) {
560 source->AddHandler (BitmapSource::PixelDataChangedEvent, source_pixel_data_changed, this);
563 if (old && old->Is(Type::BITMAPIMAGE)) {
564 old->RemoveHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
565 old->RemoveHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
566 old->RemoveHandler (BitmapImage::ImageFailedEvent, image_failed, this);
568 if (source && source->Is(Type::BITMAPIMAGE)) {
569 source->AddHandler (BitmapImage::DownloadProgressEvent, download_progress, this);
570 source->AddHandler (BitmapImage::ImageOpenedEvent, image_opened, this);
571 source->AddHandler (BitmapImage::ImageFailedEvent, image_failed, this);
573 if (((BitmapImage *)source)->GetPixelWidth () > 0 && ((BitmapImage *)source)->GetPixelHeight () > 0) {
574 ImageOpened ();
579 if (args->GetProperty ()->GetOwnerType() != Type::IMAGE) {
580 MediaBase::OnPropertyChanged (args, error);
581 return;
584 // we need to notify attachees if our DownloadProgress changed.
585 NotifyListenersOfPropertyChange (args, error);
588 bool
589 Image::InsideObject (cairo_t *cr, double x, double y)
591 if (!GetSource ())
592 return false;
594 return FrameworkElement::InsideObject (cr, x, y);
597 Value *
598 Image::CreateDefaultImageSource (DependencyObject *instance, DependencyProperty *property)
600 return Value::CreateUnrefPtr (new BitmapImage ());
604 // MediaAttributeCollection
607 MediaAttribute *
608 MediaAttributeCollection::GetItemByName (const char *name)
610 MediaAttribute *attr;
611 const char *value;
613 for (guint i = 0; i < array->len; i++) {
614 attr = ((Value *) array->pdata[i])->AsMediaAttribute ();
615 if (!(value = attr->GetName ()))
616 continue;
618 if (!g_ascii_strcasecmp (value, name))
619 return attr;
622 return NULL;
627 // TimelineMarkerCollection
631 TimelineMarkerCollection::AddWithError (Value *value, MoonError *error)
633 TimelineMarker *marker, *cur;
635 marker = value->AsTimelineMarker ();
637 for (guint i = 0; i < array->len; i++) {
638 cur = ((Value *) array->pdata[i])->AsTimelineMarker ();
639 if (cur->GetTime () >= marker->GetTime ()) {
640 DependencyObjectCollection::InsertWithError (i, value, error);
641 return i;
645 return DependencyObjectCollection::InsertWithError (array->len, value, error) ? array->len - 1 : -1;
648 bool
649 TimelineMarkerCollection::InsertWithError (int index, Value *value, MoonError *error)
651 return AddWithError (value, error) != -1;
656 // MarkerReachedEventArgs
659 MarkerReachedEventArgs::MarkerReachedEventArgs (TimelineMarker *marker)
661 SetObjectType (Type::MARKERREACHEDEVENTARGS);
662 this->marker = marker;
663 marker->ref ();
666 MarkerReachedEventArgs::~MarkerReachedEventArgs ()
668 marker->unref ();