Factor out more code from `lorem-ipsum` into a new `GlyphStore` abstraction
This commit is contained in:
parent
abacb5a4c9
commit
68f5694b6b
|
@ -24,9 +24,9 @@ use pathfinder::charmap::CodepointRanges;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
use pathfinder::error::RasterError;
|
use pathfinder::error::RasterError;
|
||||||
use pathfinder::otf::Font;
|
use pathfinder::otf::Font;
|
||||||
use pathfinder::outline::{GlyphSubpixelBounds, OutlineBuilder, Outlines};
|
use pathfinder::outline::GlyphSubpixelBounds;
|
||||||
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
||||||
use pathfinder::typesetter::{GlyphPosition, Typesetter};
|
use pathfinder::typesetter::{GlyphPosition, GlyphStore, Typesetter};
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
@ -107,10 +107,8 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let units_per_em = font.units_per_em() as f32;
|
let units_per_em = font.units_per_em() as f32;
|
||||||
let mut typesetter =
|
let page_width = device_pixel_size.width as f32 * units_per_em / INITIAL_POINT_SIZE;
|
||||||
Typesetter::new(device_pixel_size.width as f32 * units_per_em / INITIAL_POINT_SIZE,
|
let mut typesetter = Typesetter::new(page_width, &font, units_per_em);
|
||||||
&font,
|
|
||||||
units_per_em);
|
|
||||||
typesetter.add_text(&font, font.units_per_em() as f32, &text);
|
typesetter.add_text(&font, font.units_per_em() as f32, &text);
|
||||||
|
|
||||||
let renderer = Renderer::new();
|
let renderer = Renderer::new();
|
||||||
|
@ -118,41 +116,23 @@ fn main() {
|
||||||
let mut translation = Point2D::new(0, 0);
|
let mut translation = Point2D::new(0, 0);
|
||||||
let mut dirty = true;
|
let mut dirty = true;
|
||||||
|
|
||||||
let mut glyph_ids: Vec<_> = typesetter.glyph_positions.iter().map(|gp| gp.glyph_id).collect();
|
let glyph_store = typesetter.create_glyph_store(&font).unwrap();
|
||||||
|
|
||||||
// Make sure the characters include `[A-Za-z0-9 ./,]`, for the FPS display.
|
// Set up the FPS glyph store.
|
||||||
let mut chars: Vec<char> = text.chars().collect();
|
let mut fps_chars: Vec<char> = vec![];
|
||||||
chars.extend(" ./,:()".chars());
|
fps_chars.extend(" ./,:()".chars());
|
||||||
chars.extend(('A' as u32..('Z' as u32 + 1)).flat_map(char::from_u32));
|
fps_chars.extend(('A' as u32..('Z' as u32 + 1)).flat_map(char::from_u32));
|
||||||
chars.extend(('a' as u32..('z' as u32 + 1)).flat_map(char::from_u32));
|
fps_chars.extend(('a' as u32..('z' as u32 + 1)).flat_map(char::from_u32));
|
||||||
chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32));
|
fps_chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32));
|
||||||
chars.sort();
|
fps_chars.sort();
|
||||||
let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars);
|
let fps_codepoint_ranges = CodepointRanges::from_sorted_chars(&fps_chars);
|
||||||
let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges.ranges).unwrap();
|
let fps_glyph_store = GlyphStore::from_codepoints(&fps_codepoint_ranges, &font).unwrap();
|
||||||
glyph_ids.extend(glyph_mapping.iter().map(|(_, glyph_id)| glyph_id));
|
|
||||||
|
|
||||||
glyph_ids.sort();
|
|
||||||
glyph_ids.dedup();
|
|
||||||
|
|
||||||
let mut outline_builder = OutlineBuilder::new();
|
|
||||||
let mut glyph_indices = vec![];
|
|
||||||
for glyph_id in glyph_ids {
|
|
||||||
let glyph_index = outline_builder.add_glyph(&font, glyph_id).unwrap();
|
|
||||||
|
|
||||||
while glyph_id as usize >= glyph_indices.len() {
|
|
||||||
glyph_indices.push(0)
|
|
||||||
}
|
|
||||||
glyph_indices[glyph_id as usize] = glyph_index
|
|
||||||
}
|
|
||||||
|
|
||||||
let outlines = outline_builder.create_buffers().unwrap();
|
|
||||||
|
|
||||||
while !window.should_close() {
|
while !window.should_close() {
|
||||||
if dirty {
|
if dirty {
|
||||||
let redraw_result = renderer.redraw(point_size,
|
let redraw_result = renderer.redraw(point_size,
|
||||||
&font,
|
&font,
|
||||||
&outlines,
|
&glyph_store,
|
||||||
&glyph_indices,
|
|
||||||
&typesetter.glyph_positions,
|
&typesetter.glyph_positions,
|
||||||
&device_pixel_size,
|
&device_pixel_size,
|
||||||
&translation);
|
&translation);
|
||||||
|
@ -178,9 +158,8 @@ fn main() {
|
||||||
let timing = renderer.get_timing_in_ms();
|
let timing = renderer.get_timing_in_ms();
|
||||||
|
|
||||||
renderer.draw_fps(&font,
|
renderer.draw_fps(&font,
|
||||||
&outlines,
|
&fps_glyph_store,
|
||||||
&device_pixel_size,
|
&device_pixel_size,
|
||||||
&glyph_indices,
|
|
||||||
draw_time,
|
draw_time,
|
||||||
accum_time,
|
accum_time,
|
||||||
timing,
|
timing,
|
||||||
|
@ -424,8 +403,7 @@ impl Renderer {
|
||||||
fn redraw(&self,
|
fn redraw(&self,
|
||||||
point_size: f32,
|
point_size: f32,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
outlines: &Outlines,
|
glyph_store: &GlyphStore,
|
||||||
glyph_indices: &[u16],
|
|
||||||
glyph_positions: &[GlyphPosition],
|
glyph_positions: &[GlyphPosition],
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
translation: &Point2D<i32>)
|
translation: &Point2D<i32>)
|
||||||
|
@ -435,8 +413,7 @@ impl Renderer {
|
||||||
|
|
||||||
let cached_glyphs = self.determine_visible_glyphs(&mut atlas_builder,
|
let cached_glyphs = self.determine_visible_glyphs(&mut atlas_builder,
|
||||||
font,
|
font,
|
||||||
outlines,
|
glyph_store,
|
||||||
glyph_indices,
|
|
||||||
glyph_positions,
|
glyph_positions,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
translation,
|
translation,
|
||||||
|
@ -448,7 +425,7 @@ impl Renderer {
|
||||||
let events = match self.rasterizer.draw_atlas(&self.main_compute_image,
|
let events = match self.rasterizer.draw_atlas(&self.main_compute_image,
|
||||||
&rect,
|
&rect,
|
||||||
&atlas,
|
&atlas,
|
||||||
outlines,
|
&glyph_store.outlines,
|
||||||
&self.main_coverage_buffer) {
|
&self.main_coverage_buffer) {
|
||||||
Ok(events) => Some(events),
|
Ok(events) => Some(events),
|
||||||
Err(RasterError::NoGlyphsToDraw) => None,
|
Err(RasterError::NoGlyphsToDraw) => None,
|
||||||
|
@ -473,9 +450,8 @@ impl Renderer {
|
||||||
|
|
||||||
if events.is_some() {
|
if events.is_some() {
|
||||||
self.draw_glyphs(&font,
|
self.draw_glyphs(&font,
|
||||||
outlines,
|
glyph_store,
|
||||||
&self.main_composite_vertex_array,
|
&self.main_composite_vertex_array,
|
||||||
glyph_indices,
|
|
||||||
glyph_positions,
|
glyph_positions,
|
||||||
&cached_glyphs,
|
&cached_glyphs,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
|
@ -495,8 +471,7 @@ impl Renderer {
|
||||||
fn determine_visible_glyphs(&self,
|
fn determine_visible_glyphs(&self,
|
||||||
atlas_builder: &mut AtlasBuilder,
|
atlas_builder: &mut AtlasBuilder,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
outlines: &Outlines,
|
glyph_store: &GlyphStore,
|
||||||
glyph_indices: &[u16],
|
|
||||||
glyph_positions: &[GlyphPosition],
|
glyph_positions: &[GlyphPosition],
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
translation: &Point2D<i32>,
|
translation: &Point2D<i32>,
|
||||||
|
@ -504,8 +479,8 @@ impl Renderer {
|
||||||
-> Vec<CachedGlyph> {
|
-> Vec<CachedGlyph> {
|
||||||
let mut glyphs = vec![];
|
let mut glyphs = vec![];
|
||||||
for glyph_position in glyph_positions {
|
for glyph_position in glyph_positions {
|
||||||
let glyph_index = glyph_indices[glyph_position.glyph_id as usize];
|
let glyph_index = glyph_store.glyph_index(glyph_position.glyph_id).unwrap();
|
||||||
let glyph_rect = outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
let glyph_rect = glyph_store.outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
||||||
if let Some(subpixel) = self.subpixel_for_glyph_if_visible(font,
|
if let Some(subpixel) = self.subpixel_for_glyph_if_visible(font,
|
||||||
glyph_position,
|
glyph_position,
|
||||||
&glyph_rect,
|
&glyph_rect,
|
||||||
|
@ -521,7 +496,7 @@ impl Renderer {
|
||||||
|
|
||||||
glyphs.iter().map(|&(glyph_index, subpixel)| {
|
glyphs.iter().map(|&(glyph_index, subpixel)| {
|
||||||
let subpixel_offset = (subpixel as f32) / (SUBPIXEL_GRANULARITY as f32);
|
let subpixel_offset = (subpixel as f32) / (SUBPIXEL_GRANULARITY as f32);
|
||||||
let origin = atlas_builder.pack_glyph(&outlines,
|
let origin = atlas_builder.pack_glyph(&glyph_store.outlines,
|
||||||
glyph_index,
|
glyph_index,
|
||||||
point_size,
|
point_size,
|
||||||
subpixel_offset).unwrap();
|
subpixel_offset).unwrap();
|
||||||
|
@ -576,9 +551,8 @@ impl Renderer {
|
||||||
|
|
||||||
fn draw_glyphs(&self,
|
fn draw_glyphs(&self,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
outlines: &Outlines,
|
glyph_store: &GlyphStore,
|
||||||
vertex_array: &CompositeVertexArray,
|
vertex_array: &CompositeVertexArray,
|
||||||
glyph_indices: &[u16],
|
|
||||||
glyph_positions: &[GlyphPosition],
|
glyph_positions: &[GlyphPosition],
|
||||||
cached_glyphs: &[CachedGlyph],
|
cached_glyphs: &[CachedGlyph],
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
|
@ -594,8 +568,7 @@ impl Renderer {
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vertex_array.index_buffer);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vertex_array.index_buffer);
|
||||||
|
|
||||||
let vertex_count = self.upload_quads_for_text(font,
|
let vertex_count = self.upload_quads_for_text(font,
|
||||||
outlines,
|
glyph_store,
|
||||||
glyph_indices,
|
|
||||||
glyph_positions,
|
glyph_positions,
|
||||||
cached_glyphs,
|
cached_glyphs,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
|
@ -636,8 +609,7 @@ impl Renderer {
|
||||||
|
|
||||||
fn upload_quads_for_text(&self,
|
fn upload_quads_for_text(&self,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
outlines: &Outlines,
|
glyph_store: &GlyphStore,
|
||||||
glyph_indices: &[u16],
|
|
||||||
glyph_positions: &[GlyphPosition],
|
glyph_positions: &[GlyphPosition],
|
||||||
cached_glyphs: &[CachedGlyph],
|
cached_glyphs: &[CachedGlyph],
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
|
@ -649,12 +621,8 @@ impl Renderer {
|
||||||
|
|
||||||
let (mut vertices, mut indices) = (vec![], vec![]);
|
let (mut vertices, mut indices) = (vec![], vec![]);
|
||||||
for glyph_position in glyph_positions {
|
for glyph_position in glyph_positions {
|
||||||
let glyph_index = match glyph_indices.get(glyph_position.glyph_id as usize) {
|
let glyph_index = glyph_store.glyph_index(glyph_position.glyph_id).unwrap();
|
||||||
Some(&index) => index,
|
let glyph_rect = glyph_store.outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
||||||
None => glyph_indices[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
let glyph_rect = outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
|
||||||
|
|
||||||
let subpixel = if use_subpixel_positioning {
|
let subpixel = if use_subpixel_positioning {
|
||||||
match self.subpixel_for_glyph_if_visible(font,
|
match self.subpixel_for_glyph_if_visible(font,
|
||||||
|
@ -717,9 +685,8 @@ impl Renderer {
|
||||||
|
|
||||||
fn draw_fps(&self,
|
fn draw_fps(&self,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
outlines: &Outlines,
|
fps_glyph_store: &GlyphStore,
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
glyph_indices: &[u16],
|
|
||||||
draw_time: f64,
|
draw_time: f64,
|
||||||
accum_time: f64,
|
accum_time: f64,
|
||||||
composite_time: f64,
|
composite_time: f64,
|
||||||
|
@ -766,22 +733,12 @@ impl Renderer {
|
||||||
let mut fps_typesetter = Typesetter::new(f32::INFINITY, &font, font.units_per_em() as f32);
|
let mut fps_typesetter = Typesetter::new(f32::INFINITY, &font, font.units_per_em() as f32);
|
||||||
fps_typesetter.add_text(&font, font.units_per_em() as f32, &fps_text);
|
fps_typesetter.add_text(&font, font.units_per_em() as f32, &fps_text);
|
||||||
|
|
||||||
let mut fps_glyph_indices: Vec<_> =
|
|
||||||
fps_typesetter.glyph_positions.iter().map(|glyph_pos| {
|
|
||||||
match glyph_indices.get(glyph_pos.glyph_id as usize) {
|
|
||||||
Some(&index) => index,
|
|
||||||
None => glyph_indices[0],
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
fps_glyph_indices.sort();
|
|
||||||
fps_glyph_indices.dedup();
|
|
||||||
|
|
||||||
let shelf_height = font.shelf_height(FPS_DISPLAY_POINT_SIZE);
|
let shelf_height = font.shelf_height(FPS_DISPLAY_POINT_SIZE);
|
||||||
let mut fps_atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
|
let mut fps_atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
|
||||||
|
|
||||||
let mut fps_glyphs = vec![];
|
let mut fps_glyphs = vec![];
|
||||||
for &fps_glyph_index in &fps_glyph_indices {
|
for &fps_glyph_index in &fps_glyph_store.all_glyph_indices {
|
||||||
let origin = fps_atlas_builder.pack_glyph(&outlines,
|
let origin = fps_atlas_builder.pack_glyph(&fps_glyph_store.outlines,
|
||||||
fps_glyph_index,
|
fps_glyph_index,
|
||||||
FPS_DISPLAY_POINT_SIZE,
|
FPS_DISPLAY_POINT_SIZE,
|
||||||
0.0).unwrap();
|
0.0).unwrap();
|
||||||
|
@ -798,7 +755,7 @@ impl Renderer {
|
||||||
self.rasterizer.draw_atlas(&self.fps_compute_image,
|
self.rasterizer.draw_atlas(&self.fps_compute_image,
|
||||||
&rect,
|
&rect,
|
||||||
&fps_atlas,
|
&fps_atlas,
|
||||||
outlines,
|
&fps_glyph_store.outlines,
|
||||||
&self.fps_coverage_buffer).unwrap();
|
&self.fps_coverage_buffer).unwrap();
|
||||||
self.rasterizer.queue().flush().unwrap();
|
self.rasterizer.queue().flush().unwrap();
|
||||||
|
|
||||||
|
@ -810,9 +767,8 @@ impl Renderer {
|
||||||
device_pixel_size.height as i32 - FPS_PADDING - fps_line_spacing);
|
device_pixel_size.height as i32 - FPS_PADDING - fps_line_spacing);
|
||||||
|
|
||||||
self.draw_glyphs(font,
|
self.draw_glyphs(font,
|
||||||
outlines,
|
&fps_glyph_store,
|
||||||
&self.fps_composite_vertex_array,
|
&self.fps_composite_vertex_array,
|
||||||
glyph_indices,
|
|
||||||
&fps_typesetter.glyph_positions,
|
&fps_typesetter.glyph_positions,
|
||||||
&fps_glyphs,
|
&fps_glyphs,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
|
|
|
@ -49,6 +49,14 @@ impl CodepointRange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodepointRanges {
|
impl CodepointRanges {
|
||||||
|
/// Creates an empty set of codepoint ranges.
|
||||||
|
#[inline]
|
||||||
|
pub fn empty() -> CodepointRanges {
|
||||||
|
CodepointRanges {
|
||||||
|
ranges: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates codepoint ranges from a sorted array of characters, collapsing duplicates.
|
/// 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
|
/// This is useful when creating an atlas from a string. The array can be readily produced with
|
||||||
|
@ -141,7 +149,7 @@ impl GlyphMapping {
|
||||||
glyph_index: 0,
|
glyph_index: 0,
|
||||||
},
|
},
|
||||||
end: GlyphRangesIndex {
|
end: GlyphRangesIndex {
|
||||||
range_index: 0,
|
range_index: -1,
|
||||||
glyph_index: 0,
|
glyph_index: 0,
|
||||||
},
|
},
|
||||||
codepoint: 0,
|
codepoint: 0,
|
||||||
|
@ -152,11 +160,11 @@ impl GlyphMapping {
|
||||||
GlyphMappingIter {
|
GlyphMappingIter {
|
||||||
start: GlyphRangesIndex {
|
start: GlyphRangesIndex {
|
||||||
range_index: 0,
|
range_index: 0,
|
||||||
glyph_index: self.ranges[0].glyphs.start,
|
glyph_index: self.ranges[0].glyphs.start as i32,
|
||||||
},
|
},
|
||||||
end: GlyphRangesIndex {
|
end: GlyphRangesIndex {
|
||||||
range_index: (self.ranges.len() - 1) as u16,
|
range_index: self.ranges.len() as i32 - 1,
|
||||||
glyph_index: self.ranges.last().unwrap().glyphs.end,
|
glyph_index: self.ranges.last().unwrap().glyphs.end as i32,
|
||||||
},
|
},
|
||||||
codepoint: self.ranges[0].codepoint_start,
|
codepoint: self.ranges[0].codepoint_start,
|
||||||
ranges: &self.ranges,
|
ranges: &self.ranges,
|
||||||
|
@ -221,18 +229,20 @@ impl<'a> Iterator for GlyphMappingIter<'a> {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = (self.codepoint, self.start.glyph_index);
|
let item = (self.codepoint, self.start.glyph_index as u16);
|
||||||
|
|
||||||
self.codepoint += 1;
|
self.codepoint += 1;
|
||||||
self.start.glyph_index += 1;
|
self.start.glyph_index += 1;
|
||||||
|
|
||||||
while self.start.glyph_index > self.ranges[self.start.range_index as usize].glyphs.end {
|
while self.start.glyph_index > self.ranges[self.start.range_index as usize].glyphs.end as
|
||||||
|
i32 {
|
||||||
self.start.range_index += 1;
|
self.start.range_index += 1;
|
||||||
if self.start.range_index > self.end.range_index {
|
if self.start.range_index > self.end.range_index {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
self.start.glyph_index = self.ranges[self.start.range_index as usize].glyphs.start;
|
self.start.glyph_index = self.ranges[self.start.range_index as usize].glyphs.start as
|
||||||
|
i32;
|
||||||
self.codepoint = self.ranges[self.start.range_index as usize].codepoint_start;
|
self.codepoint = self.ranges[self.start.range_index as usize].codepoint_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,8 +252,8 @@ impl<'a> Iterator for GlyphMappingIter<'a> {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct GlyphRangesIndex {
|
struct GlyphRangesIndex {
|
||||||
range_index: u16,
|
range_index: i32,
|
||||||
glyph_index: u16,
|
glyph_index: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MappedGlyphRange {
|
impl MappedGlyphRange {
|
||||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
use compute_shader;
|
use compute_shader;
|
||||||
use gl::types::GLenum;
|
use gl::types::GLenum;
|
||||||
|
use otf;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
/// An OpenGL error with the given code.
|
/// An OpenGL error with the given code.
|
||||||
|
@ -63,3 +64,12 @@ pub enum RasterError {
|
||||||
UnsupportedImageFormat,
|
UnsupportedImageFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error in glyph store creation. See `Typesetter::create_glyph_store()`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GlyphStoreCreationError {
|
||||||
|
/// An error occurred when looking up a glyph ID for a character in the font.
|
||||||
|
OtfError(otf::Error),
|
||||||
|
/// An error occurred when uploading the outlines to the GPU.
|
||||||
|
GlError(GlError),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,12 @@
|
||||||
//! control over line spacing. Use Cocoa's `NSLayoutManager`, Pango, etc. for real use.
|
//! control over line spacing. Use Cocoa's `NSLayoutManager`, Pango, etc. for real use.
|
||||||
|
|
||||||
use charmap::CodepointRanges;
|
use charmap::CodepointRanges;
|
||||||
|
use error::GlyphStoreCreationError;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use otf::Font;
|
use otf::Font;
|
||||||
|
use outline::{OutlineBuilder, Outlines};
|
||||||
use shaper;
|
use shaper;
|
||||||
|
use std::u16;
|
||||||
|
|
||||||
pub struct Typesetter {
|
pub struct Typesetter {
|
||||||
pub glyph_positions: Vec<GlyphPosition>,
|
pub glyph_positions: Vec<GlyphPosition>,
|
||||||
|
@ -79,6 +82,14 @@ impl Typesetter {
|
||||||
pub fn glyph_positions(&self) -> &[GlyphPosition] {
|
pub fn glyph_positions(&self) -> &[GlyphPosition] {
|
||||||
&self.glyph_positions
|
&self.glyph_positions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_glyph_store(&self, font: &Font) -> Result<GlyphStore, GlyphStoreCreationError> {
|
||||||
|
let glyph_ids = self.glyph_positions
|
||||||
|
.iter()
|
||||||
|
.map(|glyph_position| glyph_position.glyph_id)
|
||||||
|
.collect();
|
||||||
|
GlyphStore::from_glyph_ids(glyph_ids, font)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -89,3 +100,60 @@ pub struct GlyphPosition {
|
||||||
pub glyph_id: u16,
|
pub glyph_id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GlyphStore {
|
||||||
|
pub outlines: Outlines,
|
||||||
|
pub glyph_id_to_glyph_index: Vec<u16>,
|
||||||
|
pub all_glyph_indices: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlyphStore {
|
||||||
|
fn from_glyph_ids(mut glyph_ids: Vec<u16>, font: &Font)
|
||||||
|
-> Result<GlyphStore, GlyphStoreCreationError> {
|
||||||
|
glyph_ids.sort();
|
||||||
|
glyph_ids.dedup();
|
||||||
|
|
||||||
|
let last_glyph_id = match glyph_ids.last() {
|
||||||
|
Some(&id) => id + 1,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut outline_builder = OutlineBuilder::new();
|
||||||
|
let mut glyph_id_to_glyph_index = vec![u16::MAX; last_glyph_id as usize];
|
||||||
|
let mut all_glyph_indices = vec![];
|
||||||
|
for glyph_id in glyph_ids {
|
||||||
|
let glyph_index = try!(outline_builder.add_glyph(font, glyph_id)
|
||||||
|
.map_err(GlyphStoreCreationError::OtfError));
|
||||||
|
glyph_id_to_glyph_index[glyph_id as usize] = glyph_index;
|
||||||
|
all_glyph_indices.push(glyph_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
let outlines = try!(outline_builder.create_buffers()
|
||||||
|
.map_err(GlyphStoreCreationError::GlError));
|
||||||
|
|
||||||
|
all_glyph_indices.sort();
|
||||||
|
all_glyph_indices.dedup();
|
||||||
|
|
||||||
|
Ok(GlyphStore {
|
||||||
|
outlines: outlines,
|
||||||
|
glyph_id_to_glyph_index: glyph_id_to_glyph_index,
|
||||||
|
all_glyph_indices: all_glyph_indices,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_codepoints(codepoints: &CodepointRanges, font: &Font)
|
||||||
|
-> Result<GlyphStore, GlyphStoreCreationError> {
|
||||||
|
let mapping = try!(font.glyph_mapping_for_codepoint_ranges(&codepoints.ranges)
|
||||||
|
.map_err(GlyphStoreCreationError::OtfError));
|
||||||
|
let glyph_ids = mapping.iter().map(|(_, glyph_id)| glyph_id).collect();
|
||||||
|
GlyphStore::from_glyph_ids(glyph_ids, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn glyph_index(&self, glyph_id: u16) -> Option<u16> {
|
||||||
|
match self.glyph_id_to_glyph_index.get(glyph_id as usize) {
|
||||||
|
None | Some(&u16::MAX) => None,
|
||||||
|
Some(&index) => Some(index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue