🎉 Render wasm fill images

This commit is contained in:
Alejandro Alonso 2024-11-25 17:30:24 +01:00
parent c688ae2e33
commit 0a3c6f38ef
7 changed files with 237 additions and 38 deletions

View file

@ -1,3 +0,0 @@
use skia_safe as skia;
pub type Image = skia::Image;

View file

@ -1,5 +1,4 @@
mod debug;
mod images;
mod math;
pub mod mem;
mod render;
@ -10,6 +9,7 @@ mod view;
use skia_safe as skia;
use crate::shapes::Image;
use crate::state::State;
use crate::utils::uuid_from_u32_quartet;
@ -208,6 +208,59 @@ pub extern "C" fn add_shape_fill_stops(ptr: *mut RawStopData, n_stops: i32) {
}
}
#[no_mangle]
pub extern "C" fn store_image(a: u32, b: u32, c: u32, d: u32, ptr: *mut u8, size: u32) {
if ptr.is_null() || size == 0 {
panic!("Invalid data, null pointer or zero size");
}
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let render_state = state.render_state();
let id = uuid_from_u32_quartet(a, b, c, d);
unsafe {
let image_bytes = Vec::<u8>::from_raw_parts(ptr, size as usize, size as usize);
let image_data = skia::Data::new_copy(&*image_bytes);
match Image::from_encoded(image_data) {
Some(image) => {
render_state.images.insert(id.to_string(), image);
}
None => {
eprintln!("Error on image decoding");
}
}
mem::free(ptr as *mut u8, size as usize * std::mem::size_of::<u8>());
}
}
#[no_mangle]
pub extern "C" fn is_image_cached(a: u32, b: u32, c: u32, d: u32) -> bool {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let render_state = state.render_state();
let id = uuid_from_u32_quartet(a, b, c, d);
render_state.images.contains_key(&id.to_string())
}
#[no_mangle]
pub extern "C" fn add_shape_image_fill(
a: u32,
b: u32,
c: u32,
d: u32,
alpha: f32,
width: f32,
height: f32,
) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let id = uuid_from_u32_quartet(a, b, c, d);
if let Some(shape) = state.current_shape() {
shape.add_fill(shapes::Fill::new_image_fill(
id,
(alpha * 0xff as f32).floor() as u8,
height,
width,
));
}
}
#[no_mangle]
pub extern "C" fn clear_shape_fills() {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");

View file

@ -5,8 +5,9 @@ use std::collections::HashMap;
use uuid::Uuid;
use crate::debug;
use crate::images::Image;
use crate::shapes::Fill;
use crate::shapes::Shape;
use crate::shapes::{draw_image_in_container, Image};
use crate::view::Viewbox;
struct GpuState {
@ -97,6 +98,7 @@ pub(crate) struct RenderState {
pub cached_surface_image: Option<CachedSurfaceImage>,
options: RenderOptions,
pub viewbox: Viewbox,
pub images: HashMap<String, Image>,
}
impl RenderState {
@ -119,6 +121,7 @@ impl RenderState {
cached_surface_image: None,
options: RenderOptions::default(),
viewbox: Viewbox::new(width as f32, height as f32),
images: HashMap::with_capacity(2048),
}
}
@ -210,9 +213,22 @@ impl RenderState {
self.drawing_surface.canvas().concat(&matrix);
for fill in shape.fills().rev() {
self.drawing_surface
.canvas()
.draw_rect(shape.selrect, &fill.to_paint(&shape.selrect));
if let Fill::Image(image_fill) = fill {
let image = self.images.get(&image_fill.id.to_string());
if let Some(image) = image {
draw_image_in_container(
&self.drawing_surface.canvas(),
&image,
(image_fill.width, image_fill.height),
shape.selrect,
&fill.to_paint(&shape.selrect),
);
}
} else {
self.drawing_surface
.canvas()
.draw_rect(shape.selrect, &fill.to_paint(&shape.selrect));
}
}
let mut paint = skia::Paint::default();

View file

@ -4,8 +4,10 @@ use uuid::Uuid;
mod blend;
mod fills;
mod images;
pub use blend::*;
pub use fills::*;
pub use images::*;
#[derive(Debug, Clone, Copy)]
pub enum Kind {

View file

@ -2,6 +2,7 @@ use skia_safe as skia;
use super::Color;
use crate::math;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq)]
pub struct Gradient {
@ -40,10 +41,19 @@ impl Gradient {
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImageFill {
pub id: Uuid,
pub alpha: u8,
pub height: f32,
pub width: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Fill {
Solid(Color),
LinearGradient(Gradient),
Image(ImageFill),
}
impl Fill {
@ -57,6 +67,15 @@ impl Fill {
})
}
pub fn new_image_fill(id: Uuid, alpha: u8, height: f32, width: f32) -> Self {
Self::Image(ImageFill {
id,
alpha,
height,
width,
})
}
pub fn to_paint(&self, rect: &math::Rect) -> skia::Paint {
match self {
Self::Solid(color) => {
@ -75,6 +94,14 @@ impl Fill {
p.set_blend_mode(skia::BlendMode::SrcOver);
p
}
Self::Image(image_fill) => {
let mut p = skia::Paint::default();
p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true);
p.set_blend_mode(skia::BlendMode::SrcOver);
p.set_alpha(image_fill.alpha);
p
}
}
}
}

View file

@ -0,0 +1,52 @@
use crate::math;
use skia_safe as skia;
pub type Image = skia::Image;
pub fn draw_image_in_container(
canvas: &skia::Canvas,
image: &Image,
size: (f32, f32),
container: skia::Rect,
paint: &skia::Paint,
) {
let width = size.0;
let height = size.1;
let image_aspect_ratio = width / height;
// Container size
let container_width = container.width();
let container_height = container.height();
let container_aspect_ratio = container_width / container_height;
// Calculate scale to ensure the image covers the container
let scale = if image_aspect_ratio > container_aspect_ratio {
// Image is widther, scale based on height to cover container
container_height / height
} else {
// Image is taller, scale based on width to cover container
container_width / width
};
// Scaled size of the image
let scaled_width = width * scale;
let scaled_height = height * scale;
// Calculate offset to center the image in the container
let offset_x = container.left + (container_width - scaled_width) / 2.0;
let offset_y = container.top + (container_height - scaled_height) / 2.0;
let dest_rect = math::Rect::from_xywh(offset_x, offset_y, scaled_width, scaled_height);
// Save the current canvas state
canvas.save();
// Set the clipping rectangle to the container bounds
canvas.clip_rect(container, skia::ClipOp::Intersect, true);
// Draw the image with the calculated destination rectangle
canvas.draw_image_rect(image, None, dest_rect, &paint);
// Restore the canvas to remove the clipping
canvas.restore();
}