2 * Copyright (C) 2006-2008 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
29 #include "swfdec_movie.h"
30 #include "swfdec_as_context.h"
31 #include "swfdec_as_internal.h"
32 #include "swfdec_as_strings.h"
33 #include "swfdec_button_movie.h"
34 #include "swfdec_debug.h"
35 #include "swfdec_draw.h"
36 #include "swfdec_event.h"
37 #include "swfdec_filter.h"
38 #include "swfdec_graphic.h"
39 #include "swfdec_image.h"
40 #include "swfdec_loader_internal.h"
41 #include "swfdec_player_internal.h"
42 #include "swfdec_sprite.h"
43 #include "swfdec_sprite_movie.h"
44 #include "swfdec_renderer_internal.h"
45 #include "swfdec_resource.h"
46 #include "swfdec_sandbox.h"
47 #include "swfdec_system.h"
48 #include "swfdec_text_field_movie.h"
49 #include "swfdec_utils.h"
50 #include "swfdec_video_movie.h"
68 static guint signals
[LAST_SIGNAL
] = { 0, };
70 G_DEFINE_ABSTRACT_TYPE (SwfdecMovie
, swfdec_movie
, SWFDEC_TYPE_AS_RELAY
)
73 swfdec_movie_init (SwfdecMovie
* movie
)
75 movie
->blend_mode
= SWFDEC_BLEND_MODE_NORMAL
;
79 cairo_matrix_init_identity (&movie
->original_transform
);
80 cairo_matrix_init_identity (&movie
->matrix
);
81 cairo_matrix_init_identity (&movie
->inverse_matrix
);
83 swfdec_color_transform_init_identity (&movie
->color_transform
);
85 movie
->visible
= TRUE
;
86 movie
->cache_state
= SWFDEC_MOVIE_INVALID_EXTENTS
;
87 movie
->invalidate_last
= TRUE
;
88 movie
->invalidate_next
= TRUE
;
90 swfdec_rect_init_empty (&movie
->extents
);
94 * swfdec_movie_invalidate:
95 * @movie: a #SwfdecMovie
96 * @parent_to_global: This is the matrix from the parent to the global matrix.
97 * It is only used for caching reasons
98 * @new_contents: %TRUE if this is the invalidation of the new contents, %FALSE
99 * if the old contents are invalidated.
101 * Performs an instant invalidation on @movie. You most likely don't want to
102 * call this function directly, but use swfdec_movie_invalidate_last() or
103 * swfdec_movie_invalidate_next() instead.
106 swfdec_movie_invalidate (SwfdecMovie
*movie
, const cairo_matrix_t
*parent_to_global
,
107 gboolean new_contents
)
109 SwfdecMovieClass
*klass
;
110 cairo_matrix_t matrix
;
113 movie
->invalidate_next
= FALSE
;
115 SwfdecPlayer
*player
;
116 if (movie
->invalidate_last
)
118 movie
->invalidate_last
= TRUE
;
119 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
120 player
->priv
->invalid_pending
= g_slist_prepend (player
->priv
->invalid_pending
, movie
);
122 g_assert (movie
->cache_state
<= SWFDEC_MOVIE_INVALID_CHILDREN
);
123 SWFDEC_LOG ("invalidating %s %s at %s", G_OBJECT_TYPE_NAME (movie
),
124 movie
->name
, new_contents
? "end" : "start");
125 cairo_matrix_multiply (&matrix
, &movie
->matrix
, parent_to_global
);
126 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
127 klass
->invalidate (movie
, &matrix
, new_contents
);
131 * swfdec_movie_invalidate_last:
132 * @movie: a #SwfdecMovie
134 * Ensures the movie's contents are invalidated. This function must be called
135 * before changing the movie or the output will have artifacts.
138 swfdec_movie_invalidate_last (SwfdecMovie
*movie
)
140 cairo_matrix_t matrix
;
142 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
144 if (movie
->invalidate_last
)
148 swfdec_movie_local_to_global_matrix (movie
->parent
, &matrix
);
150 cairo_matrix_init_identity (&matrix
);
151 swfdec_movie_invalidate (movie
, &matrix
, FALSE
);
152 g_assert (movie
->invalidate_last
);
156 * swfdec_movie_invalidate_next:
157 * @movie: a #SwfdecMovie
159 * Ensures the movie will be invalidated after script execution is done. So
160 * after calling this function you can modify position and contents of the
164 swfdec_movie_invalidate_next (SwfdecMovie
*movie
)
166 SwfdecPlayer
*player
;
168 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
170 swfdec_movie_invalidate_last (movie
);
171 movie
->invalidate_next
= TRUE
;
172 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
173 if (movie
== SWFDEC_MOVIE (player
->priv
->focus
))
174 swfdec_player_invalidate_focusrect (player
);
178 * swfdec_movie_queue_update:
179 * @movie: a #SwfdecMovie
180 * @state: how much needs to be updated
182 * Queues an update of all cached values inside @movie and invalidates it.
185 swfdec_movie_queue_update (SwfdecMovie
*movie
, SwfdecMovieCacheState state
)
187 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
189 while (movie
&& movie
->cache_state
< state
) {
190 movie
->cache_state
= state
;
191 movie
= movie
->parent
;
192 state
= SWFDEC_MOVIE_INVALID_CHILDREN
;
197 swfdec_movie_update_extents (SwfdecMovie
*movie
)
199 SwfdecMovieClass
*klass
;
201 SwfdecRect
*rect
= &movie
->original_extents
;
202 SwfdecRect
*extents
= &movie
->extents
;
204 *rect
= movie
->draw_extents
;
206 SwfdecRect image_extents
= { 0, 0,
207 movie
->image
->width
* SWFDEC_TWIPS_SCALE_FACTOR
,
208 movie
->image
->height
* SWFDEC_TWIPS_SCALE_FACTOR
};
209 swfdec_rect_union (rect
, rect
, &image_extents
);
211 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
212 swfdec_rect_union (rect
, rect
, &SWFDEC_MOVIE (walk
->data
)->extents
);
214 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
215 if (klass
->update_extents
)
216 klass
->update_extents (movie
, rect
);
217 if (swfdec_rect_is_empty (rect
)) {
221 swfdec_rect_transform (extents
, rect
, &movie
->matrix
);
222 if (movie
->parent
&& movie
->parent
->cache_state
< SWFDEC_MOVIE_INVALID_EXTENTS
) {
223 /* no need to invalidate here */
224 movie
->parent
->cache_state
= SWFDEC_MOVIE_INVALID_EXTENTS
;
229 swfdec_movie_begin_update_matrix (SwfdecMovie
*movie
)
231 swfdec_movie_invalidate_next (movie
);
235 swfdec_movie_end_update_matrix (SwfdecMovie
*movie
)
239 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
241 /* we operate on x0 and y0 when setting movie._x and movie._y */
242 if (movie
->modified
) {
243 movie
->matrix
.xx
= movie
->original_transform
.xx
;
244 movie
->matrix
.yx
= movie
->original_transform
.yx
;
245 movie
->matrix
.xy
= movie
->original_transform
.xy
;
246 movie
->matrix
.yy
= movie
->original_transform
.yy
;
248 movie
->matrix
= movie
->original_transform
;
251 d
= movie
->xscale
/ swfdec_matrix_get_xscale (&movie
->original_transform
);
252 e
= movie
->yscale
/ swfdec_matrix_get_yscale (&movie
->original_transform
);
253 cairo_matrix_scale (&movie
->matrix
, d
, e
);
254 if (isfinite (movie
->rotation
)) {
255 d
= movie
->rotation
- swfdec_matrix_get_rotation (&movie
->original_transform
);
256 cairo_matrix_rotate (&movie
->matrix
, d
* G_PI
/ 180);
258 swfdec_matrix_ensure_invertible (&movie
->matrix
, &movie
->inverse_matrix
);
260 g_signal_emit (movie
, signals
[MATRIX_CHANGED
], 0);
264 swfdec_movie_do_update (SwfdecMovie
*movie
)
268 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
269 SwfdecMovie
*child
= walk
->data
;
271 if (child
->cache_state
!= SWFDEC_MOVIE_UP_TO_DATE
)
272 swfdec_movie_do_update (child
);
275 switch (movie
->cache_state
) {
276 case SWFDEC_MOVIE_INVALID_EXTENTS
:
277 swfdec_movie_update_extents (movie
);
279 case SWFDEC_MOVIE_INVALID_CHILDREN
:
281 case SWFDEC_MOVIE_UP_TO_DATE
:
283 g_assert_not_reached ();
285 movie
->cache_state
= SWFDEC_MOVIE_UP_TO_DATE
;
289 * swfdec_movie_update:
290 * @movie: a #SwfdecMovie
292 * Brings the cached values of @movie up-to-date if they are not. This includes
293 * transformation matrices and extents. It needs to be called before accessing
294 * the relevant values.
297 swfdec_movie_update (SwfdecMovie
*movie
)
299 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
301 if (movie
->cache_state
== SWFDEC_MOVIE_UP_TO_DATE
)
304 if (movie
->parent
&& movie
->parent
->cache_state
!= SWFDEC_MOVIE_UP_TO_DATE
) {
305 swfdec_movie_update (movie
->parent
);
307 swfdec_movie_do_update (movie
);
312 swfdec_movie_find (SwfdecMovie
*movie
, int depth
)
316 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
318 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
319 SwfdecMovie
*cur
= walk
->data
;
321 if (cur
->depth
< depth
)
323 if (cur
->depth
== depth
)
331 swfdec_movie_unset_actor (SwfdecPlayer
*player
, SwfdecActor
*actor
)
333 SwfdecPlayerPrivate
*priv
= player
->priv
;
335 if (priv
->mouse_below
== actor
)
336 priv
->mouse_below
= NULL
;
337 if (priv
->mouse_grab
== actor
)
338 priv
->mouse_grab
= NULL
;
339 if (priv
->mouse_drag
== actor
)
340 priv
->mouse_drag
= NULL
;
342 if (priv
->focus_previous
== actor
)
343 priv
->focus_previous
= NULL
;
344 if (priv
->focus
== actor
) {
346 swfdec_player_invalidate_focusrect (player
);
351 swfdec_movie_do_remove (SwfdecMovie
*movie
, gboolean destroy
)
353 SwfdecPlayer
*player
;
355 SWFDEC_LOG ("removing %s %s", G_OBJECT_TYPE_NAME (movie
), movie
->name
);
357 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
358 while (movie
->list
) {
359 GList
*walk
= movie
->list
;
360 while (walk
&& SWFDEC_MOVIE (walk
->data
)->state
>= SWFDEC_MOVIE_STATE_REMOVED
)
364 destroy
&= swfdec_movie_do_remove (walk
->data
, destroy
);
366 /* FIXME: all of this here or in destroy callback? */
367 swfdec_movie_invalidate_last (movie
);
368 movie
->state
= SWFDEC_MOVIE_STATE_REMOVED
;
370 if (SWFDEC_IS_ACTOR (movie
)) {
371 SwfdecActor
*actor
= SWFDEC_ACTOR (movie
);
372 swfdec_movie_unset_actor (player
, actor
);
373 if ((actor
->events
&&
374 swfdec_event_list_has_conditions (actor
->events
, SWFDEC_EVENT_UNLOAD
, 0)) ||
375 swfdec_as_object_has_variable (swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie
)), SWFDEC_AS_STR_onUnload
)) {
376 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_UNLOAD
);
381 swfdec_movie_destroy (movie
);
386 * swfdec_movie_remove:
387 * @movie: #SwfdecMovie to remove
389 * Removes this movie from its parent. In contrast to swfdec_movie_destroy (),
390 * it might still be possible to reference it from Actionscript, if the movie
391 * queues onUnload event handlers.
394 swfdec_movie_remove (SwfdecMovie
*movie
)
396 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
398 if (movie
->state
> SWFDEC_MOVIE_STATE_RUNNING
)
400 if (swfdec_movie_do_remove (movie
, TRUE
))
403 swfdec_movie_set_depth (movie
, -32769 - movie
->depth
); /* don't ask me why... */
407 * swfdec_movie_destroy:
408 * @movie: #SwfdecMovie to destroy
410 * Removes this movie from its parent. After this it will no longer be present,
411 * neither visually nor via ActionScript. This function will not cause an
412 * unload event. Compare with swfdec_movie_remove ().
415 swfdec_movie_destroy (SwfdecMovie
*movie
)
417 SwfdecMovieClass
*klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
418 SwfdecPlayer
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
420 g_assert (movie
->state
< SWFDEC_MOVIE_STATE_DESTROYED
);
421 SWFDEC_LOG ("destroying movie %s", movie
->name
);
422 while (movie
->list
) {
423 swfdec_movie_destroy (movie
->list
->data
);
426 movie
->parent
->list
= g_list_remove (movie
->parent
->list
, movie
);
428 player
->priv
->roots
= g_list_remove (player
->priv
->roots
, movie
);
431 if (movie
->masked_by
)
432 movie
->masked_by
->mask_of
= NULL
;
434 movie
->mask_of
->masked_by
= NULL
;
435 movie
->masked_by
= NULL
;
436 movie
->mask_of
= NULL
;
437 /* FIXME: figure out how to handle destruction pre-init/construct.
438 * This is just a stop-gap measure to avoid dead movies in those queues */
439 if (SWFDEC_IS_ACTOR (movie
))
440 swfdec_player_remove_all_actions (player
, SWFDEC_ACTOR (movie
));
441 if (klass
->finish_movie
)
442 klass
->finish_movie (movie
);
443 player
->priv
->actors
= g_list_remove (player
->priv
->actors
, movie
);
444 if (movie
->invalidate_last
)
445 player
->priv
->invalid_pending
= g_slist_remove (player
->priv
->invalid_pending
, movie
);
446 movie
->state
= SWFDEC_MOVIE_STATE_DESTROYED
;
447 /* remove as value, so we can't be scripted anymore */
448 if (movie
->as_value
) {
449 movie
->as_value
->movie
= NULL
;
450 movie
->as_value
= NULL
;
452 g_object_unref (movie
);
456 swfdec_movie_get_version (SwfdecMovie
*movie
)
458 return movie
->resource
->version
;
462 swfdec_movie_local_to_global (SwfdecMovie
*movie
, double *x
, double *y
)
464 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
465 g_return_if_fail (x
!= NULL
);
466 g_return_if_fail (y
!= NULL
);
469 cairo_matrix_transform_point (&movie
->matrix
, x
, y
);
470 } while ((movie
= movie
->parent
));
474 swfdec_movie_rect_local_to_global (SwfdecMovie
*movie
, SwfdecRect
*rect
)
476 cairo_matrix_t matrix
;
478 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
479 g_return_if_fail (rect
!= NULL
);
481 swfdec_movie_local_to_global_matrix (movie
, &matrix
);
482 swfdec_rect_transform (rect
, rect
, &matrix
);
486 swfdec_movie_global_to_local_matrix (SwfdecMovie
*movie
, cairo_matrix_t
*matrix
)
488 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
489 g_return_if_fail (matrix
!= NULL
);
491 cairo_matrix_init_identity (matrix
);
493 cairo_matrix_multiply (matrix
, &movie
->inverse_matrix
, matrix
);
494 movie
= movie
->parent
;
499 swfdec_movie_local_to_global_matrix (SwfdecMovie
*movie
, cairo_matrix_t
*matrix
)
501 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
502 g_return_if_fail (matrix
!= NULL
);
504 cairo_matrix_init_identity (matrix
);
506 cairo_matrix_multiply (matrix
, matrix
, &movie
->matrix
);
507 movie
= movie
->parent
;
512 swfdec_movie_global_to_local (SwfdecMovie
*movie
, double *x
, double *y
)
514 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
515 g_return_if_fail (x
!= NULL
);
516 g_return_if_fail (y
!= NULL
);
519 swfdec_movie_global_to_local (movie
->parent
, x
, y
);
521 cairo_matrix_transform_point (&movie
->inverse_matrix
, x
, y
);
525 swfdec_movie_rect_global_to_local (SwfdecMovie
*movie
, SwfdecRect
*rect
)
527 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
528 g_return_if_fail (rect
!= NULL
);
530 swfdec_movie_global_to_local (movie
, &rect
->x0
, &rect
->y0
);
531 swfdec_movie_global_to_local (movie
, &rect
->x1
, &rect
->y1
);
532 if (rect
->x0
> rect
->x1
) {
533 double tmp
= rect
->x1
;
537 if (rect
->y0
> rect
->y1
) {
538 double tmp
= rect
->y1
;
545 * swfdec_movie_get_mouse:
546 * @movie: a #SwfdecMovie
547 * @x: pointer to hold result of X coordinate
548 * @y: pointer to hold result of y coordinate
550 * Gets the mouse coordinates in the coordinate space of @movie.
553 swfdec_movie_get_mouse (SwfdecMovie
*movie
, double *x
, double *y
)
555 SwfdecPlayer
*player
;
557 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
558 g_return_if_fail (x
!= NULL
);
559 g_return_if_fail (y
!= NULL
);
561 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
562 *x
= player
->priv
->mouse_x
;
563 *y
= player
->priv
->mouse_y
;
564 swfdec_player_stage_to_global (player
, x
, y
);
565 swfdec_movie_global_to_local (movie
, x
, y
);
569 * swfdec_movie_get_movie_at:
570 * @movie: a #SwfdecMovie
571 * @x: x coordinate in parent's coordinate space
572 * @y: y coordinate in the parent's coordinate space
573 * @events: %TRUE to only prefer movies that receive events
575 * Gets the child at the given coordinates. The coordinates are in the
576 * coordinate system of @movie's parent (or the global coordinate system for
577 * root movies). The @events parameter determines if movies that don't receive
578 * events should be respected.
580 * Returns: the child of @movie at the given coordinates or %NULL if none
583 swfdec_movie_get_movie_at (SwfdecMovie
*movie
, double x
, double y
, gboolean events
)
586 SwfdecMovieClass
*klass
;
588 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
590 SWFDEC_LOG ("%s %p getting mouse at: %g %g", G_OBJECT_TYPE_NAME (movie
), movie
, x
, y
);
591 if (movie
->cache_state
>= SWFDEC_MOVIE_INVALID_EXTENTS
)
592 swfdec_movie_update (movie
);
593 if (!swfdec_rect_contains (&movie
->extents
, x
, y
)) {
596 cairo_matrix_transform_point (&movie
->inverse_matrix
, &x
, &y
);
598 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
599 g_return_val_if_fail (klass
->contains
, NULL
);
600 ret
= klass
->contains (movie
, x
, y
, events
);
606 swfdec_movie_do_contains (SwfdecMovie
*movie
, double x
, double y
, gboolean events
)
610 SwfdecMovie
*ret
, *got
;
613 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
614 SwfdecMovie
*child
= walk
->data
;
616 if (!child
->visible
) {
617 SWFDEC_LOG ("%s %s (depth %d) is invisible, ignoring", G_OBJECT_TYPE_NAME (movie
), movie
->name
, movie
->depth
);
620 got
= swfdec_movie_get_movie_at (child
, x
, y
, events
);
623 /* set the return value to the topmost movie */
624 if (SWFDEC_IS_ACTOR (got
) && swfdec_actor_get_mouse_events (SWFDEC_ACTOR (got
))) {
626 } else if (ret
== NULL
) {
630 /* if thie is not a clipped movie, we've found something */
631 if (child
->clip_depth
== 0)
635 if (child
->clip_depth
) {
636 /* skip obscured movies */
637 SwfdecMovie
*tmp
= walk
->next
? walk
->next
->data
: NULL
;
638 while (tmp
&& tmp
->depth
<= child
->clip_depth
) {
640 tmp
= walk
->next
? walk
->next
->data
: NULL
;
648 for (walk2
= movie
->draws
; walk2
; walk2
= walk2
->next
) {
649 SwfdecDraw
*draw
= walk2
->data
;
651 if (swfdec_draw_contains (draw
, x
, y
))
658 /* NB: order is important */
660 SWFDEC_GROUP_NONE
= 0,
667 swfdec_movie_needs_group (SwfdecMovie
*movie
)
669 /* yes, masked movies don't get filters applied */
670 if (movie
->filters
&& movie
->masked_by
== NULL
)
671 return SWFDEC_GROUP_FILTERS
;
672 if (movie
->cache_as_bitmap
)
673 return SWFDEC_GROUP_CACHED
;
674 if (movie
->blend_mode
> 1)
675 return SWFDEC_GROUP_NORMAL
;
676 return SWFDEC_GROUP_NONE
;
679 static cairo_operator_t
680 swfdec_movie_get_operator_for_blend_mode (guint blend_mode
)
682 switch (blend_mode
) {
683 case SWFDEC_BLEND_MODE_NONE
:
684 case SWFDEC_BLEND_MODE_NORMAL
:
685 case SWFDEC_BLEND_MODE_LAYER
:
686 return CAIRO_OPERATOR_OVER
;
687 case SWFDEC_BLEND_MODE_ADD
:
688 return CAIRO_OPERATOR_ADD
;
689 case SWFDEC_BLEND_MODE_ALPHA
:
690 return CAIRO_OPERATOR_DEST_IN
;
691 case SWFDEC_BLEND_MODE_ERASE
:
692 return CAIRO_OPERATOR_DEST_OUT
;
693 case SWFDEC_BLEND_MODE_MULTIPLY
:
694 case SWFDEC_BLEND_MODE_SCREEN
:
695 case SWFDEC_BLEND_MODE_LIGHTEN
:
696 case SWFDEC_BLEND_MODE_DARKEN
:
697 case SWFDEC_BLEND_MODE_DIFFERENCE
:
698 case SWFDEC_BLEND_MODE_SUBTRACT
:
699 case SWFDEC_BLEND_MODE_INVERT
:
700 case SWFDEC_BLEND_MODE_OVERLAY
:
701 case SWFDEC_BLEND_MODE_HARDLIGHT
:
702 SWFDEC_FIXME ("blend mode %u unimplemented in cairo", blend_mode
);
703 return CAIRO_OPERATOR_OVER
;
705 SWFDEC_WARNING ("invalid blend mode %u", blend_mode
);
706 return CAIRO_OPERATOR_OVER
;
710 static cairo_pattern_t
*
711 swfdec_movie_apply_filters (SwfdecMovie
*movie
, cairo_pattern_t
*pattern
)
713 SwfdecRectangle area
;
714 SwfdecPlayer
*player
;
717 double xscale
, yscale
;
719 if (movie
->filters
== NULL
)
722 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
723 rect
= movie
->original_extents
;
724 swfdec_movie_rect_local_to_global (movie
, &rect
);
725 swfdec_rect_transform (&rect
, &rect
,
726 &player
->priv
->global_to_stage
);
727 swfdec_rectangle_init_rect (&area
, &rect
);
728 /* FIXME: hack to make textfield borders work - looks like Adobe does this, too */
731 xscale
= player
->priv
->global_to_stage
.xx
* SWFDEC_TWIPS_SCALE_FACTOR
;
732 yscale
= player
->priv
->global_to_stage
.yy
* SWFDEC_TWIPS_SCALE_FACTOR
;
733 for (walk
= movie
->filters
; walk
; walk
= walk
->next
) {
734 pattern
= swfdec_filter_apply (walk
->data
, pattern
, xscale
, yscale
, &area
);
735 swfdec_filter_get_rectangle (walk
->data
, &area
, xscale
, yscale
, &area
);
742 * @movie: The movie to act as the mask
743 * @cr: a cairo context which should be used for masking. The cairo context's
744 * matrix is assumed to be in the coordinate system of the movie's parent.
745 * @matrix: matrix to apply before rendering
747 * Creates a pattern suitable for masking. To do rendering using the returned
748 * mask, you want to use code like this:
749 * <informalexample><programlisting>
750 * mask = swfdec_movie_mask (cr, movie, matrix);
751 * cairo_push_group (cr);
752 * // do rendering here
753 * cairo_pop_group_to_source (cr);
754 * cairo_mask (cr, mask);
755 * cairo_pattern_destroy (mask);
756 * </programlisting></informalexample>
758 * Returns: A new cairo_patten_t to be used as the mask.
760 static cairo_pattern_t
*
761 swfdec_movie_mask (cairo_t
*cr
, SwfdecMovie
*movie
,
762 const cairo_matrix_t
*matrix
)
764 SwfdecColorTransform black
;
766 swfdec_color_transform_init_mask (&black
);
767 cairo_push_group_with_content (cr
, CAIRO_CONTENT_ALPHA
);
768 cairo_transform (cr
, matrix
);
770 swfdec_movie_render (movie
, cr
, &black
);
771 return cairo_pop_group (cr
);
775 swfdec_movie_render (SwfdecMovie
*movie
, cairo_t
*cr
,
776 const SwfdecColorTransform
*color_transform
)
778 SwfdecMovieClass
*klass
;
779 SwfdecColorTransform trans
;
783 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
784 g_return_if_fail (cr
!= NULL
);
785 if (cairo_status (cr
) != CAIRO_STATUS_SUCCESS
) {
786 g_warning ("%s", cairo_status_to_string (cairo_status (cr
)));
788 g_return_if_fail (color_transform
!= NULL
);
790 if (movie
->mask_of
!= NULL
&& !swfdec_color_transform_is_mask (color_transform
)) {
791 SWFDEC_LOG ("not rendering %s %p, movie is a mask",
792 G_OBJECT_TYPE_NAME (movie
), movie
->name
);
796 group
= swfdec_movie_needs_group (movie
);
797 if (group
== SWFDEC_GROUP_NORMAL
) {
798 SWFDEC_DEBUG ("pushing group for blend mode %u", movie
->blend_mode
);
799 cairo_push_group (cr
);
800 } else if (group
!= SWFDEC_GROUP_NONE
) {
801 SWFDEC_FIXME ("implement cache-as-bitmap here");
802 cairo_push_group (cr
);
804 /* yes, movie with filters, don't get masked */
805 needs_mask
= movie
->masked_by
!= NULL
&& movie
->filters
== NULL
;
807 cairo_push_group (cr
);
810 /* do extra save/restore so the render vfunc can mess with cr */
813 SWFDEC_LOG ("transforming movie, transform: %g %g %g %g %g %g",
814 movie
->matrix
.xx
, movie
->matrix
.yy
,
815 movie
->matrix
.xy
, movie
->matrix
.yx
,
816 movie
->matrix
.x0
, movie
->matrix
.y0
);
817 cairo_transform (cr
, &movie
->matrix
);
818 swfdec_color_transform_chain (&trans
, &movie
->color_transform
, color_transform
);
820 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
821 g_return_if_fail (klass
->render
);
822 klass
->render (movie
, cr
, &trans
);
824 /* code to draw a red rectangle around the area occupied by this movie clip */
827 cairo_transform (cr
, &movie
->inverse_matrix
);
828 cairo_rectangle (cr
, movie
->extents
.x0
, movie
->extents
.y0
,
829 movie
->extents
.x1
- movie
->extents
.x0
,
830 movie
->extents
.y1
- movie
->extents
.y0
);
831 swfdec_renderer_reset_matrix (cr
);
832 cairo_set_source_rgb (cr
, 0.0, 0.0, 1.0);
833 cairo_set_line_width (cr
, 2.0);
838 if (cairo_status (cr
) != CAIRO_STATUS_SUCCESS
) {
839 g_warning ("error rendering with cairo: %s", cairo_status_to_string (cairo_status (cr
)));
844 cairo_pattern_t
*mask
;
847 swfdec_movie_global_to_local_matrix (movie
->parent
, &mat
);
849 cairo_matrix_init_identity (&mat
);
850 if (movie
->masked_by
->parent
) {
852 swfdec_movie_local_to_global_matrix (movie
->masked_by
->parent
, &mat2
);
853 cairo_matrix_multiply (&mat
, &mat
, &mat2
);
855 mask
= swfdec_movie_mask (cr
, movie
->masked_by
, &mat
);
856 cairo_pop_group_to_source (cr
);
857 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
858 cairo_mask (cr
, mask
);
859 cairo_pattern_destroy (mask
);
861 if (group
== SWFDEC_GROUP_FILTERS
) {
862 cairo_pattern_t
*pattern
;
864 pattern
= cairo_pop_group (cr
);
866 swfdec_renderer_reset_matrix (cr
);
869 cairo_get_matrix (cr
, &mat
);
870 cairo_matrix_invert (&mat
);
871 cairo_pattern_set_matrix (pattern
, &mat
);
873 pattern
= swfdec_movie_apply_filters (movie
, pattern
);
874 cairo_set_source (cr
, pattern
);
875 cairo_set_operator (cr
, swfdec_movie_get_operator_for_blend_mode (movie
->blend_mode
));
877 cairo_pattern_destroy (pattern
);
879 } else if (group
!= SWFDEC_GROUP_NONE
) {
880 cairo_pop_group_to_source (cr
);
881 cairo_set_operator (cr
, swfdec_movie_get_operator_for_blend_mode (movie
->blend_mode
));
887 swfdec_movie_get_property (GObject
*object
, guint param_id
, GValue
*value
,
890 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
894 g_value_set_int (value
, movie
->depth
);
897 g_value_set_object (value
, movie
->parent
);
900 g_value_set_object (value
, movie
->resource
);
903 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
909 swfdec_movie_set_property (GObject
*object
, guint param_id
, const GValue
*value
,
912 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
913 SwfdecAsContext
*cx
= swfdec_gc_object_get_context (movie
);
915 /* The context must be set before all movie-related properties */
920 /* parent must be set after depth */
921 g_assert (movie
->parent
== NULL
);
922 movie
->depth
= g_value_get_int (value
);
925 movie
->graphic
= g_value_get_object (value
);
927 g_object_ref (movie
->graphic
);
930 movie
->name
= g_value_get_string (value
);
932 movie
->name
= swfdec_as_context_get_string (cx
, movie
->name
);
934 movie
->name
= SWFDEC_AS_STR_EMPTY
;
938 movie
->parent
= g_value_get_object (value
);
939 /* parent holds a reference */
940 g_object_ref (movie
);
942 movie
->parent
->list
= g_list_insert_sorted (movie
->parent
->list
, movie
, swfdec_movie_compare_depths
);
943 SWFDEC_DEBUG ("inserting %s %p into %s %p", G_OBJECT_TYPE_NAME (movie
), movie
,
944 G_OBJECT_TYPE_NAME (movie
->parent
), movie
->parent
);
945 /* invalidate the parent, so it gets visible */
946 swfdec_movie_queue_update (movie
->parent
, SWFDEC_MOVIE_INVALID_CHILDREN
);
948 SwfdecPlayerPrivate
*priv
= SWFDEC_PLAYER (cx
)->priv
;
949 priv
->roots
= g_list_insert_sorted (priv
->roots
, movie
, swfdec_movie_compare_depths
);
953 movie
->resource
= g_value_get_object (value
);
956 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
962 swfdec_movie_dispose (GObject
*object
)
964 SwfdecMovie
* movie
= SWFDEC_MOVIE (object
);
967 g_assert (movie
->list
== NULL
);
969 SWFDEC_LOG ("disposing movie %s (depth %d)", movie
->name
, movie
->depth
);
970 if (movie
->graphic
) {
971 g_object_unref (movie
->graphic
);
972 movie
->graphic
= NULL
;
974 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
977 g_slist_free (movie
->variable_listeners
);
978 movie
->variable_listeners
= NULL
;
981 g_object_unref (movie
->image
);
984 g_slist_foreach (movie
->draws
, (GFunc
) g_object_unref
, NULL
);
985 g_slist_free (movie
->draws
);
988 G_OBJECT_CLASS (swfdec_movie_parent_class
)->dispose (G_OBJECT (movie
));
992 swfdec_movie_mark (SwfdecGcObject
*object
)
994 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
998 swfdec_gc_object_mark (movie
->parent
);
1000 swfdec_as_movie_value_mark (movie
->as_value
);
1001 swfdec_as_string_mark (movie
->name
);
1002 g_list_foreach (movie
->list
, (GFunc
) swfdec_gc_object_mark
, NULL
);
1003 g_slist_foreach (movie
->filters
, (GFunc
) swfdec_gc_object_mark
, NULL
);
1005 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1006 SwfdecMovieVariableListener
*listener
= iter
->data
;
1007 swfdec_as_string_mark (listener
->name
);
1009 swfdec_gc_object_mark (movie
->resource
);
1011 SWFDEC_GC_OBJECT_CLASS (swfdec_movie_parent_class
)->mark (object
);
1015 * swfdec_movie_is_scriptable:
1018 * Checks if the movie may be accessed by scripts. If not, the movie is not
1019 * accessible by Actionscript and functions that would return the movie should
1020 * instead return its parent.
1022 * Returns: %TRUE if scripts may access this movie, %FALSE if the parent
1026 swfdec_movie_is_scriptable (SwfdecMovie
*movie
)
1028 /* FIXME: It would be much easier if we'd just check that there's no as_value
1029 * for non-scriptable movies */
1030 return (SWFDEC_IS_ACTOR (movie
) || SWFDEC_IS_VIDEO_MOVIE (movie
)) &&
1031 (swfdec_movie_get_version (movie
) > 5 || !SWFDEC_IS_TEXT_FIELD_MOVIE (movie
));
1034 /* FIXME: This function can definitely be implemented easier */
1036 swfdec_movie_get_by_name (SwfdecMovie
*movie
, const char *name
, gboolean unnamed
)
1040 guint version
= swfdec_gc_object_get_context (movie
)->version
;
1041 SwfdecPlayer
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
1043 if (name
[0] == '\0')
1046 i
= swfdec_player_get_level (player
, name
, version
);
1048 return SWFDEC_MOVIE (swfdec_player_get_movie_at_level (player
, i
));
1050 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
1051 SwfdecMovie
*cur
= walk
->data
;
1052 if (swfdec_strcmp (version
, cur
->name
, name
) == 0) {
1053 if (swfdec_movie_is_scriptable (cur
))
1058 if (unnamed
&& cur
->name
== SWFDEC_AS_STR_EMPTY
&& cur
->as_value
) {
1059 if (swfdec_strcmp (version
, cur
->as_value
->names
[cur
->as_value
->n_names
- 1], name
) == 0) {
1060 /* unnamed movies are always scriptable */
1069 swfdec_movie_get_root (SwfdecMovie
*movie
)
1071 SwfdecMovie
*real_root
;
1073 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1076 while (real_root
->parent
)
1077 real_root
= real_root
->parent
;
1079 while (movie
->parent
&& !(movie
->lockroot
&&
1080 (swfdec_movie_get_version (movie
) != 6 ||
1081 swfdec_movie_get_version (real_root
) != 6))) {
1082 movie
= movie
->parent
;
1089 swfdec_movie_add_variable_listener (SwfdecMovie
*movie
, gpointer data
,
1090 const char *name
, const SwfdecMovieVariableListenerFunction function
)
1092 SwfdecMovieVariableListener
*listener
;
1095 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1096 listener
= iter
->data
;
1098 if (listener
->data
== data
&& listener
->name
== name
&&
1099 listener
->function
== function
)
1103 listener
= g_new0 (SwfdecMovieVariableListener
, 1);
1104 listener
->data
= data
;
1105 listener
->name
= name
;
1106 listener
->function
= function
;
1108 movie
->variable_listeners
= g_slist_prepend (movie
->variable_listeners
,
1113 swfdec_movie_remove_variable_listener (SwfdecMovie
*movie
,
1114 gpointer data
, const char *name
,
1115 const SwfdecMovieVariableListenerFunction function
)
1119 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1120 SwfdecMovieVariableListener
*listener
= iter
->data
;
1122 if (listener
->data
== data
&& listener
->name
== name
&&
1123 listener
->function
== function
)
1129 g_free (iter
->data
);
1130 movie
->variable_listeners
=
1131 g_slist_remove (movie
->variable_listeners
, iter
->data
);
1135 swfdec_movie_call_variable_listeners (SwfdecMovie
*movie
, const char *name
,
1136 const SwfdecAsValue
*val
)
1140 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1141 SwfdecMovieVariableListener
*listener
= iter
->data
;
1143 if (listener
->name
!= name
&&
1144 (swfdec_gc_object_get_context (movie
)->version
>= 7 ||
1145 !swfdec_str_case_equal (listener
->name
, name
)))
1148 listener
->function (listener
->data
, name
, val
);
1153 SwfdecMovie
* movie
;
1158 swfdec_movie_do_render (SwfdecMovie
*movie
, cairo_t
*cr
,
1159 const SwfdecColorTransform
*ctrans
)
1161 static const cairo_matrix_t ident
= { 1, 0, 0, 1, 0, 0};
1164 GSList
*clips
= NULL
;
1165 ClipEntry
*clip
= NULL
;
1167 if (movie
->draws
|| movie
->image
) {
1170 cairo_clip_extents (cr
, &inval
.x0
, &inval
.y0
, &inval
.x1
, &inval
.y1
);
1172 /* exeute the movie's drawing commands */
1173 for (walk
= movie
->draws
; walk
; walk
= walk
->next
) {
1174 SwfdecDraw
*draw
= walk
->data
;
1176 if (!swfdec_rect_intersect (NULL
, &draw
->extents
, &inval
))
1179 swfdec_draw_paint (draw
, cr
, ctrans
);
1182 /* if the movie loaded an image, draw it here now */
1183 /* FIXME: add check to only draw if inside clip extents */
1185 SwfdecRenderer
*renderer
= swfdec_renderer_get (cr
);
1186 cairo_surface_t
*surface
;
1187 cairo_pattern_t
*pattern
;
1190 if (swfdec_color_transform_is_mask (ctrans
)) {
1192 } else if (swfdec_color_transform_is_alpha (ctrans
)) {
1193 surface
= swfdec_image_create_surface (movie
->image
, renderer
);
1194 alpha
= ctrans
->aa
/ 256.0;
1196 surface
= swfdec_image_create_surface_transformed (movie
->image
,
1200 static const cairo_matrix_t matrix
= { 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
, 0, 0, 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
, 0, 0 };
1201 pattern
= cairo_pattern_create_for_surface (surface
);
1202 SWFDEC_LOG ("rendering loaded image");
1203 cairo_pattern_set_matrix (pattern
, &matrix
);
1205 pattern
= cairo_pattern_create_rgb (1.0, 0.0, 0.0);
1207 cairo_set_source (cr
, pattern
);
1208 cairo_paint_with_alpha (cr
, alpha
);
1209 cairo_pattern_destroy (pattern
);
1210 cairo_surface_destroy (surface
);
1214 /* draw the children movies */
1215 for (g
= movie
->list
; g
; g
= g_list_next (g
)) {
1216 SwfdecMovie
*child
= g
->data
;
1218 while (clip
&& clip
->depth
< child
->depth
) {
1219 cairo_pattern_t
*mask
;
1220 SWFDEC_INFO ("unsetting clip depth %d for depth %d", clip
->depth
, child
->depth
);
1221 mask
= swfdec_movie_mask (cr
, clip
->movie
, &ident
);
1222 cairo_pop_group_to_source (cr
);
1223 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
1224 cairo_mask (cr
, mask
);
1225 cairo_pattern_destroy (mask
);
1226 g_slice_free (ClipEntry
, clip
);
1227 clips
= g_slist_delete_link (clips
, clips
);
1228 clip
= clips
? clips
->data
: NULL
;
1231 if (child
->clip_depth
) {
1232 clip
= g_slice_new (ClipEntry
);
1233 clips
= g_slist_prepend (clips
, clip
);
1234 clip
->movie
= child
;
1235 clip
->depth
= child
->clip_depth
;
1236 SWFDEC_INFO ("clipping up to depth %d by using %s with depth %d", child
->clip_depth
,
1237 child
->name
, child
->depth
);
1238 cairo_push_group (cr
);
1242 SWFDEC_LOG ("rendering %p with depth %d", child
, child
->depth
);
1244 swfdec_movie_render (child
, cr
, ctrans
);
1247 cairo_pattern_t
*mask
;
1248 SWFDEC_INFO ("unsetting clip depth %d", clip
->depth
);
1249 mask
= swfdec_movie_mask (cr
, clip
->movie
, &ident
);
1250 cairo_pop_group_to_source (cr
);
1251 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
1252 cairo_mask (cr
, mask
);
1253 cairo_pattern_destroy (mask
);
1254 g_slice_free (ClipEntry
, clip
);
1255 clips
= g_slist_delete_link (clips
, clips
);
1256 clip
= clips
? clips
->data
: NULL
;
1258 g_assert (clips
== NULL
);
1262 swfdec_movie_do_invalidate (SwfdecMovie
*movie
, const cairo_matrix_t
*matrix
, gboolean last
)
1268 rect
.x0
= rect
.y0
= 0;
1269 rect
.x1
= movie
->image
->width
* SWFDEC_TWIPS_SCALE_FACTOR
;
1270 rect
.y1
= movie
->image
->height
* SWFDEC_TWIPS_SCALE_FACTOR
;
1272 swfdec_rect_init_empty (&rect
);
1274 swfdec_rect_union (&rect
, &rect
, &movie
->draw_extents
);
1275 swfdec_rect_transform (&rect
, &rect
, matrix
);
1276 swfdec_player_invalidate (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)),
1279 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
1280 swfdec_movie_invalidate (walk
->data
, matrix
, last
);
1285 swfdec_movie_constructor (GType type
, guint n_construct_properties
,
1286 GObjectConstructParam
*construct_properties
)
1288 SwfdecPlayerPrivate
*priv
;
1289 SwfdecAsContext
*cx
;
1295 object
= G_OBJECT_CLASS (swfdec_movie_parent_class
)->constructor (type
,
1296 n_construct_properties
, construct_properties
);
1297 movie
= SWFDEC_MOVIE (object
);
1299 cx
= swfdec_gc_object_get_context (movie
);
1300 priv
= SWFDEC_PLAYER (cx
)->priv
;
1301 /* the movie is created invalid */
1302 priv
->invalid_pending
= g_slist_prepend (priv
->invalid_pending
, object
);
1304 if (movie
->name
== SWFDEC_AS_STR_EMPTY
&&
1305 (swfdec_movie_is_scriptable (movie
) || SWFDEC_IS_ACTOR (movie
))) {
1306 name
= swfdec_as_context_give_string (cx
,
1307 g_strdup_printf ("instance%u", ++priv
->unnamed_count
));
1311 if (name
!= SWFDEC_AS_STR_EMPTY
)
1312 movie
->as_value
= swfdec_as_movie_value_new (movie
, name
);
1314 /* make the resource ours if it doesn't belong to anyone yet */
1315 if (SWFDEC_AS_VALUE_IS_UNDEFINED (movie
->resource
->movie
)) {
1316 g_assert (SWFDEC_IS_SPRITE_MOVIE (movie
));
1317 SWFDEC_AS_VALUE_SET_MOVIE (&movie
->resource
->movie
, movie
);
1320 /* create AsObject */
1321 o
= swfdec_as_object_new_empty (cx
);
1323 swfdec_as_object_set_relay (o
, SWFDEC_AS_RELAY (movie
));
1325 /* set $version variable */
1326 if (movie
->parent
== NULL
) {
1328 SWFDEC_AS_VALUE_SET_STRING (&val
, swfdec_as_context_get_string (cx
, priv
->system
->version
));
1329 swfdec_as_object_set_variable (o
, SWFDEC_AS_STR__version
, &val
);
1335 swfdec_movie_class_init (SwfdecMovieClass
* movie_class
)
1337 GObjectClass
*object_class
= G_OBJECT_CLASS (movie_class
);
1338 SwfdecGcObjectClass
*gc_class
= SWFDEC_GC_OBJECT_CLASS (movie_class
);
1340 object_class
->constructor
= swfdec_movie_constructor
;
1341 object_class
->dispose
= swfdec_movie_dispose
;
1342 object_class
->get_property
= swfdec_movie_get_property
;
1343 object_class
->set_property
= swfdec_movie_set_property
;
1345 gc_class
->mark
= swfdec_movie_mark
;
1347 signals
[MATRIX_CHANGED
] = g_signal_new ("matrix-changed", G_TYPE_FROM_CLASS (movie_class
),
1348 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, g_cclosure_marshal_VOID__VOID
,
1351 g_object_class_install_property (object_class
, PROP_DEPTH
,
1352 g_param_spec_int ("depth", "depth", "z order inside the parent",
1353 G_MININT
, G_MAXINT
, 0, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1354 g_object_class_install_property (object_class
, PROP_GRAPHIC
,
1355 g_param_spec_object ("graphic", "graphic", "graphic represented by this movie",
1356 SWFDEC_TYPE_GRAPHIC
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1357 g_object_class_install_property (object_class
, PROP_NAME
,
1358 g_param_spec_string ("name", "name", "the name given to this movie (can be empty)",
1359 NULL
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1360 g_object_class_install_property (object_class
, PROP_PARENT
,
1361 g_param_spec_object ("parent", "parent", "parent movie containing this movie",
1362 SWFDEC_TYPE_MOVIE
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1363 g_object_class_install_property (object_class
, PROP_RESOURCE
,
1364 g_param_spec_object ("resource", "resource", "the resource that spawned this movie",
1365 SWFDEC_TYPE_RESOURCE
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1367 movie_class
->render
= swfdec_movie_do_render
;
1368 movie_class
->invalidate
= swfdec_movie_do_invalidate
;
1369 movie_class
->contains
= swfdec_movie_do_contains
;
1370 movie_class
->property_get
= swfdec_movie_property_do_get
;
1371 movie_class
->property_set
= swfdec_movie_property_do_set
;
1375 swfdec_movie_initialize (SwfdecMovie
*movie
)
1377 SwfdecMovieClass
*klass
;
1379 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1381 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1382 if (klass
->init_movie
)
1383 klass
->init_movie (movie
);
1387 swfdec_movie_set_depth (SwfdecMovie
*movie
, int depth
)
1389 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1391 if (movie
->depth
== depth
)
1394 swfdec_movie_invalidate_last (movie
);
1395 movie
->depth
= depth
;
1396 if (movie
->parent
) {
1397 movie
->parent
->list
= g_list_sort (movie
->parent
->list
, swfdec_movie_compare_depths
);
1399 SwfdecPlayerPrivate
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
))->priv
;
1400 player
->roots
= g_list_sort (player
->roots
, swfdec_movie_compare_depths
);
1402 g_object_notify (G_OBJECT (movie
), "depth");
1407 * @player: a #SwfdecPlayer
1408 * @depth: depth of movie
1409 * @parent: the parent movie or %NULL to make this a root movie
1410 * @resource: the resource that is responsible for this movie
1411 * @graphic: the graphic that is displayed by this movie or %NULL to create an
1413 * @name: a garbage-collected string to be used as the name for this movie or
1414 * %NULL for a default one.
1416 * Creates a new movie #SwfdecMovie for the given properties. No movie may exist
1417 * at the given @depth. The actual type of
1418 * this movie depends on the @graphic parameter. The movie will be initialized
1419 * with default properties. No script execution will be scheduled. After all
1420 * properties are set, the new-movie signal will be emitted if @player is a
1423 * Returns: a new #SwfdecMovie
1426 swfdec_movie_new (SwfdecPlayer
*player
, int depth
, SwfdecMovie
*parent
, SwfdecResource
*resource
,
1427 SwfdecGraphic
*graphic
, const char *name
)
1432 g_return_val_if_fail (SWFDEC_IS_PLAYER (player
), NULL
);
1433 g_return_val_if_fail (parent
== NULL
|| SWFDEC_IS_MOVIE (parent
), NULL
);
1434 g_return_val_if_fail (SWFDEC_IS_RESOURCE (resource
), NULL
);
1435 g_return_val_if_fail (graphic
== NULL
|| SWFDEC_IS_GRAPHIC (graphic
), NULL
);
1437 /* create the right movie */
1438 if (graphic
== NULL
) {
1439 type
= SWFDEC_TYPE_SPRITE_MOVIE
;
1441 SwfdecGraphicClass
*klass
= SWFDEC_GRAPHIC_GET_CLASS (graphic
);
1442 g_return_val_if_fail (g_type_is_a (klass
->movie_type
, SWFDEC_TYPE_MOVIE
), NULL
);
1443 type
= klass
->movie_type
;
1445 movie
= g_object_new (type
, "context", player
, "depth", depth
,
1446 "parent", parent
, "name", name
, "resource", resource
,
1447 "graphic", graphic
, NULL
);
1452 /* FIXME: since this is only used in PlaceObject, wouldn't it be easier to just have
1453 * swfdec_movie_update_static_properties (movie); that's notified when any of these change
1454 * and let PlaceObject modify the movie directly?
1457 swfdec_movie_set_static_properties (SwfdecMovie
*movie
, const cairo_matrix_t
*transform
,
1458 const SwfdecColorTransform
*ctrans
, int ratio
, int clip_depth
, guint blend_mode
,
1459 SwfdecEventList
*events
)
1461 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1462 g_return_if_fail (clip_depth
>= -16384 || clip_depth
<= 0);
1463 g_return_if_fail (ratio
>= -1);
1465 if (movie
->modified
) {
1466 SWFDEC_LOG ("%s has already been modified by scripts, ignoring updates", movie
->name
);
1470 swfdec_movie_begin_update_matrix (movie
);
1471 movie
->original_transform
= *transform
;
1472 movie
->matrix
.x0
= movie
->original_transform
.x0
;
1473 movie
->matrix
.y0
= movie
->original_transform
.y0
;
1474 movie
->xscale
= swfdec_matrix_get_xscale (&movie
->original_transform
);
1475 movie
->yscale
= swfdec_matrix_get_yscale (&movie
->original_transform
);
1476 movie
->rotation
= swfdec_matrix_get_rotation (&movie
->original_transform
);
1477 swfdec_movie_end_update_matrix (movie
);
1480 swfdec_movie_invalidate_last (movie
);
1481 movie
->color_transform
= *ctrans
;
1483 if (ratio
>= 0 && (guint
) ratio
!= movie
->original_ratio
) {
1484 SwfdecMovieClass
*klass
;
1485 movie
->original_ratio
= ratio
;
1486 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1487 if (klass
->set_ratio
)
1488 klass
->set_ratio (movie
);
1490 if (clip_depth
&& clip_depth
!= movie
->clip_depth
) {
1491 movie
->clip_depth
= clip_depth
;
1492 /* FIXME: is this correct? */
1493 swfdec_movie_invalidate_last (movie
->parent
? movie
->parent
: movie
);
1495 if (blend_mode
!= movie
->blend_mode
) {
1496 movie
->blend_mode
= blend_mode
;
1497 swfdec_movie_invalidate_last (movie
);
1500 if (SWFDEC_IS_SPRITE_MOVIE (movie
)) {
1501 SwfdecActor
*actor
= SWFDEC_ACTOR (movie
);
1503 swfdec_event_list_free (actor
->events
);
1504 actor
->events
= swfdec_event_list_copy (events
);
1506 SWFDEC_WARNING ("trying to set events on a %s, not allowed", G_OBJECT_TYPE_NAME (movie
));
1512 * swfdec_movie_duplicate:
1513 * @movie: #SwfdecMovie to copy
1514 * @name: garbage-collected name for the new copy
1515 * @depth: depth to put this movie in
1517 * Creates a duplicate of @movie. The duplicate will not be initialized or
1518 * queued up for any events. You have to do this manually. In particular calling
1519 * swfdec_movie_initialize() on the returned movie must be done.
1520 * This function must be called from within a script.
1522 * Returns: a newly created movie or %NULL on error
1525 swfdec_movie_duplicate (SwfdecMovie
*movie
, const char *name
, int depth
)
1527 SwfdecMovie
*parent
, *copy
;
1528 SwfdecSandbox
*sandbox
;
1531 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1532 g_return_val_if_fail (name
!= NULL
, NULL
);
1534 parent
= movie
->parent
;
1535 if (movie
->parent
== NULL
) {
1536 SWFDEC_FIXME ("don't know how to duplicate root movies");
1539 copy
= swfdec_movie_find (movie
->parent
, depth
);
1541 SWFDEC_LOG ("depth %d already occupied while duplicating, removing old movie", depth
);
1542 swfdec_movie_remove (copy
);
1544 copy
= swfdec_movie_new (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)), depth
,
1545 parent
, movie
->resource
, movie
->graphic
, name
);
1546 /* copy properties */
1547 swfdec_movie_set_static_properties (copy
, &movie
->original_transform
,
1548 &movie
->color_transform
, movie
->original_ratio
, movie
->clip_depth
,
1549 movie
->blend_mode
, SWFDEC_IS_ACTOR (movie
) ? SWFDEC_ACTOR (movie
)->events
: NULL
);
1550 /* Copy drawing state.
1551 * We can keep refs to all finalized draw objects, but need to create copies
1552 * of the still active ones as their path can still change */
1553 copy
->draws
= g_slist_copy (movie
->draws
);
1554 g_slist_foreach (copy
->draws
, (GFunc
) g_object_ref
, NULL
);
1555 copy
->draw_extents
= movie
->draw_extents
;
1556 for (walk
= copy
->draws
; walk
; walk
= walk
->next
) {
1557 if (walk
->data
== movie
->draw_line
) {
1558 copy
->draw_line
= swfdec_draw_copy (walk
->data
);
1559 g_object_unref (walk
->data
);
1560 walk
->data
= copy
->draw_line
;
1561 } else if (walk
->data
== movie
->draw_fill
) {
1562 copy
->draw_fill
= swfdec_draw_copy (walk
->data
);
1563 g_object_unref (walk
->data
);
1564 walk
->data
= copy
->draw_fill
;
1567 copy
->draw_x
= movie
->draw_x
;
1568 copy
->draw_y
= movie
->draw_y
;
1569 g_assert (copy
->cache_state
>= SWFDEC_MOVIE_INVALID_EXTENTS
);
1571 sandbox
= swfdec_sandbox_get (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)));
1572 swfdec_sandbox_unuse (sandbox
);
1573 if (SWFDEC_IS_SPRITE_MOVIE (copy
)) {
1574 SwfdecActor
*actor
= SWFDEC_ACTOR (copy
);
1575 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_INITIALIZE
);
1576 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_LOAD
);
1577 swfdec_actor_execute (actor
, SWFDEC_EVENT_CONSTRUCT
, 0);
1579 swfdec_movie_initialize (copy
);
1580 swfdec_sandbox_use (sandbox
);
1585 swfdec_movie_get_path (SwfdecMovie
*movie
, gboolean dot
)
1589 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1591 s
= g_string_new ("");
1593 if (movie
->parent
) {
1594 if (movie
->name
!= SWFDEC_AS_STR_EMPTY
) {
1595 g_string_prepend (s
, movie
->name
);
1596 } else if (movie
->as_value
) {
1597 g_string_prepend (s
, movie
->as_value
->names
[movie
->as_value
->n_names
- 1]);
1599 g_assert_not_reached ();
1601 g_string_prepend_c (s
, (dot
? '.' : '/'));
1605 ret
= g_strdup_printf ("_level%u%s", movie
->depth
+ 16384, s
->str
);
1606 g_string_free (s
, TRUE
);
1608 if (s
->str
[0] != '/')
1609 g_string_prepend_c (s
, '/');
1610 ret
= g_string_free (s
, FALSE
);
1614 movie
= movie
->parent
;
1617 g_assert_not_reached ();
1623 swfdec_movie_compare_depths (gconstpointer a
, gconstpointer b
)
1625 if (SWFDEC_MOVIE (a
)->depth
< SWFDEC_MOVIE (b
)->depth
)
1627 if (SWFDEC_MOVIE (a
)->depth
> SWFDEC_MOVIE (b
)->depth
)
1633 * swfdec_depth_classify:
1634 * @depth: the depth to classify
1636 * Classifies a depth. This classification is mostly used when deciding if
1637 * certain operations are valid in ActionScript.
1639 * Returns: the classification of the depth.
1642 swfdec_depth_classify (int depth
)
1645 return SWFDEC_DEPTH_CLASS_EMPTY
;
1647 return SWFDEC_DEPTH_CLASS_TIMELINE
;
1648 if (depth
< 1048576)
1649 return SWFDEC_DEPTH_CLASS_DYNAMIC
;
1650 if (depth
< 2130690046)
1651 return SWFDEC_DEPTH_CLASS_RESERVED
;
1652 return SWFDEC_DEPTH_CLASS_EMPTY
;
1656 * swfdec_movie_get_own_resource:
1657 * @movie: movie to query
1659 * Queries the movie for his own resource. A movie only has its own resource if
1660 * it contains data loaded with the loadMovie() function, or if it is the root
1663 * Returns: The own resource of @movie or %NULL
1666 swfdec_movie_get_own_resource (SwfdecMovie
*movie
)
1668 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1670 if (!SWFDEC_IS_SPRITE_MOVIE (movie
))
1673 if (SWFDEC_AS_VALUE_GET_MOVIE (movie
->resource
->movie
) != movie
)
1676 return movie
->resource
;
1680 swfdec_movie_property_set (SwfdecMovie
*movie
, guint id
, SwfdecAsValue val
)
1682 SwfdecMovieClass
*klass
;
1684 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1686 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1687 klass
->property_set (movie
, id
, val
);
1691 swfdec_movie_property_get (SwfdecMovie
*movie
, guint id
)
1693 SwfdecMovieClass
*klass
;
1695 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), SWFDEC_AS_VALUE_UNDEFINED
);
1697 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1698 return klass
->property_get (movie
, id
);