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 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 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)?; 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)?; 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)?; 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)?; let flags: u8 = 0b00001; 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 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}