Implement DefineMorphShape tags.
This commit is contained in:
parent
d76aef617b
commit
b5a67bad71
261
src/read.rs
261
src/read.rs
|
@ -500,6 +500,8 @@ impl<R: Read> Reader<R> {
|
||||||
Some(TagCode::DefineFontInfo) => tag_reader.read_define_font_info(1)?,
|
Some(TagCode::DefineFontInfo) => tag_reader.read_define_font_info(1)?,
|
||||||
Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?,
|
Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?,
|
||||||
Some(TagCode::DefineFontName) => tag_reader.read_define_font_name()?,
|
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::DefineShape) => tag_reader.read_define_shape(1)?,
|
||||||
Some(TagCode::DefineShape2) => tag_reader.read_define_shape(2)?,
|
Some(TagCode::DefineShape2) => tag_reader.read_define_shape(2)?,
|
||||||
Some(TagCode::DefineShape3) => tag_reader.read_define_shape(3)?,
|
Some(TagCode::DefineShape3) => tag_reader.read_define_shape(3)?,
|
||||||
|
@ -1174,6 +1176,265 @@ impl<R: Read> Reader<R> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_define_morph_shape(&mut self, shape_version: u8) -> Result<Tag> {
|
||||||
|
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<Tag> {
|
fn read_define_shape(&mut self, version: u8) -> Result<Tag> {
|
||||||
let id = self.read_u16()?;
|
let id = self.read_u16()?;
|
||||||
let shape_bounds = self.read_rectangle()?;
|
let shape_bounds = self.read_rectangle()?;
|
||||||
|
|
232
src/test_data.rs
232
src/test_data.rs
|
@ -534,6 +534,238 @@ pub fn tag_tests() -> Vec<TagTestData> { vec![
|
||||||
read_tag_bytes_from_file("tests/swfs/DefineFont4-CC.swf", TagCode::DefineFontName)
|
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,
|
8,
|
||||||
Tag::DefineScalingGrid {
|
Tag::DefineScalingGrid {
|
||||||
|
|
20
src/types.rs
20
src/types.rs
|
@ -318,6 +318,7 @@ pub enum Tag {
|
||||||
DefineFontAlignZones { id: CharacterId, thickness: FontThickness, zones: Vec<FontAlignZone> },
|
DefineFontAlignZones { id: CharacterId, thickness: FontThickness, zones: Vec<FontAlignZone> },
|
||||||
DefineFontInfo(Box<FontInfo>),
|
DefineFontInfo(Box<FontInfo>),
|
||||||
DefineFontName { id: CharacterId, name: String, copyright_info: String },
|
DefineFontName { id: CharacterId, name: String, copyright_info: String },
|
||||||
|
DefineMorphShape(Box<DefineMorphShape>),
|
||||||
DefineScalingGrid { id: CharacterId, splitter_rect: Rectangle },
|
DefineScalingGrid { id: CharacterId, splitter_rect: Rectangle },
|
||||||
DefineShape(Shape),
|
DefineShape(Shape),
|
||||||
DefineSound(Box<Sound>),
|
DefineSound(Box<Sound>),
|
||||||
|
@ -624,6 +625,25 @@ pub enum ButtonActionCondition {
|
||||||
KeyPress
|
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<FillStyle>,
|
||||||
|
pub line_styles: Vec<LineStyle>,
|
||||||
|
pub shape: Vec<ShapeRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct FontV1 {
|
pub struct FontV1 {
|
||||||
pub id: CharacterId,
|
pub id: CharacterId,
|
||||||
|
|
224
src/write.rs
224
src/write.rs
|
@ -628,6 +628,8 @@ impl<W: Write> Writer<W> {
|
||||||
self.write_c_string(copyright_info)?;
|
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 } => {
|
&Tag::DefineScalingGrid { id, ref splitter_rect } => {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
{
|
||||||
|
@ -912,6 +914,228 @@ impl<W: Write> Writer<W> {
|
||||||
Ok(())
|
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,
|
fn write_define_scene_and_frame_label_data(&mut self,
|
||||||
scenes: &Vec<FrameLabel>,
|
scenes: &Vec<FrameLabel>,
|
||||||
frame_labels: &Vec<FrameLabel>)
|
frame_labels: &Vec<FrameLabel>)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue