mirror of
https://github.com/penpot/penpot.git
synced 2025-05-13 17:56:38 +02:00
🎉 Render liner gradient fills
This commit is contained in:
parent
8fa334265c
commit
c8e322cd58
4 changed files with 116 additions and 15 deletions
|
@ -92,14 +92,29 @@
|
||||||
[fills]
|
[fills]
|
||||||
(h/call internal-module "_clear_shape_fills")
|
(h/call internal-module "_clear_shape_fills")
|
||||||
(run! (fn [fill]
|
(run! (fn [fill]
|
||||||
(let [opacity (:fill-opacity fill)
|
(let [opacity (or (:fill-opacity fill) 1.0)
|
||||||
color (:fill-color fill)]
|
color (:fill-color fill)
|
||||||
|
gradient (:fill-color-gradient fill)]
|
||||||
(when ^boolean color
|
(when ^boolean color
|
||||||
(let [rgb (js/parseInt (subs color 1) 16)
|
(let [rgb (js/parseInt (subs color 1) 16)
|
||||||
r (bit-shift-right rgb 16)
|
r (bit-shift-right rgb 16)
|
||||||
g (bit-and (bit-shift-right rgb 8) 255)
|
g (bit-and (bit-shift-right rgb 8) 255)
|
||||||
b (bit-and rgb 255)]
|
b (bit-and rgb 255)]
|
||||||
(h/call internal-module "_add_shape_solid_fill" r g b opacity)))))
|
(h/call internal-module "_add_shape_solid_fill" r g b opacity)))
|
||||||
|
(when (and (some? gradient) (= (:type gradient) :linear))
|
||||||
|
(h/call internal-module "_add_shape_linear_fill"
|
||||||
|
(:start-x gradient)
|
||||||
|
(:start-y gradient)
|
||||||
|
(:end-x gradient)
|
||||||
|
(:end-y gradient)
|
||||||
|
opacity)
|
||||||
|
(run! (fn [stop]
|
||||||
|
(let [rgb (js/parseInt (subs (:color stop) 1) 16)
|
||||||
|
r (bit-shift-right rgb 16)
|
||||||
|
g (bit-and (bit-shift-right rgb 8) 255)
|
||||||
|
b (bit-and rgb 255)
|
||||||
|
a (:opacity stop)]
|
||||||
|
(h/call internal-module "_add_shape_fill_stop" r g b a (:offset stop)))) (:stops gradient)))))
|
||||||
fills))
|
fills))
|
||||||
|
|
||||||
(defn- translate-blend-mode
|
(defn- translate-blend-mode
|
||||||
|
|
|
@ -159,6 +159,36 @@ pub extern "C" fn add_shape_solid_fill(r: u8, g: u8, b: u8, a: f32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn add_shape_linear_fill(
|
||||||
|
start_x: f32,
|
||||||
|
start_y: f32,
|
||||||
|
end_x: f32,
|
||||||
|
end_y: f32,
|
||||||
|
opacity: 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_linear_gradient(
|
||||||
|
(start_x, start_y),
|
||||||
|
(end_x, end_y),
|
||||||
|
opacity,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn add_shape_fill_stop(r: u8, g: u8, b: u8, a: f32, offset: f32) {
|
||||||
|
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
||||||
|
if let Some(shape) = state.current_shape() {
|
||||||
|
let alpha: u8 = (a * 0xff as f32).floor() as u8;
|
||||||
|
let color = skia::Color::from_argb(alpha, r, g, b);
|
||||||
|
shape
|
||||||
|
.add_gradient_stop(color, offset)
|
||||||
|
.expect("got no fill or an invalid one");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn clear_shape_fills() {
|
pub extern "C" fn clear_shape_fills() {
|
||||||
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
||||||
|
|
|
@ -212,7 +212,7 @@ impl RenderState {
|
||||||
for fill in shape.fills().rev() {
|
for fill in shape.fills().rev() {
|
||||||
self.drawing_surface
|
self.drawing_surface
|
||||||
.canvas()
|
.canvas()
|
||||||
.draw_rect(shape.selrect, &fill.to_paint());
|
.draw_rect(shape.selrect, &fill.to_paint(&shape.selrect));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut paint = skia::Paint::default();
|
let mut paint = skia::Paint::default();
|
||||||
|
|
|
@ -4,16 +4,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
None,
|
|
||||||
Text,
|
|
||||||
Path,
|
|
||||||
SVGRaw,
|
|
||||||
Image,
|
|
||||||
Circle,
|
|
||||||
Rect,
|
Rect,
|
||||||
Bool,
|
|
||||||
Group,
|
|
||||||
Frame,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Color = skia::Color;
|
type Color = skia::Color;
|
||||||
|
@ -41,9 +32,42 @@ impl Matrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Gradient {
|
||||||
|
colors: Vec<Color>,
|
||||||
|
offsets: Vec<f32>,
|
||||||
|
opacity: f32,
|
||||||
|
start: (f32, f32),
|
||||||
|
end: (f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gradient {
|
||||||
|
fn to_shader(&self, rect: &math::Rect) -> skia::Shader {
|
||||||
|
let start = (
|
||||||
|
rect.left + self.start.0 * rect.width(),
|
||||||
|
rect.top + self.start.1 * rect.height(),
|
||||||
|
);
|
||||||
|
let end = (
|
||||||
|
rect.left + self.end.0 * rect.width(),
|
||||||
|
rect.top + self.end.1 * rect.height(),
|
||||||
|
);
|
||||||
|
let shader = skia::shader::Shader::linear_gradient(
|
||||||
|
(start, end),
|
||||||
|
self.colors.as_slice(),
|
||||||
|
self.offsets.as_slice(),
|
||||||
|
skia::TileMode::Clamp,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
shader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Fill {
|
pub enum Fill {
|
||||||
Solid(Color), // TODO: add more fills here
|
Solid(Color),
|
||||||
|
LinearGradient(Gradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for Fill {
|
impl From<Color> for Fill {
|
||||||
|
@ -53,7 +77,17 @@ impl From<Color> for Fill {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fill {
|
impl Fill {
|
||||||
pub fn to_paint(&self) -> skia::Paint {
|
pub fn new_linear_gradient(start: (f32, f32), end: (f32, f32), opacity: f32) -> Self {
|
||||||
|
Self::LinearGradient(Gradient {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
opacity,
|
||||||
|
colors: vec![],
|
||||||
|
offsets: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_paint(&self, rect: &math::Rect) -> skia::Paint {
|
||||||
match self {
|
match self {
|
||||||
Self::Solid(color) => {
|
Self::Solid(color) => {
|
||||||
let mut p = skia::Paint::default();
|
let mut p = skia::Paint::default();
|
||||||
|
@ -63,6 +97,14 @@ impl Fill {
|
||||||
p.set_blend_mode(skia::BlendMode::SrcOver);
|
p.set_blend_mode(skia::BlendMode::SrcOver);
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
Self::LinearGradient(gradient) => {
|
||||||
|
let mut p = skia::Paint::default();
|
||||||
|
p.set_shader(gradient.to_shader(&rect));
|
||||||
|
p.set_alpha((gradient.opacity * 255.) as u8);
|
||||||
|
p.set_style(skia::PaintStyle::Fill);
|
||||||
|
p.set_blend_mode(skia::BlendMode::SrcOver);
|
||||||
|
p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +137,7 @@ impl Into<skia::BlendMode> for BlendMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct Shape {
|
pub struct Shape {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub children: Vec<Uuid>,
|
pub children: Vec<Uuid>,
|
||||||
|
@ -146,6 +189,19 @@ impl Shape {
|
||||||
self.fills.clear();
|
self.fills.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_gradient_stop(&mut self, color: skia::Color, offset: f32) -> Result<(), String> {
|
||||||
|
let fill = self.fills.last_mut().ok_or("Shape has no fills")?;
|
||||||
|
let gradient = match fill {
|
||||||
|
Fill::LinearGradient(g) => Ok(g),
|
||||||
|
_ => Err("Active fill is not a gradient"),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
gradient.colors.push(color);
|
||||||
|
gradient.offsets.push(offset);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_blend_mode(&mut self, mode: BlendMode) {
|
pub fn set_blend_mode(&mut self, mode: BlendMode) {
|
||||||
self.blend_mode = mode;
|
self.blend_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue