1use crate::complete::gradient::parse_gradient;
2use crate::streaming::basic_data_types::{
3 do_parse_i32_bits, do_parse_u16_bits, do_parse_u32_bits, parse_bool_bits, parse_i32_bits, parse_le_fixed8_p8,
4 parse_matrix, parse_s_rgb8, parse_straight_s_rgba8, parse_u16_bits,
5};
6use nom::number::complete::{le_u16 as parse_le_u16, le_u8 as parse_u8};
7use nom::{IResult as NomResult, Needed};
8use std::convert::TryFrom;
9use swf_types as swf;
10
11#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
12pub enum ShapeVersion {
13 Shape1,
14 Shape2,
15 Shape3,
16 Shape4,
17}
18
19pub fn parse_glyph(input: &[u8]) -> NomResult<&[u8], swf::Glyph> {
20 use nom::bits::bits;
21 bits(parse_glyph_bits)(input)
22}
23
24pub fn parse_glyph_bits(input: (&[u8], usize)) -> NomResult<(&[u8], usize), swf::Glyph> {
25 use nom::combinator::map;
26
27 let (input, fill) = map(do_parse_u32_bits(4), |x| usize::try_from(x).unwrap())(input)?;
28 let (input, line) = map(do_parse_u32_bits(4), |x| usize::try_from(x).unwrap())(input)?;
29 let style_bits = StyleBits { fill, line };
30 let (input, records) = parse_shape_record_string_bits(input, style_bits, ShapeVersion::Shape1)?;
31
32 Ok((input, swf::Glyph { records }))
33}
34
35pub fn parse_shape(input: &[u8], version: ShapeVersion) -> NomResult<&[u8], swf::Shape> {
36 use nom::bits::bits;
37 bits(|i| parse_shape_bits(i, version))(input)
38}
39
40pub fn parse_shape_bits(input: (&[u8], usize), version: ShapeVersion) -> NomResult<(&[u8], usize), swf::Shape> {
41 let (input, styles) = parse_shape_styles_bits(input, version)?;
42 let (input, records) = parse_shape_record_string_bits(input, styles.bits, version)?;
43
44 Ok((
45 input,
46 swf::Shape {
47 initial_styles: swf::ShapeStyles {
48 fill: styles.fill,
49 line: styles.line,
50 },
51 records,
52 },
53 ))
54}
55
56pub struct ShapeStyles {
58 pub fill: Vec<swf::FillStyle>,
59 pub line: Vec<swf::LineStyle>,
60 pub bits: StyleBits,
61}
62
63#[derive(Copy, Clone)]
64pub struct StyleBits {
65 pub fill: usize,
66 pub line: usize,
67}
68
69pub fn parse_shape_styles_bits(input: (&[u8], usize), version: ShapeVersion) -> NomResult<(&[u8], usize), ShapeStyles> {
70 use nom::bits::bytes;
71 use nom::combinator::map;
72
73 let (input, fill) = bytes(|i| parse_fill_style_list(i, version))(input)?;
74 let (input, line) = bytes(|i| parse_line_style_list(i, version))(input)?;
75 let (input, fill_bits) = map(do_parse_u32_bits(4), |x| usize::try_from(x).unwrap())(input)?;
76 let (input, line_bits) = map(do_parse_u32_bits(4), |x| usize::try_from(x).unwrap())(input)?;
77
78 Ok((
79 input,
80 ShapeStyles {
81 fill,
82 line,
83 bits: StyleBits {
84 fill: fill_bits,
85 line: line_bits,
86 },
87 },
88 ))
89}
90
91pub fn parse_shape_record_string_bits(
92 input: (&[u8], usize),
93 mut style_bits: StyleBits,
94 version: ShapeVersion,
95) -> NomResult<(&[u8], usize), Vec<swf::ShapeRecord>> {
96 let mut result: Vec<swf::ShapeRecord> = Vec::new();
97 let mut current_input = input;
98
99 loop {
100 match parse_u16_bits(current_input, 6) {
101 Ok((next_input, record_head)) => {
102 if record_head == 0 {
103 current_input = next_input;
104 break;
105 }
106 }
107 Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
108 Err(e) => return Err(e),
109 };
110
111 let is_edge = match parse_bool_bits(current_input) {
112 Ok((next_input, is_edge)) => {
113 current_input = next_input;
114 is_edge
115 }
116 Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
117 Err(e) => return Err(e),
118 };
119
120 if is_edge {
121 let is_straight_edge = match parse_bool_bits(current_input) {
122 Ok((next_input, is_straight_edge)) => {
123 current_input = next_input;
124 is_straight_edge
125 }
126 Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
127 Err(e) => return Err(e),
128 };
129 let (next_input, edge) = if is_straight_edge {
130 parse_straight_edge_bits(current_input)?
131 } else {
132 parse_curved_edge_bits(current_input)?
133 };
134 current_input = next_input;
135 result.push(swf::ShapeRecord::Edge(edge));
136 } else {
137 let (next_input, (style_change, next_style_bits)) = parse_style_change_bits(current_input, style_bits, version)?;
138 style_bits = next_style_bits;
139 result.push(swf::ShapeRecord::StyleChange(style_change));
140 current_input = next_input;
141 }
142 }
143
144 Ok((current_input, result))
145}
146
147pub fn parse_curved_edge_bits(input: (&[u8], usize)) -> NomResult<(&[u8], usize), swf::shape_records::Edge> {
148 let (input, n_bits) = parse_u16_bits(input, 4).map(|(i, x)| (i, (x as usize) + 2))?;
149 let (input, control_x) = parse_i32_bits(input, n_bits)?;
150 let (input, control_y) = parse_i32_bits(input, n_bits)?;
151 let (input, anchor_x) = parse_i32_bits(input, n_bits)?;
152 let (input, anchor_y) = parse_i32_bits(input, n_bits)?;
153
154 Ok((
155 input,
156 swf::shape_records::Edge {
157 delta: swf::Vector2D {
158 x: control_x + anchor_x,
159 y: control_y + anchor_y,
160 },
161 control_delta: Some(swf::Vector2D {
162 x: control_x,
163 y: control_y,
164 }),
165 },
166 ))
167}
168
169pub fn parse_straight_edge_bits(input: (&[u8], usize)) -> NomResult<(&[u8], usize), swf::shape_records::Edge> {
170 use nom::combinator::{cond, map};
171
172 let (input, n_bits) = map(do_parse_u16_bits(4), |x| (x as usize) + 2)(input)?;
173 let (input, is_diagonal) = parse_bool_bits(input)?;
174 let (input, is_vertical) = if is_diagonal {
175 (input, false)
176 } else {
177 parse_bool_bits(input)?
178 };
179 let (input, delta_x) = map(
180 cond(is_diagonal || !is_vertical, do_parse_i32_bits(n_bits)),
181 Option::unwrap_or_default,
182 )(input)?;
183 let (input, delta_y) = map(
184 cond(is_diagonal || is_vertical, do_parse_i32_bits(n_bits)),
185 Option::unwrap_or_default,
186 )(input)?;
187
188 Ok((
189 input,
190 swf::shape_records::Edge {
191 delta: swf::Vector2D { x: delta_x, y: delta_y },
192 control_delta: None,
193 },
194 ))
195}
196
197pub fn parse_style_change_bits(
198 input: (&[u8], usize),
199 style_bits: StyleBits,
200 version: ShapeVersion,
201) -> NomResult<(&[u8], usize), (swf::shape_records::StyleChange, StyleBits)> {
202 use nom::combinator::cond;
203
204 let (input, has_new_styles) = parse_bool_bits(input)?;
205 let (input, change_line_style) = parse_bool_bits(input)?;
206 let (input, change_right_fill) = parse_bool_bits(input)?;
207 let (input, change_left_fill) = parse_bool_bits(input)?;
208 let (input, has_move_to) = parse_bool_bits(input)?;
209 let (input, move_to) = if has_move_to {
210 let (input, move_to_bits) = parse_u16_bits(input, 5)?;
211 let (input, x) = parse_i32_bits(input, move_to_bits as usize)?;
212 let (input, y) = parse_i32_bits(input, move_to_bits as usize)?;
213 (input, Some(swf::Vector2D { x, y }))
214 } else {
215 (input, None)
216 };
217 let (input, left_fill) = cond(change_left_fill, do_parse_u16_bits(style_bits.fill))(input)?;
218 let (input, right_fill) = cond(change_right_fill, do_parse_u16_bits(style_bits.fill))(input)?;
219 let (input, line_style) = cond(change_line_style, do_parse_u16_bits(style_bits.line))(input)?;
220 let (input, (new_styles, next_style_bits)) = if has_new_styles {
221 let (input, styles) = parse_shape_styles_bits(input, version)?;
222 (
223 input,
224 (
225 Some(swf::ShapeStyles {
226 fill: styles.fill,
227 line: styles.line,
228 }),
229 styles.bits,
230 ),
231 )
232 } else {
233 (input, (None, style_bits))
234 };
235
236 Ok((
237 input,
238 (
239 swf::shape_records::StyleChange {
240 move_to,
241 left_fill: left_fill.map(usize::from),
242 right_fill: right_fill.map(usize::from),
243 line_style: line_style.map(usize::from),
244 new_styles,
245 },
246 next_style_bits,
247 ),
248 ))
249}
250
251pub fn parse_list_length(input: &[u8], allow_extended: bool) -> NomResult<&[u8], usize> {
252 let (remaining_input, u8_len) = parse_u8(input)?;
253 if u8_len == 0xff && allow_extended {
254 parse_le_u16(remaining_input).map(|(i, x)| (i, x as usize))
255 } else {
256 Ok((remaining_input, u8_len as usize))
257 }
258}
259
260pub fn parse_fill_style_list(input: &[u8], version: ShapeVersion) -> NomResult<&[u8], Vec<swf::FillStyle>> {
261 use nom::multi::count;
262 let (input, style_count) = parse_list_length(input, version >= ShapeVersion::Shape2)?;
263 count(|i| parse_fill_style(i, version >= ShapeVersion::Shape3), style_count)(input)
264}
265
266pub fn parse_fill_style(input: &[u8], with_alpha: bool) -> NomResult<&[u8], swf::FillStyle> {
267 use nom::combinator::map;
268 let (input, code) = parse_u8(input)?;
269 match code {
270 0x00 => map(|i| parse_solid_fill(i, with_alpha), swf::FillStyle::Solid)(input),
271 0x10 => map(
272 |i| parse_linear_gradient_fill(i, with_alpha),
273 swf::FillStyle::LinearGradient,
274 )(input),
275 0x12 => map(
276 |i| parse_radial_gradient_fill(i, with_alpha),
277 swf::FillStyle::RadialGradient,
278 )(input),
279 0x13 => map(
280 |i| parse_focal_gradient_fill(i, with_alpha),
281 swf::FillStyle::FocalGradient,
282 )(input),
283 0x40 => map(|i| parse_bitmap_fill(i, true, true), swf::FillStyle::Bitmap)(input),
284 0x41 => map(|i| parse_bitmap_fill(i, false, true), swf::FillStyle::Bitmap)(input),
285 0x42 => map(|i| parse_bitmap_fill(i, true, false), swf::FillStyle::Bitmap)(input),
286 0x43 => map(|i| parse_bitmap_fill(i, false, false), swf::FillStyle::Bitmap)(input),
287 _ => Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch))),
288 }
289}
290
291pub fn parse_bitmap_fill(input: &[u8], repeating: bool, smoothed: bool) -> NomResult<&[u8], swf::fill_styles::Bitmap> {
292 let (input, bitmap_id) = parse_le_u16(input)?;
293 let (input, matrix) = parse_matrix(input)?;
294 Ok((
295 input,
296 swf::fill_styles::Bitmap {
297 bitmap_id,
298 matrix,
299 repeating,
300 smoothed,
301 },
302 ))
303}
304
305pub fn parse_focal_gradient_fill(input: &[u8], with_alpha: bool) -> NomResult<&[u8], swf::fill_styles::FocalGradient> {
306 let (input, matrix) = parse_matrix(input)?;
307 let (input, gradient) = parse_gradient(input, with_alpha)?;
308 let (input, focal_point) = parse_le_fixed8_p8(input)?;
309
310 Ok((
311 input,
312 swf::fill_styles::FocalGradient {
313 matrix,
314 gradient,
315 focal_point,
316 },
317 ))
318}
319
320pub fn parse_linear_gradient_fill(
321 input: &[u8],
322 with_alpha: bool,
323) -> NomResult<&[u8], swf::fill_styles::LinearGradient> {
324 let (input, matrix) = parse_matrix(input)?;
325 let (input, gradient) = parse_gradient(input, with_alpha)?;
326
327 Ok((input, swf::fill_styles::LinearGradient { matrix, gradient }))
328}
329
330pub fn parse_radial_gradient_fill(
331 input: &[u8],
332 with_alpha: bool,
333) -> NomResult<&[u8], swf::fill_styles::RadialGradient> {
334 let (input, matrix) = parse_matrix(input)?;
335 let (input, gradient) = parse_gradient(input, with_alpha)?;
336
337 Ok((input, swf::fill_styles::RadialGradient { matrix, gradient }))
338}
339
340pub fn parse_solid_fill(input: &[u8], with_alpha: bool) -> NomResult<&[u8], swf::fill_styles::Solid> {
341 use nom::combinator::map;
342 let (input, color) = if with_alpha {
343 parse_straight_s_rgba8(input)?
344 } else {
345 map(parse_s_rgb8, |c| swf::StraightSRgba8 {
346 r: c.r,
347 g: c.g,
348 b: c.b,
349 a: 255,
350 })(input)?
351 };
352 Ok((input, swf::fill_styles::Solid { color }))
353}
354
355pub fn parse_line_style_list(input: &[u8], version: ShapeVersion) -> NomResult<&[u8], Vec<swf::LineStyle>> {
356 use nom::multi::count;
357 let (input, style_count) = parse_list_length(input, version >= ShapeVersion::Shape2)?;
358
359 if version >= ShapeVersion::Shape4 {
360 count(parse_line_style2, style_count)(input)
361 } else {
362 count(|i| parse_line_style(i, version >= ShapeVersion::Shape3), style_count)(input)
363 }
364}
365
366pub fn parse_line_style(input: &[u8], with_alpha: bool) -> NomResult<&[u8], swf::LineStyle> {
367 use nom::combinator::map;
368 let (input, width) = parse_le_u16(input)?;
369 let (input, color) = if with_alpha {
370 parse_straight_s_rgba8(input)?
371 } else {
372 map(parse_s_rgb8, |c| swf::StraightSRgba8 {
373 r: c.r,
374 g: c.g,
375 b: c.b,
376 a: 255,
377 })(input)?
378 };
379 Ok((
380 input,
381 swf::LineStyle {
382 width,
383 start_cap: swf::CapStyle::Round,
384 end_cap: swf::CapStyle::Round,
385 join: swf::JoinStyle::Round,
386 no_h_scale: false,
387 no_v_scale: false,
388 no_close: false,
389 pixel_hinting: false,
390 fill: swf::FillStyle::Solid(swf::fill_styles::Solid { color }),
391 },
392 ))
393}
394
395pub(crate) fn cap_style_from_code(cap_style_code: u16) -> Result<swf::CapStyle, ()> {
396 match cap_style_code {
397 0 => Ok(swf::CapStyle::Round),
398 1 => Ok(swf::CapStyle::None),
399 2 => Ok(swf::CapStyle::Square),
400 _ => Err(()),
401 }
402}
403
404pub fn parse_line_style2(input: &[u8]) -> NomResult<&[u8], swf::LineStyle> {
405 use nom::combinator::map;
406
407 let (input, width) = parse_le_u16(input)?;
408
409 let (input, flags) = parse_le_u16(input)?;
410 #[allow(clippy::identity_op)]
411 let pixel_hinting = (flags & (1 << 0)) != 0;
412 let no_v_scale = (flags & (1 << 1)) != 0;
413 let no_h_scale = (flags & (1 << 2)) != 0;
414 let has_fill = (flags & (1 << 3)) != 0;
415 let join_style_code = (flags >> 4) & 0b11;
416 let start_cap_style_code = (flags >> 6) & 0b11;
417 let end_cap_style_code = (flags >> 8) & 0b11;
418 let no_close = (flags & (1 << 10)) != 0;
419 let start_cap =
422 cap_style_from_code(start_cap_style_code).map_err(|_| nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch)))?;
423 let end_cap =
424 cap_style_from_code(end_cap_style_code).map_err(|_| nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch)))?;
425
426 let (input, join) = match join_style_code {
427 0 => (input, swf::JoinStyle::Round),
428 1 => (input, swf::JoinStyle::Bevel),
429 2 => {
430 let (input, limit) = parse_le_u16(input)?;
431 (input, swf::JoinStyle::Miter(swf::join_styles::Miter { limit }))
432 }
433 _ => return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch))),
434 };
435
436 let (input, fill) = if has_fill {
437 parse_fill_style(input, true)?
438 } else {
439 map(parse_straight_s_rgba8, |color| {
440 swf::FillStyle::Solid(swf::fill_styles::Solid { color })
441 })(input)?
442 };
443
444 Ok((
445 input,
446 swf::LineStyle {
447 width,
448 start_cap,
449 end_cap,
450 join,
451 no_h_scale,
452 no_v_scale,
453 no_close,
454 pixel_hinting,
455 fill,
456 },
457 ))
458}