diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 5e9d107c..5d93c31f 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -71,10 +71,10 @@ fn main() { let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)]; - let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges) - .unwrap(); + let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges) + .unwrap(); let mut outline_builder = OutlineBuilder::new(); - for (_, glyph_id) in glyph_ranges.iter().enumerate() { + for (_, glyph_id) in glyph_mapping.iter() { outline_builder.add_glyph(&font, glyph_id).unwrap(); glyph_count += 1 } diff --git a/examples/dump-outlines.rs b/examples/dump-outlines.rs index 7d27f5c4..7257fba8 100644 --- a/examples/dump-outlines.rs +++ b/examples/dump-outlines.rs @@ -17,8 +17,8 @@ fn main() { unsafe { let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); - for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() { + let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges).unwrap(); + for (glyph_index, (_, glyph_id)) in glyph_mapping.iter().enumerate() { let codepoint = '!' as u32 + glyph_index as u32; println!("Glyph {}: codepoint {} '{}':", glyph_id, diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 17a1af03..9dae7725 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -63,17 +63,19 @@ fn main() { unsafe { let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)]; - let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); + let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges).unwrap(); let mut outline_builder = OutlineBuilder::new(); - for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() { + let mut glyph_count = 0; + for (_, glyph_id) in glyph_mapping.iter() { outline_builder.add_glyph(&font, glyph_id).unwrap(); + glyph_count += 1 } outlines = outline_builder.create_buffers().unwrap(); let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height); - for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() { - atlas_builder.pack_glyph(&outlines, glyph_index as u16, point_size).unwrap(); + for glyph_index in 0..glyph_count { + atlas_builder.pack_glyph(&outlines, glyph_index, point_size).unwrap(); } atlas = atlas_builder.create_atlas().unwrap(); } diff --git a/examples/lorem-ipsum.rs b/examples/lorem-ipsum.rs index 890345dd..b5e43f50 100644 --- a/examples/lorem-ipsum.rs +++ b/examples/lorem-ipsum.rs @@ -20,9 +20,8 @@ use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode}; use memmap::{Mmap, Protection}; use pathfinder::atlas::{Atlas, AtlasBuilder}; -use pathfinder::charmap::CodepointRanges; +use pathfinder::charmap::{CodepointRanges, GlyphMapping}; use pathfinder::coverage::CoverageBuffer; -use pathfinder::glyph_range::GlyphRanges; use pathfinder::otf::Font; use pathfinder::outline::{OutlineBuilder, Outlines}; use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions}; @@ -94,24 +93,24 @@ fn main() { let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars); let file = Mmap::open_path(font_path, Protection::Read).unwrap(); - let (font, glyph_ranges); + let (font, glyph_mapping); unsafe { font = Font::new(file.as_slice()).unwrap(); - glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges.ranges).unwrap(); + glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges.ranges).unwrap(); } // Do some basic line breaking. let mut glyph_positions = vec![]; let paragraph_width = (device_pixel_size.width as f32 * font.units_per_em() as f32 / INITIAL_POINT_SIZE) as u32; - let space_advance = font.metrics_for_glyph(glyph_ranges.glyph_for(' ' as u32).unwrap()) + let space_advance = font.metrics_for_glyph(glyph_mapping.glyph_for(' ' as u32).unwrap()) .unwrap() .advance_width as u32; let line_spacing = font.units_per_em() as u32; let (mut current_x, mut current_y) = (0, line_spacing); for word in text.split_whitespace() { - let shaped_glyph_positions = shaper::shape_text(&font, &glyph_ranges, word); + let shaped_glyph_positions = shaper::shape_text(&font, &glyph_mapping, word); let total_advance: u32 = shaped_glyph_positions.iter().map(|p| p.advance as u32).sum(); if current_x + total_advance > paragraph_width { current_x = 0; @@ -138,7 +137,7 @@ fn main() { let mut outline_builder = OutlineBuilder::new(); let mut glyph_indices = vec![]; let mut glyph_count = 0; - for glyph_id in glyph_ranges.iter() { + for (_, glyph_id) in glyph_mapping.iter() { let glyph_index = outline_builder.add_glyph(&font, glyph_id).unwrap(); while glyph_id as usize >= glyph_indices.len() { @@ -177,7 +176,7 @@ fn main() { &outlines, &device_pixel_size, &glyph_indices, - &glyph_ranges, + &glyph_mapping, draw_time, accum_time, timing, @@ -597,7 +596,7 @@ impl Renderer { outlines: &Outlines, device_pixel_size: &Size2D, glyph_indices: &[u16], - glyph_ranges: &GlyphRanges, + glyph_mapping: &GlyphMapping, draw_time: f64, accum_time: f64, composite_time: f64, @@ -642,7 +641,7 @@ impl Renderer { let mut fps_glyphs = vec![]; let mut current_x = 0; - for glyph_pos in &shaper::shape_text(&font, &glyph_ranges, &fps_text) { + for glyph_pos in &shaper::shape_text(&font, &glyph_mapping, &fps_text) { fps_glyphs.push(GlyphPos { x: current_x, y: 0, diff --git a/src/atlas.rs b/src/atlas.rs index bad0e414..1535735f 100644 --- a/src/atlas.rs +++ b/src/atlas.rs @@ -74,7 +74,7 @@ impl AtlasBuilder { let atlas_origin = try!(self.rect_packer.pack(&pixel_bounds.size().cast().unwrap())); let glyph_id = outlines.glyph_id(glyph_index); - let glyph_index = self.image_descriptors.len() as u32; + let glyph_index = self.image_descriptors.len() as u16; while self.image_descriptors.len() < glyph_index as usize + 1 { self.image_descriptors.push(ImageDescriptor::default()) @@ -92,12 +92,12 @@ impl AtlasBuilder { } self.image_metadata[glyph_index as usize] = ImageMetadata { - glyph_index: glyph_index, + glyph_index: glyph_index as u32, glyph_id: glyph_id, - start_index: outlines.descriptors[glyph_index as usize].start_index(), - end_index: match outlines.descriptors.get(glyph_index as usize + 1) { - Some(ref descriptor) => descriptor.start_index() as u32, - None => outlines.indices_count as u32, + start_index: outlines.descriptor(glyph_index).unwrap().start_index(), + end_index: match outlines.descriptor(glyph_index + 1) { + Some(descriptor) => descriptor.start_index() as u32, + None => outlines.indices_count() as u32, }, }; diff --git a/src/charmap.rs b/src/charmap.rs index 6c34b42f..c9c8366a 100644 --- a/src/charmap.rs +++ b/src/charmap.rs @@ -8,19 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/// An inclusive codepoint range. +//! A font's mapping from Unicode codepoints (characters) to glyphs. +//! +//! Consulting this table is typically the first step when rendering some text. + +/// A consecutive series of Unicode codepoints. #[derive(Clone, Copy, Debug)] pub struct CodepointRange { + /// The starting code point, inclusive. pub start: u32, + /// The ending code point, *inclusive*. pub end: u32, } +/// A collection of Unicode codepoints, organized into consecutive series. #[derive(Clone, Debug)] pub struct CodepointRanges { + /// Consecutive series of codepoints. pub ranges: Vec, } impl CodepointRange { + /// Creates a new codepoint range from the given start and end codepoints, *inclusive*. #[inline] pub fn new(start: u32, end: u32) -> CodepointRange { CodepointRange { @@ -29,6 +38,7 @@ impl CodepointRange { } } + /// Returns an iterator that iterates over all codepoints in this range. #[inline] pub fn iter(&self) -> CodepointRangeIter { CodepointRangeIter { @@ -39,6 +49,10 @@ impl CodepointRange { } impl CodepointRanges { + /// Creates codepoint ranges from a sorted array of characters, collapsing duplicates. + /// + /// This is useful when creating an atlas from a string. The array can be readily produced with + /// an expression like `"Hello world".chars().collect()`. pub fn from_sorted_chars(chars: &[char]) -> CodepointRanges { let mut ranges: Vec = vec![]; for &ch in chars { @@ -59,6 +73,7 @@ impl CodepointRanges { } } +/// An iterator over all codepoints in a range. pub struct CodepointRangeIter { start: u32, end: u32, @@ -79,3 +94,173 @@ impl Iterator for CodepointRangeIter { } } +#[doc(hidden)] +#[derive(Clone, Copy, Debug)] +pub struct GlyphRange { + /// The starting glyph ID in the range, inclusive. + pub start: u16, + /// The ending glyph ID in the range, *inclusive*. + pub end: u16, +} + +#[doc(hidden)] +#[derive(Clone, Copy, Debug)] +pub struct MappedGlyphRange { + pub codepoint_start: u32, + pub glyphs: GlyphRange, +} + +/// A map from Unicode codepoints to glyph IDs. +#[derive(Clone, Debug)] +pub struct GlyphMapping { + ranges: Vec, +} + +impl GlyphRange { + /// Returns an iterator over every glyph in this range. + #[inline] + pub fn iter(&self) -> GlyphRangeIter { + GlyphRangeIter { + start: self.start, + end: self.end, + } + } +} + +impl GlyphMapping { + #[doc(hidden)] + #[inline] + pub fn new() -> GlyphMapping { + GlyphMapping { + ranges: vec![], + } + } + + #[doc(hidden)] + #[inline] + pub fn push(&mut self, range: MappedGlyphRange) { + self.ranges.push(range) + } + + + #[inline] + pub fn iter(&self) -> GlyphMappingIter { + if self.ranges.is_empty() { + return GlyphMappingIter { + start: GlyphRangesIndex { + range_index: 0, + glyph_index: 0, + }, + end: GlyphRangesIndex { + range_index: 0, + glyph_index: 0, + }, + codepoint: 0, + ranges: &self.ranges, + } + } + + GlyphMappingIter { + start: GlyphRangesIndex { + range_index: 0, + glyph_index: self.ranges[0].glyphs.start, + }, + end: GlyphRangesIndex { + range_index: (self.ranges.len() - 1) as u16, + glyph_index: self.ranges.last().unwrap().glyphs.end, + }, + codepoint: self.ranges[0].codepoint_start, + ranges: &self.ranges, + } + } + + pub fn glyph_for(&self, codepoint: u32) -> Option { + let (mut lo, mut hi) = (0, self.ranges.len()); + while lo < hi { + let mid = (lo + hi) / 2; + if codepoint < self.ranges[mid].codepoint_start { + hi = mid + } else if codepoint > self.ranges[mid].codepoint_end() { + lo = mid + 1 + } else { + return Some((codepoint - self.ranges[mid].codepoint_start) as u16 + + self.ranges[mid].glyphs.start) + } + } + None + } +} + +#[derive(Clone)] +pub struct GlyphRangeIter { + start: u16, + end: u16, +} + +impl Iterator for GlyphRangeIter { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.start > self.end { + None + } else { + let item = self.start; + self.start += 1; + Some(item) + } + } +} + +/// An iterator over the codepoint-to-glyph mapping. +/// +/// Every call to `next()` returns a tuple consisting of the codepoint and glyph ID, in that order. +#[derive(Clone)] +pub struct GlyphMappingIter<'a> { + start: GlyphRangesIndex, + end: GlyphRangesIndex, + codepoint: u32, + ranges: &'a [MappedGlyphRange], +} + +impl<'a> Iterator for GlyphMappingIter<'a> { + type Item = (u32, u16); + + #[inline] + fn next(&mut self) -> Option<(u32, u16)> { + if self.start.range_index > self.end.range_index { + return None + } + + let item = (self.codepoint, self.start.glyph_index); + + self.codepoint += 1; + self.start.glyph_index += 1; + + while self.start.glyph_index > self.ranges[self.start.range_index as usize].glyphs.end { + self.start.range_index += 1; + if self.start.range_index > self.end.range_index { + break + } + + self.start.glyph_index = self.ranges[self.start.range_index as usize].glyphs.start; + self.codepoint = self.ranges[self.start.range_index as usize].codepoint_start; + } + + Some(item) + } +} + +#[derive(Clone, Copy, Debug)] +struct GlyphRangesIndex { + range_index: u16, + glyph_index: u16, +} + +impl MappedGlyphRange { + /// Inclusive. + #[inline] + pub fn codepoint_end(&self) -> u32 { + self.codepoint_start + self.glyphs.end as u32 - self.glyphs.start as u32 + } +} diff --git a/src/glyph_range.rs b/src/glyph_range.rs deleted file mode 100644 index 9a208bcf..00000000 --- a/src/glyph_range.rs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[derive(Clone, Copy, Debug)] -pub struct GlyphRange { - pub start: u16, - pub end: u16, -} - -#[derive(Clone, Copy, Debug)] -pub struct MappedGlyphRange { - pub codepoint_start: u32, - pub glyphs: GlyphRange, -} - -#[derive(Clone, Debug)] -pub struct GlyphRanges { - pub ranges: Vec, -} - -impl GlyphRange { - #[inline] - pub fn iter(&self) -> GlyphRangeIter { - GlyphRangeIter { - start: self.start, - end: self.end, - } - } -} - -impl GlyphRanges { - #[inline] - pub fn new() -> GlyphRanges { - GlyphRanges { - ranges: vec![], - } - } - - #[inline] - pub fn iter(&self) -> GlyphRangesIter { - if self.ranges.is_empty() { - return GlyphRangesIter { - start: GlyphRangesIndex { - range_index: 0, - glyph_index: 0, - }, - end: GlyphRangesIndex { - range_index: 0, - glyph_index: 0, - }, - ranges: &self.ranges, - } - } - - GlyphRangesIter { - start: GlyphRangesIndex { - range_index: 0, - glyph_index: self.ranges[0].glyphs.start, - }, - end: GlyphRangesIndex { - range_index: (self.ranges.len() - 1) as u16, - glyph_index: self.ranges.last().unwrap().glyphs.end, - }, - ranges: &self.ranges, - } - } - - pub fn glyph_for(&self, codepoint: u32) -> Option { - let (mut lo, mut hi) = (0, self.ranges.len()); - while lo < hi { - let mid = (lo + hi) / 2; - if codepoint < self.ranges[mid].codepoint_start { - hi = mid - } else if codepoint > self.ranges[mid].codepoint_end() { - lo = mid + 1 - } else { - return Some((codepoint - self.ranges[mid].codepoint_start) as u16 + - self.ranges[mid].glyphs.start) - } - } - None - } -} - -#[derive(Clone)] -pub struct GlyphRangeIter { - start: u16, - end: u16, -} - -impl Iterator for GlyphRangeIter { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.start > self.end { - None - } else { - let item = self.start; - self.start += 1; - Some(item) - } - } -} - -#[derive(Clone)] -pub struct GlyphRangesIter<'a> { - start: GlyphRangesIndex, - end: GlyphRangesIndex, - ranges: &'a [MappedGlyphRange], -} - -impl<'a> Iterator for GlyphRangesIter<'a> { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.start.range_index > self.end.range_index { - return None - } - - let item = self.start.glyph_index; - - self.start.glyph_index += 1; - while self.start.glyph_index > self.ranges[self.start.range_index as usize].glyphs.end { - self.start.range_index += 1; - if self.start.range_index > self.end.range_index { - break - } - self.start.glyph_index = self.ranges[self.start.range_index as usize].glyphs.start - } - - Some(item) - } -} - -#[derive(Clone, Copy, Debug)] -struct GlyphRangesIndex { - range_index: u16, - glyph_index: u16, -} - -impl MappedGlyphRange { - /// Inclusive. - #[inline] - pub fn codepoint_end(&self) -> u32 { - self.codepoint_start + self.glyphs.end as u32 - self.glyphs.start as u32 - } -} - diff --git a/src/lib.rs b/src/lib.rs index 0b74dcc7..ed4ae36a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! A high-performance GPU rasterizer for OpenType fonts. +//! +//! Pathfinder rasterizes glyphs from a `.ttf`, `.ttc`, or `.otf` file loaded in memory to an atlas +//! texture. + #![cfg_attr(test, feature(test))] #[macro_use] @@ -28,7 +33,6 @@ pub mod atlas; pub mod charmap; pub mod coverage; pub mod error; -pub mod glyph_range; pub mod otf; pub mod outline; pub mod rasterizer; diff --git a/src/otf/cmap.rs b/src/otf/cmap.rs index 73fadbad..37be2c83 100644 --- a/src/otf/cmap.rs +++ b/src/otf/cmap.rs @@ -9,8 +9,7 @@ // except according to those terms. use byteorder::{BigEndian, ReadBytesExt}; -use charmap::CodepointRange; -use glyph_range::{GlyphRange, GlyphRanges, MappedGlyphRange}; +use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange}; use otf::{Error, FontTable}; use std::char; use std::cmp; @@ -41,8 +40,8 @@ impl<'a> CmapTable<'a> { } } - pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) - -> Result { + pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) + -> Result { let mut cmap_reader = self.table.bytes; // Check version. @@ -81,22 +80,22 @@ impl<'a> CmapTable<'a> { let format = try!(cmap_reader.read_u16::().map_err(Error::eof)); match format { FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => { - self.glyph_ranges_for_codepoint_ranges_segment_mapping_format(cmap_reader, - codepoint_ranges) + self.glyph_mapping_for_codepoint_ranges_segment_mapping_format(cmap_reader, + codepoint_ranges) } FORMAT_SEGMENTED_COVERAGE => { - self.glyph_ranges_for_codepoint_ranges_segmented_coverage(cmap_reader, - codepoint_ranges) + self.glyph_mapping_for_codepoint_ranges_segmented_coverage(cmap_reader, + codepoint_ranges) } _ => Err(Error::UnsupportedCmapFormat), } } - fn glyph_ranges_for_codepoint_ranges_segment_mapping_format( + fn glyph_mapping_for_codepoint_ranges_segment_mapping_format( &self, mut cmap_reader: &[u8], codepoint_ranges: &[CodepointRange]) - -> Result { + -> Result { // Read the mapping table header. let _length = try!(cmap_reader.read_u16::().map_err(Error::eof)); let _language = try!(cmap_reader.read_u16::().map_err(Error::eof)); @@ -122,12 +121,12 @@ impl<'a> CmapTable<'a> { try!(glyph_ids.jump(seg_count as usize * mem::size_of::()).map_err(Error::eof)); // Now perform the lookups. - let mut glyph_ranges = GlyphRanges::new(); + let mut glyph_mapping = GlyphMapping::new(); for codepoint_range in codepoint_ranges { let mut codepoint_range = *codepoint_range; while codepoint_range.end >= codepoint_range.start { if codepoint_range.start > u16::MAX as u32 { - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: codepoint_range.start, glyphs: GlyphRange { start: MISSING_GLYPH, @@ -170,7 +169,7 @@ impl<'a> CmapTable<'a> { let segment_index = match segment_index { Some(segment_index) => segment_index, None => { - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: codepoint_range.start, glyphs: GlyphRange { start: MISSING_GLYPH, @@ -209,7 +208,7 @@ impl<'a> CmapTable<'a> { // Microsoft's documentation is contradictory as to whether the code offset or // the actual code is added to the ID delta here. In reality it seems to be the // latter. - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: start_codepoint_range as u32, glyphs: GlyphRange { start: (start_codepoint_range as i16).wrapping_add(id_delta) as u16, @@ -226,7 +225,7 @@ impl<'a> CmapTable<'a> { id_range_offset as usize).map_err(Error::eof)); let mut glyph_id = try!(reader.read_u16::().map_err(Error::eof)); if glyph_id == 0 { - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: start_code as u32 + code_offset as u32, glyphs: GlyphRange { start: MISSING_GLYPH, @@ -235,7 +234,7 @@ impl<'a> CmapTable<'a> { }) } else { glyph_id = (glyph_id as i16).wrapping_add(id_delta) as u16; - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: start_code as u32 + code_offset as u32, glyphs: GlyphRange { start: glyph_id, @@ -247,20 +246,20 @@ impl<'a> CmapTable<'a> { } } - Ok(glyph_ranges) + Ok(glyph_mapping) } - fn glyph_ranges_for_codepoint_ranges_segmented_coverage(&self, - mut cmap_reader: &[u8], - codepoint_ranges: &[CodepointRange]) - -> Result { + fn glyph_mapping_for_codepoint_ranges_segmented_coverage(&self, + mut cmap_reader: &[u8], + codepoint_ranges: &[CodepointRange]) + -> Result { let _reserved = try!(cmap_reader.read_u16::().map_err(Error::eof)); let _length = try!(cmap_reader.read_u32::().map_err(Error::eof)); let _language = try!(cmap_reader.read_u32::().map_err(Error::eof)); let num_groups = try!(cmap_reader.read_u32::().map_err(Error::eof)); // Now perform the lookups. - let mut glyph_ranges = GlyphRanges::new(); + let mut glyph_mapping = GlyphMapping::new(); for codepoint_range in codepoint_ranges { let mut codepoint_range = *codepoint_range; while codepoint_range.end >= codepoint_range.start { @@ -291,7 +290,7 @@ impl<'a> CmapTable<'a> { match found_segment { None => { - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: codepoint_range.start, glyphs: GlyphRange { start: MISSING_GLYPH, @@ -302,7 +301,7 @@ impl<'a> CmapTable<'a> { } Some(segment) => { let end = cmp::min(codepoint_range.end, segment.end_char_code); - glyph_ranges.ranges.push(MappedGlyphRange { + glyph_mapping.push(MappedGlyphRange { codepoint_start: codepoint_range.start, glyphs: GlyphRange { start: (segment.start_glyph_id + codepoint_range.start - @@ -317,7 +316,7 @@ impl<'a> CmapTable<'a> { } } - Ok(glyph_ranges) + Ok(glyph_mapping) } } diff --git a/src/otf/mod.rs b/src/otf/mod.rs index 87796193..81741967 100644 --- a/src/otf/mod.rs +++ b/src/otf/mod.rs @@ -11,8 +11,7 @@ //! OpenType fonts. use byteorder::{BigEndian, ReadBytesExt}; -use charmap::CodepointRange; -use glyph_range::GlyphRanges; +use charmap::{CodepointRange, GlyphMapping}; use otf::cmap::CmapTable; use otf::glyf::{GlyfTable, Point}; use otf::head::HeadTable; @@ -273,9 +272,9 @@ impl<'a> Font<'a> { /// /// The returned glyph ranges are in the same order as the codepoints. #[inline] - pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) - -> Result { - self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges) + pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) + -> Result { + self.cmap.glyph_mapping_for_codepoint_ranges(codepoint_ranges) } /// Calls the given callback for each point in the supplied glyph's contour. diff --git a/src/outline.rs b/src/outline.rs index ea40e39a..894dbb97 100644 --- a/src/outline.rs +++ b/src/outline.rs @@ -24,13 +24,15 @@ static DUMMY_VERTEX: Vertex = Vertex { glyph_index: 0, }; +/// Packs up outlines for glyphs into a format that the GPU can process. pub struct OutlineBuilder { - pub vertices: Vec, - pub indices: Vec, - pub descriptors: Vec, + vertices: Vec, + indices: Vec, + descriptors: Vec, } impl OutlineBuilder { + /// Creates a new empty set of outlines. #[inline] pub fn new() -> OutlineBuilder { OutlineBuilder { @@ -40,8 +42,8 @@ impl OutlineBuilder { } } - /// Adds a new glyph to the outline builder. Returns the glyph index, useful for calls to - /// `Atlas::pack_glyph()`. + /// Adds a new glyph to the outline builder. Returns the glyph index, which is useful for later + /// calls to `Atlas::pack_glyph()`. pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result { let glyph_index = self.descriptors.len() as u16; @@ -82,6 +84,7 @@ impl OutlineBuilder { Ok(glyph_index) } + /// Uploads the outlines to the GPU. pub fn create_buffers(self) -> Result { // TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contour types and // counts. @@ -121,12 +124,13 @@ impl OutlineBuilder { } } +/// Resolution-independent glyph vectors uploaded to the GPU. pub struct Outlines { - pub vertices_buffer: GLuint, - pub indices_buffer: GLuint, - pub descriptors_buffer: GLuint, - pub descriptors: Vec, - pub indices_count: usize, + vertices_buffer: GLuint, + indices_buffer: GLuint, + descriptors_buffer: GLuint, + descriptors: Vec, + indices_count: usize, } impl Drop for Outlines { @@ -140,6 +144,36 @@ impl Drop for Outlines { } impl Outlines { + #[doc(hidden)] + #[inline] + pub fn vertices_buffer(&self) -> GLuint { + self.vertices_buffer + } + + #[doc(hidden)] + #[inline] + pub fn indices_buffer(&self) -> GLuint { + self.indices_buffer + } + + #[doc(hidden)] + #[inline] + pub fn descriptors_buffer(&self) -> GLuint { + self.descriptors_buffer + } + + #[doc(hidden)] + #[inline] + pub fn descriptor(&self, glyph_index: u16) -> Option<&GlyphDescriptor> { + self.descriptors.get(glyph_index as usize) + } + + #[doc(hidden)] + #[inline] + pub fn indices_count(&self) -> usize { + self.indices_count + } + /// Returns the glyph rectangle in font units. #[inline] pub fn glyph_bounds(&self, glyph_index: u32) -> GlyphBounds { @@ -190,14 +224,13 @@ impl GlyphDescriptor { } } +#[doc(hidden)] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct Vertex { - pub x: i16, - pub y: i16, - /// TODO(pcwalton): Try omitting this and binary search the glyph descriptors in the vertex - /// shader. Might or might not help. - pub glyph_index: u16, + x: i16, + y: i16, + glyph_index: u16, } /// The boundaries of the glyph in fractional pixels. @@ -245,6 +278,7 @@ impl GlyphPixelBounds { } } +/// The boundaries of a glyph in font units. #[derive(Copy, Clone, Debug)] pub struct GlyphBounds { pub left: i32, @@ -254,6 +288,8 @@ pub struct GlyphBounds { } impl GlyphBounds { + /// Given the units per em of the font and the point size, returns the fractional boundaries of + /// this glyph. #[inline] pub fn subpixel_bounds(&self, units_per_em: u16, point_size: f32) -> GlyphSubpixelBounds { let pixels_per_unit = point_size / units_per_em as f32; diff --git a/src/rasterizer.rs b/src/rasterizer.rs index ebd96d1d..aa76915b 100644 --- a/src/rasterizer.rs +++ b/src/rasterizer.rs @@ -163,7 +163,7 @@ impl Rasterizer { gl::UseProgram(self.draw_program); // Set up the buffer layout. - gl::BindBuffer(gl::ARRAY_BUFFER, outlines.vertices_buffer); + gl::BindBuffer(gl::ARRAY_BUFFER, outlines.vertices_buffer()); gl::VertexAttribIPointer(self.draw_position_attribute as GLuint, 2, gl::SHORT, @@ -177,9 +177,9 @@ impl Rasterizer { gl::EnableVertexAttribArray(self.draw_position_attribute as GLuint); gl::EnableVertexAttribArray(self.draw_glyph_index_attribute as GLuint); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, outlines.indices_buffer); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, outlines.indices_buffer()); - gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, outlines.descriptors_buffer); + gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, outlines.descriptors_buffer()); gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, atlas.images_buffer()); gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1); gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2); diff --git a/src/shaper.rs b/src/shaper.rs index 1f37a271..bb85e28b 100644 --- a/src/shaper.rs +++ b/src/shaper.rs @@ -14,7 +14,7 @@ //! ligation, or advanced typography features (`GSUB`, `GPOS`, text morphing). Consider HarfBuzz or //! the system shaper instead. -use glyph_range::GlyphRanges; +use charmap::GlyphMapping; use otf::Font; use std::cmp; @@ -22,11 +22,11 @@ use std::cmp; /// /// See the description of this module for caveats. /// -/// For proper operation, the given `glyph_ranges` must include all the glyphs necessary to render +/// For proper operation, the given `glyph_mapping` must include all the glyphs necessary to render /// the string. -pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec { +pub fn shape_text(font: &Font, glyph_mapping: &GlyphMapping, string: &str) -> Vec { string.chars().map(|ch| { - let glyph_id = glyph_ranges.glyph_for(ch as u32).unwrap_or(0); + let glyph_id = glyph_mapping.glyph_for(ch as u32).unwrap_or(0); let metrics = font.metrics_for_glyph(glyph_id); let advance = match metrics {