2 * Copyright (C) 2006 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
26 #include "swfdec_sprite_movie.h"
27 #include "swfdec_as_internal.h"
28 #include "swfdec_as_strings.h"
29 #include "swfdec_audio_event.h"
30 #include "swfdec_audio_stream.h"
31 #include "swfdec_debug.h"
32 #include "swfdec_filter.h"
33 #include "swfdec_graphic_movie.h"
34 #include "swfdec_player_internal.h"
35 #include "swfdec_ringbuffer.h"
36 #include "swfdec_script.h"
37 #include "swfdec_sprite.h"
38 #include "swfdec_swf_instance.h"
39 #include "swfdec_tag.h"
41 /*** SWFDEC_SPRITE_MOVIE ***/
44 swfdec_sprite_movie_remove_child (SwfdecMovie
*movie
, int depth
)
46 SwfdecMovie
*child
= swfdec_movie_find (movie
, depth
);
51 swfdec_movie_remove (child
);
56 swfdec_sprite_movie_run_script (gpointer movie
, gpointer data
)
58 swfdec_as_object_run (movie
, data
);
62 swfdec_get_clipeventflags (SwfdecMovie
*movie
, SwfdecBits
* bits
)
64 if (SWFDEC_SWF_DECODER (movie
->swf
->decoder
)->version
<= 5) {
65 return swfdec_bits_get_u16 (bits
);
67 return swfdec_bits_get_u32 (bits
);
72 swfdec_sprite_movie_perform_place (SwfdecSpriteMovie
*movie
, SwfdecBits
*bits
, guint tag
)
74 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie
)->context
);
75 SwfdecMovie
*mov
= SWFDEC_MOVIE (movie
);
77 gboolean has_clip_actions
;
78 gboolean has_clip_depth
;
82 gboolean has_transform
;
83 gboolean has_character
;
87 gboolean has_blend_mode
= 0;
88 gboolean has_filter
= 0;
90 cairo_matrix_t transform
;
91 SwfdecColorTransform ctrans
;
92 guint ratio
, id
, version
;
93 SwfdecEventList
*events
;
96 SwfdecGraphic
*graphic
;
98 version
= SWFDEC_SWF_DECODER (mov
->swf
->decoder
)->version
;
100 /* 1) check which stuff is set */
101 has_clip_actions
= swfdec_bits_getbit (bits
);
102 has_clip_depth
= swfdec_bits_getbit (bits
);
103 has_name
= swfdec_bits_getbit (bits
);
104 has_ratio
= swfdec_bits_getbit (bits
);
105 has_ctrans
= swfdec_bits_getbit (bits
);
106 has_transform
= swfdec_bits_getbit (bits
);
107 has_character
= swfdec_bits_getbit (bits
);
108 move
= swfdec_bits_getbit (bits
);
110 SWFDEC_LOG ("performing PlaceObject%d on movie %s", tag
== SWFDEC_TAG_PLACEOBJECT2
? 2 : 3, mov
->name
);
111 SWFDEC_LOG (" has_clip_actions = %d", has_clip_actions
);
112 SWFDEC_LOG (" has_clip_depth = %d", has_clip_depth
);
113 SWFDEC_LOG (" has_name = %d", has_name
);
114 SWFDEC_LOG (" has_ratio = %d", has_ratio
);
115 SWFDEC_LOG (" has_ctrans = %d", has_ctrans
);
116 SWFDEC_LOG (" has_transform = %d", has_transform
);
117 SWFDEC_LOG (" has_character = %d", has_character
);
118 SWFDEC_LOG (" move = %d", move
);
120 if (tag
== SWFDEC_TAG_PLACEOBJECT3
) {
121 swfdec_bits_getbits (bits
, 5);
122 cache
= swfdec_bits_getbit (bits
);
123 has_blend_mode
= swfdec_bits_getbit (bits
);
124 has_filter
= swfdec_bits_getbit (bits
);
125 SWFDEC_LOG (" cache = %d", cache
);
126 SWFDEC_LOG (" has filter = %d", has_filter
);
127 SWFDEC_LOG (" has blend mode = %d", has_blend_mode
);
130 /* 2) read all properties */
131 depth
= swfdec_bits_get_u16 (bits
);
132 if (depth
>= 16384) {
133 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth
);
135 SWFDEC_LOG (" depth = %d (=> %d)", depth
, depth
- 16384);
138 id
= swfdec_bits_get_u16 (bits
);
139 SWFDEC_LOG (" id = %d", id
);
145 swfdec_bits_get_matrix (bits
, &transform
, NULL
);
146 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
147 transform
.xx
, transform
.yx
,
148 transform
.xy
, transform
.yy
,
149 transform
.x0
, transform
.y0
);
152 swfdec_bits_get_color_transform (bits
, &ctrans
);
153 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
154 ctrans
.ra
, ctrans
.rb
,
155 ctrans
.ga
, ctrans
.gb
,
156 ctrans
.ba
, ctrans
.bb
,
157 ctrans
.aa
, ctrans
.ab
);
161 ratio
= swfdec_bits_get_u16 (bits
);
162 SWFDEC_LOG (" ratio = %d", ratio
);
168 char *s
= swfdec_bits_get_string_with_version (bits
, version
);
169 name
= swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player
), s
);
170 SWFDEC_LOG (" name = %s", name
);
175 if (has_clip_depth
) {
176 clip_depth
= swfdec_bits_get_u16 (bits
) - 16384;
177 SWFDEC_LOG (" clip_depth = %d (=> %d)", clip_depth
+ 16384, clip_depth
);
183 GSList
*filters
= swfdec_filter_parse (player
, bits
);
184 g_slist_free (filters
);
187 if (has_blend_mode
) {
188 blend_mode
= swfdec_bits_get_u8 (bits
);
189 SWFDEC_LOG (" blend mode = %u", blend_mode
);
194 if (has_clip_actions
) {
195 int reserved
, clip_event_flags
, event_flags
, key_code
;
198 events
= swfdec_event_list_new (player
);
199 reserved
= swfdec_bits_get_u16 (bits
);
200 clip_event_flags
= swfdec_get_clipeventflags (mov
, bits
);
203 script_name
= g_strdup (name
);
205 script_name
= g_strdup_printf ("Sprite%u", id
);
207 script_name
= g_strdup ("unknown");
208 while ((event_flags
= swfdec_get_clipeventflags (mov
, bits
)) != 0) {
209 guint length
= swfdec_bits_get_u32 (bits
);
210 SwfdecBits action_bits
;
212 swfdec_bits_init_bits (&action_bits
, bits
, length
);
213 if (event_flags
& SWFDEC_EVENT_KEY_PRESS
)
214 key_code
= swfdec_bits_get_u8 (&action_bits
);
218 SWFDEC_INFO ("clip event with flags 0x%X, key code %d", event_flags
, key_code
);
219 #define SWFDEC_IMPLEMENTED_EVENTS \
220 (SWFDEC_EVENT_LOAD | SWFDEC_EVENT_UNLOAD | SWFDEC_EVENT_ENTER | SWFDEC_EVENT_INITIALIZE | SWFDEC_EVENT_CONSTRUCT | \
221 SWFDEC_EVENT_MOUSE_DOWN | SWFDEC_EVENT_MOUSE_MOVE | SWFDEC_EVENT_MOUSE_UP)
222 if (event_flags
& ~SWFDEC_IMPLEMENTED_EVENTS
) {
223 SWFDEC_ERROR ("using non-implemented clip events %u", event_flags
& ~SWFDEC_IMPLEMENTED_EVENTS
);
225 swfdec_event_list_parse (events
, &action_bits
, version
,
226 event_flags
, key_code
, script_name
);
227 if (swfdec_bits_left (&action_bits
)) {
228 SWFDEC_ERROR ("not all action data was parsed: %u bytes left",
229 swfdec_bits_left (&action_bits
));
232 g_free (script_name
);
237 /* 3) perform the actions depending on the set properties */
238 cur
= swfdec_movie_find (mov
, depth
);
239 graphic
= swfdec_swf_decoder_get_character (SWFDEC_SWF_DECODER (mov
->swf
->decoder
), id
);
242 SWFDEC_INFO ("no movie at depth %d, ignoring move command", depth
);
246 SwfdecMovieClass
*klass
= SWFDEC_MOVIE_GET_CLASS (cur
);
248 klass
->replace (cur
, graphic
);
250 swfdec_movie_set_static_properties (cur
, has_transform
? &transform
: NULL
,
251 has_ctrans
? &ctrans
: NULL
, ratio
, clip_depth
, blend_mode
, events
);
253 if (cur
!= NULL
&& version
> 5) {
254 SWFDEC_INFO ("depth %d is already occupied by movie %s, not placing", depth
, cur
->name
);
257 if (!SWFDEC_IS_GRAPHIC (graphic
)) {
258 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id
);
260 swfdec_event_list_free (events
);
263 cur
= swfdec_movie_new (player
, depth
, mov
, graphic
, name
);
264 swfdec_movie_set_static_properties (cur
, has_transform
? &transform
: NULL
,
265 has_ctrans
? &ctrans
: NULL
, ratio
, clip_depth
, blend_mode
, events
);
266 if (SWFDEC_IS_SPRITE_MOVIE (cur
)) {
267 g_queue_push_tail (player
->init_queue
, cur
);
268 g_queue_push_tail (player
->construct_queue
, cur
);
269 swfdec_movie_queue_script (cur
, SWFDEC_EVENT_LOAD
);
271 swfdec_movie_initialize (cur
);
276 swfdec_event_list_free (events
);
281 swfdec_sprite_movie_start_sound (SwfdecMovie
*movie
, SwfdecBits
*bits
)
283 SwfdecSoundChunk
*chunk
;
286 id
= swfdec_bits_get_u16 (bits
);
287 chunk
= swfdec_sound_parse_chunk (SWFDEC_SWF_DECODER (movie
->swf
->decoder
), bits
, id
);
289 SwfdecAudio
*audio
= swfdec_audio_event_new_from_chunk (SWFDEC_PLAYER (
290 SWFDEC_AS_OBJECT (movie
)->context
), chunk
);
292 g_object_unref (audio
);
297 swfdec_sprite_movie_perform_one_action (SwfdecSpriteMovie
*movie
, guint tag
, SwfdecBuffer
*buffer
,
298 gboolean skip_scripts
)
300 SwfdecMovie
*mov
= SWFDEC_MOVIE (movie
);
301 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov
)->context
);
305 swfdec_bits_init (&bits
, buffer
);
307 SWFDEC_LOG ("%p: executing %uth tag %s in frame %u", movie
, movie
->next_action
- 1,
308 swfdec_swf_decoder_get_tag_name (tag
), movie
->frame
);
310 case SWFDEC_TAG_DOACTION
:
311 SWFDEC_LOG ("SCRIPT action");
313 SwfdecScript
*script
= swfdec_swf_decoder_get_script (
314 SWFDEC_SWF_DECODER (mov
->swf
->decoder
), buffer
->data
);
316 swfdec_player_add_action (player
, mov
, swfdec_sprite_movie_run_script
, script
);
319 case SWFDEC_TAG_PLACEOBJECT2
:
320 case SWFDEC_TAG_PLACEOBJECT3
:
321 return swfdec_sprite_movie_perform_place (movie
, &bits
, tag
);
322 case SWFDEC_TAG_REMOVEOBJECT
:
323 /* yes, this code is meant to be like this - the following u16 is the
324 * character id, that we don't care about, the rest is like RemoveObject2
326 swfdec_bits_get_u16 (&bits
);
328 case SWFDEC_TAG_REMOVEOBJECT2
:
330 int depth
= swfdec_bits_get_u16 (&bits
);
331 SWFDEC_LOG ("REMOVE action: depth %d => %d", depth
, depth
- 16384);
333 if (!swfdec_sprite_movie_remove_child (mov
, depth
))
334 SWFDEC_INFO ("could not remove, no child at depth %d", depth
);
337 case SWFDEC_TAG_STARTSOUND
:
338 swfdec_sprite_movie_start_sound (mov
, &bits
);
340 case SWFDEC_TAG_SHOWFRAME
:
341 if (movie
->frame
< movie
->n_frames
) {
344 SWFDEC_ERROR ("too many ShowFrame tags");
348 g_assert_not_reached ();
354 swfdec_movie_is_compatible (SwfdecMovie
*movie
, SwfdecMovie
*with
)
356 g_assert (movie
->depth
== with
->depth
);
358 if (movie
->original_ratio
!= with
->original_ratio
)
361 if (G_OBJECT_TYPE (movie
) != G_OBJECT_TYPE (with
))
368 my_g_list_split (GList
*list
, GList
*split
)
384 swfdec_sprite_movie_goto (SwfdecSpriteMovie
*movie
, guint goto_frame
)
387 SwfdecPlayer
*player
;
391 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie
));
393 mov
= SWFDEC_MOVIE (movie
);
394 /* lots of things where we've got nothing to do */
395 if (goto_frame
== 0 || goto_frame
> movie
->n_frames
||
396 movie
->sprite
== NULL
|| mov
->will_be_removed
|| goto_frame
== movie
->frame
)
399 if (goto_frame
> movie
->sprite
->parse_frame
) {
400 SWFDEC_WARNING ("jumping to not-yet-loaded frame %u (loaded: %u/%u)",
401 goto_frame
, movie
->sprite
->parse_frame
, movie
->sprite
->n_frames
);
405 player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie
)->context
);
406 SWFDEC_LOG ("doing goto %u for %p %d", goto_frame
, movie
,
407 SWFDEC_CHARACTER (movie
->sprite
)->id
);
409 SWFDEC_DEBUG ("performing goto %u -> %u for character %u",
410 movie
->frame
, goto_frame
, SWFDEC_CHARACTER (movie
->sprite
)->id
);
411 if (goto_frame
< movie
->frame
) {
414 for (walk
= mov
->list
; walk
&&
415 swfdec_depth_classify (SWFDEC_MOVIE (walk
->data
)->depth
) != SWFDEC_DEPTH_CLASS_TIMELINE
;
420 mov
->list
= my_g_list_split (mov
->list
, old
);
421 for (walk
= old
; walk
&&
422 swfdec_depth_classify (SWFDEC_MOVIE (walk
->data
)->depth
) == SWFDEC_DEPTH_CLASS_TIMELINE
;
426 old
= my_g_list_split (old
, walk
);
427 mov
->list
= g_list_concat (mov
->list
, walk
);
429 movie
->next_action
= 0;
431 /* NB: this path is also taken on init */
433 n
= goto_frame
- movie
->frame
;
437 SwfdecBuffer
*buffer
;
438 /* FIXME: These actions should probably just be added to the action queue */
439 if (movie
== mov
->swf
->movie
&&
440 mov
->swf
->parse_frame
<= movie
->frame
)
441 swfdec_swf_instance_advance (mov
->swf
);
442 if (!swfdec_sprite_get_action (movie
->sprite
, movie
->next_action
, &tag
, &buffer
))
444 movie
->next_action
++;
445 if (!swfdec_sprite_movie_perform_one_action (movie
, tag
, buffer
, n
> 1))
448 /* now try to copy eventual movies */
450 SwfdecMovie
*prev
, *cur
;
451 GList
*old_walk
, *walk
;
457 for (; old_walk
; old_walk
= old_walk
->next
) {
458 prev
= old_walk
->data
;
459 while (cur
->depth
< prev
->depth
) {
465 if (cur
->depth
== prev
->depth
&&
466 swfdec_movie_is_compatible (prev
, cur
)) {
467 SwfdecMovieClass
*klass
= SWFDEC_MOVIE_GET_CLASS (prev
);
469 /* FIXME: This merging stuff probably needs to be improved a _lot_ */
471 klass
->replace (prev
, cur
->graphic
);
472 swfdec_movie_set_static_properties (prev
, &cur
->original_transform
,
473 &cur
->original_ctrans
, cur
->original_ratio
, cur
->clip_depth
,
474 cur
->blend_mode
, cur
->events
);
475 swfdec_movie_destroy (cur
);
479 swfdec_movie_remove (prev
);
482 for (; old_walk
; old_walk
= old_walk
->next
) {
483 swfdec_movie_remove (old_walk
->data
);
491 G_DEFINE_TYPE (SwfdecSpriteMovie
, swfdec_sprite_movie
, SWFDEC_TYPE_MOVIE
)
494 swfdec_sprite_movie_dispose (GObject
*object
)
496 SwfdecSpriteMovie
*movie
= SWFDEC_SPRITE_MOVIE (object
);
498 g_assert (movie
->sound_stream
== NULL
);
500 G_OBJECT_CLASS (swfdec_sprite_movie_parent_class
)->dispose (object
);
504 swfdec_sprite_movie_do_enter_frame (gpointer movie
, gpointer unused
)
506 if (SWFDEC_MOVIE (movie
)->will_be_removed
)
508 swfdec_movie_execute_script (movie
, SWFDEC_EVENT_ENTER
);
512 swfdec_sprite_movie_do_init_movie (SwfdecSpriteMovie
*movie
)
514 SwfdecMovie
*mov
= SWFDEC_MOVIE (movie
);
515 SwfdecAsContext
*context
= SWFDEC_AS_OBJECT (movie
)->context
;
516 SwfdecAsObject
*constructor
= NULL
;
518 g_assert (mov
->swf
!= NULL
);
523 g_assert (movie
->sprite
->parse_frame
> 0);
524 movie
->n_frames
= movie
->sprite
->n_frames
;
525 name
= swfdec_swf_instance_get_export_name (mov
->swf
,
526 SWFDEC_CHARACTER (movie
->sprite
));
528 name
= swfdec_as_context_get_string (context
, name
);
529 constructor
= swfdec_player_get_export_class (SWFDEC_PLAYER (context
),
533 if (constructor
== NULL
)
534 constructor
= SWFDEC_PLAYER (context
)->MovieClip
;
536 swfdec_as_object_set_constructor (SWFDEC_AS_OBJECT (movie
), constructor
);
540 swfdec_sprite_movie_init_movie (SwfdecMovie
*movie
)
542 swfdec_sprite_movie_do_init_movie (SWFDEC_SPRITE_MOVIE (movie
));
543 swfdec_sprite_movie_goto (SWFDEC_SPRITE_MOVIE (movie
), 1);
547 swfdec_sprite_movie_iterate (SwfdecMovie
*mov
)
549 SwfdecSpriteMovie
*movie
= SWFDEC_SPRITE_MOVIE (mov
);
550 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov
)->context
);
553 if (mov
->will_be_removed
)
556 if (movie
->sprite
!= NULL
&& movie
->frame
== 0)
557 swfdec_sprite_movie_do_init_movie (movie
);
559 swfdec_player_add_action (player
, movie
, swfdec_sprite_movie_do_enter_frame
, NULL
);
560 if (movie
->playing
&& movie
->sprite
!= NULL
) {
561 if (movie
->frame
== movie
->n_frames
)
563 else if (movie
->sprite
&& movie
->frame
== movie
->sprite
->parse_frame
)
564 goto_frame
= movie
->frame
;
566 goto_frame
= movie
->frame
+ 1;
567 swfdec_sprite_movie_goto (movie
, goto_frame
);
571 /* FIXME: This function is a mess */
573 swfdec_sprite_movie_iterate_end (SwfdecMovie
*mov
)
575 SwfdecSpriteMovie
*movie
= SWFDEC_SPRITE_MOVIE (mov
);
576 SwfdecSpriteFrame
*last
;
577 SwfdecSpriteFrame
*current
;
578 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov
)->context
);
580 if (!SWFDEC_MOVIE_CLASS (swfdec_sprite_movie_parent_class
)->iterate_end (mov
)) {
581 g_assert (movie
->sound_stream
== NULL
);
585 if (movie
->sprite
== NULL
)
587 g_assert (movie
->frame
<= movie
->n_frames
);
588 if (movie
->frame
== 0) {
589 SWFDEC_WARNING ("not at first frame yet");
592 current
= &movie
->sprite
->frames
[movie
->frame
- 1];
594 /* then do the streaming thing */
595 if (current
->sound_head
== NULL
||
597 if (movie
->sound_stream
) {
598 swfdec_audio_remove (movie
->sound_stream
);
599 g_object_unref (movie
->sound_stream
);
600 movie
->sound_stream
= NULL
;
604 if (movie
->sound_stream
== NULL
&& current
->sound_block
== NULL
)
606 SWFDEC_LOG ("iterating audio (from %u to %u)", movie
->sound_frame
, movie
->frame
);
607 if (movie
->sound_frame
+ 1 != movie
->frame
)
609 if (movie
->sound_frame
== (guint
) -1)
611 if (current
->sound_head
&& movie
->sound_stream
== NULL
)
613 last
= &movie
->sprite
->frames
[movie
->sound_frame
];
614 if (last
->sound_head
!= current
->sound_head
)
617 movie
->sound_frame
= movie
->frame
;
621 if (movie
->sound_stream
) {
622 swfdec_audio_remove (movie
->sound_stream
);
623 g_object_unref (movie
->sound_stream
);
624 movie
->sound_stream
= NULL
;
627 if (current
->sound_block
) {
628 movie
->sound_stream
= swfdec_audio_stream_new (player
,
629 movie
->sprite
, movie
->frame
- 1);
630 movie
->sound_frame
= movie
->frame
;
636 swfdec_sprite_movie_finish_movie (SwfdecMovie
*mov
)
638 SwfdecSpriteMovie
*movie
= SWFDEC_SPRITE_MOVIE (mov
);
639 SwfdecPlayer
*player
= SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov
)->context
);
641 swfdec_player_remove_all_actions (player
, mov
);
642 if (movie
->sound_stream
) {
643 swfdec_audio_remove (movie
->sound_stream
);
644 g_object_unref (movie
->sound_stream
);
645 movie
->sound_stream
= NULL
;
650 swfdec_sprite_movie_mark (SwfdecAsObject
*object
)
654 for (walk
= SWFDEC_MOVIE (object
)->list
; walk
; walk
= walk
->next
) {
655 SwfdecAsObject
*child
= walk
->data
;
656 g_assert (child
->properties
!= NULL
);
657 swfdec_as_object_mark (child
);
660 SWFDEC_AS_OBJECT_CLASS (swfdec_sprite_movie_parent_class
)->mark (object
);
664 swfdec_sprite_movie_class_init (SwfdecSpriteMovieClass
* g_class
)
666 GObjectClass
*object_class
= G_OBJECT_CLASS (g_class
);
667 SwfdecAsObjectClass
*asobject_class
= SWFDEC_AS_OBJECT_CLASS (g_class
);
668 SwfdecMovieClass
*movie_class
= SWFDEC_MOVIE_CLASS (g_class
);
670 object_class
->dispose
= swfdec_sprite_movie_dispose
;
672 asobject_class
->mark
= swfdec_sprite_movie_mark
;
674 movie_class
->init_movie
= swfdec_sprite_movie_init_movie
;
675 movie_class
->finish_movie
= swfdec_sprite_movie_finish_movie
;
676 movie_class
->iterate_start
= swfdec_sprite_movie_iterate
;
677 movie_class
->iterate_end
= swfdec_sprite_movie_iterate_end
;
681 swfdec_sprite_movie_init (SwfdecSpriteMovie
* movie
)
683 movie
->playing
= TRUE
;