Handle glyph outlines in which the first point is off-curve correctly.
These show up in Helvetica.dfont and Times.dfont.
This commit is contained in:
parent
ce383385df
commit
da7c70f035
|
@ -25,10 +25,9 @@ use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
const POINT_SIZE: f32 = 24.0;
|
const DEFAULT_POINT_SIZE: f32 = 24.0;
|
||||||
const WIDTH: u32 = 512;
|
const WIDTH: u32 = 512;
|
||||||
const HEIGHT: u32 = 384;
|
const HEIGHT: u32 = 384;
|
||||||
const SHELF_HEIGHT: u32 = 32;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
|
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
|
||||||
|
@ -49,10 +48,19 @@ fn main() {
|
||||||
let rasterizer_options = RasterizerOptions::from_env().unwrap();
|
let rasterizer_options = RasterizerOptions::from_env().unwrap();
|
||||||
let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap();
|
let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap();
|
||||||
|
|
||||||
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
|
|
||||||
let mut batch_builder = BatchBuilder::new(device_pixel_width as GLuint, SHELF_HEIGHT);
|
|
||||||
|
|
||||||
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
|
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
|
||||||
|
|
||||||
|
let point_size = match env::args().nth(2) {
|
||||||
|
None => DEFAULT_POINT_SIZE,
|
||||||
|
Some(point_size) => point_size.parse().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME(pcwalton)
|
||||||
|
let shelf_height = (point_size * 2.0).ceil() as u32;
|
||||||
|
|
||||||
|
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
|
||||||
|
let mut batch_builder = BatchBuilder::new(device_pixel_width as GLuint, shelf_height);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let font = Font::new(file.as_slice()).unwrap();
|
let font = Font::new(file.as_slice()).unwrap();
|
||||||
let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)];
|
let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)];
|
||||||
|
@ -60,7 +68,7 @@ fn main() {
|
||||||
let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
||||||
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
||||||
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
||||||
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, POINT_SIZE).unwrap()
|
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, point_size).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,11 +52,11 @@ impl GlyphBufferBuilder {
|
||||||
glyph_index: glyph_index,
|
glyph_index: glyph_index,
|
||||||
});
|
});
|
||||||
|
|
||||||
if !point.first_point_in_contour && point.on_curve {
|
if point.index_in_contour > 0 && point.on_curve {
|
||||||
let indices = if last_point_on_curve {
|
let indices = if !last_point_on_curve {
|
||||||
[point_index - 1, 0, point_index]
|
|
||||||
} else {
|
|
||||||
[point_index - 2, point_index - 1, point_index]
|
[point_index - 2, point_index - 1, point_index]
|
||||||
|
} else {
|
||||||
|
[point_index - 1, 0, point_index]
|
||||||
};
|
};
|
||||||
self.indices.extend(indices.iter().cloned());
|
self.indices.extend(indices.iter().cloned());
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,11 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub position: Point2D<i16>,
|
pub position: Point2D<i16>,
|
||||||
|
pub index_in_contour: u16,
|
||||||
pub on_curve: bool,
|
pub on_curve: bool,
|
||||||
pub first_point_in_contour: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time.
|
/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time.
|
||||||
|
@ -97,7 +97,12 @@ impl<'a> GlyfTable<'a> {
|
||||||
for _ in 0..number_of_contours {
|
for _ in 0..number_of_contours {
|
||||||
let contour_point_count =
|
let contour_point_count =
|
||||||
try!(endpoints_reader.read_u16::<BigEndian>().map_err(drop)) - point_index + 1;
|
try!(endpoints_reader.read_u16::<BigEndian>().map_err(drop)) - point_index + 1;
|
||||||
let (mut starting_point, mut last_point_was_off_curve) = (Point2D::new(0, 0), false);
|
|
||||||
|
let mut first_on_curve_point = None;
|
||||||
|
let mut initial_off_curve_point = None;
|
||||||
|
let mut last_point_was_off_curve = false;
|
||||||
|
let mut point_index_in_contour = 0;
|
||||||
|
|
||||||
for contour_point_index in 0..contour_point_count {
|
for contour_point_index in 0..contour_point_count {
|
||||||
let flags = Flags::from_bits_truncate(*flag_parser.current);
|
let flags = Flags::from_bits_truncate(*flag_parser.current);
|
||||||
try!(flag_parser.next());
|
try!(flag_parser.next());
|
||||||
|
@ -121,37 +126,64 @@ impl<'a> GlyfTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if last_point_was_off_curve && !flags.contains(ON_CURVE) {
|
if last_point_was_off_curve && !flags.contains(ON_CURVE) {
|
||||||
callback(&Point {
|
let position = position + delta / 2;
|
||||||
position: position + delta / 2,
|
|
||||||
on_curve: true,
|
|
||||||
first_point_in_contour: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
position = position + delta;
|
// An important edge case!
|
||||||
|
if first_on_curve_point.is_none() {
|
||||||
let first_point_in_contour = contour_point_index == 0;
|
first_on_curve_point = Some(position)
|
||||||
if first_point_in_contour {
|
|
||||||
starting_point = position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(&Point {
|
callback(&Point {
|
||||||
position: position,
|
position: position,
|
||||||
on_curve: flags.contains(ON_CURVE),
|
index_in_contour: point_index_in_contour,
|
||||||
first_point_in_contour: first_point_in_contour,
|
on_curve: true,
|
||||||
});
|
});
|
||||||
|
point_index_in_contour += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
position = position + delta;
|
||||||
|
|
||||||
|
if flags.contains(ON_CURVE) && first_on_curve_point.is_none() {
|
||||||
|
first_on_curve_point = Some(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes the initial point is an off curve point. In that case, save it so we
|
||||||
|
// can emit it later when closing the path.
|
||||||
|
if !flags.contains(ON_CURVE) && first_on_curve_point.is_none() {
|
||||||
|
debug_assert!(initial_off_curve_point.is_none());
|
||||||
|
initial_off_curve_point = Some(position)
|
||||||
|
} else {
|
||||||
|
callback(&Point {
|
||||||
|
position: position,
|
||||||
|
on_curve: flags.contains(ON_CURVE),
|
||||||
|
index_in_contour: point_index_in_contour,
|
||||||
|
});
|
||||||
|
point_index_in_contour += 1
|
||||||
|
}
|
||||||
|
|
||||||
last_point_was_off_curve = !flags.contains(ON_CURVE);
|
last_point_was_off_curve = !flags.contains(ON_CURVE);
|
||||||
point_index += 1
|
point_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're about to close the path. Emit the initial off curve point if there was one.
|
||||||
|
if let Some(initial_off_curve_point) = initial_off_curve_point {
|
||||||
|
callback(&Point {
|
||||||
|
position: initial_off_curve_point,
|
||||||
|
on_curve: false,
|
||||||
|
index_in_contour: point_index_in_contour,
|
||||||
|
});
|
||||||
|
point_index_in_contour += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the path.
|
// Close the path.
|
||||||
|
if let Some(first_on_curve_point) = first_on_curve_point {
|
||||||
callback(&Point {
|
callback(&Point {
|
||||||
position: starting_point,
|
position: first_on_curve_point,
|
||||||
on_curve: true,
|
on_curve: true,
|
||||||
first_point_in_contour: false,
|
index_in_contour: point_index_in_contour,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue