115 lines · 3.8 KB · Rust
RawDownload
use super::atlas::{AllocId, AtlasAllocator};use crate::{    fonts::{FontId, GlyphId},    geometry::{rect::RectI, vector::Vector2I},    platform::{FontSystem, RasterizationOptions},    scene::ImageGlyph,    ImageData,};use anyhow::anyhow;use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};use ordered_float::OrderedFloat;use std::{collections::HashMap, mem, sync::Arc}; #[derive(Hash, Eq, PartialEq)]struct GlyphDescriptor {    font_id: FontId,    font_size: OrderedFloat<f32>,    glyph_id: GlyphId,} pub struct ImageCache {    prev_frame: HashMap<usize, (AllocId, RectI)>,    curr_frame: HashMap<usize, (AllocId, RectI)>,    image_glyphs: HashMap<GlyphDescriptor, Option<(AllocId, RectI, Vector2I)>>,    atlases: AtlasAllocator,    scale_factor: f32,    fonts: Arc<dyn FontSystem>,} impl ImageCache {    pub fn new(        device: metal::Device,        size: Vector2I,        scale_factor: f32,        fonts: Arc<dyn FontSystem>,    ) -> Self {        let descriptor = TextureDescriptor::new();        descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);        descriptor.set_width(size.x() as u64);        descriptor.set_height(size.y() as u64);        Self {            prev_frame: Default::default(),            curr_frame: Default::default(),            image_glyphs: Default::default(),            atlases: AtlasAllocator::new(device, descriptor),            scale_factor,            fonts,        }    }     pub fn set_scale_factor(&mut self, scale_factor: f32) {        if scale_factor != self.scale_factor {            self.scale_factor = scale_factor;            for (_, glyph) in self.image_glyphs.drain() {                if let Some((alloc_id, _, _)) = glyph {                    self.atlases.deallocate(alloc_id);                }            }        }    }     pub fn render(&mut self, image: &ImageData) -> (AllocId, RectI) {        let (alloc_id, atlas_bounds) = self            .prev_frame            .remove(&image.id)            .or_else(|| self.curr_frame.get(&image.id).copied())            .or_else(|| self.atlases.upload(image.size(), image.as_bytes()))            .ok_or_else(|| anyhow!("could not upload image of size {:?}", image.size()))            .unwrap();        self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));        (alloc_id, atlas_bounds)    }     pub fn render_glyph(&mut self, image_glyph: &ImageGlyph) -> Option<(AllocId, RectI, Vector2I)> {        *self            .image_glyphs            .entry(GlyphDescriptor {                font_id: image_glyph.font_id,                font_size: OrderedFloat(image_glyph.font_size),                glyph_id: image_glyph.id,            })            .or_insert_with(|| {                let (glyph_bounds, bytes) = self.fonts.rasterize_glyph(                    image_glyph.font_id,                    image_glyph.font_size,                    image_glyph.id,                    Default::default(),                    self.scale_factor,                    RasterizationOptions::Bgra,                )?;                let (alloc_id, atlas_bounds) = self                    .atlases                    .upload(glyph_bounds.size(), &bytes)                    .ok_or_else(|| {                        anyhow!(                            "could not upload image glyph of size {:?}",                            glyph_bounds.size()                        )                    })                    .unwrap();                Some((alloc_id, atlas_bounds, glyph_bounds.origin()))            })    }     pub fn finish_frame(&mut self) {        mem::swap(&mut self.prev_frame, &mut self.curr_frame);        for (_, (id, _)) in self.curr_frame.drain() {            self.atlases.deallocate(id);        }    }     pub fn atlas_texture(&self, atlas_id: usize) -> Option<&TextureRef> {        self.atlases.texture(atlas_id)    }}