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.
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_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"
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... */
43 swfdec_stroke_append_path_snapped (cairo_t
*cr
, const cairo_path_t
*path
)
45 cairo_path_data_t
*data
;
50 for (i
= 0; i
< path
->num_data
; i
++) {
51 switch (data
[i
].header
.type
) {
52 case CAIRO_PATH_MOVE_TO
:
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
);
65 case CAIRO_PATH_LINE_TO
:
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
);
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
);
92 case CAIRO_PATH_CLOSE_PATH
:
93 /* doesn't exist in our code */
95 g_assert_not_reached ();
101 swfdec_stroke_paint (SwfdecDraw
*draw
, cairo_t
*cr
, const SwfdecColorTransform
*trans
)
103 SwfdecStroke
*stroke
= SWFDEC_STROKE (draw
);
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
);
112 swfdec_stroke_append_path_snapped (cr
, &draw
->path
);
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
);
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
));
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
);
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
;
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
);
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
;
199 swfdec_stroke_init (SwfdecStroke
*stroke
)
201 SwfdecDraw
*draw
= SWFDEC_DRAW (stroke
);
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 ***/
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
);
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
);
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
)
261 return CAIRO_LINE_CAP_ROUND
;
263 return CAIRO_LINE_CAP_BUTT
;
265 return CAIRO_LINE_CAP_SQUARE
;
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
)
276 return CAIRO_LINE_JOIN_ROUND
;
278 return CAIRO_LINE_JOIN_BEVEL
;
280 return CAIRO_LINE_JOIN_MITER
;
282 SWFDEC_ERROR ("invalid line join value %u", join
);
283 return CAIRO_LINE_JOIN_ROUND
;
288 swfdec_stroke_do_parse_extended (SwfdecBits
*bits
, SwfdecSwfDecoder
*dec
, gboolean morph
)
291 gboolean has_pattern
;
292 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
294 stroke
->start_width
= swfdec_bits_get_u16 (bits
);
296 stroke
->end_width
= swfdec_bits_get_u16 (bits
);
297 SWFDEC_LOG (" width: %u => %u", stroke
->start_width
, stroke
->end_width
);
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
);
331 stroke
->pattern
= SWFDEC_PATTERN (swfdec_pattern_parse_morph (bits
, dec
));
333 stroke
->pattern
= SWFDEC_PATTERN (swfdec_pattern_parse_rgba (bits
, dec
));
336 stroke
->start_color
= swfdec_bits_get_rgba (bits
);
338 stroke
->end_color
= swfdec_bits_get_rgba (bits
);
339 SWFDEC_LOG (" color: #%08X", stroke
->start_color
);
341 stroke
->end_color
= stroke
->start_color
;
342 SWFDEC_LOG (" color: #%08X", stroke
->start_color
);
346 return SWFDEC_DRAW (stroke
);
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
);
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
);