Rename `GlyphRanges` to `GlyphMapping` and document it
This commit is contained in:
parent
dcd3eefa33
commit
ccf17493e0
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<u32>,
|
||||
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,
|
||||
|
|
12
src/atlas.rs
12
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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
187
src/charmap.rs
187
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<CodepointRange>,
|
||||
}
|
||||
|
||||
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<CodepointRange> = 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<MappedGlyphRange>,
|
||||
}
|
||||
|
||||
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<u16> {
|
||||
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<u16> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<MappedGlyphRange>,
|
||||
}
|
||||
|
||||
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<u16> {
|
||||
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<u16> {
|
||||
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<u16> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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<GlyphRanges, Error> {
|
||||
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
|
||||
-> Result<GlyphMapping, Error> {
|
||||
let mut cmap_reader = self.table.bytes;
|
||||
|
||||
// Check version.
|
||||
|
@ -81,22 +80,22 @@ impl<'a> CmapTable<'a> {
|
|||
let format = try!(cmap_reader.read_u16::<BigEndian>().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<GlyphRanges, Error> {
|
||||
-> Result<GlyphMapping, Error> {
|
||||
// Read the mapping table header.
|
||||
let _length = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
let _language = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
|
@ -122,12 +121,12 @@ impl<'a> CmapTable<'a> {
|
|||
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).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::<BigEndian>().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<GlyphRanges, Error> {
|
||||
fn glyph_mapping_for_codepoint_ranges_segmented_coverage(&self,
|
||||
mut cmap_reader: &[u8],
|
||||
codepoint_ranges: &[CodepointRange])
|
||||
-> Result<GlyphMapping, Error> {
|
||||
let _reserved = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
let _length = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
||||
let _language = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
||||
let num_groups = try!(cmap_reader.read_u32::<BigEndian>().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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<GlyphRanges, Error> {
|
||||
self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges)
|
||||
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
|
||||
-> Result<GlyphMapping, Error> {
|
||||
self.cmap.glyph_mapping_for_codepoint_ranges(codepoint_ranges)
|
||||
}
|
||||
|
||||
/// Calls the given callback for each point in the supplied glyph's contour.
|
||||
|
|
|
@ -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<Vertex>,
|
||||
pub indices: Vec<u32>,
|
||||
pub descriptors: Vec<GlyphDescriptor>,
|
||||
vertices: Vec<Vertex>,
|
||||
indices: Vec<u32>,
|
||||
descriptors: Vec<GlyphDescriptor>,
|
||||
}
|
||||
|
||||
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<u16, otf::Error> {
|
||||
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<Outlines, GlError> {
|
||||
// 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<GlyphDescriptor>,
|
||||
pub indices_count: usize,
|
||||
vertices_buffer: GLuint,
|
||||
indices_buffer: GLuint,
|
||||
descriptors_buffer: GLuint,
|
||||
descriptors: Vec<GlyphDescriptor>,
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<GlyphPos> {
|
||||
pub fn shape_text(font: &Font, glyph_mapping: &GlyphMapping, string: &str) -> Vec<GlyphPos> {
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue