Allow the same glyph at different sizes to be rendered into the same atlas.
This commit is contained in:
parent
d306ef01d1
commit
c4c19076c7
|
@ -83,7 +83,7 @@ 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).unwrap();
|
||||
}
|
||||
atlas = atlas_builder.create_atlas().unwrap();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
|||
use glfw::{Action, Context, Key, OpenGlProfileHint, SwapInterval, WindowEvent};
|
||||
use glfw::{WindowHint, WindowMode};
|
||||
use memmap::{Mmap, Protection};
|
||||
use pathfinder::atlas::{Atlas, AtlasBuilder};
|
||||
use pathfinder::atlas::AtlasBuilder;
|
||||
use pathfinder::charmap::{CodepointRanges, GlyphMapping};
|
||||
use pathfinder::coverage::CoverageBuffer;
|
||||
use pathfinder::otf::Font;
|
||||
|
@ -161,7 +161,7 @@ fn main() {
|
|||
}
|
||||
|
||||
let outlines = outline_builder.create_buffers().unwrap();
|
||||
let fps_atlas = renderer.create_fps_atlas(&font, &outlines, glyph_count);
|
||||
let fps_atlas_origins = renderer.create_fps_atlas(&font, &outlines, glyph_count);
|
||||
|
||||
while !window.should_close() {
|
||||
if dirty {
|
||||
|
@ -185,9 +185,9 @@ fn main() {
|
|||
let timing = renderer.get_timing_in_ms();
|
||||
|
||||
renderer.draw_fps(&font,
|
||||
&fps_atlas,
|
||||
&outlines,
|
||||
&device_pixel_size,
|
||||
&fps_atlas_origins,
|
||||
&glyph_indices,
|
||||
&glyph_mapping,
|
||||
draw_time,
|
||||
|
@ -438,9 +438,9 @@ impl Renderer {
|
|||
-> DrawAtlasProfilingEvents {
|
||||
let shelf_height = font.shelf_height(point_size);
|
||||
let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
|
||||
for glyph_index in 0..(glyph_count as u16) {
|
||||
let atlas_origins: Vec<_> = (0..(glyph_count as u16)).map(|glyph_index| {
|
||||
atlas_builder.pack_glyph(&outlines, glyph_index, point_size).unwrap()
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let atlas = atlas_builder.create_atlas().unwrap();
|
||||
|
||||
|
@ -468,11 +468,11 @@ impl Renderer {
|
|||
}
|
||||
|
||||
self.draw_glyphs(&font,
|
||||
&atlas,
|
||||
outlines,
|
||||
&self.main_composite_vertex_array,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
&atlas_origins,
|
||||
device_pixel_size,
|
||||
translation,
|
||||
self.main_gl_texture,
|
||||
|
@ -492,11 +492,11 @@ impl Renderer {
|
|||
|
||||
fn draw_glyphs(&self,
|
||||
font: &Font,
|
||||
atlas: &Atlas,
|
||||
outlines: &Outlines,
|
||||
vertex_array: &CompositeVertexArray,
|
||||
glyph_indices: &[u16],
|
||||
glyph_positions: &[GlyphPos],
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
translation: &Point2D<i32>,
|
||||
texture: GLuint,
|
||||
|
@ -509,10 +509,10 @@ impl Renderer {
|
|||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vertex_array.index_buffer);
|
||||
|
||||
let vertex_count = self.upload_quads_for_text(font,
|
||||
atlas,
|
||||
outlines,
|
||||
glyph_indices,
|
||||
glyph_positions,
|
||||
atlas_origins,
|
||||
point_size);
|
||||
|
||||
gl::ActiveTexture(gl::TEXTURE0);
|
||||
|
@ -548,10 +548,10 @@ impl Renderer {
|
|||
|
||||
fn upload_quads_for_text(&self,
|
||||
font: &Font,
|
||||
atlas: &Atlas,
|
||||
outlines: &Outlines,
|
||||
glyph_indices: &[u16],
|
||||
glyph_positions: &[GlyphPos],
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
point_size: f32)
|
||||
-> usize {
|
||||
let pixels_per_unit = point_size as f32 / font.units_per_em() as f32;
|
||||
|
@ -561,7 +561,7 @@ impl Renderer {
|
|||
let glyph_index = glyph_indices[position.glyph_id as usize];
|
||||
let glyph_rect_i = outlines.glyph_pixel_bounds(glyph_index, point_size);
|
||||
|
||||
let uv_tl: Point2D<u32> = atlas.atlas_origin(glyph_index).floor().cast().unwrap();
|
||||
let uv_tl: Point2D<u32> = atlas_origins[glyph_index as usize].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;
|
||||
|
@ -596,12 +596,13 @@ impl Renderer {
|
|||
indices.len()
|
||||
}
|
||||
|
||||
fn create_fps_atlas(&self, font: &Font, outlines: &Outlines, glyph_count: usize) -> Atlas {
|
||||
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);
|
||||
for glyph_index in 0..(glyph_count as u16) {
|
||||
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();
|
||||
|
||||
|
@ -611,14 +612,14 @@ impl Renderer {
|
|||
outlines,
|
||||
&self.fps_coverage_buffer).unwrap();
|
||||
|
||||
atlas
|
||||
atlas_origins
|
||||
}
|
||||
|
||||
fn draw_fps(&self,
|
||||
font: &Font,
|
||||
atlas: &Atlas,
|
||||
outlines: &Outlines,
|
||||
device_pixel_size: &Size2D<u32>,
|
||||
atlas_origins: &[Point2D<f32>],
|
||||
glyph_indices: &[u16],
|
||||
glyph_mapping: &GlyphMapping,
|
||||
draw_time: f64,
|
||||
|
@ -675,11 +676,11 @@ impl Renderer {
|
|||
}
|
||||
|
||||
self.draw_glyphs(font,
|
||||
atlas,
|
||||
outlines,
|
||||
&self.fps_composite_vertex_array,
|
||||
glyph_indices,
|
||||
&fps_glyphs,
|
||||
atlas_origins,
|
||||
device_pixel_size,
|
||||
&Point2D::new(FPS_PADDING, device_pixel_size.height as i32 - FPS_PADDING),
|
||||
self.fps_gl_texture,
|
||||
|
|
172
src/atlas.rs
172
src/atlas.rs
|
@ -32,8 +32,7 @@ use std::u16;
|
|||
/// the screen.
|
||||
pub struct AtlasBuilder {
|
||||
rect_packer: RectPacker,
|
||||
image_descriptors: Vec<ImageDescriptor>,
|
||||
image_metadata: Vec<ImageMetadata>,
|
||||
batch_builders: Vec<BatchBuilder>,
|
||||
}
|
||||
|
||||
impl AtlasBuilder {
|
||||
|
@ -50,8 +49,7 @@ impl AtlasBuilder {
|
|||
pub fn new(available_width: u32, shelf_height: u32) -> AtlasBuilder {
|
||||
AtlasBuilder {
|
||||
rect_packer: RectPacker::new(available_width, shelf_height),
|
||||
image_descriptors: vec![],
|
||||
image_metadata: vec![],
|
||||
batch_builders: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,18 +59,77 @@ impl AtlasBuilder {
|
|||
/// separate from IDs; the indices are returned from each call to
|
||||
/// `OutlineBuilder::add_glyph()`.
|
||||
///
|
||||
/// Returns an error if there is no space left for the glyph.
|
||||
///
|
||||
/// TODO(pcwalton): Support multiple outline buffers in the same atlas.
|
||||
///
|
||||
/// TODO(pcwalton): Support the same glyph drawn at multiple point sizes.
|
||||
/// 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)
|
||||
-> Result<(), ()> {
|
||||
let subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, point_size);
|
||||
-> Result<Point2D<f32>, ()> {
|
||||
let pixel_bounds = outlines.glyph_pixel_bounds(glyph_index, point_size);
|
||||
|
||||
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) {
|
||||
return Ok(atlas_origin)
|
||||
}
|
||||
}
|
||||
|
||||
let mut batch_builder = BatchBuilder::new();
|
||||
let atlas_origin = try!(batch_builder.add_glyph(outlines,
|
||||
&atlas_origin,
|
||||
glyph_index,
|
||||
point_size));
|
||||
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> {
|
||||
let mut batches = vec![];
|
||||
for batch_builder in self.batch_builders.into_iter() {
|
||||
batches.push(try!(batch_builder.create_batch()))
|
||||
}
|
||||
|
||||
Ok(Atlas {
|
||||
batches: batches,
|
||||
shelf_height: self.rect_packer.shelf_height(),
|
||||
shelf_columns: self.rect_packer.shelf_columns(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct BatchBuilder {
|
||||
image_descriptors: Vec<ImageDescriptor>,
|
||||
image_metadata: Vec<ImageMetadata>,
|
||||
}
|
||||
|
||||
impl BatchBuilder {
|
||||
fn new() -> BatchBuilder {
|
||||
BatchBuilder {
|
||||
image_descriptors: vec![],
|
||||
image_metadata: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn add_glyph(&mut self,
|
||||
outlines: &Outlines,
|
||||
atlas_origin: &Point2D<u32>,
|
||||
glyph_index: u16,
|
||||
point_size: 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 {
|
||||
// Glyph is already present.
|
||||
return Ok(Point2D::new(image_descriptor.atlas_x, image_descriptor.atlas_y))
|
||||
} else {
|
||||
// Glyph is present at a different font size. We need a new batch.
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -80,17 +137,19 @@ impl AtlasBuilder {
|
|||
self.image_descriptors.push(ImageDescriptor::default())
|
||||
}
|
||||
|
||||
self.image_descriptors[glyph_index as usize] = ImageDescriptor {
|
||||
atlas_x: atlas_origin.x as f32 + subpixel_bounds.left.fract(),
|
||||
atlas_y: atlas_origin.y as f32 + (1.0 - subpixel_bounds.top.fract()),
|
||||
point_size: point_size,
|
||||
glyph_index: glyph_index as f32,
|
||||
};
|
||||
|
||||
while self.image_metadata.len() < glyph_index as usize + 1 {
|
||||
self.image_metadata.push(ImageMetadata::default())
|
||||
}
|
||||
|
||||
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 {
|
||||
atlas_x: atlas_origin.x,
|
||||
atlas_y: atlas_origin.y,
|
||||
point_size: point_size,
|
||||
glyph_index: glyph_index as f32,
|
||||
};
|
||||
|
||||
self.image_metadata[glyph_index as usize] = ImageMetadata {
|
||||
glyph_index: glyph_index as u32,
|
||||
glyph_id: glyph_id,
|
||||
|
@ -101,11 +160,11 @@ impl AtlasBuilder {
|
|||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(atlas_origin)
|
||||
}
|
||||
|
||||
/// Creates an atlas by uploading the atlas info to the GPU.
|
||||
pub fn create_atlas(mut self) -> Result<Atlas, GlError> {
|
||||
/// 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));
|
||||
|
||||
let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
|
||||
|
@ -141,15 +200,10 @@ impl AtlasBuilder {
|
|||
gl::BindBuffer(gl::UNIFORM_BUFFER, images);
|
||||
gl::BufferData(gl::UNIFORM_BUFFER, length as GLsizeiptr, ptr, gl::DYNAMIC_DRAW);
|
||||
|
||||
Ok(Atlas {
|
||||
Ok(Batch {
|
||||
images_buffer: images,
|
||||
images: self.image_descriptors,
|
||||
|
||||
start_indices: start_indices,
|
||||
counts: counts,
|
||||
|
||||
shelf_height: self.rect_packer.shelf_height(),
|
||||
shelf_columns: self.rect_packer.shelf_columns(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -157,33 +211,17 @@ impl AtlasBuilder {
|
|||
|
||||
/// An atlas holding rendered glyphs on the GPU.
|
||||
pub struct Atlas {
|
||||
images_buffer: GLuint,
|
||||
images: Vec<ImageDescriptor>,
|
||||
|
||||
start_indices: Vec<usize>,
|
||||
counts: Vec<GLsizei>,
|
||||
|
||||
batches: Vec<Batch>,
|
||||
shelf_height: u32,
|
||||
shelf_columns: u32,
|
||||
}
|
||||
|
||||
impl Drop for Atlas {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteBuffers(1, &mut self.images_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Atlas {
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn draw(&self, primitive: GLenum) {
|
||||
debug_assert!(self.counts.len() == self.start_indices.len());
|
||||
gl::MultiDrawElements(primitive,
|
||||
self.counts.as_ptr(),
|
||||
gl::UNSIGNED_INT,
|
||||
self.start_indices.as_ptr() as *const *const GLvoid,
|
||||
self.counts.len() as GLsizei);
|
||||
for batch in &self.batches {
|
||||
batch.draw(primitive)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the height of each shelf.
|
||||
|
@ -197,20 +235,34 @@ impl Atlas {
|
|||
pub fn shelf_columns(&self) -> u32 {
|
||||
self.shelf_columns
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn images_buffer(&self) -> GLuint {
|
||||
self.images_buffer
|
||||
}
|
||||
|
||||
/// Returns the origin of the glyph with the given index in the atlas.
|
||||
///
|
||||
/// This is the subpixel origin.
|
||||
#[inline]
|
||||
pub fn atlas_origin(&self, glyph_index: u16) -> Point2D<f32> {
|
||||
let image = &self.images[glyph_index as usize];
|
||||
Point2D::new(image.atlas_x, image.atlas_y)
|
||||
struct Batch {
|
||||
images_buffer: GLuint,
|
||||
start_indices: Vec<usize>,
|
||||
counts: Vec<GLsizei>,
|
||||
}
|
||||
|
||||
impl Drop for Batch {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteBuffers(1, &mut self.images_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Batch {
|
||||
unsafe fn draw(&self, primitive: GLenum) {
|
||||
debug_assert!(self.counts.len() == self.start_indices.len());
|
||||
|
||||
// The image descriptors are bound to binding point 2. See `draw.vs.glsl`.
|
||||
gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, self.images_buffer);
|
||||
|
||||
gl::MultiDrawElements(primitive,
|
||||
self.counts.as_ptr(),
|
||||
gl::UNSIGNED_INT,
|
||||
self.start_indices.as_ptr() as *const *const GLvoid,
|
||||
self.counts.len() as GLsizei);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -216,8 +216,9 @@ impl Rasterizer {
|
|||
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, outlines.indices_buffer());
|
||||
|
||||
// Don't bind the atlas uniform buffers (binding point 2) here; the batches will do
|
||||
// that on their own.
|
||||
gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, outlines.descriptors_buffer());
|
||||
gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, atlas.images_buffer());
|
||||
gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1);
|
||||
gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2);
|
||||
|
||||
|
|
Loading…
Reference in New Issue