diff --git a/common/src/app/common/buffer.cljc b/common/src/app/common/buffer.cljc index 55ec4f83c8..8e563cd4fe 100644 --- a/common/src/app/common/buffer.cljc +++ b/common/src/app/common/buffer.cljc @@ -5,7 +5,8 @@ ;; Copyright (c) KALEIDOS INC (ns app.common.buffer - "A collection of helpers and macros for work with byte buffers" + "A collection of helpers and macros for work with byte + buffer (ByteBuffer on JVM and DataView on JS)." (:refer-clojure :exclude [clone]) (:require [app.common.uuid :as uuid]) @@ -81,6 +82,13 @@ (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] `(.put ~target ~offset (unchecked-byte ~value))))) +(defmacro write-u8 + [target offset value] + (if (:ns &env) + `(.setUint8 ~target ~offset ~value true) + (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] + `(.put ~target ~offset (unchecked-byte ~value))))) + (defmacro write-bool [target offset value] (if (:ns &env) @@ -102,6 +110,18 @@ (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] `(.putInt ~target ~offset (unchecked-int ~value))))) +(defmacro write-u32 + [target offset value] + (if (:ns &env) + `(.setUint32 ~target ~offset ~value true) + (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] + `(.putInt ~target ~offset (unchecked-int ~value))))) + +(defmacro write-i32 + "Idiomatic alias for `write-int`" + [target offset value] + `(write-int ~target ~offset ~value)) + (defmacro write-float [target offset value] (if (:ns &env) @@ -109,6 +129,11 @@ (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] `(.putFloat ~target ~offset (unchecked-float ~value))))) +(defmacro write-f32 + "Idiomatic alias for `write-float`." + [target offset value] + `(write-float ~target ~offset ~value)) + (defmacro write-uuid [target offset value] (if (:ns &env) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index eb6a3bf496..04e52d322b 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -58,13 +58,13 @@ ([{:keys [id points selrect] :as shape} content] (wasm.api/use-shape id) (wasm.api/set-shape-text id content) - (let [dimension (wasm.api/text-dimensions) - resize-v - (gpt/point - (/ (:width dimension) (-> selrect :width)) - (/ (:height dimension) (-> selrect :height))) + (let [dimension (wasm.api/get-text-dimensions) + resize-v (gpt/point + (/ (:width dimension) (-> selrect :width)) + (/ (:height dimension) (-> selrect :height))) + + origin (first points)] - origin (first points)] {id {:modifiers (ctm/resize-modifiers diff --git a/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs b/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs index 6974114acb..6214bf64b4 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs @@ -19,7 +19,7 @@ (if (features/active-feature? @st/state "render-wasm/v1") (let [transform (gsh/transform-str shape) {:keys [id x y grow-type]} shape - {:keys [width height]} (if (= :fixed grow-type) shape (wasm.api/text-dimensions id))] + {:keys [width height]} (if (= :fixed grow-type) shape (wasm.api/get-text-dimensions id))] [:rect.main.viewport-selrect {:x x :y y diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs index ca9c94db4c..af15827efc 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs @@ -310,7 +310,7 @@ [x y width height] (if (features/active-feature? @st/state "render-wasm/v1") - (let [{:keys [max-width height]} (wasm.api/text-dimensions shape-id) + (let [{:keys [max-width height]} (wasm.api/get-text-dimensions shape-id) {:keys [x y]} (:selrect shape)] [x y max-width height]) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 58ed2be049..3ebfd80860 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -309,7 +309,7 @@ (ted/export-content))] (wasm.api/use-shape edition) (wasm.api/set-shape-text-content edition content) - (let [dimension (wasm.api/text-dimensions)] + (let [dimension (wasm.api/get-text-dimensions)] (st/emit! (dwt/resize-text-editor edition dimension)) (wasm.api/clear-drawing-cache) (wasm.api/request-render "content")))))) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 93b3feb9e5..f256350064 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -208,7 +208,7 @@ (defn- process-fill-image [shape-id fill] (when-let [image (:fill-image fill)] - (let [id (dm/get-prop image :id) + (let [id (get image :id) buffer (uuid/get-u32 id) cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) @@ -283,7 +283,7 @@ (h/call wasm/internal-module "_add_shape_stroke_fill")) (some? image) - (let [image-id (dm/get-prop image :id) + (let [image-id (get image :id) buffer (uuid/get-u32 image-id) cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))] (types.fills.impl/write-image-fill offset dview opacity image) @@ -334,7 +334,7 @@ (defn set-shape-vertical-align [vertical-align] - (h/call wasm/internal-module "_set_shape_vertical_align" (sr/serialize-vertical-align vertical-align))) + (h/call wasm/internal-module "_set_shape_vertical_align" (sr/translate-vertical-align vertical-align))) (defn set-shape-opacity [opacity] @@ -378,8 +378,11 @@ (h/call wasm/internal-module "_set_shape_blur" type hidden value))) (defn set-shape-corners - [corners] - (let [[r1 r2 r3 r4] corners] + [shape] + (let [r1 (get shape :r1) + r2 (get shape :r2) + r3 (get shape :r3) + r4 (get shape :r4)] (h/call wasm/internal-module "_set_shape_corners" (d/nilv r1 0) (d/nilv r2 0) @@ -466,9 +469,11 @@ ;; alligned writes, so for heteregeneus writes we use ;; the buffer abstraction (DataView) for perform ;; surgical writes. - (mem/write-u8 dview (+ offset 0) (sr/translate-grid-track-type type)) - (mem/write-f32 dview (+ offset 1) value) - (+ offset GRID-LAYOUT-ROW-U8-SIZE)) + (-> offset + (mem/write-u8 dview (sr/translate-grid-track-type type)) + (mem/write-f32 dview value) + (mem/assert-written offset GRID-LAYOUT-ROW-U8-SIZE))) + offset entries) @@ -486,9 +491,12 @@ ;; alligned writes, so for heteregeneus writes we use ;; the buffer abstraction (DataView) for perform ;; surgical writes. - (mem/write-u8 dview (+ offset 0) (sr/translate-grid-track-type type)) - (mem/write-f32 dview (+ offset 1) value) - (+ offset GRID-LAYOUT-COLUMN-U8-SIZE)) + (-> offset + (mem/write-u8 dview (sr/translate-grid-track-type type)) + (mem/write-f32 dview value) + (mem/assert-written offset GRID-LAYOUT-COLUMN-U8-SIZE))) + + offset entries) @@ -496,83 +504,82 @@ (defn set-grid-layout-cells [cells] - (let [entries (vals cells) - size (mem/get-alloc-size cells GRID-LAYOUT-CELL-U8-SIZE) + (let [size (mem/get-alloc-size cells GRID-LAYOUT-CELL-U8-SIZE) offset (mem/alloc size) - heap (-> (mem/get-heap-u8) - (mem/view offset size))] + dview (mem/get-data-view)] - (loop [entries (seq entries) - current-offset 0] - (when-not (empty? entries) - (let [cell (first entries)] + (reduce-kv (fn [offset _ cell] + (let [shape-id (-> (get cell :shapes) first)] + (-> offset + ;; row: [u8; 4], + (mem/write-i32 dview (get cell :row)) - ;; row: [u8; 4], - (.set heap (sr/i32->u8 (:row cell)) (+ current-offset 0)) + ;; row_span: [u8; 4], + (mem/write-i32 dview (get cell :row-span)) - ;; row_span: [u8; 4], - (.set heap (sr/i32->u8 (:row-span cell)) (+ current-offset 4)) + ;; column: [u8; 4], + (mem/write-i32 dview (get cell :column)) - ;; column: [u8; 4], - (.set heap (sr/i32->u8 (:column cell)) (+ current-offset 8)) + ;; column_span: [u8; 4], + (mem/write-i32 dview (get cell :column-span)) - ;; column_span: [u8; 4], - (.set heap (sr/i32->u8 (:column-span cell)) (+ current-offset 12)) + ;; has_align_self: u8, + (mem/write-bool dview (some? (get cell :align-self))) - ;; has_align_self: u8, - (.set heap (sr/bool->u8 (some? (:align-self cell))) (+ current-offset 16)) + ;; align_self: u8, + (mem/write-u8 dview (get cell :align-self)) - ;; align_self: u8, - (.set heap (sr/u8 (sr/translate-align-self (:align-self cell))) (+ current-offset 17)) + ;; has_justify_self: u8, + (mem/write-bool dview (get cell :justify-self)) - ;; has_justify_self: u8, - (.set heap (sr/bool->u8 (some? (:justify-self cell))) (+ current-offset 18)) + ;; justify_self: u8, + (mem/write-u8 dview (sr/translate-justify-self (get cell :justify-self))) - ;; justify_self: u8, - (.set heap (sr/u8 (sr/translate-justify-self (:justify-self cell))) (+ current-offset 19)) + ;; has_shape_id: u8, + ;; (.set heap (sr/bool->u8 (d/not-empty? (:shapes cell))) (+ current-offset 20)) + (mem/write-u8 dview (some? shape-id)) - ;; has_shape_id: u8, - (.set heap (sr/bool->u8 (d/not-empty? (:shapes cell))) (+ current-offset 20)) + ;; shape_id_a: [u8; 4], + ;; shape_id_b: [u8; 4], + ;; shape_id_c: [u8; 4], + ;; shape_id_d: [u8; 4], + (mem/write-uuid dview (d/nilv shape-id uuid/zero)) + (mem/assert-written offset GRID-LAYOUT-CELL-U8-SIZE)))) - ;; shape_id_a: [u8; 4], - ;; shape_id_b: [u8; 4], - ;; shape_id_c: [u8; 4], - ;; shape_id_d: [u8; 4], - (.set heap (sr/uuid->u8 (or (-> cell :shapes first) uuid/zero)) (+ current-offset 21)) - - (recur (rest entries) (+ current-offset GRID-LAYOUT-CELL-U8-SIZE))))) + offset + cells) (h/call wasm/internal-module "_set_grid_cells"))) (defn set-grid-layout [shape] (set-grid-layout-data shape) - (set-grid-layout-rows (:layout-grid-rows shape)) - (set-grid-layout-columns (:layout-grid-columns shape)) - (set-grid-layout-cells (:layout-grid-cells shape))) + (set-grid-layout-rows (get shape :layout-grid-rows)) + (set-grid-layout-columns (get shape :layout-grid-columns)) + (set-grid-layout-cells (get shape :layout-grid-cells))) (defn set-layout-child [shape] - (let [margins (dm/get-prop shape :layout-item-margin) - margin-top (or (dm/get-prop margins :m1) 0) - margin-right (or (dm/get-prop margins :m2) 0) - margin-bottom (or (dm/get-prop margins :m3) 0) - margin-left (or (dm/get-prop margins :m4) 0) + (let [margins (get shape :layout-item-margin) + margin-top (get margins :m1 0) + margin-right (get margins :m2 0) + margin-bottom (get margins :m3 0) + margin-left (get margins :m4 0) - h-sizing (-> (dm/get-prop shape :layout-item-h-sizing) (or :fix) sr/translate-layout-sizing) - v-sizing (-> (dm/get-prop shape :layout-item-v-sizing) (or :fix) sr/translate-layout-sizing) - align-self (-> (dm/get-prop shape :layout-item-align-self) sr/translate-align-self) + h-sizing (-> (get shape :layout-item-h-sizing) sr/translate-layout-sizing) + v-sizing (-> (get shape :layout-item-v-sizing) sr/translate-layout-sizing) + align-self (-> (get shape :layout-item-align-self) sr/translate-align-self) - max-h (dm/get-prop shape :layout-item-max-h) - has-max-h (some? max-h) - min-h (dm/get-prop shape :layout-item-min-h) - has-min-h (some? min-h) - max-w (dm/get-prop shape :layout-item-max-w) - has-max-w (some? max-w) - min-w (dm/get-prop shape :layout-item-min-w) - has-min-w (some? min-w) - is-absolute (boolean (dm/get-prop shape :layout-item-absolute)) - z-index (-> (dm/get-prop shape :layout-item-z-index) (or 0))] + max-h (get shape :layout-item-max-h) + has-max-h (some? max-h) + min-h (get shape :layout-item-min-h) + has-min-h (some? min-h) + max-w (get shape :layout-item-max-w) + has-max-w (some? max-w) + min-w (get shape :layout-item-min-w) + has-min-w (some? min-w) + is-absolute (boolean (get shape :layout-item-absolute)) + z-index (get shape :layout-item-z-index)] (h/call wasm/internal-module "_set_layout_child_data" margin-top @@ -582,17 +589,17 @@ h-sizing v-sizing has-max-h - (or max-h 0) + (d/nilv max-h 0) has-min-h - (or min-h 0) + (d/nilv min-h 0) has-max-w - (or max-w 0) + (d/nilv max-w 0) has-min-w - (or min-w 0) + (d/nilv min-w 0) (some? align-self) - (or align-self 0) + (d/nilv align-self 0) is-absolute - z-index))) + (d/nilv z-index)))) (defn clear-layout [] @@ -615,50 +622,63 @@ (defn set-shape-shadows [shadows] (h/call wasm/internal-module "_clear_shape_shadows") - (let [total-shadows (count shadows)] - (loop [index 0] - (when (< index total-shadows) - (let [shadow (nth shadows index) - color (dm/get-prop shadow :color) - blur (dm/get-prop shadow :blur) - rgba (sr-clr/hex->u32argb (dm/get-prop color :color) (dm/get-prop color :opacity)) - hidden (dm/get-prop shadow :hidden) - x (dm/get-prop shadow :offset-x) - y (dm/get-prop shadow :offset-y) - spread (dm/get-prop shadow :spread) - style (dm/get-prop shadow :style)] - (h/call wasm/internal-module "_add_shape_shadow" rgba blur spread x y (sr/translate-shadow-style style) hidden) - (recur (inc index))))))) -;; (declare propagate-apply) + (run! (fn [shadow] + (let [color (get shadow :color) + blur (get shadow :blur) + rgba (sr-clr/hex->u32argb (get color :color) + (get color :opacity)) + hidden (get shadow :hidden) + x (get shadow :offset-x) + y (get shadow :offset-y) + spread (get shadow :spread) + style (get shadow :style)] + (h/call wasm/internal-module "_add_shape_shadow" + rgba + blur + spread + x + y + (sr/translate-shadow-style style) + hidden))) + shadows)) (defn set-shape-text-content [shape-id content] (h/call wasm/internal-module "_clear_shape_text") - (set-shape-vertical-align (dm/get-prop content :vertical-align)) + (set-shape-vertical-align (get content :vertical-align)) - (let [paragraph-set (first (dm/get-prop content :children)) - paragraphs (dm/get-prop paragraph-set :children) - fonts (fonts/get-content-fonts content) - emoji? (atom false) - languages (atom #{})] - (loop [index 0] - (when (< index (count paragraphs)) + (let [paragraph-set (first (get content :children)) + paragraphs (get paragraph-set :children) + fonts (fonts/get-content-fonts content) + total (count paragraphs)] + + (loop [index 0 + emoji? false + langs #{}] + + (if (< index total) (let [paragraph (nth paragraphs index) - leaves (dm/get-prop paragraph :children)] - (when (seq leaves) - (let [text (apply str (map :text leaves))] - (when (and (not @emoji?) (t/contains-emoji? text)) - (reset! emoji? true)) - (swap! languages into (t/get-languages text)) - (t/write-shape-text leaves paragraph text)) - (recur (inc index)))))) + leaves (get paragraph :children)] + (if (empty? (seq leaves)) + (recur (inc index) + emoji? + langs) - (let [updated-fonts - (-> fonts - (cond-> @emoji? (f/add-emoji-font)) - (f/add-noto-fonts @languages))] - (f/store-fonts shape-id updated-fonts)))) + (let [text (apply str (map :text leaves)) + emoji? (if emoji? emoji? (t/contains-emoji? text)) + langs (t/collect-used-languages langs text)] + + (t/write-shape-text leaves paragraph text) + (recur (inc index) + emoji? + langs)))) + + (let [updated-fonts + (-> fonts + (cond-> ^boolean emoji? (f/add-emoji-font)) + (f/add-noto-fonts langs))] + (f/store-fonts shape-id updated-fonts)))))) (defn set-shape-text [shape-id content] @@ -670,16 +690,17 @@ [grow-type] (h/call wasm/internal-module "_set_shape_grow_type" (sr/translate-grow-type grow-type))) -(defn text-dimensions +(defn get-text-dimensions ([id] (use-shape id) - (text-dimensions)) + (get-text-dimensions)) ([] - (let [offset (h/call wasm/internal-module "_get_text_dimensions") - heapf32 (mem/get-heap-f32) - width (aget heapf32 (mem/->offset-32 offset)) - height (aget heapf32 (mem/->offset-32 (+ offset 4))) - max-width (aget heapf32 (mem/->offset-32 (+ offset 8)))] + (let [offset (-> (h/call wasm/internal-module "_get_text_dimensions") + (mem/->offset-32)) + heapf32 (mem/get-heap-f32) + width (aget heapf32 (+ offset 0)) + height (aget heapf32 (+ offset 1)) + max-width (aget heapf32 (+ offset 2))] (mem/free) {:width width :height height :max-width max-width}))) @@ -699,37 +720,34 @@ [objects shape] (perf/begin-measure "set-object") (let [id (dm/get-prop shape :id) - parent-id (dm/get-prop shape :parent-id) type (dm/get-prop shape :type) - masked (dm/get-prop shape :masked-group) - selrect (dm/get-prop shape :selrect) - constraint-h (dm/get-prop shape :constraints-h) - constraint-v (dm/get-prop shape :constraints-v) + + parent-id (get shape :parent-id) + masked (get shape :masked-group) + selrect (get shape :selrect) + constraint-h (get shape :constraints-h) + constraint-v (get shape :constraints-v) clip-content (if (= type :frame) - (not (dm/get-prop shape :show-content)) + (not (get shape :show-content)) false) - rotation (dm/get-prop shape :rotation) - transform (dm/get-prop shape :transform) + rotation (get shape :rotation) + transform (get shape :transform) ;; Groups from imported SVG's can have their own fills - fills (dm/get-prop shape :fills) + fills (get shape :fills) strokes (if (= type :group) - [] (dm/get-prop shape :strokes)) - children (dm/get-prop shape :shapes) - blend-mode (dm/get-prop shape :blend-mode) - opacity (dm/get-prop shape :opacity) - hidden (dm/get-prop shape :hidden) - content (dm/get-prop shape :content) - bool-type (dm/get-prop shape :bool-type) - grow-type (dm/get-prop shape :grow-type) - blur (dm/get-prop shape :blur) - svg-attrs (dm/get-prop shape :svg-attrs) - shadows (dm/get-prop shape :shadow) - r1 (dm/get-prop shape :r1) - r2 (dm/get-prop shape :r2) - r3 (dm/get-prop shape :r3) - r4 (dm/get-prop shape :r4)] + [] (get shape :strokes)) + children (get shape :shapes) + blend-mode (get shape :blend-mode) + opacity (get shape :opacity) + hidden (get shape :hidden) + content (get shape :content) + bool-type (get shape :bool-type) + grow-type (get shape :grow-type) + blur (get shape :blur) + svg-attrs (get shape :svg-attrs) + shadows (get shape :shadow)] (use-shape id) (set-parent-id parent-id) @@ -743,7 +761,7 @@ (set-shape-opacity opacity) (set-shape-hidden hidden) (set-shape-children children) - (set-shape-corners [r1 r2 r3 r4]) + (set-shape-corners shape) (when (and (= type :group) masked) (set-masked masked)) (when (some? blur) @@ -773,7 +791,6 @@ (perf/end-measure "set-object") pending))) - (defn process-pending [pending] (let [event (js/CustomEvent. "wasm:set-objects-finished") diff --git a/frontend/src/app/render_wasm/api/fonts.cljs b/frontend/src/app/render_wasm/api/fonts.cljs index 4d391c19a5..7bcb176d06 100644 --- a/frontend/src/app/render_wasm/api/fonts.cljs +++ b/frontend/src/app/render_wasm/api/fonts.cljs @@ -151,21 +151,17 @@ "italic" 1 0)) -(defn serialize-font-id +(defn normalize-font-id [font-id] (try - (if (nil? font-id) - (do - [uuid/zero]) - (let [google-font? (str/starts-with? font-id "gfont-")] - (if google-font? - (uuid/get-u32 (google-font-id->uuid font-id)) - (let [no-prefix (subs font-id (inc (str/index-of font-id "-")))] - (if (or (nil? no-prefix) (not (string? no-prefix)) (str/blank? no-prefix)) - [uuid/zero] - (uuid/get-u32 (uuid/uuid no-prefix))))))) + (if ^boolean (str/starts-with? font-id "gfont-") + (google-font-id->uuid font-id) + (let [no-prefix (subs font-id (inc (str/index-of font-id "-")))] + (if (or (nil? no-prefix) (not (string? no-prefix)) (str/blank? no-prefix)) + uuid/zero + (uuid/parse no-prefix)))) (catch :default _e - [uuid/zero]))) + uuid/zero))) (defn serialize-font-weight [font-weight] diff --git a/frontend/src/app/render_wasm/api/texts.cljs b/frontend/src/app/render_wasm/api/texts.cljs index 5aff401ef5..9f6dbd4575 100644 --- a/frontend/src/app/render_wasm/api/texts.cljs +++ b/frontend/src/app/render_wasm/api/texts.cljs @@ -6,24 +6,31 @@ (ns app.render-wasm.api.texts (:require + [app.common.data :as d] [app.common.types.fills.impl :as types.fills.impl] + [app.common.uuid :as uuid] [app.render-wasm.api.fonts :as f] [app.render-wasm.helpers :as h] [app.render-wasm.mem :as mem] [app.render-wasm.serializers :as sr] [app.render-wasm.wasm :as wasm])) -(defn utf8->buffer [text] +(def ^:const PARAGRAPH-ATTR-U8-SIZE 44) +(def ^:const LEAF-ATTR-U8-SIZE 56) + +(defn- encode-text + "Into an UTF8 buffer. Returns an ArrayBuffer instance" + [text] (let [encoder (js/TextEncoder.)] (.encode encoder text))) -(defn set-text-leaf-fills - [fills current-offset dview] +(defn- write-leaf-fills + [offset dview fills] (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)] + (let [opacity (get fill :fill-opacity 1.0) + color (get fill :fill-color) + gradient (get fill :fill-color-gradient) + image (get fill :fill-image)] (cond (some? color) @@ -33,115 +40,119 @@ (types.fills.impl/write-gradient-fill offset dview opacity gradient) (some? image) - (types.fills.impl/write-image-fill offset dview opacity image)) + (types.fills.impl/write-image-fill offset dview opacity image)))) - (+ offset types.fills.impl/FILL-U8-SIZE))) - current-offset + offset fills)) -(defn total-fills-count +(defn- get-total-fills [leaves] (reduce #(+ %1 (count (:fills %2))) 0 leaves)) +(defn- write-paragraph + [offset dview paragraph] + (let [text-align (sr/translate-text-align (get paragraph :text-align)) + text-direction (sr/translate-text-direction (get paragraph :text-direction)) + text-decoration (sr/translate-text-decoration (get paragraph :text-decoration)) + text-transform (sr/translate-text-transform (get paragraph :text-transform)) + line-height (get paragraph :line-height) + letter-spacing (get paragraph :letter-spacing) + + typography-ref-file (get paragraph :typography-ref-file) + typography-ref-id (get paragraph :typography-ref-id)] + + (-> offset + (mem/write-u8 dview text-align) + (mem/write-u8 dview text-direction) + (mem/write-u8 dview text-decoration) + (mem/write-u8 dview text-transform) + + (mem/write-f32 dview line-height) + (mem/write-f32 dview letter-spacing) + + (mem/write-uuid dview (d/nilv typography-ref-file uuid/zero)) + (mem/write-uuid dview (d/nilv typography-ref-id uuid/zero)) + (mem/assert-written offset PARAGRAPH-ATTR-U8-SIZE)))) + +(defn- write-leaves + [offset dview leaves paragraph] + (reduce (fn [offset leaf] + (let [font-style (sr/translate-font-style (get leaf :font-style)) + font-size (get leaf :font-size) + font-weight (get leaf :font-weight) + font-id (f/normalize-font-id (get leaf :font-id)) + font-family (hash (get leaf :font-family)) + + text-buffer (encode-text (get leaf :text)) + text-length (mem/size text-buffer) + fills (get leaf :fills) + total-fills (count fills) + + font-variant-id + (get leaf :font-variant-id) + + font-variant-id + (if (uuid? font-variant-id) + font-variant-id + uuid/zero) + + text-decoration + (or (sr/translate-text-decoration (:text-decoration leaf)) + (sr/translate-text-decoration (:text-decoration paragraph)) + (sr/translate-text-decoration "none")) + + text-transform + (or (sr/translate-text-transform (:text-transform leaf)) + (sr/translate-text-transform (:text-transform paragraph)) + (sr/translate-text-transform "none"))] + + (-> offset + (mem/write-u8 dview font-style) + (mem/write-u8 dview text-decoration) + (mem/write-u8 dview text-transform) + (+ 1) ;;padding + + (mem/write-f32 dview font-size) + (mem/write-u32 dview font-weight) + + (mem/write-uuid dview font-id) + (mem/write-i32 dview font-family) + (mem/write-uuid dview (d/nilv font-variant-id uuid/zero)) + + (mem/write-i32 dview text-length) + (mem/write-i32 dview total-fills) + (mem/assert-written offset LEAF-ATTR-U8-SIZE) + + (write-leaf-fills dview fills)))) + offset + leaves)) + (defn write-shape-text ;; buffer has the following format: ;; [ ] [leaves paragraph text] - (let [le? true - num-leaves (count leaves) - paragraph-attr-size 48 - total-fills (total-fills-count leaves) - total-fills-size (* types.fills.impl/FILL-U8-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-size (.-byteLength text-buffer) - buffer (js/ArrayBuffer. (+ metadata-size text-size)) - dview (js/DataView. buffer)] + (let [num-leaves (count leaves) + fills-size (* types.fills.impl/FILL-U8-SIZE + (get-total-fills leaves)) + metadata-size (+ PARAGRAPH-ATTR-U8-SIZE + (* num-leaves LEAF-ATTR-U8-SIZE) + fills-size) - (.setUint32 dview 0 num-leaves le?) + text-buffer (encode-text text) + text-size (mem/size text-buffer) - ;; Serialize paragraph attributes - (let [text-align (sr/serialize-text-align (:text-align paragraph)) - text-direction (sr/serialize-text-direction (:text-direction paragraph)) - text-decoration (sr/serialize-text-decoration (:text-decoration paragraph)) - text-transform (sr/serialize-text-transform (:text-transform paragraph)) - line-height (:line-height paragraph) - letter-spacing (:letter-spacing paragraph) - typography-ref-file (sr/serialize-uuid (:typography-ref-file paragraph)) - typography-ref-id (sr/serialize-uuid (:typography-ref-id paragraph))] + total-size (+ 4 metadata-size text-size) + heapu8 (mem/get-heap-u8) + dview (mem/get-data-view) + offset (mem/alloc total-size)] - (.setUint8 dview 4 text-align le?) - (.setUint8 dview 5 text-direction le?) - (.setUint8 dview 6 text-decoration le?) - (.setUint8 dview 7 text-transform le?) + (-> offset + (mem/write-u32 dview num-leaves) + (write-paragraph dview paragraph) + (write-leaves dview leaves paragraph) + (mem/write-buffer heapu8 text-buffer)) - (.setFloat32 dview 8 line-height le?) - (.setFloat32 dview 12 letter-spacing le?) - - (.setUint32 dview 16 (aget typography-ref-file 0) le?) - (.setUint32 dview 20 (aget typography-ref-file 1) le?) - (.setUint32 dview 24 (aget typography-ref-file 2) le?) - (.setInt32 dview 28 (aget typography-ref-file 3) le?) - - (.setUint32 dview 32 (aget typography-ref-id 0) le?) - (.setUint32 dview 36 (aget typography-ref-id 1) le?) - (.setUint32 dview 40 (aget typography-ref-id 2) le?) - (.setInt32 dview 44 (aget typography-ref-id 3) le?)) - - ;; Serialize leaves attributes - (loop [index 0 offset paragraph-attr-size] - (when (< index num-leaves) - (let [leaf (nth leaves index) - font-style (f/serialize-font-style (:font-style leaf)) - font-size (:font-size leaf) - font-weight (:font-weight leaf) - font-id (f/serialize-font-id (:font-id leaf)) - font-family (hash (:font-family leaf)) - font-variant-id (sr/serialize-uuid (:font-variant-id leaf)) - leaf-text-decoration (or (sr/serialize-text-decoration (:text-decoration leaf)) (sr/serialize-text-decoration (:text-decoration paragraph))) - leaf-text-transform (or (sr/serialize-text-transform (:text-transform leaf)) (sr/serialize-text-transform (:text-transform paragraph))) - text-buffer (utf8->buffer (:text leaf)) - text-length (.-byteLength text-buffer) - fills (:fills leaf) - total-fills (count fills)] - - (.setUint8 dview offset font-style le?) - (.setUint8 dview (+ offset 1) leaf-text-decoration le?) - (.setUint8 dview (+ offset 2) leaf-text-transform le?) - - (.setFloat32 dview (+ offset 4) font-size le?) - (.setUint32 dview (+ offset 8) font-weight le?) - (.setUint32 dview (+ offset 12) (aget font-id 0) le?) - (.setUint32 dview (+ offset 16) (aget font-id 1) le?) - (.setUint32 dview (+ offset 20) (aget font-id 2) le?) - (.setInt32 dview (+ offset 24) (aget font-id 3) le?) - - (.setInt32 dview (+ offset 28) font-family le?) - - (.setUint32 dview (+ offset 32) (aget font-variant-id 0) le?) - (.setUint32 dview (+ offset 36) (aget font-variant-id 1) le?) - (.setUint32 dview (+ offset 40) (aget font-variant-id 2) le?) - (.setInt32 dview (+ offset 44) (aget font-variant-id 3) le?) - - (.setInt32 dview (+ offset 48) text-length le?) - (.setInt32 dview (+ offset 52) total-fills le?) - - (let [new-offset (set-text-leaf-fills fills (+ offset leaf-attr-size) dview)] - (recur (inc index) new-offset))))) - - ;; Add text content to buffer - (let [text-offset metadata-size - buffer-u8 (js/Uint8Array. buffer)] - (.set buffer-u8 (js/Uint8Array. text-buffer) text-offset)) - - ;; Allocate memory and set buffer - (let [total-size (.-byteLength buffer) - metadata-offset (mem/alloc total-size) - heap (mem/get-heap-u8)] - (.set heap (js/Uint8Array. buffer) metadata-offset))) - - (h/call wasm/internal-module "_set_shape_text_content")) + (h/call wasm/internal-module "_set_shape_text_content"))) (def ^:private emoji-pattern #"[\uD83C-\uDBFF][\uDC00-\uDFFF]|[\u2600-\u27BF]") @@ -199,10 +210,20 @@ (defn contains-emoji? [text] (boolean (some #(re-find emoji-pattern %) (seq text)))) -(defn get-languages [text] +(defn collect-used-languages + [used text] (reduce-kv (fn [result lang pattern] - (if (re-find pattern text) + (cond + ;; Skip regex operation if we already know that + ;; langage is present + (contains? result lang) + result + + (re-find pattern text) (conj result lang) + + :else result)) - #{} + used unicode-ranges)) + diff --git a/frontend/src/app/render_wasm/mem.cljs b/frontend/src/app/render_wasm/mem.cljs index 59562329c8..affccbc16c 100644 --- a/frontend/src/app/render_wasm/mem.cljs +++ b/frontend/src/app/render_wasm/mem.cljs @@ -67,12 +67,6 @@ [heap offset size] (.slice ^js heap offset (+ offset size))) -(defn view - "Returns a new typed array on the same ArrayBuffer store and with the - same element types as for this typed array." - [heap offset size] - (.subarray ^js heap offset (+ offset size))) - (defn get-data-view "Returns a heap wrapped in a DataView for surgical write operations" [] @@ -80,10 +74,64 @@ (defn write-u8 "Write unsigned int8. Expects a DataView instance" - [target offset value] - (buf/write-byte target offset value)) + [offset target value] + (buf/write-u8 target offset value) + (+ offset 1)) (defn write-f32 "Write float32. Expects a DataView instance" - [target offset value] - (buf/write-float target offset value)) + [offset target value] + (buf/write-f32 target offset value) + (+ offset 4)) + +(defn write-i32 + "Write int32. Expects a DataView instance" + [offset target value] + (buf/write-i32 target offset value) + (+ offset 4)) + +(defn write-u32 + "Write int32. Expects a DataView instance" + [offset target value] + (buf/write-i32 target offset value) + (+ offset 4)) + +(defn write-bool + "Write int32. Expects a DataView instance" + [offset target value] + (buf/write-bool target offset value) + (+ offset 1)) + +(defn write-uuid + "Write uuid. Expects a DataView instance" + [offset target value] + (buf/write-uuid target offset value) + (+ offset 16)) + +(defn write-buffer + [offset target value] + (assert (instance? js/Uint8Array target) "target should be u8 addressable heap") + + (let [value (cond + (instance? js/ArrayBuffer value) + (new js/Uint8Array. value) + + (instance? js/Uint8Array value) + value + + :else + (throw (js/Error. "unexpected type")))] + (.set ^js target value offset) + (+ offset (.-byteLength value)))) + +(defn assert-written + [final-offset prev-offset expected] + (assert (= expected (- final-offset prev-offset)) + (str "expected to be written " expected " but finally writted " (- final-offset prev-offset))) + final-offset) + +(defn size + "Get buffer size" + [o] + (.-byteLength ^js o)) + diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index 5efd965b6d..13fc6a62c3 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -227,7 +227,8 @@ (case value :fill 0 :fix 1 - :auto 2)) + :auto 2 + 1)) (defn translate-align-self [value] @@ -270,26 +271,51 @@ :auto-height 2 0)) -(defn- serialize-enum - [value enum-map] - (get enum-map value 0)) - -(defn serialize-vertical-align +(defn translate-vertical-align [vertical-align] - (serialize-enum vertical-align {"top" 0 "center" 1 "bottom" 2})) + (case vertical-align + "top" 0 + "center" 1 + "bottom" 2 + 0)) -(defn serialize-text-align +(defn translate-text-align [text-align] - (serialize-enum text-align {"left" 0 "center" 1 "right" 2 "justify" 3})) + (case text-align + "left" 0 + "center" 1 + "right" 2 + "justify" 3 + 0)) -(defn serialize-text-transform +(defn translate-text-transform [text-transform] - (serialize-enum text-transform {"none" 0 "uppercase" 1 "lowercase" 2 "capitalize" 3})) + (case text-transform + "none" 0 + "uppercase" 1 + "lowercase" 2 + "capitalize" 3 + nil)) -(defn serialize-text-decoration +(defn translate-text-decoration [text-decoration] - (serialize-enum text-decoration {"none" 0 "underline" 1 "line-through" 2 "overline" 3})) + (case text-decoration + "none" 0 + "underline" 1 + "line-through" 2 + "overline" 3 + nil)) -(defn serialize-text-direction +(defn translate-text-direction [text-direction] - (serialize-enum text-direction {"ltr" 0 "rtl" 1})) + (case text-direction + "ltr" 0 + "rtl" 1)) + +(defn translate-font-style + [font-style] + (case font-style + "normal" 0 + "regular" 0 + "italic" 1 + 0))