From b5a67bad717cdd0df26c7a170ac61ede31f11ba7 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Wed, 22 Feb 2017 18:48:12 -0800 Subject: [PATCH] Implement DefineMorphShape tags. --- src/read.rs | 261 ++++++++++++++++++++++++++++ src/test_data.rs | 232 +++++++++++++++++++++++++ src/types.rs | 20 +++ src/write.rs | 224 ++++++++++++++++++++++++ tests/swfs/DefineMorphShape-MX.fla | Bin 0 -> 27648 bytes tests/swfs/DefineMorphShape-MX.swf | Bin 0 -> 558 bytes tests/swfs/DefineMorphShape2-CC.fla | Bin 0 -> 6787 bytes tests/swfs/DefineMorphShape2-CC.swf | Bin 0 -> 644 bytes 8 files changed, 737 insertions(+) create mode 100644 tests/swfs/DefineMorphShape-MX.fla create mode 100644 tests/swfs/DefineMorphShape-MX.swf create mode 100644 tests/swfs/DefineMorphShape2-CC.fla create mode 100644 tests/swfs/DefineMorphShape2-CC.swf diff --git a/src/read.rs b/src/read.rs index 653192355..be42735f1 100644 --- a/src/read.rs +++ b/src/read.rs @@ -500,6 +500,8 @@ impl Reader { Some(TagCode::DefineFontInfo) => tag_reader.read_define_font_info(1)?, Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?, Some(TagCode::DefineFontName) => tag_reader.read_define_font_name()?, + Some(TagCode::DefineMorphShape) => tag_reader.read_define_morph_shape(1)?, + Some(TagCode::DefineMorphShape2) => tag_reader.read_define_morph_shape(2)?, Some(TagCode::DefineShape) => tag_reader.read_define_shape(1)?, Some(TagCode::DefineShape2) => tag_reader.read_define_shape(2)?, Some(TagCode::DefineShape3) => tag_reader.read_define_shape(3)?, @@ -1174,6 +1176,265 @@ impl Reader { }) } + fn read_define_morph_shape(&mut self, shape_version: u8) -> Result { + let id = self.read_character_id()?; + let start_shape_bounds = self.read_rectangle()?; + let end_shape_bounds = self.read_rectangle()?; + let (start_edge_bounds, end_edge_bounds, + has_non_scaling_strokes, has_scaling_strokes) = + if shape_version >= 2 { + let start_edge_bounds = self.read_rectangle()?; + let end_edge_bounds = self.read_rectangle()?; + let flags = self.read_u8()?; + (start_edge_bounds, end_edge_bounds, flags & 0b10 != 0, flags & 0b1 != 0) + } else { + (start_shape_bounds.clone(), end_shape_bounds.clone(), true, false) + }; + + self.read_u32()?; // Offset to EndEdges. + + let num_fill_styles = match self.read_u8()? { + 0xff => self.read_u16()? as usize, + n => n as usize, + }; + let mut start_fill_styles = Vec::with_capacity(num_fill_styles); + let mut end_fill_styles = Vec::with_capacity(num_fill_styles); + for _ in 0..num_fill_styles { + let (start, end) = self.read_morph_fill_style(shape_version)?; + start_fill_styles.push(start); + end_fill_styles.push(end); + } + + let num_line_styles = match self.read_u8()? { + 0xff => self.read_u16()? as usize, + n => n as usize, + }; + let mut start_line_styles = Vec::with_capacity(num_line_styles); + let mut end_line_styles = Vec::with_capacity(num_line_styles); + for _ in 0..num_line_styles { + let (start, end) = self.read_morph_line_style(shape_version)?; + start_line_styles.push(start); + end_line_styles.push(end); + } + + // TODO(Herschel): Add read_shape + self.num_fill_bits = self.read_ubits(4)? as u8; + self.num_line_bits = self.read_ubits(4)? as u8; + let mut start_shape = Vec::new(); + while let Some(record) = self.read_shape_record(1)? { + start_shape.push(record); + } + + self.byte_align(); + let mut end_shape = Vec::new(); + self.read_u8()?; // NumFillBits and NumLineBits are written as 0 for the end shape. + while let Some(record) = self.read_shape_record(1)? { + end_shape.push(record); + } + Ok(Tag::DefineMorphShape(Box::new(DefineMorphShape { + id: id, + version: shape_version, + has_non_scaling_strokes: has_non_scaling_strokes, + has_scaling_strokes: has_scaling_strokes, + start: MorphShape { + shape_bounds: start_shape_bounds, + edge_bounds: start_edge_bounds, + shape: start_shape, + fill_styles: start_fill_styles, + line_styles: start_line_styles, + }, + end: MorphShape { + shape_bounds: end_shape_bounds, + edge_bounds: end_edge_bounds, + shape: end_shape, + fill_styles: end_fill_styles, + line_styles: end_line_styles, + }, + }))) + } + + fn read_morph_line_style(&mut self, shape_version: u8) -> Result<(LineStyle, LineStyle)> { + if shape_version < 2 { + let start_width = self.read_u16()?; + let end_width = self.read_u16()?; + let start_color = self.read_rgba()?; + let end_color = self.read_rgba()?; + + Ok(( + LineStyle::new_v1(start_width, start_color), + LineStyle::new_v1(end_width, end_color) + )) + } else { + // MorphLineStyle2 in DefineMorphShape2. + let start_width = self.read_u16()?; + let end_width = self.read_u16()?; + let start_cap = match self.read_ubits(2)? { + 0 => LineCapStyle::Round, + 1 => LineCapStyle::None, + 2 => LineCapStyle::Square, + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")), + }; + let join_style_id = self.read_ubits(2)?; + let has_fill = self.read_bit()?; + let allow_scale_x = !self.read_bit()?; + let allow_scale_y = !self.read_bit()?; + let is_pixel_hinted = self.read_bit()?; + self.read_ubits(5)?; + let allow_close = !self.read_bit()?; + let end_cap = match self.read_ubits(2)? { + 0 => LineCapStyle::Round, + 1 => LineCapStyle::None, + 2 => LineCapStyle::Square, + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")), + }; + let join_style = match join_style_id { + 0 => LineJoinStyle::Round, + 1 => LineJoinStyle::Bevel, + 2 => LineJoinStyle::Miter(self.read_fixed8()?), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")), + }; + let (start_color, end_color) = if !has_fill { + (self.read_rgba()?, self.read_rgba()?) + } else { + (Color { r: 0, g: 0, b: 0, a: 0 }, Color { r: 0, g: 0, b: 0, a: 0 }) + }; + let (start_fill_style, end_fill_style) = if has_fill { + let (start, end) = self.read_morph_fill_style(shape_version)?; + (Some(start), Some(end)) + } else { + (None, None) + }; + Ok(( + LineStyle { + width: start_width, + color: start_color, + start_cap: start_cap, + end_cap: end_cap, + join_style: join_style, + allow_scale_x: allow_scale_x, + allow_scale_y: allow_scale_y, + is_pixel_hinted: is_pixel_hinted, + allow_close: allow_close, + fill_style: start_fill_style, + }, + LineStyle { + width: end_width, + color: end_color, + start_cap: start_cap, + end_cap: end_cap, + join_style: join_style, + allow_scale_x: allow_scale_x, + allow_scale_y: allow_scale_y, + is_pixel_hinted: is_pixel_hinted, + allow_close: allow_close, + fill_style: end_fill_style, + } + )) + } + } + + fn read_morph_fill_style(&mut self, shape_version: u8) -> Result<(FillStyle, FillStyle)> { + let fill_style_type = self.read_u8()?; + let fill_style = match fill_style_type { + 0x00 => { + let start_color = self.read_rgba()?; + let end_color = self.read_rgba()?; + (FillStyle::Color(start_color), FillStyle::Color(end_color)) + }, + + 0x10 => { + let (start_gradient, end_gradient) = self.read_morph_gradient()?; + ( + FillStyle::LinearGradient(start_gradient), + FillStyle::LinearGradient(end_gradient) + ) + }, + + 0x12 => { + let (start_gradient, end_gradient) = self.read_morph_gradient()?; + ( + FillStyle::RadialGradient(start_gradient), + FillStyle::RadialGradient(end_gradient) + ) + }, + + 0x13 => { + if self.version < 8 || shape_version < 2 { + return Err(Error::new(ErrorKind::InvalidData, + "Focal gradients are only supported in SWF version 8 \ + or higher.")); + } + // TODO(Herschel): How is focal_point stored? + let (start_gradient, end_gradient) = self.read_morph_gradient()?; + let start_focal_point = self.read_fixed8()?; + let end_focal_point = self.read_fixed8()?; + ( + FillStyle::FocalGradient { + gradient: start_gradient, + focal_point: start_focal_point, + }, + FillStyle::FocalGradient { + gradient: end_gradient, + focal_point: end_focal_point, + } + ) + }, + + 0x40...0x43 => { + let id = self.read_character_id()?; + ( + FillStyle::Bitmap { + id: id, + matrix: self.read_matrix()?, + is_smoothed: (fill_style_type & 0b10) == 0, + is_repeating: (fill_style_type & 0b01) == 0, + }, + FillStyle::Bitmap { + id: id, + matrix: self.read_matrix()?, + is_smoothed: (fill_style_type & 0b10) == 0, + is_repeating: (fill_style_type & 0b01) == 0, + }, + ) + } + + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid fill style.")), + }; + Ok(fill_style) + } + + fn read_morph_gradient(&mut self) -> Result<(Gradient, Gradient)> { + let start_matrix = self.read_matrix()?; + let end_matrix = self.read_matrix()?; + let num_records = self.read_u8()? as usize; + let mut start_records = Vec::with_capacity(num_records); + let mut end_records = Vec::with_capacity(num_records); + for _ in 0..num_records { + start_records.push(GradientRecord { + ratio: self.read_u8()?, + color: self.read_rgba()?, + }); + end_records.push(GradientRecord { + ratio: self.read_u8()?, + color: self.read_rgba()?, + }); + } + Ok(( + Gradient { + matrix: start_matrix, + spread: GradientSpread::Pad, // TODO(Herschel): What are the defaults? + interpolation: GradientInterpolation::RGB, + records: start_records, + }, + Gradient { + matrix: end_matrix, + spread: GradientSpread::Pad, // TODO(Herschel): What are the defaults? + interpolation: GradientInterpolation::RGB, + records: end_records, + } + )) + } + fn read_define_shape(&mut self, version: u8) -> Result { let id = self.read_u16()?; let shape_bounds = self.read_rectangle()?; diff --git a/src/test_data.rs b/src/test_data.rs index d6203fb9e..ec90951fa 100644 --- a/src/test_data.rs +++ b/src/test_data.rs @@ -534,6 +534,238 @@ pub fn tag_tests() -> Vec { vec![ read_tag_bytes_from_file("tests/swfs/DefineFont4-CC.swf", TagCode::DefineFontName) ), + ( + 3, + Tag::DefineMorphShape(Box::new(DefineMorphShape { + version: 1, + id: 1, + has_non_scaling_strokes: true, + has_scaling_strokes: false, + start: MorphShape { + shape_bounds: Rectangle { x_min: 15.0, x_max: 65.0, y_min: 15.0, y_max: 65.0 }, + edge_bounds: Rectangle { x_min: 15.0, x_max: 65.0, y_min: 15.0, y_max: 65.0 }, + fill_styles: vec![ + FillStyle::LinearGradient( + Gradient { + matrix: Matrix { + translate_x: 40.0, + translate_y: 40.0, + scale_x: 0.024429321, + scale_y: 0.024429321, + rotate_skew_0: 0.024429321, + rotate_skew_1: -0.024429321 + }, + spread: GradientSpread::Pad, + interpolation: GradientInterpolation::RGB, + records: vec![ + GradientRecord { ratio: 0, color: Color { r: 255, g: 255, b: 255, a: 255 } }, + GradientRecord { ratio: 255, color: Color { r: 0, g: 0, b: 0, a: 255 } }, + ] + } + ) + ], + line_styles: vec![LineStyle::new_v1(200, Color { r: 0, g: 255, b: 0, a: 255 })], + shape: vec![ + ShapeRecord::StyleChange(StyleChangeData { + move_to: Some((20.0, 20.0)), + fill_style_0: None, + fill_style_1: None, + line_style: Some(1), + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 40.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: 40.0 }, + ShapeRecord::StraightEdge { delta_x: -40.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: -40.0 }, + ShapeRecord::StyleChange(StyleChangeData { + move_to: None, + fill_style_0: Some(1), + fill_style_1: None, + line_style: None, + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 40.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: 40.0 }, + ShapeRecord::StraightEdge { delta_x: -40.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: -40.0 }, + ] + }, + end: MorphShape { + shape_bounds: Rectangle { x_min: 19.0, x_max: 75.05, y_min: 8.35, y_max: 61.0 }, + edge_bounds: Rectangle { x_min: 19.0, x_max: 75.05, y_min: 8.35, y_max: 61.0 }, + fill_styles: vec![ + FillStyle::LinearGradient(Gradient { + matrix: Matrix { + translate_x: 48.4, + translate_y: 34.65, + scale_x: 0.0058898926, + scale_y: 0.030914307, + rotate_skew_0: 0.0, + rotate_skew_1: 0.0 + }, + spread: GradientSpread::Pad, + interpolation: GradientInterpolation::RGB, + records: vec![ + GradientRecord { ratio: 56, color: Color { r: 255, g: 0, b: 0, a: 255 } }, + GradientRecord { ratio: 157, color: Color { r: 0, g: 0, b: 255, a: 255 } } + ] + }) + ], + line_styles: vec![LineStyle::new_v1( 40, Color { r: 255, g: 255, b: 0, a: 255 } )], + shape: vec![ + ShapeRecord::StyleChange(StyleChangeData { + move_to: Some((20.0, 60.0)), + fill_style_0: None, + fill_style_1: None, + line_style: None, + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 17.4, delta_y: -50.65 }, + ShapeRecord::StraightEdge { delta_x: 22.6, delta_y: 10.65 }, + ShapeRecord::CurvedEdge { control_delta_x: 28.15, control_delta_y: 19.1, anchor_delta_x: -28.15, anchor_delta_y: 20.9 }, + ShapeRecord::CurvedEdge { control_delta_x: -19.05, control_delta_y: -22.0, anchor_delta_x: -20.95, anchor_delta_y: 22.0 }, + ShapeRecord::StraightEdge { delta_x: 17.4, delta_y: -50.65 }, + ShapeRecord::StraightEdge { delta_x: 22.6, delta_y: 10.65 }, + ShapeRecord::CurvedEdge { control_delta_x: 28.15, control_delta_y: 19.1, anchor_delta_x: -28.15, anchor_delta_y: 20.9 }, + ShapeRecord::CurvedEdge { control_delta_x: -19.05, control_delta_y: -22.0, anchor_delta_x: -20.95, anchor_delta_y: 22.0 } + ] + } + })), + read_tag_bytes_from_file("tests/swfs/DefineMorphShape-MX.swf", TagCode::DefineMorphShape) + ), + + ( + 8, + Tag::DefineMorphShape(Box::new(DefineMorphShape { + version: 2, + id: 1, + has_non_scaling_strokes: false, + has_scaling_strokes: true, + start: MorphShape { + shape_bounds: Rectangle { x_min: 15.0, x_max: 225.0, y_min: 15.0, y_max: 225.0 }, + edge_bounds: Rectangle { x_min: 20.0, x_max: 220.0, y_min: 20.0, y_max: 220.0 }, + fill_styles: vec![ + FillStyle::FocalGradient { + gradient: Gradient { + matrix: Matrix { + translate_x: 116.05, + translate_y: 135.05, + scale_x: 0.11468506, + scale_y: 0.18927002, + rotate_skew_0: 0.0, + rotate_skew_1: 0.0 + }, + spread: GradientSpread::Pad, + interpolation: GradientInterpolation::RGB, + records: vec![ + GradientRecord { ratio: 0, color: Color { r: 255, g: 0, b: 0, a: 255 } }, + GradientRecord { ratio: 70, color: Color { r: 255, g: 0, b: 255, a: 255 } }, + GradientRecord { ratio: 255, color: Color { r: 0, g: 0, b: 0, a: 255 } } + ] + }, + focal_point: 0.97265625 + } + ], + line_styles: vec![ + LineStyle { + width: 200, + color: Color { r: 0, g: 255, b: 0, a: 255 }, + start_cap: LineCapStyle::Round, + end_cap: LineCapStyle::Round, + join_style: LineJoinStyle::Round, + fill_style: None, + allow_scale_x: true, + allow_scale_y: true, + is_pixel_hinted: false, + allow_close: true + } + ], + shape: vec![ + ShapeRecord::StyleChange(StyleChangeData { + move_to: Some((20.0, 20.0)), + fill_style_0: None, + fill_style_1: None, + line_style: Some(1), + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 200.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: 200.0 }, + ShapeRecord::StraightEdge { delta_x: -200.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: -200.0 }, + ShapeRecord::StyleChange(StyleChangeData { + move_to: None, + fill_style_0: Some(1), + fill_style_1: None, + line_style: None, + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 200.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: 200.0 }, + ShapeRecord::StraightEdge { delta_x: -200.0, delta_y: 0.0 }, + ShapeRecord::StraightEdge { delta_x: 0.0, delta_y: -200.0 } + ] + }, + end: MorphShape { + shape_bounds: Rectangle { x_min: 25.0, x_max: 212.05, y_min: 15.35, y_max: 148.35 }, + edge_bounds: Rectangle { x_min: 26.0, x_max: 211.05, y_min: 16.35, y_max: 147.35 }, + fill_styles: vec![ + FillStyle::FocalGradient { + gradient: Gradient { + matrix: Matrix { + translate_x: 164.0, + translate_y: 150.05, + scale_x: 0.036087036, + scale_y: 0.041992188, + rotate_skew_0: 0.1347351, + rotate_skew_1: -0.15675354 + }, + spread: GradientSpread::Pad, + interpolation: GradientInterpolation::RGB, + records: vec![ + GradientRecord { ratio: 0, color: Color { r: 0, g: 255, b: 255, a: 255 } }, + GradientRecord { ratio: 183, color: Color { r: 0, g: 255, b: 0, a: 255 } }, + GradientRecord { ratio: 226, color: Color { r: 255, g: 0, b: 255, a: 255 } } + ] + }, + focal_point: -0.9921875 + } + ], + line_styles: vec![ + LineStyle { + width: 40, + color: Color { r: 255, g: 255, b:0, a: 255 }, + start_cap: LineCapStyle::Round, + end_cap: LineCapStyle::Round, + join_style: LineJoinStyle::Round, + fill_style: None, + allow_scale_x: true, + allow_scale_y: true, + is_pixel_hinted: false, + allow_close: true + } + ], + shape: vec![ + ShapeRecord::StyleChange(StyleChangeData { + move_to: Some((26.0, 147.35)), + fill_style_0: None, + fill_style_1: None, + line_style: None, + new_styles: None + }), + ShapeRecord::StraightEdge { delta_x: 95.0, delta_y: -131.0 }, + ShapeRecord::StraightEdge { delta_x: 59.0, delta_y: 17.0 }, + ShapeRecord::CurvedEdge { control_delta_x: 62.1, control_delta_y: 57.0, anchor_delta_x: -62.1, anchor_delta_y: 57.0 }, + ShapeRecord::CurvedEdge { control_delta_x: -73.2, control_delta_y: -70.6, anchor_delta_x: -80.8, anchor_delta_y: 70.6 }, + ShapeRecord::StraightEdge { delta_x: 95.0, delta_y: -131.0 }, + ShapeRecord::StraightEdge { delta_x: 59.0, delta_y: 17.0 }, + ShapeRecord::CurvedEdge { control_delta_x: 62.1, control_delta_y: 57.0, anchor_delta_x: -62.1, anchor_delta_y: 57.0 }, + ShapeRecord::CurvedEdge { control_delta_x: -73.2, control_delta_y: -70.6, anchor_delta_x: -80.8, anchor_delta_y: 70.6 } + ] + } + })), + read_tag_bytes_from_file("tests/swfs/DefineMorphShape2-CC.swf", TagCode::DefineMorphShape2) + ), + ( 8, Tag::DefineScalingGrid { diff --git a/src/types.rs b/src/types.rs index 5b07cfeb0..ed2108ef8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -318,6 +318,7 @@ pub enum Tag { DefineFontAlignZones { id: CharacterId, thickness: FontThickness, zones: Vec }, DefineFontInfo(Box), DefineFontName { id: CharacterId, name: String, copyright_info: String }, + DefineMorphShape(Box), DefineScalingGrid { id: CharacterId, splitter_rect: Rectangle }, DefineShape(Shape), DefineSound(Box), @@ -624,6 +625,25 @@ pub enum ButtonActionCondition { KeyPress } +#[derive(Clone, Debug, PartialEq)] +pub struct DefineMorphShape { + pub version: u8, + pub id: CharacterId, + pub has_non_scaling_strokes: bool, + pub has_scaling_strokes: bool, + pub start: MorphShape, + pub end: MorphShape, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MorphShape { + pub shape_bounds: Rectangle, + pub edge_bounds: Rectangle, + pub fill_styles: Vec, + pub line_styles: Vec, + pub shape: Vec, +} + #[derive(Clone, Debug, PartialEq)] pub struct FontV1 { pub id: CharacterId, diff --git a/src/write.rs b/src/write.rs index cbadd4a52..ea61d800e 100644 --- a/src/write.rs +++ b/src/write.rs @@ -628,6 +628,8 @@ impl Writer { self.write_c_string(copyright_info)?; }, + &Tag::DefineMorphShape(ref define_morph_shape) => self.write_define_morph_shape(define_morph_shape)?, + &Tag::DefineScalingGrid { id, ref splitter_rect } => { let mut buf = Vec::new(); { @@ -912,6 +914,228 @@ impl Writer { Ok(()) } + fn write_define_morph_shape(&mut self, data: &DefineMorphShape) -> Result<()> { + if data.start.fill_styles.len() != data.end.fill_styles.len() || + data.start.line_styles.len() != data.end.line_styles.len() { + return Err(Error::new(ErrorKind::InvalidData, + "Start and end state of a morph shape must have the same number of styles.")); + } + + let num_fill_styles = data.start.fill_styles.len(); + let num_line_styles = data.start.line_styles.len(); + let num_fill_bits = count_ubits(num_fill_styles as u32); + let num_line_bits = count_ubits(num_line_styles as u32); + + // Need to write styles first, to calculate offset to EndEdges. + let mut start_buf = Vec::new(); + { + let mut writer = Writer::new(&mut start_buf, self.version); + + // Styles + // TODO(Herschel): Make fn write_style_len. Check version. + if num_fill_styles >= 0xff { + writer.write_u8(0xff)?; + writer.write_u16(num_fill_styles as u16)?; + } else { + writer.write_u8(num_fill_styles as u8)?; + } + for (start, end) in data.start.fill_styles.iter().zip(data.end.fill_styles.iter()) { + writer.write_morph_fill_style(start, end, data.version)?; + } + + if num_line_styles >= 0xff { + writer.write_u8(0xff)?; + writer.write_u16(num_line_styles as u16)?; + } else { + writer.write_u8(num_line_styles as u8)?; + } + for (start, end) in data.start.line_styles.iter().zip(data.end.line_styles.iter()) { + writer.write_morph_line_style(start, end, data.version)?; + } + + // TODO(Herschel): Make fn write_shape. + writer.write_ubits(4, num_fill_bits as u32)?; + writer.write_ubits(4, num_line_bits as u32)?; + writer.num_fill_bits = num_fill_bits; + writer.num_line_bits = num_line_bits; + for shape_record in &data.start.shape { + writer.write_shape_record(shape_record, 1)?; + } + // End shape record. + writer.write_ubits(6, 0)?; + writer.flush_bits()?; + } + + let mut buf = Vec::new(); + { + let mut writer = Writer::new(&mut buf, self.version); + writer.write_character_id(data.id)?; + writer.write_rectangle(&data.start.shape_bounds)?; + writer.write_rectangle(&data.end.shape_bounds)?; + if data.version >= 2 { + writer.write_rectangle(&data.start.edge_bounds)?; + writer.write_rectangle(&data.end.edge_bounds)?; + writer.write_u8( + if data.has_non_scaling_strokes { 0b10 } else { 0 } | + if data.has_scaling_strokes { 0b1 } else { 0 } + )?; + } + + // Offset to EndEdges. + writer.write_u32(start_buf.len() as u32)?; + + writer.output.write_all(&start_buf)?; + + // EndEdges. + writer.write_u8(0)?; // NumFillBits and NumLineBits are written as 0 for the end shape. + writer.num_fill_bits = num_fill_bits; + writer.num_line_bits = num_line_bits; + for shape_record in &data.end.shape { + writer.write_shape_record(shape_record, 1)?; + } + // End shape record. + writer.write_ubits(6, 0)?; + writer.flush_bits()?; + } + + let tag_code = if data.version == 1 { TagCode::DefineMorphShape } else { TagCode::DefineMorphShape2 }; + self.write_tag_header(tag_code, buf.len() as u32)?; + self.output.write_all(&buf)?; + Ok(()) + } + + fn write_morph_fill_style(&mut self, start: &FillStyle, end: &FillStyle, shape_version: u8) -> Result<()> { + match (start, end) { + (&FillStyle::Color(ref start_color), &FillStyle::Color(ref end_color)) => { + self.write_u8(0x00)?; // Solid color. + self.write_rgba(start_color)?; + self.write_rgba(end_color)?; + }, + + (&FillStyle::LinearGradient(ref start_gradient), &FillStyle::LinearGradient(ref end_gradient)) => { + self.write_u8(0x10)?; // Linear gradient. + self.write_morph_gradient(start_gradient, end_gradient)?; + }, + + (&FillStyle::RadialGradient(ref start_gradient), &FillStyle::RadialGradient(ref end_gradient)) => { + self.write_u8(0x12)?; // Linear gradient. + self.write_morph_gradient(start_gradient, end_gradient)?; + }, + + ( + &FillStyle::FocalGradient { gradient: ref start_gradient, focal_point: start_focal_point }, + &FillStyle::FocalGradient { gradient: ref end_gradient, focal_point: end_focal_point } + ) => { + if self.version < 8 || shape_version < 2 { + return Err(Error::new(ErrorKind::InvalidData, + "Focal gradients are only support in SWF version 8 \ + and higher.")); + } + + self.write_u8(0x13)?; // Focal gradient. + self.write_morph_gradient(start_gradient, end_gradient)?; + self.write_fixed8(start_focal_point)?; + self.write_fixed8(end_focal_point)?; + }, + + ( + &FillStyle::Bitmap { id, matrix: ref start_matrix, is_smoothed, is_repeating }, + &FillStyle::Bitmap { id: end_id, matrix: ref end_matrix, is_smoothed: end_is_smoothed, is_repeating: end_is_repeating } + ) if id == end_id && is_smoothed == end_is_smoothed || is_repeating == end_is_repeating => { + let fill_style_type = match (is_smoothed, is_repeating) { + (true, true) => 0x40, + (true, false) => 0x41, + (false, true) => 0x42, + (false, false) => 0x43, + }; + self.write_u8(fill_style_type)?; + self.write_u16(id)?; + self.write_matrix(start_matrix)?; + self.write_matrix(end_matrix)?; + }, + + _ => return Err(Error::new(ErrorKind::InvalidData, + "Morph start and end fill styles must be the same variant.")), + } + Ok(()) + } + + fn write_morph_gradient(&mut self, start: &Gradient, end: &Gradient) -> Result<()> { + self.write_matrix(&start.matrix)?; + self.write_matrix(&end.matrix)?; + if start.records.len() != end.records.len() { + return Err(Error::new(ErrorKind::InvalidData, + "Morph start and end gradient must have the same amount of records.")); + } + self.write_u8(start.records.len() as u8)?; + for (start_record, end_record) in start.records.iter().zip(end.records.iter()) { + self.write_u8(start_record.ratio)?; + self.write_rgba(&start_record.color)?; + self.write_u8(end_record.ratio)?; + self.write_rgba(&end_record.color)?; + } + Ok(()) + } + + fn write_morph_line_style(&mut self, start: &LineStyle, end: &LineStyle, shape_version: u8) -> Result<()> { + if shape_version < 2 { + self.write_u16(start.width)?; + self.write_u16(end.width)?; + self.write_rgba(&start.color)?; + self.write_rgba(&end.color)?; + } else { + if start.start_cap != end.start_cap || start.join_style != end.join_style || + start.allow_scale_x != end.allow_scale_x || start.allow_scale_y != end.allow_scale_y || + start.is_pixel_hinted != end.is_pixel_hinted || start.allow_close != end.allow_close || + start.end_cap != end.end_cap { + return Err(Error::new(ErrorKind::InvalidData, + "Morph start and end line styles must have the same join parameters.")); + } + + self.write_u16(start.width)?; + self.write_u16(end.width)?; + + // MorphLineStyle2 + self.write_ubits(2, match start.start_cap { + LineCapStyle::Round => 0, + LineCapStyle::None => 1, + LineCapStyle::Square => 2, + })?; + self.write_ubits(2, match start.join_style { + LineJoinStyle::Round => 0, + LineJoinStyle::Bevel => 1, + LineJoinStyle::Miter(_) => 2, + })?; + self.write_bit(start.fill_style.is_some())?; + self.write_bit(!start.allow_scale_x)?; + self.write_bit(!start.allow_scale_y)?; + self.write_bit(start.is_pixel_hinted)?; + self.write_ubits(5, 0)?; + self.write_bit(!start.allow_close)?; + self.write_ubits(2, match start.end_cap { + LineCapStyle::Round => 0, + LineCapStyle::None => 1, + LineCapStyle::Square => 2, + })?; + if let LineJoinStyle::Miter(miter_factor) = start.join_style { + self.write_fixed8(miter_factor)?; + } + match (&start.fill_style, &end.fill_style) { + (&None, &None) => { + self.write_rgba(&start.color)?; + self.write_rgba(&end.color)?; + }, + + (&Some(ref start_fill), &Some(ref end_fill)) => + self.write_morph_fill_style(start_fill, end_fill, shape_version)?, + + _ => return Err(Error::new(ErrorKind::InvalidData, + "Morph start and end line styles must both have fill styles.")), + } + } + Ok(()) + } + fn write_define_scene_and_frame_label_data(&mut self, scenes: &Vec, frame_labels: &Vec) diff --git a/tests/swfs/DefineMorphShape-MX.fla b/tests/swfs/DefineMorphShape-MX.fla new file mode 100644 index 0000000000000000000000000000000000000000..fae79cf9713a2715c358f68c185ae32ec4e557b8 GIT binary patch literal 27648 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 HpTGVuh>-oK literal 0 HcmV?d00001 diff --git a/tests/swfs/DefineMorphShape-MX.swf b/tests/swfs/DefineMorphShape-MX.swf new file mode 100644 index 0000000000000000000000000000000000000000..08f0466bbd01b374cabb52fe8ccea42236925900 GIT binary patch literal 558 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 H2jKqzo@tq2 literal 0 HcmV?d00001 diff --git a/tests/swfs/DefineMorphShape2-CC.swf b/tests/swfs/DefineMorphShape2-CC.swf new file mode 100644 index 0000000000000000000000000000000000000000..2e9d7b2f86fbcd63cb3705f31a821972816bfa2b GIT binary patch literal 644 zcmZ9~ODF_!9KiA43}(lpw1(Oo7_T_&g>bN}71k<`vSbfih*chGJ&IyWYl&jzU{i`s zgguD7i?AolMK~ywya_kt^!MNK`f5{?NlXOD4BL?w=D7X?)-dQatNV4`jV!w)dhTd7-nF|eyjwn&v)V-;ARrxtXbBLqPEQD30lD_#3s}PJLjqk$7wAqN zSI`snVv#jGs>Ho>4z`D#uNGO2LRGm7;5Wq^BU{(X1y@nPO#X`9QD7e}$yFCx-R&w< zbK3Nkt8`rVRI~q77OTHKQ;N{XaSVMa&_FLIXW*1a%*hd5;fUfGM2r&gnT$BhKul^8 zt;L8u10uW%@m7a$w;&vyh