0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-22 14:39:45 -05:00

🎉 Render path fills

This commit is contained in:
Belén Albeza 2024-12-09 15:26:51 +01:00
parent 0bfcc1f854
commit 99bb3ee962
6 changed files with 118 additions and 90 deletions

View file

@ -296,9 +296,9 @@
(defn- debug-flags (defn- debug-flags
[] []
(cond-> 0 (cond-> 0
(dbg/enabled? :wasm-viewbox) (dbg/enabled? :wasm-viewbox)
(bit-or 2r00000000000000000000000000000001))) (bit-or 2r00000000000000000000000000000001)))
(defn assign-canvas (defn assign-canvas
[canvas] [canvas]

View file

@ -3,70 +3,70 @@
(def command-size 28) (def command-size 28)
#_(defn content->buffer #_(defn content->buffer
"Converts the path content into binary format." "Converts the path content into binary format."
[content] [content]
(let [total (count content) (let [total (count content)
buffer (new js/ArrayBuffer (* total command-size)) buffer (new js/ArrayBuffer (* total command-size))
dview (new js/DataView buffer)] dview (new js/DataView buffer)]
(loop [index 0] (loop [index 0]
(when (< index total) (when (< index total)
(let [segment (nth content index) (let [segment (nth content index)
offset (* index command-size)] offset (* index command-size)]
(case (:command segment) (case (:command segment)
:move-to :move-to
(let [{:keys [x y]} (:params segment)] (let [{:keys [x y]} (:params segment)]
(.setUint16 dview (+ offset 0) 1) (.setUint16 dview (+ offset 0) 1)
(.setFloat32 dview (+ offset 20) x) (.setFloat32 dview (+ offset 20) x)
(.setFloat32 dview (+ offset 24) y)) (.setFloat32 dview (+ offset 24) y))
:line-to :line-to
(let [{:keys [x y]} (:params segment)] (let [{:keys [x y]} (:params segment)]
(.setUint16 dview (+ offset 0) 2) (.setUint16 dview (+ offset 0) 2)
(.setFloat32 dview (+ offset 20) x) (.setFloat32 dview (+ offset 20) x)
(.setFloat32 dview (+ offset 24) y)) (.setFloat32 dview (+ offset 24) y))
:curve-to :curve-to
(let [{:keys [c1x c1y c2x c2y x y]} (:params segment)] (let [{:keys [c1x c1y c2x c2y x y]} (:params segment)]
(.setUint16 dview (+ offset 0) 3) (.setUint16 dview (+ offset 0) 3)
(.setFloat32 dview (+ offset 4) c1x) (.setFloat32 dview (+ offset 4) c1x)
(.setFloat32 dview (+ offset 8) c1y) (.setFloat32 dview (+ offset 8) c1y)
(.setFloat32 dview (+ offset 12) c2x) (.setFloat32 dview (+ offset 12) c2x)
(.setFloat32 dview (+ offset 16) c2y) (.setFloat32 dview (+ offset 16) c2y)
(.setFloat32 dview (+ offset 20) x) (.setFloat32 dview (+ offset 20) x)
(.setFloat32 dview (+ offset 24) y)) (.setFloat32 dview (+ offset 24) y))
:close-path :close-path
(.setUint16 dview (+ offset 0) 4)) (.setUint16 dview (+ offset 0) 4))
(recur (inc index))))) (recur (inc index)))))
buffer)) buffer))
#_(defn buffer->content #_(defn buffer->content
"Converts the a buffer to a path content vector" "Converts the a buffer to a path content vector"
[buffer] [buffer]
(assert (instance? js/ArrayBuffer buffer) "expected ArrayBuffer instance") (assert (instance? js/ArrayBuffer buffer) "expected ArrayBuffer instance")
(let [total (/ (.-byteLength buffer) command-size) (let [total (/ (.-byteLength buffer) command-size)
dview (new js/DataView buffer)] dview (new js/DataView buffer)]
(loop [index 0 (loop [index 0
result []] result []]
(if (< index total) (if (< index total)
(let [offset (* index command-size) (let [offset (* index command-size)
type (.getUint16 dview (+ offset 0)) type (.getUint16 dview (+ offset 0))
command (case type command (case type
1 :move-to 1 :move-to
2 :line-to 2 :line-to
3 :curve-to 3 :curve-to
4 :close-path) 4 :close-path)
params (case type params (case type
1 {:x (.getFloat32 dview (+ offset 20)) 1 {:x (.getFloat32 dview (+ offset 20))
:y (.getFloat32 dview (+ offset 24))} :y (.getFloat32 dview (+ offset 24))}
2 {:x (.getFloat32 dview (+ offset 20)) 2 {:x (.getFloat32 dview (+ offset 20))
:y (.getFloat32 dview (+ offset 24))} :y (.getFloat32 dview (+ offset 24))}
3 {:c1x (.getFloat32 dview (+ offset 4)) 3 {:c1x (.getFloat32 dview (+ offset 4))
:c1y (.getFloat32 dview (+ offset 8)) :c1y (.getFloat32 dview (+ offset 8))
:c2x (.getFloat32 dview (+ offset 12)) :c2x (.getFloat32 dview (+ offset 12))
:c2y (.getFloat32 dview (+ offset 16)) :c2y (.getFloat32 dview (+ offset 16))
:x (.getFloat32 dview (+ offset 20)) :x (.getFloat32 dview (+ offset 20))
:y (.getFloat32 dview (+ offset 24))} :y (.getFloat32 dview (+ offset 24))}
4 {})] 4 {})]
(recur (inc index) (recur (inc index)
(conj result {:command command (conj result {:command command
:params params}))) :params params})))
result)))) result))))

View file

@ -7,7 +7,6 @@ mod state;
mod utils; mod utils;
mod view; mod view;
use shapes::RawPathData;
use skia_safe as skia; use skia_safe as skia;
use crate::state::State; use crate::state::State;
@ -107,7 +106,7 @@ pub extern "C" fn use_shape(a: u32, b: u32, c: u32, d: u32) {
pub extern "C" fn set_shape_selrect(left: f32, top: f32, right: f32, bottom: f32) { pub extern "C" fn set_shape_selrect(left: f32, top: f32, right: f32, bottom: f32) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() { if let Some(shape) = state.current_shape() {
shape.selrect.set_ltrb(left, top, right, bottom); shape.set_selrect(left, top, right, bottom);
} }
} }

View file

@ -6,7 +6,7 @@ use uuid::Uuid;
use crate::debug; use crate::debug;
use crate::math::Rect; use crate::math::Rect;
use crate::shapes::{draw_image_in_container, Fill, Image, Shape}; use crate::shapes::{draw_image_in_container, Fill, Image, Kind, Shape};
use crate::view::Viewbox; use crate::view::Viewbox;
struct GpuState { struct GpuState {
@ -224,7 +224,7 @@ impl RenderState {
self.drawing_surface.canvas().concat(&matrix); self.drawing_surface.canvas().concat(&matrix);
for fill in shape.fills().rev() { for fill in shape.fills().rev() {
self.render_fill(fill, shape.selrect); self.render_fill(fill, shape.selrect, &shape.kind);
} }
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
@ -281,22 +281,30 @@ impl RenderState {
self.flush(); self.flush();
} }
fn render_fill(&mut self, fill: &Fill, selrect: Rect) { fn render_fill(&mut self, fill: &Fill, selrect: Rect, kind: &Kind) {
if let Fill::Image(image_fill) = fill { match (fill, kind) {
let image = self.images.get(&image_fill.id()); (Fill::Image(image_fill), kind) => {
if let Some(image) = image { let image = self.images.get(&image_fill.id());
draw_image_in_container( if let Some(image) = image {
&self.drawing_surface.canvas(), draw_image_in_container(
&image, &self.drawing_surface.canvas(),
image_fill.size(), &image,
selrect, image_fill.size(),
&fill.to_paint(&selrect), kind,
); &fill.to_paint(&selrect),
);
}
}
(_, Kind::Rect(rect)) => {
self.drawing_surface
.canvas()
.draw_rect(rect, &fill.to_paint(&selrect));
}
(_, Kind::Path(path)) => {
self.drawing_surface
.canvas()
.draw_path(&path.to_skia_path(), &fill.to_paint(&selrect));
} }
} else {
self.drawing_surface
.canvas()
.draw_rect(selrect, &fill.to_paint(&selrect));
} }
} }

View file

@ -13,7 +13,7 @@ pub use paths::*;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Kind { pub enum Kind {
Rect, Rect(math::Rect),
Path(Path), Path(Path),
} }
@ -62,7 +62,7 @@ impl Shape {
Self { Self {
id, id,
children: Vec::<Uuid>::new(), children: Vec::<Uuid>::new(),
kind: Kind::Rect, kind: Kind::Rect(math::Rect::new_empty()),
selrect: math::Rect::new_empty(), selrect: math::Rect::new_empty(),
transform: Matrix::identity(), transform: Matrix::identity(),
rotation: 0., rotation: 0.,
@ -73,6 +73,13 @@ impl Shape {
} }
} }
pub fn set_selrect(&mut self, left: f32, top: f32, right: f32, bottom: f32) {
self.selrect.set_ltrb(left, top, right, bottom);
if let Kind::Rect(_) = self.kind {
self.kind = Kind::Rect(self.selrect.to_owned());
}
}
pub fn translation(&self) -> (f32, f32) { pub fn translation(&self) -> (f32, f32) {
(self.transform.e, self.transform.f) (self.transform.e, self.transform.f)
} }

View file

@ -3,17 +3,24 @@ use skia_safe as skia;
pub type Image = skia::Image; pub type Image = skia::Image;
use crate::shapes::Kind;
pub fn draw_image_in_container( pub fn draw_image_in_container(
canvas: &skia::Canvas, canvas: &skia::Canvas,
image: &Image, image: &Image,
size: (i32, i32), size: (i32, i32),
container: skia::Rect, kind: &Kind,
paint: &skia::Paint, paint: &skia::Paint,
) { ) {
let width = size.0 as f32; let width = size.0 as f32;
let height = size.1 as f32; let height = size.1 as f32;
let image_aspect_ratio = width / height; let image_aspect_ratio = width / height;
let container = match kind {
Kind::Rect(r) => r.to_owned(),
Kind::Path(p) => p.to_skia_path().bounds().to_owned(),
};
// Container size // Container size
let container_width = container.width(); let container_width = container.width();
let container_height = container.height(); let container_height = container.height();
@ -42,7 +49,14 @@ pub fn draw_image_in_container(
canvas.save(); canvas.save();
// Set the clipping rectangle to the container bounds // Set the clipping rectangle to the container bounds
canvas.clip_rect(container, skia::ClipOp::Intersect, true); match kind {
Kind::Rect(_) => {
canvas.clip_rect(container, skia::ClipOp::Intersect, true);
}
Kind::Path(p) => {
canvas.clip_path(&p.to_skia_path(), skia::ClipOp::Intersect, true);
}
}
// Draw the image with the calculated destination rectangle // Draw the image with the calculated destination rectangle
canvas.draw_image_rect(image, None, dest_rect, &paint); canvas.draw_image_rect(image, None, dest_rect, &paint);