1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007, 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
21 #include "mediaelement.h"
23 #include "transform.h"
24 #include "mediaplayer.h"
25 #include "bitmapimage.h"
29 // SL-Cairo convertion and helper routines
33 convert_gradient_spread_method (GradientSpreadMethod method
)
36 case GradientSpreadMethodPad
:
37 return CAIRO_EXTEND_PAD
;
38 case GradientSpreadMethodReflect
:
39 return CAIRO_EXTEND_REFLECT
;
40 // unknown (e.g. bad) values are considered to be Repeat by Silverlight
41 // even if the default, i.e. *no* value) is Pad
42 case GradientSpreadMethodRepeat
:
44 return CAIRO_EXTEND_REPEAT
;
49 brush_matrix_invert (cairo_matrix_t
*matrix
)
51 cairo_status_t status
= cairo_matrix_invert (matrix
);
52 if (status
!= CAIRO_STATUS_SUCCESS
) {
53 printf ("Moonlight: Error inverting matrix falling back\n");
54 cairo_matrix_init_identity (matrix
);
65 SetObjectType (Type::BRUSH
);
69 Brush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
71 g_warning ("Brush:SetupBrush has been called. The derived class should have overridden it.");
75 Brush::Fill (cairo_t
*cr
, bool preserve
)
78 cairo_fill_preserve (cr
);
84 Brush::Stroke (cairo_t
*cr
, bool preserve
)
87 cairo_stroke_preserve (cr
);
93 Brush::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
95 // if our transforms change in some fashion, we need to redraw
97 NotifyListenersOfPropertyChange (Brush::ChangedProperty
, NULL
);
99 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
105 return !IS_TRANSLUCENT (GetOpacity ());
109 Brush::IsAnimating ()
115 transform_get_absolute_transform (Transform
*relative_transform
, double width
, double height
, cairo_matrix_t
*result
)
119 cairo_matrix_init_scale (result
, width
, height
);
120 relative_transform
->GetTransform (&tm
);
121 cairo_matrix_multiply (result
, &tm
, result
);
122 cairo_matrix_scale (result
, 1.0/width
, 1.0/height
);
131 SolidColorBrush::SolidColorBrush ()
133 SetObjectType (Type::SOLIDCOLORBRUSH
);
136 SolidColorBrush::SolidColorBrush (const char *color
)
138 SetObjectType (Type::SOLIDCOLORBRUSH
);
139 Color
*c
= color_from_str (color
);
145 SolidColorBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
147 double opacity
= GetOpacity ();
148 Color
*color
= GetColor ();
150 cairo_set_source_rgba (cr
, color
->r
, color
->g
, color
->b
, opacity
* color
->a
);
154 SolidColorBrush::IsOpaque ()
156 return Brush::IsOpaque () && !IS_TRANSLUCENT (GetColor ()->a
);
164 GradientBrush::GradientBrush ()
166 SetObjectType (Type::GRADIENTBRUSH
);
169 GradientBrush::~GradientBrush ()
174 GradientBrush::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
176 if (col
!= GetValue (GradientBrush::GradientStopsProperty
)->AsCollection ()) {
177 Brush::OnCollectionChanged (col
, args
);
181 NotifyListenersOfPropertyChange (GradientBrush::GradientStopsProperty
, NULL
);
185 GradientBrush::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
187 if (col
!= GetValue (GradientBrush::GradientStopsProperty
)->AsCollection ()) {
188 Brush::OnCollectionItemChanged (col
, obj
, args
);
192 NotifyListenersOfPropertyChange (GradientBrush::GradientStopsProperty
, NULL
);
196 GradientBrush::SetupGradient (cairo_pattern_t
*pattern
, const Rect
&area
, bool single
)
198 GradientStopCollection
*children
= GetGradientStops ();
199 GradientSpreadMethod gsm
= GetSpreadMethod ();
200 double opacity
= GetOpacity ();
205 cairo_pattern_set_extend (pattern
, convert_gradient_spread_method (gsm
));
207 // TODO - ColorInterpolationModeProperty is ignored (map to ?)
209 // if a single color is shown (e.g. start == end point) Cairo will,
210 // by default, use the start color while SL use the end color
211 index
= children
->GetCount () - 1;
216 GradientStop
*negative_stop
= NULL
; //the biggest negative stop
217 double neg_offset
= 0.0; //the cached associated offset
218 GradientStop
*first_stop
= NULL
; //the smallest positive stop
219 double first_offset
= 0.0; //idem
220 GradientStop
*last_stop
= NULL
; //the biggest stop <= 1
221 double last_offset
= 0.0; //idem
222 GradientStop
*outofbounds_stop
= NULL
; //the smallest stop > 1
223 double out_offset
= 0.0; //idem
225 for ( ; index
< children
->GetCount (); index
++) {
226 stop
= children
->GetValueAt (index
)->AsGradientStop ();
227 offset
= stop
->GetOffset ();
229 if (offset
>= 0.0 && offset
<= 1.0) {
230 Color
*color
= stop
->GetColor ();
232 cairo_pattern_add_color_stop_rgba (pattern
, offset
, color
->r
, color
->g
, color
->b
, color
->a
* opacity
);
234 if (!first_stop
|| (first_offset
!= 0.0 && offset
< first_offset
)) {
235 first_offset
= offset
;
239 if (!last_stop
|| (last_offset
!= 1.0 && offset
> last_offset
)) {
240 last_offset
= offset
;
243 } else if (offset
< 0.0 && (!negative_stop
|| offset
> neg_offset
)) {
244 negative_stop
= stop
;
246 } else if (offset
> 1.0 && (!outofbounds_stop
|| offset
< out_offset
)) {
247 outofbounds_stop
= stop
;
252 if (negative_stop
&& first_stop
&& first_offset
!= 0.0) { //take care of the negative stop
253 Color
*neg_color
= negative_stop
->GetColor ();
254 Color
*first_color
= first_stop
->GetColor ();
255 double ratio
= neg_offset
/ (neg_offset
- first_offset
);
257 cairo_pattern_add_color_stop_rgba (pattern
, 0.0,
258 neg_color
->r
+ ratio
* (first_color
->r
- neg_color
->r
),
259 neg_color
->g
+ ratio
* (first_color
->g
- neg_color
->g
),
260 neg_color
->b
+ ratio
* (first_color
->b
- neg_color
->b
),
261 (neg_color
->a
+ ratio
* (first_color
->a
- neg_color
->a
)) * opacity
);
264 if (outofbounds_stop
&& last_stop
&& last_offset
!= 1.0) { //take care of the >1 stop
265 Color
*last_color
= last_stop
->GetColor ();
266 Color
*out_color
= outofbounds_stop
->GetColor ();
267 double ratio
= (1.0 - last_offset
) / (out_offset
- last_offset
);
269 cairo_pattern_add_color_stop_rgba (pattern
, 1.0,
270 last_color
->r
+ ratio
* (out_color
->r
- last_color
->r
),
271 last_color
->g
+ ratio
* (out_color
->g
- last_color
->g
),
272 last_color
->b
+ ratio
* (out_color
->b
- last_color
->b
),
273 (last_color
->a
+ ratio
* (out_color
->a
- last_color
->a
)) * opacity
);
276 if (negative_stop
&& outofbounds_stop
&& !first_stop
&& !last_stop
) { //only 2 stops, one < 0, the other > 1
277 Color
*neg_color
= negative_stop
->GetColor ();
278 Color
*out_color
= outofbounds_stop
->GetColor ();
279 double ratio
= neg_offset
/ (neg_offset
- out_offset
);
281 cairo_pattern_add_color_stop_rgba (pattern
, 0.0,
282 neg_color
->r
+ ratio
* (out_color
->r
- neg_color
->r
),
283 neg_color
->g
+ ratio
* (out_color
->g
- neg_color
->g
),
284 neg_color
->b
+ ratio
* (out_color
->b
- neg_color
->b
),
285 (neg_color
->a
+ ratio
* (out_color
->a
- neg_color
->a
)) * opacity
);
287 ratio
= (1.0 - neg_offset
) / (out_offset
- neg_offset
);
289 cairo_pattern_add_color_stop_rgba (pattern
, 1.0,
290 neg_color
->r
+ ratio
* (out_color
->r
- neg_color
->r
),
291 neg_color
->g
+ ratio
* (out_color
->g
- neg_color
->g
),
292 neg_color
->b
+ ratio
* (out_color
->b
- neg_color
->b
),
293 (neg_color
->a
+ ratio
* (out_color
->a
- neg_color
->a
)) * opacity
);
296 if (negative_stop
&& !outofbounds_stop
&& !first_stop
&& !last_stop
) { //only negative stops
297 Color
*color
= negative_stop
->GetColor ();
299 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, color
->r
, color
->g
, color
->b
, color
->a
* opacity
);
302 if (outofbounds_stop
&& !negative_stop
&& !first_stop
&& !last_stop
) { //only > 1 stops
303 Color
*color
= outofbounds_stop
->GetColor ();
305 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, color
->r
, color
->g
, color
->b
, color
->a
* opacity
);
310 GradientBrush::IsOpaque ()
312 if (!Brush::IsOpaque ())
315 GradientStopCollection
*stops
= GetGradientStops ();
319 for (int i
= 0; i
< stops
->GetCount (); i
++) {
320 stop
= stops
->GetValueAt (i
)->AsGradientStop ();
321 c
= stop
->GetColor ();
322 if (IS_TRANSLUCENT (c
->a
))
330 // GradientStopCollection
333 GradientStopCollection::GradientStopCollection ()
335 SetObjectType (Type::GRADIENTSTOP_COLLECTION
);
338 GradientStopCollection::~GradientStopCollection ()
347 GradientStop::GradientStop()
349 SetObjectType (Type::GRADIENTSTOP
);
352 GradientStop::~GradientStop()
357 // LinearGradientBrush
360 LinearGradientBrush::LinearGradientBrush ()
362 SetObjectType (Type::LINEARGRADIENTBRUSH
);
365 LinearGradientBrush::~LinearGradientBrush ()
370 LinearGradientBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
372 Point
*start
= GetStartPoint ();
373 Point
*end
= GetEndPoint ();
374 double x0
, y0
, x1
, y1
;
375 cairo_matrix_t offset_matrix
;
376 Point p
= area
.GetTopLeft ();
378 switch (GetMappingMode ()) {
379 // unknown (e.g. bad) values are considered to be Absolute to Silverlight
380 // even if the default, i.e. *no* value) is RelativeToBoundingBox
381 case BrushMappingModeAbsolute
:
383 y0
= start
? start
->y
: 0.0;
384 x0
= start
? start
->x
: 0.0;
385 y1
= end
? end
->y
: area
.height
;
386 x1
= end
? end
->x
: area
.width
;
388 case BrushMappingModeRelativeToBoundingBox
:
389 y0
= start
? (start
->y
* area
.height
) : 0.0;
390 x0
= start
? (start
->x
* area
.width
) : 0.0;
391 y1
= end
? (end
->y
* area
.height
) : area
.height
;
392 x1
= end
? (end
->x
* area
.width
) : area
.width
;
396 cairo_pattern_t
*pattern
= cairo_pattern_create_linear (x0
, y0
, x1
, y1
);
398 cairo_matrix_t matrix
;
399 cairo_matrix_init_identity (&matrix
);
401 Transform
*transform
= GetTransform ();
405 transform
->GetTransform (&tm
);
406 // TODO - optimization, check for empty/identity matrix too ?
407 cairo_matrix_multiply (&matrix
, &matrix
, &tm
);
410 Transform
*relative_transform
= GetRelativeTransform ();
411 if (relative_transform
) {
413 transform_get_absolute_transform (relative_transform
, area
.width
, area
.height
, &tm
);
414 cairo_matrix_multiply (&matrix
, &matrix
, &tm
);
417 if (p
.x
!= 0.0 && p
.y
!= 0.0) {
418 cairo_matrix_init_translate (&offset_matrix
, p
.x
, p
.y
);
419 cairo_matrix_multiply (&matrix
, &matrix
, &offset_matrix
);
422 brush_matrix_invert (&matrix
);
423 cairo_pattern_set_matrix (pattern
, &matrix
);
425 bool only_start
= (x0
== x1
&& y0
== y1
);
426 GradientBrush::SetupGradient (pattern
, area
, only_start
);
428 if (cairo_pattern_status (pattern
) == CAIRO_STATUS_SUCCESS
)
429 cairo_set_source (cr
, pattern
);
431 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
433 cairo_pattern_destroy (pattern
);
437 // RadialGradientBrush
440 RadialGradientBrush::RadialGradientBrush ()
442 SetObjectType (Type::RADIALGRADIENTBRUSH
);
445 RadialGradientBrush::~RadialGradientBrush()
450 RadialGradientBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
452 Point
*origin
= GetGradientOrigin ();
453 double ox
= (origin
? origin
->x
: 0.5);
454 double oy
= (origin
? origin
->y
: 0.5);
455 cairo_matrix_t offset_matrix
;
457 Point
*center
= GetCenter ();
458 double cx
= (center
? center
->x
: 0.5);
459 double cy
= (center
? center
->y
: 0.5);
461 double rx
= GetRadiusX ();
462 double ry
= GetRadiusY ();
464 cairo_pattern_t
*pattern
= cairo_pattern_create_radial (ox
/rx
, oy
/ry
, 0.0, cx
/rx
, cy
/ry
, 1);
466 cairo_matrix_t matrix
;
467 switch (GetMappingMode ()) {
468 // unknown (e.g. bad) values are considered to be Absolute to Silverlight
469 // even if the default, i.e. *no* value) is RelativeToBoundingBox
470 case BrushMappingModeAbsolute
:
472 cairo_matrix_init_translate (&matrix
, cx
, cy
);
473 cairo_matrix_scale (&matrix
, rx
, ry
);
474 cairo_matrix_translate (&matrix
, -cx
/rx
, -cy
/ry
);
476 case BrushMappingModeRelativeToBoundingBox
:
477 cairo_matrix_init_translate (&matrix
, cx
* area
.width
, cy
* area
.height
);
478 cairo_matrix_scale (&matrix
, area
.width
* rx
, area
.height
* ry
);
479 cairo_matrix_translate (&matrix
, -cx
/rx
, -cy
/ry
);
483 Transform
*transform
= GetTransform ();
487 transform
->GetTransform (&tm
);
488 // TODO - optimization, check for empty/identity matrix too ?
489 cairo_matrix_multiply (&matrix
, &matrix
, &tm
);
492 Transform
*relative_transform
= GetRelativeTransform ();
493 if (relative_transform
) {
495 transform_get_absolute_transform (relative_transform
, area
.width
, area
.height
, &tm
);
496 // TODO - optimization, check for empty/identity matrix too ?
497 cairo_matrix_multiply (&matrix
, &matrix
, &tm
);
500 if (area
.x
!= 0.0 || area
.y
!= 0.0) {
501 cairo_matrix_init_translate (&offset_matrix
, area
.x
, area
.y
);
502 cairo_matrix_multiply (&matrix
, &matrix
, &offset_matrix
);
505 brush_matrix_invert (&matrix
);
507 cairo_pattern_set_matrix (pattern
, &matrix
);
508 GradientBrush::SetupGradient (pattern
, area
);
510 if (cairo_pattern_status (pattern
) == CAIRO_STATUS_SUCCESS
)
511 cairo_set_source (cr
, pattern
);
513 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
515 cairo_pattern_destroy (pattern
);
522 ImageBrush::ImageBrush ()
524 SetObjectType (Type::IMAGEBRUSH
);
527 ImageBrush::~ImageBrush ()
532 ImageBrush::Dispose ()
534 BitmapImage
*source
= (BitmapImage
*) GetImageSource ();
537 source
->RemoveHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
538 source
->RemoveHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
539 source
->RemoveHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
540 source
->RemoveHandler (BitmapSource::PixelDataChangedEvent
, source_pixel_data_changed
, this);
543 TileBrush::Dispose ();
547 ImageBrush::download_progress (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
549 ImageBrush
*media
= (ImageBrush
*) closure
;
551 media
->DownloadProgress ();
555 ImageBrush::image_opened (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
557 ImageBrush
*media
= (ImageBrush
*) closure
;
559 media
->ImageOpened ((RoutedEventArgs
*) calldata
);
563 ImageBrush::image_failed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
565 ImageBrush
*media
= (ImageBrush
*) closure
;
567 media
->ImageFailed ((ImageErrorEventArgs
*) calldata
);
571 ImageBrush::source_pixel_data_changed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
573 ImageBrush
*media
= (ImageBrush
*) closure
;
575 media
->SourcePixelDataChanged ();
579 ImageBrush::DownloadProgress ()
581 BitmapImage
*source
= (BitmapImage
*) GetImageSource ();
583 SetDownloadProgress (source
->GetProgress ());
584 Emit (DownloadProgressChangedEvent
);
588 ImageBrush::ImageOpened (RoutedEventArgs
*args
)
590 BitmapImage
*source
= (BitmapImage
*)GetImageSource ();
592 source
->RemoveHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
593 source
->RemoveHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
594 source
->RemoveHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
596 args
->ref (); // to counter the unref in Emit
597 Emit (ImageOpenedEvent
, args
);
601 ImageBrush::ImageFailed (ImageErrorEventArgs
*args
)
603 BitmapImage
*source
= (BitmapImage
*)GetImageSource ();
605 source
->RemoveHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
606 source
->RemoveHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
607 source
->RemoveHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
609 args
->ref (); // to counter the unref in Emit
610 Emit (ImageFailedEvent
, args
);
614 ImageBrush::SourcePixelDataChanged ()
616 NotifyListenersOfPropertyChange (Brush::ChangedProperty
, NULL
);
620 ImageBrush::SetSource (Downloader
*downloader
, const char *PartName
)
622 BitmapImage
*source
= (BitmapImage
*) GetImageSource ();
624 if (source
== NULL
) {
625 source
= new BitmapImage ();
626 SetImageSource (source
);
629 source
->AddHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
630 source
->AddHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
631 source
->AddHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
633 source
->SetDownloader (downloader
, NULL
, PartName
);
637 ImageBrush::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
639 if (args
->GetProperty ()->GetOwnerType() != Type::IMAGEBRUSH
) {
640 TileBrush::OnPropertyChanged (args
, error
);
642 } else if (args
->GetId () == ImageSourceProperty
) {
643 ImageSource
*source
= args
->GetNewValue () ? args
->GetNewValue ()->AsImageSource () : NULL
;
644 ImageSource
*old
= args
->GetOldValue () ? args
->GetOldValue ()->AsImageSource () : NULL
;
646 if (old
&& old
->Is(Type::BITMAPSOURCE
)) {
647 old
->RemoveHandler (BitmapSource::PixelDataChangedEvent
, source_pixel_data_changed
, this);
649 if (source
&& source
->Is(Type::BITMAPSOURCE
)) {
650 source
->AddHandler (BitmapSource::PixelDataChangedEvent
, source_pixel_data_changed
, this);
653 if (old
&& old
->Is(Type::BITMAPIMAGE
)) {
654 old
->RemoveHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
655 old
->RemoveHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
656 old
->RemoveHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
658 if (source
&& source
->Is(Type::BITMAPIMAGE
)) {
659 BitmapImage
*bitmap
= (BitmapImage
*) source
;
660 Uri
*uri
= bitmap
->GetUriSource ();
662 source
->AddHandler (BitmapImage::DownloadProgressEvent
, download_progress
, this);
663 source
->AddHandler (BitmapImage::ImageOpenedEvent
, image_opened
, this);
664 source
->AddHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
666 // can uri ever be null?
668 ImageErrorEventArgs
*args
= NULL
;
670 if (uri
->IsInvalidPath ()) {
671 args
= new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE
, 0, "invalid path found in uri"));
672 } else if (!bitmap
->ValidateDownloadPolicy ()) {
673 args
= new ImageErrorEventArgs (MoonError (MoonError::ARGUMENT_OUT_OF_RANGE
, 0, "Security Policy Violation"));
677 source
->RemoveHandler (BitmapImage::ImageFailedEvent
, image_failed
, this);
678 EmitAsync (ImageFailedEvent
, args
);
682 SourcePixelDataChanged ();
685 NotifyListenersOfPropertyChange (args
, error
);
689 ImageBrush::IsOpaque ()
691 // XXX punt for now and return false here.
696 image_brush_create_similar (cairo_t
*cairo
, int width
, int height
)
698 #if USE_OPT_IMAGE_ONLY
699 return cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
701 return cairo_surface_create_similar (cairo_get_group_target (cairo
),
702 CAIRO_CONTENT_COLOR_ALPHA
,
709 image_brush_compute_pattern_matrix (cairo_matrix_t
*matrix
, double width
, double height
, int sw
, int sh
,
710 Stretch stretch
, AlignmentX align_x
, AlignmentY align_y
, Transform
*transform
, Transform
*relative_transform
)
712 // scale required to "fit" for both axes
713 double sx
= sw
/ width
;
714 double sy
= sh
/ height
;
723 // Fill is the simplest case because AlignementX and AlignmentY don't matter in this case
724 if (stretch
== StretchFill
) {
725 // fill extents in both axes
726 cairo_matrix_init_scale (matrix
, sx
, sy
);
734 // fill without cuting the image, center the other axes
735 scale
= (sx
< sy
) ? sy
: sx
;
737 case StretchUniformToFill
:
738 // fill by, potentially, cuting the image on one axe, center on both axes
739 scale
= (sx
< sy
) ? sx
: sy
;
744 g_warning ("Invalid Stretch value (%d).", stretch
);
752 case AlignmentXCenter
:
753 dx
= (sw
- (scale
* width
)) / 2;
755 // Silverlight+Javascript default to AlignmentXRight for (some) invalid values (others results in an alert)
756 case AlignmentXRight
:
758 dx
= (sw
- (scale
* width
));
766 case AlignmentYCenter
:
767 dy
= (sh
- (scale
* height
)) / 2;
769 // Silverlight+Javascript default to AlignmentXBottom for (some) invalid values (others results in an alert)
770 case AlignmentYBottom
:
772 dy
= (sh
- (scale
* height
));
776 if (stretch
== StretchNone
) {
777 // no strech, no scale
778 cairo_matrix_init_translate (matrix
, dx
, dy
);
780 // otherwise there's both a scale and translation to be done
781 cairo_matrix_init (matrix
, scale
, 0, 0, scale
, dx
, dy
);
785 if (transform
|| relative_transform
) {
789 transform
->GetTransform (&tm
);
790 brush_matrix_invert (&tm
);
791 cairo_matrix_multiply (matrix
, &tm
, matrix
);
794 if (relative_transform
) {
797 transform_get_absolute_transform (relative_transform
, width
, height
, &tm
);
798 brush_matrix_invert (&tm
);
799 cairo_matrix_multiply (matrix
, &tm
, matrix
);
805 is_stretch_valid (Stretch stretch
)
811 case StretchUniformToFill
:
819 ImageBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
821 ImageSource
*source
= GetImageSource ();
822 cairo_surface_t
*surface
;
823 cairo_pattern_t
*pattern
;
824 cairo_matrix_t matrix
;
825 Transform
*transform
;
826 Transform
*relative_transform
;
831 if (!source
) goto failed
;
835 surface
= source
->GetSurface (cr
);
837 stretch
= GetStretch ();
839 if (!surface
|| !is_stretch_valid (stretch
)) {
844 ax
= GetAlignmentX ();
845 ay
= GetAlignmentY ();
847 transform
= GetTransform ();
848 relative_transform
= GetRelativeTransform ();
850 pattern
= cairo_pattern_create_for_surface (surface
);
852 image_brush_compute_pattern_matrix (&matrix
, area
.width
, area
.height
, source
->GetPixelWidth (), source
->GetPixelHeight (), stretch
, ax
, ay
, transform
, relative_transform
);
853 cairo_matrix_translate (&matrix
, -area
.x
, -area
.y
);
854 cairo_pattern_set_matrix (pattern
, &matrix
);
856 if (cairo_pattern_status (pattern
) == CAIRO_STATUS_SUCCESS
)
857 cairo_set_source (cr
, pattern
);
859 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
861 cairo_pattern_destroy (pattern
);
868 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
876 TileBrush::TileBrush ()
878 SetObjectType (Type::TILEBRUSH
);
881 TileBrush::~TileBrush ()
886 TileBrush::Fill (cairo_t
*cr
, bool preserve
)
888 double opacity
= GetOpacity ();
890 if (IS_INVISIBLE (opacity
)) {
896 if (!IS_TRANSLUCENT (opacity
)) {
897 Brush::Fill (cr
, preserve
);
903 cairo_paint_with_alpha (cr
, opacity
);
911 TileBrush::Stroke (cairo_t
*cr
, bool preserve
)
913 double opacity
= GetOpacity ();
915 if (IS_INVISIBLE (opacity
)) {
921 if (!IS_TRANSLUCENT (opacity
)) {
922 Brush::Stroke (cr
, preserve
);
927 cairo_push_group_with_content (cr
, CAIRO_CONTENT_ALPHA
);
928 cairo_set_source_rgba (cr
, 1.0, 1.0, 1.0, opacity
);
931 cairo_pattern_t
*mask
= cairo_pop_group (cr
);
933 if (cairo_pattern_status (mask
) == CAIRO_STATUS_SUCCESS
) {
934 cairo_mask (cr
, mask
);
936 cairo_pattern_destroy (mask
);
946 VideoBrush::VideoBrush ()
948 SetObjectType (Type::VIDEOBRUSH
);
952 VideoBrush::~VideoBrush ()
955 media
->RemovePropertyChangeListener (this);
956 media
->RemoveHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
962 VideoBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
964 Stretch stretch
= GetStretch ();
965 if (!is_stretch_valid (stretch
)) {
966 // bad enum value for stretch, nothing should be drawn
967 // XXX Removing this _source_set at all?
968 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
972 MediaPlayer
*mplayer
= media
? media
->GetMediaPlayer () : NULL
;
973 Transform
*transform
= GetTransform ();
974 Transform
*relative_transform
= GetRelativeTransform ();
975 AlignmentX ax
= GetAlignmentX ();
976 AlignmentY ay
= GetAlignmentY ();
977 cairo_surface_t
*surface
;
978 cairo_pattern_t
*pattern
;
979 cairo_matrix_t matrix
;
982 DependencyObject
*obj
;
985 name
= GetSourceName ();
987 if (name
== NULL
|| *name
== '\0')
990 if ((obj
= FindName (name
)) && obj
->Is (Type::MEDIAELEMENT
)) {
991 obj
->AddPropertyChangeListener (this);
992 media
= (MediaElement
*) obj
;
993 media
->AddHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
994 mplayer
= media
->GetMediaPlayer ();
996 } else if (obj
== NULL
) {
997 printf ("could not find element `%s'\n", name
);
999 printf ("obj %p is not of type MediaElement (it is %s)\n", obj
,
1000 obj
->GetTypeName ());
1004 if (!mplayer
|| !(surface
= mplayer
->GetCairoSurface ())) {
1005 // not yet available, draw gray-ish shadow where the brush should be applied
1006 cairo_set_source_rgba (cr
, 0.5, 0.5, 0.5, 0.5);
1010 pattern
= cairo_pattern_create_for_surface (surface
);
1011 cairo_filter_t filter
;
1012 switch (media
? media
->GetQualityLevel (0, 3) : 0) {
1013 case 0: filter
= CAIRO_FILTER_FAST
; break;
1014 case 1: filter
= CAIRO_FILTER_GOOD
; break;
1015 case 2: filter
= CAIRO_FILTER_BILINEAR
; break;
1016 default: filter
= CAIRO_FILTER_BEST
; break;
1018 cairo_pattern_set_filter (pattern
, filter
);
1020 image_brush_compute_pattern_matrix (&matrix
, area
.width
, area
.height
, mplayer
->GetVideoWidth (),
1021 mplayer
->GetVideoHeight (), stretch
, ax
, ay
,
1022 transform
, relative_transform
);
1024 cairo_matrix_translate (&matrix
, -area
.x
, -area
.y
);
1025 cairo_pattern_set_matrix (pattern
, &matrix
);
1027 if (cairo_pattern_status (pattern
) == CAIRO_STATUS_SUCCESS
)
1028 cairo_set_source (cr
, pattern
);
1030 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 0.0);
1032 cairo_pattern_destroy (pattern
);
1036 VideoBrush::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
1038 if (args
->GetProperty ()->GetOwnerType() != Type::VIDEOBRUSH
) {
1039 TileBrush::OnPropertyChanged (args
, error
);
1043 if (args
->GetId () == VideoBrush::SourceNameProperty
) {
1044 char *name
= args
->GetNewValue() ? args
->GetNewValue()->AsString () : NULL
;
1045 DependencyObject
*obj
;
1047 if (media
!= NULL
) {
1048 media
->RemovePropertyChangeListener (this);
1049 media
->RemoveHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
1054 if (name
&& (obj
= FindName (name
)) && obj
->Is (Type::MEDIAELEMENT
)) {
1055 obj
->AddPropertyChangeListener (this);
1056 media
= (MediaElement
*) obj
;
1057 media
->AddHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
1060 // Note: This may have failed because the parser hasn't set the
1061 // toplevel element yet, we'll try again in SetupBrush()
1065 NotifyListenersOfPropertyChange (args
, error
);
1069 VideoBrush::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
1071 /* this is being handled in the base class */
1073 if (subobj_args->GetId () == MediaElement::PositionProperty) {
1074 // We to changes in this MediaElement property so we
1075 // can notify whoever is using us to paint that they
1076 // need to redraw themselves.
1077 NotifyListenersOfPropertyChange (Brush::ChangedProperty);
1081 TileBrush::OnSubPropertyChanged (prop
, obj
, subobj_args
);
1085 VideoBrush::SetSource (MediaElement
*source
)
1089 source
->AddHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
1094 if (media
!= NULL
) {
1095 media
->RemovePropertyChangeListener (this);
1096 media
->RemoveHandler (MediaElement::MediaInvalidatedEvent
, update_brush
, this);
1105 VideoBrush::IsOpaque ()
1107 // XXX punt for now and return false here.
1112 VideoBrush::IsAnimating ()
1114 if (media
&& media
->IsPlaying ())
1117 return TileBrush::IsAnimating ();
1121 VideoBrush::update_brush (EventObject
*, EventArgs
*, gpointer closure
)
1123 VideoBrush
*b
= (VideoBrush
*)closure
;
1124 b
->NotifyListenersOfPropertyChange (Brush::ChangedProperty
, NULL
);
1131 VisualBrush::VisualBrush ()
1133 SetObjectType (Type::VISUALBRUSH
);
1136 VisualBrush::~VisualBrush ()
1141 VisualBrush::SetupBrush (cairo_t
*cr
, const Rect
&area
)
1143 UIElement
*ui
= (UIElement
*) GetVisual ();
1145 // not yet available, draw gray-ish shadow where the brush should be applied
1146 cairo_set_source_rgba (cr
, 0.5, 0.5, 0.5, 0.5);
1150 // XXX we should cache the surface so that it can be
1151 // used multiple times without having to re-render each time.
1152 Rect bounds
= ui
->GetSubtreeBounds().RoundOut ();
1154 surface
= image_brush_create_similar (cr
, (int) bounds
.width
, (int) bounds
.height
);
1156 cairo_t
*surface_cr
= cairo_create (surface
);
1157 Region region
= Region (0, 0, bounds
.width
, bounds
.height
);
1158 ui
->Render (surface_cr
, ®ion
);
1159 cairo_destroy (surface_cr
);
1161 Stretch stretch
= GetStretch ();
1163 AlignmentX ax
= GetAlignmentX ();
1164 AlignmentY ay
= GetAlignmentY ();
1166 Transform
*transform
= GetTransform ();
1167 Transform
*relative_transform
= GetRelativeTransform ();
1169 cairo_pattern_t
*pattern
= cairo_pattern_create_for_surface (surface
);
1170 cairo_matrix_t matrix
;
1171 image_brush_compute_pattern_matrix (&matrix
, area
.width
, area
.height
,
1172 (int) bounds
.width
, (int) bounds
.height
,
1173 stretch
, ax
, ay
, transform
, relative_transform
);
1175 cairo_matrix_translate (&matrix
, -area
.x
, -area
.y
);
1176 cairo_pattern_set_matrix (pattern
, &matrix
);
1178 cairo_set_source (cr
, pattern
);
1179 cairo_pattern_destroy (pattern
);
1181 cairo_surface_destroy (surface
);
1185 VisualBrush::update_brush (EventObject
*, EventArgs
*, gpointer closure
)
1187 VisualBrush
*b
= (VisualBrush
*)closure
;
1188 b
->NotifyListenersOfPropertyChange (Brush::ChangedProperty
, NULL
);
1192 VisualBrush::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
1194 if (args
->GetProperty ()->GetOwnerType() != Type::VISUALBRUSH
) {
1195 TileBrush::OnPropertyChanged (args
, error
);
1199 if (args
->GetId () == VisualBrush::VisualProperty
) {
1200 // XXX we really need a way to disconnect from the preview visual
1201 UIElement
*v
= args
->GetNewValue()->AsUIElement();
1202 v
->AddHandler (((UIElement
*)v
)->InvalidatedEvent
, update_brush
, this);
1205 NotifyListenersOfPropertyChange (args
, error
);
1209 VisualBrush::IsOpaque ()
1211 // XXX punt for now and return false here.