Don't distinguish between render targets and other images in the texture atlas

allocator
This commit is contained in:
Patrick Walton 2020-03-04 19:41:24 -08:00
parent a8f7438cd9
commit 9935c9fdff
4 changed files with 105 additions and 128 deletions

View File

@ -10,8 +10,7 @@
//! A simple quadtree-based texture allocator. //! A simple quadtree-based texture allocator.
use crate::gpu_data::TexturePageId; use crate::gpu_data::{TextureLocation, TexturePageId};
use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_geometry::vector::{Vector2F, Vector2I};
@ -28,8 +27,6 @@ pub enum TexturePageAllocator {
Atlas(TextureAtlasAllocator), Atlas(TextureAtlasAllocator),
// A single image. // A single image.
Image { size: Vector2I }, Image { size: Vector2I },
// A render target.
RenderTarget { size: Vector2I, id: RenderTargetId },
} }
#[derive(Debug)] #[derive(Debug)]
@ -38,12 +35,6 @@ pub struct TextureAtlasAllocator {
size: u32, size: u32,
} }
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TextureLocation {
pub page: TexturePageId,
pub rect: RectI,
}
#[derive(Debug)] #[derive(Debug)]
enum TreeNode { enum TreeNode {
EmptyLeaf, EmptyLeaf,
@ -75,8 +66,7 @@ impl TextureAllocator {
// Try to add to each atlas. // Try to add to each atlas.
for (page_index, page) in self.pages.iter_mut().enumerate() { for (page_index, page) in self.pages.iter_mut().enumerate() {
match *page { match *page {
TexturePageAllocator::Image { .. } | TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::RenderTarget { .. } => {}
TexturePageAllocator::Atlas(ref mut allocator) => { TexturePageAllocator::Atlas(ref mut allocator) => {
if let Some(rect) = allocator.allocate(requested_size) { if let Some(rect) = allocator.allocate(requested_size) {
return TextureLocation { page: TexturePageId(page_index as u32), rect }; return TextureLocation { page: TexturePageId(page_index as u32), rect };
@ -93,26 +83,17 @@ impl TextureAllocator {
TextureLocation { page, rect } TextureLocation { page, rect }
} }
fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation { pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation {
let page = TexturePageId(self.pages.len() as u32); let page = TexturePageId(self.pages.len() as u32);
let rect = RectI::new(Vector2I::default(), requested_size); let rect = RectI::new(Vector2I::default(), requested_size);
self.pages.push(TexturePageAllocator::Image { size: rect.size() }); self.pages.push(TexturePageAllocator::Image { size: rect.size() });
TextureLocation { page, rect } TextureLocation { page, rect }
} }
pub fn allocate_render_target(&mut self, requested_size: Vector2I, id: RenderTargetId)
-> TextureLocation {
let page = TexturePageId(self.pages.len() as u32);
let rect = RectI::new(Vector2I::default(), requested_size);
self.pages.push(TexturePageAllocator::RenderTarget { size: rect.size(), id });
TextureLocation { page, rect }
}
pub fn page_size(&self, page_index: TexturePageId) -> Vector2I { pub fn page_size(&self, page_index: TexturePageId) -> Vector2I {
match self.pages[page_index.0 as usize] { match self.pages[page_index.0 as usize] {
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
TexturePageAllocator::Image { size, .. } | TexturePageAllocator::Image { size, .. } => size,
TexturePageAllocator::RenderTarget { size, .. } => size,
} }
} }
@ -124,14 +105,6 @@ impl TextureAllocator {
pub fn page_count(&self) -> u32 { pub fn page_count(&self) -> u32 {
self.pages.len() as u32 self.pages.len() as u32
} }
#[inline]
pub fn page_render_target_id(&self, page_index: TexturePageId) -> Option<RenderTargetId> {
match self.pages[page_index.0 as usize] {
TexturePageAllocator::RenderTarget { id, .. } => Some(id),
TexturePageAllocator::Atlas(_) | TexturePageAllocator::Image { .. } => None,
}
}
} }
impl TextureAtlasAllocator { impl TextureAtlasAllocator {

View File

@ -20,7 +20,7 @@ use crate::gpu::shaders::{SolidTileVertexArray, StencilProgram, StencilVertexArr
use crate::gpu::shaders::{TileFilterBasicProgram, TileFilterBlurProgram, TileFilterProgram}; use crate::gpu::shaders::{TileFilterBasicProgram, TileFilterBlurProgram, TileFilterProgram};
use crate::gpu::shaders::{TileFilterTextProgram, TileFilterVertexArray}; use crate::gpu::shaders::{TileFilterTextProgram, TileFilterVertexArray};
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, RenderCommand, RenderTargetTile}; use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, RenderCommand, RenderTargetTile};
use crate::gpu_data::{SolidTileVertex, TexturePageDescriptor, TexturePageId}; use crate::gpu_data::{SolidTileVertex, TextureLocation, TexturePageDescriptor, TexturePageId};
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF, ColorU}; use pathfinder_color::{self as color, ColorF, ColorU};
@ -430,11 +430,11 @@ where
RenderCommand::AllocateTexturePages(ref texture_page_descriptors) => { RenderCommand::AllocateTexturePages(ref texture_page_descriptors) => {
self.allocate_texture_pages(texture_page_descriptors) self.allocate_texture_pages(texture_page_descriptors)
} }
RenderCommand::UploadTexelData { page, ref texels, rect } => { RenderCommand::UploadTexelData { ref texels, location } => {
self.upload_texel_data(page, texels, rect) self.upload_texel_data(texels, location)
} }
RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => { RenderCommand::DeclareRenderTarget { id, location } => {
self.declare_render_target(render_target_id, texture_page_id) self.declare_render_target(id, location)
} }
RenderCommand::AddFills(ref fills) => self.add_fills(fills), RenderCommand::AddFills(ref fills) => self.add_fills(fills),
RenderCommand::FlushFills => { RenderCommand::FlushFills => {
@ -599,23 +599,25 @@ where
} }
} }
fn upload_texel_data(&mut self, page_id: TexturePageId, texels: &[ColorU], rect: RectI) { fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) {
let texture_page = &mut self.texture_pages[page_id.0 as usize]; let texture_page = &mut self.texture_pages[location.page.0 as usize];
let texture = self.device.framebuffer_texture(&texture_page.framebuffer); let texture = self.device.framebuffer_texture(&texture_page.framebuffer);
let texels = color::color_slice_to_u8_slice(texels); let texels = color::color_slice_to_u8_slice(texels);
self.device.upload_to_texture(texture, rect, TextureDataRef::U8(texels)); self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels));
texture_page.must_preserve_contents = true; texture_page.must_preserve_contents = true;
} }
fn declare_render_target(&mut self, fn declare_render_target(&mut self,
render_target_id: RenderTargetId, render_target_id: RenderTargetId,
texture_page_id: TexturePageId) { location: TextureLocation) {
while self.render_targets.len() < render_target_id.0 as usize + 1 { while self.render_targets.len() < render_target_id.0 as usize + 1 {
self.render_targets.push(RenderTargetInfo { texture_page: TexturePageId(!0) }); self.render_targets.push(RenderTargetInfo {
location: TextureLocation { page: TexturePageId(!0), rect: RectI::default() },
});
} }
let mut render_target = &mut self.render_targets[render_target_id.0 as usize]; let mut render_target = &mut self.render_targets[render_target_id.0 as usize];
debug_assert_eq!(render_target.texture_page, TexturePageId(!0)); debug_assert_eq!(render_target.location.page, TexturePageId(!0));
render_target.texture_page = texture_page_id; render_target.location = location;
} }
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) { fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) {
@ -1153,7 +1155,7 @@ where
pub fn draw_render_target(&self) -> RenderTarget<D> { pub fn draw_render_target(&self) -> RenderTarget<D> {
match self.render_target_stack.last() { match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => {
let texture_page_id = self.render_target_texture_page_id(render_target_id); let texture_page_id = self.render_target_location(render_target_id).page;
let framebuffer = self.texture_page_framebuffer(texture_page_id); let framebuffer = self.texture_page_framebuffer(texture_page_id);
RenderTarget::Framebuffer(framebuffer) RenderTarget::Framebuffer(framebuffer)
} }
@ -1264,7 +1266,7 @@ where
render_target_id: RenderTargetId, render_target_id: RenderTargetId,
direction: BlurDirection, direction: BlurDirection,
sigma: f32) { sigma: f32) {
let src_texture_page = self.render_target_texture_page_id(render_target_id); let src_texture_page = self.render_target_location(render_target_id).page;
let src_texture = self.texture_page(src_texture_page); let src_texture = self.texture_page(src_texture_page);
let src_texture_size = self.device.texture_size(src_texture); let src_texture_size = self.device.texture_size(src_texture);
@ -1310,8 +1312,10 @@ where
blend_state: Option<BlendState>) { blend_state: Option<BlendState>) {
let clear_color = self.clear_color_for_draw_operation(); let clear_color = self.clear_color_for_draw_operation();
let main_viewport = self.main_viewport(); let main_viewport = self.main_viewport();
let src_texture_page = self.render_target_texture_page_id(render_target_id);
let src_texture = self.texture_page(src_texture_page); // TODO(pcwalton): Other viewports.
let src_texture_location = self.render_target_location(render_target_id);
let src_texture = self.texture_page(src_texture_location.page);
let src_texture_size = self.device.texture_size(src_texture); let src_texture_size = self.device.texture_size(src_texture);
uniforms.extend_from_slice(&[ uniforms.extend_from_slice(&[
@ -1379,7 +1383,7 @@ where
fn clear_color_for_draw_operation(&self) -> Option<ColorF> { fn clear_color_for_draw_operation(&self) -> Option<ColorF> {
let must_preserve_contents = match self.render_target_stack.last() { let must_preserve_contents = match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => {
let texture_page = self.render_target_texture_page_id(render_target_id); let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents self.texture_pages[texture_page.0 as usize].must_preserve_contents
} }
None => { None => {
@ -1400,7 +1404,7 @@ where
fn preserve_draw_framebuffer(&mut self) { fn preserve_draw_framebuffer(&mut self) {
match self.render_target_stack.last() { match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => {
let texture_page = self.render_target_texture_page_id(render_target_id); let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents = true; self.texture_pages[texture_page.0 as usize].must_preserve_contents = true;
} }
None => { None => {
@ -1412,11 +1416,7 @@ where
pub fn draw_viewport(&self) -> RectI { pub fn draw_viewport(&self) -> RectI {
match self.render_target_stack.last() { match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => self.render_target_location(render_target_id).rect,
let texture_page = self.render_target_texture_page_id(render_target_id);
let texture = self.texture_page(texture_page);
RectI::new(Vector2I::default(), self.device.texture_size(texture))
}
None => self.main_viewport(), None => self.main_viewport(),
} }
} }
@ -1438,8 +1438,8 @@ where
Vector2I::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT)) Vector2I::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT))
} }
fn render_target_texture_page_id(&self, render_target_id: RenderTargetId) -> TexturePageId { fn render_target_location(&self, render_target_id: RenderTargetId) -> TextureLocation {
self.render_targets[render_target_id.0 as usize].texture_page self.render_targets[render_target_id.0 as usize].location
} }
fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer { fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer {
@ -1584,7 +1584,7 @@ struct TexturePage<D> where D: Device {
} }
struct RenderTargetInfo { struct RenderTargetInfo {
texture_page: TexturePageId, location: TextureLocation,
} }
trait ToBlendState { trait ToBlendState {

View File

@ -42,12 +42,12 @@ pub enum RenderCommand {
AllocateTexturePages(Vec<TexturePageDescriptor>), AllocateTexturePages(Vec<TexturePageDescriptor>),
// Uploads data to a texture page. // Uploads data to a texture page.
UploadTexelData { page: TexturePageId, texels: Vec<ColorU>, rect: RectI }, UploadTexelData { texels: Vec<ColorU>, location: TextureLocation },
// Associates a render target with a texture page. // Associates a render target with a texture page.
// //
// TODO(pcwalton): Add a rect to this so we can render to subrects of a page. // TODO(pcwalton): Add a rect to this so we can render to subrects of a page.
DeclareRenderTarget { render_target_id: RenderTargetId, texture_page_id: TexturePageId }, DeclareRenderTarget { id: RenderTargetId, location: TextureLocation },
// Adds fills to the queue. // Adds fills to the queue.
AddFills(Vec<FillBatchPrimitive>), AddFills(Vec<FillBatchPrimitive>),
@ -90,6 +90,12 @@ pub struct TexturePageDescriptor {
pub size: Vector2I, pub size: Vector2I,
} }
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TextureLocation {
pub page: TexturePageId,
pub rect: RectI,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AlphaTileBatch { pub struct AlphaTileBatch {
pub tiles: Vec<AlphaTile>, pub tiles: Vec<AlphaTile>,
@ -214,14 +220,11 @@ impl Debug for RenderCommand {
RenderCommand::AllocateTexturePages(ref pages) => { RenderCommand::AllocateTexturePages(ref pages) => {
write!(formatter, "AllocateTexturePages(x{})", pages.len()) write!(formatter, "AllocateTexturePages(x{})", pages.len())
} }
RenderCommand::UploadTexelData { page, rect, .. } => { RenderCommand::UploadTexelData { ref texels, location } => {
write!(formatter, "UploadTexelData({:?}, {:?})", page, rect) write!(formatter, "UploadTexelData({:?}, {:?})", texels, location)
} }
RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => { RenderCommand::DeclareRenderTarget { id, location } => {
write!(formatter, write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location)
"DeclareRenderTarget({:?}, {:?})",
render_target_id,
texture_page_id)
} }
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()), RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
RenderCommand::FlushFills => write!(formatter, "FlushFills"), RenderCommand::FlushFills => write!(formatter, "FlushFills"),

View File

@ -8,8 +8,8 @@
// 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 crate::allocator::{AllocationMode, TextureAllocator, TextureLocation}; use crate::allocator::{AllocationMode, TextureAllocator};
use crate::gpu_data::{RenderCommand, TexturePageDescriptor, TexturePageId}; use crate::gpu_data::{RenderCommand, TextureLocation, TexturePageDescriptor, TexturePageId};
use crate::scene::RenderTarget; use crate::scene::RenderTarget;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use hashbrown::HashMap; use hashbrown::HashMap;
@ -24,7 +24,6 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gpu::TextureSamplingFlags; use pathfinder_gpu::TextureSamplingFlags;
use pathfinder_simd::default::{F32x2, F32x4}; use pathfinder_simd::default::{F32x2, F32x4};
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem;
// The size of a gradient tile. // The size of a gradient tile.
// //
@ -199,10 +198,8 @@ impl Palette {
// Assign render target locations. // Assign render target locations.
let mut render_target_locations = vec![]; let mut render_target_locations = vec![];
for (render_target_index, render_target) in self.render_targets.iter().enumerate() { for render_target in &self.render_targets {
let render_target_id = RenderTargetId(render_target_index as u32); render_target_locations.push(allocator.allocate_image(render_target.size()));
render_target_locations.push(allocator.allocate_render_target(render_target.size(),
render_target_id));
} }
// Assign paint locations. // Assign paint locations.
@ -313,18 +310,9 @@ impl Palette {
// Allocate the texels. // Allocate the texels.
// //
// TODO(pcwalton): This is slow. Do more on GPU. // TODO(pcwalton): This is slow. Do more on GPU.
let mut page_texels = vec![]; let mut page_texels: Vec<_> = metadata.iter().map(|metadata| {
for page_index in 0..allocator.page_count() { Texels::new(allocator.page_size(metadata.texture_page))
let page_id = TexturePageId(page_index); }).collect();
let page_size = allocator.page_size(page_id);
match allocator.page_render_target_id(page_id) {
Some(_) => page_texels.push(vec![]),
None => {
let page_area = page_size.x() as usize * page_size.y() as usize;
page_texels.push(vec![ColorU::default(); page_area]);
}
}
}
// Draw to texels. // Draw to texels.
// //
@ -332,26 +320,22 @@ impl Palette {
for (paint, metadata) in self.paints.iter().zip(metadata.iter()) { for (paint, metadata) in self.paints.iter().zip(metadata.iter()) {
let texture_page = metadata.texture_page; let texture_page = metadata.texture_page;
let texels = &mut page_texels[texture_page.0 as usize]; let texels = &mut page_texels[texture_page.0 as usize];
let page_size = allocator.page_size(texture_page);
let page_scale = allocator.page_scale(texture_page);
match paint { match paint {
Paint::Color(color) => { Paint::Color(color) => {
put_pixel(metadata.texture_rect.origin(), *color, texels, page_size); texels.put_texel(metadata.texture_rect.origin(), *color);
} }
Paint::Gradient(ref gradient) => { Paint::Gradient(ref gradient) => {
self.render_gradient(gradient, self.render_gradient(gradient,
metadata.texture_rect, metadata.texture_rect,
&metadata.texture_transform, &metadata.texture_transform,
texels, texels);
page_size,
page_scale);
} }
Paint::Pattern(ref pattern) => { Paint::Pattern(ref pattern) => {
match pattern.source { match pattern.source {
PatternSource::RenderTarget(_) => {} PatternSource::RenderTarget(_) => {}
PatternSource::Image(ref image) => { PatternSource::Image(ref image) => {
self.render_image(image, metadata.texture_rect, texels, page_size); self.render_image(image, metadata.texture_rect, texels);
} }
} }
} }
@ -362,23 +346,19 @@ impl Palette {
let mut render_commands = vec![ let mut render_commands = vec![
RenderCommand::AllocateTexturePages(texture_page_descriptors) RenderCommand::AllocateTexturePages(texture_page_descriptors)
]; ];
for page_index in 0..allocator.page_count() { for (index, location) in render_target_locations.into_iter().enumerate() {
let page_id = TexturePageId(page_index); let id = RenderTargetId(index as u32);
let page_size = allocator.page_size(page_id); render_commands.push(RenderCommand::DeclareRenderTarget { id, location });
match allocator.page_render_target_id(page_id) { }
Some(render_target_id) => { for (page_index, texels) in page_texels.into_iter().enumerate() {
render_commands.push(RenderCommand::DeclareRenderTarget { if let Some(texel_data) = texels.data {
render_target_id, let page_id = TexturePageId(page_index as u32);
texture_page_id: page_id, let page_size = allocator.page_size(page_id);
}); let rect = RectI::new(Vector2I::default(), page_size);
} render_commands.push(RenderCommand::UploadTexelData {
None => { texels: texel_data,
render_commands.push(RenderCommand::UploadTexelData { location: TextureLocation { page: page_id, rect },
page: page_id, });
texels: mem::replace(&mut page_texels[page_index as usize], vec![]),
rect: RectI::new(Vector2I::default(), page_size),
});
}
} }
} }
@ -390,9 +370,7 @@ impl Palette {
gradient: &Gradient, gradient: &Gradient,
tex_rect: RectI, tex_rect: RectI,
tex_transform: &Transform2F, tex_transform: &Transform2F,
texels: &mut [ColorU], texels: &mut Texels) {
tex_size: Vector2I,
tex_scale: Vector2F) {
match *gradient.geometry() { match *gradient.geometry() {
GradientGeometry::Linear(gradient_line) => { GradientGeometry::Linear(gradient_line) => {
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec. // FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
@ -404,12 +382,13 @@ impl Palette {
for y in 0..(GRADIENT_TILE_LENGTH as i32) { for y in 0..(GRADIENT_TILE_LENGTH as i32) {
for x in 0..(GRADIENT_TILE_LENGTH as i32) { for x in 0..(GRADIENT_TILE_LENGTH as i32) {
let point = tex_rect.origin() + Vector2I::new(x, y); let point = tex_rect.origin() + Vector2I::new(x, y);
let vector = point.to_f32().scale_xy(tex_scale) - gradient_line.from(); let vector = point.to_f32().scale_xy(texels.scale()) -
gradient_line.from();
let mut t = gradient_line.vector().projection_coefficient(vector); let mut t = gradient_line.vector().projection_coefficient(vector);
t = util::clamp(t, 0.0, 1.0); t = util::clamp(t, 0.0, 1.0);
put_pixel(point, gradient.sample(t), texels, tex_size); texels.put_texel(point, gradient.sample(t));
} }
} }
} }
@ -530,27 +509,20 @@ impl Palette {
} }
}; };
put_pixel(point, color, texels, tex_size); texels.put_texel(point, color);
} }
} }
} }
} }
} }
fn render_image(&self, fn render_image(&self, image: &Image, tex_rect: RectI, texels: &mut Texels) {
image: &Image,
tex_rect: RectI,
texels: &mut [ColorU],
tex_size: Vector2I) {
let image_size = image.size(); let image_size = image.size();
for y in 0..image_size.y() { for y in 0..image_size.y() {
let dest_origin = tex_rect.origin() + Vector2I::new(0, y); let dest_origin = tex_rect.origin() + Vector2I::new(0, y);
let dest_start_index = paint_texel_index(dest_origin, tex_size);
let src_start_index = y as usize * image_size.x() as usize; let src_start_index = y as usize * image_size.x() as usize;
let dest_end_index = dest_start_index + image_size.x() as usize;
let src_end_index = src_start_index + image_size.x() as usize; let src_end_index = src_start_index + image_size.x() as usize;
texels[dest_start_index..dest_end_index].copy_from_slice( texels.blit_scanline(dest_origin, &image.pixels()[src_start_index..src_end_index]);
&image.pixels()[src_start_index..src_end_index]);
} }
} }
} }
@ -565,12 +537,41 @@ impl PaintMetadata {
} }
} }
fn paint_texel_index(position: Vector2I, tex_size: Vector2I) -> usize { struct Texels {
position.y() as usize * tex_size.x() as usize + position.x() as usize data: Option<Vec<ColorU>>,
size: Vector2I,
} }
fn put_pixel(position: Vector2I, color: ColorU, texels: &mut [ColorU], tex_size: Vector2I) { impl Texels {
texels[paint_texel_index(position, tex_size)] = color fn new(size: Vector2I) -> Texels {
Texels { data: None, size }
}
fn texel_index(&self, position: Vector2I) -> usize {
position.y() as usize * self.size.x() as usize + position.x() as usize
}
fn allocate_texels_if_necessary(&mut self) {
if self.data.is_none() {
let area = self.size.x() as usize * self.size.y() as usize;
self.data = Some(vec![ColorU::transparent_black(); area]);
}
}
fn blit_scanline(&mut self, dest_origin: Vector2I, src: &[ColorU]) {
self.allocate_texels_if_necessary();
let start_index = self.texel_index(dest_origin);
let end_index = start_index + src.len();
self.data.as_mut().unwrap()[start_index..end_index].copy_from_slice(src)
}
fn put_texel(&mut self, position: Vector2I, color: ColorU) {
self.blit_scanline(position, &[color])
}
fn scale(&self) -> Vector2F {
Vector2F::splat(1.0) / self.size.to_f32()
}
} }
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF { fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {