mirror of
https://github.com/penpot/penpot.git
synced 2025-05-13 22:06:37 +02:00
Merge pull request #5410 from penpot/superalex-radial-gradients-wasm-render
🎉 Support for radial gradients with wasm render
This commit is contained in:
commit
33e70a4108
5 changed files with 106 additions and 16 deletions
|
@ -155,19 +155,27 @@
|
||||||
(let [rgba (rgba-from-hex color opacity)]
|
(let [rgba (rgba-from-hex color opacity)]
|
||||||
(h/call internal-module "_add_shape_solid_fill" rgba))
|
(h/call internal-module "_add_shape_solid_fill" rgba))
|
||||||
|
|
||||||
(and (some? gradient) (= (:type gradient) :linear))
|
(some? gradient)
|
||||||
(let [stops (:stops gradient)
|
(let [stops (:stops gradient)
|
||||||
n-stops (count stops)
|
n-stops (count stops)
|
||||||
mem-size (* 5 n-stops)
|
mem-size (* 5 n-stops)
|
||||||
stops-ptr (h/call internal-module "_alloc_bytes" mem-size)
|
stops-ptr (h/call internal-module "_alloc_bytes" mem-size)
|
||||||
heap (gobj/get ^js internal-module "HEAPU8")
|
heap (gobj/get ^js internal-module "HEAPU8")
|
||||||
mem (js/Uint8Array. (.-buffer heap) stops-ptr mem-size)]
|
mem (js/Uint8Array. (.-buffer heap) stops-ptr mem-size)]
|
||||||
(h/call internal-module "_add_shape_linear_fill"
|
(if (= (:type gradient) :linear)
|
||||||
(:start-x gradient)
|
(h/call internal-module "_add_shape_linear_fill"
|
||||||
(:start-y gradient)
|
(:start-x gradient)
|
||||||
(:end-x gradient)
|
(:start-y gradient)
|
||||||
(:end-y gradient)
|
(:end-x gradient)
|
||||||
opacity)
|
(:end-y gradient)
|
||||||
|
opacity)
|
||||||
|
(h/call internal-module "_add_shape_radial_fill"
|
||||||
|
(:start-x gradient)
|
||||||
|
(:start-y gradient)
|
||||||
|
(:end-x gradient)
|
||||||
|
(:end-y gradient)
|
||||||
|
opacity
|
||||||
|
(:width gradient)))
|
||||||
(.set mem (js/Uint8Array. (clj->js (flatten (map (fn [stop]
|
(.set mem (js/Uint8Array. (clj->js (flatten (map (fn [stop]
|
||||||
(let [[r g b a] (rgba-bytes-from-hex (:color stop) (:opacity stop))
|
(let [[r g b a] (rgba-bytes-from-hex (:color stop) (:opacity stop))
|
||||||
offset (:offset stop)]
|
offset (:offset stop)]
|
||||||
|
|
|
@ -183,6 +183,26 @@ pub extern "C" fn add_shape_linear_fill(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn add_shape_radial_fill(
|
||||||
|
start_x: f32,
|
||||||
|
start_y: f32,
|
||||||
|
end_x: f32,
|
||||||
|
end_y: f32,
|
||||||
|
opacity: f32,
|
||||||
|
width: f32,
|
||||||
|
) {
|
||||||
|
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
||||||
|
if let Some(shape) = state.current_shape() {
|
||||||
|
shape.add_fill(shapes::Fill::new_radial_gradient(
|
||||||
|
(start_x, start_y),
|
||||||
|
(end_x, end_y),
|
||||||
|
opacity,
|
||||||
|
width,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn add_shape_fill_stops(ptr: *mut shapes::RawStopData, n_stops: u32) {
|
pub extern "C" fn add_shape_fill_stops(ptr: *mut shapes::RawStopData, n_stops: u32) {
|
||||||
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
||||||
|
|
|
@ -198,13 +198,11 @@ impl RenderState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_single_shape(&mut self, shape: &Shape) {
|
pub fn render_single_shape(&mut self, shape: &Shape) {
|
||||||
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
let mut transform = skia::Matrix::new_identity();
|
||||||
let mut matrix = skia::Matrix::new_identity();
|
|
||||||
let (translate_x, translate_y) = shape.translation();
|
let (translate_x, translate_y) = shape.translation();
|
||||||
let (scale_x, scale_y) = shape.scale();
|
let (scale_x, scale_y) = shape.scale();
|
||||||
let (skew_x, skew_y) = shape.skew();
|
let (skew_x, skew_y) = shape.skew();
|
||||||
|
transform.set_all(
|
||||||
matrix.set_all(
|
|
||||||
scale_x,
|
scale_x,
|
||||||
skew_x,
|
skew_x,
|
||||||
translate_x,
|
translate_x,
|
||||||
|
@ -216,10 +214,12 @@ impl RenderState {
|
||||||
1.,
|
1.,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut center = shape.selrect.center();
|
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
||||||
matrix.post_translate(center);
|
let center = shape.selrect.center();
|
||||||
center.negate();
|
let mut matrix = skia::Matrix::new_identity();
|
||||||
matrix.pre_translate(center);
|
matrix.pre_translate(center);
|
||||||
|
matrix.pre_concat(&transform);
|
||||||
|
matrix.pre_translate(-center);
|
||||||
|
|
||||||
self.drawing_surface.canvas().concat(&matrix);
|
self.drawing_surface.canvas().concat(&matrix);
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ impl Shape {
|
||||||
let fill = self.fills.last_mut().ok_or("Shape has no fills")?;
|
let fill = self.fills.last_mut().ok_or("Shape has no fills")?;
|
||||||
let gradient = match fill {
|
let gradient = match fill {
|
||||||
Fill::LinearGradient(g) => Ok(g),
|
Fill::LinearGradient(g) => Ok(g),
|
||||||
|
Fill::RadialGradient(g) => Ok(g),
|
||||||
_ => Err("Active fill is not a gradient"),
|
_ => Err("Active fill is not a gradient"),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub struct Gradient {
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
start: (f32, f32),
|
start: (f32, f32),
|
||||||
end: (f32, f32),
|
end: (f32, f32),
|
||||||
|
width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gradient {
|
impl Gradient {
|
||||||
|
@ -36,7 +37,7 @@ impl Gradient {
|
||||||
self.offsets.push(offset);
|
self.offsets.push(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_shader(&self, rect: &math::Rect) -> skia::Shader {
|
fn to_linear_shader(&self, rect: &math::Rect) -> skia::Shader {
|
||||||
let start = (
|
let start = (
|
||||||
rect.left + self.start.0 * rect.width(),
|
rect.left + self.start.0 * rect.width(),
|
||||||
rect.top + self.start.1 * rect.height(),
|
rect.top + self.start.1 * rect.height(),
|
||||||
|
@ -56,6 +57,41 @@ impl Gradient {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
shader
|
shader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_radial_shader(&self, rect: &math::Rect) -> skia::Shader {
|
||||||
|
let center = skia::Point::new(
|
||||||
|
rect.left + self.start.0 * rect.width(),
|
||||||
|
rect.top + self.start.1 * rect.height(),
|
||||||
|
);
|
||||||
|
let end = skia::Point::new(
|
||||||
|
rect.left + self.end.0 * rect.width(),
|
||||||
|
rect.top + self.end.1 * rect.height(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let direction = end - center;
|
||||||
|
let distance = (direction.x.powi(2) + direction.y.powi(2)).sqrt();
|
||||||
|
let angle = direction.y.atan2(direction.x).to_degrees();
|
||||||
|
|
||||||
|
// Based on the code from frontend/src/app/main/ui/shapes/gradients.cljs
|
||||||
|
let mut transform = skia::Matrix::new_identity();
|
||||||
|
transform.pre_translate((center.x, center.y));
|
||||||
|
transform.pre_rotate(angle + 90., skia::Point::new(0., 0.));
|
||||||
|
// We need an extra transform, because in skia radial gradients are circular and we need them to be ellipses if they must adapt to the shape
|
||||||
|
transform.pre_scale((self.width * rect.width() / rect.height(), 1.), None);
|
||||||
|
transform.pre_translate((-center.x, -center.y));
|
||||||
|
|
||||||
|
let shader = skia::shader::Shader::radial_gradient(
|
||||||
|
center,
|
||||||
|
distance,
|
||||||
|
self.colors.as_slice(),
|
||||||
|
self.offsets.as_slice(),
|
||||||
|
skia::TileMode::Clamp,
|
||||||
|
None,
|
||||||
|
Some(&transform),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
shader
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -80,6 +116,7 @@ impl ImageFill {
|
||||||
pub enum Fill {
|
pub enum Fill {
|
||||||
Solid(Color),
|
Solid(Color),
|
||||||
LinearGradient(Gradient),
|
LinearGradient(Gradient),
|
||||||
|
RadialGradient(Gradient),
|
||||||
Image(ImageFill),
|
Image(ImageFill),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +128,22 @@ impl Fill {
|
||||||
opacity,
|
opacity,
|
||||||
colors: vec![],
|
colors: vec![],
|
||||||
offsets: vec![],
|
offsets: vec![],
|
||||||
|
width: 0.,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn new_radial_gradient(
|
||||||
|
start: (f32, f32),
|
||||||
|
end: (f32, f32),
|
||||||
|
opacity: f32,
|
||||||
|
width: f32,
|
||||||
|
) -> Self {
|
||||||
|
Self::RadialGradient(Gradient {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
opacity,
|
||||||
|
colors: vec![],
|
||||||
|
offsets: vec![],
|
||||||
|
width,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +168,15 @@ impl Fill {
|
||||||
}
|
}
|
||||||
Self::LinearGradient(gradient) => {
|
Self::LinearGradient(gradient) => {
|
||||||
let mut p = skia::Paint::default();
|
let mut p = skia::Paint::default();
|
||||||
p.set_shader(gradient.to_shader(&rect));
|
p.set_shader(gradient.to_linear_shader(&rect));
|
||||||
|
p.set_alpha((gradient.opacity * 255.) as u8);
|
||||||
|
p.set_style(skia::PaintStyle::Fill);
|
||||||
|
p.set_blend_mode(skia::BlendMode::SrcOver);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
Self::RadialGradient(gradient) => {
|
||||||
|
let mut p = skia::Paint::default();
|
||||||
|
p.set_shader(gradient.to_radial_shader(&rect));
|
||||||
p.set_alpha((gradient.opacity * 255.) as u8);
|
p.set_alpha((gradient.opacity * 255.) as u8);
|
||||||
p.set_style(skia::PaintStyle::Fill);
|
p.set_style(skia::PaintStyle::Fill);
|
||||||
p.set_blend_mode(skia::BlendMode::SrcOver);
|
p.set_blend_mode(skia::BlendMode::SrcOver);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue