2 * Copyright (C) 2007-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
24 #include "swfdec_movie.h"
25 #include "swfdec_as_internal.h"
26 #include "swfdec_as_strings.h"
27 #include "swfdec_bitmap_pattern.h"
28 #include "swfdec_color.h"
29 #include "swfdec_debug.h"
30 #include "swfdec_gradient_pattern.h"
31 #include "swfdec_path.h"
32 #include "swfdec_pattern.h"
33 #include "swfdec_stroke.h"
34 #include "swfdec_utils.h"
36 /* FIXME: This whole code assumes it works for MovieClip, Button and TextField
37 * objects. If it only works for MovieClip objects, fix this. */
40 swfdec_stroke_copy (SwfdecDraw
*draw
)
42 SwfdecStroke
*sstroke
= SWFDEC_STROKE (draw
);
43 SwfdecStroke
*dstroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
45 dstroke
->start_width
= sstroke
->start_width
;
46 dstroke
->start_color
= sstroke
->start_color
;
48 dstroke
->pattern
= g_object_ref (sstroke
->pattern
);
49 dstroke
->start_cap
= sstroke
->start_cap
;
50 dstroke
->end_cap
= sstroke
->end_cap
;
51 dstroke
->join
= sstroke
->join
;
52 dstroke
->miter_limit
= sstroke
->miter_limit
;
53 dstroke
->no_vscale
= sstroke
->no_vscale
;
54 dstroke
->no_hscale
= sstroke
->no_hscale
;
56 return SWFDEC_DRAW (dstroke
);
60 swfdec_sprite_movie_end_fill (SwfdecMovie
*movie
, SwfdecDraw
*new)
62 /* FIXME: need to cairo_close_path()? */
63 movie
->draw_fill
= new;
67 movie
->draws
= g_slist_append (movie
->draws
, new);
69 /* need to begin a new line segment to ensure proper stacking order */
70 if (movie
->draw_line
) {
71 movie
->draw_line
= swfdec_stroke_copy (movie
->draw_line
);
72 movie
->draws
= g_slist_append (movie
->draws
, movie
->draw_line
);
76 #define SWFDEC_COLOR_FROM_COLOR_ALPHA(color, alpha) \
77 (((color) & 0xFFFFFF) | SWFDEC_COLOR_COMBINE (0, 0, 0, CLAMP ((alpha), 0, 100) * 255 / 100))
79 SWFDEC_AS_NATIVE (901, 1, swfdec_sprite_movie_beginFill
)
81 swfdec_sprite_movie_beginFill (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
82 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
86 int color
, alpha
= 100;
88 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "|ii", &color
, &alpha
);
89 movie
->draw_fill
= NULL
;
91 if (argc
== 0 || SWFDEC_AS_VALUE_IS_UNDEFINED (&argv
[0])) {
94 color
= color
& 0xFFFFFF;
95 color
= SWFDEC_COLOR_FROM_COLOR_ALPHA (color
, alpha
);
97 draw
= SWFDEC_DRAW (swfdec_pattern_new_color (color
));
98 swfdec_path_move_to (&draw
->path
, movie
->draw_x
, movie
->draw_y
);
99 swfdec_sprite_movie_end_fill (movie
, draw
);
103 swfdec_sprite_movie_gradient_fill_get_length (SwfdecAsObject
*o
)
108 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_length
, &val
);
109 length
= swfdec_as_value_to_integer (swfdec_gc_object_get_context (o
), &val
);
110 return MAX (length
, 0);
114 swfdec_sprite_movie_gradient_fill_check_length (SwfdecAsObject
*colors
, SwfdecAsObject
*alphas
, SwfdecAsObject
*ratios
)
118 c
= swfdec_sprite_movie_gradient_fill_get_length (colors
);
119 a
= swfdec_sprite_movie_gradient_fill_get_length (alphas
);
120 r
= swfdec_sprite_movie_gradient_fill_get_length (ratios
);
121 if (c
!= a
|| a
!= r
)
127 swfdec_sprite_movie_extract_matrix (SwfdecAsObject
*o
, cairo_matrix_t
*mat
)
129 SwfdecAsContext
*cx
= swfdec_gc_object_get_context (o
);
132 /* FIXME: This function does not call valueOf in the right order */
133 if (swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_matrixType
, &val
)) {
134 const char *s
= swfdec_as_value_to_string (cx
, &val
);
135 cairo_matrix_init_translate (mat
, SWFDEC_TWIPS_SCALE_FACTOR
/ 2.0, SWFDEC_TWIPS_SCALE_FACTOR
/ 2.0);
136 cairo_matrix_scale (mat
, SWFDEC_TWIPS_SCALE_FACTOR
/ 32768.0, SWFDEC_TWIPS_SCALE_FACTOR
/ 32768.0);
137 if (s
== SWFDEC_AS_STR_box
) {
138 double x
, y
, w
, h
, r
;
139 cairo_matrix_t input
;
140 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_x
, &val
);
141 x
= swfdec_as_value_to_number (cx
, &val
);
142 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_y
, &val
);
143 y
= swfdec_as_value_to_number (cx
, &val
);
144 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_w
, &val
);
145 w
= swfdec_as_value_to_number (cx
, &val
);
146 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_h
, &val
);
147 h
= swfdec_as_value_to_number (cx
, &val
);
148 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_r
, &val
);
149 r
= swfdec_as_value_to_number (cx
, &val
);
150 cairo_matrix_init_translate (&input
, (x
+ w
) / 2, (y
+ h
) / 2);
151 cairo_matrix_scale (&input
, w
, h
);
152 cairo_matrix_rotate (&input
, r
);
153 cairo_matrix_multiply (mat
, mat
, &input
);
155 SWFDEC_WARNING ("my friend, there's no other matrixType than \"box\"");
157 } else if (cx
->version
>= 8 && swfdec_matrix_from_as_object (mat
, o
)) {
158 mat
->x0
*= SWFDEC_TWIPS_SCALE_FACTOR
;
159 mat
->y0
*= SWFDEC_TWIPS_SCALE_FACTOR
;
161 cairo_matrix_t input
;
162 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_a
, &val
);
163 input
.xx
= swfdec_as_value_to_number (cx
, &val
);
164 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_b
, &val
);
165 input
.yx
= swfdec_as_value_to_number (cx
, &val
);
166 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_d
, &val
);
167 input
.xy
= swfdec_as_value_to_number (cx
, &val
);
168 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_e
, &val
);
169 input
.yy
= swfdec_as_value_to_number (cx
, &val
);
170 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_g
, &val
);
171 input
.x0
= swfdec_as_value_to_number (cx
, &val
) * SWFDEC_TWIPS_SCALE_FACTOR
;
172 swfdec_as_object_get_variable (o
, SWFDEC_AS_STR_h
, &val
);
173 input
.y0
= swfdec_as_value_to_number (cx
, &val
) * SWFDEC_TWIPS_SCALE_FACTOR
;
174 cairo_matrix_init_scale (mat
, SWFDEC_TWIPS_SCALE_FACTOR
/ 32768.0, SWFDEC_TWIPS_SCALE_FACTOR
/ 32768.0);
175 cairo_matrix_multiply (mat
, mat
, &input
);
179 SWFDEC_AS_NATIVE (901, 2, swfdec_sprite_movie_beginGradientFill
)
181 swfdec_sprite_movie_beginGradientFill (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
182 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
184 SwfdecGradientPattern
*gradient
;
185 SwfdecPattern
*pattern
;
188 SwfdecAsObject
*colors
, *alphas
, *ratios
, *matrix
;
193 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "soooo", &s
, &colors
, &alphas
, &ratios
, &matrix
);
194 movie
->draw_fill
= NULL
;
196 if (s
== SWFDEC_AS_STR_linear
) {
198 } else if (s
== SWFDEC_AS_STR_radial
) {
201 SWFDEC_WARNING ("invalid fill type %s", s
);
204 len
= swfdec_sprite_movie_gradient_fill_check_length (colors
, alphas
, ratios
);
206 SWFDEC_ERROR ("different lengths for colors, alphas and ratios, aborting");
209 draw
= swfdec_gradient_pattern_new ();
210 pattern
= SWFDEC_PATTERN (draw
);
211 gradient
= SWFDEC_GRADIENT_PATTERN (draw
);
212 gradient
->radial
= radial
;
214 gradient
->n_gradients
= len
;
215 for (i
= 0; i
< len
; i
++) {
218 int check
= swfdec_sprite_movie_gradient_fill_check_length (colors
, alphas
, ratios
);
220 const char *name
= swfdec_as_integer_to_string (cx
, i
);
221 if (swfdec_as_object_get_variable (colors
, name
, &v
)
222 && SWFDEC_AS_VALUE_IS_NUMBER (&v
))
223 c
= swfdec_as_value_to_integer (cx
, &v
);
226 if (!swfdec_as_object_get_variable (alphas
, name
, &v
)) {
228 } else if (!SWFDEC_AS_VALUE_IS_NUMBER (&v
)) {
231 a
= swfdec_as_value_to_integer (cx
, &v
);
233 if (!swfdec_as_object_get_variable (ratios
, name
, &v
))
234 r
= CLAMP (a
, 0, 255);
235 else if (!SWFDEC_AS_VALUE_IS_NUMBER (&v
))
238 r
= swfdec_as_value_to_integer (cx
, &v
);
242 if (r
> 255 || r
< 0) {
243 SWFDEC_WARNING ("ratio %d not in [0, 255], ignoring gradient", r
);
244 g_object_unref (draw
);
249 gradient
->gradient
[i
].color
= SWFDEC_COLOR_FROM_COLOR_ALPHA (c
, a
);
250 gradient
->gradient
[i
].ratio
= r
;
252 swfdec_sprite_movie_extract_matrix (matrix
, &pattern
->start_transform
);
253 pattern
->transform
= pattern
->start_transform
;
254 if (cairo_matrix_invert (&pattern
->transform
)) {
255 SWFDEC_ERROR ("gradient transform matrix not invertible, resetting");
256 cairo_matrix_init_identity (&pattern
->transform
);
259 swfdec_path_move_to (&draw
->path
, movie
->draw_x
, movie
->draw_y
);
260 swfdec_sprite_movie_end_fill (movie
, draw
);
263 SWFDEC_AS_NATIVE (901, 3, swfdec_sprite_movie_moveTo
)
265 swfdec_sprite_movie_moveTo (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
266 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
271 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "|nn", &x
, &y
);
273 x
= SWFDEC_DOUBLE_TO_TWIPS (x
);
274 y
= SWFDEC_DOUBLE_TO_TWIPS (y
);
275 /* NB: moves do not extend extents */
276 if (movie
->draw_fill
) {
277 swfdec_path_move_to (&movie
->draw_fill
->path
, x
, y
);
279 if (movie
->draw_line
) {
280 swfdec_path_move_to (&movie
->draw_line
->path
, x
, y
);
287 swfdec_spite_movie_recompute_draw (SwfdecMovie
*movie
, SwfdecDraw
*draw
)
289 swfdec_draw_recompute (draw
);
290 if (swfdec_rect_inside (&movie
->draw_extents
, &draw
->extents
)) {
291 swfdec_movie_invalidate_last (movie
);
293 swfdec_movie_invalidate_next (movie
);
294 swfdec_rect_union (&movie
->draw_extents
, &movie
->draw_extents
, &draw
->extents
);
295 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
299 SWFDEC_AS_NATIVE (901, 4, swfdec_sprite_movie_lineTo
)
301 swfdec_sprite_movie_lineTo (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
302 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
307 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "nn", &x
, &y
);
309 x
= SWFDEC_DOUBLE_TO_TWIPS (x
);
310 y
= SWFDEC_DOUBLE_TO_TWIPS (y
);
311 if (movie
->draw_fill
) {
312 swfdec_path_line_to (&movie
->draw_fill
->path
, x
, y
);
313 swfdec_spite_movie_recompute_draw (movie
, movie
->draw_fill
);
315 if (movie
->draw_line
) {
316 swfdec_path_line_to (&movie
->draw_line
->path
, x
, y
);
317 swfdec_spite_movie_recompute_draw (movie
, movie
->draw_line
);
323 SWFDEC_AS_NATIVE (901, 5, swfdec_sprite_movie_curveTo
)
325 swfdec_sprite_movie_curveTo (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
326 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
329 double x
, y
, c_x
, c_y
;
331 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "nnnn", &c_x
, &c_y
, &x
, &y
);
333 x
= SWFDEC_DOUBLE_TO_TWIPS (x
);
334 y
= SWFDEC_DOUBLE_TO_TWIPS (y
);
335 c_x
= SWFDEC_DOUBLE_TO_TWIPS (c_x
);
336 c_y
= SWFDEC_DOUBLE_TO_TWIPS (c_y
);
337 if (movie
->draw_fill
) {
338 swfdec_path_curve_to (&movie
->draw_fill
->path
, movie
->draw_x
, movie
->draw_y
,
340 swfdec_spite_movie_recompute_draw (movie
, movie
->draw_fill
);
342 if (movie
->draw_line
) {
343 swfdec_path_curve_to (&movie
->draw_line
->path
, movie
->draw_x
, movie
->draw_y
,
345 swfdec_spite_movie_recompute_draw (movie
, movie
->draw_line
);
351 SWFDEC_AS_NATIVE (901, 6, swfdec_sprite_movie_lineStyle
)
353 swfdec_sprite_movie_lineStyle (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
354 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
357 SwfdecStroke
*stroke
;
358 int width
, color
= 0, alpha
= 100;
360 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "i|ii", &width
, &color
, &alpha
);
362 movie
->draw_line
= NULL
;
363 if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv
[0]))
366 SWFDEC_FIXME ("implement Flash 8 arguments to lineStyle");
368 color
= color
& 0xFFFFFF;
369 alpha
= CLAMP (alpha
, 0, 100);
370 alpha
= SWFDEC_COLOR_COMBINE (0, 0, 0, alpha
* 255 / 100);
371 color
= color
| alpha
;
372 stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
373 stroke
->start_color
= color
;
374 stroke
->start_width
= SWFDEC_DOUBLE_TO_TWIPS (width
);
375 movie
->draw_line
= SWFDEC_DRAW (stroke
);
376 swfdec_path_move_to (&movie
->draw_line
->path
, movie
->draw_x
, movie
->draw_y
);
377 movie
->draws
= g_slist_append (movie
->draws
, movie
->draw_line
);
380 SWFDEC_AS_NATIVE (901, 7, swfdec_sprite_movie_endFill
)
382 swfdec_sprite_movie_endFill (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
383 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
387 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "");
388 swfdec_sprite_movie_end_fill (movie
, NULL
);
391 SWFDEC_AS_NATIVE (901, 8, swfdec_sprite_movie_clear
)
393 swfdec_sprite_movie_clear (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
394 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*rval
)
398 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "");
399 if (movie
->draws
== NULL
)
401 swfdec_movie_invalidate_last (movie
);
402 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_EXTENTS
);
403 swfdec_rect_init_empty (&movie
->draw_extents
);
404 g_slist_foreach (movie
->draws
, (GFunc
) g_object_unref
, NULL
);
405 g_slist_free (movie
->draws
);
407 movie
->draw_fill
= NULL
;
408 movie
->draw_line
= NULL
;
411 SWFDEC_AS_NATIVE (901, 9, swfdec_sprite_movie_lineGradientStyle
)
413 swfdec_sprite_movie_lineGradientStyle (SwfdecAsContext
*cx
,
414 SwfdecAsObject
*object
, guint argc
, SwfdecAsValue
*argv
,
417 SWFDEC_STUB ("MovieClip.lineGradientStyle");
420 SWFDEC_AS_NATIVE (901, 10, swfdec_sprite_movie_beginMeshFill
)
422 swfdec_sprite_movie_beginMeshFill (SwfdecAsContext
*cx
,
423 SwfdecAsObject
*object
, guint argc
, SwfdecAsValue
*argv
,
426 SWFDEC_STUB ("MovieClip.beginMeshFill");
429 SWFDEC_AS_NATIVE (901, 11, swfdec_sprite_movie_beginBitmapFill
)
431 swfdec_sprite_movie_beginBitmapFill (SwfdecAsContext
*cx
,
432 SwfdecAsObject
*object
, guint argc
, SwfdecAsValue
*argv
,
436 SwfdecBitmapData
*bitmap
;
437 SwfdecPattern
*pattern
;
439 SwfdecAsObject
*mat
= NULL
;
440 gboolean repeat
= TRUE
;
441 gboolean smoothing
= FALSE
;
443 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE
, &movie
, "O|Obb",
444 &bitmap
, &mat
, &repeat
, &smoothing
);
445 movie
->draw_fill
= NULL
;
446 if (!SWFDEC_IS_BITMAP_DATA (bitmap
))
449 pattern
= swfdec_bitmap_pattern_new (bitmap
);
450 /* NB: This signal assumes that the pattern is destroyed before the movie is,
451 * because it is never removed anywhere */
452 g_signal_connect_swapped (pattern
, "invalidate", G_CALLBACK (swfdec_movie_invalidate_last
), movie
);
454 if (mat
!= NULL
&& !swfdec_matrix_from_as_object (&pattern
->start_transform
, mat
))
455 cairo_matrix_init_identity (&pattern
->start_transform
);
456 cairo_matrix_scale (&pattern
->start_transform
, SWFDEC_TWIPS_SCALE_FACTOR
, SWFDEC_TWIPS_SCALE_FACTOR
);
457 pattern
->start_transform
.x0
*= SWFDEC_TWIPS_SCALE_FACTOR
;
458 pattern
->start_transform
.y0
*= SWFDEC_TWIPS_SCALE_FACTOR
;
459 pattern
->transform
= pattern
->start_transform
;
460 if (cairo_matrix_invert (&pattern
->transform
) != CAIRO_STATUS_SUCCESS
) {
461 SWFDEC_ERROR ("non-invertible matrix used for transform");
462 cairo_matrix_init_scale (&pattern
->transform
, 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
,
463 1.0 / SWFDEC_TWIPS_SCALE_FACTOR
);
465 /* FIXME: or use FAST/GOOD? */
466 SWFDEC_BITMAP_PATTERN (pattern
)->filter
= smoothing
? CAIRO_FILTER_BILINEAR
: CAIRO_FILTER_NEAREST
;
467 SWFDEC_BITMAP_PATTERN (pattern
)->extend
= repeat
? CAIRO_EXTEND_REPEAT
: CAIRO_EXTEND_PAD
;
469 draw
= SWFDEC_DRAW (pattern
);
470 swfdec_path_move_to (&draw
->path
, movie
->draw_x
, movie
->draw_y
);
471 swfdec_sprite_movie_end_fill (movie
, draw
);
474 SWFDEC_AS_NATIVE (901, 12, swfdec_sprite_movie_get_scale9Grid
)
476 swfdec_sprite_movie_get_scale9Grid (SwfdecAsContext
*cx
,
477 SwfdecAsObject
*object
, guint argc
, SwfdecAsValue
*argv
,
480 SWFDEC_STUB ("MovieClip.scale9Grid (get)");
483 SWFDEC_AS_NATIVE (901, 13, swfdec_sprite_movie_set_scale9Grid
)
485 swfdec_sprite_movie_set_scale9Grid (SwfdecAsContext
*cx
,
486 SwfdecAsObject
*object
, guint argc
, SwfdecAsValue
*argv
,
489 SWFDEC_STUB ("MovieClip.scale9Grid (set)");