use nom::{IResult as NomResult, Needed};
use nom::{le_u16 as parse_le_u16, le_u8 as parse_u8};
use crate::parsers::basic_data_types::{
parse_bool_bits,
parse_i32_bits,
parse_le_fixed8_p8,
parse_matrix,
parse_straight_s_rgba8,
parse_u16_bits,
parse_u32_bits,
};
use crate::parsers::gradient::parse_morph_gradient;
use crate::parsers::shape::{parse_curved_edge_bits, parse_list_length, parse_straight_edge_bits};
use swf_tree as ast;
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
pub enum MorphShapeVersion {
MorphShape1,
MorphShape2,
}
pub fn parse_morph_shape(input: &[u8], version: MorphShapeVersion) -> NomResult<&[u8], ast::MorphShape> {
let (input, _end_offset) = take!(input, 4)?;
bits!(input, apply!(parse_morph_shape_bits, version))
}
pub fn parse_morph_shape_bits(input: (&[u8], usize), version: MorphShapeVersion) -> NomResult<(&[u8], usize), ast::MorphShape> {
do_parse!(
input,
styles: apply!(parse_morph_shape_styles_bits, version) >>
start_records: apply!(parse_morph_shape_start_record_string_bits, styles.fill_bits, styles.line_bits, version) >>
style_bits: bytes!(bits!(parse_style_bits_len_bits)) >>
records: apply!(parse_morph_shape_end_record_string_bits, start_records, style_bits.0, style_bits.1, version) >>
(ast::MorphShape {
initial_styles: ast::MorphShapeStyles {
fill: styles.fill,
line: styles.line,
},
records: records,
})
)
}
fn parse_style_bits_len_bits(input: (&[u8], usize)) -> NomResult<(&[u8], usize), (usize, usize)> {
do_parse!(
input,
fill_bits: map!(apply!(parse_u32_bits, 4), |x| x as usize) >>
line_bits: map!(apply!(parse_u32_bits, 4), |x| x as usize) >>
((fill_bits, line_bits))
)
}
pub struct InternalMorphShapeStyles {
pub fill: Vec<ast::MorphFillStyle>,
pub line: Vec<ast::MorphLineStyle>,
pub fill_bits: usize,
pub line_bits: usize,
}
pub fn parse_morph_shape_styles_bits(input: (&[u8], usize), version: MorphShapeVersion) -> NomResult<(&[u8], usize), InternalMorphShapeStyles> {
do_parse!(
input,
fill: bytes!(parse_morph_fill_style_list) >>
line: bytes!(apply!(parse_morph_line_style_list, version)) >>
fill_bits: map!(apply!(parse_u32_bits, 4), |x| x as usize) >>
line_bits: map!(apply!(parse_u32_bits, 4), |x| x as usize) >>
(InternalMorphShapeStyles {
fill: fill,
line: line,
fill_bits: fill_bits,
line_bits: line_bits,
})
)
}
enum MixedShapeRecord {
StraightEdge(ast::shape_records::StraightEdge),
CurvedEdge(ast::shape_records::CurvedEdge),
MorphStyleChange(ast::shape_records::MorphStyleChange),
}
fn parse_morph_shape_start_record_string_bits(input: (&[u8], usize), mut fill_bits: usize, mut line_bits: usize, version: MorphShapeVersion) -> NomResult<(&[u8], usize), Vec<MixedShapeRecord>> {
let mut result: Vec<MixedShapeRecord> = Vec::new();
let mut current_input = input;
loop {
match parse_u16_bits(current_input, 6) {
Ok((next_input, record_head)) => if record_head == 0 {
current_input = next_input;
break;
},
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
let is_edge = match parse_bool_bits(current_input) {
Ok((next_input, is_edge)) => {
current_input = next_input;
is_edge
}
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
if is_edge {
let is_straight_edge = match parse_bool_bits(current_input) {
Ok((next_input, is_straight_edge)) => {
current_input = next_input;
is_straight_edge
}
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
if is_straight_edge {
let (next_input, straight_edge) = parse_straight_edge_bits(current_input)?;
current_input = next_input;
result.push(MixedShapeRecord::StraightEdge(straight_edge));
} else {
let (next_input, curved_edge) = parse_curved_edge_bits(current_input)?;
current_input = next_input;
result.push(MixedShapeRecord::CurvedEdge(curved_edge));
}
} else {
let (next_input, (style_change, style_bits)) = parse_morph_style_change_bits(current_input, fill_bits, line_bits, version)?;
fill_bits = style_bits.0;
line_bits = style_bits.1;
result.push(MixedShapeRecord::MorphStyleChange(style_change));
current_input = next_input;
}
}
Ok((current_input, result))
}
fn to_curved_edge(straight: ast::shape_records::StraightEdge) -> ast::shape_records::CurvedEdge {
ast::shape_records::CurvedEdge {
control_delta: ast::Vector2D { x: straight.delta.x / 2, y: straight.delta.y / 2 },
anchor_delta: ast::Vector2D { x: straight.delta.x / 2, y: straight.delta.y / 2 },
}
}
fn as_morph_shape_record(start: MixedShapeRecord, end: MixedShapeRecord) -> ast::MorphShapeRecord {
match (start, end) {
(MixedShapeRecord::StraightEdge(s), MixedShapeRecord::StraightEdge(e)) => {
ast::MorphShapeRecord::StraightEdge(ast::shape_records::MorphStraightEdge {
delta: s.delta,
morph_delta: e.delta,
})
},
(MixedShapeRecord::StraightEdge(s), MixedShapeRecord::CurvedEdge(e)) => {
let s = to_curved_edge(s);
ast::MorphShapeRecord::CurvedEdge(ast::shape_records::MorphCurvedEdge {
control_delta: s.control_delta,
anchor_delta: s.anchor_delta,
morph_control_delta: e.control_delta,
morph_anchor_delta: e.anchor_delta,
})
},
(MixedShapeRecord::CurvedEdge(s), MixedShapeRecord::StraightEdge(e)) => {
let e = to_curved_edge(e);
ast::MorphShapeRecord::CurvedEdge(ast::shape_records::MorphCurvedEdge {
control_delta: s.control_delta,
anchor_delta: s.anchor_delta,
morph_control_delta: e.control_delta,
morph_anchor_delta: e.anchor_delta,
})
},
(MixedShapeRecord::CurvedEdge(s), MixedShapeRecord::CurvedEdge(e)) => {
ast::MorphShapeRecord::CurvedEdge(ast::shape_records::MorphCurvedEdge {
control_delta: s.control_delta,
anchor_delta: s.anchor_delta,
morph_control_delta: e.control_delta,
morph_anchor_delta: e.anchor_delta,
})
},
(MixedShapeRecord::MorphStyleChange(s), MixedShapeRecord::MorphStyleChange(e)) => {
ast::MorphShapeRecord::StyleChange(ast::shape_records::MorphStyleChange {
move_to: s.move_to,
morph_move_to: e.move_to,
left_fill: s.left_fill,
right_fill: s.right_fill,
line_style: s.line_style,
new_styles: s.new_styles,
})
},
_ => panic!("NonMatchingEdges")
}
}
fn parse_morph_shape_end_record_string_bits(
input: (&[u8], usize),
start_records: Vec<MixedShapeRecord>,
mut fill_bits: usize,
mut line_bits: usize,
version: MorphShapeVersion,
) -> NomResult<(&[u8], usize), Vec<ast::MorphShapeRecord>> {
let mut result: Vec<ast::MorphShapeRecord> = Vec::new();
let mut current_input = input;
for start_record in start_records.into_iter() {
let start_record = match start_record {
MixedShapeRecord::MorphStyleChange(sr) => {
if sr.move_to.is_none() {
result.push(ast::MorphShapeRecord::StyleChange(sr));
continue;
} else {
MixedShapeRecord::MorphStyleChange(sr)
}
}
sr => sr,
};
match parse_u16_bits(current_input, 6) {
Ok((_, 0)) => panic!("MissingMorphShapeEndRecords"),
Ok((_, _)) => {},
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
let is_edge = match parse_bool_bits(current_input) {
Ok((next_input, is_edge)) => {
current_input = next_input;
is_edge
}
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
let end_record = if is_edge {
let is_straight_edge = match parse_bool_bits(current_input) {
Ok((next_input, is_straight_edge)) => {
current_input = next_input;
is_straight_edge
}
Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
Err(e) => return Err(e),
};
if is_straight_edge {
let (next_input, straight_edge) = parse_straight_edge_bits(current_input)?;
current_input = next_input;
MixedShapeRecord::StraightEdge(straight_edge)
} else {
let (next_input, curved_edge) = parse_curved_edge_bits(current_input)?;
current_input = next_input;
MixedShapeRecord::CurvedEdge(curved_edge)
}
} else {
let (next_input, (style_change, style_bits)) = parse_morph_style_change_bits(current_input, fill_bits, line_bits, version)?;
fill_bits = style_bits.0;
line_bits = style_bits.1;
current_input = next_input;
MixedShapeRecord::MorphStyleChange(style_change)
};
result.push(as_morph_shape_record(start_record, end_record));
}
Ok((current_input, result))
}
pub fn parse_morph_style_change_bits(input: (&[u8], usize), fill_bits: usize, line_bits: usize, version: MorphShapeVersion) -> NomResult<(&[u8], usize), (ast::shape_records::MorphStyleChange, (usize, usize))> {
do_parse!(
input,
has_new_styles: parse_bool_bits >>
change_line_style: parse_bool_bits >>
change_right_fill: parse_bool_bits >>
change_left_fill: parse_bool_bits >>
has_move_to: parse_bool_bits >>
move_to: cond!(has_move_to,
do_parse!(
move_to_bits: map!(apply!(parse_u16_bits, 5), |bits| bits as usize) >>
x: apply!(parse_i32_bits, move_to_bits) >>
y: apply!(parse_i32_bits, move_to_bits) >>
(ast::Vector2D {x: x, y: y})
)
) >>
left_fill: cond!(change_left_fill, apply!(parse_u16_bits, fill_bits)) >>
right_fill: cond!(change_right_fill, apply!(parse_u16_bits, fill_bits)) >>
line_style: cond!(change_line_style, apply!(parse_u16_bits, line_bits)) >>
styles: map!(
cond!(has_new_styles, apply!(parse_morph_shape_styles_bits, version)),
|styles| match styles {
Option::Some(styles) => (
Option::Some(ast::MorphShapeStyles {fill: styles.fill, line: styles.line}),
styles.fill_bits,
styles.line_bits,
),
Option::None => (Option::None, fill_bits, line_bits),
}
) >>
((
ast::shape_records::MorphStyleChange {
move_to: move_to,
morph_move_to: Option::None,
left_fill: left_fill.map(|x| x as usize),
right_fill: right_fill.map(|x| x as usize),
line_style: line_style.map(|x| x as usize),
new_styles: styles.0,
},
(styles.1, styles.2),
))
)
}
pub fn parse_morph_fill_style_list(input: &[u8]) -> NomResult<&[u8], Vec<ast::MorphFillStyle>> {
length_count!(input, apply!(parse_list_length, true), parse_morph_fill_style)
}
pub fn parse_morph_fill_style(input: &[u8]) -> NomResult<&[u8], ast::MorphFillStyle> {
switch!(input, parse_u8,
0x00 => map!(parse_morph_solid_fill, |fill| ast::MorphFillStyle::Solid(fill)) |
0x10 => map!(parse_morph_linear_gradient_fill, |fill| ast::MorphFillStyle::LinearGradient(fill)) |
0x12 => map!(parse_morph_radial_gradient_fill, |fill| ast::MorphFillStyle::RadialGradient(fill)) |
0x13 => map!(parse_morph_focal_gradient_fill, |fill| ast::MorphFillStyle::FocalGradient(fill)) |
0x40 => map!(apply!(parse_morph_bitmap_fill, true, true), |fill| ast::MorphFillStyle::Bitmap(fill)) |
0x41 => map!(apply!(parse_morph_bitmap_fill, false, true), |fill| ast::MorphFillStyle::Bitmap(fill)) |
0x42 => map!(apply!(parse_morph_bitmap_fill, true, false), |fill| ast::MorphFillStyle::Bitmap(fill)) |
0x43 => map!(apply!(parse_morph_bitmap_fill, false, false), |fill| ast::MorphFillStyle::Bitmap(fill))
)
}
pub fn parse_morph_bitmap_fill(input: &[u8], repeating: bool, smoothed: bool) -> NomResult<&[u8], ast::fill_styles::MorphBitmap> {
do_parse!(
input,
bitmap_id: parse_le_u16 >>
matrix: parse_matrix >>
morph_matrix: parse_matrix >>
(ast::fill_styles::MorphBitmap {
bitmap_id: bitmap_id,
matrix: matrix,
morph_matrix: morph_matrix,
repeating: repeating,
smoothed: smoothed,
})
)
}
pub fn parse_morph_focal_gradient_fill(input: &[u8]) -> NomResult<&[u8], ast::fill_styles::MorphFocalGradient> {
do_parse!(
input,
matrix: parse_matrix >>
morph_matrix: parse_matrix >>
gradient: apply!(parse_morph_gradient, true) >>
focal_point: parse_le_fixed8_p8 >>
morph_focal_point: parse_le_fixed8_p8 >>
(ast::fill_styles::MorphFocalGradient {
matrix: matrix,
morph_matrix: morph_matrix,
gradient: gradient,
focal_point: focal_point,
morph_focal_point: morph_focal_point,
})
)
}
pub fn parse_morph_linear_gradient_fill(input: &[u8]) -> NomResult<&[u8], ast::fill_styles::MorphLinearGradient> {
do_parse!(
input,
matrix: parse_matrix >>
morph_matrix: parse_matrix >>
gradient: apply!(parse_morph_gradient, true) >>
(ast::fill_styles::MorphLinearGradient {
matrix: matrix,
morph_matrix: morph_matrix,
gradient: gradient
})
)
}
pub fn parse_morph_radial_gradient_fill(input: &[u8]) -> NomResult<&[u8], ast::fill_styles::MorphRadialGradient> {
do_parse!(
input,
matrix: parse_matrix >>
morph_matrix: parse_matrix >>
gradient: apply!(parse_morph_gradient, true) >>
(ast::fill_styles::MorphRadialGradient {
matrix: matrix,
morph_matrix: morph_matrix,
gradient: gradient
})
)
}
pub fn parse_morph_solid_fill(input: &[u8]) -> NomResult<&[u8], ast::fill_styles::MorphSolid> {
do_parse!(
input,
color: parse_straight_s_rgba8 >>
morph_color: parse_straight_s_rgba8 >>
(ast::fill_styles::MorphSolid {color, morph_color})
)
}
pub fn parse_morph_line_style_list(input: &[u8], version: MorphShapeVersion) -> NomResult<&[u8], Vec<ast::MorphLineStyle>> {
if version >= MorphShapeVersion::MorphShape2 {
length_count!(input, apply!(parse_list_length, true), parse_morph_line_style2)
} else {
length_count!(input, apply!(parse_list_length, true), parse_morph_line_style1)
}
}
pub fn parse_morph_line_style1(input: &[u8]) -> NomResult<&[u8], ast::MorphLineStyle> {
do_parse!(
input,
width: parse_le_u16 >>
morph_width: parse_le_u16 >>
color: parse_straight_s_rgba8 >>
morph_color: parse_straight_s_rgba8 >>
(ast::MorphLineStyle {
width: width,
morph_width: morph_width,
start_cap: ast::CapStyle::Round,
end_cap: ast::CapStyle::Round,
join: ast::JoinStyle::Round,
no_h_scale: false,
no_v_scale: false,
no_close: false,
pixel_hinting: false,
fill: ast::MorphFillStyle::Solid(ast::fill_styles::MorphSolid {
color,
morph_color,
}),
})
)
}
pub fn parse_morph_line_style2(input: &[u8]) -> NomResult<&[u8], ast::MorphLineStyle> {
fn cap_style_from_id(cap_style_id: u16) -> ast::CapStyle {
match cap_style_id {
0 => ast::CapStyle::Round,
1 => ast::CapStyle::None,
2 => ast::CapStyle::Square,
_ => panic!("Unexpected cap style id"),
}
}
do_parse!(
input,
width: parse_le_u16 >>
morph_width: parse_le_u16 >>
flags: parse_le_u16 >>
pixel_hinting: value!((flags & (1 << 0)) != 0) >>
no_v_scale: value!((flags & (1 << 1)) != 0) >>
no_h_scale: value!((flags & (1 << 2)) != 0) >>
has_fill: value!((flags & (1 << 3)) != 0) >>
join_style_id: value!((flags >> 4) & 0b11) >>
start_cap_style_id: value!((flags >> 6) & 0b11) >>
end_cap_style_id: value!((flags >> 8) & 0b11) >>
no_close: value!((flags & (1 << 10)) != 0) >>
start_cap: map!(value!(start_cap_style_id), cap_style_from_id) >>
end_cap: map!(value!(end_cap_style_id), cap_style_from_id) >>
join: switch!(value!(join_style_id),
0 => value!(ast::JoinStyle::Round) |
1 => value!(ast::JoinStyle::Bevel) |
2 => do_parse!(
limit: parse_le_u16 >>
(ast::JoinStyle::Miter(ast::join_styles::Miter{limit}))
)
) >>
fill: switch!(value!(has_fill),
true => call!(parse_morph_fill_style) |
false => do_parse!(
color: parse_straight_s_rgba8 >>
morph_color: parse_straight_s_rgba8 >>
(ast::MorphFillStyle::Solid(ast::fill_styles::MorphSolid { color, morph_color }))
)
) >>
(ast::MorphLineStyle {
width,
morph_width,
fill,
pixel_hinting,
no_v_scale,
no_h_scale,
no_close,
join,
start_cap,
end_cap,
})
)
}