parent
e2014cff13
commit
49408b95cb
|
@ -90,7 +90,8 @@ fn main() {
|
|||
let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint,
|
||||
shelf_height);
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -5,10 +5,9 @@ extern crate euclid;
|
|||
extern crate memmap;
|
||||
extern crate pathfinder;
|
||||
|
||||
use euclid::Point2D;
|
||||
use memmap::{Mmap, Protection};
|
||||
use pathfinder::charmap::CodepointRange;
|
||||
use pathfinder::otf::Font;
|
||||
use pathfinder::otf::{Font, PointKind};
|
||||
use std::char;
|
||||
use std::env;
|
||||
|
||||
|
@ -25,28 +24,26 @@ fn main() {
|
|||
codepoint,
|
||||
char::from_u32(codepoint).unwrap_or('?'));
|
||||
|
||||
let mut last_point: Option<Point2D<i16>> = None;
|
||||
let mut last_point_was_off_curve = false;
|
||||
let mut last_point_was_on_curve = false;
|
||||
font.for_each_point(glyph_id, |point| {
|
||||
if point.index_in_contour == 0 {
|
||||
println!("M {},{}", point.position.x, point.position.y);
|
||||
let prefix = if point.index_in_contour == 0 {
|
||||
"M "
|
||||
} else {
|
||||
let last = last_point.unwrap();
|
||||
if point.on_curve {
|
||||
if last_point_was_off_curve {
|
||||
println!("Q {},{} {},{}",
|
||||
last.x,
|
||||
last.y,
|
||||
point.position.x,
|
||||
point.position.y);
|
||||
} else {
|
||||
println!("L {},{}", point.position.x, point.position.y);
|
||||
}
|
||||
match point.kind {
|
||||
PointKind::OnCurve if last_point_was_on_curve => "L ",
|
||||
PointKind::OnCurve => " ",
|
||||
PointKind::QuadControl => "Q ",
|
||||
PointKind::FirstCubicControl => "C ",
|
||||
PointKind::SecondCubicControl => " ",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
last_point_was_off_curve = !point.on_curve;
|
||||
last_point = Some(point.position);
|
||||
print!("{}{},{}", prefix, point.position.x, point.position.y);
|
||||
|
||||
last_point_was_on_curve = point.kind == PointKind::OnCurve;
|
||||
if last_point_was_on_curve {
|
||||
println!("")
|
||||
}
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ fn main() {
|
|||
|
||||
let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height);
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ extern crate pathfinder;
|
|||
|
||||
use clap::{App, Arg};
|
||||
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 euclid::{Point2D, Rect, Size2D};
|
||||
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
|
@ -22,8 +22,9 @@ use memmap::{Mmap, Protection};
|
|||
use pathfinder::atlas::AtlasBuilder;
|
||||
use pathfinder::charmap::{CodepointRanges, GlyphMapping};
|
||||
use pathfinder::coverage::CoverageBuffer;
|
||||
use pathfinder::error::RasterError;
|
||||
use pathfinder::otf::Font;
|
||||
use pathfinder::outline::{OutlineBuilder, Outlines};
|
||||
use pathfinder::outline::{GlyphSubpixelBounds, OutlineBuilder, Outlines};
|
||||
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
||||
use pathfinder::shaper;
|
||||
use std::char;
|
||||
|
@ -39,9 +40,11 @@ const WIDTH: u32 = 640;
|
|||
const HEIGHT: u32 = 480;
|
||||
const SCROLL_SPEED: f64 = 6.0;
|
||||
|
||||
const SUBPIXEL_GRANULARITY: f32 = 0.25;
|
||||
|
||||
const INITIAL_POINT_SIZE: f32 = 24.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_PADDING: i32 = 6;
|
||||
|
@ -151,52 +154,56 @@ fn main() {
|
|||
|
||||
let mut outline_builder = OutlineBuilder::new();
|
||||
let mut glyph_indices = vec![];
|
||||
let mut glyph_count = 0;
|
||||
for (_, glyph_id) in glyph_mapping.iter() {
|
||||
let glyph_index = outline_builder.add_glyph(&font, glyph_id).unwrap();
|
||||
|
||||
while glyph_id as usize >= glyph_indices.len() {
|
||||
glyph_indices.push(0)
|
||||
}
|
||||
glyph_indices[glyph_id as usize] = glyph_index;
|
||||
|
||||
glyph_count += 1
|
||||
glyph_indices[glyph_id as usize] = glyph_index
|
||||
}
|
||||
|
||||
let outlines = outline_builder.create_buffers().unwrap();
|
||||
let fps_atlas_origins = renderer.create_fps_atlas(&font, &outlines, glyph_count);
|
||||
|
||||
while !window.should_close() {
|
||||
if dirty {
|
||||
let events = renderer.redraw(point_size,
|
||||
&font,
|
||||
&outlines,
|
||||
&glyph_indices,
|
||||
glyph_count,
|
||||
&glyph_positions,
|
||||
&device_pixel_size,
|
||||
&translation);
|
||||
let redraw_result = renderer.redraw(point_size,
|
||||
&font,
|
||||
&outlines,
|
||||
&glyph_indices,
|
||||
&glyph_positions,
|
||||
&device_pixel_size,
|
||||
&translation);
|
||||
|
||||
let mut draw_time = 0u64;
|
||||
unsafe {
|
||||
gl::Flush();
|
||||
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_time);
|
||||
let (draw_time, accum_time);
|
||||
match redraw_result.events {
|
||||
Some(events) => {
|
||||
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();
|
||||
|
||||
renderer.draw_fps(&font,
|
||||
&outlines,
|
||||
&device_pixel_size,
|
||||
&fps_atlas_origins,
|
||||
&glyph_indices,
|
||||
&glyph_mapping,
|
||||
draw_time,
|
||||
accum_time,
|
||||
timing,
|
||||
glyph_count);
|
||||
redraw_result.glyphs_drawn);
|
||||
|
||||
window.swap_buffers();
|
||||
|
||||
|
@ -344,7 +351,7 @@ impl Renderer {
|
|||
|
||||
gl::VertexAttribPointer(composite_position_attribute as GLuint,
|
||||
2,
|
||||
gl::INT,
|
||||
gl::FLOAT,
|
||||
gl::FALSE,
|
||||
mem::size_of::<Vertex>() as GLsizei,
|
||||
0 as *const GLvoid);
|
||||
|
@ -438,26 +445,35 @@ impl Renderer {
|
|||
font: &Font,
|
||||
outlines: &Outlines,
|
||||
glyph_indices: &[u16],
|
||||
glyph_count: usize,
|
||||
glyph_positions: &[GlyphPos],
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
translation: &Point2D<i32>)
|
||||
-> DrawAtlasProfilingEvents {
|
||||
-> RedrawResult {
|
||||
let shelf_height = font.shelf_height(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, point_size).unwrap()
|
||||
}).collect();
|
||||
|
||||
let cached_glyphs = self.determine_visible_glyphs(&mut atlas_builder,
|
||||
font,
|
||||
outlines,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
device_pixel_size,
|
||||
translation,
|
||||
point_size);
|
||||
|
||||
let atlas = atlas_builder.create_atlas().unwrap();
|
||||
|
||||
let rect = Rect::new(Point2D::new(0, 0), self.atlas_size);
|
||||
|
||||
let events = self.rasterizer.draw_atlas(&self.main_compute_image,
|
||||
&rect,
|
||||
&atlas,
|
||||
outlines,
|
||||
&self.main_coverage_buffer).unwrap();
|
||||
let events = match self.rasterizer.draw_atlas(&self.main_compute_image,
|
||||
&rect,
|
||||
&atlas,
|
||||
outlines,
|
||||
&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();
|
||||
|
||||
unsafe {
|
||||
|
@ -474,19 +490,67 @@ impl Renderer {
|
|||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
self.draw_glyphs(&font,
|
||||
outlines,
|
||||
&self.main_composite_vertex_array,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
&atlas_origins,
|
||||
device_pixel_size,
|
||||
translation,
|
||||
self.main_gl_texture,
|
||||
point_size,
|
||||
&TEXT_COLOR);
|
||||
if events.is_some() {
|
||||
self.draw_glyphs(&font,
|
||||
outlines,
|
||||
&self.main_composite_vertex_array,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
&cached_glyphs,
|
||||
device_pixel_size,
|
||||
translation,
|
||||
self.main_gl_texture,
|
||||
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 {
|
||||
|
@ -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,
|
||||
font: &Font,
|
||||
outlines: &Outlines,
|
||||
vertex_array: &CompositeVertexArray,
|
||||
glyph_indices: &[u16],
|
||||
glyph_positions: &[GlyphPos],
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
cached_glyphs: &[CachedGlyph],
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
translation: &Point2D<i32>,
|
||||
texture: GLuint,
|
||||
point_size: f32,
|
||||
color: &[f32]) {
|
||||
color: &[f32],
|
||||
use_subpixel_positioning: bool) {
|
||||
unsafe {
|
||||
gl::UseProgram(self.composite_program);
|
||||
gl::BindVertexArray(vertex_array.vertex_array);
|
||||
|
@ -519,8 +616,11 @@ impl Renderer {
|
|||
outlines,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
atlas_origins,
|
||||
point_size);
|
||||
cached_glyphs,
|
||||
device_pixel_size,
|
||||
translation,
|
||||
point_size,
|
||||
use_subpixel_positioning);
|
||||
|
||||
gl::ActiveTexture(gl::TEXTURE0);
|
||||
gl::BindTexture(gl::TEXTURE_RECTANGLE, texture);
|
||||
|
@ -558,26 +658,53 @@ impl Renderer {
|
|||
outlines: &Outlines,
|
||||
glyph_indices: &[u16],
|
||||
glyph_positions: &[GlyphPos],
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
point_size: f32)
|
||||
cached_glyphs: &[CachedGlyph],
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
translation: &Point2D<i32>,
|
||||
point_size: f32,
|
||||
use_subpixel_positioning: bool)
|
||||
-> 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![]);
|
||||
for position in glyph_positions {
|
||||
let glyph_index = glyph_indices[position.glyph_id as usize];
|
||||
let glyph_rect_i = outlines.glyph_pixel_bounds(glyph_index, point_size);
|
||||
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);
|
||||
|
||||
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 bearing_pos = (position.x as f32 * pixels_per_unit).round() as i32;
|
||||
let baseline_pos = (position.y as f32 * pixels_per_unit).round() as i32;
|
||||
|
||||
let left_pos = bearing_pos + glyph_rect_i.left;
|
||||
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 left_pos = bearing_pos + glyph_rect_i.left as f32;
|
||||
let top_pos = baseline_pos - glyph_rect_i.top as f32;
|
||||
let right_pos = bearing_pos + glyph_rect_i.right as f32;
|
||||
let bottom_pos = baseline_pos - glyph_rect_i.bottom as f32;
|
||||
|
||||
let first_index = vertices.len() as u16;
|
||||
|
||||
|
@ -603,36 +730,16 @@ impl Renderer {
|
|||
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,
|
||||
font: &Font,
|
||||
outlines: &Outlines,
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
glyph_indices: &[u16],
|
||||
glyph_mapping: &GlyphMapping,
|
||||
draw_time: f64,
|
||||
accum_time: f64,
|
||||
composite_time: f64,
|
||||
glyph_count: usize) {
|
||||
glyphs_drawn: u32) {
|
||||
// Draw the background color.
|
||||
unsafe {
|
||||
gl::BindVertexArray(self.solid_color_vertex_array);
|
||||
|
@ -665,34 +772,67 @@ impl Renderer {
|
|||
accum: {:.3}ms ({:.3}us/glyph), \
|
||||
composite: {:.3}ms ({:.3}us/glyph)",
|
||||
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 / (1000.0 * glyph_count as f64),
|
||||
accum_time / (1000.0 * glyphs_drawn as f64),
|
||||
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;
|
||||
for glyph_pos in &shaper::shape_text(&font, &glyph_mapping, &fps_text) {
|
||||
fps_glyphs.push(GlyphPos {
|
||||
fps_glyph_positions.push(GlyphPos {
|
||||
x: current_x,
|
||||
y: 0,
|
||||
glyph_id: glyph_pos.glyph_id,
|
||||
});
|
||||
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,
|
||||
outlines,
|
||||
&self.fps_composite_vertex_array,
|
||||
glyph_indices,
|
||||
&fps_glyph_positions,
|
||||
&fps_glyphs,
|
||||
atlas_origins,
|
||||
device_pixel_size,
|
||||
&Point2D::new(FPS_PADDING, device_pixel_size.height as i32 - FPS_PADDING),
|
||||
self.fps_gl_texture,
|
||||
FPS_DISPLAY_POINT_SIZE,
|
||||
&FPS_FOREGROUND_COLOR);
|
||||
&FPS_FOREGROUND_COLOR,
|
||||
false);
|
||||
}
|
||||
|
||||
fn take_screenshot(&self) {
|
||||
|
@ -729,14 +869,14 @@ impl Renderer {
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct Vertex {
|
||||
x: i32,
|
||||
y: i32,
|
||||
x: f32,
|
||||
y: f32,
|
||||
u: u32,
|
||||
v: u32,
|
||||
}
|
||||
|
||||
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 {
|
||||
x: x,
|
||||
y: y,
|
||||
|
@ -753,6 +893,14 @@ struct GlyphPos {
|
|||
glyph_id: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct CachedGlyph {
|
||||
x: f32,
|
||||
y: f32,
|
||||
glyph_index: u16,
|
||||
subpixel: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompositeVertexArray {
|
||||
vertex_array: GLuint,
|
||||
|
@ -806,6 +954,8 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Image, GL
|
|||
buffer::Protection::ReadWrite,
|
||||
&atlas_size).unwrap();
|
||||
|
||||
rasterizer.queue().submit_clear(&compute_image, &Color::UInt(0, 0, 0, 0), &[]).unwrap();
|
||||
|
||||
let mut gl_texture = 0;
|
||||
unsafe {
|
||||
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)
|
||||
}
|
||||
|
||||
struct RedrawResult {
|
||||
events: Option<DrawAtlasProfilingEvents>,
|
||||
glyphs_drawn: u32,
|
||||
}
|
||||
|
||||
static COMPOSITE_VERTEX_SHADER: &'static str = "\
|
||||
#version 330
|
||||
|
||||
|
|
|
@ -40,8 +40,6 @@ layout(std140) uniform ubImageDescriptors {
|
|||
in ivec2 aPosition;
|
||||
|
||||
// Which glyph the vertex belongs to.
|
||||
//
|
||||
// TODO(pcwalton): See if this is faster as a binary search on the vertex ID.
|
||||
in uint aGlyphIndex;
|
||||
|
||||
// The vertex ID, passed along onto the TCS.
|
||||
|
|
80
src/atlas.rs
80
src/atlas.rs
|
@ -11,7 +11,7 @@
|
|||
//! Atlases, which hold rendered glyphs on the GPU.
|
||||
|
||||
use error::GlError;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use euclid::Point2D;
|
||||
use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
use gl;
|
||||
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
|
||||
/// 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>, ()> {
|
||||
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()));
|
||||
|
||||
for batch_builder in &mut self.batch_builders {
|
||||
if let Ok(atlas_origin) = batch_builder.add_glyph(outlines,
|
||||
&atlas_origin,
|
||||
glyph_index,
|
||||
point_size) {
|
||||
point_size,
|
||||
horizontal_offset) {
|
||||
return Ok(atlas_origin)
|
||||
}
|
||||
}
|
||||
|
@ -79,13 +88,14 @@ impl AtlasBuilder {
|
|||
let atlas_origin = try!(batch_builder.add_glyph(outlines,
|
||||
&atlas_origin,
|
||||
glyph_index,
|
||||
point_size));
|
||||
point_size,
|
||||
horizontal_offset));
|
||||
self.batch_builders.push(batch_builder);
|
||||
Ok(atlas_origin)
|
||||
}
|
||||
|
||||
/// 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![];
|
||||
for batch_builder in self.batch_builders.into_iter() {
|
||||
batches.push(try!(batch_builder.create_batch()))
|
||||
|
@ -116,11 +126,14 @@ impl BatchBuilder {
|
|||
outlines: &Outlines,
|
||||
atlas_origin: &Point2D<u32>,
|
||||
glyph_index: u16,
|
||||
point_size: f32)
|
||||
point_size: f32,
|
||||
horizontal_offset: f32)
|
||||
-> Result<Point2D<f32>, ()> {
|
||||
// Check to see if we're already rendering this glyph.
|
||||
if let Some(image_descriptor) = self.image_descriptors.get(glyph_index as usize) {
|
||||
if image_descriptor.point_size == point_size {
|
||||
let image_index = glyph_index as usize;
|
||||
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.
|
||||
return Ok(Point2D::new(image_descriptor.atlas_x, image_descriptor.atlas_y))
|
||||
} else {
|
||||
|
@ -131,28 +144,29 @@ impl BatchBuilder {
|
|||
|
||||
let subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
||||
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 {
|
||||
self.image_descriptors.push(ImageDescriptor::default())
|
||||
while self.image_descriptors.len() < image_index + 1 {
|
||||
self.image_descriptors.push(ImageDescriptor::default());
|
||||
self.image_metadata.push(ImageMetadata::default());
|
||||
}
|
||||
|
||||
while self.image_metadata.len() < glyph_index as usize + 1 {
|
||||
self.image_metadata.push(ImageMetadata::default())
|
||||
}
|
||||
let units_per_em = outlines.glyph_units_per_em(glyph_index) as f32;
|
||||
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(),
|
||||
atlas_origin.y as f32 + 1.0 - subpixel_bounds.top.fract());
|
||||
self.image_descriptors[glyph_index as usize] = ImageDescriptor {
|
||||
let atlas_origin = Point2D::new(
|
||||
atlas_origin.x as f32 + subpixel_bounds.left.fract() + horizontal_px_offset,
|
||||
atlas_origin.y as f32 + 1.0 - subpixel_bounds.top.fract());
|
||||
self.image_descriptors[image_index] = ImageDescriptor {
|
||||
atlas_x: atlas_origin.x,
|
||||
atlas_y: atlas_origin.y,
|
||||
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_id: glyph_id,
|
||||
horizontal_offset: horizontal_offset,
|
||||
start_index: outlines.descriptor(glyph_index).unwrap().start_index(),
|
||||
end_index: match outlines.descriptor(glyph_index + 1) {
|
||||
Some(descriptor) => descriptor.start_index() as u32,
|
||||
|
@ -164,14 +178,13 @@ impl BatchBuilder {
|
|||
}
|
||||
|
||||
/// Uploads this batch data to the GPU.
|
||||
fn create_batch(mut self) -> Result<Batch, GlError> {
|
||||
self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index));
|
||||
|
||||
fn create_batch(self) -> Result<Batch, GlError> {
|
||||
let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
|
||||
for image_metadata in &self.image_metadata {
|
||||
let glyph_index = image_metadata.glyph_index;
|
||||
let start_index = image_metadata.start_index;
|
||||
let end_index = image_metadata.end_index;
|
||||
let (start_index, end_index) = (image_metadata.start_index, image_metadata.end_index);
|
||||
if start_index == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
match current_range {
|
||||
Some((current_first, current_last)) if start_index == current_last => {
|
||||
|
@ -179,7 +192,7 @@ impl BatchBuilder {
|
|||
}
|
||||
Some((current_first, current_last)) => {
|
||||
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))
|
||||
}
|
||||
None => current_range = Some((start_index, end_index)),
|
||||
|
@ -187,7 +200,7 @@ impl BatchBuilder {
|
|||
}
|
||||
if let Some((current_first, current_last)) = current_range {
|
||||
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.
|
||||
|
@ -235,6 +248,12 @@ impl Atlas {
|
|||
pub fn shelf_columns(&self) -> u32 {
|
||||
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 {
|
||||
|
@ -274,7 +293,7 @@ pub struct ImageDescriptor {
|
|||
atlas_x: f32,
|
||||
atlas_y: f32,
|
||||
point_size: f32,
|
||||
glyph_index: f32,
|
||||
pad: f32,
|
||||
}
|
||||
|
||||
// Information about each image that we keep around ourselves.
|
||||
|
@ -282,8 +301,9 @@ pub struct ImageDescriptor {
|
|||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct ImageMetadata {
|
||||
glyph_index: u32,
|
||||
glyph_id: u16,
|
||||
start_index: u32,
|
||||
end_index: u32,
|
||||
horizontal_offset: f32,
|
||||
glyph_id: u16,
|
||||
}
|
||||
|
||||
|
|
|
@ -116,17 +116,6 @@ pub struct GlyphMapping {
|
|||
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 {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
|
|
|
@ -51,6 +51,8 @@ pub enum InitError {
|
|||
/// A rasterization error. This could be an OpenGL error or a compute error.
|
||||
#[derive(Debug)]
|
||||
pub enum RasterError {
|
||||
/// No glyphs were supplied.
|
||||
NoGlyphsToDraw,
|
||||
/// An OpenGL error occurred.
|
||||
GlError(GlError),
|
||||
/// An error occurred during GPU compute.
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use euclid::Point2D;
|
||||
use otf::glyf::{Point, PointKind};
|
||||
use otf::head::HeadTable;
|
||||
use otf::{Error, FontTable};
|
||||
use outline::GlyphBounds;
|
||||
use std::cmp;
|
||||
|
@ -107,7 +106,7 @@ impl<'a> CffTable<'a> {
|
|||
|
||||
4 => {
|
||||
// |- 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;
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
|
@ -295,9 +294,7 @@ impl<'a> CffTable<'a> {
|
|||
start = 1
|
||||
}
|
||||
|
||||
for (i, chunk) in stack.array[start..stack.size as usize]
|
||||
.chunks(4)
|
||||
.enumerate() {
|
||||
for chunk in stack.array[start..stack.size as usize].chunks(4) {
|
||||
add_curve(0, chunk[0] as i16,
|
||||
chunk[1] as i16, chunk[2] as i16,
|
||||
0, chunk[3] as i16,
|
||||
|
@ -317,9 +314,7 @@ impl<'a> CffTable<'a> {
|
|||
start = 1
|
||||
}
|
||||
|
||||
for (i, chunk) in stack.array[start..stack.size as usize]
|
||||
.chunks(4)
|
||||
.enumerate() {
|
||||
for chunk in stack.array[start..stack.size as usize].chunks(4) {
|
||||
add_curve(chunk[0] as i16, 0,
|
||||
chunk[1] as i16, chunk[2] as i16,
|
||||
chunk[3] as i16, 0,
|
||||
|
@ -364,7 +359,7 @@ impl<'a> CffTable<'a> {
|
|||
}
|
||||
21 => {
|
||||
// |- 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);
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
|
@ -377,7 +372,7 @@ impl<'a> CffTable<'a> {
|
|||
}
|
||||
22 => {
|
||||
// |- 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;
|
||||
callback(&Point {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -411,12 +406,12 @@ impl<'a> CffTable<'a> {
|
|||
// TODO(pcwalton): Compute this at the same time as `for_each_point`, perhaps?
|
||||
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> {
|
||||
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.bottom = cmp::min(bounds.bottom, point.position.y as i32);
|
||||
bounds.right = cmp::max(bounds.right, point.position.x as i32);
|
||||
bounds.top = cmp::max(bounds.top, point.position.y as i32);
|
||||
});
|
||||
}));
|
||||
Ok(bounds)
|
||||
}
|
||||
}
|
||||
|
@ -547,10 +542,7 @@ impl EvaluationStack {
|
|||
}
|
||||
}
|
||||
|
||||
fn close_path_if_necessary<F>(pos: &mut Point2D<i16>,
|
||||
start: &Point2D<i16>,
|
||||
index_in_contour: u16,
|
||||
mut callback: F)
|
||||
fn close_path_if_necessary<F>(start: &Point2D<i16>, index_in_contour: u16, mut callback: F)
|
||||
where F: FnMut(&Point) {
|
||||
if index_in_contour == 0 {
|
||||
// No path to close.
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange};
|
||||
use otf::{Error, FontTable};
|
||||
use std::char;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::u16;
|
||||
|
|
|
@ -90,7 +90,7 @@ impl<'a> GlyfTable<'a> {
|
|||
head_table: &HeadTable,
|
||||
loca_table: &LocaTable,
|
||||
glyph_id: u16,
|
||||
mut callback: F)
|
||||
callback: F)
|
||||
-> Result<(), Error> where F: FnMut(&Point) {
|
||||
let mut reader = self.table.bytes;
|
||||
|
||||
|
@ -154,7 +154,7 @@ impl<'a> GlyfTable<'a> {
|
|||
let mut last_point_was_off_curve = false;
|
||||
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);
|
||||
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)) {
|
||||
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| {
|
||||
try!(self.for_each_point_in_simple_glyph(reader, |point| {
|
||||
callback(&transform.transform(&point))
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
if !flags.contains(MORE_COMPONENTS) {
|
||||
|
|
|
@ -24,7 +24,6 @@ bitflags! {
|
|||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KernTable<'a> {
|
||||
table: FontTable<'a>,
|
||||
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 mut horizontal_table = None;
|
||||
for table_index in 0..n_tables {
|
||||
for _ in 0..n_tables {
|
||||
let mut table_reader = kern_reader;
|
||||
let _version = 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 {
|
||||
Some(horizontal_table) => {
|
||||
Ok(KernTable {
|
||||
table: table,
|
||||
horizontal_table: horizontal_table,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -138,9 +138,9 @@ impl<'a> Font<'a> {
|
|||
///
|
||||
/// 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> {
|
||||
// Check magic number.
|
||||
// Check the magic number.
|
||||
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 {
|
||||
TTCF => {
|
||||
// This is a font collection. Read the first font.
|
||||
|
@ -171,10 +171,8 @@ impl<'a> Font<'a> {
|
|||
let mut reader = bytes;
|
||||
try!(reader.jump(offset as usize).map_err(Error::eof));
|
||||
|
||||
let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
// Check version.
|
||||
if !SFNT_VERSIONS.contains(&magic_number) {
|
||||
// Check the magic number.
|
||||
if !SFNT_VERSIONS.contains(&try!(reader.read_u32::<BigEndian>().map_err(Error::eof))) {
|
||||
return Err(Error::UnknownFormat)
|
||||
}
|
||||
|
||||
|
@ -252,8 +250,8 @@ impl<'a> Font<'a> {
|
|||
// Read the Mac resource file header.
|
||||
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_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_data_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.
|
||||
reader = bytes;
|
||||
|
@ -262,7 +260,7 @@ impl<'a> Font<'a> {
|
|||
|
||||
// Read the type list and name list offsets.
|
||||
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.
|
||||
reader = bytes;
|
||||
|
@ -272,7 +270,7 @@ impl<'a> Font<'a> {
|
|||
// Find the 'sfnt' type.
|
||||
let type_count = (try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) + 1) as usize;
|
||||
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 resource_count = 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.
|
||||
try!(reader.jump(index as usize * (mem::size_of::<u16>() * 2 + mem::size_of::<u32>() * 2))
|
||||
.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_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_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) &
|
||||
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.
|
||||
reader = bytes;
|
||||
|
|
|
@ -191,17 +191,17 @@ impl Outlines {
|
|||
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.
|
||||
#[inline]
|
||||
pub fn glyph_id(&self, glyph_index: u16) -> u16 {
|
||||
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)]
|
||||
|
|
|
@ -237,8 +237,16 @@ impl Rasterizer {
|
|||
outlines: &Outlines,
|
||||
coverage_buffer: &CoverageBuffer)
|
||||
-> Result<DrawAtlasProfilingEvents, RasterError> {
|
||||
if atlas.is_empty() {
|
||||
return Err(RasterError::NoGlyphsToDraw)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
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);
|
||||
|
||||
// TODO(pcwalton): Scissor to the image rect to clear faster?
|
||||
|
@ -302,7 +310,9 @@ impl Rasterizer {
|
|||
gl::Disable(gl::CULL_FACE);
|
||||
gl::Disable(gl::BLEND);
|
||||
|
||||
// Restore our old framebuffer and viewport.
|
||||
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
|
||||
// OpenCL, but I don't know how to do that portably (i.e. on Mac…) Just using
|
||||
|
|
Loading…
Reference in New Issue