Add a new example and a toy shaper
This commit is contained in:
parent
1deaf9136e
commit
89a2bf54b0
|
@ -16,7 +16,7 @@ use euclid::{Point2D, Rect, Size2D};
|
|||
use gl::types::{GLint, GLuint};
|
||||
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
||||
use memmap::{Mmap, Protection};
|
||||
use pathfinder::batch::{BatchBuilder, GlyphRange};
|
||||
use pathfinder::batch::BatchBuilder;
|
||||
use pathfinder::charmap::CodepointRange;
|
||||
use pathfinder::coverage::CoverageBuffer;
|
||||
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
||||
|
@ -58,7 +58,7 @@ fn main() {
|
|||
let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)];
|
||||
|
||||
let glyph_ranges = font.cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
||||
for (glyph_index, glyph_id) in glyph_ranges.iter().flat_map(GlyphRange::iter).enumerate() {
|
||||
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
||||
glyph_buffer_builder.add_glyph(&font, glyph_id as u32).unwrap();
|
||||
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, POINT_SIZE).unwrap()
|
||||
}
|
||||
|
@ -93,9 +93,7 @@ fn main() {
|
|||
gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl::Viewport(0, 0, device_pixel_width, device_pixel_height);
|
||||
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
|
||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
extern crate compute_shader;
|
||||
extern crate euclid;
|
||||
extern crate gl;
|
||||
extern crate glfw;
|
||||
extern crate memmap;
|
||||
extern crate pathfinder;
|
||||
|
||||
use compute_shader::buffer;
|
||||
use compute_shader::instance::Instance;
|
||||
use compute_shader::texture::{ExternalTexture, Format, Texture};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
||||
use memmap::{Mmap, Protection};
|
||||
use pathfinder::batch::BatchBuilder;
|
||||
use pathfinder::charmap::CodepointRanges;
|
||||
use pathfinder::coverage::CoverageBuffer;
|
||||
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
||||
use pathfinder::glyph_range::GlyphRanges;
|
||||
use pathfinder::otf::Font;
|
||||
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
||||
use pathfinder::shaper::{self, GlyphPos};
|
||||
use std::env;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
const ATLAS_SIZE: u32 = 1024;
|
||||
const WIDTH: u32 = 512;
|
||||
const HEIGHT: u32 = 384;
|
||||
const UNITS_PER_EM: u32 = 2048;
|
||||
|
||||
const INITIAL_POINT_SIZE: f32 = 24.0;
|
||||
const MIN_POINT_SIZE: f32 = 6.0;
|
||||
const MAX_POINT_SIZE: f32 = 400.0;
|
||||
|
||||
static TEXT: &'static str = "Loremipsumdolorsitamet";
|
||||
|
||||
fn main() {
|
||||
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
|
||||
glfw.window_hint(WindowHint::ContextVersion(3, 3));
|
||||
glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
|
||||
glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
|
||||
let context = glfw.create_window(WIDTH, HEIGHT, "lorem-ipsum", WindowMode::Windowed);
|
||||
|
||||
let (mut window, events) = context.expect("Couldn't create a window!");
|
||||
window.make_current();
|
||||
window.set_scroll_polling(true);
|
||||
window.set_size_polling(true);
|
||||
window.set_framebuffer_size_polling(true);
|
||||
|
||||
gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void);
|
||||
|
||||
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 = CodepointRanges::from_sorted_chars(&chars);
|
||||
|
||||
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
|
||||
let (font, glyph_positions, glyph_ranges);
|
||||
unsafe {
|
||||
font = Font::new(file.as_slice()).unwrap();
|
||||
glyph_ranges = font.cmap
|
||||
.glyph_ranges_for_codepoint_ranges(&codepoint_ranges.ranges)
|
||||
.unwrap();
|
||||
glyph_positions = shaper::shape_text(&font, &glyph_ranges, TEXT)
|
||||
}
|
||||
|
||||
let renderer = Renderer::new();
|
||||
let mut point_size = INITIAL_POINT_SIZE;
|
||||
let mut dirty = true;
|
||||
|
||||
while !window.should_close() {
|
||||
if dirty {
|
||||
renderer.redraw(&font,
|
||||
point_size,
|
||||
&glyph_ranges,
|
||||
&glyph_positions,
|
||||
&device_pixel_size);
|
||||
window.swap_buffers();
|
||||
dirty = false
|
||||
}
|
||||
|
||||
glfw.wait_events();
|
||||
for (_, event) in glfw::flush_messages(&events) {
|
||||
match event {
|
||||
WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
|
||||
window.set_should_close(true)
|
||||
}
|
||||
WindowEvent::Scroll(_, y) => {
|
||||
point_size += y as f32;
|
||||
|
||||
if point_size < MIN_POINT_SIZE {
|
||||
point_size = MIN_POINT_SIZE
|
||||
} else if point_size > MAX_POINT_SIZE {
|
||||
point_size = MAX_POINT_SIZE
|
||||
}
|
||||
|
||||
dirty = true
|
||||
}
|
||||
WindowEvent::Size(_, _) | WindowEvent::FramebufferSize(_, _) => {
|
||||
let (width, height) = window.get_framebuffer_size();
|
||||
device_pixel_size = Size2D::new(width as u32, height as u32);
|
||||
dirty = true
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Renderer {
|
||||
rasterizer: Rasterizer,
|
||||
|
||||
program: GLuint,
|
||||
atlas_uniform: GLint,
|
||||
transform_uniform: GLint,
|
||||
translation_uniform: GLint,
|
||||
|
||||
vertex_array: GLuint,
|
||||
vertex_buffer: GLuint,
|
||||
index_buffer: GLuint,
|
||||
|
||||
atlas_size: Size2D<u32>,
|
||||
|
||||
coverage_buffer: CoverageBuffer,
|
||||
compute_texture: Texture,
|
||||
gl_texture: GLuint,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
fn new() -> Renderer {
|
||||
let instance = Instance::new().unwrap();
|
||||
let device = instance.create_device().unwrap();
|
||||
let queue = device.create_queue().unwrap();
|
||||
|
||||
let rasterizer_options = RasterizerOptions::from_env().unwrap();
|
||||
let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap();
|
||||
|
||||
let (program, position_attribute, tex_coord_attribute, atlas_uniform);
|
||||
let (transform_uniform, translation_uniform);
|
||||
let (mut vertex_array, mut vertex_buffer, mut index_buffer) = (0, 0, 0);
|
||||
unsafe {
|
||||
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
|
||||
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
|
||||
gl::ShaderSource(vertex_shader,
|
||||
1,
|
||||
&(VERTEX_SHADER.as_ptr() as *const u8 as *const GLchar),
|
||||
&(VERTEX_SHADER.len() as GLint));
|
||||
gl::ShaderSource(fragment_shader,
|
||||
1,
|
||||
&(FRAGMENT_SHADER.as_ptr() as *const u8 as *const GLchar),
|
||||
&(FRAGMENT_SHADER.len() as GLint));
|
||||
gl::CompileShader(vertex_shader);
|
||||
gl::CompileShader(fragment_shader);
|
||||
|
||||
program = gl::CreateProgram();
|
||||
gl::AttachShader(program, vertex_shader);
|
||||
gl::AttachShader(program, fragment_shader);
|
||||
gl::LinkProgram(program);
|
||||
gl::UseProgram(program);
|
||||
|
||||
position_attribute = gl::GetAttribLocation(program,
|
||||
"aPosition\0".as_ptr() as *const GLchar);
|
||||
tex_coord_attribute = gl::GetAttribLocation(program,
|
||||
"aTexCoord\0".as_ptr() as *const GLchar);
|
||||
atlas_uniform = gl::GetUniformLocation(program, "uAtlas\0".as_ptr() as *const GLchar);
|
||||
transform_uniform = gl::GetUniformLocation(program,
|
||||
"uTransform\0".as_ptr() as *const GLchar);
|
||||
translation_uniform =
|
||||
gl::GetUniformLocation(program, "uTranslation\0".as_ptr() as *const GLchar);
|
||||
|
||||
gl::GenVertexArrays(1, &mut vertex_array);
|
||||
gl::BindVertexArray(vertex_array);
|
||||
|
||||
gl::GenBuffers(1, &mut vertex_buffer);
|
||||
gl::GenBuffers(1, &mut index_buffer);
|
||||
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
|
||||
|
||||
gl::VertexAttribPointer(position_attribute as GLuint,
|
||||
2,
|
||||
gl::UNSIGNED_INT,
|
||||
gl::FALSE,
|
||||
mem::size_of::<Vertex>() as GLsizei,
|
||||
0 as *const GLvoid);
|
||||
gl::VertexAttribPointer(tex_coord_attribute as GLuint,
|
||||
2,
|
||||
gl::UNSIGNED_INT,
|
||||
gl::FALSE,
|
||||
mem::size_of::<Vertex>() as GLsizei,
|
||||
(mem::size_of::<f32>() * 2) as *const GLvoid);
|
||||
gl::EnableVertexAttribArray(position_attribute as GLuint);
|
||||
gl::EnableVertexAttribArray(tex_coord_attribute as GLuint);
|
||||
}
|
||||
|
||||
// FIXME(pcwalton)
|
||||
let atlas_size = Size2D::new(ATLAS_SIZE, ATLAS_SIZE);
|
||||
|
||||
let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap();
|
||||
|
||||
let compute_texture = rasterizer.device.create_texture(Format::R8,
|
||||
buffer::Protection::WriteOnly,
|
||||
&atlas_size).unwrap();
|
||||
|
||||
let mut gl_texture = 0;
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut gl_texture);
|
||||
compute_texture.bind_to(&ExternalTexture::Gl(gl_texture)).unwrap();
|
||||
|
||||
gl::BindTexture(gl::TEXTURE_RECTANGLE, gl_texture);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE,
|
||||
gl::TEXTURE_WRAP_S,
|
||||
gl::CLAMP_TO_EDGE as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_RECTANGLE,
|
||||
gl::TEXTURE_WRAP_T,
|
||||
gl::CLAMP_TO_EDGE as GLint);
|
||||
}
|
||||
|
||||
Renderer {
|
||||
rasterizer: rasterizer,
|
||||
|
||||
program: program,
|
||||
atlas_uniform: atlas_uniform,
|
||||
transform_uniform: transform_uniform,
|
||||
translation_uniform: translation_uniform,
|
||||
|
||||
vertex_array: vertex_array,
|
||||
vertex_buffer: vertex_buffer,
|
||||
index_buffer: index_buffer,
|
||||
|
||||
atlas_size: atlas_size,
|
||||
|
||||
coverage_buffer: coverage_buffer,
|
||||
compute_texture: compute_texture,
|
||||
gl_texture: gl_texture,
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(&self,
|
||||
font: &Font,
|
||||
point_size: f32,
|
||||
glyph_ranges: &GlyphRanges,
|
||||
glyph_positions: &[GlyphPos],
|
||||
device_pixel_size: &Size2D<u32>) {
|
||||
// FIXME(pcwalton)
|
||||
let shelf_height = (point_size * 2.0).ceil() as u32;
|
||||
|
||||
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
|
||||
let mut batch_builder = BatchBuilder::new(device_pixel_size.width, shelf_height);
|
||||
|
||||
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
||||
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
||||
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, point_size).unwrap()
|
||||
}
|
||||
|
||||
let glyph_buffer = glyph_buffer_builder.finish().unwrap();
|
||||
let batch = batch_builder.finish(&glyph_buffer_builder).unwrap();
|
||||
|
||||
self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
|
||||
shelf_height,
|
||||
&glyph_buffer,
|
||||
&batch,
|
||||
&self.coverage_buffer,
|
||||
&self.compute_texture).unwrap();
|
||||
|
||||
self.rasterizer.queue.flush().unwrap();
|
||||
|
||||
unsafe {
|
||||
gl::UseProgram(self.program);
|
||||
gl::BindVertexArray(self.vertex_array);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.index_buffer);
|
||||
|
||||
let (mut vertices, mut indices) = (vec![], vec![]);
|
||||
let mut left_pos = 0;
|
||||
for position in glyph_positions {
|
||||
let glyph_index = batch_builder.glyph_index_for(position.glyph_id).unwrap();
|
||||
let uv_rect = batch_builder.atlas_rect(glyph_index);
|
||||
let (uv_bl, uv_tr) = (uv_rect.origin, uv_rect.bottom_right());
|
||||
let right_pos = left_pos + uv_rect.size.width;
|
||||
let bottom_pos = uv_rect.size.height;
|
||||
|
||||
let first_index = vertices.len() as u16;
|
||||
|
||||
vertices.push(Vertex::new(left_pos, 0, uv_bl.x, uv_tr.y));
|
||||
vertices.push(Vertex::new(right_pos, 0, uv_tr.x, uv_tr.y));
|
||||
vertices.push(Vertex::new(right_pos, bottom_pos, uv_tr.x, uv_bl.y));
|
||||
vertices.push(Vertex::new(left_pos, bottom_pos, uv_bl.x, uv_bl.y));
|
||||
|
||||
indices.extend([0, 1, 3, 1, 2, 3].iter().map(|index| first_index + index));
|
||||
|
||||
left_pos += ((position.advance as f32 * point_size) /
|
||||
(UNITS_PER_EM as f32)).ceil() as u32
|
||||
}
|
||||
|
||||
gl::BufferData(gl::ARRAY_BUFFER,
|
||||
(vertices.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
|
||||
vertices.as_ptr() as *const GLvoid,
|
||||
gl::STATIC_DRAW);
|
||||
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
|
||||
(indices.len() * mem::size_of::<u16>()) as GLsizeiptr,
|
||||
indices.as_ptr() as *const GLvoid,
|
||||
gl::STATIC_DRAW);
|
||||
|
||||
gl::ActiveTexture(gl::TEXTURE0);
|
||||
gl::BindTexture(gl::TEXTURE_RECTANGLE, self.gl_texture);
|
||||
gl::Uniform1i(self.atlas_uniform, 0);
|
||||
|
||||
let matrix = [
|
||||
2.0 / device_pixel_size.width as f32, 0.0,
|
||||
0.0, 2.0 / device_pixel_size.height as f32,
|
||||
];
|
||||
gl::UniformMatrix2fv(self.transform_uniform, 1, gl::FALSE, matrix.as_ptr());
|
||||
|
||||
gl::Uniform2f(self.translation_uniform,
|
||||
-1.0,
|
||||
1.0 - point_size * 2.0 / (device_pixel_size.height as f32));
|
||||
|
||||
gl::Viewport(0,
|
||||
0,
|
||||
device_pixel_size.width as GLint,
|
||||
device_pixel_size.height as GLint);
|
||||
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
|
||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
|
||||
gl::DrawElements(gl::TRIANGLES,
|
||||
indices.len() as GLsizei,
|
||||
gl::UNSIGNED_SHORT,
|
||||
0 as *const GLvoid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct Vertex {
|
||||
x: u32,
|
||||
y: u32,
|
||||
u: u32,
|
||||
v: u32,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
fn new(x: u32, y: u32, u: u32, v: u32) -> Vertex {
|
||||
Vertex {
|
||||
x: x,
|
||||
y: y,
|
||||
u: u,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VERTEX_SHADER: &'static str = "\
|
||||
#version 330
|
||||
|
||||
uniform mat2 uTransform;
|
||||
uniform vec2 uTranslation;
|
||||
|
||||
in vec2 aPosition;
|
||||
in vec2 aTexCoord;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
vTexCoord = aTexCoord;
|
||||
gl_Position = vec4(uTransform * aPosition + uTranslation, 0.0f, 1.0f);
|
||||
}
|
||||
";
|
||||
|
||||
static FRAGMENT_SHADER: &'static str = "\
|
||||
#version 330
|
||||
|
||||
uniform sampler2DRect uAtlas;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
|
||||
out vec4 oFragColor;
|
||||
|
||||
void main() {
|
||||
float value = 1.0f - texture(uAtlas, vTexCoord).r;
|
||||
oFragColor = vec4(value, value, value, 1.0f);
|
||||
}
|
||||
";
|
||||
|
95
src/batch.rs
95
src/batch.rs
|
@ -9,6 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
use atlas::Atlas;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use gl::types::{GLsizei, GLsizeiptr, GLuint};
|
||||
use gl;
|
||||
use glyph_buffer::GlyphBufferBuilder;
|
||||
|
@ -18,8 +19,8 @@ use std::u16;
|
|||
|
||||
pub struct BatchBuilder {
|
||||
pub atlas: Atlas,
|
||||
pub images: Vec<ImageDescriptor>,
|
||||
pub glyph_indices: Vec<u32>,
|
||||
pub image_descriptors: Vec<ImageDescriptor>,
|
||||
pub image_metadata: Vec<ImageMetadata>,
|
||||
}
|
||||
|
||||
impl BatchBuilder {
|
||||
|
@ -28,8 +29,8 @@ impl BatchBuilder {
|
|||
pub fn new(available_width: u32, shelf_height: u32) -> BatchBuilder {
|
||||
BatchBuilder {
|
||||
atlas: Atlas::new(available_width, shelf_height),
|
||||
images: vec![],
|
||||
glyph_indices: vec![],
|
||||
image_descriptors: vec![],
|
||||
image_metadata: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,27 +47,33 @@ impl BatchBuilder {
|
|||
let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap();
|
||||
let atlas_origin = try!(self.atlas.place(&pixel_size));
|
||||
|
||||
while self.images.len() < glyph_index as usize + 1 {
|
||||
self.images.push(ImageDescriptor::default())
|
||||
while self.image_descriptors.len() < glyph_index as usize + 1 {
|
||||
self.image_descriptors.push(ImageDescriptor::default())
|
||||
}
|
||||
|
||||
self.images[glyph_index as usize] = ImageDescriptor {
|
||||
self.image_descriptors[glyph_index as usize] = ImageDescriptor {
|
||||
atlas_x: atlas_origin.x,
|
||||
atlas_y: atlas_origin.y,
|
||||
point_size: (point_size * 65536.0) as u32,
|
||||
glyph_index: glyph_index,
|
||||
};
|
||||
|
||||
self.glyph_indices.push(glyph_index);
|
||||
self.image_metadata.push(ImageMetadata {
|
||||
atlas_size: pixel_size,
|
||||
glyph_index: glyph_index,
|
||||
glyph_id: descriptor.glyph_id,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn finish(&mut self, glyph_buffer_builder: &GlyphBufferBuilder) -> Result<Batch, ()> {
|
||||
self.glyph_indices.sort();
|
||||
self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index));
|
||||
|
||||
let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
|
||||
for &glyph_index in &self.glyph_indices {
|
||||
for image_metadata in &self.image_metadata {
|
||||
let glyph_index = image_metadata.glyph_index;
|
||||
|
||||
let first_index = glyph_buffer_builder.descriptors[glyph_index as usize].start_index as
|
||||
usize;
|
||||
let last_index = match glyph_buffer_builder.descriptors.get(glyph_index as usize + 1) {
|
||||
|
@ -96,11 +103,10 @@ impl BatchBuilder {
|
|||
let mut images = 0;
|
||||
gl::GenBuffers(1, &mut images);
|
||||
|
||||
let length = self.image_descriptors.len() * mem::size_of::<ImageDescriptor>();
|
||||
let ptr = self.image_descriptors.as_ptr() as *const ImageDescriptor as *const c_void;
|
||||
gl::BindBuffer(gl::UNIFORM_BUFFER, images);
|
||||
gl::BufferData(gl::UNIFORM_BUFFER,
|
||||
(self.images.len() * mem::size_of::<ImageDescriptor>()) as GLsizeiptr,
|
||||
self.images.as_ptr() as *const ImageDescriptor as *const c_void,
|
||||
gl::DYNAMIC_DRAW);
|
||||
gl::BufferData(gl::UNIFORM_BUFFER, length as GLsizeiptr, ptr, gl::DYNAMIC_DRAW);
|
||||
|
||||
Ok(Batch {
|
||||
start_indices: start_indices,
|
||||
|
@ -109,6 +115,21 @@ impl BatchBuilder {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn glyph_index_for(&self, glyph_id: u16) -> Option<u32> {
|
||||
match self.image_metadata.binary_search_by(|metadata| metadata.glyph_id.cmp(&glyph_id)) {
|
||||
Ok(glyph_index) => Some(self.image_metadata[glyph_index].glyph_index),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn atlas_rect(&self, glyph_index: u32) -> Rect<u32> {
|
||||
let descriptor = &self.image_descriptors[glyph_index as usize];
|
||||
let metadata = &self.image_metadata[glyph_index as usize];
|
||||
Rect::new(Point2D::new(descriptor.atlas_x, descriptor.atlas_y), metadata.atlas_size)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Batch {
|
||||
|
@ -125,43 +146,7 @@ impl Drop for Batch {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GlyphRange {
|
||||
pub start: u16,
|
||||
pub end: u16,
|
||||
}
|
||||
|
||||
impl GlyphRange {
|
||||
#[inline]
|
||||
pub fn iter(&self) -> GlyphRangeIter {
|
||||
GlyphRangeIter {
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about each image that we send to the GPU.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct ImageDescriptor {
|
||||
|
@ -171,3 +156,11 @@ pub struct ImageDescriptor {
|
|||
glyph_index: u32,
|
||||
}
|
||||
|
||||
/// Information about each image that we keep around ourselves.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ImageMetadata {
|
||||
atlas_size: Size2D<u32>,
|
||||
glyph_index: u32,
|
||||
glyph_id: u16,
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,11 @@ pub struct CodepointRange {
|
|||
pub end: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CodepointRanges {
|
||||
pub ranges: Vec<CodepointRange>,
|
||||
}
|
||||
|
||||
impl CodepointRange {
|
||||
#[inline]
|
||||
pub fn new(start: u32, end: u32) -> CodepointRange {
|
||||
|
@ -33,6 +38,27 @@ impl CodepointRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl CodepointRanges {
|
||||
pub fn from_sorted_chars(chars: &[char]) -> CodepointRanges {
|
||||
let mut ranges: Vec<CodepointRange> = vec![];
|
||||
for &ch in chars {
|
||||
match ranges.last_mut() {
|
||||
Some(ref mut range) if range.end == ch as u32 => continue,
|
||||
Some(ref mut range) if range.end == ch as u32 + 1 => {
|
||||
range.end += 1;
|
||||
continue
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ranges.push(CodepointRange::new(ch as u32, ch as u32))
|
||||
}
|
||||
|
||||
CodepointRanges {
|
||||
ranges: ranges,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CodepointRangeIter {
|
||||
start: u32,
|
||||
end: u32,
|
||||
|
|
|
@ -37,7 +37,7 @@ impl GlyphBufferBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_glyph(&mut self, font: &Font, glyph_id: u32) -> Result<(), ()> {
|
||||
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<(), ()> {
|
||||
let glyph_index = self.descriptors.len() as u16;
|
||||
|
||||
let mut point_index = self.vertices.len() as u32;
|
||||
|
@ -78,7 +78,7 @@ impl GlyphBufferBuilder {
|
|||
units_per_em: font.head.units_per_em as u32,
|
||||
start_point: start_point as u32,
|
||||
start_index: start_index,
|
||||
pad: 0,
|
||||
glyph_id: glyph_id,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
@ -137,7 +137,7 @@ pub struct GlyphDescriptor {
|
|||
pub units_per_em: u32,
|
||||
pub start_point: u32,
|
||||
pub start_index: u32,
|
||||
pub pad: u32,
|
||||
pub glyph_id: u16,
|
||||
}
|
||||
|
||||
impl GlyphDescriptor {
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
// 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
|
||||
}
|
||||
}
|
||||
|
|
@ -29,8 +29,10 @@ pub mod batch;
|
|||
pub mod charmap;
|
||||
pub mod coverage;
|
||||
pub mod glyph_buffer;
|
||||
pub mod glyph_range;
|
||||
pub mod otf;
|
||||
pub mod rasterizer;
|
||||
pub mod shaper;
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use batch::GlyphRange;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use charmap::CodepointRange;
|
||||
use glyph_range::{GlyphRange, GlyphRanges, MappedGlyphRange};
|
||||
use otf::FontTable;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
|
@ -40,7 +40,7 @@ impl<'a> CmapTable<'a> {
|
|||
}
|
||||
|
||||
pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
|
||||
-> Result<Vec<GlyphRange>, ()> {
|
||||
-> Result<GlyphRanges, ()> {
|
||||
let mut cmap_reader = self.table.bytes;
|
||||
|
||||
// Check version.
|
||||
|
@ -96,16 +96,19 @@ impl<'a> CmapTable<'a> {
|
|||
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()));
|
||||
|
||||
// Now perform the lookups.
|
||||
let mut glyph_ranges = vec![];
|
||||
let mut glyph_ranges = GlyphRanges::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 {
|
||||
codepoint_range.start += 1;
|
||||
glyph_ranges.push(GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
glyph_ranges.ranges.push(MappedGlyphRange {
|
||||
codepoint_start: codepoint_range.start,
|
||||
glyphs: GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
},
|
||||
});
|
||||
codepoint_range.start += 1;
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -141,11 +144,14 @@ impl<'a> CmapTable<'a> {
|
|||
let segment_index = match segment_index {
|
||||
Some(segment_index) => segment_index,
|
||||
None => {
|
||||
codepoint_range.start += 1;
|
||||
glyph_ranges.push(GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
glyph_ranges.ranges.push(MappedGlyphRange {
|
||||
codepoint_start: codepoint_range.start,
|
||||
glyphs: GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
},
|
||||
});
|
||||
codepoint_range.start += 1;
|
||||
continue
|
||||
}
|
||||
};
|
||||
|
@ -176,9 +182,12 @@ 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.push(GlyphRange {
|
||||
start: (start_codepoint_range as i16).wrapping_add(id_delta) as u16,
|
||||
end: (end_codepoint_range as i16).wrapping_add(id_delta) as u16,
|
||||
glyph_ranges.ranges.push(MappedGlyphRange {
|
||||
codepoint_start: start_codepoint_range as u32,
|
||||
glyphs: GlyphRange {
|
||||
start: (start_codepoint_range as i16).wrapping_add(id_delta) as u16,
|
||||
end: (end_codepoint_range as i16).wrapping_add(id_delta) as u16,
|
||||
},
|
||||
});
|
||||
continue
|
||||
}
|
||||
|
@ -189,15 +198,21 @@ impl<'a> CmapTable<'a> {
|
|||
try!(glyph_id.jump((id_range_offset as usize + code_offset as usize) * 2));
|
||||
let mut glyph_id = try!(glyph_id.read_u16::<BigEndian>().map_err(drop));
|
||||
if glyph_id == 0 {
|
||||
glyph_ranges.push(GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
glyph_ranges.ranges.push(MappedGlyphRange {
|
||||
codepoint_start: start_code as u32 + code_offset as u32,
|
||||
glyphs: GlyphRange {
|
||||
start: MISSING_GLYPH,
|
||||
end: MISSING_GLYPH,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
glyph_id = (glyph_id as i16).wrapping_add(id_delta) as u16;
|
||||
glyph_ranges.push(GlyphRange {
|
||||
start: glyph_id,
|
||||
end: glyph_id,
|
||||
glyph_ranges.ranges.push(MappedGlyphRange {
|
||||
codepoint_start: start_code as u32 + code_offset as u32,
|
||||
glyphs: GlyphRange {
|
||||
start: glyph_id,
|
||||
end: glyph_id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ impl<'a> GlyfTable<'a> {
|
|||
pub fn for_each_point<F>(&self,
|
||||
head_table: &HeadTable,
|
||||
loca_table: &LocaTable,
|
||||
glyph_id: u32,
|
||||
glyph_id: u16,
|
||||
mut callback: F)
|
||||
-> Result<(), ()> where F: FnMut(&Point) {
|
||||
let mut reader = self.table.bytes;
|
||||
|
@ -149,7 +149,7 @@ impl<'a> GlyfTable<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bounding_rect(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u32)
|
||||
pub fn bounding_rect(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16)
|
||||
-> Result<Rect<i16>, ()> {
|
||||
let mut reader = self.table.bytes;
|
||||
let offset = try!(loca_table.location_of(head_table, glyph_id));
|
||||
|
|
|
@ -15,6 +15,7 @@ use util::Jump;
|
|||
|
||||
const MAGIC_NUMBER: u32 = 0x5f0f3cf5;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HeadTable {
|
||||
pub units_per_em: u16,
|
||||
pub index_to_loc_format: i16,
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// 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.
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use otf::FontTable;
|
||||
use std::mem;
|
||||
use util::Jump;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HheaTable {
|
||||
pub number_of_h_metrics: u16,
|
||||
}
|
||||
|
||||
impl HheaTable {
|
||||
pub fn new(table: FontTable) -> Result<HheaTable, ()> {
|
||||
let mut reader = table.bytes;
|
||||
|
||||
// Check the version.
|
||||
let major_version = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
if (major_version, minor_version) != (1, 0) {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
// Read the number of `hmtx` entries.
|
||||
try!(reader.jump(mem::size_of::<u16>() * 15));
|
||||
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
|
||||
Ok(HheaTable {
|
||||
number_of_h_metrics: number_of_h_metrics,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// 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.
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use otf::FontTable;
|
||||
use otf::hhea::HheaTable;
|
||||
use std::mem;
|
||||
use util::Jump;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HmtxTable<'a> {
|
||||
table: FontTable<'a>,
|
||||
}
|
||||
|
||||
impl<'a> HmtxTable<'a> {
|
||||
pub fn new(table: FontTable) -> HmtxTable {
|
||||
HmtxTable {
|
||||
table: table,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16)
|
||||
-> Result<HorizontalMetrics, ()> {
|
||||
let mut reader = self.table.bytes;
|
||||
|
||||
// Read the advance width.
|
||||
let advance_width;
|
||||
if glyph_id < hhea_table.number_of_h_metrics {
|
||||
try!(reader.jump(mem::size_of::<u16>() * 2 * glyph_id as usize));
|
||||
advance_width = try!(reader.read_u16::<BigEndian>().map_err(drop))
|
||||
} else {
|
||||
try!(reader.jump(mem::size_of::<u16>() * 2 *
|
||||
(hhea_table.number_of_h_metrics - 1) as usize));
|
||||
advance_width = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
try!(reader.jump(mem::size_of::<i16>() * glyph_id as usize));
|
||||
}
|
||||
|
||||
// Read the left-side bearing.
|
||||
let lsb = try!(reader.read_i16::<BigEndian>().map_err(drop));
|
||||
|
||||
Ok(HorizontalMetrics {
|
||||
advance_width: advance_width,
|
||||
lsb: lsb,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct HorizontalMetrics {
|
||||
pub advance_width: u16,
|
||||
pub lsb: i16,
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ impl<'a> LocaTable<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn location_of(&self, head_table: &HeadTable, glyph_id: u32) -> Result<u32, ()> {
|
||||
pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16) -> Result<u32, ()> {
|
||||
let mut reader = self.table.bytes;
|
||||
match head_table.index_to_loc_format {
|
||||
0 => {
|
||||
|
|
|
@ -12,6 +12,8 @@ use byteorder::{BigEndian, ReadBytesExt};
|
|||
use otf::cmap::CmapTable;
|
||||
use otf::glyf::GlyfTable;
|
||||
use otf::head::HeadTable;
|
||||
use otf::hhea::HheaTable;
|
||||
use otf::hmtx::HmtxTable;
|
||||
use otf::loca::LocaTable;
|
||||
use std::mem;
|
||||
use std::u16;
|
||||
|
@ -20,6 +22,8 @@ use util::Jump;
|
|||
pub mod cmap;
|
||||
pub mod glyf;
|
||||
pub mod head;
|
||||
pub mod hhea;
|
||||
pub mod hmtx;
|
||||
pub mod loca;
|
||||
|
||||
const CMAP: u32 = ((b'c' as u32) << 24) |
|
||||
|
@ -34,6 +38,10 @@ const HEAD: u32 = ((b'h' as u32) << 24) |
|
|||
((b'e' as u32) << 16) |
|
||||
((b'a' as u32) << 8) |
|
||||
(b'd' as u32);
|
||||
const HHEA: u32 = ((b'h' as u32) << 24) |
|
||||
((b'h' as u32) << 16) |
|
||||
((b'e' as u32) << 8) |
|
||||
(b'a' as u32);
|
||||
const HMTX: u32 = ((b'h' as u32) << 24) |
|
||||
((b'm' as u32) << 16) |
|
||||
((b't' as u32) << 8) |
|
||||
|
@ -48,7 +56,8 @@ pub struct Font<'a> {
|
|||
|
||||
pub cmap: CmapTable<'a>,
|
||||
pub head: HeadTable,
|
||||
pub hmtx: FontTable<'a>,
|
||||
pub hhea: HheaTable,
|
||||
pub hmtx: HmtxTable<'a>,
|
||||
|
||||
pub glyf: Option<GlyfTable<'a>>,
|
||||
pub loca: Option<LocaTable<'a>>,
|
||||
|
@ -72,7 +81,8 @@ impl<'a> Font<'a> {
|
|||
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
try!(reader.jump(mem::size_of::<u16>() * 3));
|
||||
|
||||
let (mut cmap_table, mut head_table, mut hmtx_table) = (None, None, None);
|
||||
let (mut cmap_table, mut head_table) = (None, None);
|
||||
let (mut hhea_table, mut hmtx_table) = (None, None);
|
||||
let (mut glyf_table, mut loca_table) = (None, None);
|
||||
|
||||
for _ in 0..num_tables {
|
||||
|
@ -87,6 +97,7 @@ impl<'a> Font<'a> {
|
|||
let mut slot = match table_id {
|
||||
CMAP => &mut cmap_table,
|
||||
HEAD => &mut head_table,
|
||||
HHEA => &mut hhea_table,
|
||||
HMTX => &mut hmtx_table,
|
||||
GLYF => &mut glyf_table,
|
||||
LOCA => &mut loca_table,
|
||||
|
@ -113,7 +124,8 @@ impl<'a> Font<'a> {
|
|||
|
||||
cmap: CmapTable::new(try!(cmap_table.ok_or(()))),
|
||||
head: try!(HeadTable::new(try!(head_table.ok_or(())))),
|
||||
hmtx: try!(hmtx_table.ok_or(())),
|
||||
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(())))),
|
||||
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(()))),
|
||||
|
||||
glyf: glyf_table.map(GlyfTable::new),
|
||||
loca: loca_table,
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
//! A very basic text shaper for simple needs.
|
||||
//!
|
||||
//! Do not use this for international or high-quality text. This shaper does not do kerning,
|
||||
//! ligation, or advanced typography features (`GSUB`, `GPOS`, text morphing). Consider HarfBuzz or
|
||||
//! the system shaper instead.
|
||||
|
||||
use glyph_range::GlyphRanges;
|
||||
use otf::Font;
|
||||
|
||||
pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec<GlyphPos> {
|
||||
string.chars().map(|ch| {
|
||||
let glyph_id = glyph_ranges.glyph_for(ch as u32).unwrap_or(0);
|
||||
let advance = match font.hmtx.metrics_for_glyph(&font.hhea, glyph_id) {
|
||||
Ok(metrics) => metrics.advance_width,
|
||||
Err(_) => 0,
|
||||
};
|
||||
GlyphPos {
|
||||
glyph_id: glyph_id,
|
||||
advance: advance,
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GlyphPos {
|
||||
pub glyph_id: u16,
|
||||
pub advance: u16,
|
||||
}
|
||||
|
Loading…
Reference in New Issue