Add support for composite glyphs
This commit is contained in:
parent
5453afa7a1
commit
23f7f88f43
|
@ -20,17 +20,21 @@ 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::AtlasBuilder;
|
||||
use pathfinder::charmap::CodepointRange;
|
||||
use pathfinder::charmap::CodepointRanges;
|
||||
use pathfinder::coverage::CoverageBuffer;
|
||||
use pathfinder::glyph_range::GlyphRanges;
|
||||
use pathfinder::otf::Font;
|
||||
use pathfinder::outline::{OutlineBuffers, OutlineBuilder};
|
||||
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
||||
use pathfinder::shaper;
|
||||
use std::char;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
const ATLAS_SIZE: u32 = 2048;
|
||||
const WIDTH: u32 = 640;
|
||||
|
@ -70,16 +74,32 @@ fn main() {
|
|||
let (width, height) = window.get_framebuffer_size();
|
||||
let mut device_pixel_size = Size2D::new(width as u32, height as u32);
|
||||
|
||||
let mut chars: Vec<char> = TEXT.chars().collect();
|
||||
chars.sort();
|
||||
let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)];
|
||||
let mut args = env::args();
|
||||
args.next();
|
||||
let font_path = args.next().unwrap_or_else(|| usage());
|
||||
|
||||
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
|
||||
let mut text = "".to_string();
|
||||
match args.next() {
|
||||
Some(path) => drop(File::open(path).unwrap().read_to_string(&mut text).unwrap()),
|
||||
None => text.push_str(TEXT),
|
||||
}
|
||||
text = text.replace(&['\n', '\r', '\t'][..], " ");
|
||||
|
||||
// Make sure the characters include `[A-Za-z0-9 ./,]`, for the FPS display.
|
||||
let mut chars: Vec<char> = text.chars().collect();
|
||||
chars.extend(" ./,:()".chars());
|
||||
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));
|
||||
chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32));
|
||||
chars.sort();
|
||||
let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars);
|
||||
|
||||
let file = Mmap::open_path(font_path, Protection::Read).unwrap();
|
||||
let (font, shaped_glyph_positions, glyph_ranges);
|
||||
unsafe {
|
||||
font = Font::new(file.as_slice()).unwrap();
|
||||
glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
||||
shaped_glyph_positions = shaper::shape_text(&font, &glyph_ranges, TEXT)
|
||||
glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges.ranges).unwrap();
|
||||
shaped_glyph_positions = shaper::shape_text(&font, &glyph_ranges, &text)
|
||||
}
|
||||
|
||||
let paragraph_width = (device_pixel_size.width as f32 * UNITS_PER_EM as f32 /
|
||||
|
@ -724,6 +744,11 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Image, GL
|
|||
(compute_image, gl_texture)
|
||||
}
|
||||
|
||||
fn usage() -> ! {
|
||||
println!("usage: lorem-ipsum /path/to/font.ttf [/path/to/text.txt]");
|
||||
process::exit(0)
|
||||
}
|
||||
|
||||
static COMPOSITE_VERTEX_SHADER: &'static str = "\
|
||||
#version 330
|
||||
|
||||
|
|
138
src/otf/glyf.rs
138
src/otf/glyf.rs
|
@ -15,10 +15,14 @@ use otf::loca::LocaTable;
|
|||
use otf::{Error, FontTable};
|
||||
use outline::GlyphBounds;
|
||||
use std::mem;
|
||||
use std::ops::Mul;
|
||||
use util::Jump;
|
||||
|
||||
const F2DOT14_ZERO: F2Dot14 = F2Dot14(0);
|
||||
const F2DOT14_ONE: F2Dot14 = F2Dot14(0b0100_0000_0000_0000);
|
||||
|
||||
bitflags! {
|
||||
flags Flags: u8 {
|
||||
flags SimpleFlags: u8 {
|
||||
const ON_CURVE = 1 << 0,
|
||||
const X_SHORT_VECTOR = 1 << 1,
|
||||
const Y_SHORT_VECTOR = 1 << 2,
|
||||
|
@ -28,6 +32,18 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
flags CompositeFlags: u16 {
|
||||
const ARG_1_AND_2_ARE_WORDS = 1 << 0,
|
||||
const ARGS_ARE_XY_VALUES = 1 << 1,
|
||||
const ROUND_XY_TO_GRID = 1 << 2,
|
||||
const WE_HAVE_A_SCALE = 1 << 3,
|
||||
const MORE_COMPONENTS = 1 << 5,
|
||||
const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6,
|
||||
const WE_HAVE_A_TWO_BY_TWO = 1 << 7,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct Point {
|
||||
pub position: Point2D<i16>,
|
||||
|
@ -65,11 +81,18 @@ impl<'a> GlyfTable<'a> {
|
|||
Some(offset) => try!(reader.jump(offset as usize).map_err(Error::eof)),
|
||||
}
|
||||
|
||||
let glyph_start = reader;
|
||||
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
if number_of_contours < 0 {
|
||||
// TODO(pcwalton): Composite glyphs.
|
||||
return Err(Error::CompositeGlyph)
|
||||
if number_of_contours >= 0 {
|
||||
self.for_each_point_in_simple_glyph(glyph_start, callback)
|
||||
} else {
|
||||
self.for_each_point_in_composite_glyph(glyph_start, head_table, loca_table, callback)
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_point_in_simple_glyph<F>(&self, mut reader: &[u8], mut callback: F)
|
||||
-> Result<(), Error> where F: FnMut(&Point) {
|
||||
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
try!(reader.jump(mem::size_of::<i16>() * 4).map_err(Error::eof));
|
||||
|
||||
// Find out how many points we have.
|
||||
|
@ -105,7 +128,7 @@ impl<'a> GlyfTable<'a> {
|
|||
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);
|
||||
let flags = SimpleFlags::from_bits_truncate(*flag_parser.current);
|
||||
try!(flag_parser.next());
|
||||
|
||||
let mut delta = Point2D::new(0, 0);
|
||||
|
@ -189,6 +212,66 @@ impl<'a> GlyfTable<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Consider rasterizing pieces of composite glyphs independently and
|
||||
// compositing them together.
|
||||
fn for_each_point_in_composite_glyph<F>(&self,
|
||||
mut reader: &[u8],
|
||||
head_table: &HeadTable,
|
||||
loca_table: &LocaTable,
|
||||
mut callback: F)
|
||||
-> Result<(), Error> where F: FnMut(&Point) {
|
||||
try!(reader.jump(mem::size_of::<i16>() * 5).map_err(Error::eof));
|
||||
|
||||
loop {
|
||||
let flags = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
let flags = CompositeFlags::from_bits_truncate(flags);
|
||||
let glyph_index = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
let (arg0, arg1);
|
||||
if flags.contains(ARG_1_AND_2_ARE_WORDS) {
|
||||
arg0 = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
arg1 = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
} else {
|
||||
arg0 = try!(reader.read_i8().map_err(Error::eof)) as i16;
|
||||
arg1 = try!(reader.read_i8().map_err(Error::eof)) as i16;
|
||||
}
|
||||
|
||||
let mut transform = Mat3x2::identity();
|
||||
if flags.contains(ARGS_ARE_XY_VALUES) {
|
||||
transform.m02 = arg0;
|
||||
transform.m12 = arg1;
|
||||
}
|
||||
|
||||
if flags.contains(WE_HAVE_A_SCALE) {
|
||||
let scale = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
transform.m00 = scale;
|
||||
transform.m11 = scale;
|
||||
} else if flags.contains(WE_HAVE_AN_X_AND_Y_SCALE) {
|
||||
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
} else if flags.contains(WE_HAVE_A_TWO_BY_TWO) {
|
||||
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
transform.m01 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
transform.m10 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
||||
}
|
||||
|
||||
if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) {
|
||||
let mut reader = self.table.bytes;
|
||||
try!(reader.jump(offset as usize).map_err(Error::eof));
|
||||
self.for_each_point_in_simple_glyph(reader, |point| {
|
||||
callback(&transform.transform(&point))
|
||||
});
|
||||
}
|
||||
|
||||
if !flags.contains(MORE_COMPONENTS) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn glyph_bounds(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16)
|
||||
-> Result<GlyphBounds, Error> {
|
||||
let mut reader = self.table.bytes;
|
||||
|
@ -229,7 +312,7 @@ fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_p
|
|||
-> Result<u16, Error> {
|
||||
let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
|
||||
while points_left > 0 {
|
||||
let flags = Flags::from_bits_truncate(try!(reader.read_u8().map_err(Error::eof)));
|
||||
let flags = SimpleFlags::from_bits_truncate(try!(reader.read_u8().map_err(Error::eof)));
|
||||
let repeat_count = if !flags.contains(REPEAT) {
|
||||
1
|
||||
} else {
|
||||
|
@ -278,7 +361,7 @@ impl<'a> FlagParser<'a> {
|
|||
None => return Err(Error::UnexpectedEof),
|
||||
};
|
||||
|
||||
let flags = Flags::from_bits_truncate(*self.current);
|
||||
let flags = SimpleFlags::from_bits_truncate(*self.current);
|
||||
self.next = &self.next[1..];
|
||||
|
||||
if flags.contains(REPEAT) {
|
||||
|
@ -296,3 +379,44 @@ impl<'a> FlagParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Mat3x2 {
|
||||
m00: F2Dot14,
|
||||
m01: F2Dot14,
|
||||
m02: i16,
|
||||
m10: F2Dot14,
|
||||
m11: F2Dot14,
|
||||
m12: i16,
|
||||
}
|
||||
|
||||
impl Mat3x2 {
|
||||
fn identity() -> Mat3x2 {
|
||||
Mat3x2 {
|
||||
m00: F2DOT14_ONE, m01: F2DOT14_ZERO, m02: 0,
|
||||
m10: F2DOT14_ZERO, m11: F2DOT14_ONE, m12: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(pcwalton): SIMD/FMA.
|
||||
fn transform(&self, point: &Point) -> Point {
|
||||
let p = point.position;
|
||||
Point {
|
||||
position: Point2D::new(self.m00 * p.x + self.m01 * p.y + self.m02,
|
||||
self.m10 * p.x + self.m11 * p.y + self.m12),
|
||||
..*point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct F2Dot14(i16);
|
||||
|
||||
impl Mul<i16> for F2Dot14 {
|
||||
type Output = i16;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: i16) -> i16 {
|
||||
((self.0 as i32 * other as i32) >> 14) as i16
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue