oops, I shifted the wrong value.
[swfdec.git] / swfdec / swfdec_movie_as_drawing.c
blob7cc28a13c2ddf8f5786e0b3509868bcddc5f3e76
1 /* Swfdec
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.
8 *
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
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
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. */
39 static SwfdecDraw *
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;
47 if (sstroke->pattern)
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);
59 static void
60 swfdec_sprite_movie_end_fill (SwfdecMovie *movie, SwfdecDraw *new)
62 /* FIXME: need to cairo_close_path()? */
63 movie->draw_fill = new;
64 if (new == NULL)
65 return;
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)
80 void
81 swfdec_sprite_movie_beginFill (SwfdecAsContext *cx, SwfdecAsObject *object,
82 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
84 SwfdecMovie *movie;
85 SwfdecDraw *draw;
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])) {
92 color = 0;
93 } else {
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);
102 static guint
103 swfdec_sprite_movie_gradient_fill_get_length (SwfdecAsObject *o)
105 int length;
106 SwfdecAsValue val;
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);
113 static int
114 swfdec_sprite_movie_gradient_fill_check_length (SwfdecAsObject *colors, SwfdecAsObject *alphas, SwfdecAsObject *ratios)
116 guint c, a, r;
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)
122 return -1;
123 return c;
126 static void
127 swfdec_sprite_movie_extract_matrix (SwfdecAsObject *o, cairo_matrix_t *mat)
129 SwfdecAsContext *cx = swfdec_gc_object_get_context (o);
130 SwfdecAsValue val;
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);
154 } else {
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;
160 } else {
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)
180 void
181 swfdec_sprite_movie_beginGradientFill (SwfdecAsContext *cx, SwfdecAsObject *object,
182 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
184 SwfdecGradientPattern *gradient;
185 SwfdecPattern *pattern;
186 SwfdecMovie *movie;
187 SwfdecDraw *draw;
188 SwfdecAsObject *colors, *alphas, *ratios, *matrix;
189 const char *s;
190 gboolean radial;
191 int i, len;
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) {
197 radial = FALSE;
198 } else if (s == SWFDEC_AS_STR_radial) {
199 radial = TRUE;
200 } else {
201 SWFDEC_WARNING ("invalid fill type %s", s);
202 return;
204 len = swfdec_sprite_movie_gradient_fill_check_length (colors, alphas, ratios);
205 if (len < 0) {
206 SWFDEC_ERROR ("different lengths for colors, alphas and ratios, aborting");
207 return;
209 draw = swfdec_gradient_pattern_new ();
210 pattern = SWFDEC_PATTERN (draw);
211 gradient = SWFDEC_GRADIENT_PATTERN (draw);
212 gradient->radial = radial;
213 len = MIN (len, 8);
214 gradient->n_gradients = len;
215 for (i = 0; i < len; i++) {
216 int c, a, r;
217 SwfdecAsValue v;
218 int check = swfdec_sprite_movie_gradient_fill_check_length (colors, alphas, ratios);
219 if (check > i) {
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);
224 else
225 c = 0;
226 if (!swfdec_as_object_get_variable (alphas, name, &v)) {
227 a = c;
228 } else if (!SWFDEC_AS_VALUE_IS_NUMBER (&v)) {
229 a = 0;
230 } else {
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))
236 r = 0;
237 else
238 r = swfdec_as_value_to_integer (cx, &v);
239 } else {
240 c = a = r = 0;
242 if (r > 255 || r < 0) {
243 SWFDEC_WARNING ("ratio %d not in [0, 255], ignoring gradient", r);
244 g_object_unref (draw);
245 return;
246 } else if (r < 0) {
247 r = 0;
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)
264 void
265 swfdec_sprite_movie_moveTo (SwfdecAsContext *cx, SwfdecAsObject *object,
266 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
268 SwfdecMovie *movie;
269 double x = 0, y = 0;
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);
282 movie->draw_x = x;
283 movie->draw_y = y;
286 static void
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);
292 } else {
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)
300 void
301 swfdec_sprite_movie_lineTo (SwfdecAsContext *cx, SwfdecAsObject *object,
302 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
304 SwfdecMovie *movie;
305 double x, y;
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);
319 movie->draw_x = x;
320 movie->draw_y = y;
323 SWFDEC_AS_NATIVE (901, 5, swfdec_sprite_movie_curveTo)
324 void
325 swfdec_sprite_movie_curveTo (SwfdecAsContext *cx, SwfdecAsObject *object,
326 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
328 SwfdecMovie *movie;
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,
339 c_x, c_y, x, 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,
344 c_x, c_y, x, y);
345 swfdec_spite_movie_recompute_draw (movie, movie->draw_line);
347 movie->draw_x = x;
348 movie->draw_y = y;
351 SWFDEC_AS_NATIVE (901, 6, swfdec_sprite_movie_lineStyle)
352 void
353 swfdec_sprite_movie_lineStyle (SwfdecAsContext *cx, SwfdecAsObject *object,
354 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
356 SwfdecMovie *movie;
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]))
364 return;
365 if (argc > 3) {
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)
381 void
382 swfdec_sprite_movie_endFill (SwfdecAsContext *cx, SwfdecAsObject *object,
383 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
385 SwfdecMovie *movie;
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)
392 void
393 swfdec_sprite_movie_clear (SwfdecAsContext *cx, SwfdecAsObject *object,
394 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
396 SwfdecMovie *movie;
398 SWFDEC_AS_CHECK (SWFDEC_TYPE_MOVIE, &movie, "");
399 if (movie->draws == NULL)
400 return;
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);
406 movie->draws = NULL;
407 movie->draw_fill = NULL;
408 movie->draw_line = NULL;
411 SWFDEC_AS_NATIVE (901, 9, swfdec_sprite_movie_lineGradientStyle)
412 void
413 swfdec_sprite_movie_lineGradientStyle (SwfdecAsContext *cx,
414 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
415 SwfdecAsValue *rval)
417 SWFDEC_STUB ("MovieClip.lineGradientStyle");
420 SWFDEC_AS_NATIVE (901, 10, swfdec_sprite_movie_beginMeshFill)
421 void
422 swfdec_sprite_movie_beginMeshFill (SwfdecAsContext *cx,
423 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
424 SwfdecAsValue *rval)
426 SWFDEC_STUB ("MovieClip.beginMeshFill");
429 SWFDEC_AS_NATIVE (901, 11, swfdec_sprite_movie_beginBitmapFill)
430 void
431 swfdec_sprite_movie_beginBitmapFill (SwfdecAsContext *cx,
432 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
433 SwfdecAsValue *rval)
435 SwfdecMovie *movie;
436 SwfdecBitmapData *bitmap;
437 SwfdecPattern *pattern;
438 SwfdecDraw *draw;
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))
447 return;
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)
475 void
476 swfdec_sprite_movie_get_scale9Grid (SwfdecAsContext *cx,
477 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
478 SwfdecAsValue *rval)
480 SWFDEC_STUB ("MovieClip.scale9Grid (get)");
483 SWFDEC_AS_NATIVE (901, 13, swfdec_sprite_movie_set_scale9Grid)
484 void
485 swfdec_sprite_movie_set_scale9Grid (SwfdecAsContext *cx,
486 SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
487 SwfdecAsValue *rval)
489 SWFDEC_STUB ("MovieClip.scale9Grid (set)");