Rename "batch" to "atlas" and "atlas" to "rect packer"
This commit is contained in:
parent
86fec9ff54
commit
d5be9e1f16
|
@ -19,7 +19,7 @@ use euclid::{Point2D, Rect, Size2D};
|
||||||
use gl::types::GLuint;
|
use gl::types::GLuint;
|
||||||
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
|
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use pathfinder::batch::BatchBuilder;
|
use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
||||||
|
@ -63,11 +63,12 @@ fn main() {
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
let start = time::precise_time_ns();
|
let start = time::precise_time_ns();
|
||||||
let mut last_time = start;
|
let mut last_time = start;
|
||||||
let (mut glyph_buffer_builder, mut batch_builder, mut glyph_count);
|
let (mut glyph_buffer_builder, mut glyph_buffers, mut glyph_count);
|
||||||
let (mut glyph_buffers, mut batch);
|
let (mut atlas_builder, mut atlas);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
glyph_buffer_builder = GlyphBufferBuilder::new();
|
glyph_buffer_builder = GlyphBufferBuilder::new();
|
||||||
batch_builder = BatchBuilder::new(device_pixel_width as GLuint, shelf_height);
|
atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height);
|
||||||
glyph_count = 0;
|
glyph_count = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
let font = Font::new(file.as_slice()).unwrap();
|
let font = Font::new(file.as_slice()).unwrap();
|
||||||
|
@ -77,17 +78,16 @@ fn main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
||||||
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
||||||
batch_builder.add_glyph(&glyph_buffer_builder,
|
atlas_builder.pack_glyph(&glyph_buffer_builder,
|
||||||
glyph_index as u32,
|
glyph_index as u32,
|
||||||
point_size as f32)
|
point_size as f32).unwrap();
|
||||||
.unwrap();
|
|
||||||
glyph_count += 1
|
glyph_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
||||||
batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
|
atlas = atlas_builder.create_atlas(&glyph_buffer_builder).unwrap();
|
||||||
|
|
||||||
let end = time::precise_time_ns();
|
let end = time::precise_time_ns();
|
||||||
results.push((end - last_time) as f64);
|
results.push((end - last_time) as f64);
|
||||||
|
@ -108,15 +108,14 @@ fn main() {
|
||||||
.create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size)
|
.create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let rect = Rect::new(Point2D::new(0, 0), atlas_size);
|
||||||
|
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
let start_time = time::precise_time_ns();
|
let start_time = time::precise_time_ns();
|
||||||
loop {
|
loop {
|
||||||
let events = rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), atlas_size),
|
let events =
|
||||||
&batch_builder.atlas,
|
rasterizer.draw_atlas(&image, &rect, &atlas, &glyph_buffers, &coverage_buffer)
|
||||||
&glyph_buffers,
|
.unwrap();
|
||||||
&batch,
|
|
||||||
&coverage_buffer,
|
|
||||||
&image).unwrap();
|
|
||||||
|
|
||||||
let mut draw_time = 0u64;
|
let mut draw_time = 0u64;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn main() {
|
||||||
let mut last_point: Option<Point2D<i16>> = None;
|
let mut last_point: Option<Point2D<i16>> = None;
|
||||||
let mut last_point_was_off_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.first_point_in_contour {
|
if point.index_in_contour == 0 {
|
||||||
println!("M {},{}", point.position.x, point.position.y);
|
println!("M {},{}", point.position.x, point.position.y);
|
||||||
} else {
|
} else {
|
||||||
let last = last_point.unwrap();
|
let last = last_point.unwrap();
|
||||||
|
|
|
@ -16,7 +16,7 @@ use euclid::{Point2D, Rect, Size2D};
|
||||||
use gl::types::{GLint, GLuint};
|
use gl::types::{GLint, GLuint};
|
||||||
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use pathfinder::batch::BatchBuilder;
|
use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
use pathfinder::glyph_buffer::GlyphBufferBuilder;
|
||||||
|
@ -59,7 +59,7 @@ fn main() {
|
||||||
let shelf_height = (point_size * 2.0).ceil() as u32;
|
let shelf_height = (point_size * 2.0).ceil() as u32;
|
||||||
|
|
||||||
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
|
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
|
||||||
let mut batch_builder = BatchBuilder::new(device_pixel_width as GLuint, shelf_height);
|
let mut atlas_builder = AtlasBuilder::new(device_pixel_width as GLuint, shelf_height);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let font = Font::new(file.as_slice()).unwrap();
|
let font = Font::new(file.as_slice()).unwrap();
|
||||||
|
@ -68,12 +68,13 @@ fn main() {
|
||||||
let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
|
||||||
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
for (glyph_index, glyph_id) in glyph_ranges.iter().enumerate() {
|
||||||
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
glyph_buffer_builder.add_glyph(&font, glyph_id).unwrap();
|
||||||
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, point_size).unwrap()
|
atlas_builder.pack_glyph(&glyph_buffer_builder, glyph_index as u32, point_size)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
let glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
||||||
let batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
|
let atlas = atlas_builder.create_atlas(&glyph_buffer_builder).unwrap();
|
||||||
|
|
||||||
let atlas_size = Size2D::new(device_pixel_width as GLuint, device_pixel_height as GLuint);
|
let atlas_size = Size2D::new(device_pixel_width as GLuint, device_pixel_height as GLuint);
|
||||||
let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap();
|
let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap();
|
||||||
|
@ -82,12 +83,9 @@ fn main() {
|
||||||
.create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size)
|
.create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), atlas_size),
|
let rect = Rect::new(Point2D::new(0, 0), atlas_size);
|
||||||
&batch_builder.atlas,
|
|
||||||
&glyph_buffers,
|
rasterizer.draw_atlas(&image, &rect, &atlas, &glyph_buffers, &coverage_buffer).unwrap();
|
||||||
&batch,
|
|
||||||
&coverage_buffer,
|
|
||||||
&image).unwrap();
|
|
||||||
rasterizer.queue.flush().unwrap();
|
rasterizer.queue.flush().unwrap();
|
||||||
|
|
||||||
let draw_context = lord_drawquaad::Context::new();
|
let draw_context = lord_drawquaad::Context::new();
|
||||||
|
|
|
@ -19,7 +19,7 @@ use euclid::{Point2D, Rect, Size2D};
|
||||||
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||||
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use pathfinder::batch::BatchBuilder;
|
use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
use pathfinder::glyph_buffer::{GlyphBufferBuilder, GlyphBuffers};
|
use pathfinder::glyph_buffer::{GlyphBufferBuilder, GlyphBuffers};
|
||||||
|
@ -115,7 +115,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
let glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
let glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
|
||||||
|
|
||||||
let mut fps_batch = renderer.create_fps_batch(&glyph_buffer_builder,
|
let mut fps_atlas = renderer.create_fps_atlas(&glyph_buffer_builder,
|
||||||
&glyph_buffers,
|
&glyph_buffers,
|
||||||
glyph_count);
|
glyph_count);
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ fn main() {
|
||||||
let timing = renderer.get_timing_in_ms();
|
let timing = renderer.get_timing_in_ms();
|
||||||
|
|
||||||
renderer.draw_fps(&font,
|
renderer.draw_fps(&font,
|
||||||
&mut fps_batch,
|
&mut fps_atlas,
|
||||||
&glyph_buffer_builder,
|
&glyph_buffer_builder,
|
||||||
&device_pixel_size,
|
&device_pixel_size,
|
||||||
&glyph_ranges,
|
&glyph_ranges,
|
||||||
|
@ -384,18 +384,20 @@ impl Renderer {
|
||||||
// FIXME(pcwalton)
|
// FIXME(pcwalton)
|
||||||
let shelf_height = (point_size * 2.0).ceil() as u32;
|
let shelf_height = (point_size * 2.0).ceil() as u32;
|
||||||
|
|
||||||
let mut batch_builder = BatchBuilder::new(ATLAS_SIZE, shelf_height);
|
let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
|
||||||
for glyph_index in 0..(glyph_count as u32) {
|
for glyph_index in 0..(glyph_count as u32) {
|
||||||
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index, point_size).unwrap()
|
atlas_builder.pack_glyph(&glyph_buffer_builder, glyph_index, point_size).unwrap()
|
||||||
}
|
}
|
||||||
let batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
|
|
||||||
|
|
||||||
let events = self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
|
let atlas = atlas_builder.create_atlas(&glyph_buffer_builder).unwrap();
|
||||||
&batch_builder.atlas,
|
|
||||||
|
let rect = Rect::new(Point2D::new(0, 0), self.atlas_size);
|
||||||
|
|
||||||
|
let events = self.rasterizer.draw_atlas(&self.main_compute_image,
|
||||||
|
&rect,
|
||||||
|
&atlas,
|
||||||
glyph_buffers,
|
glyph_buffers,
|
||||||
&batch,
|
&self.coverage_buffer).unwrap();
|
||||||
&self.coverage_buffer,
|
|
||||||
&self.main_compute_image).unwrap();
|
|
||||||
self.rasterizer.queue.flush().unwrap();
|
self.rasterizer.queue.flush().unwrap();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -407,7 +409,7 @@ impl Renderer {
|
||||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.draw_glyphs(&mut batch_builder,
|
self.draw_glyphs(&mut atlas_builder,
|
||||||
glyph_buffer_builder,
|
glyph_buffer_builder,
|
||||||
glyph_positions,
|
glyph_positions,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
|
@ -428,7 +430,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_glyphs(&self,
|
fn draw_glyphs(&self,
|
||||||
batch_builder: &mut BatchBuilder,
|
atlas_builder: &mut AtlasBuilder,
|
||||||
glyph_buffer_builder: &GlyphBufferBuilder,
|
glyph_buffer_builder: &GlyphBufferBuilder,
|
||||||
glyph_positions: &[GlyphPos],
|
glyph_positions: &[GlyphPos],
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
|
@ -442,7 +444,7 @@ impl Renderer {
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.composite_vertex_buffer);
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.composite_vertex_buffer);
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.composite_index_buffer);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.composite_index_buffer);
|
||||||
|
|
||||||
let vertex_count = self.upload_quads_for_text(batch_builder,
|
let vertex_count = self.upload_quads_for_text(atlas_builder,
|
||||||
glyph_buffer_builder,
|
glyph_buffer_builder,
|
||||||
glyph_positions,
|
glyph_positions,
|
||||||
point_size);
|
point_size);
|
||||||
|
@ -479,7 +481,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_quads_for_text(&self,
|
fn upload_quads_for_text(&self,
|
||||||
batch_builder: &mut BatchBuilder,
|
atlas_builder: &mut AtlasBuilder,
|
||||||
glyph_buffer_builder: &GlyphBufferBuilder,
|
glyph_buffer_builder: &GlyphBufferBuilder,
|
||||||
glyph_positions: &[GlyphPos],
|
glyph_positions: &[GlyphPos],
|
||||||
point_size: f32)
|
point_size: f32)
|
||||||
|
@ -488,12 +490,12 @@ impl Renderer {
|
||||||
|
|
||||||
let (mut vertices, mut indices) = (vec![], vec![]);
|
let (mut vertices, mut indices) = (vec![], vec![]);
|
||||||
for position in glyph_positions {
|
for position in glyph_positions {
|
||||||
let glyph_index = match batch_builder.glyph_index_for(position.glyph_id) {
|
let glyph_index = match atlas_builder.glyph_index_for(position.glyph_id) {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(glyph_index) => glyph_index,
|
Some(glyph_index) => glyph_index,
|
||||||
};
|
};
|
||||||
let glyph_bounds = glyph_buffer_builder.glyph_bounds(glyph_index);
|
let glyph_bounds = glyph_buffer_builder.glyph_bounds(glyph_index);
|
||||||
let uv_rect = batch_builder.atlas_rect(glyph_index);
|
let uv_rect = atlas_builder.atlas_rect(glyph_index);
|
||||||
let (uv_bl, uv_tr) = (uv_rect.origin, uv_rect.bottom_right());
|
let (uv_bl, uv_tr) = (uv_rect.origin, uv_rect.bottom_right());
|
||||||
|
|
||||||
let left_pos = (position.x as f32 * pixels_per_unit).round() as i32;
|
let left_pos = (position.x as f32 * pixels_per_unit).round() as i32;
|
||||||
|
@ -526,35 +528,36 @@ impl Renderer {
|
||||||
indices.len()
|
indices.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fps_batch(&self,
|
fn create_fps_atlas(&self,
|
||||||
glyph_buffer_builder: &GlyphBufferBuilder,
|
glyph_buffer_builder: &GlyphBufferBuilder,
|
||||||
glyph_buffers: &GlyphBuffers,
|
glyph_buffers: &GlyphBuffers,
|
||||||
glyph_count: usize)
|
glyph_count: usize)
|
||||||
-> BatchBuilder {
|
-> AtlasBuilder {
|
||||||
// FIXME(pcwalton)
|
// FIXME(pcwalton)
|
||||||
let shelf_height = (FPS_DISPLAY_POINT_SIZE * 2.0).ceil() as u32;
|
let shelf_height = (FPS_DISPLAY_POINT_SIZE * 2.0).ceil() as u32;
|
||||||
|
|
||||||
let mut batch_builder = BatchBuilder::new(ATLAS_SIZE, shelf_height);
|
let mut atlas_builder = AtlasBuilder::new(ATLAS_SIZE, shelf_height);
|
||||||
for glyph_index in 0..(glyph_count as u32) {
|
for glyph_index in 0..(glyph_count as u32) {
|
||||||
batch_builder.add_glyph(&glyph_buffer_builder,
|
atlas_builder.pack_glyph(&glyph_buffer_builder, glyph_index, FPS_DISPLAY_POINT_SIZE)
|
||||||
glyph_index,
|
.unwrap()
|
||||||
FPS_DISPLAY_POINT_SIZE).unwrap()
|
|
||||||
}
|
}
|
||||||
let batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
|
|
||||||
|
|
||||||
self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
|
let atlas = atlas_builder.create_atlas(&glyph_buffer_builder).unwrap();
|
||||||
&batch_builder.atlas,
|
|
||||||
|
let rect = Rect::new(Point2D::new(0, 0), self.atlas_size);
|
||||||
|
|
||||||
|
self.rasterizer.draw_atlas(&self.fps_compute_image,
|
||||||
|
&rect,
|
||||||
|
&atlas,
|
||||||
glyph_buffers,
|
glyph_buffers,
|
||||||
&batch,
|
&self.coverage_buffer).unwrap();
|
||||||
&self.coverage_buffer,
|
|
||||||
&self.fps_compute_image).unwrap();
|
|
||||||
|
|
||||||
batch_builder
|
atlas_builder
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_fps(&self,
|
fn draw_fps(&self,
|
||||||
font: &Font,
|
font: &Font,
|
||||||
batch_builder: &mut BatchBuilder,
|
atlas_builder: &mut AtlasBuilder,
|
||||||
glyph_buffer_builder: &GlyphBufferBuilder,
|
glyph_buffer_builder: &GlyphBufferBuilder,
|
||||||
device_pixel_size: &Size2D<u32>,
|
device_pixel_size: &Size2D<u32>,
|
||||||
glyph_ranges: &GlyphRanges,
|
glyph_ranges: &GlyphRanges,
|
||||||
|
@ -611,7 +614,7 @@ impl Renderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.draw_glyphs(batch_builder,
|
self.draw_glyphs(atlas_builder,
|
||||||
glyph_buffer_builder,
|
glyph_buffer_builder,
|
||||||
&fps_glyphs,
|
&fps_glyphs,
|
||||||
device_pixel_size,
|
device_pixel_size,
|
||||||
|
|
264
src/atlas.rs
264
src/atlas.rs
|
@ -9,108 +9,188 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||||
|
use gl;
|
||||||
|
use glyph_buffer::GlyphBufferBuilder;
|
||||||
|
use rect_packer::RectPacker;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::u16;
|
||||||
|
|
||||||
|
pub struct AtlasBuilder {
|
||||||
|
pub rect_packer: RectPacker,
|
||||||
|
image_descriptors: Vec<ImageDescriptor>,
|
||||||
|
image_metadata: Vec<ImageMetadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtlasBuilder {
|
||||||
|
/// FIXME(pcwalton): Including the shelf height here may be a bad API.
|
||||||
|
#[inline]
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME(pcwalton): Support the same glyph drawn at multiple point sizes.
|
||||||
|
pub fn pack_glyph(&mut self,
|
||||||
|
glyph_buffer_builder: &GlyphBufferBuilder,
|
||||||
|
glyph_index: u32,
|
||||||
|
point_size: f32)
|
||||||
|
-> Result<(), ()> {
|
||||||
|
// FIXME(pcwalton): I think this will check for negative values and panic, which is
|
||||||
|
// unnecessary.
|
||||||
|
let pixel_size = glyph_buffer_builder.glyph_pixel_bounds(glyph_index, point_size)
|
||||||
|
.size
|
||||||
|
.ceil()
|
||||||
|
.cast()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let glyph_id = glyph_buffer_builder.glyph_id(glyph_index);
|
||||||
|
|
||||||
|
let atlas_origin = try!(self.rect_packer.pack(&pixel_size));
|
||||||
|
|
||||||
|
let glyph_index = self.image_descriptors.len() as u32;
|
||||||
|
|
||||||
|
while self.image_descriptors.len() < glyph_index as usize + 1 {
|
||||||
|
self.image_descriptors.push(ImageDescriptor::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
self.image_descriptors[glyph_index as usize] = ImageDescriptor {
|
||||||
|
atlas_x: atlas_origin.x,
|
||||||
|
atlas_y: atlas_origin.y,
|
||||||
|
point_size: (point_size * 65536.0) as u32,
|
||||||
|
glyph_index: glyph_index,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.image_metadata.push(ImageMetadata {
|
||||||
|
atlas_size: pixel_size,
|
||||||
|
glyph_index: glyph_index,
|
||||||
|
glyph_id: glyph_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_atlas(&mut self, glyph_buffer_builder: &GlyphBufferBuilder)
|
||||||
|
-> Result<Atlas, ()> {
|
||||||
|
self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index));
|
||||||
|
|
||||||
|
let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
|
||||||
|
for image_metadata in &self.image_metadata {
|
||||||
|
let glyph_index = image_metadata.glyph_index;
|
||||||
|
|
||||||
|
let first_index = glyph_buffer_builder.descriptors[glyph_index as usize].start_index as
|
||||||
|
usize;
|
||||||
|
let last_index = match glyph_buffer_builder.descriptors.get(glyph_index as usize + 1) {
|
||||||
|
Some(ref descriptor) => descriptor.start_index as usize,
|
||||||
|
None => glyph_buffer_builder.indices.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match current_range {
|
||||||
|
Some((current_first, current_last)) if first_index == current_last => {
|
||||||
|
current_range = Some((current_first, last_index))
|
||||||
|
}
|
||||||
|
Some((current_first, current_last)) => {
|
||||||
|
counts.push((current_last - current_first) as GLsizei);
|
||||||
|
start_indices.push(current_first);
|
||||||
|
current_range = Some((first_index, last_index))
|
||||||
|
}
|
||||||
|
None => current_range = Some((first_index, last_index)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some((current_first, current_last)) = current_range {
|
||||||
|
counts.push((current_last - current_first) as GLsizei);
|
||||||
|
start_indices.push(current_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Try using `glMapBuffer` here.
|
||||||
|
unsafe {
|
||||||
|
let mut images = 0;
|
||||||
|
gl::GenBuffers(1, &mut images);
|
||||||
|
|
||||||
|
let length = self.image_descriptors.len() * mem::size_of::<ImageDescriptor>();
|
||||||
|
let ptr = self.image_descriptors.as_ptr() as *const ImageDescriptor as *const c_void;
|
||||||
|
gl::BindBuffer(gl::UNIFORM_BUFFER, images);
|
||||||
|
gl::BufferData(gl::UNIFORM_BUFFER, length as GLsizeiptr, ptr, gl::DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
Ok(Atlas {
|
||||||
|
start_indices: start_indices,
|
||||||
|
counts: counts,
|
||||||
|
images: images,
|
||||||
|
|
||||||
|
shelf_height: self.rect_packer.shelf_height(),
|
||||||
|
shelf_columns: self.rect_packer.shelf_columns(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn glyph_index_for(&self, glyph_id: u16) -> Option<u32> {
|
||||||
|
match self.image_metadata.binary_search_by(|metadata| metadata.glyph_id.cmp(&glyph_id)) {
|
||||||
|
Ok(glyph_index) => Some(self.image_metadata[glyph_index].glyph_index),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn atlas_rect(&self, glyph_index: u32) -> Rect<u32> {
|
||||||
|
let descriptor = &self.image_descriptors[glyph_index as usize];
|
||||||
|
let metadata = &self.image_metadata[glyph_index as usize];
|
||||||
|
Rect::new(Point2D::new(descriptor.atlas_x, descriptor.atlas_y), metadata.atlas_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Atlas {
|
pub struct Atlas {
|
||||||
free_rects: Vec<Rect<u32>>,
|
start_indices: Vec<usize>,
|
||||||
available_width: u32,
|
counts: Vec<GLsizei>,
|
||||||
shelf_height: u32,
|
images: GLuint,
|
||||||
shelf_count: u32,
|
|
||||||
/// The amount of horizontal space allocated in the last shelf.
|
pub shelf_height: u32,
|
||||||
width_of_last_shelf: u32,
|
pub shelf_columns: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Atlas {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
gl::DeleteBuffers(1, &mut self.images);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Atlas {
|
impl Atlas {
|
||||||
#[inline]
|
pub unsafe fn draw(&self, primitive: GLenum) {
|
||||||
pub fn new(available_width: u32, shelf_height: u32) -> Atlas {
|
debug_assert!(self.counts.len() == self.start_indices.len());
|
||||||
Atlas {
|
gl::MultiDrawElements(primitive,
|
||||||
free_rects: vec![],
|
self.counts.as_ptr(),
|
||||||
available_width: available_width,
|
gl::UNSIGNED_INT,
|
||||||
shelf_height: shelf_height,
|
self.start_indices.as_ptr() as *const *const GLvoid,
|
||||||
shelf_count: 0,
|
self.counts.len() as GLsizei);
|
||||||
width_of_last_shelf: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn place(&mut self, size: &Size2D<u32>) -> Result<Point2D<u32>, ()> {
|
|
||||||
// Add a one-pixel border to prevent bleed.
|
|
||||||
let alloc_size = *size + Size2D::new(2, 2);
|
|
||||||
|
|
||||||
let chosen_index_and_rect =
|
|
||||||
self.free_rects
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|&(_, rect)| {
|
|
||||||
alloc_size.width <= rect.size.width && alloc_size.height <= rect.size.height
|
|
||||||
})
|
|
||||||
.min_by(|&(_, a), &(_, b)| area(a).cmp(&area(b)))
|
|
||||||
.map(|(index, rect)| (index, *rect));
|
|
||||||
|
|
||||||
let chosen_rect;
|
|
||||||
match chosen_index_and_rect {
|
|
||||||
None => {
|
|
||||||
// Make a new shelf.
|
|
||||||
chosen_rect = Rect::new(Point2D::new(0, self.shelf_height * self.shelf_count),
|
|
||||||
Size2D::new(self.available_width, self.shelf_height));
|
|
||||||
self.shelf_count += 1;
|
|
||||||
self.width_of_last_shelf = 0
|
|
||||||
}
|
|
||||||
Some((index, rect)) => {
|
|
||||||
self.free_rects.swap_remove(index);
|
|
||||||
chosen_rect = rect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guillotine to bottom.
|
|
||||||
let free_below =
|
|
||||||
Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + alloc_size.height),
|
|
||||||
Size2D::new(alloc_size.width, chosen_rect.size.height - alloc_size.height));
|
|
||||||
if !free_below.is_empty() {
|
|
||||||
self.free_rects.push(free_below);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guillotine to right.
|
|
||||||
let free_to_right =
|
|
||||||
Rect::new(Point2D::new(chosen_rect.origin.x + alloc_size.width, chosen_rect.origin.y),
|
|
||||||
Size2D::new(chosen_rect.size.width - alloc_size.width,
|
|
||||||
chosen_rect.size.height));
|
|
||||||
if !free_to_right.is_empty() {
|
|
||||||
self.free_rects.push(free_to_right);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update width of last shelf if necessary.
|
|
||||||
let on_last_shelf = chosen_rect.max_y() >= self.shelf_height * (self.shelf_count - 1);
|
|
||||||
if on_last_shelf && self.width_of_last_shelf < chosen_rect.max_x() {
|
|
||||||
self.width_of_last_shelf = chosen_rect.max_x()
|
|
||||||
}
|
|
||||||
|
|
||||||
let object_origin = chosen_rect.origin + Point2D::new(1, 1);
|
|
||||||
Ok(object_origin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn available_width(&self) -> u32 {
|
pub fn images(&self) -> GLuint {
|
||||||
self.available_width
|
self.images
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn shelf_height(&self) -> u32 {
|
|
||||||
self.shelf_height
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn shelf_columns(&self) -> u32 {
|
|
||||||
let full_shelf_count = if self.shelf_count == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.shelf_count - 1
|
|
||||||
};
|
|
||||||
|
|
||||||
full_shelf_count * self.available_width + self.width_of_last_shelf
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Information about each image that we send to the GPU.
|
||||||
fn area(rect: &Rect<u32>) -> u32 {
|
#[repr(C)]
|
||||||
rect.size.width * rect.size.height
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub struct ImageDescriptor {
|
||||||
|
atlas_x: u32,
|
||||||
|
atlas_y: u32,
|
||||||
|
point_size: u32,
|
||||||
|
glyph_index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about each image that we keep around ourselves.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ImageMetadata {
|
||||||
|
atlas_size: Size2D<u32>,
|
||||||
|
glyph_index: u32,
|
||||||
|
glyph_id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
183
src/batch.rs
183
src/batch.rs
|
@ -1,183 +0,0 @@
|
||||||
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
use atlas::Atlas;
|
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
|
||||||
use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
|
||||||
use gl;
|
|
||||||
use glyph_buffer::GlyphBufferBuilder;
|
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::u16;
|
|
||||||
|
|
||||||
pub struct BatchBuilder {
|
|
||||||
pub atlas: Atlas,
|
|
||||||
image_descriptors: Vec<ImageDescriptor>,
|
|
||||||
image_metadata: Vec<ImageMetadata>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BatchBuilder {
|
|
||||||
/// FIXME(pcwalton): Including the shelf height here may be a bad API.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(available_width: u32, shelf_height: u32) -> BatchBuilder {
|
|
||||||
BatchBuilder {
|
|
||||||
atlas: Atlas::new(available_width, shelf_height),
|
|
||||||
image_descriptors: vec![],
|
|
||||||
image_metadata: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FIXME(pcwalton): Support the same glyph drawn at multiple point sizes.
|
|
||||||
pub fn add_glyph(&mut self,
|
|
||||||
glyph_buffer_builder: &GlyphBufferBuilder,
|
|
||||||
glyph_index: u32,
|
|
||||||
point_size: f32)
|
|
||||||
-> Result<(), ()> {
|
|
||||||
let descriptor = &glyph_buffer_builder.descriptors[glyph_index as usize];
|
|
||||||
|
|
||||||
// FIXME(pcwalton): I think this will check for negative values and panic, which is
|
|
||||||
// unnecessary.
|
|
||||||
let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap();
|
|
||||||
let atlas_origin = try!(self.atlas.place(&pixel_size));
|
|
||||||
|
|
||||||
while self.image_descriptors.len() < glyph_index as usize + 1 {
|
|
||||||
self.image_descriptors.push(ImageDescriptor::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
self.image_descriptors[glyph_index as usize] = ImageDescriptor {
|
|
||||||
atlas_x: atlas_origin.x,
|
|
||||||
atlas_y: atlas_origin.y,
|
|
||||||
point_size: (point_size * 65536.0) as u32,
|
|
||||||
glyph_index: glyph_index,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.image_metadata.push(ImageMetadata {
|
|
||||||
atlas_size: pixel_size,
|
|
||||||
glyph_index: glyph_index,
|
|
||||||
glyph_id: descriptor.glyph_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_batch(&mut self, glyph_buffer_builder: &GlyphBufferBuilder)
|
|
||||||
-> Result<Batch, ()> {
|
|
||||||
self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index));
|
|
||||||
|
|
||||||
let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]);
|
|
||||||
for image_metadata in &self.image_metadata {
|
|
||||||
let glyph_index = image_metadata.glyph_index;
|
|
||||||
|
|
||||||
let first_index = glyph_buffer_builder.descriptors[glyph_index as usize].start_index as
|
|
||||||
usize;
|
|
||||||
let last_index = match glyph_buffer_builder.descriptors.get(glyph_index as usize + 1) {
|
|
||||||
Some(ref descriptor) => descriptor.start_index as usize,
|
|
||||||
None => glyph_buffer_builder.indices.len(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match current_range {
|
|
||||||
Some((current_first, current_last)) if first_index == current_last => {
|
|
||||||
current_range = Some((current_first, last_index))
|
|
||||||
}
|
|
||||||
Some((current_first, current_last)) => {
|
|
||||||
counts.push((current_last - current_first) as GLsizei);
|
|
||||||
start_indices.push(current_first);
|
|
||||||
current_range = Some((first_index, last_index))
|
|
||||||
}
|
|
||||||
None => current_range = Some((first_index, last_index)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some((current_first, current_last)) = current_range {
|
|
||||||
counts.push((current_last - current_first) as GLsizei);
|
|
||||||
start_indices.push(current_first);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pcwalton): Try using `glMapBuffer` here.
|
|
||||||
unsafe {
|
|
||||||
let mut images = 0;
|
|
||||||
gl::GenBuffers(1, &mut images);
|
|
||||||
|
|
||||||
let length = self.image_descriptors.len() * mem::size_of::<ImageDescriptor>();
|
|
||||||
let ptr = self.image_descriptors.as_ptr() as *const ImageDescriptor as *const c_void;
|
|
||||||
gl::BindBuffer(gl::UNIFORM_BUFFER, images);
|
|
||||||
gl::BufferData(gl::UNIFORM_BUFFER, length as GLsizeiptr, ptr, gl::DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
Ok(Batch {
|
|
||||||
start_indices: start_indices,
|
|
||||||
counts: counts,
|
|
||||||
images: images,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn glyph_index_for(&self, glyph_id: u16) -> Option<u32> {
|
|
||||||
match self.image_metadata.binary_search_by(|metadata| metadata.glyph_id.cmp(&glyph_id)) {
|
|
||||||
Ok(glyph_index) => Some(self.image_metadata[glyph_index].glyph_index),
|
|
||||||
Err(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn atlas_rect(&self, glyph_index: u32) -> Rect<u32> {
|
|
||||||
let descriptor = &self.image_descriptors[glyph_index as usize];
|
|
||||||
let metadata = &self.image_metadata[glyph_index as usize];
|
|
||||||
Rect::new(Point2D::new(descriptor.atlas_x, descriptor.atlas_y), metadata.atlas_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Batch {
|
|
||||||
start_indices: Vec<usize>,
|
|
||||||
counts: Vec<GLsizei>,
|
|
||||||
images: GLuint,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Batch {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gl::DeleteBuffers(1, &mut self.images);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Batch {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn images(&self) -> GLuint {
|
|
||||||
self.images
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about each image that we send to the GPU.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Default, Debug)]
|
|
||||||
pub struct ImageDescriptor {
|
|
||||||
atlas_x: u32,
|
|
||||||
atlas_y: u32,
|
|
||||||
point_size: u32,
|
|
||||||
glyph_index: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about each image that we keep around ourselves.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct ImageMetadata {
|
|
||||||
atlas_size: Size2D<u32>,
|
|
||||||
glyph_index: u32,
|
|
||||||
glyph_id: u16,
|
|
||||||
}
|
|
||||||
|
|
|
@ -83,6 +83,18 @@ impl GlyphBufferBuilder {
|
||||||
self.descriptors[glyph_index as usize].bounds
|
self.descriptors[glyph_index as usize].bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the glyph rectangle in pixels.
|
||||||
|
#[inline]
|
||||||
|
pub fn glyph_pixel_bounds(&self, glyph_index: u32, point_size: f32) -> Rect<f32> {
|
||||||
|
self.descriptors[glyph_index as usize].pixel_rect(point_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ID of the glyph with the given index.
|
||||||
|
#[inline]
|
||||||
|
pub fn glyph_id(&self, glyph_index: u32) -> u16 {
|
||||||
|
self.descriptors[glyph_index as usize].glyph_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_buffers(&self) -> Result<GlyphBuffers, ()> {
|
pub fn create_buffers(&self) -> Result<GlyphBuffers, ()> {
|
||||||
// TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contour types and
|
// TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contour types and
|
||||||
// counts.
|
// counts.
|
||||||
|
@ -148,7 +160,7 @@ pub struct GlyphDescriptor {
|
||||||
|
|
||||||
impl GlyphDescriptor {
|
impl GlyphDescriptor {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixel_rect(&self, point_size: f32) -> Rect<f32> {
|
fn pixel_rect(&self, point_size: f32) -> Rect<f32> {
|
||||||
let pixels_per_unit = point_size / self.units_per_em as f32;
|
let pixels_per_unit = point_size / self.units_per_em as f32;
|
||||||
Rect::new(Point2D::new(self.bounds.left as f32, self.bounds.bottom as f32),
|
Rect::new(Point2D::new(self.bounds.left as f32, self.bounds.bottom as f32),
|
||||||
Size2D::new((self.bounds.right - self.bounds.left) as f32,
|
Size2D::new((self.bounds.right - self.bounds.left) as f32,
|
||||||
|
|
|
@ -25,13 +25,13 @@ extern crate quickcheck;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
pub mod atlas;
|
pub mod atlas;
|
||||||
pub mod batch;
|
|
||||||
pub mod charmap;
|
pub mod charmap;
|
||||||
pub mod coverage;
|
pub mod coverage;
|
||||||
pub mod glyph_buffer;
|
pub mod glyph_buffer;
|
||||||
pub mod glyph_range;
|
pub mod glyph_range;
|
||||||
pub mod otf;
|
pub mod otf;
|
||||||
pub mod rasterizer;
|
pub mod rasterizer;
|
||||||
|
pub mod rect_packer;
|
||||||
pub mod shaper;
|
pub mod shaper;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use atlas::Atlas;
|
use atlas::Atlas;
|
||||||
use batch::Batch;
|
|
||||||
use compute_shader::device::Device;
|
use compute_shader::device::Device;
|
||||||
use compute_shader::image::Image;
|
use compute_shader::image::Image;
|
||||||
use compute_shader::instance::{Instance, ShadingLanguage};
|
use compute_shader::instance::{Instance, ShadingLanguage};
|
||||||
|
@ -144,18 +143,17 @@ impl Rasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_atlas(&self,
|
pub fn draw_atlas(&self,
|
||||||
atlas_rect: &Rect<u32>,
|
image: &Image,
|
||||||
|
rect: &Rect<u32>,
|
||||||
atlas: &Atlas,
|
atlas: &Atlas,
|
||||||
glyph_buffers: &GlyphBuffers,
|
glyph_buffers: &GlyphBuffers,
|
||||||
batch: &Batch,
|
coverage_buffer: &CoverageBuffer)
|
||||||
coverage_buffer: &CoverageBuffer,
|
|
||||||
image: &Image)
|
|
||||||
-> Result<DrawAtlasProfilingEvents, ()> {
|
-> Result<DrawAtlasProfilingEvents, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer());
|
gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer());
|
||||||
gl::Viewport(0, 0, atlas_rect.size.width as GLint, atlas_rect.size.height as GLint);
|
gl::Viewport(0, 0, rect.size.width as GLint, rect.size.height as GLint);
|
||||||
|
|
||||||
// TODO(pcwalton): Scissor to the atlas rect to clear faster?
|
// TODO(pcwalton): Scissor to the image rect to clear faster?
|
||||||
gl::ClearColor(0.0, 0.0, 0.0, 1.0);
|
gl::ClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
@ -180,13 +178,11 @@ impl Rasterizer {
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, glyph_buffers.indices);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, glyph_buffers.indices);
|
||||||
|
|
||||||
gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, glyph_buffers.descriptors);
|
gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, glyph_buffers.descriptors);
|
||||||
gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, batch.images());
|
gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, atlas.images());
|
||||||
gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1);
|
gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1);
|
||||||
gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2);
|
gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2);
|
||||||
|
|
||||||
gl::Uniform2ui(self.draw_atlas_size_uniform,
|
gl::Uniform2ui(self.draw_atlas_size_uniform, rect.size.width, rect.size.height);
|
||||||
atlas_rect.size.width,
|
|
||||||
atlas_rect.size.height);
|
|
||||||
|
|
||||||
gl::PatchParameteri(gl::PATCH_VERTICES, 3);
|
gl::PatchParameteri(gl::PATCH_VERTICES, 3);
|
||||||
|
|
||||||
|
@ -210,7 +206,7 @@ impl Rasterizer {
|
||||||
};
|
};
|
||||||
// Now draw the glyph ranges.
|
// Now draw the glyph ranges.
|
||||||
gl::BeginQuery(gl::TIME_ELAPSED, self.draw_query);
|
gl::BeginQuery(gl::TIME_ELAPSED, self.draw_query);
|
||||||
batch.draw(primitive);
|
atlas.draw(primitive);
|
||||||
gl::EndQuery(gl::TIME_ELAPSED);
|
gl::EndQuery(gl::TIME_ELAPSED);
|
||||||
|
|
||||||
gl::Disable(gl::CULL_FACE);
|
gl::Disable(gl::CULL_FACE);
|
||||||
|
@ -224,22 +220,15 @@ impl Rasterizer {
|
||||||
gl::Flush();
|
gl::Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
let atlas_rect_uniform = [
|
|
||||||
atlas_rect.origin.x,
|
|
||||||
atlas_rect.origin.y,
|
|
||||||
atlas_rect.max_x(),
|
|
||||||
atlas_rect.max_y()
|
|
||||||
];
|
|
||||||
|
|
||||||
let accum_uniforms = [
|
let accum_uniforms = [
|
||||||
(0, Uniform::Image(image)),
|
(0, Uniform::Image(image)),
|
||||||
(1, Uniform::Image(coverage_buffer.image())),
|
(1, Uniform::Image(coverage_buffer.image())),
|
||||||
(2, Uniform::UVec4(atlas_rect_uniform)),
|
(2, Uniform::UVec4([rect.origin.x, rect.origin.y, rect.max_x(), rect.max_y()])),
|
||||||
(3, Uniform::U32(atlas.shelf_height())),
|
(3, Uniform::U32(atlas.shelf_height)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let accum_event = try!(self.queue.submit_compute(&self.accum_program,
|
let accum_event = try!(self.queue.submit_compute(&self.accum_program,
|
||||||
&[atlas.shelf_columns()],
|
&[atlas.shelf_columns],
|
||||||
&accum_uniforms,
|
&accum_uniforms,
|
||||||
&[]).map_err(drop));
|
&[]).map_err(drop));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
|
||||||
|
pub struct RectPacker {
|
||||||
|
free_rects: Vec<Rect<u32>>,
|
||||||
|
available_width: u32,
|
||||||
|
shelf_height: u32,
|
||||||
|
shelf_count: u32,
|
||||||
|
/// The amount of horizontal space allocated in the last shelf.
|
||||||
|
width_of_last_shelf: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectPacker {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(available_width: u32, shelf_height: u32) -> RectPacker {
|
||||||
|
RectPacker {
|
||||||
|
free_rects: vec![],
|
||||||
|
available_width: available_width,
|
||||||
|
shelf_height: shelf_height,
|
||||||
|
shelf_count: 0,
|
||||||
|
width_of_last_shelf: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pack(&mut self, size: &Size2D<u32>) -> Result<Point2D<u32>, ()> {
|
||||||
|
// Add a one-pixel border to prevent bleed.
|
||||||
|
let alloc_size = *size + Size2D::new(2, 2);
|
||||||
|
|
||||||
|
let chosen_index_and_rect =
|
||||||
|
self.free_rects
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|&(_, rect)| {
|
||||||
|
alloc_size.width <= rect.size.width && alloc_size.height <= rect.size.height
|
||||||
|
})
|
||||||
|
.min_by(|&(_, a), &(_, b)| area(a).cmp(&area(b)))
|
||||||
|
.map(|(index, rect)| (index, *rect));
|
||||||
|
|
||||||
|
let chosen_rect;
|
||||||
|
match chosen_index_and_rect {
|
||||||
|
None => {
|
||||||
|
// Make a new shelf.
|
||||||
|
chosen_rect = Rect::new(Point2D::new(0, self.shelf_height * self.shelf_count),
|
||||||
|
Size2D::new(self.available_width, self.shelf_height));
|
||||||
|
self.shelf_count += 1;
|
||||||
|
self.width_of_last_shelf = 0
|
||||||
|
}
|
||||||
|
Some((index, rect)) => {
|
||||||
|
self.free_rects.swap_remove(index);
|
||||||
|
chosen_rect = rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guillotine to bottom.
|
||||||
|
let free_below =
|
||||||
|
Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + alloc_size.height),
|
||||||
|
Size2D::new(alloc_size.width, chosen_rect.size.height - alloc_size.height));
|
||||||
|
if !free_below.is_empty() {
|
||||||
|
self.free_rects.push(free_below);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guillotine to right.
|
||||||
|
let free_to_right =
|
||||||
|
Rect::new(Point2D::new(chosen_rect.origin.x + alloc_size.width, chosen_rect.origin.y),
|
||||||
|
Size2D::new(chosen_rect.size.width - alloc_size.width,
|
||||||
|
chosen_rect.size.height));
|
||||||
|
if !free_to_right.is_empty() {
|
||||||
|
self.free_rects.push(free_to_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update width of last shelf if necessary.
|
||||||
|
let on_last_shelf = chosen_rect.max_y() >= self.shelf_height * (self.shelf_count - 1);
|
||||||
|
if on_last_shelf && self.width_of_last_shelf < chosen_rect.max_x() {
|
||||||
|
self.width_of_last_shelf = chosen_rect.max_x()
|
||||||
|
}
|
||||||
|
|
||||||
|
let object_origin = chosen_rect.origin + Point2D::new(1, 1);
|
||||||
|
Ok(object_origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn available_width(&self) -> u32 {
|
||||||
|
self.available_width
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn shelf_height(&self) -> u32 {
|
||||||
|
self.shelf_height
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn shelf_columns(&self) -> u32 {
|
||||||
|
let full_shelf_count = if self.shelf_count == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.shelf_count - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
full_shelf_count * self.available_width + self.width_of_last_shelf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn area(rect: &Rect<u32>) -> u32 {
|
||||||
|
rect.size.width * rect.size.height
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
// 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.
|
||||||
|
|
||||||
mod atlas;
|
|
||||||
mod buffers;
|
mod buffers;
|
||||||
|
mod rect_packer;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
use atlas::Atlas;
|
use rect_packer::RectPacker;
|
||||||
use euclid::{Rect, Size2D};
|
use euclid::{Rect, Size2D};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
fn place_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (Atlas, Vec<Rect<u32>>) {
|
fn pack_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (RectPacker, Vec<Rect<u32>>) {
|
||||||
let objects: Vec<_> = objects.iter()
|
let objects: Vec<_> = objects.iter()
|
||||||
.map(|&(width, height)| Size2D::new(width, height))
|
.map(|&(width, height)| Size2D::new(width, height))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -14,16 +14,16 @@ fn place_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (Atlas, Vec<
|
||||||
cmp::max(available_width, objects.iter().map(|object| object.width).max().unwrap_or(0));
|
cmp::max(available_width, objects.iter().map(|object| object.width).max().unwrap_or(0));
|
||||||
let shelf_height = objects.iter().map(|object| object.height).max().unwrap_or(0) + 2;
|
let shelf_height = objects.iter().map(|object| object.height).max().unwrap_or(0) + 2;
|
||||||
|
|
||||||
let mut atlas = Atlas::new(available_width, shelf_height);
|
let mut rect_packer = RectPacker::new(available_width, shelf_height);
|
||||||
let rects = objects.iter()
|
let rects = objects.iter()
|
||||||
.map(|object| Rect::new(atlas.place(object).unwrap(), *object))
|
.map(|object| Rect::new(rect_packer.pack(object).unwrap(), *object))
|
||||||
.collect();
|
.collect();
|
||||||
(atlas, rects)
|
(rect_packer, rects)
|
||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
fn objects_dont_overlap(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
fn objects_dont_overlap(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
let (_, rects) = place_objects(available_width, objects);
|
let (_, rects) = pack_objects(available_width, objects);
|
||||||
for (i, a) in rects.iter().enumerate() {
|
for (i, a) in rects.iter().enumerate() {
|
||||||
for b in &rects[(i + 1)..] {
|
for b in &rects[(i + 1)..] {
|
||||||
assert!(!a.intersects(b))
|
assert!(!a.intersects(b))
|
||||||
|
@ -33,15 +33,15 @@ quickcheck! {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn objects_dont_exceed_available_width(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
fn objects_dont_exceed_available_width(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
let (atlas, rects) = place_objects(available_width, objects);
|
let (rect_packer, rects) = pack_objects(available_width, objects);
|
||||||
rects.iter().all(|rect| rect.max_x() <= atlas.available_width())
|
rects.iter().all(|rect| rect.max_x() <= rect_packer.available_width())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn objects_dont_cross_shelves(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
fn objects_dont_cross_shelves(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
let (atlas, rects) = place_objects(available_width, objects);
|
let (rect_packer, rects) = pack_objects(available_width, objects);
|
||||||
rects.iter().all(|rect| {
|
rects.iter().all(|rect| {
|
||||||
rect.is_empty() ||
|
let shelf_height = rect_packer.shelf_height();
|
||||||
rect.origin.y / atlas.shelf_height() == (rect.max_y() - 1) / atlas.shelf_height()
|
rect.is_empty() || rect.origin.y / shelf_height == (rect.max_y() - 1) / shelf_height
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue