use crate::{
Allocation, AsBufferPass, AtlasSet, GpuRenderer, GraphicsError,
InstanceBuffer, SetBuffers, StaticVertexBuffer, Text, TextRenderPipeline,
TextVertex, Vec2, instance_buffer::OrderedIndex,
};
use cosmic_text::{CacheKey, SwashCache, SwashImage};
#[cfg(feature = "logging")]
use log::{error, warn};
pub struct TextAtlas {
pub(crate) text: AtlasSet<CacheKey, Vec2>,
pub(crate) emoji: AtlasSet<CacheKey, Vec2>,
}
impl TextAtlas {
pub fn new(
renderer: &mut GpuRenderer,
size: u32,
) -> Result<Self, GraphicsError> {
Ok(Self {
text: AtlasSet::new(
renderer,
wgpu::TextureFormat::R8Unorm,
false,
size,
),
emoji: AtlasSet::new(
renderer,
wgpu::TextureFormat::Rgba8UnormSrgb,
false,
size,
),
})
}
pub fn trim(&mut self) {
self.emoji.trim();
self.text.trim();
}
pub fn get_by_key(
&mut self,
key: &CacheKey,
) -> Option<(Allocation<Vec2>, bool)> {
if let Some(allocation) = self.text.get_by_key(key) {
Some((allocation, false))
} else {
self.emoji
.get_by_key(key)
.map(|allocation| (allocation, true))
}
}
pub fn upload_with_alloc(
&mut self,
renderer: &mut GpuRenderer,
is_color: bool,
key: CacheKey,
image: &SwashImage,
) -> Result<(Allocation<Vec2>, bool), GraphicsError> {
if is_color {
let (_, allocation) = self
.emoji
.upload_with_alloc(
key,
&image.data,
image.placement.width,
image.placement.height,
Vec2::new(
image.placement.left as f32,
image.placement.top as f32,
),
renderer,
)
.ok_or(GraphicsError::AtlasFull)?;
Ok((allocation, is_color))
} else {
let (_, allocation) = self
.text
.upload_with_alloc(
key,
&image.data,
image.placement.width,
image.placement.height,
Vec2::new(
image.placement.left as f32,
image.placement.top as f32,
),
renderer,
)
.ok_or(GraphicsError::AtlasFull)?;
Ok((allocation, is_color))
}
}
}
#[derive(Debug)]
pub struct TextRenderer {
pub buffer: InstanceBuffer<TextVertex>,
pub swash_cache: SwashCache,
}
impl TextRenderer {
pub fn new(renderer: &GpuRenderer) -> Result<Self, GraphicsError> {
Ok(Self {
buffer: InstanceBuffer::with_capacity(renderer.gpu_device(), 1024),
swash_cache: SwashCache::new(),
})
}
pub fn add_buffer_store(
&mut self,
renderer: &GpuRenderer,
index: OrderedIndex,
buffer_layer: usize,
) {
self.buffer.add_buffer_store(renderer, index, buffer_layer);
}
pub fn finalize(&mut self, renderer: &mut GpuRenderer) {
self.buffer.finalize(renderer);
}
pub fn update(
&mut self,
text: &mut Text,
atlas: &mut TextAtlas,
renderer: &mut GpuRenderer,
buffer_layer: usize,
) -> Result<(), GraphicsError> {
let index = text.update(&mut self.swash_cache, atlas, renderer)?;
self.add_buffer_store(renderer, index, buffer_layer);
Ok(())
}
}
pub trait RenderText<'a, 'b>
where
'b: 'a,
{
fn render_text(
&mut self,
renderer: &'b GpuRenderer,
buffer: &'b TextRenderer,
atlas: &'b TextAtlas,
buffer_layer: usize,
);
}
impl<'a, 'b> RenderText<'a, 'b> for wgpu::RenderPass<'a>
where
'b: 'a,
{
fn render_text(
&mut self,
renderer: &'b GpuRenderer,
buffer: &'b TextRenderer,
atlas: &'b TextAtlas,
buffer_layer: usize,
) {
if let Some(Some(details)) = buffer.buffer.buffers.get(buffer_layer)
&& buffer.buffer.count() > 0
{
self.set_buffers(renderer.buffer_object.as_buffer_pass());
self.set_bind_group(1, atlas.text.bind_group(), &[]);
self.set_bind_group(2, atlas.emoji.bind_group(), &[]);
self.set_vertex_buffer(1, buffer.buffer.instances(None));
self.set_pipeline(
renderer.get_pipelines(TextRenderPipeline).unwrap(),
);
self.draw_indexed(
0..StaticVertexBuffer::index_count(),
0,
details.start..details.end,
);
}
}
}