swf_parser/complete/
shape.rs

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
56// TODO: Rename to ShapeStylesWithBits
57pub 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  // (Skip bits [11, 15])
420
421  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}