Add support for subpixel offsets.

Closes #11.
This commit is contained in:
Patrick Walton 2017-02-20 16:01:15 -08:00
parent e2014cff13
commit 49408b95cb
15 changed files with 366 additions and 207 deletions

View File

@ -90,7 +90,8 @@ fn main() {
let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint,
shelf_height); shelf_height);
for glyph_index in 0..(glyph_count as u16) { for glyph_index in 0..(glyph_count as u16) {
atlas_builder.pack_glyph(&outlines, glyph_index, point_size as f32).unwrap(); atlas_builder.pack_glyph(&outlines, glyph_index, point_size as f32, 0.0)
.unwrap();
} }
atlas = atlas_builder.create_atlas().unwrap(); atlas = atlas_builder.create_atlas().unwrap();
} }

View File

@ -5,10 +5,9 @@ extern crate euclid;
extern crate memmap; extern crate memmap;
extern crate pathfinder; extern crate pathfinder;
use euclid::Point2D;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use pathfinder::charmap::CodepointRange; use pathfinder::charmap::CodepointRange;
use pathfinder::otf::Font; use pathfinder::otf::{Font, PointKind};
use std::char; use std::char;
use std::env; use std::env;
@ -25,28 +24,26 @@ fn main() {
codepoint, codepoint,
char::from_u32(codepoint).unwrap_or('?')); char::from_u32(codepoint).unwrap_or('?'));
let mut last_point: Option<Point2D<i16>> = None; let mut last_point_was_on_curve = false;
let mut last_point_was_off_curve = false;
font.for_each_point(glyph_id, |point| { font.for_each_point(glyph_id, |point| {
if point.index_in_contour == 0 { let prefix = if point.index_in_contour == 0 {
println!("M {},{}", point.position.x, point.position.y); "M "
} else { } else {
let last = last_point.unwrap(); match point.kind {
if point.on_curve { PointKind::OnCurve if last_point_was_on_curve => "L ",
if last_point_was_off_curve { PointKind::OnCurve => " ",
println!("Q {},{} {},{}", PointKind::QuadControl => "Q ",
last.x, PointKind::FirstCubicControl => "C ",
last.y, PointKind::SecondCubicControl => " ",
point.position.x,
point.position.y);
} else {
println!("L {},{}", point.position.x, point.position.y);
}
} }
} };
last_point_was_off_curve = !point.on_curve; print!("{}{},{}", prefix, point.position.x, point.position.y);
last_point = Some(point.position);
last_point_was_on_curve = point.kind == PointKind::OnCurve;
if last_point_was_on_curve {
println!("")
}
}).unwrap() }).unwrap()
} }
} }

View File

@ -101,7 +101,7 @@ fn main() {
let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height); let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height);
for glyph_index in 0..glyph_count { for glyph_index in 0..glyph_count {
atlas_builder.pack_glyph(&outlines, glyph_index, point_size).unwrap(); atlas_builder.pack_glyph(&outlines, glyph_index, point_size, 0.0).unwrap();
} }
atlas = atlas_builder.create_atlas().unwrap(); atlas = atlas_builder.create_atlas().unwrap();
} }

View File

@ -12,7 +12,7 @@ extern crate pathfinder;
use clap::{App, Arg}; use clap::{App, Arg};
use compute_shader::buffer; use compute_shader::buffer;
use compute_shader::image::{ExternalImage, Format, Image}; use compute_shader::image::{Color, ExternalImage, Format, Image};
use compute_shader::instance::{Instance, ShadingLanguage}; use compute_shader::instance::{Instance, ShadingLanguage};
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
@ -22,8 +22,9 @@ use memmap::{Mmap, Protection};
use pathfinder::atlas::AtlasBuilder; use pathfinder::atlas::AtlasBuilder;
use pathfinder::charmap::{CodepointRanges, GlyphMapping}; use pathfinder::charmap::{CodepointRanges, GlyphMapping};
use pathfinder::coverage::CoverageBuffer; use pathfinder::coverage::CoverageBuffer;
use pathfinder::error::RasterError;
use pathfinder::otf::Font; use pathfinder::otf::Font;
use pathfinder::outline::{OutlineBuilder, Outlines}; use pathfinder::outline::{GlyphSubpixelBounds, OutlineBuilder, Outlines};
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions}; use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
use pathfinder::shaper; use pathfinder::shaper;
use std::char; use std::char;
@ -39,9 +40,11 @@ const WIDTH: u32 = 640;
const HEIGHT: u32 = 480; const HEIGHT: u32 = 480;
const SCROLL_SPEED: f64 = 6.0; const SCROLL_SPEED: f64 = 6.0;
const SUBPIXEL_GRANULARITY: f32 = 0.25;
const INITIAL_POINT_SIZE: f32 = 24.0; const INITIAL_POINT_SIZE: f32 = 24.0;
const MIN_POINT_SIZE: f32 = 6.0; const MIN_POINT_SIZE: f32 = 6.0;
const MAX_POINT_SIZE: f32 = 256.0; const MAX_POINT_SIZE: f32 = 512.0;
const FPS_DISPLAY_POINT_SIZE: f32 = 24.0; const FPS_DISPLAY_POINT_SIZE: f32 = 24.0;
const FPS_PADDING: i32 = 6; const FPS_PADDING: i32 = 6;
@ -151,52 +154,56 @@ fn main() {
let mut outline_builder = OutlineBuilder::new(); let mut outline_builder = OutlineBuilder::new();
let mut glyph_indices = vec![]; let mut glyph_indices = vec![];
let mut glyph_count = 0;
for (_, glyph_id) in glyph_mapping.iter() { for (_, glyph_id) in glyph_mapping.iter() {
let glyph_index = outline_builder.add_glyph(&font, glyph_id).unwrap(); let glyph_index = outline_builder.add_glyph(&font, glyph_id).unwrap();
while glyph_id as usize >= glyph_indices.len() { while glyph_id as usize >= glyph_indices.len() {
glyph_indices.push(0) glyph_indices.push(0)
} }
glyph_indices[glyph_id as usize] = glyph_index; glyph_indices[glyph_id as usize] = glyph_index
glyph_count += 1
} }
let outlines = outline_builder.create_buffers().unwrap(); let outlines = outline_builder.create_buffers().unwrap();
let fps_atlas_origins = renderer.create_fps_atlas(&font, &outlines, glyph_count);
while !window.should_close() { while !window.should_close() {
if dirty { if dirty {
let events = renderer.redraw(point_size, let redraw_result = renderer.redraw(point_size,
&font, &font,
&outlines, &outlines,
&glyph_indices, &glyph_indices,
glyph_count, &glyph_positions,
&glyph_positions, &device_pixel_size,
&device_pixel_size, &translation);
&translation);
let mut draw_time = 0u64; let (draw_time, accum_time);
unsafe { match redraw_result.events {
gl::Flush(); Some(events) => {
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_time); let mut draw_nanos = 0u64;
unsafe {
gl::Flush();
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_nanos);
}
draw_time = draw_nanos as f64;
accum_time = events.accum.time_elapsed().unwrap() as f64;
}
None => {
draw_time = 0.0;
accum_time = 0.0;
}
} }
let draw_time = draw_time as f64;
let accum_time = events.accum.time_elapsed().unwrap() as f64;
let timing = renderer.get_timing_in_ms(); let timing = renderer.get_timing_in_ms();
renderer.draw_fps(&font, renderer.draw_fps(&font,
&outlines, &outlines,
&device_pixel_size, &device_pixel_size,
&fps_atlas_origins,
&glyph_indices, &glyph_indices,
&glyph_mapping, &glyph_mapping,
draw_time, draw_time,
accum_time, accum_time,
timing, timing,
glyph_count); redraw_result.glyphs_drawn);
window.swap_buffers(); window.swap_buffers();
@ -344,7 +351,7 @@ impl Renderer {
gl::VertexAttribPointer(composite_position_attribute as GLuint, gl::VertexAttribPointer(composite_position_attribute as GLuint,
2, 2,
gl::INT, gl::FLOAT,
gl::FALSE, gl::FALSE,
mem::size_of::<Vertex>() as GLsizei, mem::size_of::<Vertex>() as GLsizei,
0 as *const GLvoid); 0 as *const GLvoid);
@ -438,26 +445,35 @@ impl Renderer {
font: &Font, font: &Font,
outlines: &Outlines, outlines: &Outlines,
glyph_indices: &[u16], glyph_indices: &[u16],
glyph_count: usize,
glyph_positions: &[GlyphPos], glyph_positions: &[GlyphPos],
device_pixel_size: &Size2D<u32>, device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>) translation: &Point2D<i32>)
-> DrawAtlasProfilingEvents { -> RedrawResult {
let shelf_height = font.shelf_height(point_size); let shelf_height = font.shelf_height(point_size);
let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height); let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
let atlas_origins: Vec<_> = (0..(glyph_count as u16)).map(|glyph_index| {
atlas_builder.pack_glyph(&outlines, glyph_index, point_size).unwrap() let cached_glyphs = self.determine_visible_glyphs(&mut atlas_builder,
}).collect(); font,
outlines,
glyph_indices,
glyph_positions,
device_pixel_size,
translation,
point_size);
let atlas = atlas_builder.create_atlas().unwrap(); let atlas = atlas_builder.create_atlas().unwrap();
let rect = Rect::new(Point2D::new(0, 0), self.atlas_size); let rect = Rect::new(Point2D::new(0, 0), self.atlas_size);
let events = self.rasterizer.draw_atlas(&self.main_compute_image, let events = match self.rasterizer.draw_atlas(&self.main_compute_image,
&rect, &rect,
&atlas, &atlas,
outlines, outlines,
&self.main_coverage_buffer).unwrap(); &self.main_coverage_buffer) {
Ok(events) => Some(events),
Err(RasterError::NoGlyphsToDraw) => None,
Err(error) => panic!("Failed to rasterize atlas: {:?}", error),
};
self.rasterizer.queue().flush().unwrap(); self.rasterizer.queue().flush().unwrap();
unsafe { unsafe {
@ -474,19 +490,67 @@ impl Renderer {
gl::Clear(gl::COLOR_BUFFER_BIT); gl::Clear(gl::COLOR_BUFFER_BIT);
} }
self.draw_glyphs(&font, if events.is_some() {
outlines, self.draw_glyphs(&font,
&self.main_composite_vertex_array, outlines,
glyph_indices, &self.main_composite_vertex_array,
glyph_positions, glyph_indices,
&atlas_origins, glyph_positions,
device_pixel_size, &cached_glyphs,
translation, device_pixel_size,
self.main_gl_texture, translation,
point_size, self.main_gl_texture,
&TEXT_COLOR); point_size,
&TEXT_COLOR,
true)
}
events RedrawResult {
events: events,
glyphs_drawn: cached_glyphs.len() as u32,
}
}
fn determine_visible_glyphs(&self,
atlas_builder: &mut AtlasBuilder,
font: &Font,
outlines: &Outlines,
glyph_indices: &[u16],
glyph_positions: &[GlyphPos],
device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>,
point_size: f32)
-> Vec<CachedGlyph> {
let mut glyphs = vec![];
for glyph_position in glyph_positions {
let glyph_index = glyph_indices[glyph_position.glyph_id as usize];
let glyph_rect = outlines.glyph_subpixel_bounds(glyph_index, point_size);
if let Some(subpixel) = self.subpixel_for_glyph_if_visible(font,
glyph_position,
&glyph_rect,
device_pixel_size,
translation,
point_size) {
glyphs.push((glyph_index, subpixel))
}
}
glyphs.sort();
glyphs.dedup();
glyphs.iter().map(|&(glyph_index, subpixel)| {
let subpixel_offset = (subpixel as f32) / (SUBPIXEL_GRANULARITY as f32);
let origin = atlas_builder.pack_glyph(&outlines,
glyph_index,
point_size,
subpixel_offset).unwrap();
CachedGlyph {
x: origin.x,
y: origin.y,
glyph_index: glyph_index,
subpixel: subpixel,
}
}).collect()
} }
fn get_timing_in_ms(&self) -> f64 { fn get_timing_in_ms(&self) -> f64 {
@ -497,18 +561,51 @@ impl Renderer {
} }
} }
fn subpixel_for_glyph_if_visible(&self,
font: &Font,
glyph_position: &GlyphPos,
glyph_rect: &GlyphSubpixelBounds,
device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>,
point_size: f32)
-> Option<u8> {
let pixels_per_unit = point_size / font.units_per_em() as f32;
let viewport: Rect<f32> = Rect::new(Point2D::zero(), device_pixel_size.cast().unwrap());
let glyph_size = glyph_rect.size();
let glyph_x_pos = glyph_position.x as f32 * pixels_per_unit;
let glyph_y_pos = glyph_position.y as f32 * pixels_per_unit - glyph_rect.top as f32;
let subpixel_rect = Rect::new(Point2D::new(glyph_x_pos, glyph_y_pos), glyph_size);
let subpixel_rect = subpixel_rect.translate(&translation.cast().unwrap());
let snapped_x_pos = (subpixel_rect.origin.x / SUBPIXEL_GRANULARITY).round() *
SUBPIXEL_GRANULARITY;
let snapped_y_pos = (subpixel_rect.origin.y / SUBPIXEL_GRANULARITY).round() *
SUBPIXEL_GRANULARITY;
let snapped_origin = Point2D::new(snapped_x_pos, snapped_y_pos);
if Rect::new(snapped_origin, subpixel_rect.size).intersects(&viewport) {
let mut subpixel_fract = snapped_x_pos.fract();
if subpixel_fract < 0.0 {
subpixel_fract += 1.0
}
Some((subpixel_fract / SUBPIXEL_GRANULARITY).round() as u8)
} else {
None
}
}
fn draw_glyphs(&self, fn draw_glyphs(&self,
font: &Font, font: &Font,
outlines: &Outlines, outlines: &Outlines,
vertex_array: &CompositeVertexArray, vertex_array: &CompositeVertexArray,
glyph_indices: &[u16], glyph_indices: &[u16],
glyph_positions: &[GlyphPos], glyph_positions: &[GlyphPos],
atlas_origins: &[Point2D<f32>], cached_glyphs: &[CachedGlyph],
device_pixel_size: &Size2D<u32>, device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>, translation: &Point2D<i32>,
texture: GLuint, texture: GLuint,
point_size: f32, point_size: f32,
color: &[f32]) { color: &[f32],
use_subpixel_positioning: bool) {
unsafe { unsafe {
gl::UseProgram(self.composite_program); gl::UseProgram(self.composite_program);
gl::BindVertexArray(vertex_array.vertex_array); gl::BindVertexArray(vertex_array.vertex_array);
@ -519,8 +616,11 @@ impl Renderer {
outlines, outlines,
glyph_indices, glyph_indices,
glyph_positions, glyph_positions,
atlas_origins, cached_glyphs,
point_size); device_pixel_size,
translation,
point_size,
use_subpixel_positioning);
gl::ActiveTexture(gl::TEXTURE0); gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_RECTANGLE, texture); gl::BindTexture(gl::TEXTURE_RECTANGLE, texture);
@ -558,26 +658,53 @@ impl Renderer {
outlines: &Outlines, outlines: &Outlines,
glyph_indices: &[u16], glyph_indices: &[u16],
glyph_positions: &[GlyphPos], glyph_positions: &[GlyphPos],
atlas_origins: &[Point2D<f32>], cached_glyphs: &[CachedGlyph],
point_size: f32) device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>,
point_size: f32,
use_subpixel_positioning: bool)
-> usize { -> usize {
let pixels_per_unit = point_size as f32 / font.units_per_em() as f32; let pixels_per_granule = point_size / font.units_per_em() as f32 / SUBPIXEL_GRANULARITY;
let (mut vertices, mut indices) = (vec![], vec![]); let (mut vertices, mut indices) = (vec![], vec![]);
for position in glyph_positions { for glyph_position in glyph_positions {
let glyph_index = glyph_indices[position.glyph_id as usize]; let glyph_index = glyph_indices[glyph_position.glyph_id as usize];
let glyph_rect_i = outlines.glyph_pixel_bounds(glyph_index, point_size); let glyph_rect = outlines.glyph_subpixel_bounds(glyph_index, point_size);
let uv_tl: Point2D<u32> = atlas_origins[glyph_index as usize].floor().cast().unwrap(); let subpixel = if use_subpixel_positioning {
match self.subpixel_for_glyph_if_visible(font,
glyph_position,
&glyph_rect,
device_pixel_size,
translation,
point_size) {
None => continue,
Some(subpixel) => subpixel,
}
} else {
0
};
let glyph_rect_i = glyph_rect.round_out();
let bearing_pos = (glyph_position.x as f32 * pixels_per_granule).round() *
SUBPIXEL_GRANULARITY;
let baseline_pos = (glyph_position.y as f32 * pixels_per_granule).round() *
SUBPIXEL_GRANULARITY;
let cached_glyph_index = cached_glyphs.binary_search_by(|cached_glyph| {
(cached_glyph.glyph_index, cached_glyph.subpixel).cmp(&(glyph_index, subpixel))
}).expect("Didn't cache the glyph properly!");
let cached_glyph = cached_glyphs[cached_glyph_index];
let uv_tl: Point2D<u32> = Point2D::new(cached_glyph.x,
cached_glyph.y).floor().cast().unwrap();
let uv_br = uv_tl + glyph_rect_i.size().cast().unwrap(); let uv_br = uv_tl + glyph_rect_i.size().cast().unwrap();
let bearing_pos = (position.x as f32 * pixels_per_unit).round() as i32; let left_pos = bearing_pos + glyph_rect_i.left as f32;
let baseline_pos = (position.y as f32 * pixels_per_unit).round() as i32; let top_pos = baseline_pos - glyph_rect_i.top as f32;
let right_pos = bearing_pos + glyph_rect_i.right as f32;
let left_pos = bearing_pos + glyph_rect_i.left; let bottom_pos = baseline_pos - glyph_rect_i.bottom as f32;
let top_pos = baseline_pos - glyph_rect_i.top;
let right_pos = bearing_pos + glyph_rect_i.right;
let bottom_pos = baseline_pos - glyph_rect_i.bottom;
let first_index = vertices.len() as u16; let first_index = vertices.len() as u16;
@ -603,36 +730,16 @@ impl Renderer {
indices.len() indices.len()
} }
fn create_fps_atlas(&self, font: &Font, outlines: &Outlines, glyph_count: usize)
-> Vec<Point2D<f32>> {
let shelf_height = font.shelf_height(FPS_DISPLAY_POINT_SIZE);
let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
let atlas_origins: Vec<_> = (0..(glyph_count as u16)).map(|glyph_index| {
atlas_builder.pack_glyph(&outlines, glyph_index, FPS_DISPLAY_POINT_SIZE).unwrap()
}).collect();
let atlas = atlas_builder.create_atlas().unwrap();
self.rasterizer.draw_atlas(&self.fps_compute_image,
&Rect::new(Point2D::new(0, 0), self.atlas_size),
&atlas,
outlines,
&self.fps_coverage_buffer).unwrap();
atlas_origins
}
fn draw_fps(&self, fn draw_fps(&self,
font: &Font, font: &Font,
outlines: &Outlines, outlines: &Outlines,
device_pixel_size: &Size2D<u32>, device_pixel_size: &Size2D<u32>,
atlas_origins: &[Point2D<f32>],
glyph_indices: &[u16], glyph_indices: &[u16],
glyph_mapping: &GlyphMapping, glyph_mapping: &GlyphMapping,
draw_time: f64, draw_time: f64,
accum_time: f64, accum_time: f64,
composite_time: f64, composite_time: f64,
glyph_count: usize) { glyphs_drawn: u32) {
// Draw the background color. // Draw the background color.
unsafe { unsafe {
gl::BindVertexArray(self.solid_color_vertex_array); gl::BindVertexArray(self.solid_color_vertex_array);
@ -665,34 +772,67 @@ impl Renderer {
accum: {:.3}ms ({:.3}us/glyph), \ accum: {:.3}ms ({:.3}us/glyph), \
composite: {:.3}ms ({:.3}us/glyph)", composite: {:.3}ms ({:.3}us/glyph)",
draw_time / 1_000_000.0, draw_time / 1_000_000.0,
draw_time / (1000.0 * glyph_count as f64), draw_time / (1000.0 * glyphs_drawn as f64),
accum_time / 1_000_000.0, accum_time / 1_000_000.0,
accum_time / (1000.0 * glyph_count as f64), accum_time / (1000.0 * glyphs_drawn as f64),
composite_time, composite_time,
(composite_time * 1000.0) / (glyph_count as f64)); (composite_time * 1000.0) / (glyphs_drawn as f64));
let mut fps_glyphs = vec![]; // TODO(pcwalton): Subpixel positioning for the FPS display.
let (mut fps_glyph_positions, mut fps_glyph_indices) = (vec![], vec![]);
let mut current_x = 0; let mut current_x = 0;
for glyph_pos in &shaper::shape_text(&font, &glyph_mapping, &fps_text) { for glyph_pos in &shaper::shape_text(&font, &glyph_mapping, &fps_text) {
fps_glyphs.push(GlyphPos { fps_glyph_positions.push(GlyphPos {
x: current_x, x: current_x,
y: 0, y: 0,
glyph_id: glyph_pos.glyph_id, glyph_id: glyph_pos.glyph_id,
}); });
current_x += glyph_pos.advance as u32; current_x += glyph_pos.advance as u32;
fps_glyph_indices.push(glyph_indices[glyph_pos.glyph_id as usize]);
} }
let shelf_height = font.shelf_height(FPS_DISPLAY_POINT_SIZE);
let mut fps_atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
let mut fps_glyphs = vec![];
fps_glyph_indices.sort();
fps_glyph_indices.dedup();
for &fps_glyph_index in &fps_glyph_indices {
let origin = fps_atlas_builder.pack_glyph(&outlines,
fps_glyph_index,
FPS_DISPLAY_POINT_SIZE,
0.0).unwrap();
fps_glyphs.push(CachedGlyph {
x: origin.x,
y: origin.y,
glyph_index: fps_glyph_index,
subpixel: 0,
})
}
let fps_atlas = fps_atlas_builder.create_atlas().unwrap();
let rect = Rect::new(Point2D::new(0, 0), self.atlas_size);
self.rasterizer.draw_atlas(&self.fps_compute_image,
&rect,
&fps_atlas,
outlines,
&self.fps_coverage_buffer).unwrap();
self.rasterizer.queue().flush().unwrap();
self.draw_glyphs(font, self.draw_glyphs(font,
outlines, outlines,
&self.fps_composite_vertex_array, &self.fps_composite_vertex_array,
glyph_indices, glyph_indices,
&fps_glyph_positions,
&fps_glyphs, &fps_glyphs,
atlas_origins,
device_pixel_size, device_pixel_size,
&Point2D::new(FPS_PADDING, device_pixel_size.height as i32 - FPS_PADDING), &Point2D::new(FPS_PADDING, device_pixel_size.height as i32 - FPS_PADDING),
self.fps_gl_texture, self.fps_gl_texture,
FPS_DISPLAY_POINT_SIZE, FPS_DISPLAY_POINT_SIZE,
&FPS_FOREGROUND_COLOR); &FPS_FOREGROUND_COLOR,
false);
} }
fn take_screenshot(&self) { fn take_screenshot(&self) {
@ -729,14 +869,14 @@ impl Renderer {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(C)] #[repr(C)]
struct Vertex { struct Vertex {
x: i32, x: f32,
y: i32, y: f32,
u: u32, u: u32,
v: u32, v: u32,
} }
impl Vertex { impl Vertex {
fn new(x: i32, y: i32, u: u32, v: u32) -> Vertex { fn new(x: f32, y: f32, u: u32, v: u32) -> Vertex {
Vertex { Vertex {
x: x, x: x,
y: y, y: y,
@ -753,6 +893,14 @@ struct GlyphPos {
glyph_id: u16, glyph_id: u16,
} }
#[derive(Clone, Copy, Debug)]
struct CachedGlyph {
x: f32,
y: f32,
glyph_index: u16,
subpixel: u8,
}
#[derive(Debug)] #[derive(Debug)]
struct CompositeVertexArray { struct CompositeVertexArray {
vertex_array: GLuint, vertex_array: GLuint,
@ -806,6 +954,8 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Image, GL
buffer::Protection::ReadWrite, buffer::Protection::ReadWrite,
&atlas_size).unwrap(); &atlas_size).unwrap();
rasterizer.queue().submit_clear(&compute_image, &Color::UInt(0, 0, 0, 0), &[]).unwrap();
let mut gl_texture = 0; let mut gl_texture = 0;
unsafe { unsafe {
gl::GenTextures(1, &mut gl_texture); gl::GenTextures(1, &mut gl_texture);
@ -821,6 +971,11 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Image, GL
(compute_image, gl_texture) (compute_image, gl_texture)
} }
struct RedrawResult {
events: Option<DrawAtlasProfilingEvents>,
glyphs_drawn: u32,
}
static COMPOSITE_VERTEX_SHADER: &'static str = "\ static COMPOSITE_VERTEX_SHADER: &'static str = "\
#version 330 #version 330

View File

@ -40,8 +40,6 @@ layout(std140) uniform ubImageDescriptors {
in ivec2 aPosition; in ivec2 aPosition;
// Which glyph the vertex belongs to. // Which glyph the vertex belongs to.
//
// TODO(pcwalton): See if this is faster as a binary search on the vertex ID.
in uint aGlyphIndex; in uint aGlyphIndex;
// The vertex ID, passed along onto the TCS. // The vertex ID, passed along onto the TCS.

View File

@ -11,7 +11,7 @@
//! Atlases, which hold rendered glyphs on the GPU. //! Atlases, which hold rendered glyphs on the GPU.
use error::GlError; use error::GlError;
use euclid::{Point2D, Rect, Size2D}; use euclid::Point2D;
use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid}; use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid};
use gl; use gl;
use outline::Outlines; use outline::Outlines;
@ -61,16 +61,25 @@ impl AtlasBuilder {
/// ///
/// Returns the subpixel origin of the glyph in the atlas if successful or an error if there is /// Returns the subpixel origin of the glyph in the atlas if successful or an error if there is
/// no space left for the glyph. /// no space left for the glyph.
pub fn pack_glyph(&mut self, outlines: &Outlines, glyph_index: u16, point_size: f32) pub fn pack_glyph(&mut self,
outlines: &Outlines,
glyph_index: u16,
point_size: f32,
horizontal_offset: f32)
-> Result<Point2D<f32>, ()> { -> Result<Point2D<f32>, ()> {
let pixel_bounds = outlines.glyph_pixel_bounds(glyph_index, point_size); let mut subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, point_size);
subpixel_bounds.left += horizontal_offset;
subpixel_bounds.right += horizontal_offset;
let pixel_bounds = subpixel_bounds.round_out();
let atlas_origin = try!(self.rect_packer.pack(&pixel_bounds.size().cast().unwrap())); let atlas_origin = try!(self.rect_packer.pack(&pixel_bounds.size().cast().unwrap()));
for batch_builder in &mut self.batch_builders { for batch_builder in &mut self.batch_builders {
if let Ok(atlas_origin) = batch_builder.add_glyph(outlines, if let Ok(atlas_origin) = batch_builder.add_glyph(outlines,
&atlas_origin, &atlas_origin,
glyph_index, glyph_index,
point_size) { point_size,
horizontal_offset) {
return Ok(atlas_origin) return Ok(atlas_origin)
} }
} }
@ -79,13 +88,14 @@ impl AtlasBuilder {
let atlas_origin = try!(batch_builder.add_glyph(outlines, let atlas_origin = try!(batch_builder.add_glyph(outlines,
&atlas_origin, &atlas_origin,
glyph_index, glyph_index,
point_size)); point_size,
horizontal_offset));
self.batch_builders.push(batch_builder); self.batch_builders.push(batch_builder);
Ok(atlas_origin) Ok(atlas_origin)
} }
/// Creates an atlas by uploading the atlas info to the GPU. /// Creates an atlas by uploading the atlas info to the GPU.
pub fn create_atlas(mut self) -> Result<Atlas, GlError> { pub fn create_atlas(self) -> Result<Atlas, GlError> {
let mut batches = vec![]; let mut batches = vec![];
for batch_builder in self.batch_builders.into_iter() { for batch_builder in self.batch_builders.into_iter() {
batches.push(try!(batch_builder.create_batch())) batches.push(try!(batch_builder.create_batch()))
@ -116,11 +126,14 @@ impl BatchBuilder {
outlines: &Outlines, outlines: &Outlines,
atlas_origin: &Point2D<u32>, atlas_origin: &Point2D<u32>,
glyph_index: u16, glyph_index: u16,
point_size: f32) point_size: f32,
horizontal_offset: f32)
-> Result<Point2D<f32>, ()> { -> Result<Point2D<f32>, ()> {
// Check to see if we're already rendering this glyph. // Check to see if we're already rendering this glyph.
if let Some(image_descriptor) = self.image_descriptors.get(glyph_index as usize) { let image_index = glyph_index as usize;
if image_descriptor.point_size == point_size { if let Some(image_descriptor) = self.image_descriptors.get(image_index) {
if image_descriptor.point_size == point_size &&
self.image_metadata[image_index].horizontal_offset == horizontal_offset {
// Glyph is already present. // Glyph is already present.
return Ok(Point2D::new(image_descriptor.atlas_x, image_descriptor.atlas_y)) return Ok(Point2D::new(image_descriptor.atlas_x, image_descriptor.atlas_y))
} else { } else {
@ -131,28 +144,29 @@ impl BatchBuilder {
let subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, point_size); let subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, point_size);
let glyph_id = outlines.glyph_id(glyph_index); let glyph_id = outlines.glyph_id(glyph_index);
let glyph_index = self.image_descriptors.len() as u16;
while self.image_descriptors.len() < glyph_index as usize + 1 { while self.image_descriptors.len() < image_index + 1 {
self.image_descriptors.push(ImageDescriptor::default()) self.image_descriptors.push(ImageDescriptor::default());
self.image_metadata.push(ImageMetadata::default());
} }
while self.image_metadata.len() < glyph_index as usize + 1 { let units_per_em = outlines.glyph_units_per_em(glyph_index) as f32;
self.image_metadata.push(ImageMetadata::default()) let horizontal_px_offset = horizontal_offset / units_per_em * point_size;
}
let atlas_origin = Point2D::new(atlas_origin.x as f32 + subpixel_bounds.left.fract(), let atlas_origin = Point2D::new(
atlas_origin.y as f32 + 1.0 - subpixel_bounds.top.fract()); atlas_origin.x as f32 + subpixel_bounds.left.fract() + horizontal_px_offset,
self.image_descriptors[glyph_index as usize] = ImageDescriptor { atlas_origin.y as f32 + 1.0 - subpixel_bounds.top.fract());
self.image_descriptors[image_index] = ImageDescriptor {
atlas_x: atlas_origin.x, atlas_x: atlas_origin.x,
atlas_y: atlas_origin.y, atlas_y: atlas_origin.y,
point_size: point_size, point_size: point_size,
glyph_index: glyph_index as f32, pad: 0.0,
}; };
self.image_metadata[glyph_index as usize] = ImageMetadata { self.image_metadata[image_index] = ImageMetadata {
glyph_index: glyph_index as u32, glyph_index: glyph_index as u32,
glyph_id: glyph_id, glyph_id: glyph_id,
horizontal_offset: horizontal_offset,
start_index: outlines.descriptor(glyph_index).unwrap().start_index(), start_index: outlines.descriptor(glyph_index).unwrap().start_index(),
end_index: match outlines.descriptor(glyph_index + 1) { end_index: match outlines.descriptor(glyph_index + 1) {
Some(descriptor) => descriptor.start_index() as u32, Some(descriptor) => descriptor.start_index() as u32,
@ -164,14 +178,13 @@ impl BatchBuilder {
} }
/// Uploads this batch data to the GPU. /// Uploads this batch data to the GPU.
fn create_batch(mut self) -> Result<Batch, GlError> { fn create_batch(self) -> Result<Batch, GlError> {
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![]); let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
for image_metadata in &self.image_metadata { for image_metadata in &self.image_metadata {
let glyph_index = image_metadata.glyph_index; let (start_index, end_index) = (image_metadata.start_index, image_metadata.end_index);
let start_index = image_metadata.start_index; if start_index == 0 {
let end_index = image_metadata.end_index; continue
}
match current_range { match current_range {
Some((current_first, current_last)) if start_index == current_last => { Some((current_first, current_last)) if start_index == current_last => {
@ -179,7 +192,7 @@ impl BatchBuilder {
} }
Some((current_first, current_last)) => { Some((current_first, current_last)) => {
counts.push((current_last - current_first) as GLsizei); counts.push((current_last - current_first) as GLsizei);
start_indices.push(current_first as usize); start_indices.push((current_first as usize) * mem::size_of::<u32>());
current_range = Some((start_index, end_index)) current_range = Some((start_index, end_index))
} }
None => current_range = Some((start_index, end_index)), None => current_range = Some((start_index, end_index)),
@ -187,7 +200,7 @@ impl BatchBuilder {
} }
if let Some((current_first, current_last)) = current_range { if let Some((current_first, current_last)) = current_range {
counts.push((current_last - current_first) as GLsizei); counts.push((current_last - current_first) as GLsizei);
start_indices.push(current_first as usize); start_indices.push((current_first as usize) * mem::size_of::<u32>());
} }
// TODO(pcwalton): Try using `glMapBuffer` here. // TODO(pcwalton): Try using `glMapBuffer` here.
@ -235,6 +248,12 @@ impl Atlas {
pub fn shelf_columns(&self) -> u32 { pub fn shelf_columns(&self) -> u32 {
self.shelf_columns self.shelf_columns
} }
/// Returns true if this atlas has no glyphs of nonzero size in it.
#[inline]
pub fn is_empty(&self) -> bool {
self.shelf_columns == 0
}
} }
struct Batch { struct Batch {
@ -274,7 +293,7 @@ pub struct ImageDescriptor {
atlas_x: f32, atlas_x: f32,
atlas_y: f32, atlas_y: f32,
point_size: f32, point_size: f32,
glyph_index: f32, pad: f32,
} }
// Information about each image that we keep around ourselves. // Information about each image that we keep around ourselves.
@ -282,8 +301,9 @@ pub struct ImageDescriptor {
#[derive(Clone, Copy, Default, Debug)] #[derive(Clone, Copy, Default, Debug)]
pub struct ImageMetadata { pub struct ImageMetadata {
glyph_index: u32, glyph_index: u32,
glyph_id: u16,
start_index: u32, start_index: u32,
end_index: u32, end_index: u32,
horizontal_offset: f32,
glyph_id: u16,
} }

View File

@ -116,17 +116,6 @@ pub struct GlyphMapping {
ranges: Vec<MappedGlyphRange>, ranges: Vec<MappedGlyphRange>,
} }
impl GlyphRange {
/// Returns an iterator over every glyph in this range.
#[inline]
fn iter(&self) -> GlyphRangeIter {
GlyphRangeIter {
start: self.start,
end: self.end,
}
}
}
impl GlyphMapping { impl GlyphMapping {
#[doc(hidden)] #[doc(hidden)]
#[inline] #[inline]

View File

@ -51,6 +51,8 @@ pub enum InitError {
/// A rasterization error. This could be an OpenGL error or a compute error. /// A rasterization error. This could be an OpenGL error or a compute error.
#[derive(Debug)] #[derive(Debug)]
pub enum RasterError { pub enum RasterError {
/// No glyphs were supplied.
NoGlyphsToDraw,
/// An OpenGL error occurred. /// An OpenGL error occurred.
GlError(GlError), GlError(GlError),
/// An error occurred during GPU compute. /// An error occurred during GPU compute.

View File

@ -8,10 +8,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use euclid::Point2D; use euclid::Point2D;
use otf::glyf::{Point, PointKind}; use otf::glyf::{Point, PointKind};
use otf::head::HeadTable;
use otf::{Error, FontTable}; use otf::{Error, FontTable};
use outline::GlyphBounds; use outline::GlyphBounds;
use std::cmp; use std::cmp;
@ -107,7 +106,7 @@ impl<'a> CffTable<'a> {
4 => { 4 => {
// |- dy1 vmoveto // |- dy1 vmoveto
close_path_if_necessary(&mut pos, &start, index_in_contour, &mut callback); close_path_if_necessary(&start, index_in_contour, &mut callback);
pos.y += stack.array[0] as i16; pos.y += stack.array[0] as i16;
callback(&Point { callback(&Point {
position: pos, position: pos,
@ -295,9 +294,7 @@ impl<'a> CffTable<'a> {
start = 1 start = 1
} }
for (i, chunk) in stack.array[start..stack.size as usize] for chunk in stack.array[start..stack.size as usize].chunks(4) {
.chunks(4)
.enumerate() {
add_curve(0, chunk[0] as i16, add_curve(0, chunk[0] as i16,
chunk[1] as i16, chunk[2] as i16, chunk[1] as i16, chunk[2] as i16,
0, chunk[3] as i16, 0, chunk[3] as i16,
@ -317,9 +314,7 @@ impl<'a> CffTable<'a> {
start = 1 start = 1
} }
for (i, chunk) in stack.array[start..stack.size as usize] for chunk in stack.array[start..stack.size as usize].chunks(4) {
.chunks(4)
.enumerate() {
add_curve(chunk[0] as i16, 0, add_curve(chunk[0] as i16, 0,
chunk[1] as i16, chunk[2] as i16, chunk[1] as i16, chunk[2] as i16,
chunk[3] as i16, 0, chunk[3] as i16, 0,
@ -364,7 +359,7 @@ impl<'a> CffTable<'a> {
} }
21 => { 21 => {
// |- dx1 dy1 rmoveto // |- dx1 dy1 rmoveto
close_path_if_necessary(&mut pos, &start, index_in_contour, &mut callback); close_path_if_necessary(&start, index_in_contour, &mut callback);
pos = pos + Point2D::new(stack.array[0] as i16, stack.array[1] as i16); pos = pos + Point2D::new(stack.array[0] as i16, stack.array[1] as i16);
callback(&Point { callback(&Point {
position: pos, position: pos,
@ -377,7 +372,7 @@ impl<'a> CffTable<'a> {
} }
22 => { 22 => {
// |- dx1 hmoveto // |- dx1 hmoveto
close_path_if_necessary(&mut pos, &start, index_in_contour, &mut callback); close_path_if_necessary(&start, index_in_contour, &mut callback);
pos.x += stack.array[0] as i16; pos.x += stack.array[0] as i16;
callback(&Point { callback(&Point {
position: pos, position: pos,
@ -403,7 +398,7 @@ impl<'a> CffTable<'a> {
} }
} }
close_path_if_necessary(&mut pos, &start, index_in_contour, &mut callback); close_path_if_necessary(&start, index_in_contour, &mut callback);
Ok(()) Ok(())
} }
@ -411,12 +406,12 @@ impl<'a> CffTable<'a> {
// TODO(pcwalton): Compute this at the same time as `for_each_point`, perhaps? // TODO(pcwalton): Compute this at the same time as `for_each_point`, perhaps?
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> { pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> {
let mut bounds = GlyphBounds::default(); let mut bounds = GlyphBounds::default();
self.for_each_point(glyph_id, |point| { try!(self.for_each_point(glyph_id, |point| {
bounds.left = cmp::min(bounds.left, point.position.x as i32); bounds.left = cmp::min(bounds.left, point.position.x as i32);
bounds.bottom = cmp::min(bounds.bottom, point.position.y as i32); bounds.bottom = cmp::min(bounds.bottom, point.position.y as i32);
bounds.right = cmp::max(bounds.right, point.position.x as i32); bounds.right = cmp::max(bounds.right, point.position.x as i32);
bounds.top = cmp::max(bounds.top, point.position.y as i32); bounds.top = cmp::max(bounds.top, point.position.y as i32);
}); }));
Ok(bounds) Ok(bounds)
} }
} }
@ -547,10 +542,7 @@ impl EvaluationStack {
} }
} }
fn close_path_if_necessary<F>(pos: &mut Point2D<i16>, fn close_path_if_necessary<F>(start: &Point2D<i16>, index_in_contour: u16, mut callback: F)
start: &Point2D<i16>,
index_in_contour: u16,
mut callback: F)
where F: FnMut(&Point) { where F: FnMut(&Point) {
if index_in_contour == 0 { if index_in_contour == 0 {
// No path to close. // No path to close.

View File

@ -11,7 +11,6 @@
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange}; use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange};
use otf::{Error, FontTable}; use otf::{Error, FontTable};
use std::char;
use std::cmp; use std::cmp;
use std::mem; use std::mem;
use std::u16; use std::u16;

View File

@ -90,7 +90,7 @@ impl<'a> GlyfTable<'a> {
head_table: &HeadTable, head_table: &HeadTable,
loca_table: &LocaTable, loca_table: &LocaTable,
glyph_id: u16, glyph_id: u16,
mut callback: F) callback: F)
-> Result<(), Error> where F: FnMut(&Point) { -> Result<(), Error> where F: FnMut(&Point) {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
@ -154,7 +154,7 @@ impl<'a> GlyfTable<'a> {
let mut last_point_was_off_curve = false; let mut last_point_was_off_curve = false;
let mut point_index_in_contour = 0; let mut point_index_in_contour = 0;
for contour_point_index in 0..contour_point_count { for _ in 0..contour_point_count {
let flags = SimpleFlags::from_bits_truncate(*flag_parser.current); let flags = SimpleFlags::from_bits_truncate(*flag_parser.current);
try!(flag_parser.next()); try!(flag_parser.next());
@ -301,9 +301,9 @@ impl<'a> GlyfTable<'a> {
if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) { if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
try!(reader.jump(offset as usize).map_err(Error::eof)); try!(reader.jump(offset as usize).map_err(Error::eof));
self.for_each_point_in_simple_glyph(reader, |point| { try!(self.for_each_point_in_simple_glyph(reader, |point| {
callback(&transform.transform(&point)) callback(&transform.transform(&point))
}); }));
} }
if !flags.contains(MORE_COMPONENTS) { if !flags.contains(MORE_COMPONENTS) {

View File

@ -24,7 +24,6 @@ bitflags! {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct KernTable<'a> { pub struct KernTable<'a> {
table: FontTable<'a>,
horizontal_table: &'a [u8], horizontal_table: &'a [u8],
} }
@ -38,7 +37,7 @@ impl<'a> KernTable<'a> {
let n_tables = try!(kern_reader.read_u16::<BigEndian>().map_err(Error::eof)); let n_tables = try!(kern_reader.read_u16::<BigEndian>().map_err(Error::eof));
let mut horizontal_table = None; let mut horizontal_table = None;
for table_index in 0..n_tables { for _ in 0..n_tables {
let mut table_reader = kern_reader; let mut table_reader = kern_reader;
let _version = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof)); let _version = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
let length = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof)); let length = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
@ -58,7 +57,6 @@ impl<'a> KernTable<'a> {
match horizontal_table { match horizontal_table {
Some(horizontal_table) => { Some(horizontal_table) => {
Ok(KernTable { Ok(KernTable {
table: table,
horizontal_table: horizontal_table, horizontal_table: horizontal_table,
}) })
} }

View File

@ -138,9 +138,9 @@ impl<'a> Font<'a> {
/// ///
/// Returns the font on success or an error on failure. /// Returns the font on success or an error on failure.
pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, Error> { pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, Error> {
// Check magic number. // Check the magic number.
let mut reader = bytes; let mut reader = bytes;
let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
match magic_number { match magic_number {
TTCF => { TTCF => {
// This is a font collection. Read the first font. // This is a font collection. Read the first font.
@ -171,10 +171,8 @@ impl<'a> Font<'a> {
let mut reader = bytes; let mut reader = bytes;
try!(reader.jump(offset as usize).map_err(Error::eof)); try!(reader.jump(offset as usize).map_err(Error::eof));
let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); // Check the magic number.
if !SFNT_VERSIONS.contains(&try!(reader.read_u32::<BigEndian>().map_err(Error::eof))) {
// Check version.
if !SFNT_VERSIONS.contains(&magic_number) {
return Err(Error::UnknownFormat) return Err(Error::UnknownFormat)
} }
@ -252,8 +250,8 @@ impl<'a> Font<'a> {
// Read the Mac resource file header. // Read the Mac resource file header.
let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let _resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let _resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Move to the fields we care about in the resource map. // Move to the fields we care about in the resource map.
reader = bytes; reader = bytes;
@ -262,7 +260,7 @@ impl<'a> Font<'a> {
// Read the type list and name list offsets. // Read the type list and name list offsets.
let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let name_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let _name_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
// Move to the type list. // Move to the type list.
reader = bytes; reader = bytes;
@ -272,7 +270,7 @@ impl<'a> Font<'a> {
// Find the 'sfnt' type. // Find the 'sfnt' type.
let type_count = (try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) + 1) as usize; let type_count = (try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) + 1) as usize;
let mut resource_count_and_list_offset = None; let mut resource_count_and_list_offset = None;
for type_index in 0..type_count { for _ in 0..type_count {
let type_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let type_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_count = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let resource_count = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
@ -302,11 +300,11 @@ impl<'a> Font<'a> {
// Find the font we're interested in. // Find the font we're interested in.
try!(reader.jump(index as usize * (mem::size_of::<u16>() * 2 + mem::size_of::<u32>() * 2)) try!(reader.jump(index as usize * (mem::size_of::<u16>() * 2 + mem::size_of::<u32>() * 2))
.map_err(Error::eof)); .map_err(Error::eof));
let sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let _sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)); let _sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) & let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) &
0x00ffffff; 0x00ffffff;
let sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)); let _sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Load the resource. // Load the resource.
reader = bytes; reader = bytes;

View File

@ -191,17 +191,17 @@ impl Outlines {
self.descriptors[glyph_index as usize].subpixel_bounds(point_size) self.descriptors[glyph_index as usize].subpixel_bounds(point_size)
} }
/// Returns the boundaries of the glyph, rounded out to the nearest pixel.
#[inline]
pub fn glyph_pixel_bounds(&self, glyph_index: u16, point_size: f32) -> GlyphPixelBounds {
self.descriptors[glyph_index as usize].subpixel_bounds(point_size).round_out()
}
/// Returns the ID of the glyph with the given index. /// Returns the ID of the glyph with the given index.
#[inline] #[inline]
pub fn glyph_id(&self, glyph_index: u16) -> u16 { pub fn glyph_id(&self, glyph_index: u16) -> u16 {
self.descriptors[glyph_index as usize].glyph_id self.descriptors[glyph_index as usize].glyph_id
} }
/// Returns the units per em for the glyph with the given index.
#[inline]
pub fn glyph_units_per_em(&self, glyph_index: u16) -> u32 {
self.descriptors[glyph_index as usize].units_per_em
}
} }
#[doc(hidden)] #[doc(hidden)]

View File

@ -237,8 +237,16 @@ impl Rasterizer {
outlines: &Outlines, outlines: &Outlines,
coverage_buffer: &CoverageBuffer) coverage_buffer: &CoverageBuffer)
-> Result<DrawAtlasProfilingEvents, RasterError> { -> Result<DrawAtlasProfilingEvents, RasterError> {
if atlas.is_empty() {
return Err(RasterError::NoGlyphsToDraw)
}
unsafe { unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer()); gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer());
// Save the old viewport so we can restore it later.
let mut old_viewport: [GLint; 4] = [0; 4];
gl::GetIntegerv(gl::VIEWPORT, old_viewport.as_mut_ptr());
gl::Viewport(0, 0, rect.size.width as GLint, rect.size.height as GLint); gl::Viewport(0, 0, rect.size.width as GLint, rect.size.height as GLint);
// TODO(pcwalton): Scissor to the image rect to clear faster? // TODO(pcwalton): Scissor to the image rect to clear faster?
@ -302,7 +310,9 @@ impl Rasterizer {
gl::Disable(gl::CULL_FACE); gl::Disable(gl::CULL_FACE);
gl::Disable(gl::BLEND); gl::Disable(gl::BLEND);
// Restore our old framebuffer and viewport.
gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::Viewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
// FIXME(pcwalton): We should have some better synchronization here if we're using // FIXME(pcwalton): We should have some better synchronization here if we're using
// OpenCL, but I don't know how to do that portably (i.e. on Mac…) Just using // OpenCL, but I don't know how to do that portably (i.e. on Mac…) Just using