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_graphic.h"
38 #include "swfdec_image.h"
39 #include "swfdec_loader_internal.h"
40 #include "swfdec_player_internal.h"
41 #include "swfdec_sprite.h"
42 #include "swfdec_sprite_movie.h"
43 #include "swfdec_renderer_internal.h"
44 #include "swfdec_resource.h"
45 #include "swfdec_system.h"
46 #include "swfdec_text_field_movie.h"
47 #include "swfdec_utils.h"
48 #include "swfdec_video_movie.h"
66 static guint signals
[LAST_SIGNAL
] = { 0, };
68 G_DEFINE_ABSTRACT_TYPE (SwfdecMovie
, swfdec_movie
, SWFDEC_TYPE_AS_OBJECT
)
71 swfdec_movie_init (SwfdecMovie
* movie
)
73 movie
->blend_mode
= 1;
77 cairo_matrix_init_identity (&movie
->original_transform
);
78 cairo_matrix_init_identity (&movie
->matrix
);
79 cairo_matrix_init_identity (&movie
->inverse_matrix
);
81 swfdec_color_transform_init_identity (&movie
->color_transform
);
83 movie
->visible
= TRUE
;
84 movie
->cache_state
= SWFDEC_MOVIE_INVALID_EXTENTS
;
85 movie
->invalidate_last
= TRUE
;
86 movie
->invalidate_next
= TRUE
;
88 swfdec_rect_init_empty (&movie
->extents
);
92 * swfdec_movie_invalidate:
93 * @movie: a #SwfdecMovie
94 * @parent_to_global: This is the matrix from the parent to the global matrix.
95 * It is only used for caching reasons
96 * @new_contents: %TRUE if this is the invalidation of the new contents, %FALSE
97 * if the old contents are invalidated.
99 * Performs an instant invalidation on @movie. You most likely don't want to
100 * call this function directly, but use swfdec_movie_invalidate_last() or
101 * swfdec_movie_invalidate_next() instead.
104 swfdec_movie_invalidate (SwfdecMovie
*movie
, const cairo_matrix_t
*parent_to_global
,
105 gboolean new_contents
)
107 SwfdecMovieClass
*klass
;
108 cairo_matrix_t matrix
;
111 movie
->invalidate_next
= FALSE
;
113 SwfdecPlayer
*player
;
114 if (movie
->invalidate_last
)
116 movie
->invalidate_last
= TRUE
;
117 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
118 player
->priv
->invalid_pending
= g_slist_prepend (player
->priv
->invalid_pending
, movie
);
120 g_assert (movie
->cache_state
<= SWFDEC_MOVIE_INVALID_CHILDREN
);
121 SWFDEC_LOG ("invalidating %s %s at %s", G_OBJECT_TYPE_NAME (movie
),
122 movie
->name
, new_contents
? "end" : "start");
123 cairo_matrix_multiply (&matrix
, &movie
->matrix
, parent_to_global
);
124 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
125 klass
->invalidate (movie
, &matrix
, new_contents
);
129 * swfdec_movie_invalidate_last:
130 * @movie: a #SwfdecMovie
132 * Ensures the movie's contents are invalidated. This function must be called
133 * before changing the movie or the output will have artifacts.
136 swfdec_movie_invalidate_last (SwfdecMovie
*movie
)
138 cairo_matrix_t matrix
;
140 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
142 if (movie
->invalidate_last
)
146 swfdec_movie_local_to_global_matrix (movie
->parent
, &matrix
);
148 cairo_matrix_init_identity (&matrix
);
149 swfdec_movie_invalidate (movie
, &matrix
, FALSE
);
150 g_assert (movie
->invalidate_last
);
154 * swfdec_movie_invalidate_next:
155 * @movie: a #SwfdecMovie
157 * Ensures the movie will be invalidated after script execution is done. So
158 * after calling this function you can modify position and contents of the
162 swfdec_movie_invalidate_next (SwfdecMovie
*movie
)
164 SwfdecPlayer
*player
;
166 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
168 swfdec_movie_invalidate_last (movie
);
169 movie
->invalidate_next
= TRUE
;
170 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
171 if (movie
== SWFDEC_MOVIE (player
->priv
->focus
))
172 swfdec_player_invalidate_focusrect (player
);
176 * swfdec_movie_queue_update:
177 * @movie: a #SwfdecMovie
178 * @state: how much needs to be updated
180 * Queues an update of all cached values inside @movie and invalidates it.
183 swfdec_movie_queue_update (SwfdecMovie
*movie
, SwfdecMovieCacheState state
)
185 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
187 while (movie
&& movie
->cache_state
< state
) {
188 movie
->cache_state
= state
;
189 movie
= movie
->parent
;
190 state
= SWFDEC_MOVIE_INVALID_CHILDREN
;
195 swfdec_movie_update_extents (SwfdecMovie
*movie
)
197 SwfdecMovieClass
*klass
;
199 SwfdecRect
*rect
= &movie
->original_extents
;
200 SwfdecRect
*extents
= &movie
->extents
;
202 *rect
= movie
->draw_extents
;
204 SwfdecRect image_extents
= { 0, 0,
205 movie
->image
->width
* SWFDEC_TWIPS_SCALE_FACTOR
,
206 movie
->image
->height
* SWFDEC_TWIPS_SCALE_FACTOR
};
207 swfdec_rect_union (rect
, rect
, &image_extents
);
209 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
210 swfdec_rect_union (rect
, rect
, &SWFDEC_MOVIE (walk
->data
)->extents
);
212 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
213 if (klass
->update_extents
)
214 klass
->update_extents (movie
, rect
);
215 if (swfdec_rect_is_empty (rect
)) {
219 swfdec_rect_transform (extents
, rect
, &movie
->matrix
);
220 if (movie
->parent
&& movie
->parent
->cache_state
< SWFDEC_MOVIE_INVALID_EXTENTS
) {
221 /* no need to invalidate here */
222 movie
->parent
->cache_state
= SWFDEC_MOVIE_INVALID_EXTENTS
;
227 swfdec_movie_begin_update_matrix (SwfdecMovie
*movie
)
229 swfdec_movie_invalidate_next (movie
);
233 swfdec_movie_end_update_matrix (SwfdecMovie
*movie
)
237 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
239 /* we operate on x0 and y0 when setting movie._x and movie._y */
240 if (movie
->modified
) {
241 movie
->matrix
.xx
= movie
->original_transform
.xx
;
242 movie
->matrix
.yx
= movie
->original_transform
.yx
;
243 movie
->matrix
.xy
= movie
->original_transform
.xy
;
244 movie
->matrix
.yy
= movie
->original_transform
.yy
;
246 movie
->matrix
= movie
->original_transform
;
249 d
= movie
->xscale
/ swfdec_matrix_get_xscale (&movie
->original_transform
);
250 e
= movie
->yscale
/ swfdec_matrix_get_yscale (&movie
->original_transform
);
251 cairo_matrix_scale (&movie
->matrix
, d
, e
);
252 if (isfinite (movie
->rotation
)) {
253 d
= movie
->rotation
- swfdec_matrix_get_rotation (&movie
->original_transform
);
254 cairo_matrix_rotate (&movie
->matrix
, d
* G_PI
/ 180);
256 swfdec_matrix_ensure_invertible (&movie
->matrix
, &movie
->inverse_matrix
);
258 g_signal_emit (movie
, signals
[MATRIX_CHANGED
], 0);
262 swfdec_movie_do_update (SwfdecMovie
*movie
)
266 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
267 SwfdecMovie
*child
= walk
->data
;
269 if (child
->cache_state
!= SWFDEC_MOVIE_UP_TO_DATE
)
270 swfdec_movie_do_update (child
);
273 switch (movie
->cache_state
) {
274 case SWFDEC_MOVIE_INVALID_EXTENTS
:
275 swfdec_movie_update_extents (movie
);
277 case SWFDEC_MOVIE_INVALID_CHILDREN
:
279 case SWFDEC_MOVIE_UP_TO_DATE
:
281 g_assert_not_reached ();
283 movie
->cache_state
= SWFDEC_MOVIE_UP_TO_DATE
;
287 * swfdec_movie_update:
288 * @movie: a #SwfdecMovie
290 * Brings the cached values of @movie up-to-date if they are not. This includes
291 * transformation matrices and extents. It needs to be called before accessing
292 * the relevant values.
295 swfdec_movie_update (SwfdecMovie
*movie
)
297 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
299 if (movie
->cache_state
== SWFDEC_MOVIE_UP_TO_DATE
)
302 if (movie
->parent
&& movie
->parent
->cache_state
!= SWFDEC_MOVIE_UP_TO_DATE
) {
303 swfdec_movie_update (movie
->parent
);
305 swfdec_movie_do_update (movie
);
310 swfdec_movie_find (SwfdecMovie
*movie
, int depth
)
314 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
316 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
317 SwfdecMovie
*cur
= walk
->data
;
319 if (cur
->depth
< depth
)
321 if (cur
->depth
== depth
)
329 swfdec_movie_unset_actor (SwfdecPlayer
*player
, SwfdecActor
*actor
)
331 SwfdecPlayerPrivate
*priv
= player
->priv
;
333 if (priv
->mouse_below
== actor
)
334 priv
->mouse_below
= NULL
;
335 if (priv
->mouse_grab
== actor
)
336 priv
->mouse_grab
= NULL
;
337 if (priv
->mouse_drag
== actor
)
338 priv
->mouse_drag
= NULL
;
340 if (priv
->focus_previous
== actor
)
341 priv
->focus_previous
= NULL
;
342 if (priv
->focus
== actor
) {
344 swfdec_player_invalidate_focusrect (player
);
349 swfdec_movie_do_remove (SwfdecMovie
*movie
, gboolean destroy
)
351 SwfdecPlayer
*player
;
353 SWFDEC_LOG ("removing %s %s", G_OBJECT_TYPE_NAME (movie
), movie
->name
);
355 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
356 while (movie
->list
) {
357 GList
*walk
= movie
->list
;
358 while (walk
&& SWFDEC_MOVIE (walk
->data
)->state
>= SWFDEC_MOVIE_STATE_REMOVED
)
362 destroy
&= swfdec_movie_do_remove (walk
->data
, destroy
);
364 /* FIXME: all of this here or in destroy callback? */
365 swfdec_movie_invalidate_last (movie
);
366 movie
->state
= SWFDEC_MOVIE_STATE_REMOVED
;
368 if (SWFDEC_IS_ACTOR (movie
)) {
369 SwfdecActor
*actor
= SWFDEC_ACTOR (movie
);
370 swfdec_movie_unset_actor (player
, actor
);
371 if ((actor
->events
&&
372 swfdec_event_list_has_conditions (actor
->events
, SWFDEC_AS_OBJECT (movie
), SWFDEC_EVENT_UNLOAD
, 0)) ||
373 swfdec_as_object_has_variable (SWFDEC_AS_OBJECT (movie
), SWFDEC_AS_STR_onUnload
)) {
374 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_UNLOAD
);
379 swfdec_movie_destroy (movie
);
384 * swfdec_movie_remove:
385 * @movie: #SwfdecMovie to remove
387 * Removes this movie from its parent. In contrast to swfdec_movie_destroy (),
388 * it might still be possible to reference it from Actionscript, if the movie
389 * queues onUnload event handlers.
392 swfdec_movie_remove (SwfdecMovie
*movie
)
394 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
396 if (movie
->state
> SWFDEC_MOVIE_STATE_RUNNING
)
398 if (swfdec_movie_do_remove (movie
, TRUE
))
401 swfdec_movie_set_depth (movie
, -32769 - movie
->depth
); /* don't ask me why... */
405 * swfdec_movie_destroy:
406 * @movie: #SwfdecMovie to destroy
408 * Removes this movie from its parent. After this it will no longer be present,
409 * neither visually nor via ActionScript. This function will not cause an
410 * unload event. Compare with swfdec_movie_remove ().
413 swfdec_movie_destroy (SwfdecMovie
*movie
)
415 SwfdecMovieClass
*klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
416 SwfdecPlayer
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
418 g_assert (movie
->state
< SWFDEC_MOVIE_STATE_DESTROYED
);
419 SWFDEC_LOG ("destroying movie %s", movie
->name
);
420 while (movie
->list
) {
421 swfdec_movie_destroy (movie
->list
->data
);
424 movie
->parent
->list
= g_list_remove (movie
->parent
->list
, movie
);
426 player
->priv
->roots
= g_list_remove (player
->priv
->roots
, movie
);
429 if (movie
->masked_by
)
430 movie
->masked_by
->mask_of
= NULL
;
432 movie
->mask_of
->masked_by
= NULL
;
433 movie
->masked_by
= NULL
;
434 movie
->mask_of
= NULL
;
435 /* FIXME: figure out how to handle destruction pre-init/construct.
436 * This is just a stop-gap measure to avoid dead movies in those queues */
437 if (SWFDEC_IS_ACTOR (movie
))
438 swfdec_player_remove_all_actions (player
, SWFDEC_ACTOR (movie
));
439 if (klass
->finish_movie
)
440 klass
->finish_movie (movie
);
441 player
->priv
->actors
= g_list_remove (player
->priv
->actors
, movie
);
442 if (movie
->invalidate_last
)
443 player
->priv
->invalid_pending
= g_slist_remove (player
->priv
->invalid_pending
, movie
);
444 movie
->state
= SWFDEC_MOVIE_STATE_DESTROYED
;
445 /* unset prototype here, so we don't work in AS anymore */
446 SWFDEC_AS_OBJECT (movie
)->prototype
= NULL
;
447 g_object_unref (movie
);
451 * swfdec_movie_resolve:
452 * @movie: movie to resolve
454 * Resolves a movie clip to its real version. Since movie clips can be
455 * explicitly destroyed, they have problems with references to them. In the
456 * case of destruction, these references will remain as "dangling pointers".
457 * However, if a movie with the same name is later created again, the reference
458 * will point to that movie. This function does this resolving.
460 * Returns: The movie clip @movie resolves to or %NULL if none.
463 swfdec_movie_resolve (SwfdecMovie
*movie
)
467 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
469 if (movie
->state
!= SWFDEC_MOVIE_STATE_DESTROYED
)
471 if (movie
->parent
== NULL
) {
472 SWFDEC_FIXME ("figure out how to resolve root movies");
475 parent
= swfdec_movie_resolve (movie
->parent
);
478 /* FIXME: include unnamed ones? */
479 return swfdec_movie_get_by_name (parent
, movie
->original_name
, FALSE
);
483 swfdec_movie_get_version (SwfdecMovie
*movie
)
485 return movie
->resource
->version
;
489 swfdec_movie_local_to_global (SwfdecMovie
*movie
, double *x
, double *y
)
491 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
492 g_return_if_fail (x
!= NULL
);
493 g_return_if_fail (y
!= NULL
);
496 cairo_matrix_transform_point (&movie
->matrix
, x
, y
);
497 } while ((movie
= movie
->parent
));
501 swfdec_movie_rect_local_to_global (SwfdecMovie
*movie
, SwfdecRect
*rect
)
503 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
504 g_return_if_fail (rect
!= NULL
);
506 swfdec_movie_local_to_global (movie
, &rect
->x0
, &rect
->y0
);
507 swfdec_movie_local_to_global (movie
, &rect
->x1
, &rect
->y1
);
508 if (rect
->x0
> rect
->x1
) {
509 double tmp
= rect
->x1
;
513 if (rect
->y0
> rect
->y1
) {
514 double tmp
= rect
->y1
;
521 swfdec_movie_global_to_local_matrix (SwfdecMovie
*movie
, cairo_matrix_t
*matrix
)
523 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
524 g_return_if_fail (matrix
!= NULL
);
526 cairo_matrix_init_identity (matrix
);
528 cairo_matrix_multiply (matrix
, &movie
->inverse_matrix
, matrix
);
529 movie
= movie
->parent
;
534 swfdec_movie_local_to_global_matrix (SwfdecMovie
*movie
, cairo_matrix_t
*matrix
)
536 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
537 g_return_if_fail (matrix
!= NULL
);
539 cairo_matrix_init_identity (matrix
);
541 cairo_matrix_multiply (matrix
, matrix
, &movie
->matrix
);
542 movie
= movie
->parent
;
547 swfdec_movie_global_to_local (SwfdecMovie
*movie
, double *x
, double *y
)
549 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
550 g_return_if_fail (x
!= NULL
);
551 g_return_if_fail (y
!= NULL
);
554 swfdec_movie_global_to_local (movie
->parent
, x
, y
);
556 cairo_matrix_transform_point (&movie
->inverse_matrix
, x
, y
);
560 swfdec_movie_rect_global_to_local (SwfdecMovie
*movie
, SwfdecRect
*rect
)
562 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
563 g_return_if_fail (rect
!= NULL
);
565 swfdec_movie_global_to_local (movie
, &rect
->x0
, &rect
->y0
);
566 swfdec_movie_global_to_local (movie
, &rect
->x1
, &rect
->y1
);
567 if (rect
->x0
> rect
->x1
) {
568 double tmp
= rect
->x1
;
572 if (rect
->y0
> rect
->y1
) {
573 double tmp
= rect
->y1
;
580 * swfdec_movie_get_mouse:
581 * @movie: a #SwfdecMovie
582 * @x: pointer to hold result of X coordinate
583 * @y: pointer to hold result of y coordinate
585 * Gets the mouse coordinates in the coordinate space of @movie.
588 swfdec_movie_get_mouse (SwfdecMovie
*movie
, double *x
, double *y
)
590 SwfdecPlayer
*player
;
592 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
593 g_return_if_fail (x
!= NULL
);
594 g_return_if_fail (y
!= NULL
);
596 player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
597 *x
= player
->priv
->mouse_x
;
598 *y
= player
->priv
->mouse_y
;
599 swfdec_player_stage_to_global (player
, x
, y
);
600 swfdec_movie_global_to_local (movie
, x
, y
);
604 * swfdec_movie_get_movie_at:
605 * @movie: a #SwfdecMovie
606 * @x: x coordinate in parent's coordinate space
607 * @y: y coordinate in the parent's coordinate space
608 * @events: %TRUE to only prefer movies that receive events
610 * Gets the child at the given coordinates. The coordinates are in the
611 * coordinate system of @movie's parent (or the global coordinate system for
612 * root movies). The @events parameter determines if movies that don't receive
613 * events should be respected.
615 * Returns: the child of @movie at the given coordinates or %NULL if none
618 swfdec_movie_get_movie_at (SwfdecMovie
*movie
, double x
, double y
, gboolean events
)
621 SwfdecMovieClass
*klass
;
623 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
625 SWFDEC_LOG ("%s %p getting mouse at: %g %g", G_OBJECT_TYPE_NAME (movie
), movie
, x
, y
);
626 if (movie
->cache_state
>= SWFDEC_MOVIE_INVALID_EXTENTS
)
627 swfdec_movie_update (movie
);
628 if (!swfdec_rect_contains (&movie
->extents
, x
, y
)) {
631 cairo_matrix_transform_point (&movie
->inverse_matrix
, &x
, &y
);
633 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
634 g_return_val_if_fail (klass
->contains
, NULL
);
635 ret
= klass
->contains (movie
, x
, y
, events
);
641 swfdec_movie_do_contains (SwfdecMovie
*movie
, double x
, double y
, gboolean events
)
645 SwfdecMovie
*ret
, *got
;
648 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
649 SwfdecMovie
*child
= walk
->data
;
651 if (!child
->visible
) {
652 SWFDEC_LOG ("%s %s (depth %d) is invisible, ignoring", G_OBJECT_TYPE_NAME (movie
), movie
->name
, movie
->depth
);
655 got
= swfdec_movie_get_movie_at (child
, x
, y
, events
);
658 /* set the return value to the topmost movie */
659 if (SWFDEC_IS_ACTOR (got
) && swfdec_actor_get_mouse_events (SWFDEC_ACTOR (got
))) {
661 } else if (ret
== NULL
) {
665 /* if thie is not a clipped movie, we've found something */
666 if (child
->clip_depth
== 0)
670 if (child
->clip_depth
) {
671 /* skip obscured movies */
672 SwfdecMovie
*tmp
= walk
->next
? walk
->next
->data
: NULL
;
673 while (tmp
&& tmp
->depth
<= child
->clip_depth
) {
675 tmp
= walk
->next
? walk
->next
->data
: NULL
;
683 for (walk2
= movie
->draws
; walk2
; walk2
= walk2
->next
) {
684 SwfdecDraw
*draw
= walk2
->data
;
686 if (swfdec_draw_contains (draw
, x
, y
))
694 swfdec_movie_needs_group (SwfdecMovie
*movie
)
696 return (movie
->blend_mode
> 1);
699 static cairo_operator_t
700 swfdec_movie_get_operator_for_blend_mode (guint blend_mode
)
702 switch (blend_mode
) {
703 case SWFDEC_BLEND_MODE_NORMAL
:
704 SWFDEC_ERROR ("shouldn't need to get operator without blend mode?!");
705 case SWFDEC_BLEND_MODE_LAYER
:
706 return CAIRO_OPERATOR_OVER
;
707 case SWFDEC_BLEND_MODE_ADD
:
708 return CAIRO_OPERATOR_ADD
;
709 case SWFDEC_BLEND_MODE_ALPHA
:
710 return CAIRO_OPERATOR_DEST_IN
;
711 case SWFDEC_BLEND_MODE_ERASE
:
712 return CAIRO_OPERATOR_DEST_OUT
;
713 case SWFDEC_BLEND_MODE_MULTIPLY
:
714 case SWFDEC_BLEND_MODE_SCREEN
:
715 case SWFDEC_BLEND_MODE_LIGHTEN
:
716 case SWFDEC_BLEND_MODE_DARKEN
:
717 case SWFDEC_BLEND_MODE_DIFFERENCE
:
718 case SWFDEC_BLEND_MODE_SUBTRACT
:
719 case SWFDEC_BLEND_MODE_INVERT
:
720 case SWFDEC_BLEND_MODE_OVERLAY
:
721 case SWFDEC_BLEND_MODE_HARDLIGHT
:
722 SWFDEC_FIXME ("blend mode %u unimplemented in cairo", blend_mode
);
723 return CAIRO_OPERATOR_OVER
;
725 SWFDEC_WARNING ("invalid blend mode %u", blend_mode
);
726 return CAIRO_OPERATOR_OVER
;
732 * @movie: The movie to act as the mask
733 * @cr: a cairo context which should be used for masking. The cairo context's
734 * matrix is assumed to be in the coordinate system of the movie's parent.
735 * @matrix: matrix to apply before rendering
737 * Creates a pattern suitable for masking. To do rendering using the returned
738 * mask, you want to use code like this:
739 * <informalexample><programlisting>
740 * mask = swfdec_movie_mask (cr, movie, matrix);
741 * cairo_push_group (cr);
742 * // do rendering here
743 * cairo_pop_group_to_source (cr);
744 * cairo_mask (cr, mask);
745 * cairo_pattern_destroy (mask);
746 * </programlisting></informalexample>
748 * Returns: A new cairo_patten_t to be used as the mask.
750 static cairo_pattern_t
*
751 swfdec_movie_mask (cairo_t
*cr
, SwfdecMovie
*movie
,
752 const cairo_matrix_t
*matrix
)
754 SwfdecColorTransform black
;
756 swfdec_color_transform_init_mask (&black
);
757 cairo_push_group_with_content (cr
, CAIRO_CONTENT_ALPHA
);
758 cairo_transform (cr
, matrix
);
760 swfdec_movie_render (movie
, cr
, &black
);
761 return cairo_pop_group (cr
);
765 swfdec_movie_render (SwfdecMovie
*movie
, cairo_t
*cr
,
766 const SwfdecColorTransform
*color_transform
)
768 SwfdecMovieClass
*klass
;
769 SwfdecColorTransform trans
;
772 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
773 g_return_if_fail (cr
!= NULL
);
774 if (cairo_status (cr
) != CAIRO_STATUS_SUCCESS
) {
775 g_warning ("%s", cairo_status_to_string (cairo_status (cr
)));
777 g_return_if_fail (color_transform
!= NULL
);
779 if (movie
->mask_of
!= NULL
&& !swfdec_color_transform_is_mask (color_transform
)) {
780 SWFDEC_LOG ("not rendering %s %p, movie is a mask",
781 G_OBJECT_TYPE_NAME (movie
), movie
->name
);
785 if (movie
->masked_by
!= NULL
) {
786 cairo_push_group (cr
);
788 group
= swfdec_movie_needs_group (movie
);
790 SWFDEC_DEBUG ("pushing group for blend mode %u", movie
->blend_mode
);
791 cairo_push_group (cr
);
795 SWFDEC_LOG ("transforming movie, transform: %g %g %g %g %g %g",
796 movie
->matrix
.xx
, movie
->matrix
.yy
,
797 movie
->matrix
.xy
, movie
->matrix
.yx
,
798 movie
->matrix
.x0
, movie
->matrix
.y0
);
799 cairo_transform (cr
, &movie
->matrix
);
800 swfdec_color_transform_chain (&trans
, &movie
->color_transform
, color_transform
);
802 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
803 g_return_if_fail (klass
->render
);
804 klass
->render (movie
, cr
, &trans
);
806 /* code to draw a red rectangle around the area occupied by this movie clip */
808 double x
= 1.0, y
= 0.0;
809 cairo_transform (cr
, &movie
->inverse_transform
);
810 cairo_user_to_device_distance (cr
, &x
, &y
);
811 cairo_set_source_rgb (cr
, 1.0, 0.0, 0.0);
812 cairo_set_line_width (cr
, 1 / sqrt (x
* x
+ y
* y
));
813 cairo_rectangle (cr
, object
->extents
.x0
+ 10, object
->extents
.y0
+ 10,
814 object
->extents
.x1
- object
->extents
.x0
- 20,
815 object
->extents
.y1
- object
->extents
.y0
- 20);
819 if (cairo_status (cr
) != CAIRO_STATUS_SUCCESS
) {
820 g_warning ("error rendering with cairo: %s", cairo_status_to_string (cairo_status (cr
)));
824 cairo_pattern_t
*pattern
;
826 pattern
= cairo_pop_group (cr
);
827 cairo_set_source (cr
, pattern
);
828 cairo_set_operator (cr
, swfdec_movie_get_operator_for_blend_mode (movie
->blend_mode
));
830 cairo_pattern_destroy (pattern
);
832 if (movie
->masked_by
) {
833 cairo_pattern_t
*mask
;
836 swfdec_movie_global_to_local_matrix (movie
->parent
, &mat
);
838 cairo_matrix_init_identity (&mat
);
839 if (movie
->masked_by
->parent
) {
841 swfdec_movie_local_to_global_matrix (movie
->masked_by
->parent
, &mat2
);
842 cairo_matrix_multiply (&mat
, &mat
, &mat2
);
844 mask
= swfdec_movie_mask (cr
, movie
->masked_by
, &mat
);
845 cairo_pop_group_to_source (cr
);
846 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
847 cairo_mask (cr
, mask
);
848 cairo_pattern_destroy (mask
);
853 swfdec_movie_get_property (GObject
*object
, guint param_id
, GValue
*value
,
856 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
860 g_value_set_int (value
, movie
->depth
);
863 g_value_set_object (value
, movie
->parent
);
866 g_value_set_object (value
, movie
->resource
);
869 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
875 swfdec_movie_set_property (GObject
*object
, guint param_id
, const GValue
*value
,
878 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
879 SwfdecAsContext
*cx
= swfdec_gc_object_get_context (movie
);
881 /* The context must be set before all movie-related properties */
886 /* parent must be set after depth */
887 g_assert (movie
->parent
== NULL
);
888 movie
->depth
= g_value_get_int (value
);
891 movie
->graphic
= g_value_get_object (value
);
893 g_object_ref (movie
->graphic
);
896 movie
->name
= g_value_get_string (value
);
898 movie
->name
= swfdec_as_context_get_string (cx
, movie
->name
);
899 movie
->original_name
= movie
->name
;
901 movie
->original_name
= SWFDEC_AS_STR_EMPTY
;
902 if (SWFDEC_IS_SPRITE_MOVIE (movie
) || SWFDEC_IS_BUTTON_MOVIE (movie
)) {
903 movie
->name
= swfdec_as_context_give_string (cx
,
904 g_strdup_printf ("instance%u", ++SWFDEC_PLAYER (cx
)->priv
->unnamed_count
));
906 movie
->name
= SWFDEC_AS_STR_EMPTY
;
911 movie
->parent
= g_value_get_object (value
);
912 /* parent holds a reference */
913 g_object_ref (movie
);
915 movie
->parent
->list
= g_list_insert_sorted (movie
->parent
->list
, movie
, swfdec_movie_compare_depths
);
916 SWFDEC_DEBUG ("inserting %s %p into %s %p", G_OBJECT_TYPE_NAME (movie
), movie
,
917 G_OBJECT_TYPE_NAME (movie
->parent
), movie
->parent
);
918 /* invalidate the parent, so it gets visible */
919 swfdec_movie_queue_update (movie
->parent
, SWFDEC_MOVIE_INVALID_CHILDREN
);
922 SwfdecPlayerPrivate
*priv
= SWFDEC_PLAYER (cx
)->priv
;
923 priv
->roots
= g_list_insert_sorted (priv
->roots
, movie
, swfdec_movie_compare_depths
);
924 SWFDEC_AS_VALUE_SET_STRING (&val
, swfdec_as_context_get_string (cx
, priv
->system
->version
));
925 swfdec_as_object_set_variable (SWFDEC_AS_OBJECT (movie
), SWFDEC_AS_STR__version
, &val
);
929 movie
->resource
= g_value_get_object (value
);
930 /* NB: the resource assumes it can access the player via the movie */
931 if (movie
->resource
->movie
== NULL
) {
932 g_assert (SWFDEC_IS_SPRITE_MOVIE (movie
));
933 movie
->resource
->movie
= SWFDEC_SPRITE_MOVIE (movie
);
937 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
943 swfdec_movie_dispose (GObject
*object
)
945 SwfdecMovie
* movie
= SWFDEC_MOVIE (object
);
948 g_assert (movie
->list
== NULL
);
950 SWFDEC_LOG ("disposing movie %s (depth %d)", movie
->name
, movie
->depth
);
951 if (movie
->graphic
) {
952 g_object_unref (movie
->graphic
);
953 movie
->graphic
= NULL
;
955 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
958 g_slist_free (movie
->variable_listeners
);
959 movie
->variable_listeners
= NULL
;
962 g_object_unref (movie
->image
);
965 g_slist_foreach (movie
->draws
, (GFunc
) g_object_unref
, NULL
);
966 g_slist_free (movie
->draws
);
969 G_OBJECT_CLASS (swfdec_movie_parent_class
)->dispose (G_OBJECT (movie
));
973 swfdec_movie_mark (SwfdecGcObject
*object
)
975 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
980 swfdec_gc_object_mark (movie
->parent
);
981 swfdec_as_string_mark (movie
->original_name
);
982 swfdec_as_string_mark (movie
->name
);
983 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
984 swfdec_gc_object_mark (walk
->data
);
986 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
987 SwfdecMovieVariableListener
*listener
= iter
->data
;
988 swfdec_gc_object_mark (listener
->object
);
989 swfdec_as_string_mark (listener
->name
);
991 swfdec_gc_object_mark (movie
->resource
);
993 SWFDEC_GC_OBJECT_CLASS (swfdec_movie_parent_class
)->mark (object
);
997 * swfdec_movie_is_scriptable:
1000 * Checks if the movie may be accessed by scripts. If not, the movie is not
1001 * accessible by Actionscript and functions that would return the movie should
1002 * instead return its parent.
1004 * Returns: %TRUE if scripts may access this movie, %FALSE if the parent
1008 swfdec_movie_is_scriptable (SwfdecMovie
*movie
)
1010 return (SWFDEC_IS_ACTOR (movie
) || SWFDEC_IS_VIDEO_MOVIE (movie
)) &&
1011 (swfdec_movie_get_version (movie
) > 5 || !SWFDEC_IS_TEXT_FIELD_MOVIE (movie
));
1014 /* FIXME: This function can definitely be implemented easier */
1016 swfdec_movie_get_by_name (SwfdecMovie
*movie
, const char *name
, gboolean unnamed
)
1020 guint version
= swfdec_gc_object_get_context (movie
)->version
;
1021 SwfdecPlayer
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
));
1023 i
= swfdec_player_get_level (player
, name
, version
);
1025 return SWFDEC_MOVIE (swfdec_player_get_movie_at_level (player
, i
));
1027 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
1028 SwfdecMovie
*cur
= walk
->data
;
1029 if (cur
->original_name
== SWFDEC_AS_STR_EMPTY
&& !unnamed
)
1031 if (swfdec_strcmp (version
, cur
->name
, name
) == 0) {
1032 if (swfdec_movie_is_scriptable (cur
))
1042 swfdec_movie_get_root (SwfdecMovie
*movie
)
1044 SwfdecMovie
*real_root
;
1046 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1049 while (real_root
->parent
)
1050 real_root
= real_root
->parent
;
1052 while (movie
->parent
&& !(movie
->lockroot
&&
1053 (swfdec_movie_get_version (movie
) != 6 ||
1054 swfdec_movie_get_version (real_root
) != 6))) {
1055 movie
= movie
->parent
;
1062 swfdec_movie_get_variable (SwfdecAsObject
*object
, SwfdecAsObject
*orig
,
1063 const char *variable
, SwfdecAsValue
*val
, guint
*flags
)
1065 SwfdecMovie
*movie
, *ret
;
1068 movie
= SWFDEC_MOVIE (object
);
1069 movie
= swfdec_movie_resolve (movie
);
1072 object
= SWFDEC_AS_OBJECT (movie
);
1074 if (SWFDEC_AS_OBJECT_CLASS (swfdec_movie_parent_class
)->get (object
, orig
, variable
, val
, flags
))
1077 /* FIXME: check that this is correct */
1078 if (swfdec_gc_object_get_context (object
)->version
> 5 && variable
== SWFDEC_AS_STR__global
) {
1079 SWFDEC_AS_VALUE_SET_OBJECT (val
, SWFDEC_AS_OBJECT (movie
->resource
->sandbox
));
1084 ret
= swfdec_movie_get_by_name (movie
, variable
, FALSE
);
1086 SWFDEC_AS_VALUE_SET_OBJECT (val
, SWFDEC_AS_OBJECT (ret
));
1091 prop_id
= swfdec_movie_property_lookup (variable
);
1092 if (prop_id
!= G_MAXUINT
) {
1093 swfdec_movie_property_get (movie
, prop_id
, val
);
1102 swfdec_movie_add_variable_listener (SwfdecMovie
*movie
, SwfdecAsObject
*object
,
1103 const char *name
, const SwfdecMovieVariableListenerFunction function
)
1105 SwfdecMovieVariableListener
*listener
;
1108 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1109 listener
= iter
->data
;
1111 if (listener
->object
== object
&& listener
->name
== name
&&
1112 listener
->function
== function
)
1118 listener
= g_new0 (SwfdecMovieVariableListener
, 1);
1119 listener
->object
= object
;
1120 listener
->name
= name
;
1121 listener
->function
= function
;
1123 movie
->variable_listeners
= g_slist_prepend (movie
->variable_listeners
,
1128 swfdec_movie_remove_variable_listener (SwfdecMovie
*movie
,
1129 SwfdecAsObject
*object
, const char *name
,
1130 const SwfdecMovieVariableListenerFunction function
)
1134 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1135 SwfdecMovieVariableListener
*listener
= iter
->data
;
1137 if (listener
->object
== object
&& listener
->name
== name
&&
1138 listener
->function
== function
)
1144 g_free (iter
->data
);
1145 movie
->variable_listeners
=
1146 g_slist_remove (movie
->variable_listeners
, iter
->data
);
1150 swfdec_movie_call_variable_listeners (SwfdecMovie
*movie
, const char *name
,
1151 const SwfdecAsValue
*val
)
1155 for (iter
= movie
->variable_listeners
; iter
!= NULL
; iter
= iter
->next
) {
1156 SwfdecMovieVariableListener
*listener
= iter
->data
;
1158 if (listener
->name
!= name
&&
1159 (swfdec_gc_object_get_context (movie
)->version
>= 7 ||
1160 !swfdec_str_case_equal (listener
->name
, name
)))
1163 listener
->function (listener
->object
, name
, val
);
1168 swfdec_movie_set_variable (SwfdecAsObject
*object
, const char *variable
,
1169 const SwfdecAsValue
*val
, guint flags
)
1171 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
1174 movie
= swfdec_movie_resolve (movie
);
1177 object
= SWFDEC_AS_OBJECT (movie
);
1179 prop_id
= swfdec_movie_property_lookup (variable
);
1180 if (prop_id
!= G_MAXUINT
) {
1181 swfdec_movie_property_set (movie
, prop_id
, val
);
1185 swfdec_movie_call_variable_listeners (movie
, variable
, val
);
1187 SWFDEC_AS_OBJECT_CLASS (swfdec_movie_parent_class
)->set (object
, variable
, val
, flags
);
1191 swfdec_movie_foreach_variable (SwfdecAsObject
*object
, SwfdecAsVariableForeach func
, gpointer data
)
1193 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
1198 ret
= SWFDEC_AS_OBJECT_CLASS (swfdec_movie_parent_class
)->foreach (object
, func
, data
);
1200 for (walk
= movie
->list
; walk
&& ret
; walk
= walk
->next
) {
1201 SwfdecMovie
*cur
= walk
->data
;
1202 if (cur
->original_name
== SWFDEC_AS_STR_EMPTY
)
1204 SWFDEC_AS_VALUE_SET_OBJECT (&val
, walk
->data
);
1205 ret
&= func (object
, cur
->name
, &val
, 0, data
);
1212 swfdec_movie_get_debug (SwfdecAsObject
*object
)
1214 SwfdecMovie
*movie
= SWFDEC_MOVIE (object
);
1216 return swfdec_movie_get_path (movie
, TRUE
);
1220 SwfdecMovie
* movie
;
1225 swfdec_movie_do_render (SwfdecMovie
*movie
, cairo_t
*cr
,
1226 const SwfdecColorTransform
*ctrans
)
1228 static const cairo_matrix_t ident
= { 1, 0, 0, 1, 0, 0};
1231 GSList
*clips
= NULL
;
1232 ClipEntry
*clip
= NULL
;
1234 if (movie
->draws
|| movie
->image
) {
1237 cairo_clip_extents (cr
, &inval
.x0
, &inval
.y0
, &inval
.x1
, &inval
.y1
);
1239 /* exeute the movie's drawing commands */
1240 for (walk
= movie
->draws
; walk
; walk
= walk
->next
) {
1241 SwfdecDraw
*draw
= walk
->data
;
1243 if (!swfdec_rect_intersect (NULL
, &draw
->extents
, &inval
))
1246 swfdec_draw_paint (draw
, cr
, ctrans
);
1249 /* if the movie loaded an image, draw it here now */
1250 /* FIXME: add check to only draw if inside clip extents */
1252 SwfdecRenderer
*renderer
= swfdec_renderer_get (cr
);
1253 cairo_surface_t
*surface
;
1254 cairo_pattern_t
*pattern
;
1257 if (swfdec_color_transform_is_mask (ctrans
)) {
1259 } else if (swfdec_color_transform_is_alpha (ctrans
)) {
1260 surface
= swfdec_image_create_surface (movie
->image
, renderer
);
1261 alpha
= ctrans
->aa
/ 256.0;
1263 surface
= swfdec_image_create_surface_transformed (movie
->image
,
1267 static const cairo_matrix_t matrix
= { 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
, 0, 0, 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
, 0, 0 };
1268 pattern
= cairo_pattern_create_for_surface (surface
);
1269 SWFDEC_LOG ("rendering loaded image");
1270 cairo_pattern_set_matrix (pattern
, &matrix
);
1272 pattern
= cairo_pattern_create_rgb (1.0, 0.0, 0.0);
1274 cairo_set_source (cr
, pattern
);
1275 cairo_paint_with_alpha (cr
, alpha
);
1276 cairo_pattern_destroy (pattern
);
1277 cairo_surface_destroy (surface
);
1281 /* draw the children movies */
1282 for (g
= movie
->list
; g
; g
= g_list_next (g
)) {
1283 SwfdecMovie
*child
= g
->data
;
1285 while (clip
&& clip
->depth
< child
->depth
) {
1286 cairo_pattern_t
*mask
;
1287 SWFDEC_INFO ("unsetting clip depth %d for depth %d", clip
->depth
, child
->depth
);
1288 mask
= swfdec_movie_mask (cr
, clip
->movie
, &ident
);
1289 cairo_pop_group_to_source (cr
);
1290 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
1291 cairo_mask (cr
, mask
);
1292 cairo_pattern_destroy (mask
);
1293 g_slice_free (ClipEntry
, clip
);
1294 clips
= g_slist_delete_link (clips
, clips
);
1295 clip
= clips
? clips
->data
: NULL
;
1298 if (child
->clip_depth
) {
1299 clip
= g_slice_new (ClipEntry
);
1300 clips
= g_slist_prepend (clips
, clip
);
1301 clip
->movie
= child
;
1302 clip
->depth
= child
->clip_depth
;
1303 SWFDEC_INFO ("clipping up to depth %d by using %s with depth %d", child
->clip_depth
,
1304 child
->name
, child
->depth
);
1305 cairo_push_group (cr
);
1309 SWFDEC_LOG ("rendering %p with depth %d", child
, child
->depth
);
1311 swfdec_movie_render (child
, cr
, ctrans
);
1314 cairo_pattern_t
*mask
;
1315 SWFDEC_INFO ("unsetting clip depth %d", clip
->depth
);
1316 mask
= swfdec_movie_mask (cr
, clip
->movie
, &ident
);
1317 cairo_pop_group_to_source (cr
);
1318 cairo_set_operator (cr
, CAIRO_OPERATOR_OVER
);
1319 cairo_mask (cr
, mask
);
1320 cairo_pattern_destroy (mask
);
1321 g_slice_free (ClipEntry
, clip
);
1322 clips
= g_slist_delete_link (clips
, clips
);
1323 clip
= clips
? clips
->data
: NULL
;
1325 g_assert (clips
== NULL
);
1329 swfdec_movie_do_invalidate (SwfdecMovie
*movie
, const cairo_matrix_t
*matrix
, gboolean last
)
1335 rect
.x0
= rect
.y0
= 0;
1336 rect
.x1
= movie
->image
->width
* SWFDEC_TWIPS_SCALE_FACTOR
;
1337 rect
.y1
= movie
->image
->height
* SWFDEC_TWIPS_SCALE_FACTOR
;
1339 swfdec_rect_init_empty (&rect
);
1341 swfdec_rect_union (&rect
, &rect
, &movie
->draw_extents
);
1342 swfdec_rect_transform (&rect
, &rect
, matrix
);
1343 swfdec_player_invalidate (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)), &rect
);
1345 for (walk
= movie
->list
; walk
; walk
= walk
->next
) {
1346 swfdec_movie_invalidate (walk
->data
, matrix
, last
);
1351 swfdec_movie_constructor (GType type
, guint n_construct_properties
,
1352 GObjectConstructParam
*construct_properties
)
1355 SwfdecPlayerPrivate
*priv
;
1357 object
= G_OBJECT_CLASS (swfdec_movie_parent_class
)->constructor (type
,
1358 n_construct_properties
, construct_properties
);
1360 priv
= SWFDEC_PLAYER (swfdec_gc_object_get_context (object
))->priv
;
1361 /* the movie is created invalid */
1362 priv
->invalid_pending
= g_slist_prepend (priv
->invalid_pending
, object
);
1368 swfdec_movie_class_init (SwfdecMovieClass
* movie_class
)
1370 GObjectClass
*object_class
= G_OBJECT_CLASS (movie_class
);
1371 SwfdecGcObjectClass
*gc_class
= SWFDEC_GC_OBJECT_CLASS (movie_class
);
1372 SwfdecAsObjectClass
*asobject_class
= SWFDEC_AS_OBJECT_CLASS (movie_class
);
1374 object_class
->constructor
= swfdec_movie_constructor
;
1375 object_class
->dispose
= swfdec_movie_dispose
;
1376 object_class
->get_property
= swfdec_movie_get_property
;
1377 object_class
->set_property
= swfdec_movie_set_property
;
1379 gc_class
->mark
= swfdec_movie_mark
;
1381 asobject_class
->get
= swfdec_movie_get_variable
;
1382 asobject_class
->set
= swfdec_movie_set_variable
;
1383 asobject_class
->foreach
= swfdec_movie_foreach_variable
;
1384 asobject_class
->debug
= swfdec_movie_get_debug
;
1386 signals
[MATRIX_CHANGED
] = g_signal_new ("matrix-changed", G_TYPE_FROM_CLASS (movie_class
),
1387 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, g_cclosure_marshal_VOID__VOID
,
1390 g_object_class_install_property (object_class
, PROP_DEPTH
,
1391 g_param_spec_int ("depth", "depth", "z order inside the parent",
1392 G_MININT
, G_MAXINT
, 0, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1393 g_object_class_install_property (object_class
, PROP_GRAPHIC
,
1394 g_param_spec_object ("graphic", "graphic", "graphic represented by this movie",
1395 SWFDEC_TYPE_GRAPHIC
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1396 g_object_class_install_property (object_class
, PROP_NAME
,
1397 g_param_spec_string ("name", "name", "the name given to this movie (can be empty)",
1398 NULL
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1399 g_object_class_install_property (object_class
, PROP_PARENT
,
1400 g_param_spec_object ("parent", "parent", "parent movie containing this movie",
1401 SWFDEC_TYPE_MOVIE
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1402 g_object_class_install_property (object_class
, PROP_RESOURCE
,
1403 g_param_spec_object ("resource", "resource", "the resource that spawned this movie",
1404 SWFDEC_TYPE_RESOURCE
, G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1406 movie_class
->render
= swfdec_movie_do_render
;
1407 movie_class
->invalidate
= swfdec_movie_do_invalidate
;
1408 movie_class
->contains
= swfdec_movie_do_contains
;
1409 movie_class
->property_get
= swfdec_movie_property_do_get
;
1410 movie_class
->property_set
= swfdec_movie_property_do_set
;
1414 swfdec_movie_initialize (SwfdecMovie
*movie
)
1416 SwfdecMovieClass
*klass
;
1418 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1420 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1421 if (klass
->init_movie
)
1422 klass
->init_movie (movie
);
1426 swfdec_movie_set_depth (SwfdecMovie
*movie
, int depth
)
1428 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1430 if (movie
->depth
== depth
)
1433 swfdec_movie_invalidate_last (movie
);
1434 movie
->depth
= depth
;
1435 if (movie
->parent
) {
1436 movie
->parent
->list
= g_list_sort (movie
->parent
->list
, swfdec_movie_compare_depths
);
1438 SwfdecPlayerPrivate
*player
= SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
))->priv
;
1439 player
->roots
= g_list_sort (player
->roots
, swfdec_movie_compare_depths
);
1441 g_object_notify (G_OBJECT (movie
), "depth");
1446 * @player: a #SwfdecPlayer
1447 * @depth: depth of movie
1448 * @parent: the parent movie or %NULL to make this a root movie
1449 * @resource: the resource that is responsible for this movie
1450 * @graphic: the graphic that is displayed by this movie or %NULL to create an
1452 * @name: a garbage-collected string to be used as the name for this movie or
1453 * %NULL for a default one.
1455 * Creates a new movie #SwfdecMovie for the given properties. No movie may exist
1456 * at the given @depth. The actual type of
1457 * this movie depends on the @graphic parameter. The movie will be initialized
1458 * with default properties. No script execution will be scheduled. After all
1459 * properties are set, the new-movie signal will be emitted if @player is a
1462 * Returns: a new #SwfdecMovie
1465 swfdec_movie_new (SwfdecPlayer
*player
, int depth
, SwfdecMovie
*parent
, SwfdecResource
*resource
,
1466 SwfdecGraphic
*graphic
, const char *name
)
1471 g_return_val_if_fail (SWFDEC_IS_PLAYER (player
), NULL
);
1472 g_return_val_if_fail (parent
== NULL
|| SWFDEC_IS_MOVIE (parent
), NULL
);
1473 g_return_val_if_fail (SWFDEC_IS_RESOURCE (resource
), NULL
);
1474 g_return_val_if_fail (graphic
== NULL
|| SWFDEC_IS_GRAPHIC (graphic
), NULL
);
1476 /* create the right movie */
1477 if (graphic
== NULL
) {
1478 type
= SWFDEC_TYPE_SPRITE_MOVIE
;
1480 SwfdecGraphicClass
*klass
= SWFDEC_GRAPHIC_GET_CLASS (graphic
);
1481 g_return_val_if_fail (g_type_is_a (klass
->movie_type
, SWFDEC_TYPE_MOVIE
), NULL
);
1482 type
= klass
->movie_type
;
1484 movie
= g_object_new (type
, "context", player
, "depth", depth
,
1485 "parent", parent
, "name", name
, "resource", resource
,
1486 "graphic", graphic
, NULL
);
1491 /* FIXME: since this is only used in PlaceObject, wouldn't it be easier to just have
1492 * swfdec_movie_update_static_properties (movie); that's notified when any of these change
1493 * and let PlaceObject modify the movie directly?
1496 swfdec_movie_set_static_properties (SwfdecMovie
*movie
, const cairo_matrix_t
*transform
,
1497 const SwfdecColorTransform
*ctrans
, int ratio
, int clip_depth
, guint blend_mode
,
1498 SwfdecEventList
*events
)
1500 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1501 g_return_if_fail (clip_depth
>= -16384 || clip_depth
<= 0);
1502 g_return_if_fail (ratio
>= -1);
1504 if (movie
->modified
) {
1505 SWFDEC_LOG ("%s has already been modified by scripts, ignoring updates", movie
->name
);
1509 swfdec_movie_begin_update_matrix (movie
);
1510 movie
->original_transform
= *transform
;
1511 movie
->matrix
.x0
= movie
->original_transform
.x0
;
1512 movie
->matrix
.y0
= movie
->original_transform
.y0
;
1513 movie
->xscale
= swfdec_matrix_get_xscale (&movie
->original_transform
);
1514 movie
->yscale
= swfdec_matrix_get_yscale (&movie
->original_transform
);
1515 movie
->rotation
= swfdec_matrix_get_rotation (&movie
->original_transform
);
1516 swfdec_movie_end_update_matrix (movie
);
1519 swfdec_movie_invalidate_last (movie
);
1520 movie
->color_transform
= *ctrans
;
1522 if (ratio
>= 0 && (guint
) ratio
!= movie
->original_ratio
) {
1523 SwfdecMovieClass
*klass
;
1524 movie
->original_ratio
= ratio
;
1525 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1526 if (klass
->set_ratio
)
1527 klass
->set_ratio (movie
);
1529 if (clip_depth
&& clip_depth
!= movie
->clip_depth
) {
1530 movie
->clip_depth
= clip_depth
;
1531 /* FIXME: is this correct? */
1532 swfdec_movie_invalidate_last (movie
->parent
? movie
->parent
: movie
);
1534 if (blend_mode
!= movie
->blend_mode
) {
1535 movie
->blend_mode
= blend_mode
;
1536 swfdec_movie_invalidate_last (movie
);
1539 if (SWFDEC_IS_SPRITE_MOVIE (movie
)) {
1540 SwfdecActor
*actor
= SWFDEC_ACTOR (movie
);
1542 swfdec_event_list_free (actor
->events
);
1543 actor
->events
= swfdec_event_list_copy (events
);
1545 SWFDEC_WARNING ("trying to set events on a %s, not allowed", G_OBJECT_TYPE_NAME (movie
));
1551 * swfdec_movie_duplicate:
1552 * @movie: #SwfdecMovie to copy
1553 * @name: garbage-collected name for the new copy
1554 * @depth: depth to put this movie in
1556 * Creates a duplicate of @movie. The duplicate will not be initialized or
1557 * queued up for any events. You have to do this manually. In particular calling
1558 * swfdec_movie_initialize() on the returned movie must be done.
1559 * This function must be called from within a script.
1561 * Returns: a newly created movie or %NULL on error
1564 swfdec_movie_duplicate (SwfdecMovie
*movie
, const char *name
, int depth
)
1566 SwfdecMovie
*parent
, *copy
;
1567 SwfdecSandbox
*sandbox
;
1570 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1571 g_return_val_if_fail (name
!= NULL
, NULL
);
1573 parent
= movie
->parent
;
1574 if (movie
->parent
== NULL
) {
1575 SWFDEC_FIXME ("don't know how to duplicate root movies");
1578 copy
= swfdec_movie_find (movie
->parent
, depth
);
1580 SWFDEC_LOG ("depth %d already occupied while duplicating, removing old movie", depth
);
1581 swfdec_movie_remove (copy
);
1583 copy
= swfdec_movie_new (SWFDEC_PLAYER (swfdec_gc_object_get_context (movie
)), depth
,
1584 parent
, movie
->resource
, movie
->graphic
, name
);
1585 /* copy properties */
1586 swfdec_movie_set_static_properties (copy
, &movie
->original_transform
,
1587 &movie
->color_transform
, movie
->original_ratio
, movie
->clip_depth
,
1588 movie
->blend_mode
, SWFDEC_IS_ACTOR (movie
) ? SWFDEC_ACTOR (movie
)->events
: NULL
);
1589 /* Copy drawing state.
1590 * We can keep refs to all finalized draw objects, but need to create copies
1591 * of the still active ones as their path can still change */
1592 copy
->draws
= g_slist_copy (movie
->draws
);
1593 g_slist_foreach (copy
->draws
, (GFunc
) g_object_ref
, NULL
);
1594 copy
->draw_extents
= movie
->draw_extents
;
1595 for (walk
= copy
->draws
; walk
; walk
= walk
->next
) {
1596 if (walk
->data
== movie
->draw_line
) {
1597 copy
->draw_line
= swfdec_draw_copy (walk
->data
);
1598 g_object_unref (walk
->data
);
1599 walk
->data
= copy
->draw_line
;
1600 } else if (walk
->data
== movie
->draw_fill
) {
1601 copy
->draw_fill
= swfdec_draw_copy (walk
->data
);
1602 g_object_unref (walk
->data
);
1603 walk
->data
= copy
->draw_fill
;
1606 copy
->draw_x
= movie
->draw_x
;
1607 copy
->draw_y
= movie
->draw_y
;
1608 g_assert (copy
->cache_state
>= SWFDEC_MOVIE_INVALID_EXTENTS
);
1610 sandbox
= SWFDEC_SANDBOX (swfdec_gc_object_get_context (movie
)->global
);
1611 swfdec_sandbox_unuse (sandbox
);
1612 if (SWFDEC_IS_SPRITE_MOVIE (copy
)) {
1613 SwfdecActor
*actor
= SWFDEC_ACTOR (copy
);
1614 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_INITIALIZE
);
1615 swfdec_actor_queue_script (actor
, SWFDEC_EVENT_LOAD
);
1616 swfdec_actor_execute (actor
, SWFDEC_EVENT_CONSTRUCT
, 0);
1618 swfdec_movie_initialize (copy
);
1619 swfdec_sandbox_use (sandbox
);
1624 swfdec_movie_get_path (SwfdecMovie
*movie
, gboolean dot
)
1628 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1630 s
= g_string_new ("");
1632 if (movie
->parent
) {
1633 g_string_prepend (s
, movie
->name
);
1634 g_string_prepend_c (s
, (dot
? '.' : '/'));
1638 ret
= g_strdup_printf ("_level%u%s", movie
->depth
+ 16384, s
->str
);
1639 g_string_free (s
, TRUE
);
1641 if (s
->str
[0] != '/')
1642 g_string_prepend_c (s
, '/');
1643 ret
= g_string_free (s
, FALSE
);
1647 movie
= movie
->parent
;
1650 g_assert_not_reached ();
1656 swfdec_movie_compare_depths (gconstpointer a
, gconstpointer b
)
1658 if (SWFDEC_MOVIE (a
)->depth
< SWFDEC_MOVIE (b
)->depth
)
1660 if (SWFDEC_MOVIE (a
)->depth
> SWFDEC_MOVIE (b
)->depth
)
1666 * swfdec_depth_classify:
1667 * @depth: the depth to classify
1669 * Classifies a depth. This classification is mostly used when deciding if
1670 * certain operations are valid in ActionScript.
1672 * Returns: the classification of the depth.
1675 swfdec_depth_classify (int depth
)
1678 return SWFDEC_DEPTH_CLASS_EMPTY
;
1680 return SWFDEC_DEPTH_CLASS_TIMELINE
;
1681 if (depth
< 1048576)
1682 return SWFDEC_DEPTH_CLASS_DYNAMIC
;
1683 if (depth
< 2130690046)
1684 return SWFDEC_DEPTH_CLASS_RESERVED
;
1685 return SWFDEC_DEPTH_CLASS_EMPTY
;
1689 * swfdec_movie_get_own_resource:
1690 * @movie: movie to query
1692 * Queries the movie for his own resource. A movie only has its own resource if
1693 * it contains data loaded with the loadMovie() function, or if it is the root
1696 * Returns: The own resource of @movie or %NULL
1699 swfdec_movie_get_own_resource (SwfdecMovie
*movie
)
1701 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie
), NULL
);
1703 if (!SWFDEC_IS_SPRITE_MOVIE (movie
))
1706 if (movie
->resource
->movie
!= SWFDEC_SPRITE_MOVIE (movie
))
1709 return movie
->resource
;
1713 swfdec_movie_property_set (SwfdecMovie
*movie
, guint id
, const SwfdecAsValue
*val
)
1715 SwfdecMovieClass
*klass
;
1717 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1718 g_return_if_fail (val
!= NULL
);
1720 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1721 klass
->property_set (movie
, id
, val
);
1725 swfdec_movie_property_get (SwfdecMovie
*movie
, guint id
, SwfdecAsValue
*val
)
1727 SwfdecMovieClass
*klass
;
1729 g_return_if_fail (SWFDEC_IS_MOVIE (movie
));
1730 g_return_if_fail (val
!= NULL
);
1732 klass
= SWFDEC_MOVIE_GET_CLASS (movie
);
1733 klass
->property_get (movie
, id
, val
);