mirror of
https://github.com/penpot/penpot.git
synced 2025-01-06 14:50:20 -05: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)]
|
||||
(h/call internal-module "_add_shape_solid_fill" rgba))
|
||||
|
||||
(and (some? gradient) (= (:type gradient) :linear))
|
||||
(some? gradient)
|
||||
(let [stops (:stops gradient)
|
||||
n-stops (count stops)
|
||||
mem-size (* 5 n-stops)
|
||||
stops-ptr (h/call internal-module "_alloc_bytes" mem-size)
|
||||
heap (gobj/get ^js internal-module "HEAPU8")
|
||||
mem (js/Uint8Array. (.-buffer heap) stops-ptr mem-size)]
|
||||
(h/call internal-module "_add_shape_linear_fill"
|
||||
(:start-x gradient)
|
||||
(:start-y gradient)
|
||||
(:end-x gradient)
|
||||
(:end-y gradient)
|
||||
opacity)
|
||||
(if (= (:type gradient) :linear)
|
||||
(h/call internal-module "_add_shape_linear_fill"
|
||||
(:start-x gradient)
|
||||
(:start-y gradient)
|
||||
(:end-x gradient)
|
||||
(: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]
|
||||
(let [[r g b a] (rgba-bytes-from-hex (:color stop) (:opacity 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]
|
||||
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");
|
||||
|
|
|
@ -198,13 +198,11 @@ impl RenderState {
|
|||
}
|
||||
|
||||
pub fn render_single_shape(&mut self, shape: &Shape) {
|
||||
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
let mut transform = skia::Matrix::new_identity();
|
||||
let (translate_x, translate_y) = shape.translation();
|
||||
let (scale_x, scale_y) = shape.scale();
|
||||
let (skew_x, skew_y) = shape.skew();
|
||||
|
||||
matrix.set_all(
|
||||
transform.set_all(
|
||||
scale_x,
|
||||
skew_x,
|
||||
translate_x,
|
||||
|
@ -216,10 +214,12 @@ impl RenderState {
|
|||
1.,
|
||||
);
|
||||
|
||||
let mut center = shape.selrect.center();
|
||||
matrix.post_translate(center);
|
||||
center.negate();
|
||||
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
||||
let center = shape.selrect.center();
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_translate(center);
|
||||
matrix.pre_concat(&transform);
|
||||
matrix.pre_translate(-center);
|
||||
|
||||
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 gradient = match fill {
|
||||
Fill::LinearGradient(g) => Ok(g),
|
||||
Fill::RadialGradient(g) => Ok(g),
|
||||
_ => Err("Active fill is not a gradient"),
|
||||
}?;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ pub struct Gradient {
|
|||
opacity: f32,
|
||||
start: (f32, f32),
|
||||
end: (f32, f32),
|
||||
width: f32,
|
||||
}
|
||||
|
||||
impl Gradient {
|
||||
|
@ -36,7 +37,7 @@ impl Gradient {
|
|||
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 = (
|
||||
rect.left + self.start.0 * rect.width(),
|
||||
rect.top + self.start.1 * rect.height(),
|
||||
|
@ -56,6 +57,41 @@ impl Gradient {
|
|||
.unwrap();
|
||||
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)]
|
||||
|
@ -80,6 +116,7 @@ impl ImageFill {
|
|||
pub enum Fill {
|
||||
Solid(Color),
|
||||
LinearGradient(Gradient),
|
||||
RadialGradient(Gradient),
|
||||
Image(ImageFill),
|
||||
}
|
||||
|
||||
|
@ -91,6 +128,22 @@ impl Fill {
|
|||
opacity,
|
||||
colors: 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) => {
|
||||
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_style(skia::PaintStyle::Fill);
|
||||
p.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
|
Loading…
Reference in a new issue