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:
parent
0bfcc1f854
commit
99bb3ee962
6 changed files with 118 additions and 90 deletions
|
@ -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]
|
||||||
|
|
|
@ -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))))
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue