| 1 |
|
| 2 |
|
| 3 |
use guillotiere::{AllocId, Allocation, AtlasAllocator, size2}; |
| 4 |
|
| 5 |
pub(crate) struct GlyphAtlas { |
| 6 |
pub(crate) texture: wgpu::Texture, |
| 7 |
pub(crate) view: wgpu::TextureView, |
| 8 |
pub(crate) size: u32, |
| 9 |
|
| 10 |
|
| 11 |
pub(crate) solid_uv: [f32; 2], |
| 12 |
allocator: AtlasAllocator, |
| 13 |
} |
| 14 |
|
| 15 |
#[derive(Copy, Clone)] |
| 16 |
pub(crate) struct AtlasSlot { |
| 17 |
pub(crate) _id: AllocId, |
| 18 |
pub(crate) uv_min: [f32; 2], |
| 19 |
pub(crate) uv_max: [f32; 2], |
| 20 |
pub(crate) px: [u32; 2], |
| 21 |
} |
| 22 |
|
| 23 |
impl GlyphAtlas { |
| 24 |
pub(crate) fn new(device: &wgpu::Device, queue: &wgpu::Queue, size: u32) -> Self { |
| 25 |
let texture = device.create_texture(&wgpu::TextureDescriptor { |
| 26 |
label: Some("shop.glyph_atlas"), |
| 27 |
size: wgpu::Extent3d { |
| 28 |
width: size, |
| 29 |
height: size, |
| 30 |
depth_or_array_layers: 1, |
| 31 |
}, |
| 32 |
mip_level_count: 1, |
| 33 |
sample_count: 1, |
| 34 |
dimension: wgpu::TextureDimension::D2, |
| 35 |
format: wgpu::TextureFormat::R8Unorm, |
| 36 |
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, |
| 37 |
view_formats: &[], |
| 38 |
}); |
| 39 |
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); |
| 40 |
let mut allocator = AtlasAllocator::new(size2(size as i32, size as i32)); |
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
let solid_alloc = allocator |
| 45 |
.allocate(size2(4, 4)) |
| 46 |
.expect("atlas must fit a 4x4 solid reservation"); |
| 47 |
let sx = solid_alloc.rectangle.min.x as u32; |
| 48 |
let sy = solid_alloc.rectangle.min.y as u32; |
| 49 |
queue.write_texture( |
| 50 |
wgpu::TexelCopyTextureInfo { |
| 51 |
texture: &texture, |
| 52 |
mip_level: 0, |
| 53 |
origin: wgpu::Origin3d { x: sx, y: sy, z: 0 }, |
| 54 |
aspect: wgpu::TextureAspect::All, |
| 55 |
}, |
| 56 |
&[255u8; 16], |
| 57 |
wgpu::TexelCopyBufferLayout { |
| 58 |
offset: 0, |
| 59 |
bytes_per_row: Some(4), |
| 60 |
rows_per_image: Some(4), |
| 61 |
}, |
| 62 |
wgpu::Extent3d { |
| 63 |
width: 4, |
| 64 |
height: 4, |
| 65 |
depth_or_array_layers: 1, |
| 66 |
}, |
| 67 |
); |
| 68 |
let s = size as f32; |
| 69 |
let solid_uv = [(sx as f32 + 2.0) / s, (sy as f32 + 2.0) / s]; |
| 70 |
|
| 71 |
Self { |
| 72 |
texture, |
| 73 |
view, |
| 74 |
size, |
| 75 |
solid_uv, |
| 76 |
allocator, |
| 77 |
} |
| 78 |
} |
| 79 |
|
| 80 |
|
| 81 |
|
| 82 |
pub(crate) fn upload( |
| 83 |
&mut self, |
| 84 |
queue: &wgpu::Queue, |
| 85 |
width: u32, |
| 86 |
height: u32, |
| 87 |
bitmap: &[u8], |
| 88 |
) -> Option<AtlasSlot> { |
| 89 |
if width == 0 || height == 0 { |
| 90 |
return Some(AtlasSlot { |
| 91 |
_id: AllocId::deserialize(0), |
| 92 |
uv_min: [0.0, 0.0], |
| 93 |
uv_max: [0.0, 0.0], |
| 94 |
px: [0, 0], |
| 95 |
}); |
| 96 |
} |
| 97 |
let Allocation { id, rectangle } = self |
| 98 |
.allocator |
| 99 |
.allocate(size2(width as i32 + 1, height as i32 + 1))?; |
| 100 |
let x = rectangle.min.x as u32; |
| 101 |
let y = rectangle.min.y as u32; |
| 102 |
|
| 103 |
queue.write_texture( |
| 104 |
wgpu::TexelCopyTextureInfo { |
| 105 |
texture: &self.texture, |
| 106 |
mip_level: 0, |
| 107 |
origin: wgpu::Origin3d { x, y, z: 0 }, |
| 108 |
aspect: wgpu::TextureAspect::All, |
| 109 |
}, |
| 110 |
bitmap, |
| 111 |
wgpu::TexelCopyBufferLayout { |
| 112 |
offset: 0, |
| 113 |
bytes_per_row: Some(width), |
| 114 |
rows_per_image: Some(height), |
| 115 |
}, |
| 116 |
wgpu::Extent3d { |
| 117 |
width, |
| 118 |
height, |
| 119 |
depth_or_array_layers: 1, |
| 120 |
}, |
| 121 |
); |
| 122 |
|
| 123 |
let s = self.size as f32; |
| 124 |
Some(AtlasSlot { |
| 125 |
_id: id, |
| 126 |
uv_min: [x as f32 / s, y as f32 / s], |
| 127 |
uv_max: [(x + width) as f32 / s, (y + height) as f32 / s], |
| 128 |
px: [width, height], |
| 129 |
}) |
| 130 |
} |
| 131 |
} |
| 132 |
|