actually set the variable here
[swfdec.git] / swfdec / swfdec_stroke.c
blobef56022cf329d1ffc01b5e1bc0e3943182b1206f
1 /* Swfdec
2 * Copyright (C) 2006-2007 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 <math.h>
26 #include "swfdec_stroke.h"
27 #include "swfdec_bits.h"
28 #include "swfdec_color.h"
29 #include "swfdec_debug.h"
30 #include "swfdec_decoder.h"
31 #include "swfdec_path.h"
32 #include "swfdec_renderer_internal.h"
34 #define MAX_ALIGN 10
36 G_DEFINE_TYPE (SwfdecStroke, swfdec_stroke, SWFDEC_TYPE_DRAW);
38 /* FIXME: This is wrong. Snapping only happens for vertical or horizontal lines.
39 * And the snapping shouldn't happen to the cr's device units, but to the
40 * Stage coordinate system.
41 * This would of course require this function to know that matrix... */
42 static void
43 swfdec_stroke_append_path_snapped (cairo_t *cr, const cairo_path_t *path)
45 cairo_path_data_t *data;
46 double x, y;
47 int i;
49 data = path->data;
50 for (i = 0; i < path->num_data; i++) {
51 switch (data[i].header.type) {
52 case CAIRO_PATH_MOVE_TO:
53 i++;
54 x = data[i].point.x;
55 y = data[i].point.y;
56 cairo_user_to_device (cr, &x, &y);
57 x = rint (x - 0.5) + 0.5;
58 y = rint (y - 0.5) + 0.5;
59 cairo_device_to_user (cr, &x, &y);
60 /* FIXME: currently we need to clamp this due to extents */
61 x = CLAMP (x, data[i].point.x - MAX_ALIGN, data[i].point.x + MAX_ALIGN);
62 y = CLAMP (y, data[i].point.y - MAX_ALIGN, data[i].point.y + MAX_ALIGN);
63 cairo_move_to (cr, x, y);
64 break;
65 case CAIRO_PATH_LINE_TO:
66 i++;
67 x = data[i].point.x;
68 y = data[i].point.y;
69 cairo_user_to_device (cr, &x, &y);
70 x = rint (x - 0.5) + 0.5;
71 y = rint (y - 0.5) + 0.5;
72 cairo_device_to_user (cr, &x, &y);
73 /* FIXME: currently we need to clamp this due to extents */
74 x = CLAMP (x, data[i].point.x - MAX_ALIGN, data[i].point.x + MAX_ALIGN);
75 y = CLAMP (y, data[i].point.y - MAX_ALIGN, data[i].point.y + MAX_ALIGN);
76 cairo_line_to (cr, x, y);
77 break;
78 case CAIRO_PATH_CURVE_TO:
79 x = data[i+3].point.x;
80 y = data[i+3].point.y;
81 cairo_user_to_device (cr, &x, &y);
82 x = rint (x - 0.5) + 0.5;
83 y = rint (y - 0.5) + 0.5;
84 cairo_device_to_user (cr, &x, &y);
85 /* FIXME: currently we need to clamp this due to extents */
86 x = CLAMP (x, data[i+3].point.x - MAX_ALIGN, data[i+3].point.x + MAX_ALIGN);
87 y = CLAMP (y, data[i+3].point.y - MAX_ALIGN, data[i+3].point.y + MAX_ALIGN);
88 cairo_curve_to (cr, data[i+1].point.x, data[i+1].point.y,
89 data[i+2].point.x, data[i+2].point.y, x, y);
90 i += 3;
91 break;
92 case CAIRO_PATH_CLOSE_PATH:
93 /* doesn't exist in our code */
94 default:
95 g_assert_not_reached ();
100 static void
101 swfdec_stroke_paint (SwfdecDraw *draw, cairo_t *cr, const SwfdecColorTransform *trans)
103 SwfdecStroke *stroke = SWFDEC_STROKE (draw);
104 SwfdecColor color;
106 cairo_set_line_cap (cr, stroke->start_cap);
107 cairo_set_line_join (cr, stroke->join);
108 if (stroke->join == CAIRO_LINE_JOIN_MITER)
109 cairo_set_miter_limit (cr, stroke->miter_limit);
111 if (draw->snap)
112 swfdec_stroke_append_path_snapped (cr, &draw->path);
113 else
114 cairo_append_path (cr, &draw->path);
116 if (stroke->pattern) {
117 cairo_pattern_t *pattern = swfdec_pattern_get_pattern (stroke->pattern,
118 swfdec_renderer_get (cr), trans);
119 cairo_set_source (cr, pattern);
120 cairo_pattern_destroy (pattern);
121 } else {
122 color = swfdec_color_apply_transform (stroke->start_color, trans);
123 swfdec_color_set_source (cr, color);
125 cairo_set_line_width (cr, MAX (stroke->start_width, SWFDEC_TWIPS_SCALE_FACTOR));
126 cairo_stroke (cr);
129 static void
130 swfdec_stroke_morph (SwfdecDraw *dest, SwfdecDraw *source, guint ratio)
132 SwfdecStroke *dstroke = SWFDEC_STROKE (dest);
133 SwfdecStroke *sstroke = SWFDEC_STROKE (source);
135 dstroke->start_color = swfdec_color_apply_morph (sstroke->start_color,
136 sstroke->end_color, ratio);
137 dstroke->end_color = dstroke->start_color;
138 dstroke->start_width = (sstroke->start_width * ratio +
139 sstroke->end_width * (65535 - ratio)) / 65535;
140 dstroke->end_width = dstroke->start_width;
141 if (sstroke->pattern) {
142 dstroke->pattern = SWFDEC_PATTERN (swfdec_draw_morph (
143 SWFDEC_DRAW (sstroke->pattern), ratio));
145 dstroke->start_cap = sstroke->start_cap;
146 dstroke->end_cap = sstroke->end_cap;
147 dstroke->join = sstroke->join;
148 dstroke->miter_limit = sstroke->miter_limit;
149 dstroke->no_vscale = sstroke->no_vscale;
150 dstroke->no_hscale = sstroke->no_hscale;
151 dstroke->no_close = sstroke->no_close;
153 SWFDEC_DRAW_CLASS (swfdec_stroke_parent_class)->morph (dest, source, ratio);
156 static void
157 swfdec_stroke_compute_extents (SwfdecDraw *draw)
159 guint width = SWFDEC_STROKE (draw)->start_width;
161 if (SWFDEC_STROKE (draw)->join != CAIRO_LINE_JOIN_ROUND) {
162 SWFDEC_FIXME ("work out extents computation for non-round line joins");
164 swfdec_path_get_extents (&draw->path, &draw->extents);
165 draw->extents.x0 -= width;
166 draw->extents.x1 += width;
167 draw->extents.y0 -= width;
168 draw->extents.y1 += width;
171 static gboolean
172 swfdec_stroke_contains (SwfdecDraw *draw, cairo_t *cr, double x, double y)
174 SwfdecStroke *stroke = SWFDEC_STROKE (draw);
176 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
177 cairo_set_line_cap (cr, stroke->start_cap);
178 cairo_set_line_join (cr, stroke->join);
179 if (stroke->join == CAIRO_LINE_JOIN_MITER)
180 cairo_set_miter_limit (cr, stroke->miter_limit);
181 cairo_set_line_width (cr, MAX (stroke->start_width, SWFDEC_TWIPS_SCALE_FACTOR));
182 /* FIXME: do snapping here? */
183 cairo_append_path (cr, &draw->path);
184 return cairo_in_stroke (cr, x, y);
187 static void
188 swfdec_stroke_class_init (SwfdecStrokeClass *klass)
190 SwfdecDrawClass *draw_class = SWFDEC_DRAW_CLASS (klass);
192 draw_class->morph = swfdec_stroke_morph;
193 draw_class->paint = swfdec_stroke_paint;
194 draw_class->compute_extents = swfdec_stroke_compute_extents;
195 draw_class->contains = swfdec_stroke_contains;
198 static void
199 swfdec_stroke_init (SwfdecStroke *stroke)
201 SwfdecDraw *draw = SWFDEC_DRAW (stroke);
203 draw->snap = TRUE;
205 stroke->start_cap = CAIRO_LINE_CAP_ROUND;
206 stroke->end_cap = CAIRO_LINE_CAP_ROUND;
207 stroke->join = CAIRO_LINE_JOIN_ROUND;
210 /*** EXPORTED API ***/
212 SwfdecDraw *
213 swfdec_stroke_parse (SwfdecBits *bits, SwfdecSwfDecoder *dec)
215 SwfdecStroke *stroke = g_object_new (SWFDEC_TYPE_STROKE, NULL);
217 stroke->start_width = swfdec_bits_get_u16 (bits);
218 stroke->end_width = stroke->start_width;
219 stroke->start_color = swfdec_bits_get_color (bits);
220 stroke->end_color = stroke->start_color;
221 SWFDEC_LOG ("new stroke: width %u color %08x", stroke->start_width, stroke->start_color);
223 return SWFDEC_DRAW (stroke);
226 SwfdecDraw *
227 swfdec_stroke_parse_rgba (SwfdecBits *bits, SwfdecSwfDecoder *dec)
229 SwfdecStroke *stroke = g_object_new (SWFDEC_TYPE_STROKE, NULL);
231 stroke->start_width = swfdec_bits_get_u16 (bits);
232 stroke->end_width = stroke->start_width;
233 stroke->start_color = swfdec_bits_get_rgba (bits);
234 stroke->end_color = stroke->start_color;
235 SWFDEC_LOG ("new stroke: width %u color %08x", stroke->start_width, stroke->start_color);
237 return SWFDEC_DRAW (stroke);
240 SwfdecDraw *
241 swfdec_stroke_parse_morph (SwfdecBits *bits, SwfdecSwfDecoder *dec)
243 SwfdecStroke *stroke = g_object_new (SWFDEC_TYPE_STROKE, NULL);
245 stroke->start_width = swfdec_bits_get_u16 (bits);
246 stroke->end_width = swfdec_bits_get_u16 (bits);
247 stroke->start_color = swfdec_bits_get_rgba (bits);
248 stroke->end_color = swfdec_bits_get_rgba (bits);
249 SWFDEC_LOG ("new stroke: width %u => %u color %08X => %08X",
250 stroke->start_width, stroke->end_width,
251 stroke->start_color, stroke->end_color);
253 return SWFDEC_DRAW (stroke);
256 static cairo_line_cap_t
257 swfdec_line_cap_get (guint cap)
259 switch (cap) {
260 case 0:
261 return CAIRO_LINE_CAP_ROUND;
262 case 1:
263 return CAIRO_LINE_CAP_BUTT;
264 case 2:
265 return CAIRO_LINE_CAP_SQUARE;
266 default:
267 SWFDEC_ERROR ("invalid line cap value %u", cap);
268 return CAIRO_LINE_CAP_ROUND;
271 static cairo_line_join_t
272 swfdec_line_join_get (guint join)
274 switch (join) {
275 case 0:
276 return CAIRO_LINE_JOIN_ROUND;
277 case 1:
278 return CAIRO_LINE_JOIN_BEVEL;
279 case 2:
280 return CAIRO_LINE_JOIN_MITER;
281 default:
282 SWFDEC_ERROR ("invalid line join value %u", join);
283 return CAIRO_LINE_JOIN_ROUND;
287 static SwfdecDraw *
288 swfdec_stroke_do_parse_extended (SwfdecBits *bits, SwfdecSwfDecoder *dec, gboolean morph)
290 guint tmp;
291 gboolean has_pattern;
292 SwfdecStroke *stroke = g_object_new (SWFDEC_TYPE_STROKE, NULL);
294 stroke->start_width = swfdec_bits_get_u16 (bits);
295 if (morph) {
296 stroke->end_width = swfdec_bits_get_u16 (bits);
297 SWFDEC_LOG (" width: %u => %u", stroke->start_width, stroke->end_width);
298 } else {
299 stroke->end_width = stroke->start_width;
300 SWFDEC_LOG (" width: %u", stroke->start_width);
302 tmp = swfdec_bits_getbits (bits, 2);
303 SWFDEC_LOG (" start cap: %u", tmp);
304 stroke->start_cap = swfdec_line_cap_get (tmp);
305 tmp = swfdec_bits_getbits (bits, 2);
306 SWFDEC_LOG (" line join: %u", tmp);
307 stroke->join = swfdec_line_join_get (tmp);
308 has_pattern = swfdec_bits_getbit (bits);
309 SWFDEC_LOG (" has pattern: %d", has_pattern);
310 stroke->no_hscale = swfdec_bits_getbit (bits);
311 SWFDEC_LOG (" no hscale: %d", stroke->no_hscale);
312 stroke->no_vscale = swfdec_bits_getbit (bits);
313 SWFDEC_LOG (" no vscale: %d", stroke->no_vscale);
314 SWFDEC_DRAW (stroke)->snap = swfdec_bits_getbit (bits);
315 SWFDEC_LOG (" align pixels: %d", SWFDEC_DRAW (stroke)->snap);
316 tmp = swfdec_bits_getbits (bits, 5);
317 stroke->no_close = swfdec_bits_getbit (bits);
318 SWFDEC_LOG (" no close: %d", stroke->no_close);
319 tmp = swfdec_bits_getbits (bits, 2);
320 SWFDEC_LOG (" end cap: %u", tmp);
321 stroke->end_cap = swfdec_line_cap_get (tmp);
322 if (stroke->end_cap != stroke->start_cap) {
323 SWFDEC_WARNING ("FIXME: different caps on start and end of line are unsupported");
325 if (stroke->join == CAIRO_LINE_JOIN_MITER) {
326 stroke->miter_limit = swfdec_bits_get_u16 (bits);
327 SWFDEC_LOG (" miter limit: %u", stroke->miter_limit);
329 if (has_pattern) {
330 if (morph) {
331 stroke->pattern = SWFDEC_PATTERN (swfdec_pattern_parse_morph (bits, dec));
332 } else {
333 stroke->pattern = SWFDEC_PATTERN (swfdec_pattern_parse_rgba (bits, dec));
335 } else {
336 stroke->start_color = swfdec_bits_get_rgba (bits);
337 if (morph) {
338 stroke->end_color = swfdec_bits_get_rgba (bits);
339 SWFDEC_LOG (" color: #%08X", stroke->start_color);
340 } else {
341 stroke->end_color = stroke->start_color;
342 SWFDEC_LOG (" color: #%08X", stroke->start_color);
346 return SWFDEC_DRAW (stroke);
349 SwfdecDraw *
350 swfdec_stroke_parse_extended (SwfdecBits *bits, SwfdecSwfDecoder *dec)
352 g_return_val_if_fail (bits != NULL, NULL);
353 g_return_val_if_fail (SWFDEC_IS_SWF_DECODER (dec), NULL);
355 return swfdec_stroke_do_parse_extended (bits, dec, FALSE);
358 SwfdecDraw *
359 swfdec_stroke_parse_morph_extended (SwfdecBits *bits, SwfdecSwfDecoder *dec)
361 g_return_val_if_fail (bits != NULL, NULL);
362 g_return_val_if_fail (SWFDEC_IS_SWF_DECODER (dec), NULL);
364 return swfdec_stroke_do_parse_extended (bits, dec, TRUE);