Split get_glyph_shape, reduce complexity

parent 21622bf1
......@@ -644,68 +644,7 @@ impl<Data: Deref<Target = [u8]>> FontInfo<Data> {
/// Like `get_codepoint_shape`, but takes a glyph index instead. Use this
/// if you have cached the glyph index for a codepoint.
#[allow(cyclomatic_complexity)] // FIXME rework
pub fn get_glyph_shape(&self, glyph_index: u32) -> Option<Vec<Vertex>> {
use VertexType::*;
#[derive(Clone)]
struct FlagData {
flags: u8,
x: i16,
y: i16,
}
fn close_shape(
vertices: &mut Vec<Vertex>,
// num_vertices: &mut usize,
was_off: bool,
start_off: bool,
sx: i16,
sy: i16,
scx: i16,
scy: i16,
cx: i16,
cy: i16,
) {
use VertexType::*;
if start_off {
if was_off {
vertices.push(Vertex {
type_: CurveTo as u8,
x: (cx + scx) >> 1,
y: (cy + scy) >> 1,
cx,
cy,
});
}
vertices.push(Vertex {
type_: CurveTo as u8,
x: sx as i16,
y: sy as i16,
cx: scx,
cy: scy,
});
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x: sx as i16,
y: sy as i16,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x: sx as i16,
y: sy as i16,
cx: 0,
cy: 0,
}
});
}
}
let g = match self.get_glyf_offset(glyph_index) {
Some(g) => &self.data[g as usize..],
None => return None,
......@@ -713,223 +652,7 @@ impl<Data: Deref<Target = [u8]>> FontInfo<Data> {
let number_of_contours = BE::read_i16(g);
let vertices: Vec<Vertex> = if number_of_contours > 0 {
let number_of_contours = number_of_contours as usize;
let mut start_off = false;
let mut was_off = false;
let end_points_of_contours = &g[10..];
let ins = BE::read_u16(&g[10 + number_of_contours * 2..]) as usize;
let mut points = &g[10 + number_of_contours * 2 + 2 + ins..];
let n =
1 + BE::read_u16(&end_points_of_contours[number_of_contours * 2 - 2..]) as usize;
let m = n + 2 * number_of_contours; // a loose bound on how many vertices we might need
let mut vertices: Vec<Vertex> = Vec::with_capacity(m);
// unsafe { vertices.set_len(m) };
let mut flag_data = Vec::with_capacity(n);
let mut next_move = 0;
// in first pass, we load uninterpreted data into the allocated array
// above, shifted to the end of the array so we won't overwrite it when
// we create our final data starting from the front
// starting offset for uninterpreted data, regardless of how m ends up being
// calculated
// let off = m - n;
// first load flags
{
let mut flagcount = 0;
let mut flags = 0;
for _ in 0..n {
if flagcount == 0 {
flags = points[0];
if flags & 8 != 0 {
flagcount = points[1];
points = &points[2..];
} else {
points = &points[1..];
}
} else {
flagcount -= 1;
}
flag_data.push(FlagData { flags, x: 0, y: 0 });
}
}
// now load x coordinates
let mut x_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 2 != 0 {
let dx = i16::from(points[0]);
points = &points[1..];
if flags & 16 != 0 {
// ???
x_coord += dx;
} else {
x_coord -= dx;
}
} else if flags & 16 == 0 {
x_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.x = x_coord;
}
// now load y coordinates
let mut y_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 4 != 0 {
let dy = i16::from(points[0]);
points = &points[1..];
if flags & 32 != 0 {
y_coord += dy;
} else {
y_coord -= dy;
}
} else if flags & 32 == 0 {
y_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.y = y_coord;
}
// now convert them to our format
// let mut num_vertices = 0;
let mut sx = 0;
let mut sy = 0;
let mut cx = 0;
let mut cy = 0;
let mut scx = 0;
let mut scy = 0;
// let mut i = 0;
let mut j = 0;
let mut iter = flag_data.into_iter().enumerate().peekable();
while let Some((index, FlagData { flags, x, y })) = iter.next() {
// while i < n {
// let flags = vertices[off + i].type_;
// x = vertices[off + i].x as i32;
// y = vertices[off + i].y as i32;
if next_move == index {
if index != 0 {
close_shape(
&mut vertices,
// &mut num_vertices,
was_off,
start_off,
sx,
sy,
scx,
scy,
cx,
cy,
);
}
// now start the new one
start_off = flags & 1 == 0;
if start_off {
// if we start off with an off-curve point, then when we need to find a
// point on the curve where we can start, and we
// need to save some state for
// when we wraparound.
scx = x;
scy = y;
let (next_flags, next_x, next_y) = {
let peek = &iter.peek().unwrap().1;
(peek.flags, peek.x, peek.y)
};
if next_flags & 1 == 0 {
// next point is also a curve point, so interpolate an on-point curve
sx = (x + next_x) >> 1;
sy = (y + next_y) >> 1;
} else {
// otherwise just use the next point as our start point
sx = next_x;
sy = next_y;
let _ = iter.next();
// i += 1; // we're using point i+1 as the starting point, so skip it
}
} else {
sx = x;
sy = y;
}
vertices.push(Vertex {
type_: MoveTo as u8,
x: sx,
y: sy,
cx: 0,
cy: 0,
});
// num_vertices += 1;
was_off = false;
next_move = 1 + BE::read_u16(&end_points_of_contours[j * 2..]) as usize;
j += 1;
} else if flags & 1 == 0 {
// if it's a curve
if was_off {
// two off-curve control points in a row means interpolate an on-curve
// midpoint
vertices.push(Vertex {
type_: CurveTo as u8,
x: ((cx + x) >> 1),
y: ((cy + y) >> 1),
cx,
cy,
});
// num_vertices += 1;
}
cx = x;
cy = y;
was_off = true;
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x,
y,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x,
y,
cx: 0,
cy: 0,
}
});
// num_vertices += 1;
was_off = false;
}
// i += 1;
}
close_shape(
&mut vertices,
// &mut num_vertices,
was_off,
start_off,
sx,
sy,
scx,
scy,
cx,
cy,
);
// assert!(num_vertices <= vertices.len());
// vertices.truncate(num_vertices);
vertices
self.glyph_shape_positive_contours(g, number_of_contours as usize)
} else if number_of_contours == -1 {
// Compound shapes
let mut more = true;
......@@ -1020,6 +743,258 @@ impl<Data: Deref<Target = [u8]>> FontInfo<Data> {
Some(vertices)
}
#[inline]
fn glyph_shape_positive_contours(
&self,
glyph_data: &[u8],
number_of_contours: usize,
) -> Vec<Vertex> {
use VertexType::*;
struct FlagData {
flags: u8,
x: i16,
y: i16,
}
#[inline]
fn close_shape(
vertices: &mut Vec<Vertex>,
was_off: bool,
start_off: bool,
sx: i16,
sy: i16,
scx: i16,
scy: i16,
cx: i16,
cy: i16,
) {
if start_off {
if was_off {
vertices.push(Vertex {
type_: CurveTo as u8,
x: (cx + scx) >> 1,
y: (cy + scy) >> 1,
cx,
cy,
});
}
vertices.push(Vertex {
type_: CurveTo as u8,
x: sx as i16,
y: sy as i16,
cx: scx,
cy: scy,
});
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x: sx as i16,
y: sy as i16,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x: sx as i16,
y: sy as i16,
cx: 0,
cy: 0,
}
});
}
}
let number_of_contours = number_of_contours as usize;
let mut start_off = false;
let mut was_off = false;
let end_points_of_contours = &glyph_data[10..];
let ins = BE::read_u16(&glyph_data[10 + number_of_contours * 2..]) as usize;
let mut points = &glyph_data[10 + number_of_contours * 2 + 2 + ins..];
let n = 1 + BE::read_u16(&end_points_of_contours[number_of_contours * 2 - 2..]) as usize;
let m = n + 2 * number_of_contours; // a loose bound on how many vertices we might need
let mut vertices: Vec<Vertex> = Vec::with_capacity(m);
let mut flag_data = Vec::with_capacity(n);
let mut next_move = 0;
// in first pass, we load uninterpreted data into the allocated array above
// first load flags
{
let mut flagcount = 0;
let mut flags = 0;
for _ in 0..n {
if flagcount == 0 {
flags = points[0];
if flags & 8 != 0 {
flagcount = points[1];
points = &points[2..];
} else {
points = &points[1..];
}
} else {
flagcount -= 1;
}
flag_data.push(FlagData { flags, x: 0, y: 0 });
}
}
// now load x coordinates
let mut x_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 2 != 0 {
let dx = i16::from(points[0]);
points = &points[1..];
if flags & 16 != 0 {
// ???
x_coord += dx;
} else {
x_coord -= dx;
}
} else if flags & 16 == 0 {
x_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.x = x_coord;
}
// now load y coordinates
let mut y_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 4 != 0 {
let dy = i16::from(points[0]);
points = &points[1..];
if flags & 32 != 0 {
y_coord += dy;
} else {
y_coord -= dy;
}
} else if flags & 32 == 0 {
y_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.y = y_coord;
}
// now convert them to our format
let mut sx = 0;
let mut sy = 0;
let mut cx = 0;
let mut cy = 0;
let mut scx = 0;
let mut scy = 0;
let mut j = 0;
let mut iter = flag_data.into_iter().enumerate().peekable();
while let Some((index, FlagData { flags, x, y })) = iter.next() {
if next_move == index {
if index != 0 {
close_shape(&mut vertices, was_off, start_off, sx, sy, scx, scy, cx, cy);
}
// now start the new one
start_off = flags & 1 == 0;
if start_off {
// if we start off with an off-curve point, then when we need to find a
// point on the curve where we can start, and we
// need to save some state for
// when we wraparound.
scx = x;
scy = y;
let (next_flags, next_x, next_y) = {
let peek = &iter.peek().unwrap().1;
(peek.flags, peek.x, peek.y)
};
if next_flags & 1 == 0 {
// next point is also a curve point, so interpolate an on-point curve
sx = (x + next_x) >> 1;
sy = (y + next_y) >> 1;
} else {
// otherwise just use the next point as our start point
sx = next_x;
sy = next_y;
// we're using point i+1 as the starting point, so skip it
let _ = iter.next();
}
} else {
sx = x;
sy = y;
}
vertices.push(Vertex {
type_: MoveTo as u8,
x: sx,
y: sy,
cx: 0,
cy: 0,
});
was_off = false;
next_move = 1 + BE::read_u16(&end_points_of_contours[j * 2..]) as usize;
j += 1;
} else if flags & 1 == 0 {
// if it's a curve
if was_off {
// two off-curve control points in a row means interpolate an on-curve
// midpoint
vertices.push(Vertex {
type_: CurveTo as u8,
x: ((cx + x) >> 1),
y: ((cy + y) >> 1),
cx,
cy,
});
}
cx = x;
cy = y;
was_off = true;
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x,
y,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x,
y,
cx: 0,
cy: 0,
}
});
was_off = false;
}
}
close_shape(
&mut vertices,
// &mut num_vertices,
was_off,
start_off,
sx,
sy,
scx,
scy,
cx,
cy,
);
vertices
}
/// like `get_codepoint_h_metrics`, but takes a glyph index instead. Use
/// this if you have cached the glyph index for a codepoint.
pub fn get_glyph_h_metrics(&self, glyph_index: u32) -> HMetrics {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment