diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 392ed118..f7b05ed4 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -25,10 +25,9 @@ use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; use std::env; use std::os::raw::c_void; -const POINT_SIZE: f32 = 24.0; +const DEFAULT_POINT_SIZE: f32 = 24.0; const WIDTH: u32 = 512; const HEIGHT: u32 = 384; -const SHELF_HEIGHT: u32 = 32; fn main() { let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); @@ -49,10 +48,19 @@ fn main() { let rasterizer_options = RasterizerOptions::from_env().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 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 { let font = Font::new(file.as_slice()).unwrap(); 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(); for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() { 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() } } diff --git a/src/glyph_buffer.rs b/src/glyph_buffer.rs index 0a0e9c43..3978d4dd 100644 --- a/src/glyph_buffer.rs +++ b/src/glyph_buffer.rs @@ -52,11 +52,11 @@ impl GlyphBufferBuilder { glyph_index: glyph_index, }); - if !point.first_point_in_contour && point.on_curve { - let indices = if last_point_on_curve { - [point_index - 1, 0, point_index] - } else { + if point.index_in_contour > 0 && point.on_curve { + let indices = if !last_point_on_curve { [point_index - 2, point_index - 1, point_index] + } else { + [point_index - 1, 0, point_index] }; self.indices.extend(indices.iter().cloned()); } diff --git a/src/otf/glyf.rs b/src/otf/glyf.rs index b73244c1..d3f252d2 100644 --- a/src/otf/glyf.rs +++ b/src/otf/glyf.rs @@ -28,11 +28,11 @@ bitflags! { } } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Debug)] pub struct Point { pub position: Point2D, + pub index_in_contour: u16, 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. @@ -97,7 +97,12 @@ impl<'a> GlyfTable<'a> { for _ in 0..number_of_contours { let contour_point_count = try!(endpoints_reader.read_u16::().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 { let flags = Flags::from_bits_truncate(*flag_parser.current); try!(flag_parser.next()); @@ -121,36 +126,63 @@ impl<'a> GlyfTable<'a> { } if last_point_was_off_curve && !flags.contains(ON_CURVE) { + let position = position + delta / 2; + + // An important edge case! + if first_on_curve_point.is_none() { + first_on_curve_point = Some(position) + } + callback(&Point { - position: position + delta / 2, + position: position, + index_in_contour: point_index_in_contour, on_curve: true, - first_point_in_contour: false, - }) + }); + point_index_in_contour += 1 } position = position + delta; - let first_point_in_contour = contour_point_index == 0; - if first_point_in_contour { - starting_point = position + if flags.contains(ON_CURVE) && first_on_curve_point.is_none() { + first_on_curve_point = Some(position) } - callback(&Point { - position: position, - on_curve: flags.contains(ON_CURVE), - first_point_in_contour: first_point_in_contour, - }); + // 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); - 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. - callback(&Point { - position: starting_point, - on_curve: true, - first_point_in_contour: false, - }) + if let Some(first_on_curve_point) = first_on_curve_point { + callback(&Point { + position: first_on_curve_point, + on_curve: true, + index_in_contour: point_index_in_contour, + }) + } } Ok(())