parent
e2014cff13
commit
49408b95cb
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
80
src/atlas.rs
80
src/atlas.rs
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue