mirror of
https://github.com/penpot/penpot.git
synced 2025-05-25 21:36:09 +02:00
🎉 Add text fills
This commit is contained in:
parent
c2ce7c6cf6
commit
42ef2f929a
11 changed files with 451 additions and 279 deletions
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
([{:keys [id points selrect] :as shape} content]
|
([{:keys [id points selrect] :as shape} content]
|
||||||
(wasm.api/use-shape id)
|
(wasm.api/use-shape id)
|
||||||
(wasm.api/set-shape-text-content content)
|
(wasm.api/set-shape-text content)
|
||||||
(let [dimension (wasm.api/text-dimensions)
|
(let [dimension (wasm.api/text-dimensions)
|
||||||
resize-v
|
resize-v
|
||||||
(gpt/point
|
(gpt/point
|
||||||
|
|
|
@ -188,7 +188,6 @@
|
||||||
|
|
||||||
(defn- store-image
|
(defn- store-image
|
||||||
[id]
|
[id]
|
||||||
|
|
||||||
(let [buffer (uuid/get-u32 id)
|
(let [buffer (uuid/get-u32 id)
|
||||||
url (cf/resolve-file-media {:id id})]
|
url (cf/resolve-file-media {:id id})]
|
||||||
(->> (http/send! {:method :get
|
(->> (http/send! {:method :get
|
||||||
|
@ -209,6 +208,33 @@
|
||||||
(aget buffer 3))
|
(aget buffer 3))
|
||||||
true))))))
|
true))))))
|
||||||
|
|
||||||
|
(defn- get-fill-images
|
||||||
|
[leaf]
|
||||||
|
(filter :fill-image (:fills leaf)))
|
||||||
|
|
||||||
|
(defn- process-fill-image
|
||||||
|
[fill]
|
||||||
|
(rx/from
|
||||||
|
(when-let [image (:fill-image fill)]
|
||||||
|
(let [id (dm/get-prop image :id)
|
||||||
|
buffer (uuid/get-u32 id)
|
||||||
|
cached-image? (h/call wasm/internal-module "_is_image_cached"
|
||||||
|
(aget buffer 0)
|
||||||
|
(aget buffer 1)
|
||||||
|
(aget buffer 2)
|
||||||
|
(aget buffer 3))]
|
||||||
|
(when (zero? cached-image?)
|
||||||
|
(store-image id))))))
|
||||||
|
|
||||||
|
(defn set-shape-text-images
|
||||||
|
[content]
|
||||||
|
(let [paragraph-set (first (get content :children))
|
||||||
|
paragraphs (get paragraph-set :children)]
|
||||||
|
(->> paragraphs
|
||||||
|
(mapcat :children)
|
||||||
|
(mapcat get-fill-images)
|
||||||
|
(map process-fill-image))))
|
||||||
|
|
||||||
(defn set-shape-fills
|
(defn set-shape-fills
|
||||||
[fills]
|
[fills]
|
||||||
(h/call wasm/internal-module "_clear_shape_fills")
|
(h/call wasm/internal-module "_clear_shape_fills")
|
||||||
|
@ -218,23 +244,24 @@
|
||||||
gradient (:fill-color-gradient fill)
|
gradient (:fill-color-gradient fill)
|
||||||
image (:fill-image fill)
|
image (:fill-image fill)
|
||||||
offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
|
offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
|
||||||
heap (mem/get-heap-u32)]
|
heap (mem/get-heap-u8)
|
||||||
|
dview (js/DataView. (.-buffer heap))]
|
||||||
(cond
|
(cond
|
||||||
(some? color)
|
(some? color)
|
||||||
(let [argb (sr-clr/hex->u32argb color opacity)]
|
(do
|
||||||
(sr-fills/write-solid-fill! offset heap argb)
|
(sr-fills/write-solid-fill! offset dview (sr-clr/hex->u32argb color opacity))
|
||||||
(h/call wasm/internal-module "_add_shape_fill"))
|
(h/call wasm/internal-module "_add_shape_fill"))
|
||||||
|
|
||||||
(some? gradient)
|
(some? gradient)
|
||||||
(do
|
(do
|
||||||
(sr-fills/write-gradient-fill! offset heap gradient opacity)
|
(sr-fills/write-gradient-fill! offset dview gradient opacity)
|
||||||
(h/call wasm/internal-module "_add_shape_fill"))
|
(h/call wasm/internal-module "_add_shape_fill"))
|
||||||
|
|
||||||
(some? image)
|
(some? image)
|
||||||
(let [id (dm/get-prop image :id)
|
(let [id (dm/get-prop image :id)
|
||||||
buffer (uuid/get-u32 id)
|
buffer (uuid/get-u32 id)
|
||||||
cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))]
|
cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))]
|
||||||
(sr-fills/write-image-fill! offset heap id opacity (dm/get-prop image :width) (dm/get-prop image :height))
|
(sr-fills/write-image-fill! offset dview id opacity (dm/get-prop image :width) (dm/get-prop image :height))
|
||||||
(h/call wasm/internal-module "_add_shape_fill")
|
(h/call wasm/internal-module "_add_shape_fill")
|
||||||
(when (== cached-image? 0)
|
(when (== cached-image? 0)
|
||||||
(store-image id))))))
|
(store-image id))))))
|
||||||
|
@ -254,7 +281,8 @@
|
||||||
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap)
|
||||||
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)
|
||||||
offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
|
offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE)
|
||||||
heap (mem/get-heap-u32)]
|
heap (mem/get-heap-u8)
|
||||||
|
dview (js/DataView. (.-buffer heap))]
|
||||||
(case align
|
(case align
|
||||||
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end)
|
:inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end)
|
||||||
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end)
|
:outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end)
|
||||||
|
@ -262,22 +290,22 @@
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(some? gradient)
|
(some? gradient)
|
||||||
(let [_ nil]
|
(do
|
||||||
(sr-fills/write-gradient-fill! offset heap gradient opacity)
|
(sr-fills/write-gradient-fill! offset dview gradient opacity)
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill"))
|
(h/call wasm/internal-module "_add_shape_stroke_fill"))
|
||||||
|
|
||||||
(some? image)
|
(some? image)
|
||||||
(let [id (dm/get-prop image :id)
|
(let [id (dm/get-prop image :id)
|
||||||
buffer (uuid/get-u32 id)
|
buffer (uuid/get-u32 id)
|
||||||
cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))]
|
cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))]
|
||||||
(sr-fills/write-image-fill! offset heap id opacity (dm/get-prop image :width) (dm/get-prop image :height))
|
(sr-fills/write-image-fill! offset dview id opacity (dm/get-prop image :width) (dm/get-prop image :height))
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill")
|
(h/call wasm/internal-module "_add_shape_stroke_fill")
|
||||||
(when (== cached-image? 0)
|
(when (== cached-image? 0)
|
||||||
(store-image id)))
|
(store-image id)))
|
||||||
|
|
||||||
(some? color)
|
(some? color)
|
||||||
(let [argb (sr-clr/hex->u32argb color opacity)]
|
(do
|
||||||
(sr-fills/write-solid-fill! offset heap argb)
|
(sr-fills/write-solid-fill! offset dview (sr-clr/hex->u32argb color opacity))
|
||||||
(h/call wasm/internal-module "_add_shape_stroke_fill")))))
|
(h/call wasm/internal-module "_add_shape_stroke_fill")))))
|
||||||
strokes))
|
strokes))
|
||||||
|
|
||||||
|
@ -608,6 +636,12 @@
|
||||||
fonts)]
|
fonts)]
|
||||||
(f/store-fonts fonts))))
|
(f/store-fonts fonts))))
|
||||||
|
|
||||||
|
(defn set-shape-text
|
||||||
|
[content]
|
||||||
|
(concat
|
||||||
|
(set-shape-text-images content)
|
||||||
|
(set-shape-text-content content)))
|
||||||
|
|
||||||
(defn set-shape-grow-type
|
(defn set-shape-grow-type
|
||||||
[grow-type]
|
[grow-type]
|
||||||
(h/call wasm/internal-module "_set_shape_grow_type" (sr/translate-grow-type grow-type)))
|
(h/call wasm/internal-module "_set_shape_grow_type" (sr/translate-grow-type grow-type)))
|
||||||
|
@ -697,7 +731,7 @@
|
||||||
(when (some? corners) (set-shape-corners corners))
|
(when (some? corners) (set-shape-corners corners))
|
||||||
(when (some? shadows) (set-shape-shadows shadows))
|
(when (some? shadows) (set-shape-shadows shadows))
|
||||||
(when (and (= type :text) (some? content))
|
(when (and (= type :text) (some? content))
|
||||||
(set-shape-text-content content))
|
(set-shape-text content))
|
||||||
(when (= type :text)
|
(when (= type :text)
|
||||||
(set-shape-grow-type grow-type))
|
(set-shape-grow-type grow-type))
|
||||||
(when (or (ctl/any-layout? shape)
|
(when (or (ctl/any-layout? shape)
|
||||||
|
@ -711,9 +745,7 @@
|
||||||
(set-grid-layout shape))
|
(set-grid-layout shape))
|
||||||
|
|
||||||
(let [pending (into [] (concat
|
(let [pending (into [] (concat
|
||||||
(if (and (= type :text) (some? content))
|
(set-shape-text content)
|
||||||
(set-shape-text-content content)
|
|
||||||
[])
|
|
||||||
(set-shape-fills fills)
|
(set-shape-fills fills)
|
||||||
(set-shape-strokes strokes)))]
|
(set-shape-strokes strokes)))]
|
||||||
(perf/end-measure "set-object")
|
(perf/end-measure "set-object")
|
||||||
|
|
|
@ -6,30 +6,65 @@
|
||||||
|
|
||||||
(ns app.render-wasm.api.texts
|
(ns app.render-wasm.api.texts
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.render-wasm.api.fonts :as f]
|
[app.render-wasm.api.fonts :as f]
|
||||||
[app.render-wasm.helpers :as h]
|
[app.render-wasm.helpers :as h]
|
||||||
[app.render-wasm.mem :as mem]
|
[app.render-wasm.mem :as mem]
|
||||||
[app.render-wasm.serializers :as sr]
|
[app.render-wasm.serializers :as sr]
|
||||||
|
[app.render-wasm.serializers.color :as sr-clr]
|
||||||
|
[app.render-wasm.serializers.fills :as sr-fills]
|
||||||
[app.render-wasm.wasm :as wasm]))
|
[app.render-wasm.wasm :as wasm]))
|
||||||
|
|
||||||
(defn utf8->buffer [text]
|
(defn utf8->buffer [text]
|
||||||
(let [encoder (js/TextEncoder.)]
|
(let [encoder (js/TextEncoder.)]
|
||||||
(.encode encoder text)))
|
(.encode encoder text)))
|
||||||
|
|
||||||
|
(defn set-text-leaf-fills
|
||||||
|
[fills current-offset dview]
|
||||||
|
(reduce (fn [offset fill]
|
||||||
|
(let [opacity (or (:fill-opacity fill) 1.0)
|
||||||
|
color (:fill-color fill)
|
||||||
|
gradient (:fill-color-gradient fill)
|
||||||
|
image (:fill-image fill)]
|
||||||
|
(cond
|
||||||
|
(some? color)
|
||||||
|
(sr-fills/write-solid-fill! offset dview (sr-clr/hex->u32argb color opacity))
|
||||||
|
|
||||||
|
(some? gradient)
|
||||||
|
(sr-fills/write-gradient-fill! offset dview gradient opacity)
|
||||||
|
|
||||||
|
(some? image)
|
||||||
|
(sr-fills/write-image-fill! offset dview
|
||||||
|
(dm/get-prop image :id)
|
||||||
|
opacity
|
||||||
|
(dm/get-prop image :width)
|
||||||
|
(dm/get-prop image :height)))
|
||||||
|
|
||||||
|
(+ offset sr-fills/FILL-BYTE-SIZE)))
|
||||||
|
current-offset
|
||||||
|
fills))
|
||||||
|
|
||||||
|
(defn total-fills-count
|
||||||
|
[leaves]
|
||||||
|
(reduce #(+ %1 (count (:fills %2))) 0 leaves))
|
||||||
|
|
||||||
(defn write-shape-text
|
(defn write-shape-text
|
||||||
;; buffer has the following format:
|
;; buffer has the following format:
|
||||||
;; [<num-leaves> <paragraph_attributes> <leaves_attributes> <text>]
|
;; [<num-leaves> <paragraph_attributes> <leaves_attributes> <text>]
|
||||||
[leaves paragraph text]
|
[leaves paragraph text]
|
||||||
(let [num-leaves (count leaves)
|
(let [le? true
|
||||||
|
num-leaves (count leaves)
|
||||||
paragraph-attr-size 48
|
paragraph-attr-size 48
|
||||||
leaf-attr-size 52
|
total-fills (total-fills-count leaves)
|
||||||
metadata-size (+ 1 paragraph-attr-size (* num-leaves leaf-attr-size))
|
total-fills-size (* sr-fills/FILL-BYTE-SIZE total-fills)
|
||||||
|
leaf-attr-size 56
|
||||||
|
metadata-size (+ paragraph-attr-size (* num-leaves leaf-attr-size) total-fills-size)
|
||||||
text-buffer (utf8->buffer text)
|
text-buffer (utf8->buffer text)
|
||||||
text-size (.-byteLength text-buffer)
|
text-size (.-byteLength text-buffer)
|
||||||
buffer (js/ArrayBuffer. (+ metadata-size text-size))
|
buffer (js/ArrayBuffer. (+ metadata-size text-size))
|
||||||
dview (js/DataView. buffer)]
|
dview (js/DataView. buffer)]
|
||||||
|
|
||||||
(.setUint32 dview 0 num-leaves)
|
(.setUint32 dview 0 num-leaves le?)
|
||||||
|
|
||||||
;; Serialize paragraph attributes
|
;; Serialize paragraph attributes
|
||||||
(let [text-align (sr/serialize-text-align (:text-align paragraph))
|
(let [text-align (sr/serialize-text-align (:text-align paragraph))
|
||||||
|
@ -41,26 +76,26 @@
|
||||||
typography-ref-file (sr/serialize-uuid (:typography-ref-file paragraph))
|
typography-ref-file (sr/serialize-uuid (:typography-ref-file paragraph))
|
||||||
typography-ref-id (sr/serialize-uuid (:typography-ref-id paragraph))]
|
typography-ref-id (sr/serialize-uuid (:typography-ref-id paragraph))]
|
||||||
|
|
||||||
(.setUint8 dview 4 text-align)
|
(.setUint8 dview 4 text-align le?)
|
||||||
(.setUint8 dview 5 text-direction)
|
(.setUint8 dview 5 text-direction le?)
|
||||||
(.setUint8 dview 6 text-decoration)
|
(.setUint8 dview 6 text-decoration le?)
|
||||||
(.setUint8 dview 7 text-transform)
|
(.setUint8 dview 7 text-transform le?)
|
||||||
|
|
||||||
(.setFloat32 dview 8 line-height)
|
(.setFloat32 dview 8 line-height le?)
|
||||||
(.setFloat32 dview 12 letter-spacing)
|
(.setFloat32 dview 12 letter-spacing le?)
|
||||||
|
|
||||||
(.setUint32 dview 16 (aget typography-ref-file 0))
|
(.setUint32 dview 16 (aget typography-ref-file 0) le?)
|
||||||
(.setUint32 dview 20 (aget typography-ref-file 1))
|
(.setUint32 dview 20 (aget typography-ref-file 1) le?)
|
||||||
(.setUint32 dview 24 (aget typography-ref-file 2))
|
(.setUint32 dview 24 (aget typography-ref-file 2) le?)
|
||||||
(.setInt32 dview 28 (aget typography-ref-file 3))
|
(.setInt32 dview 28 (aget typography-ref-file 3) le?)
|
||||||
|
|
||||||
(.setUint32 dview 32 (aget typography-ref-id 0))
|
(.setUint32 dview 32 (aget typography-ref-id 0) le?)
|
||||||
(.setUint32 dview 36 (aget typography-ref-id 1))
|
(.setUint32 dview 36 (aget typography-ref-id 1) le?)
|
||||||
(.setUint32 dview 40 (aget typography-ref-id 2))
|
(.setUint32 dview 40 (aget typography-ref-id 2) le?)
|
||||||
(.setInt32 dview 44 (aget typography-ref-id 3)))
|
(.setInt32 dview 44 (aget typography-ref-id 3) le?))
|
||||||
|
|
||||||
;; Serialize leaves attributes
|
;; Serialize leaves attributes
|
||||||
(loop [index 0 offset (+ 1 paragraph-attr-size)]
|
(loop [index 0 offset paragraph-attr-size]
|
||||||
(when (< index num-leaves)
|
(when (< index num-leaves)
|
||||||
(let [leaf (nth leaves index)
|
(let [leaf (nth leaves index)
|
||||||
font-style (f/serialize-font-style (:font-style leaf))
|
font-style (f/serialize-font-style (:font-style leaf))
|
||||||
|
@ -70,26 +105,30 @@
|
||||||
font-family (hash (:font-family leaf))
|
font-family (hash (:font-family leaf))
|
||||||
font-variant-id (sr/serialize-uuid (:font-variant-id leaf))
|
font-variant-id (sr/serialize-uuid (:font-variant-id leaf))
|
||||||
text-buffer (utf8->buffer (:text leaf))
|
text-buffer (utf8->buffer (:text leaf))
|
||||||
text-length (.-byteLength text-buffer)]
|
text-length (.-byteLength text-buffer)
|
||||||
|
fills (:fills leaf)
|
||||||
|
total-fills (count fills)]
|
||||||
|
|
||||||
(.setUint8 dview offset font-style)
|
(.setUint8 dview offset font-style le?)
|
||||||
(.setFloat32 dview (+ offset 4) font-size)
|
(.setFloat32 dview (+ offset 4) font-size le?)
|
||||||
(.setUint32 dview (+ offset 8) font-weight)
|
(.setUint32 dview (+ offset 8) font-weight le?)
|
||||||
(.setUint32 dview (+ offset 12) (aget font-id 0))
|
(.setUint32 dview (+ offset 12) (aget font-id 0) le?)
|
||||||
(.setUint32 dview (+ offset 16) (aget font-id 1))
|
(.setUint32 dview (+ offset 16) (aget font-id 1) le?)
|
||||||
(.setUint32 dview (+ offset 20) (aget font-id 2))
|
(.setUint32 dview (+ offset 20) (aget font-id 2) le?)
|
||||||
(.setInt32 dview (+ offset 24) (aget font-id 3))
|
(.setInt32 dview (+ offset 24) (aget font-id 3) le?)
|
||||||
|
|
||||||
(.setInt32 dview (+ offset 28) font-family)
|
(.setInt32 dview (+ offset 28) font-family le?)
|
||||||
|
|
||||||
(.setUint32 dview (+ offset 32) (aget font-variant-id 0))
|
(.setUint32 dview (+ offset 32) (aget font-variant-id 0) le?)
|
||||||
(.setUint32 dview (+ offset 36) (aget font-variant-id 1))
|
(.setUint32 dview (+ offset 36) (aget font-variant-id 1) le?)
|
||||||
(.setUint32 dview (+ offset 40) (aget font-variant-id 2))
|
(.setUint32 dview (+ offset 40) (aget font-variant-id 2) le?)
|
||||||
(.setInt32 dview (+ offset 44) (aget font-variant-id 3))
|
(.setInt32 dview (+ offset 44) (aget font-variant-id 3) le?)
|
||||||
|
|
||||||
(.setInt32 dview (+ offset 48) text-length)
|
(.setInt32 dview (+ offset 48) text-length le?)
|
||||||
|
(.setInt32 dview (+ offset 52) total-fills le?)
|
||||||
|
|
||||||
(recur (inc index) (+ offset leaf-attr-size)))))
|
(let [new-offset (set-text-leaf-fills fills (+ offset leaf-attr-size) dview)]
|
||||||
|
(recur (inc index) new-offset)))))
|
||||||
|
|
||||||
;; Add text content to buffer
|
;; Add text content to buffer
|
||||||
(let [text-offset metadata-size
|
(let [text-offset metadata-size
|
||||||
|
|
|
@ -15,16 +15,14 @@
|
||||||
(def FILL-BYTE-SIZE (+ 4 (max GRADIENT-BYTE-SIZE IMAGE-BYTE-SIZE SOLID-BYTE-SIZE)))
|
(def FILL-BYTE-SIZE (+ 4 (max GRADIENT-BYTE-SIZE IMAGE-BYTE-SIZE SOLID-BYTE-SIZE)))
|
||||||
|
|
||||||
(defn write-solid-fill!
|
(defn write-solid-fill!
|
||||||
[offset heap-u32 argb]
|
[offset dview argb]
|
||||||
(let [dview (js/DataView. (.-buffer heap-u32))]
|
(.setUint8 dview offset 0x00 true)
|
||||||
(.setUint8 dview offset 0x00 true)
|
(.setUint32 dview (+ offset 4) argb true)
|
||||||
(.setUint32 dview (+ offset 4) argb true)
|
(+ offset FILL-BYTE-SIZE))
|
||||||
(+ offset FILL-BYTE-SIZE)))
|
|
||||||
|
|
||||||
(defn write-image-fill!
|
(defn write-image-fill!
|
||||||
[offset heap-u32 id opacity width height]
|
[offset dview id opacity width height]
|
||||||
(let [dview (js/DataView. (.-buffer heap-u32))
|
(let [uuid-buffer (uuid/get-u32 id)]
|
||||||
uuid-buffer (uuid/get-u32 id)]
|
|
||||||
(.setUint8 dview offset 0x03 true)
|
(.setUint8 dview offset 0x03 true)
|
||||||
(.setUint32 dview (+ offset 4) (aget uuid-buffer 0) true)
|
(.setUint32 dview (+ offset 4) (aget uuid-buffer 0) true)
|
||||||
(.setUint32 dview (+ offset 8) (aget uuid-buffer 1) true)
|
(.setUint32 dview (+ offset 8) (aget uuid-buffer 1) true)
|
||||||
|
@ -35,22 +33,19 @@
|
||||||
(.setInt32 dview (+ offset 28) height true)
|
(.setInt32 dview (+ offset 28) height true)
|
||||||
(+ offset FILL-BYTE-SIZE)))
|
(+ offset FILL-BYTE-SIZE)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn write-gradient-fill!
|
(defn write-gradient-fill!
|
||||||
[offset heap-u32 gradient opacity]
|
[offset dview gradient opacity]
|
||||||
(let [dview (js/DataView. (.-buffer heap-u32))
|
(let [start-x (:start-x gradient)
|
||||||
start-x (:start-x gradient)
|
|
||||||
start-y (:start-y gradient)
|
start-y (:start-y gradient)
|
||||||
end-x (:end-x gradient)
|
end-x (:end-x gradient)
|
||||||
end-y (:end-y gradient)
|
end-y (:end-y gradient)
|
||||||
width (or (:width gradient) 0)
|
width (or (:width gradient) 0)
|
||||||
stops (take shp/MAX-GRADIENT-STOPS (:stops gradient))
|
stops (take shp/MAX-GRADIENT-STOPS (:stops gradient))
|
||||||
type (if (= (:type gradient) :linear) 0x01 0x02)]
|
type (if (= (:type gradient) :linear) 0x01 0x02)]
|
||||||
(.setUint8 dview offset type true)
|
(.setUint8 dview offset type true)
|
||||||
(.setFloat32 dview (+ offset 4) start-x true)
|
(.setFloat32 dview (+ offset 4) start-x true)
|
||||||
(.setFloat32 dview (+ offset 8) start-y true)
|
(.setFloat32 dview (+ offset 8) start-y true)
|
||||||
(.setFloat32 dview (+ offset 12) end-x true)
|
(.setFloat32 dview (+ offset 12) end-x true)
|
||||||
(.setFloat32 dview (+ offset 16) end-y true)
|
(.setFloat32 dview (+ offset 16) end-y true)
|
||||||
(.setFloat32 dview (+ offset 20) opacity true)
|
(.setFloat32 dview (+ offset 20) opacity true)
|
||||||
(.setFloat32 dview (+ offset 24) width true)
|
(.setFloat32 dview (+ offset 24) width true)
|
||||||
|
@ -60,8 +55,8 @@
|
||||||
(+ offset FILL-BYTE-SIZE)
|
(+ offset FILL-BYTE-SIZE)
|
||||||
(let [stop (first stops)
|
(let [stop (first stops)
|
||||||
hex-color (:color stop)
|
hex-color (:color stop)
|
||||||
opacity (:opacity stop)
|
stop-opacity (:opacity stop)
|
||||||
argb (clr/hex->u32argb hex-color opacity)
|
argb (clr/hex->u32argb hex-color stop-opacity)
|
||||||
stop-offset (:offset stop)]
|
stop-offset (:offset stop)]
|
||||||
(.setUint32 dview loop-offset argb true)
|
(.setUint32 dview loop-offset argb true)
|
||||||
(.setFloat32 dview (+ loop-offset 4) stop-offset true)
|
(.setFloat32 dview (+ loop-offset 4) stop-offset true)
|
||||||
|
|
|
@ -150,7 +150,7 @@
|
||||||
(api/set-shape-svg-raw-content (api/get-static-markup shape))
|
(api/set-shape-svg-raw-content (api/get-static-markup shape))
|
||||||
|
|
||||||
(= (:type shape) :text)
|
(= (:type shape) :text)
|
||||||
(into [] (api/set-shape-text-content v)))
|
(api/set-shape-text v))
|
||||||
|
|
||||||
:grow-type
|
:grow-type
|
||||||
(api/set-shape-grow-type v)
|
(api/set-shape-grow-type v)
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod strokes;
|
||||||
mod surfaces;
|
mod surfaces;
|
||||||
mod text;
|
mod text;
|
||||||
|
|
||||||
use skia_safe::{self as skia, image, Matrix, RRect, Rect};
|
use skia_safe::{self as skia, Matrix, RRect, Rect};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use options::RenderOptions;
|
||||||
use surfaces::{SurfaceId, Surfaces};
|
use surfaces::{SurfaceId, Surfaces};
|
||||||
|
|
||||||
use crate::performance;
|
use crate::performance;
|
||||||
use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type};
|
use crate::shapes::{modified_children_ids, Corners, Shape, StructureEntry, Type};
|
||||||
use crate::tiles::{self, TileRect, TileViewbox, TileWithDistance};
|
use crate::tiles::{self, TileRect, TileViewbox, TileWithDistance};
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
use crate::view::Viewbox;
|
use crate::view::Viewbox;
|
||||||
|
@ -427,13 +427,11 @@ impl RenderState {
|
||||||
text::render(self, &shape, ¶graphs, None, None);
|
text::render(self, &shape, ¶graphs, None, None);
|
||||||
|
|
||||||
for stroke in shape.strokes().rev() {
|
for stroke in shape.strokes().rev() {
|
||||||
let mut image: Option<image::Image> = None;
|
let stroke_paragraphs = text_content.get_skia_stroke_paragraphs(
|
||||||
if let Fill::Image(image_fill) = &stroke.fill {
|
stroke,
|
||||||
image = self.images.get(&image_fill.id()).cloned();
|
&shape.selrect(),
|
||||||
}
|
self.fonts.font_collection(),
|
||||||
let stroke_paints = shape.get_text_stroke_paint(stroke, image.as_ref());
|
);
|
||||||
let stroke_paragraphs = text_content
|
|
||||||
.get_skia_stroke_paragraphs(self.fonts.font_collection(), &stroke_paints);
|
|
||||||
shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias);
|
shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias);
|
||||||
text::render(
|
text::render(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use skia_safe::Image;
|
use skia_safe::{self as skia};
|
||||||
use skia_safe::{self as skia, paint::Paint};
|
|
||||||
|
|
||||||
use crate::render::BlendMode;
|
use crate::render::BlendMode;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
@ -806,87 +805,6 @@ impl Shape {
|
||||||
pub fn has_fills(&self) -> bool {
|
pub fn has_fills(&self) -> bool {
|
||||||
!self.fills.is_empty()
|
!self.fills.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_paint_fill(&self, paint: &mut Paint, fill: &Fill, image: Option<Image>) {
|
|
||||||
match fill {
|
|
||||||
Fill::Solid(SolidColor(color)) => {
|
|
||||||
paint.set_color(*color);
|
|
||||||
}
|
|
||||||
Fill::LinearGradient(gradient) => {
|
|
||||||
paint.set_shader(gradient.to_linear_shader(&self.selrect()));
|
|
||||||
}
|
|
||||||
Fill::RadialGradient(gradient) => {
|
|
||||||
paint.set_shader(gradient.to_radial_shader(&self.selrect()));
|
|
||||||
}
|
|
||||||
Fill::Image(image_fill) => {
|
|
||||||
if let Some(image) = image {
|
|
||||||
let position = (self.selrect().x(), self.selrect().y());
|
|
||||||
let sampling_options = skia::SamplingOptions::new(
|
|
||||||
skia::FilterMode::Linear,
|
|
||||||
skia::MipmapMode::Nearest,
|
|
||||||
);
|
|
||||||
let tile_modes = (skia::TileMode::Clamp, skia::TileMode::Clamp);
|
|
||||||
let mut matrix = skia::Matrix::default();
|
|
||||||
matrix.set_translate(position);
|
|
||||||
|
|
||||||
let shader = image.to_shader(tile_modes, sampling_options, &matrix);
|
|
||||||
paint.set_shader(shader);
|
|
||||||
paint.set_alpha(image_fill.opacity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_text_stroke_paint(&self, stroke: &Stroke, image: Option<&Image>) -> Vec<Paint> {
|
|
||||||
let mut paints = Vec::new();
|
|
||||||
|
|
||||||
match stroke.kind {
|
|
||||||
StrokeKind::Inner => {
|
|
||||||
let mut paint = skia::Paint::default();
|
|
||||||
paint.set_blend_mode(skia::BlendMode::DstOver);
|
|
||||||
paint.set_anti_alias(true);
|
|
||||||
paints.push(paint);
|
|
||||||
|
|
||||||
let mut paint = skia::Paint::default();
|
|
||||||
paint.set_style(skia::PaintStyle::Stroke);
|
|
||||||
paint.set_blend_mode(skia::BlendMode::SrcATop);
|
|
||||||
paint.set_anti_alias(true);
|
|
||||||
paint.set_stroke_width(stroke.width * 2.0);
|
|
||||||
|
|
||||||
self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
|
|
||||||
|
|
||||||
paints.push(paint);
|
|
||||||
}
|
|
||||||
StrokeKind::Center => {
|
|
||||||
let mut paint = skia::Paint::default();
|
|
||||||
paint.set_style(skia::PaintStyle::Stroke);
|
|
||||||
paint.set_anti_alias(true);
|
|
||||||
paint.set_stroke_width(stroke.width);
|
|
||||||
|
|
||||||
self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
|
|
||||||
|
|
||||||
paints.push(paint);
|
|
||||||
}
|
|
||||||
StrokeKind::Outer => {
|
|
||||||
let mut paint = skia::Paint::default();
|
|
||||||
paint.set_style(skia::PaintStyle::Stroke);
|
|
||||||
paint.set_blend_mode(skia::BlendMode::DstOver);
|
|
||||||
paint.set_anti_alias(true);
|
|
||||||
paint.set_stroke_width(stroke.width * 2.0);
|
|
||||||
|
|
||||||
self.set_paint_fill(&mut paint, &stroke.fill, image.cloned());
|
|
||||||
|
|
||||||
paints.push(paint);
|
|
||||||
|
|
||||||
let mut paint = skia::Paint::default();
|
|
||||||
paint.set_blend_mode(skia::BlendMode::Clear);
|
|
||||||
paint.set_anti_alias(true);
|
|
||||||
paints.push(paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paints
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use skia_safe::{self as skia, Rect};
|
use skia_safe::{self as skia, Paint, Rect};
|
||||||
|
|
||||||
pub use super::Color;
|
pub use super::Color;
|
||||||
|
use crate::utils::get_image;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -176,3 +177,79 @@ impl Fill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_fill_shader(fill: &Fill, bounding_box: &Rect) -> Option<skia::Shader> {
|
||||||
|
match fill {
|
||||||
|
Fill::Solid(SolidColor(color)) => Some(skia::shaders::color(*color)),
|
||||||
|
Fill::LinearGradient(gradient) => gradient.to_linear_shader(bounding_box),
|
||||||
|
Fill::RadialGradient(gradient) => gradient.to_radial_shader(bounding_box),
|
||||||
|
Fill::Image(image_fill) => {
|
||||||
|
let mut image_shader = None;
|
||||||
|
let image = get_image(&image_fill.id);
|
||||||
|
if let Some(image) = image {
|
||||||
|
let sampling_options =
|
||||||
|
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest);
|
||||||
|
|
||||||
|
// FIXME no image ratio applied, centered to the current rect
|
||||||
|
let tile_modes = (skia::TileMode::Clamp, skia::TileMode::Clamp);
|
||||||
|
let image_width = image_fill.width as f32;
|
||||||
|
let image_height = image_fill.height as f32;
|
||||||
|
let scale_x = bounding_box.width() / image_width;
|
||||||
|
let scale_y = bounding_box.height() / image_height;
|
||||||
|
let scale = scale_x.max(scale_y);
|
||||||
|
let scaled_width = image_width * scale;
|
||||||
|
let scaled_height = image_height * scale;
|
||||||
|
let pos_x = bounding_box.left() - (scaled_width - bounding_box.width()) / 2.0;
|
||||||
|
let pos_y = bounding_box.top() - (scaled_height - bounding_box.height()) / 2.0;
|
||||||
|
|
||||||
|
let mut matrix = skia::Matrix::new_identity();
|
||||||
|
matrix.pre_translate((pos_x, pos_y));
|
||||||
|
matrix.pre_scale((scale, scale), None);
|
||||||
|
|
||||||
|
let opacity = image_fill.opacity();
|
||||||
|
let alpha_color = skia::Color4f::new(1.0, 1.0, 1.0, opacity as f32 / 255.0);
|
||||||
|
let alpha_shader = skia::shaders::color(alpha_color.to_color());
|
||||||
|
|
||||||
|
image_shader = image.to_shader(tile_modes, sampling_options, &matrix);
|
||||||
|
if let Some(shader) = image_shader {
|
||||||
|
image_shader = Some(skia::shaders::blend(
|
||||||
|
skia::Blender::mode(skia::BlendMode::DstIn),
|
||||||
|
shader,
|
||||||
|
alpha_shader,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image_shader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_fills(fills: &[Fill], bounding_box: Rect) -> skia::Paint {
|
||||||
|
let mut combined_shader: Option<skia::Shader> = None;
|
||||||
|
let mut fills_paint = skia::Paint::default();
|
||||||
|
|
||||||
|
for fill in fills {
|
||||||
|
let shader = get_fill_shader(fill, &bounding_box);
|
||||||
|
|
||||||
|
if let Some(shader) = shader {
|
||||||
|
combined_shader = match combined_shader {
|
||||||
|
Some(existing_shader) => Some(skia::shaders::blend(
|
||||||
|
skia::Blender::mode(skia::BlendMode::Overlay),
|
||||||
|
existing_shader,
|
||||||
|
shader,
|
||||||
|
)),
|
||||||
|
None => Some(shader),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fills_paint.set_shader(combined_shader.clone());
|
||||||
|
fills_paint
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_paint_fill(paint: &mut Paint, fill: &Fill, bounding_box: &Rect) {
|
||||||
|
let shader = get_fill_shader(fill, bounding_box);
|
||||||
|
if let Some(shader) = shader {
|
||||||
|
paint.set_shader(shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ use skia_safe::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::FontFamily;
|
use super::FontFamily;
|
||||||
|
use crate::shapes::{self, merge_fills, set_paint_fill, Stroke, StrokeKind};
|
||||||
use crate::utils::uuid_from_u32;
|
use crate::utils::uuid_from_u32;
|
||||||
|
use crate::wasm::fills::parse_fills_from_bytes;
|
||||||
use crate::Uuid;
|
use crate::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
@ -52,9 +54,9 @@ pub fn set_paragraphs_width(width: f32, paragraphs: &mut Vec<Vec<skia::textlayou
|
||||||
impl TextContent {
|
impl TextContent {
|
||||||
pub fn new(bounds: Rect, grow_type: GrowType) -> Self {
|
pub fn new(bounds: Rect, grow_type: GrowType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
paragraphs: Vec::new(),
|
||||||
bounds,
|
bounds,
|
||||||
grow_type,
|
grow_type,
|
||||||
..Self::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ impl TextContent {
|
||||||
let paragraph_style = p.paragraph_to_style();
|
let paragraph_style = p.paragraph_to_style();
|
||||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||||
for leaf in &p.children {
|
for leaf in &p.children {
|
||||||
let text_style = leaf.to_style(p);
|
let text_style = leaf.to_style(p, &self.bounds); // FIXME
|
||||||
let text = leaf.apply_text_transform(p.text_transform);
|
let text = leaf.apply_text_transform(p.text_transform);
|
||||||
builder.push_style(&text_style);
|
builder.push_style(&text_style);
|
||||||
builder.add_text(&text);
|
builder.add_text(&text);
|
||||||
|
@ -115,10 +117,12 @@ impl TextContent {
|
||||||
|
|
||||||
pub fn to_stroke_paragraphs(
|
pub fn to_stroke_paragraphs(
|
||||||
&self,
|
&self,
|
||||||
|
stroke: &Stroke,
|
||||||
|
bounds: &Rect,
|
||||||
fonts: &FontCollection,
|
fonts: &FontCollection,
|
||||||
stroke_paints: &Vec<Paint>,
|
|
||||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||||
let mut paragraph_group = Vec::new();
|
let mut paragraph_group = Vec::new();
|
||||||
|
let stroke_paints = get_text_stroke_paints(stroke, bounds);
|
||||||
|
|
||||||
for stroke_paint in stroke_paints {
|
for stroke_paint in stroke_paints {
|
||||||
let mut stroke_paragraphs = Vec::new();
|
let mut stroke_paragraphs = Vec::new();
|
||||||
|
@ -126,7 +130,7 @@ impl TextContent {
|
||||||
let paragraph_style = paragraph.paragraph_to_style();
|
let paragraph_style = paragraph.paragraph_to_style();
|
||||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||||
for leaf in ¶graph.children {
|
for leaf in ¶graph.children {
|
||||||
let stroke_style = leaf.to_stroke_style(paragraph, stroke_paint);
|
let stroke_style = leaf.to_stroke_style(paragraph, &stroke_paint);
|
||||||
let text: String = leaf.apply_text_transform(paragraph.text_transform);
|
let text: String = leaf.apply_text_transform(paragraph.text_transform);
|
||||||
builder.push_style(&stroke_style);
|
builder.push_style(&stroke_style);
|
||||||
builder.add_text(&text);
|
builder.add_text(&text);
|
||||||
|
@ -163,10 +167,11 @@ impl TextContent {
|
||||||
|
|
||||||
pub fn get_skia_stroke_paragraphs(
|
pub fn get_skia_stroke_paragraphs(
|
||||||
&self,
|
&self,
|
||||||
|
stroke: &Stroke,
|
||||||
|
bounds: &Rect,
|
||||||
fonts: &FontCollection,
|
fonts: &FontCollection,
|
||||||
paints: &Vec<Paint>,
|
|
||||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||||
self.collect_paragraphs(self.to_stroke_paragraphs(fonts, paints))
|
self.collect_paragraphs(self.to_stroke_paragraphs(stroke, bounds, fonts))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow_type(&self) -> GrowType {
|
pub fn grow_type(&self) -> GrowType {
|
||||||
|
@ -190,6 +195,7 @@ impl Default for TextContent {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Paragraph {
|
pub struct Paragraph {
|
||||||
|
num_leaves: u32,
|
||||||
text_align: u8,
|
text_align: u8,
|
||||||
text_decoration: u8,
|
text_decoration: u8,
|
||||||
text_direction: u8,
|
text_direction: u8,
|
||||||
|
@ -204,6 +210,7 @@ pub struct Paragraph {
|
||||||
impl Default for Paragraph {
|
impl Default for Paragraph {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
num_leaves: 0,
|
||||||
text_align: 0,
|
text_align: 0,
|
||||||
text_decoration: 0,
|
text_decoration: 0,
|
||||||
text_direction: 0,
|
text_direction: 0,
|
||||||
|
@ -218,9 +225,9 @@ impl Default for Paragraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Paragraph {
|
impl Paragraph {
|
||||||
// FIXME: These arguments could be grouped or simplified
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
num_leaves: u32,
|
||||||
text_align: u8,
|
text_align: u8,
|
||||||
text_decoration: u8,
|
text_decoration: u8,
|
||||||
text_direction: u8,
|
text_direction: u8,
|
||||||
|
@ -232,6 +239,7 @@ impl Paragraph {
|
||||||
children: Vec<TextLeaf>,
|
children: Vec<TextLeaf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
num_leaves,
|
||||||
text_align,
|
text_align,
|
||||||
text_decoration,
|
text_decoration,
|
||||||
text_direction,
|
text_direction,
|
||||||
|
@ -286,6 +294,7 @@ pub struct TextLeaf {
|
||||||
font_style: u8,
|
font_style: u8,
|
||||||
font_weight: i32,
|
font_weight: i32,
|
||||||
font_variant_id: Uuid,
|
font_variant_id: Uuid,
|
||||||
|
fills: Vec<shapes::Fill>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextLeaf {
|
impl TextLeaf {
|
||||||
|
@ -296,6 +305,7 @@ impl TextLeaf {
|
||||||
font_style: u8,
|
font_style: u8,
|
||||||
font_weight: i32,
|
font_weight: i32,
|
||||||
font_variant_id: Uuid,
|
font_variant_id: Uuid,
|
||||||
|
fills: Vec<shapes::Fill>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
|
@ -304,12 +314,26 @@ impl TextLeaf {
|
||||||
font_style,
|
font_style,
|
||||||
font_weight,
|
font_weight,
|
||||||
font_variant_id,
|
font_variant_id,
|
||||||
|
fills,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_style(&self, paragraph: &Paragraph) -> skia::textlayout::TextStyle {
|
pub fn to_style(
|
||||||
|
&self,
|
||||||
|
paragraph: &Paragraph,
|
||||||
|
content_bounds: &Rect,
|
||||||
|
) -> skia::textlayout::TextStyle {
|
||||||
let mut style = skia::textlayout::TextStyle::default();
|
let mut style = skia::textlayout::TextStyle::default();
|
||||||
style.set_color(skia::Color::BLACK);
|
|
||||||
|
let bounding_box = Rect::from_xywh(
|
||||||
|
content_bounds.x(),
|
||||||
|
content_bounds.y(),
|
||||||
|
self.font_size * self.text.len() as f32,
|
||||||
|
self.font_size,
|
||||||
|
);
|
||||||
|
|
||||||
|
let paint = merge_fills(&self.fills, bounding_box);
|
||||||
|
style.set_foreground_paint(&paint);
|
||||||
style.set_font_size(self.font_size);
|
style.set_font_size(self.font_size);
|
||||||
style.set_letter_spacing(paragraph.letter_spacing);
|
style.set_letter_spacing(paragraph.letter_spacing);
|
||||||
style.set_height(paragraph.line_height);
|
style.set_height(paragraph.line_height);
|
||||||
|
@ -336,7 +360,7 @@ impl TextLeaf {
|
||||||
paragraph: &Paragraph,
|
paragraph: &Paragraph,
|
||||||
stroke_paint: &Paint,
|
stroke_paint: &Paint,
|
||||||
) -> skia::textlayout::TextStyle {
|
) -> skia::textlayout::TextStyle {
|
||||||
let mut style = self.to_style(paragraph);
|
let mut style = self.to_style(paragraph, &Rect::default());
|
||||||
style.set_foreground_paint(stroke_paint);
|
style.set_foreground_paint(stroke_paint);
|
||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
@ -366,11 +390,44 @@ impl TextLeaf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const RAW_PARAGRAPH_DATA_SIZE: usize = 48;
|
const RAW_PARAGRAPH_DATA_SIZE: usize = std::mem::size_of::<RawParagraphData>();
|
||||||
pub const RAW_LEAF_DATA_SIZE: usize = 52;
|
//const RAW_LEAF_DATA_SIZE: usize = std::mem::size_of::<RawTextLeaf>();
|
||||||
|
// FIXME
|
||||||
|
pub const RAW_LEAF_DATA_SIZE: usize = 56;
|
||||||
|
pub const RAW_LEAF_FILLS_SIZE: usize = 160;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct RawTextLeaf {
|
||||||
|
font_style: u8,
|
||||||
|
font_size: f32,
|
||||||
|
font_weight: i32,
|
||||||
|
font_id: [u32; 4],
|
||||||
|
font_family: [u8; 4],
|
||||||
|
font_variant_id: [u32; 4],
|
||||||
|
text_length: u32,
|
||||||
|
total_fills: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; RAW_LEAF_DATA_SIZE]> for RawTextLeaf {
|
||||||
|
fn from(bytes: [u8; RAW_LEAF_DATA_SIZE]) -> Self {
|
||||||
|
unsafe { std::mem::transmute(bytes) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for RawTextLeaf {
|
||||||
|
type Error = String;
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
let data: [u8; RAW_LEAF_DATA_SIZE] = bytes
|
||||||
|
.get(0..RAW_LEAF_DATA_SIZE)
|
||||||
|
.and_then(|slice| slice.try_into().ok())
|
||||||
|
.ok_or("Invalid text leaf data".to_string())?;
|
||||||
|
Ok(RawTextLeaf::from(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct RawTextLeafData {
|
pub struct RawTextLeafData {
|
||||||
font_style: u8,
|
font_style: u8,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
|
@ -379,15 +436,48 @@ pub struct RawTextLeafData {
|
||||||
font_family: [u8; 4],
|
font_family: [u8; 4],
|
||||||
font_variant_id: [u32; 4],
|
font_variant_id: [u32; 4],
|
||||||
text_length: u32,
|
text_length: u32,
|
||||||
|
total_fills: u32,
|
||||||
|
fills: Vec<shapes::Fill>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for RawTextLeafData {
|
||||||
|
fn from(bytes: &[u8]) -> Self {
|
||||||
|
let text_leaf: RawTextLeaf = RawTextLeaf::try_from(bytes).unwrap();
|
||||||
|
let total_fills = text_leaf.total_fills as usize;
|
||||||
|
|
||||||
|
// Use checked_mul to prevent overflow
|
||||||
|
let fills_size = total_fills
|
||||||
|
.checked_mul(RAW_LEAF_FILLS_SIZE)
|
||||||
|
.expect("Overflow occurred while calculating fills size");
|
||||||
|
|
||||||
|
let fills_start = RAW_LEAF_DATA_SIZE;
|
||||||
|
let fills_end = fills_start + fills_size;
|
||||||
|
let buffer = &bytes[fills_start..fills_end];
|
||||||
|
let fills = parse_fills_from_bytes(buffer, total_fills);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
font_style: text_leaf.font_style,
|
||||||
|
font_size: text_leaf.font_size,
|
||||||
|
font_weight: text_leaf.font_weight,
|
||||||
|
font_id: text_leaf.font_id,
|
||||||
|
font_family: text_leaf.font_family,
|
||||||
|
font_variant_id: text_leaf.font_variant_id,
|
||||||
|
text_length: text_leaf.text_length,
|
||||||
|
total_fills: text_leaf.total_fills,
|
||||||
|
fills,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[repr(align(4))]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct RawParagraphData {
|
pub struct RawParagraphData {
|
||||||
|
num_leaves: u32,
|
||||||
text_align: u8,
|
text_align: u8,
|
||||||
text_transform: u8,
|
|
||||||
text_decoration: u8,
|
|
||||||
text_direction: u8,
|
text_direction: u8,
|
||||||
|
text_decoration: u8,
|
||||||
|
text_transform: u8,
|
||||||
line_height: f32,
|
line_height: f32,
|
||||||
letter_spacing: f32,
|
letter_spacing: f32,
|
||||||
typography_ref_file: [u32; 4],
|
typography_ref_file: [u32; 4],
|
||||||
|
@ -396,54 +486,22 @@ pub struct RawParagraphData {
|
||||||
|
|
||||||
impl From<[u8; RAW_PARAGRAPH_DATA_SIZE]> for RawParagraphData {
|
impl From<[u8; RAW_PARAGRAPH_DATA_SIZE]> for RawParagraphData {
|
||||||
fn from(bytes: [u8; RAW_PARAGRAPH_DATA_SIZE]) -> Self {
|
fn from(bytes: [u8; RAW_PARAGRAPH_DATA_SIZE]) -> Self {
|
||||||
Self {
|
unsafe { std::mem::transmute(bytes) }
|
||||||
text_align: bytes[4],
|
|
||||||
text_direction: bytes[5],
|
|
||||||
text_decoration: bytes[6],
|
|
||||||
text_transform: bytes[7],
|
|
||||||
line_height: f32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
|
|
||||||
letter_spacing: f32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
|
|
||||||
typography_ref_file: [
|
|
||||||
u32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]),
|
|
||||||
u32::from_be_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
|
|
||||||
u32::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
|
|
||||||
u32::from_be_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
|
|
||||||
],
|
|
||||||
typography_ref_id: [
|
|
||||||
u32::from_be_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]),
|
|
||||||
u32::from_be_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
|
|
||||||
u32::from_be_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]),
|
|
||||||
u32::from_be_bytes([bytes[44], bytes[45], bytes[46], bytes[47]]),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RawTextData {
|
impl TryFrom<&[u8]> for RawParagraphData {
|
||||||
pub paragraph: Paragraph,
|
type Error = String;
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
let data: [u8; RAW_PARAGRAPH_DATA_SIZE] = bytes
|
||||||
|
.get(0..RAW_PARAGRAPH_DATA_SIZE)
|
||||||
|
.and_then(|slice| slice.try_into().ok())
|
||||||
|
.ok_or("Invalid paragraph data".to_string())?;
|
||||||
|
Ok(RawParagraphData::from(data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTextData {
|
impl RawTextData {
|
||||||
fn leaves_attrs_from_bytes(buffer: &[u8], num_leaves: usize) -> Vec<RawTextLeafData> {
|
|
||||||
let mut attrs = Vec::new();
|
|
||||||
for i in 0..num_leaves {
|
|
||||||
let start = i * RAW_LEAF_DATA_SIZE;
|
|
||||||
let end = start + RAW_LEAF_DATA_SIZE;
|
|
||||||
let bytes = &buffer[start..end];
|
|
||||||
let array: [u8; RAW_LEAF_DATA_SIZE] = bytes.try_into().expect("Slice length mismatch");
|
|
||||||
let leaf_attrs = RawTextLeafData::from(array);
|
|
||||||
attrs.push(leaf_attrs);
|
|
||||||
}
|
|
||||||
attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paragraph_attrs_from_bytes(buffer: &[u8]) -> RawParagraphData {
|
|
||||||
let bytes: [u8; RAW_PARAGRAPH_DATA_SIZE] = buffer[..RAW_PARAGRAPH_DATA_SIZE]
|
|
||||||
.try_into()
|
|
||||||
.expect("Slice length mismatch for paragraph attributes");
|
|
||||||
RawParagraphData::from(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_from_bytes(buffer: &[u8], offset: usize, text_length: u32) -> (String, usize) {
|
fn text_from_bytes(buffer: &[u8], offset: usize, text_length: u32) -> (String, usize) {
|
||||||
let text_length = text_length as usize;
|
let text_length = text_length as usize;
|
||||||
let text_end = offset + text_length;
|
let text_end = offset + text_length;
|
||||||
|
@ -467,75 +525,60 @@ impl RawTextData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<[u8; RAW_LEAF_DATA_SIZE]> for RawTextLeafData {
|
pub struct RawTextData {
|
||||||
fn from(bytes: [u8; RAW_LEAF_DATA_SIZE]) -> Self {
|
pub paragraph: Paragraph,
|
||||||
Self {
|
|
||||||
font_style: bytes[0],
|
|
||||||
font_size: f32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
|
|
||||||
font_weight: i32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
|
|
||||||
font_id: [
|
|
||||||
u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
|
|
||||||
u32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]),
|
|
||||||
u32::from_be_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
|
|
||||||
u32::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
|
|
||||||
],
|
|
||||||
font_family: [bytes[28], bytes[29], bytes[30], bytes[31]],
|
|
||||||
font_variant_id: [
|
|
||||||
u32::from_be_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]),
|
|
||||||
u32::from_be_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
|
|
||||||
u32::from_be_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]),
|
|
||||||
u32::from_be_bytes([bytes[44], bytes[45], bytes[46], bytes[47]]),
|
|
||||||
],
|
|
||||||
text_length: u32::from_be_bytes([bytes[48], bytes[49], bytes[50], bytes[51]]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Vec<u8>> for RawTextData {
|
impl From<&Vec<u8>> for RawTextData {
|
||||||
fn from(bytes: &Vec<u8>) -> Self {
|
fn from(bytes: &Vec<u8>) -> Self {
|
||||||
let num_leaves = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
|
let paragraph = RawParagraphData::try_from(&bytes[..RAW_PARAGRAPH_DATA_SIZE]).unwrap();
|
||||||
|
let mut offset = RAW_PARAGRAPH_DATA_SIZE;
|
||||||
let paragraph_attrs =
|
let mut raw_text_leaves: Vec<RawTextLeafData> = Vec::new();
|
||||||
RawTextData::paragraph_attrs_from_bytes(&bytes[..RAW_PARAGRAPH_DATA_SIZE]);
|
|
||||||
let leaves_attrs =
|
|
||||||
RawTextData::leaves_attrs_from_bytes(&bytes[1 + RAW_PARAGRAPH_DATA_SIZE..], num_leaves);
|
|
||||||
|
|
||||||
let metadata_size = 1 + RAW_PARAGRAPH_DATA_SIZE + num_leaves * RAW_LEAF_DATA_SIZE;
|
|
||||||
let text_start = metadata_size;
|
|
||||||
let mut offset = text_start;
|
|
||||||
let mut text_leaves: Vec<TextLeaf> = Vec::new();
|
let mut text_leaves: Vec<TextLeaf> = Vec::new();
|
||||||
|
|
||||||
for attrs in leaves_attrs {
|
for _ in 0..paragraph.num_leaves {
|
||||||
let (text, new_offset) = RawTextData::text_from_bytes(bytes, offset, attrs.text_length);
|
let text_leaf = RawTextLeafData::from(&bytes[offset..]);
|
||||||
offset = new_offset;
|
raw_text_leaves.push(text_leaf.clone());
|
||||||
|
offset += RAW_LEAF_DATA_SIZE + (text_leaf.total_fills as usize * RAW_LEAF_FILLS_SIZE);
|
||||||
let font_id = uuid_from_u32(attrs.font_id);
|
|
||||||
let font_variant_id = uuid_from_u32(attrs.font_variant_id);
|
|
||||||
|
|
||||||
let font_family =
|
|
||||||
FontFamily::new(font_id, attrs.font_weight as u32, attrs.font_style.into());
|
|
||||||
|
|
||||||
let text_leaf = TextLeaf::new(
|
|
||||||
text,
|
|
||||||
font_family,
|
|
||||||
attrs.font_size,
|
|
||||||
attrs.font_style,
|
|
||||||
attrs.font_weight,
|
|
||||||
font_variant_id,
|
|
||||||
);
|
|
||||||
text_leaves.push(text_leaf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let typography_ref_file = uuid_from_u32(paragraph_attrs.typography_ref_file);
|
for text_leaf in raw_text_leaves.iter() {
|
||||||
let typography_ref_id = uuid_from_u32(paragraph_attrs.typography_ref_id);
|
let (text, new_offset) =
|
||||||
|
RawTextData::text_from_bytes(bytes, offset, text_leaf.text_length);
|
||||||
|
offset = new_offset;
|
||||||
|
|
||||||
|
let font_id = uuid_from_u32(text_leaf.font_id);
|
||||||
|
let font_variant_id = uuid_from_u32(text_leaf.font_variant_id);
|
||||||
|
|
||||||
|
let font_family = FontFamily::new(
|
||||||
|
font_id,
|
||||||
|
text_leaf.font_weight as u32,
|
||||||
|
text_leaf.font_style.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_text_leaf = TextLeaf::new(
|
||||||
|
text,
|
||||||
|
font_family,
|
||||||
|
text_leaf.font_size,
|
||||||
|
text_leaf.font_style,
|
||||||
|
text_leaf.font_weight,
|
||||||
|
font_variant_id,
|
||||||
|
text_leaf.fills.clone(),
|
||||||
|
);
|
||||||
|
text_leaves.push(new_text_leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let typography_ref_file = uuid_from_u32(paragraph.typography_ref_file);
|
||||||
|
let typography_ref_id = uuid_from_u32(paragraph.typography_ref_id);
|
||||||
|
|
||||||
let paragraph = Paragraph::new(
|
let paragraph = Paragraph::new(
|
||||||
paragraph_attrs.text_align,
|
paragraph.num_leaves,
|
||||||
paragraph_attrs.text_decoration,
|
paragraph.text_align,
|
||||||
paragraph_attrs.text_direction,
|
paragraph.text_direction,
|
||||||
paragraph_attrs.text_transform,
|
paragraph.text_decoration,
|
||||||
paragraph_attrs.line_height,
|
paragraph.text_transform,
|
||||||
paragraph_attrs.letter_spacing,
|
paragraph.line_height,
|
||||||
|
paragraph.letter_spacing,
|
||||||
typography_ref_file,
|
typography_ref_file,
|
||||||
typography_ref_id,
|
typography_ref_id,
|
||||||
text_leaves.clone(),
|
text_leaves.clone(),
|
||||||
|
@ -557,3 +600,54 @@ pub fn auto_height(paragraphs: &[Vec<skia::textlayout::Paragraph>]) -> f32 {
|
||||||
.flatten()
|
.flatten()
|
||||||
.fold(0.0, |auto_height, p| auto_height + p.height())
|
.fold(0.0, |auto_height, p| auto_height + p.height())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect) -> Vec<Paint> {
|
||||||
|
let mut paints = Vec::new();
|
||||||
|
|
||||||
|
match stroke.kind {
|
||||||
|
StrokeKind::Inner => {
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_blend_mode(skia::BlendMode::DstOver);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
paints.push(paint);
|
||||||
|
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_style(skia::PaintStyle::Stroke);
|
||||||
|
paint.set_blend_mode(skia::BlendMode::SrcATop);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
paint.set_stroke_width(stroke.width * 2.0);
|
||||||
|
|
||||||
|
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||||
|
|
||||||
|
paints.push(paint);
|
||||||
|
}
|
||||||
|
StrokeKind::Center => {
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_style(skia::PaintStyle::Stroke);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
paint.set_stroke_width(stroke.width);
|
||||||
|
|
||||||
|
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||||
|
|
||||||
|
paints.push(paint);
|
||||||
|
}
|
||||||
|
StrokeKind::Outer => {
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_style(skia::PaintStyle::Stroke);
|
||||||
|
paint.set_blend_mode(skia::BlendMode::DstOver);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
paint.set_stroke_width(stroke.width * 2.0);
|
||||||
|
|
||||||
|
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||||
|
|
||||||
|
paints.push(paint);
|
||||||
|
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_blend_mode(skia::BlendMode::Clear);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
paints.push(paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paints
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
use crate::skia::Image;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
use crate::with_state;
|
||||||
|
use crate::STATE;
|
||||||
|
|
||||||
pub fn uuid_from_u32_quartet(a: u32, b: u32, c: u32, d: u32) -> Uuid {
|
pub fn uuid_from_u32_quartet(a: u32, b: u32, c: u32, d: u32) -> Uuid {
|
||||||
let hi: u64 = ((a as u64) << 32) | b as u64;
|
let hi: u64 = ((a as u64) << 32) | b as u64;
|
||||||
|
@ -18,3 +21,7 @@ pub fn uuid_to_u32_quartet(id: &Uuid) -> (u32, u32, u32, u32) {
|
||||||
pub fn uuid_from_u32(id: [u32; 4]) -> Uuid {
|
pub fn uuid_from_u32(id: [u32; 4]) -> Uuid {
|
||||||
uuid_from_u32_quartet(id[0], id[1], id[2], id[3])
|
uuid_from_u32_quartet(id[0], id[1], id[2], id[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_image(image_id: &Uuid) -> Option<&Image> {
|
||||||
|
with_state!(state, { state.render_state().images.get(image_id) })
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,18 @@ impl TryFrom<&[u8]> for RawFillData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_fills_from_bytes(buffer: &[u8], num_fills: usize) -> Vec<shapes::Fill> {
|
||||||
|
buffer
|
||||||
|
.chunks_exact(RAW_FILL_DATA_SIZE)
|
||||||
|
.take(num_fills)
|
||||||
|
.map(|bytes| {
|
||||||
|
RawFillData::try_from(bytes)
|
||||||
|
.expect("Invalid fill data")
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn add_shape_fill() {
|
pub extern "C" fn add_shape_fill() {
|
||||||
with_current_shape!(state, |shape: &mut Shape| {
|
with_current_shape!(state, |shape: &mut Shape| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue