fix jsut committed crasher by rewriting startDrag action
[swfdec.git] / libswfdec / swfdec_shape.c
blobf954314df5966b1fd6c2b41af3a834be4d14a81b
1 /* Swfdec
2 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
3 * 2005-2006 Eric Anholt <eric@anholt.net>
4 * 2006-2007 Benjamin Otte <otte@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #include <math.h>
27 #include <string.h>
29 #include "swfdec_shape.h"
30 #include "swfdec.h"
31 #include "swfdec_debug.h"
32 #include "swfdec_stroke.h"
34 G_DEFINE_TYPE (SwfdecShape, swfdec_shape, SWFDEC_TYPE_GRAPHIC)
36 /*** PATHS ***/
38 static void
39 swfdec_path_init (cairo_path_t *path)
41 path->status = CAIRO_STATUS_SUCCESS;
42 path->data = NULL;
43 path->num_data = 0;
46 static void
47 swfdec_path_reset (cairo_path_t *path)
49 path->status = CAIRO_STATUS_SUCCESS;
50 g_free (path->data);
51 path->data = NULL;
52 path->num_data = 0;
55 #define swfdec_path_require_size(path, steps) \
56 swfdec_path_ensure_size ((path), (path)->num_data + steps)
57 static void
58 swfdec_path_ensure_size (cairo_path_t *path, int size)
60 #define SWFDEC_PATH_STEPS 32
61 /* round up to next multiple of SWFDEC_PATH_STEPS */
62 int current_size = path->num_data - path->num_data % SWFDEC_PATH_STEPS;
63 if (path->num_data % SWFDEC_PATH_STEPS)
64 current_size += SWFDEC_PATH_STEPS;
66 if (size % SWFDEC_PATH_STEPS)
67 size += SWFDEC_PATH_STEPS - size % SWFDEC_PATH_STEPS;
68 g_assert (current_size % SWFDEC_PATH_STEPS == 0);
69 g_assert (size % SWFDEC_PATH_STEPS == 0);
70 while (size <= current_size)
71 return;
72 SWFDEC_LOG ("extending size of %p from %u to %u", path, current_size, size);
73 path->data = g_renew (cairo_path_data_t, path->data, size);
76 static void
77 swfdec_path_move_to (cairo_path_t *path, double x, double y)
79 cairo_path_data_t *cur;
81 swfdec_path_require_size (path, 2);
82 cur = &path->data[path->num_data++];
83 cur->header.type = CAIRO_PATH_MOVE_TO;
84 cur->header.length = 2;
85 cur = &path->data[path->num_data++];
86 cur->point.x = x;
87 cur->point.y = y;
90 static void
91 swfdec_path_line_to (cairo_path_t *path, double x, double y)
93 cairo_path_data_t *cur;
95 swfdec_path_require_size (path, 2);
96 cur = &path->data[path->num_data++];
97 cur->header.type = CAIRO_PATH_LINE_TO;
98 cur->header.length = 2;
99 cur = &path->data[path->num_data++];
100 cur->point.x = x;
101 cur->point.y = y;
104 static void
105 swfdec_path_curve_to (cairo_path_t *path, double start_x, double start_y,
106 double control_x, double control_y, double end_x, double end_y)
108 cairo_path_data_t *cur;
110 swfdec_path_require_size (path, 4);
111 cur = &path->data[path->num_data++];
112 cur->header.type = CAIRO_PATH_CURVE_TO;
113 cur->header.length = 4;
114 #define WEIGHT (2.0/3.0)
115 cur = &path->data[path->num_data++];
116 cur->point.x = control_x * WEIGHT + (1-WEIGHT) * start_x;
117 cur->point.y = control_y * WEIGHT + (1-WEIGHT) * start_y;
118 cur = &path->data[path->num_data++];
119 cur->point.x = control_x * WEIGHT + (1-WEIGHT) * end_x;
120 cur->point.y = control_y * WEIGHT + (1-WEIGHT) * end_y;
121 cur = &path->data[path->num_data++];
122 cur->point.x = end_x;
123 cur->point.y = end_y;
126 static void
127 swfdec_path_append (cairo_path_t *path, const cairo_path_t *append)
129 swfdec_path_require_size (path, append->num_data);
130 memcpy (&path->data[path->num_data], append->data, sizeof (cairo_path_data_t) * append->num_data);
131 path->num_data += append->num_data;
134 static void
135 swfdec_path_append_reverse (cairo_path_t *path, const cairo_path_t *append,
136 double x, double y)
138 cairo_path_data_t *out, *in;
139 int i;
141 swfdec_path_require_size (path, append->num_data);
142 path->num_data += append->num_data;
143 out = &path->data[path->num_data - 1];
144 in = append->data;
145 for (i = 0; i < append->num_data; i++) {
146 switch (in[i].header.type) {
147 case CAIRO_PATH_LINE_TO:
148 out[-i].point.x = x;
149 out[-i].point.y = y;
150 out[-i - 1].header = in[i].header;
151 i++;
152 break;
153 case CAIRO_PATH_CURVE_TO:
154 out[-i].point.x = x;
155 out[-i].point.y = y;
156 out[-i - 3].header = in[i].header;
157 out[-i - 1].point = in[i + 1].point;
158 out[-i - 2].point = in[i + 2].point;
159 i += 3;
160 break;
161 case CAIRO_PATH_CLOSE_PATH:
162 case CAIRO_PATH_MOVE_TO:
163 /* these two don't exist in our code */
164 default:
165 g_assert_not_reached ();
167 x = in[i].point.x;
168 y = in[i].point.y;
172 /*** SUBPATH ***/
174 typedef struct {
175 int x_start, y_start;
176 int x_end, y_end;
177 cairo_path_t path;
178 guint fill0style;
179 guint fill1style;
180 guint linestyle;
181 guint max_index;
182 } SubPath;
184 /*** SHAPE ***/
186 static void
187 swfdec_shape_vec_finish (SwfdecShapeVec * shapevec)
189 if (shapevec->pattern) {
190 g_object_unref (shapevec->pattern);
191 shapevec->pattern = NULL;
193 if (shapevec->fill_cr) {
194 cairo_destroy (shapevec->fill_cr);
195 shapevec->fill_cr = NULL;
198 swfdec_path_reset (&shapevec->path);
201 static void
202 swfdec_shape_vec_init (SwfdecShapeVec *vec)
204 swfdec_path_init (&vec->path);
207 static void
208 swfdec_shape_dispose (GObject *object)
210 guint i;
211 SwfdecShape * shape = SWFDEC_SHAPE (object);
213 for (i = 0; i < shape->vecs->len; i++) {
214 swfdec_shape_vec_finish (&g_array_index (shape->vecs, SwfdecShapeVec, i));
216 g_array_free (shape->vecs, TRUE);
217 for (i = 0; i < shape->fills->len; i++) {
218 if (g_ptr_array_index (shape->fills, i))
219 g_object_unref (g_ptr_array_index (shape->fills, i));
221 g_ptr_array_free (shape->fills, TRUE);
223 for (i = 0; i < shape->lines->len; i++) {
224 if (g_ptr_array_index (shape->lines, i))
225 g_object_unref (g_ptr_array_index (shape->lines, i));
227 g_ptr_array_free (shape->lines, TRUE);
229 G_OBJECT_CLASS (swfdec_shape_parent_class)->dispose (G_OBJECT (shape));
232 static void
233 swfdec_shape_render (SwfdecGraphic *graphic, cairo_t *cr,
234 const SwfdecColorTransform *trans, const SwfdecRect *inval, gboolean fill)
236 SwfdecShape *shape = SWFDEC_SHAPE (graphic);
237 guint i;
239 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
240 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
242 for (i = 0; i < shape->vecs->len; i++) {
243 SwfdecShapeVec *vec = &g_array_index (shape->vecs, SwfdecShapeVec, i);
245 g_assert (vec->path.num_data);
246 g_assert (vec->pattern);
247 if (!swfdec_rect_intersect (NULL, &vec->extents, inval))
248 continue;
250 /* hack to not append paths for lines */
251 if (!fill && vec->last_index % 2 != 0)
252 continue;
254 if (fill) {
255 if (SWFDEC_IS_PATTERN (vec->pattern)) {
256 swfdec_pattern_paint (vec->pattern, cr, &vec->path, trans, 0);
257 } else {
258 swfdec_stroke_paint (vec->pattern, cr, &vec->path, trans, 0);
260 } else {
261 cairo_append_path (cr, &vec->path);
266 static gboolean
267 swfdec_shape_mouse_in (SwfdecGraphic *graphic, double x, double y)
269 SwfdecShapeVec *shapevec;
270 SwfdecShape *shape = SWFDEC_SHAPE (graphic);
271 static cairo_surface_t *surface = NULL;
272 guint i;
274 for (i = 0; i < shape->vecs->len; i++) {
275 shapevec = &g_array_index (shape->vecs, SwfdecShapeVec, i);
277 g_assert (shapevec->path.num_data);
278 g_assert (shapevec->pattern);
279 /* FIXME: handle strokes */
280 if (SWFDEC_IS_STROKE (shapevec->pattern))
281 continue;
282 if (shapevec->fill_cr == NULL) {
283 /* FIXME: do less memory intensive fill checking plz */
284 if (surface == NULL)
285 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1);
286 shapevec->fill_cr = cairo_create (surface);
287 cairo_set_fill_rule (shapevec->fill_cr, CAIRO_FILL_RULE_EVEN_ODD);
288 cairo_append_path (shapevec->fill_cr, &shapevec->path);
290 if (cairo_in_fill (shapevec->fill_cr, x, y))
291 return TRUE;
293 return FALSE;
296 static void
297 swfdec_shape_class_init (SwfdecShapeClass * g_class)
299 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
300 SwfdecGraphicClass *graphic_class = SWFDEC_GRAPHIC_CLASS (g_class);
302 object_class->dispose = swfdec_shape_dispose;
304 graphic_class->render = swfdec_shape_render;
305 graphic_class->mouse_in = swfdec_shape_mouse_in;
308 static void
309 swfdec_shape_init (SwfdecShape * shape)
311 shape->fills = g_ptr_array_new ();
312 shape->lines = g_ptr_array_new ();
313 shape->vecs = g_array_new (FALSE, TRUE, sizeof (SwfdecShapeVec));
316 static void
317 swfdec_shape_add_styles (SwfdecSwfDecoder * s, SwfdecShape * shape,
318 SwfdecPatternFunc parse_fill, SwfdecStrokeFunc parse_stroke)
320 int n_fill_styles;
321 int n_line_styles;
322 int i;
323 SwfdecBits *bits = &s->b;
325 swfdec_bits_syncbits (bits);
326 shape->fills_offset = shape->fills->len;
327 n_fill_styles = swfdec_bits_get_u8 (bits);
328 if (n_fill_styles == 0xff) {
329 n_fill_styles = swfdec_bits_get_u16 (bits);
331 SWFDEC_LOG (" n_fill_styles %d", n_fill_styles);
332 for (i = 0; i < n_fill_styles && swfdec_bits_left (bits); i++) {
333 SwfdecPattern *pattern;
335 SWFDEC_LOG (" fill style %d:", i);
337 pattern = parse_fill (s);
338 g_ptr_array_add (shape->fills, pattern);
341 shape->lines_offset = shape->lines->len;
342 n_line_styles = swfdec_bits_get_u8 (bits);
343 if (n_line_styles == 0xff) {
344 n_line_styles = swfdec_bits_get_u16 (bits);
346 SWFDEC_LOG (" n_line_styles %d", n_line_styles);
347 for (i = 0; i < n_line_styles && swfdec_bits_left (bits); i++) {
348 g_ptr_array_add (shape->lines, parse_stroke (s));
351 shape->n_fill_bits = swfdec_bits_getbits (bits, 4);
352 shape->n_line_bits = swfdec_bits_getbits (bits, 4);
356 tag_define_shape (SwfdecSwfDecoder * s, guint tag)
358 SwfdecBits *bits = &s->b;
359 SwfdecShape *shape;
360 int id;
362 id = swfdec_bits_get_u16 (bits);
364 shape = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_SHAPE);
365 if (!shape)
366 return SWFDEC_STATUS_OK;
368 SWFDEC_INFO ("id=%d", id);
370 swfdec_bits_get_rect (bits, &SWFDEC_GRAPHIC (shape)->extents);
372 swfdec_shape_add_styles (s, shape, swfdec_pattern_parse, swfdec_stroke_parse);
374 swfdec_shape_get_recs (s, shape, swfdec_pattern_parse, swfdec_stroke_parse);
376 return SWFDEC_STATUS_OK;
380 tag_define_shape_3 (SwfdecSwfDecoder * s, guint tag)
382 SwfdecBits *bits = &s->b;
383 SwfdecShape *shape;
384 int id;
386 id = swfdec_bits_get_u16 (bits);
387 shape = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_SHAPE);
388 if (!shape)
389 return SWFDEC_STATUS_OK;
391 swfdec_bits_get_rect (bits, &SWFDEC_GRAPHIC (shape)->extents);
393 swfdec_shape_add_styles (s, shape, swfdec_pattern_parse_rgba, swfdec_stroke_parse_rgba);
395 swfdec_shape_get_recs (s, shape, swfdec_pattern_parse_rgba, swfdec_stroke_parse_rgba);
397 return SWFDEC_STATUS_OK;
401 tag_define_shape_4 (SwfdecSwfDecoder *s, guint tag)
403 SwfdecBits *bits = &s->b;
404 SwfdecShape *shape;
405 int id;
406 SwfdecRect tmp;
407 gboolean has_scale_strokes, has_noscale_strokes;
409 id = swfdec_bits_get_u16 (bits);
410 shape = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_SHAPE);
411 if (!shape)
412 return SWFDEC_STATUS_OK;
414 swfdec_bits_get_rect (bits, &SWFDEC_GRAPHIC (shape)->extents);
415 SWFDEC_LOG (" extents: %g %g x %g %g",
416 SWFDEC_GRAPHIC (shape)->extents.x0, SWFDEC_GRAPHIC (shape)->extents.y0,
417 SWFDEC_GRAPHIC (shape)->extents.x1, SWFDEC_GRAPHIC (shape)->extents.y1);
418 swfdec_bits_get_rect (bits, &tmp);
419 SWFDEC_LOG (" extents: %g %g x %g %g",
420 tmp.x0, tmp.y0, tmp.x1, tmp.y1);
421 swfdec_bits_getbits (bits, 6);
422 has_scale_strokes = swfdec_bits_getbit (bits);
423 has_noscale_strokes = swfdec_bits_getbit (bits);
424 SWFDEC_LOG (" has scaling strokes: %d", has_scale_strokes);
425 SWFDEC_LOG (" has non-scaling strokes: %d", has_noscale_strokes);
427 swfdec_shape_add_styles (s, shape, swfdec_pattern_parse_rgba, swfdec_stroke_parse_extended);
429 swfdec_shape_get_recs (s, shape, swfdec_pattern_parse_rgba, swfdec_stroke_parse_extended);
431 return SWFDEC_STATUS_OK;
434 /* The shape creation process is a bit complicated since it requires matching
435 * the Flash model to the cairo model. Note that this code is just random
436 * guessing and nothing substantial. Otherwise I'd have a testsuite. :)
437 * It does the following steps:
438 * - Accumulate all sub path into an array of SubPath structs. a sub path is
439 * the path between 2 style change records.
440 * - For every fill style used, accumulate all paths in order of appearance.
441 * The code assumes that every fill path is closed.
442 * - Collect the lines.
443 * - Sort the array so that lines get drawn after their left and right fill,
444 * but as early as possible. SubPath.max_index and ShapeVec.last_index are
445 * used for this.
447 /* FIXME: It might be that the Flash code uses a different order, namely
448 * drawing all fills followed by all lines per style record. So everything after
449 * new styles appeared is drawn later.
450 * The current code works though, so I'm not interested in testing that idea ;)
453 static GSList *
454 swfdec_shape_accumulate_one_polygon (SwfdecShape *shape, SwfdecShapeVec *target,
455 guint style, SubPath *paths, guint start, guint paths_len, guint *max)
457 guint i;
458 GSList *found = NULL;
459 int x, y;
461 g_assert (style != 0);
462 /* paths[start] is our starting point */
463 if (paths[start].fill0style == style) {
464 paths[start].fill0style = 0;
465 } else {
466 g_assert (style == paths[start].fill1style);
467 paths[start].fill1style = 0;
469 x = paths[start].x_end;
470 y = paths[start].y_end;
471 found = g_slist_prepend (found, &paths[start]);
472 *max = start;
473 swfdec_path_move_to (&target->path, paths[start].x_start, paths[start].y_start);
474 swfdec_path_append (&target->path, &paths[start].path);
475 while (x != paths[start].x_start || y != paths[start].y_start) {
476 SWFDEC_LOG ("now looking for point %u %u", x, y);
477 for (i = start; i < paths_len; i++) {
478 if (paths[i].fill0style != style && paths[i].fill1style != style)
479 continue;
480 if (paths[i].x_start == x && paths[i].y_start == y) {
481 SWFDEC_LOG (" found it %u", i);
482 x = paths[i].x_end;
483 y = paths[i].y_end;
484 swfdec_path_append (&target->path, &paths[i].path);
485 } else if (paths[i].x_end == x && paths[i].y_end == y) {
486 SWFDEC_LOG (" found it reverse %u", i);
487 x = paths[i].x_start;
488 y = paths[i].y_start;
489 swfdec_path_append_reverse (&target->path, &paths[i].path, x, y);
490 } else {
491 continue;
493 if (paths[i].fill0style == style)
494 paths[i].fill0style = 0;
495 else
496 paths[i].fill1style = 0;
497 found = g_slist_prepend (found, &paths[i]);
498 *max = i;
499 break;
501 if (i == paths_len) {
502 SWFDEC_ERROR ("could not find a closed path for style %u, starting at %d %d", style,
503 paths[start].x_start, paths[start].y_start);
504 goto fail;
507 return found;
509 fail:
510 g_slist_free (found);
511 return NULL;
514 static void
515 swfdec_shape_accumulate_one_fill (SwfdecShape *shape, SubPath *paths,
516 guint start, guint paths_len)
518 SwfdecShapeVec *target;
519 guint i, style, max = 0, cur;
520 GSList *walk, *found = NULL;
522 g_array_set_size (shape->vecs, shape->vecs->len + 1);
523 target = &g_array_index (shape->vecs, SwfdecShapeVec, shape->vecs->len - 1);
524 swfdec_shape_vec_init (target);
525 if (paths[start].fill0style != 0)
526 style = paths[start].fill0style;
527 else
528 style = paths[start].fill1style;
530 for (i = start; i < paths_len; i++) {
531 if (paths[i].fill0style == style) {
532 walk = swfdec_shape_accumulate_one_polygon (shape, target, style,
533 paths, i, paths_len, &cur);
534 if (walk) {
535 found = g_slist_concat (found, walk);
536 max = MAX (max, cur);
537 } else {
538 goto fail;
541 if (paths[i].fill1style == style) {
542 walk = swfdec_shape_accumulate_one_polygon (shape, target, style,
543 paths, i, paths_len, &cur);
544 if (walk) {
545 found = g_slist_concat (found, walk);
546 max = MAX (max, cur);
547 } else {
548 goto fail;
552 target->last_index = 2 * max;
553 for (walk = found; walk; walk = walk->next) {
554 SubPath *sub = walk->data;
555 sub->max_index = MAX (sub->max_index, max);
557 if (style > shape->fills->len) {
558 SWFDEC_ERROR ("style index %u is too big, only %u styles available", style,
559 shape->fills->len);
560 goto fail;
561 } else {
562 target->pattern = g_ptr_array_index (shape->fills, style - 1);
563 if (target->pattern == NULL)
564 goto fail;
565 g_object_ref (target->pattern);
567 g_slist_free (found);
568 return;
570 fail:
571 g_slist_free (found);
572 swfdec_shape_vec_finish (target);
573 g_array_set_size (shape->vecs, shape->vecs->len - 1);
576 static void
577 swfdec_shape_accumulate_fills (SwfdecShape *shape, SubPath *paths, guint paths_len)
579 guint i;
581 for (i = 0; i < paths_len; i++) {
582 if (paths[i].fill0style != 0)
583 swfdec_shape_accumulate_one_fill (shape, paths, i, paths_len);
584 if (paths[i].fill1style != 0)
585 swfdec_shape_accumulate_one_fill (shape, paths, i, paths_len);
589 static void
590 swfdec_shape_accumulate_lines (SwfdecShape *shape, SubPath *paths, guint paths_len)
592 SwfdecShapeVec target = { 0, };
593 guint i;
595 for (i = 0; i < paths_len; i++) {
596 if (paths[i].linestyle == 0)
597 continue;
598 if (paths[i].linestyle > shape->lines->len) {
599 SWFDEC_ERROR ("linestyle index %u is too big, only %u styles available",
600 paths[i].linestyle, shape->lines->len);
601 continue;
604 swfdec_shape_vec_init (&target);
605 swfdec_path_move_to (&target.path, paths[i].x_start, paths[i].y_start);
606 swfdec_path_append (&target.path, &paths[i].path);
607 target.pattern = g_ptr_array_index (shape->lines, paths[i].linestyle - 1);
608 g_object_ref (target.pattern);
609 target.last_index = 2 * paths[i].max_index + 1;
610 g_array_append_val (shape->vecs, target);
614 static int
615 swfdec_shape_vec_compare (gconstpointer a, gconstpointer b)
617 return ((const SwfdecShapeVec *) a)->last_index - ((const SwfdecShapeVec *) b)->last_index;
620 typedef enum {
621 SWFDEC_SHAPE_TYPE_END,
622 SWFDEC_SHAPE_TYPE_CHANGE,
623 SWFDEC_SHAPE_TYPE_LINE,
624 SWFDEC_SHAPE_TYPE_CURVE
625 } SwfdecShapeType;
627 static SwfdecShapeType
628 swfdec_shape_peek_type (SwfdecBits *bits)
630 guint ret = swfdec_bits_peekbits (bits, 6);
632 if (ret == 0)
633 return SWFDEC_SHAPE_TYPE_END;
634 if ((ret & 0x20) == 0)
635 return SWFDEC_SHAPE_TYPE_CHANGE;
636 if ((ret & 0x10) == 0)
637 return SWFDEC_SHAPE_TYPE_CURVE;
638 return SWFDEC_SHAPE_TYPE_LINE;
641 static void
642 swfdec_shape_parse_curve (SwfdecBits *bits, SubPath *path,
643 int *x, int *y)
645 int n_bits;
646 int cur_x, cur_y;
647 int control_x, control_y;
649 if (swfdec_bits_getbits (bits, 2) != 2) {
650 g_assert_not_reached ();
653 n_bits = swfdec_bits_getbits (bits, 4) + 2;
655 cur_x = *x;
656 cur_y = *y;
658 control_x = cur_x + swfdec_bits_getsbits (bits, n_bits);
659 control_y = cur_y + swfdec_bits_getsbits (bits, n_bits);
660 SWFDEC_LOG (" control %d,%d", control_x, control_y);
662 *x = control_x + swfdec_bits_getsbits (bits, n_bits);
663 *y = control_y + swfdec_bits_getsbits (bits, n_bits);
664 SWFDEC_LOG (" anchor %d,%d", *x, *y);
665 if (path) {
666 swfdec_path_curve_to (&path->path,
667 cur_x, cur_y,
668 control_x, control_y,
669 *x, *y);
670 } else {
671 SWFDEC_ERROR ("no path to curve in");
675 static void
676 swfdec_shape_parse_line (SwfdecBits *bits, SubPath *path,
677 int *x, int *y, gboolean add_as_curve)
679 int n_bits;
680 int general_line_flag;
681 int cur_x, cur_y;
683 if (swfdec_bits_getbits (bits, 2) != 3) {
684 g_assert_not_reached ();
687 cur_x = *x;
688 cur_y = *y;
689 n_bits = swfdec_bits_getbits (bits, 4) + 2;
690 general_line_flag = swfdec_bits_getbit (bits);
691 if (general_line_flag == 1) {
692 *x += swfdec_bits_getsbits (bits, n_bits);
693 *y += swfdec_bits_getsbits (bits, n_bits);
694 } else {
695 int vert_line_flag = swfdec_bits_getbit (bits);
696 if (vert_line_flag == 0) {
697 *x += swfdec_bits_getsbits (bits, n_bits);
698 } else {
699 *y += swfdec_bits_getsbits (bits, n_bits);
702 SWFDEC_LOG (" line to %d,%d", *x, *y);
703 if (path) {
704 if (add_as_curve)
705 swfdec_path_curve_to (&path->path, cur_x, cur_y,
706 (cur_x + *x) / 2, (cur_y + *y) / 2, *x, *y);
707 else
708 swfdec_path_line_to (&path->path, *x, *y);
709 } else {
710 SWFDEC_ERROR ("no path to line in");
714 static SubPath *
715 swfdec_shape_parse_change (SwfdecSwfDecoder *s, SwfdecShape *shape, GArray *path_array, SubPath *path,
716 int *x, int *y, SwfdecPatternFunc parse_fill, SwfdecStrokeFunc parse_stroke)
718 int state_new_styles, state_line_styles, state_fill_styles1, state_fill_styles0, state_moveto;
719 SwfdecBits *bits = &s->b;
721 if (swfdec_bits_getbit (bits) != 0) {
722 g_assert_not_reached ();
725 state_new_styles = swfdec_bits_getbit (bits);
726 state_line_styles = swfdec_bits_getbit (bits);
727 state_fill_styles1 = swfdec_bits_getbit (bits);
728 state_fill_styles0 = swfdec_bits_getbit (bits);
729 state_moveto = swfdec_bits_getbit (bits);
731 if (path) {
732 path->x_end = *x;
733 path->y_end = *y;
735 g_array_set_size (path_array, path_array->len + 1);
736 path = &g_array_index (path_array, SubPath, path_array->len - 1);
737 if (path_array->len > 1)
738 *path = g_array_index (path_array, SubPath, path_array->len - 2);
739 swfdec_path_init (&path->path);
740 if (state_moveto) {
741 int n_bits = swfdec_bits_getbits (bits, 5);
742 *x = swfdec_bits_getsbits (bits, n_bits);
743 *y = swfdec_bits_getsbits (bits, n_bits);
745 SWFDEC_LOG (" moveto %d,%d", *x, *y);
747 path->x_start = *x;
748 path->y_start = *y;
749 if (state_fill_styles0) {
750 path->fill0style = swfdec_bits_getbits (bits, shape->n_fill_bits);
751 if (path->fill0style)
752 path->fill0style += shape->fills_offset;
753 SWFDEC_LOG (" * fill0style = %d", path->fill0style);
754 } else {
755 SWFDEC_LOG (" * not changing fill0style");
757 if (state_fill_styles1) {
758 path->fill1style = swfdec_bits_getbits (bits, shape->n_fill_bits);
759 if (path->fill1style)
760 path->fill1style += shape->fills_offset;
761 SWFDEC_LOG (" * fill1style = %d", path->fill1style);
762 } else {
763 SWFDEC_LOG (" * not changing fill1style");
765 if (state_line_styles) {
766 path->linestyle = swfdec_bits_getbits (bits, shape->n_line_bits);
767 if (path->linestyle)
768 path->linestyle += shape->lines_offset;
769 SWFDEC_LOG (" * linestyle = %d", path->linestyle);
770 } else {
771 SWFDEC_LOG (" * not changing linestyle");
773 if (state_new_styles) {
774 guint old_fills_offset = shape->fills_offset;
775 guint old_lines_offset = shape->lines_offset;
776 SWFDEC_LOG (" * new styles");
777 swfdec_shape_add_styles (s, shape, parse_fill, parse_stroke);
778 if (path->fill0style)
779 path->fill0style += shape->fills_offset - old_fills_offset;
780 if (path->fill1style)
781 path->fill1style += shape->fills_offset - old_fills_offset;
782 if (path->linestyle)
783 path->linestyle += shape->lines_offset - old_lines_offset;
785 return path;
788 static void
789 swfdec_shape_initialize_from_sub_paths (SwfdecShape *shape, GArray *path_array)
791 guint i;
793 #if 0
794 g_print ("\n\n");
795 for (i = 0; i < path_array->len; i++) {
796 SubPath *path = &g_array_index (path_array, SubPath, i);
797 g_print ("%d %d => %d %d - %u %u %u\n", path->x_start, path->y_start, path->x_end, path->y_end,
798 path->fill0style, path->fill1style, path->linestyle);
800 #endif
801 swfdec_shape_accumulate_fills (shape, (SubPath *) path_array->data, path_array->len);
802 swfdec_shape_accumulate_lines (shape, (SubPath *) path_array->data, path_array->len);
803 for (i = 0; i < path_array->len; i++) {
804 swfdec_path_reset (&g_array_index (path_array, SubPath, i).path);
806 g_array_free (path_array, TRUE);
808 g_array_sort (shape->vecs, swfdec_shape_vec_compare);
809 for (i = 0; i < shape->vecs->len; i++) {
810 SwfdecShapeVec *vec = &g_array_index (shape->vecs, SwfdecShapeVec, i);
811 swfdec_pattern_get_path_extents (vec->pattern, &vec->path, &vec->extents);
812 swfdec_rect_union (&SWFDEC_GRAPHIC (shape)->extents, &SWFDEC_GRAPHIC (shape)->extents, &vec->extents);
816 void
817 swfdec_shape_get_recs (SwfdecSwfDecoder * s, SwfdecShape * shape,
818 SwfdecPatternFunc pattern_func, SwfdecStrokeFunc stroke_func)
820 int x = 0, y = 0;
821 SubPath *path = NULL;
822 GArray *path_array;
823 SwfdecShapeType type;
824 SwfdecBits *bits = &s->b;
826 /* First, accumulate all sub-paths into an array */
827 path_array = g_array_new (FALSE, TRUE, sizeof (SubPath));
829 while ((type = swfdec_shape_peek_type (bits))) {
830 switch (type) {
831 case SWFDEC_SHAPE_TYPE_CHANGE:
832 path = swfdec_shape_parse_change (s, shape, path_array, path, &x, &y, pattern_func, stroke_func);
833 break;
834 case SWFDEC_SHAPE_TYPE_LINE:
835 swfdec_shape_parse_line (bits, path, &x, &y, FALSE);
836 break;
837 case SWFDEC_SHAPE_TYPE_CURVE:
838 swfdec_shape_parse_curve (bits, path, &x, &y);
839 break;
840 case SWFDEC_SHAPE_TYPE_END:
841 default:
842 g_assert_not_reached ();
843 break;
846 if (path) {
847 path->x_end = x;
848 path->y_end = y;
850 swfdec_bits_getbits (bits, 6);
851 swfdec_bits_syncbits (bits);
853 swfdec_shape_initialize_from_sub_paths (shape, path_array);
856 /*** MORPH SHAPE ***/
858 #include "swfdec_morphshape.h"
860 static SubPath *
861 swfdec_morph_shape_do_change (SwfdecBits *end_bits, SubPath *other, SwfdecMorphShape *morph,
862 GArray *path_array, SubPath *path, int *x, int *y)
864 if (path) {
865 path->x_end = *x;
866 path->y_end = *y;
868 g_array_set_size (path_array, path_array->len + 1);
869 path = &g_array_index (path_array, SubPath, path_array->len - 1);
870 *path = *other;
871 swfdec_path_init (&path->path);
872 if (swfdec_shape_peek_type (end_bits) == SWFDEC_SHAPE_TYPE_CHANGE) {
873 int state_line_styles, state_fill_styles1, state_fill_styles0, state_moveto;
875 if (swfdec_bits_getbit (end_bits) != 0) {
876 g_assert_not_reached ();
878 if (swfdec_bits_getbit (end_bits)) {
879 SWFDEC_ERROR ("new styles aren't allowed in end edges, ignoring");
881 state_line_styles = swfdec_bits_getbit (end_bits);
882 state_fill_styles1 = swfdec_bits_getbit (end_bits);
883 state_fill_styles0 = swfdec_bits_getbit (end_bits);
884 state_moveto = swfdec_bits_getbit (end_bits);
885 if (state_moveto) {
886 int n_bits = swfdec_bits_getbits (end_bits, 5);
887 *x = swfdec_bits_getsbits (end_bits, n_bits);
888 *y = swfdec_bits_getsbits (end_bits, n_bits);
890 SWFDEC_LOG (" moveto %d,%d", *x, *y);
892 if (state_fill_styles0) {
893 guint check = swfdec_bits_getbits (end_bits, morph->n_fill_bits) +
894 SWFDEC_SHAPE (morph)->fills_offset;
895 if (check != path->fill0style)
896 SWFDEC_ERROR ("end fill0style %u differs from start fill0style %u", check, path->fill0style);
898 if (state_fill_styles1) {
899 guint check = swfdec_bits_getbits (end_bits, morph->n_fill_bits) +
900 SWFDEC_SHAPE (morph)->fills_offset;
901 if (check != path->fill1style)
902 SWFDEC_ERROR ("end fill1style %u differs from start fill1style %u", check, path->fill1style);
904 if (state_line_styles) {
905 guint check = swfdec_bits_getbits (end_bits, morph->n_line_bits) +
906 SWFDEC_SHAPE (morph)->lines_offset;
907 if (check != path->linestyle)
908 SWFDEC_ERROR ("end linestyle %u differs from start linestyle %u", check, path->linestyle);
911 path->x_start = *x;
912 path->y_start = *y;
913 return path;
916 static void
917 swfdec_morph_shape_get_recs (SwfdecSwfDecoder * s, SwfdecMorphShape *morph, SwfdecBits *end_bits)
919 int start_x = 0, start_y = 0, end_x = 0, end_y = 0;
920 SubPath *start_path = NULL, *end_path = NULL;
921 GArray *start_path_array, *end_path_array, *tmp;
922 SwfdecShapeType start_type, end_type;
923 SwfdecBits *start_bits = &s->b;
924 SwfdecShape *shape = SWFDEC_SHAPE (morph);
926 /* First, accumulate all sub-paths into an array */
927 start_path_array = g_array_new (FALSE, TRUE, sizeof (SubPath));
928 end_path_array = g_array_new (FALSE, TRUE, sizeof (SubPath));
930 while ((start_type = swfdec_shape_peek_type (start_bits))) {
931 end_type = swfdec_shape_peek_type (end_bits);
932 if (end_type == SWFDEC_SHAPE_TYPE_CHANGE && start_type != SWFDEC_SHAPE_TYPE_CHANGE) {
933 SubPath *path;
934 if (start_path) {
935 start_path->x_end = start_x;
936 start_path->y_end = start_y;
938 g_array_set_size (start_path_array, start_path_array->len + 1);
939 path = &g_array_index (start_path_array, SubPath, start_path_array->len - 1);
940 if (start_path)
941 *path = *start_path;
942 start_path = path;
943 swfdec_path_init (&start_path->path);
944 start_path->x_start = start_x;
945 start_path->y_start = start_y;
946 end_path = swfdec_morph_shape_do_change (end_bits, start_path, morph, end_path_array, end_path, &end_x, &end_y);
947 continue;
949 switch (start_type) {
950 case SWFDEC_SHAPE_TYPE_CHANGE:
951 start_path = swfdec_shape_parse_change (s, shape, start_path_array, start_path, &start_x, &start_y,
952 swfdec_pattern_parse_morph, swfdec_stroke_parse_morph);
953 end_path = swfdec_morph_shape_do_change (end_bits, start_path, morph, end_path_array, end_path, &end_x, &end_y);
954 break;
955 case SWFDEC_SHAPE_TYPE_LINE:
956 if (end_type == SWFDEC_SHAPE_TYPE_LINE) {
957 swfdec_shape_parse_line (start_bits, start_path, &start_x, &start_y, FALSE);
958 swfdec_shape_parse_line (end_bits, end_path, &end_x, &end_y, FALSE);
959 } else if (end_type == SWFDEC_SHAPE_TYPE_CURVE) {
960 swfdec_shape_parse_line (start_bits, start_path, &start_x, &start_y, TRUE);
961 swfdec_shape_parse_curve (end_bits, end_path, &end_x, &end_y);
962 } else {
963 SWFDEC_ERROR ("edge type didn't match, wanted line or curve, but got %s",
964 end_type ? "change" : "end");
965 goto error;
967 break;
968 case SWFDEC_SHAPE_TYPE_CURVE:
969 swfdec_shape_parse_curve (start_bits, start_path, &start_x, &start_y);
970 if (end_type == SWFDEC_SHAPE_TYPE_LINE) {
971 swfdec_shape_parse_line (end_bits, end_path, &end_x, &end_y, TRUE);
972 } else if (end_type == SWFDEC_SHAPE_TYPE_CURVE) {
973 swfdec_shape_parse_curve (end_bits, end_path, &end_x, &end_y);
974 } else {
975 SWFDEC_ERROR ("edge type didn't match, wanted line or curve, but got %s",
976 end_type ? "change" : "end");
977 goto error;
979 break;
980 case SWFDEC_SHAPE_TYPE_END:
981 default:
982 g_assert_not_reached ();
983 break;
986 if (start_path) {
987 start_path->x_end = start_x;
988 start_path->y_end = start_y;
990 if (end_path) {
991 end_path->x_end = end_x;
992 end_path->y_end = end_y;
994 swfdec_bits_getbits (start_bits, 6);
995 swfdec_bits_syncbits (start_bits);
996 if (swfdec_bits_getbits (end_bits, 6) != 0) {
997 SWFDEC_ERROR ("end shapes are not finished when start shapes are");
999 swfdec_bits_syncbits (end_bits);
1001 error:
1002 /* FIXME: there's probably a problem if start and end paths get accumulated in
1003 * different ways, this could lead to the morphs not looking like they should.
1004 * Need a good testcase for this first though.
1005 * FIXME: Also, due to error handling, there needs to be syncing of code paths
1007 tmp = shape->vecs;
1008 shape->vecs = morph->end_vecs;
1009 swfdec_shape_initialize_from_sub_paths (shape, end_path_array);
1010 morph->end_vecs = shape->vecs;
1011 shape->vecs = tmp;
1012 swfdec_shape_initialize_from_sub_paths (shape, start_path_array);
1013 g_assert (morph->end_vecs->len == shape->vecs->len);
1017 tag_define_morph_shape (SwfdecSwfDecoder * s, guint tag)
1019 SwfdecBits end_bits;
1020 SwfdecBits *bits = &s->b;
1021 SwfdecMorphShape *morph;
1022 guint offset;
1023 int id;
1024 id = swfdec_bits_get_u16 (bits);
1026 morph = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_MORPH_SHAPE);
1027 if (!morph)
1028 return SWFDEC_STATUS_OK;
1030 SWFDEC_INFO ("id=%d", id);
1032 swfdec_bits_get_rect (bits, &SWFDEC_GRAPHIC (morph)->extents);
1033 swfdec_bits_get_rect (bits, &morph->end_extents);
1034 offset = swfdec_bits_get_u32 (bits);
1035 end_bits = *bits;
1036 if (swfdec_bits_skip_bytes (&end_bits, offset) != offset) {
1037 SWFDEC_ERROR ("wrong offset in DefineMorphShape");
1038 return SWFDEC_STATUS_OK;
1040 bits->end = end_bits.ptr;
1042 swfdec_shape_add_styles (s, SWFDEC_SHAPE (morph),
1043 swfdec_pattern_parse_morph, swfdec_stroke_parse_morph);
1045 morph->n_fill_bits = swfdec_bits_getbits (&end_bits, 4);
1046 morph->n_line_bits = swfdec_bits_getbits (&end_bits, 4);
1047 SWFDEC_LOG ("%u fill bits, %u line bits in end shape", morph->n_fill_bits, morph->n_line_bits);
1049 swfdec_morph_shape_get_recs (s, morph, &end_bits);
1050 if (swfdec_bits_left (&s->b)) {
1051 SWFDEC_WARNING ("early finish when parsing start shapes: %u bytes",
1052 swfdec_bits_left (&s->b));
1055 s->b = end_bits;
1057 return SWFDEC_STATUS_OK;