swf_emitter/
morph_shape.rs

1use std::convert::{TryFrom, TryInto};
2use std::io;
3
4use swf_types as ast;
5
6use crate::basic_data_types::{emit_matrix, emit_straight_s_rgba8};
7use crate::bit_count::{get_i32_min_bit_count, get_u32_bit_count};
8use crate::gradient::emit_morph_gradient;
9use crate::io_bits::{BitsWriter, WriteBits};
10use crate::primitives::{emit_le_i16, emit_le_u16, emit_le_u32, emit_u8};
11use crate::shape::{cap_style_to_code, emit_edge_bits, emit_list_length, join_style_to_code};
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum MorphShapeVersion {
15  MorphShape1,
16  MorphShape2,
17}
18
19pub(crate) fn emit_morph_shape<W: io::Write>(
20  writer: &mut W,
21  value: &ast::MorphShape,
22  version: MorphShapeVersion,
23) -> io::Result<()> {
24  let mut bits_writer = BitsWriter::new(Vec::new());
25  let (fill_bits, line_bits) = emit_morph_shape_styles_bits(&mut bits_writer, &value.initial_styles, version)?;
26  emit_morph_shape_start_record_string_bits(&mut bits_writer, &value.records, fill_bits, line_bits, version)?;
27  let inner_bits_writer = bits_writer.into_inner()?;
28  let start_size = inner_bits_writer.len();
29
30  let mut bits_writer = BitsWriter::new(inner_bits_writer);
31
32  // `0` for the style bits: there are no style changes in the end state.
33  bits_writer.write_u32_bits(4, 0)?;
34  bits_writer.write_u32_bits(4, 0)?;
35  emit_morph_shape_end_record_string_bits(&mut bits_writer, &value.records)?;
36
37  emit_le_u32(writer, start_size.try_into().unwrap())?;
38  writer.write_all(&bits_writer.into_inner()?)
39}
40
41pub(crate) fn emit_morph_shape_styles_bits<W: WriteBits>(
42  writer: &mut W,
43  value: &ast::MorphShapeStyles,
44  version: MorphShapeVersion,
45) -> io::Result<(u32, u32)> {
46  let bytes_writer = writer.write_bytes()?;
47  emit_morph_fill_style_list(bytes_writer, &value.fill)?;
48  emit_morph_line_style_list(bytes_writer, &value.line, version)?;
49  // The max style `id` is `.len()` (and not `.len() - 1`) because `0` always
50  // represents the empty style and custom styles are 1-indexed.
51  let max_fill_id: u32 = u32::try_from(value.fill.len()).unwrap();
52  let max_line_id: u32 = u32::try_from(value.line.len()).unwrap();
53  let fill_bits: u32 = get_u32_bit_count(max_fill_id);
54  let line_bits: u32 = get_u32_bit_count(max_line_id);
55  writer.write_u32_bits(4, fill_bits)?;
56  writer.write_u32_bits(4, line_bits)?;
57  Ok((fill_bits, line_bits))
58}
59
60pub(crate) fn emit_morph_shape_start_record_string_bits<W: WriteBits>(
61  writer: &mut W,
62  value: &[ast::MorphShapeRecord],
63  mut fill_bits: u32,
64  mut line_bits: u32,
65  version: MorphShapeVersion,
66) -> io::Result<()> {
67  for record in value {
68    match record {
69      ast::MorphShapeRecord::Edge(ref record) => {
70        writer.write_bool_bits(true)?; // is_edge
71        emit_edge_bits(
72          writer,
73          &ast::shape_records::Edge {
74            delta: record.delta,
75            control_delta: record.control_delta,
76          },
77        )?;
78      }
79      ast::MorphShapeRecord::StyleChange(ref record) => {
80        writer.write_bool_bits(false)?; // is_edge
81        let (next_fill_bits, next_line_bits) =
82          emit_morph_style_change_bits(writer, record, fill_bits, line_bits, version)?;
83        fill_bits = next_fill_bits;
84        line_bits = next_line_bits;
85      }
86    }
87  }
88  writer.write_u32_bits(6, 0)
89}
90
91pub(crate) fn emit_morph_shape_end_record_string_bits<W: WriteBits>(
92  writer: &mut W,
93  value: &[ast::MorphShapeRecord],
94) -> io::Result<()> {
95  for record in value {
96    match record {
97      ast::MorphShapeRecord::Edge(ref record) => {
98        writer.write_bool_bits(true)?; // is_edge
99        emit_edge_bits(
100          writer,
101          &ast::shape_records::Edge {
102            delta: record.morph_delta,
103            control_delta: record.morph_control_delta,
104          },
105        )?;
106      }
107      ast::MorphShapeRecord::StyleChange(ref record) => {
108        if record.move_to.is_none() {
109          continue;
110        }
111        writer.write_bool_bits(false)?; // is_edge
112        let flags: u8 = 0b00001; // Pure `moveTo`
113        writer.write_u32_bits(5, flags.into())?;
114        let morph_move_to = record.morph_move_to.unwrap();
115        let bits = get_i32_min_bit_count(vec![morph_move_to.x, morph_move_to.y].into_iter());
116        writer.write_u32_bits(5, bits)?;
117        writer.write_i32_bits(bits, morph_move_to.x)?;
118        writer.write_i32_bits(bits, morph_move_to.y)?;
119      }
120    }
121  }
122  writer.write_u32_bits(6, 0)
123}
124
125pub(crate) fn emit_morph_style_change_bits<W: WriteBits>(
126  writer: &mut W,
127  value: &ast::shape_records::MorphStyleChange,
128  fill_bits: u32,
129  line_bits: u32,
130  version: MorphShapeVersion,
131) -> io::Result<(u32, u32)> {
132  let has_move_to = value.move_to.is_some();
133  let has_new_left_fill = value.left_fill.is_some();
134  let has_new_right_fill = value.right_fill.is_some();
135  let has_new_line_style = value.line_style.is_some();
136  let has_new_styles = value.new_styles.is_some();
137
138  #[allow(clippy::identity_op)]
139  let flags: u8 = 0
140    | (if has_move_to { 1 << 0 } else { 0 })
141    | (if has_new_left_fill { 1 << 1 } else { 0 })
142    | (if has_new_right_fill { 1 << 2 } else { 0 })
143    | (if has_new_line_style { 1 << 3 } else { 0 })
144    | (if has_new_styles { 1 << 4 } else { 0 });
145
146  assert_ne!(flags, 0);
147
148  writer.write_u32_bits(5, flags.into())?;
149
150  if let Some(move_to) = value.move_to {
151    let bits = get_i32_min_bit_count(vec![move_to.x, move_to.y].into_iter());
152    writer.write_u32_bits(5, bits)?;
153    writer.write_i32_bits(bits, move_to.x)?;
154    writer.write_i32_bits(bits, move_to.y)?;
155  }
156
157  if let Some(left_fill) = value.left_fill {
158    writer.write_u32_bits(fill_bits, left_fill.try_into().unwrap())?;
159  }
160  if let Some(right_fill) = value.right_fill {
161    writer.write_u32_bits(fill_bits, right_fill.try_into().unwrap())?;
162  }
163  if let Some(line_style) = value.line_style {
164    writer.write_u32_bits(line_bits, line_style.try_into().unwrap())?;
165  }
166
167  if let Some(ref new_styles) = &value.new_styles {
168    emit_morph_shape_styles_bits(writer, new_styles, version)
169  } else {
170    Ok((fill_bits, line_bits))
171  }
172}
173
174pub(crate) fn emit_morph_fill_style_list<W: io::Write + ?Sized>(
175  writer: &mut W,
176  value: &[ast::MorphFillStyle],
177) -> io::Result<()> {
178  emit_list_length(writer, value.len(), true)?;
179  for fill_style in value {
180    emit_morph_fill_style(writer, fill_style)?;
181  }
182  Ok(())
183}
184
185pub(crate) fn emit_morph_fill_style<W: io::Write + ?Sized>(
186  writer: &mut W,
187  value: &ast::MorphFillStyle,
188) -> io::Result<()> {
189  match value {
190    ast::MorphFillStyle::Bitmap(ref style) => {
191      #[allow(clippy::identity_op)]
192      let code: u8 = 0
193        | (if !style.repeating { 1 << 0 } else { 0 })
194        | (if !style.smoothed { 1 << 1 } else { 0 })
195        | 0x40;
196      emit_u8(writer, code)?;
197      emit_morph_bitmap_fill(writer, style)
198    }
199    ast::MorphFillStyle::FocalGradient(ref style) => {
200      emit_u8(writer, 0x13)?;
201      emit_morph_focal_gradient_fill(writer, style)
202    }
203    ast::MorphFillStyle::LinearGradient(ref style) => {
204      emit_u8(writer, 0x10)?;
205      emit_morph_linear_gradient_fill(writer, style)
206    }
207    ast::MorphFillStyle::RadialGradient(ref style) => {
208      emit_u8(writer, 0x12)?;
209      emit_morph_radial_gradient_fill(writer, style)
210    }
211    ast::MorphFillStyle::Solid(ref style) => {
212      emit_u8(writer, 0x00)?;
213      emit_morph_solid_fill(writer, style)
214    }
215  }
216}
217
218pub(crate) fn emit_morph_bitmap_fill<W: io::Write + ?Sized>(
219  writer: &mut W,
220  value: &ast::fill_styles::MorphBitmap,
221) -> io::Result<()> {
222  emit_le_u16(writer, value.bitmap_id)?;
223  emit_matrix(writer, &value.matrix)?;
224  emit_matrix(writer, &value.morph_matrix)
225}
226
227pub(crate) fn emit_morph_focal_gradient_fill<W: io::Write + ?Sized>(
228  writer: &mut W,
229  value: &ast::fill_styles::MorphFocalGradient,
230) -> io::Result<()> {
231  emit_matrix(writer, &value.matrix)?;
232  emit_matrix(writer, &value.morph_matrix)?;
233  emit_morph_gradient(writer, &value.gradient)?;
234  emit_le_i16(writer, value.focal_point.epsilons)?;
235  emit_le_i16(writer, value.morph_focal_point.epsilons)
236}
237
238pub(crate) fn emit_morph_linear_gradient_fill<W: io::Write + ?Sized>(
239  writer: &mut W,
240  value: &ast::fill_styles::MorphLinearGradient,
241) -> io::Result<()> {
242  emit_matrix(writer, &value.matrix)?;
243  emit_matrix(writer, &value.morph_matrix)?;
244  emit_morph_gradient(writer, &value.gradient)
245}
246
247pub(crate) fn emit_morph_radial_gradient_fill<W: io::Write + ?Sized>(
248  writer: &mut W,
249  value: &ast::fill_styles::MorphRadialGradient,
250) -> io::Result<()> {
251  emit_matrix(writer, &value.matrix)?;
252  emit_matrix(writer, &value.morph_matrix)?;
253  emit_morph_gradient(writer, &value.gradient)
254}
255
256pub(crate) fn emit_morph_solid_fill<W: io::Write + ?Sized>(
257  writer: &mut W,
258  value: &ast::fill_styles::MorphSolid,
259) -> io::Result<()> {
260  emit_straight_s_rgba8(writer, value.color)?;
261  emit_straight_s_rgba8(writer, value.morph_color)
262}
263
264pub(crate) fn emit_morph_line_style_list<W: io::Write + ?Sized>(
265  writer: &mut W,
266  value: &[ast::MorphLineStyle],
267  version: MorphShapeVersion,
268) -> io::Result<()> {
269  emit_list_length(writer, value.len(), true)?;
270  for line_style in value {
271    if version < MorphShapeVersion::MorphShape2 {
272      emit_morph_line_style1(writer, line_style)?;
273    } else {
274      emit_morph_line_style2(writer, line_style)?;
275    }
276  }
277  Ok(())
278}
279
280pub(crate) fn emit_morph_line_style1<W: io::Write + ?Sized>(
281  writer: &mut W,
282  value: &ast::MorphLineStyle,
283) -> io::Result<()> {
284  match value.fill {
285    ast::MorphFillStyle::Solid(ref style) => {
286      emit_le_u16(writer, value.width)?;
287      emit_le_u16(writer, value.morph_width)?;
288      emit_morph_solid_fill(writer, style)
289    }
290    _ => panic!("InvalidMorphLineStyle1Fill"),
291  }
292}
293
294pub(crate) fn emit_morph_line_style2<W: io::Write + ?Sized>(
295  writer: &mut W,
296  value: &ast::MorphLineStyle,
297) -> io::Result<()> {
298  emit_le_u16(writer, value.width)?;
299  emit_le_u16(writer, value.morph_width)?;
300
301  let has_fill = !matches!(&value.fill, ast::MorphFillStyle::Solid(_));
302  let join_style_code = join_style_to_code(value.join);
303  let start_cap_style_code = cap_style_to_code(value.start_cap);
304  let end_cap_style_code = cap_style_to_code(value.end_cap);
305
306  #[allow(clippy::identity_op)]
307  let flags: u16 = 0
308    | (if value.pixel_hinting { 1 << 0 } else { 0 })
309    | (if value.no_v_scale { 1 << 1 } else { 0 })
310    | (if value.no_h_scale { 1 << 2 } else { 0 })
311    | (if has_fill { 1 << 3 } else { 0 })
312    | ((u16::from(join_style_code) & 0b11) << 4)
313    | ((u16::from(start_cap_style_code) & 0b11) << 6)
314    | ((u16::from(end_cap_style_code) & 0b11) << 8)
315    | (if value.no_close { 1 << 10 } else { 0 });
316  // Skip bits [11, 15]
317  emit_le_u16(writer, flags)?;
318
319  if let ast::JoinStyle::Miter(miter) = value.join {
320    emit_le_u16(writer, miter.limit)?;
321  }
322
323  match &value.fill {
324    ast::MorphFillStyle::Solid(ref style) => emit_morph_solid_fill(writer, style),
325    style => emit_morph_fill_style(writer, style),
326  }
327}