mirror of
https://github.com/penpot/penpot.git
synced 2025-07-16 11:05:20 +02:00
🐛 Fix asynchronous content rendering
This commit is contained in:
parent
d0425cabda
commit
d71fa659d5
12 changed files with 166 additions and 100 deletions
|
@ -290,17 +290,31 @@ pub extern "C" fn set_children() {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn store_image(a: u32, b: u32, c: u32, d: u32) {
|
||||
pub extern "C" fn store_image(
|
||||
a1: u32,
|
||||
b1: u32,
|
||||
c1: u32,
|
||||
d1: u32,
|
||||
a2: u32,
|
||||
b2: u32,
|
||||
c2: u32,
|
||||
d2: u32,
|
||||
) {
|
||||
with_state!(state, {
|
||||
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||
let image_id = uuid_from_u32_quartet(a2, b2, c2, d2);
|
||||
let image_bytes = mem::bytes();
|
||||
|
||||
if let Err(msg) = state.render_state().add_image(id, &image_bytes) {
|
||||
if let Err(msg) = state.render_state().add_image(image_id, &image_bytes) {
|
||||
eprintln!("{}", msg);
|
||||
}
|
||||
|
||||
mem::free_bytes();
|
||||
});
|
||||
|
||||
with_state!(state, {
|
||||
let shape_id = uuid_from_u32_quartet(a1, b1, c1, d1);
|
||||
state.update_tile_for_shape(shape_id);
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
|
@ -220,14 +220,14 @@ impl RenderState {
|
|||
let tiles = tiles::TileHashMap::new();
|
||||
|
||||
RenderState {
|
||||
gpu_state,
|
||||
gpu_state: gpu_state.clone(),
|
||||
options: RenderOptions::default(),
|
||||
surfaces,
|
||||
fonts,
|
||||
viewbox,
|
||||
cached_viewbox: Viewbox::new(0., 0.),
|
||||
cached_target_snapshot: None,
|
||||
images: ImageStore::new(),
|
||||
images: ImageStore::new(gpu_state.context.clone()),
|
||||
background_color: skia::Color::TRANSPARENT,
|
||||
render_request_id: None,
|
||||
render_in_progress: false,
|
||||
|
@ -257,7 +257,7 @@ impl RenderState {
|
|||
}
|
||||
|
||||
pub fn add_image(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
|
||||
self.images.add(id, image_data, &mut self.gpu_state.context)
|
||||
self.images.add(id, image_data)
|
||||
}
|
||||
|
||||
pub fn has_image(&mut self, id: &Uuid) -> bool {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -191,6 +191,12 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_tile_for_shape(&mut self, shape_id: Uuid) {
|
||||
if let Some(shape) = self.shapes.get_mut(&shape_id) {
|
||||
self.render_state.update_tile_for(shape);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_tile_for_current_shape(&mut self) {
|
||||
match self.current_shape.as_mut() {
|
||||
Some(shape) => {
|
||||
|
|
|
@ -7,17 +7,21 @@ use crate::shapes::FontFamily;
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn store_font(
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
d: u32,
|
||||
a1: u32,
|
||||
b1: u32,
|
||||
c1: u32,
|
||||
d1: u32,
|
||||
a2: u32,
|
||||
b2: u32,
|
||||
c2: u32,
|
||||
d2: u32,
|
||||
weight: u32,
|
||||
style: u8,
|
||||
is_emoji: bool,
|
||||
is_fallback: bool,
|
||||
) {
|
||||
with_state!(state, {
|
||||
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||
let id = uuid_from_u32_quartet(a2, b2, c2, d2);
|
||||
let font_bytes = mem::bytes();
|
||||
|
||||
let family = FontFamily::new(id, weight, style.into());
|
||||
|
@ -28,6 +32,11 @@ pub extern "C" fn store_font(
|
|||
|
||||
mem::free_bytes();
|
||||
});
|
||||
|
||||
with_state!(state, {
|
||||
let shape_id = uuid_from_u32_quartet(a1, b1, c1, d1);
|
||||
state.update_tile_for_shape(shape_id);
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue