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_image.h"
35 G_DEFINE_TYPE (SwfdecStroke
, swfdec_stroke
, G_TYPE_OBJECT
);
38 swfdec_stroke_append_path_snapped (cairo_t
*cr
, const cairo_path_t
*path
)
40 cairo_path_data_t
*data
;
45 for (i
= 0; i
< path
->num_data
; i
++) {
46 switch (data
[i
].header
.type
) {
47 case CAIRO_PATH_MOVE_TO
:
51 cairo_user_to_device (cr
, &x
, &y
);
52 x
= rint (x
- 0.5) + 0.5;
53 y
= rint (y
- 0.5) + 0.5;
54 cairo_device_to_user (cr
, &x
, &y
);
55 /* FIXME: currently we need to clamp this due to extents */
56 x
= CLAMP (x
, data
[i
].point
.x
- MAX_ALIGN
, data
[i
].point
.x
+ MAX_ALIGN
);
57 y
= CLAMP (y
, data
[i
].point
.y
- MAX_ALIGN
, data
[i
].point
.y
+ MAX_ALIGN
);
58 cairo_move_to (cr
, x
, y
);
60 case CAIRO_PATH_LINE_TO
:
64 cairo_user_to_device (cr
, &x
, &y
);
65 x
= rint (x
- 0.5) + 0.5;
66 y
= rint (y
- 0.5) + 0.5;
67 cairo_device_to_user (cr
, &x
, &y
);
68 /* FIXME: currently we need to clamp this due to extents */
69 x
= CLAMP (x
, data
[i
].point
.x
- MAX_ALIGN
, data
[i
].point
.x
+ MAX_ALIGN
);
70 y
= CLAMP (y
, data
[i
].point
.y
- MAX_ALIGN
, data
[i
].point
.y
+ MAX_ALIGN
);
71 cairo_line_to (cr
, x
, y
);
73 case CAIRO_PATH_CURVE_TO
:
74 x
= data
[i
+3].point
.x
;
75 y
= data
[i
+3].point
.y
;
76 cairo_user_to_device (cr
, &x
, &y
);
77 x
= rint (x
- 0.5) + 0.5;
78 y
= rint (y
- 0.5) + 0.5;
79 cairo_device_to_user (cr
, &x
, &y
);
80 /* FIXME: currently we need to clamp this due to extents */
81 x
= CLAMP (x
, data
[i
+3].point
.x
- MAX_ALIGN
, data
[i
+3].point
.x
+ MAX_ALIGN
);
82 y
= CLAMP (y
, data
[i
+3].point
.y
- MAX_ALIGN
, data
[i
+3].point
.y
+ MAX_ALIGN
);
83 cairo_curve_to (cr
, data
[i
+1].point
.x
, data
[i
+1].point
.y
,
84 data
[i
+2].point
.x
, data
[i
+2].point
.y
, x
, y
);
87 case CAIRO_PATH_CLOSE_PATH
:
88 /* doesn't exist in our code */
90 g_assert_not_reached ();
96 swfdec_stroke_paint (SwfdecStroke
*stroke
, cairo_t
*cr
, const cairo_path_t
*path
,
97 const SwfdecColorTransform
*trans
, guint ratio
)
102 g_return_if_fail (SWFDEC_IS_STROKE (stroke
));
103 g_return_if_fail (cr
!= NULL
);
104 g_return_if_fail (path
!= NULL
);
105 g_return_if_fail (trans
!= NULL
);
106 g_return_if_fail (ratio
< 65536);
108 cairo_set_line_cap (cr
, stroke
->start_cap
);
109 cairo_set_line_join (cr
, stroke
->join
);
110 if (stroke
->join
== CAIRO_LINE_JOIN_MITER
)
111 cairo_set_miter_limit (cr
, stroke
->miter_limit
);
113 swfdec_stroke_append_path_snapped (cr
, path
);
114 color
= swfdec_color_apply_morph (stroke
->start_color
, stroke
->end_color
, ratio
);
115 color
= swfdec_color_apply_transform (color
, trans
);
116 swfdec_color_set_source (cr
, color
);
118 width
= stroke
->start_width
;
119 } else if (ratio
== 65535) {
120 width
= stroke
->end_width
;
122 width
= (stroke
->start_width
* (65535 - ratio
) + stroke
->end_width
* ratio
) / 65535;
124 if (width
< SWFDEC_TWIPS_SCALE_FACTOR
)
125 width
= SWFDEC_TWIPS_SCALE_FACTOR
;
126 cairo_set_line_width (cr
, width
);
131 swfdec_stroke_class_init (SwfdecStrokeClass
*klass
)
136 swfdec_stroke_init (SwfdecStroke
*stroke
)
138 stroke
->start_cap
= CAIRO_LINE_CAP_ROUND
;
139 stroke
->end_cap
= CAIRO_LINE_CAP_ROUND
;
140 stroke
->join
= CAIRO_LINE_JOIN_ROUND
;
143 /*** EXPORTED API ***/
146 swfdec_stroke_parse (SwfdecSwfDecoder
*dec
)
148 SwfdecBits
*bits
= &dec
->b
;
149 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
151 stroke
->start_width
= swfdec_bits_get_u16 (bits
);
152 stroke
->end_width
= stroke
->start_width
;
153 stroke
->start_color
= swfdec_bits_get_color (bits
);
154 stroke
->end_color
= stroke
->start_color
;
155 SWFDEC_LOG ("new stroke: width %u color %08x", stroke
->start_width
, stroke
->start_color
);
161 swfdec_stroke_parse_rgba (SwfdecSwfDecoder
*dec
)
163 SwfdecBits
*bits
= &dec
->b
;
164 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
166 stroke
->start_width
= swfdec_bits_get_u16 (bits
);
167 stroke
->end_width
= stroke
->start_width
;
168 stroke
->start_color
= swfdec_bits_get_rgba (bits
);
169 stroke
->end_color
= stroke
->start_color
;
170 SWFDEC_LOG ("new stroke: width %u color %08x", stroke
->start_width
, stroke
->start_color
);
176 swfdec_stroke_parse_morph (SwfdecSwfDecoder
*dec
)
178 SwfdecBits
*bits
= &dec
->b
;
179 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
181 stroke
->start_width
= swfdec_bits_get_u16 (bits
);
182 stroke
->end_width
= swfdec_bits_get_u16 (bits
);
183 stroke
->start_color
= swfdec_bits_get_rgba (bits
);
184 stroke
->end_color
= swfdec_bits_get_rgba (bits
);
185 SWFDEC_LOG ("new stroke: width %u => %u color %08X => %08X",
186 stroke
->start_width
, stroke
->end_width
,
187 stroke
->start_color
, stroke
->end_color
);
193 swfdec_stroke_new (guint width
, SwfdecColor color
)
195 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
197 stroke
->start_width
= width
;
198 stroke
->end_width
= width
;
199 stroke
->start_color
= color
;
200 stroke
->end_color
= color
;
205 static cairo_line_cap_t
206 swfdec_line_cap_get (guint cap
)
210 return CAIRO_LINE_CAP_ROUND
;
212 return CAIRO_LINE_CAP_BUTT
;
214 return CAIRO_LINE_CAP_SQUARE
;
216 SWFDEC_ERROR ("invalid line cap value %u", cap
);
217 return CAIRO_LINE_CAP_ROUND
;
220 static cairo_line_join_t
221 swfdec_line_join_get (guint join
)
225 return CAIRO_LINE_JOIN_ROUND
;
227 return CAIRO_LINE_JOIN_BEVEL
;
229 return CAIRO_LINE_JOIN_MITER
;
231 SWFDEC_ERROR ("invalid line join value %u", join
);
232 return CAIRO_LINE_JOIN_ROUND
;
236 static SwfdecStroke
*
237 swfdec_stroke_do_parse_extended (SwfdecSwfDecoder
*dec
, gboolean morph
)
239 SwfdecBits
*bits
= &dec
->b
;
241 gboolean has_pattern
;
242 SwfdecStroke
*stroke
= g_object_new (SWFDEC_TYPE_STROKE
, NULL
);
244 stroke
->start_width
= swfdec_bits_get_u16 (bits
);
246 stroke
->end_width
= swfdec_bits_get_u16 (bits
);
247 SWFDEC_LOG (" width: %u => %u", stroke
->start_width
, stroke
->end_width
);
249 stroke
->end_width
= stroke
->start_width
;
250 SWFDEC_LOG (" width: %u", stroke
->start_width
);
252 tmp
= swfdec_bits_getbits (bits
, 2);
253 SWFDEC_LOG (" start cap: %u", tmp
);
254 stroke
->start_cap
= swfdec_line_cap_get (tmp
);
255 tmp
= swfdec_bits_getbits (bits
, 2);
256 SWFDEC_LOG (" line join: %u", tmp
);
257 stroke
->join
= swfdec_line_join_get (tmp
);
258 has_pattern
= swfdec_bits_getbit (bits
);
259 SWFDEC_LOG (" has pattern: %d", has_pattern
);
260 stroke
->no_hscale
= swfdec_bits_getbit (bits
);
261 SWFDEC_LOG (" no hscale: %d", stroke
->no_hscale
);
262 stroke
->no_vscale
= swfdec_bits_getbit (bits
);
263 SWFDEC_LOG (" no vscale: %d", stroke
->no_vscale
);
264 stroke
->align_pixel
= swfdec_bits_getbit (bits
);
265 SWFDEC_LOG (" align pixels: %d", stroke
->align_pixel
);
266 tmp
= swfdec_bits_getbits (bits
, 5);
267 stroke
->no_close
= swfdec_bits_getbit (bits
);
268 SWFDEC_LOG (" no close: %d", stroke
->no_close
);
269 tmp
= swfdec_bits_getbits (bits
, 2);
270 SWFDEC_LOG (" end cap: %u", tmp
);
271 stroke
->end_cap
= swfdec_line_cap_get (tmp
);
272 if (stroke
->end_cap
!= stroke
->start_cap
) {
273 SWFDEC_WARNING ("FIXME: different caps on start and end of line are unsupported");
275 if (stroke
->join
== CAIRO_LINE_JOIN_MITER
) {
276 stroke
->miter_limit
= swfdec_bits_get_u16 (bits
);
277 SWFDEC_LOG (" miter limit: %u", stroke
->miter_limit
);
281 stroke
->pattern
= swfdec_pattern_parse_morph (dec
);
283 stroke
->pattern
= swfdec_pattern_parse_rgba (dec
);
286 stroke
->start_color
= swfdec_bits_get_rgba (bits
);
288 stroke
->end_color
= swfdec_bits_get_rgba (bits
);
289 SWFDEC_LOG (" color: #%08X", stroke
->start_color
);
291 stroke
->end_color
= stroke
->start_color
;
292 SWFDEC_LOG (" color: #%08X", stroke
->start_color
);
300 swfdec_stroke_parse_extended (SwfdecSwfDecoder
*dec
)
302 g_return_val_if_fail (SWFDEC_IS_SWF_DECODER (dec
), NULL
);
304 return swfdec_stroke_do_parse_extended (dec
, FALSE
);
308 swfdec_stroke_parse_morph_extended (SwfdecSwfDecoder
*dec
)
310 g_return_val_if_fail (SWFDEC_IS_SWF_DECODER (dec
), NULL
);
312 return swfdec_stroke_do_parse_extended (dec
, TRUE
);