🐛 Fix asynchronous content rendering

This commit is contained in:
Alejandro Alonso 2025-06-17 08:11:27 +02:00
parent d0425cabda
commit d71fa659d5
12 changed files with 166 additions and 100 deletions

View file

@ -1,6 +1,7 @@
use skia_safe::gpu::{self, gl::FramebufferInfo, gl::TextureInfo, DirectContext};
use skia_safe::{self as skia, ISize};
#[derive(Debug, Clone)]
pub struct GpuState {
pub context: DirectContext,
framebuffer_info: FramebufferInfo,

View file

@ -7,60 +7,86 @@ use std::collections::HashMap;
pub type Image = skia::Image;
enum StoredImage {
Raw(Vec<u8>),
Gpu(Image),
}
pub struct ImageStore {
images: HashMap<Uuid, Image>,
images: HashMap<Uuid, StoredImage>,
context: Box<DirectContext>,
}
impl ImageStore {
pub fn new() -> Self {
pub fn new(context: DirectContext) -> Self {
Self {
images: HashMap::with_capacity(2048),
context: Box::new(context),
}
}
pub fn add(
&mut self,
id: Uuid,
image_data: &[u8],
context: &mut DirectContext,
) -> Result<(), String> {
let image_data = unsafe { skia::Data::new_bytes(image_data) };
let image = Image::from_encoded(image_data).ok_or("Error decoding image data")?;
pub fn add(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
if self.images.contains_key(&id) {
return Err("Image already exists".to_string());
}
let width = image.width();
let height = image.height();
let image_info = skia::ImageInfo::new_n32_premul((width, height), None);
let mut surface = surfaces::render_target(
context,
Budgeted::Yes,
&image_info,
None,
None,
None,
None,
false,
)
.ok_or("Can't create GPU surface")?;
let dest_rect = MathRect::from_xywh(0.0, 0.0, width as f32, height as f32);
surface
.canvas()
.draw_image_rect(&image, None, dest_rect, &skia::Paint::default());
let gpu_image = surface.image_snapshot();
// This way we store the image as a texture
self.images.insert(id, gpu_image);
self.images
.insert(id, StoredImage::Raw(image_data.to_vec()));
Ok(())
}
pub fn contains(&mut self, id: &Uuid) -> bool {
pub fn contains(&self, id: &Uuid) -> bool {
self.images.contains_key(id)
}
pub fn get(&self, id: &Uuid) -> Option<&Image> {
self.images.get(id)
pub fn get(&mut self, id: &Uuid) -> Option<&Image> {
// Use entry API to mutate the HashMap in-place if needed
if let Some(entry) = self.images.get_mut(id) {
match entry {
StoredImage::Gpu(ref img) => Some(img),
StoredImage::Raw(raw_data) => {
// Decode and upload to GPU
let data = unsafe { skia::Data::new_bytes(raw_data) };
let image = Image::from_encoded(data)?;
let width = image.width();
let height = image.height();
let image_info = skia::ImageInfo::new_n32_premul((width, height), None);
let mut surface = surfaces::render_target(
&mut self.context,
Budgeted::Yes,
&image_info,
None,
None,
None,
None,
false,
)?;
let dest_rect = MathRect::from_xywh(0.0, 0.0, width as f32, height as f32);
surface.canvas().draw_image_rect(
&image,
None,
dest_rect,
&skia::Paint::default(),
);
let gpu_image = surface.image_snapshot();
// Replace raw data with GPU image
*entry = StoredImage::Gpu(gpu_image);
if let StoredImage::Gpu(ref img) = entry {
Some(img)
} else {
None
}
}
}
} else {
None
}
}
}

View file

@ -388,13 +388,13 @@ fn draw_image_stroke_in_container(
image_fill: &ImageFill,
antialias: bool,
) {
let scale = render_state.get_scale();
let image = render_state.images.get(&image_fill.id());
if image.is_none() {
return;
}
let size = image_fill.size();
let scale = render_state.get_scale();
let canvas = render_state.surfaces.canvas(SurfaceId::Strokes);
let container = &shape.selrect;
let path_transform = shape.to_path_transform();