From ae3aef8dccd3e0a31e5feb2d4bc768cf7e543f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Fri, 1 Aug 2025 15:57:31 +0200 Subject: [PATCH 1/2] :sparkles: Use existing space for storing image fill flags --- common/src/app/common/buffer.cljc | 7 +++++++ common/src/app/common/types/fills/impl.cljc | 21 +++++++++++++-------- render-wasm/src/wasm/fills/image.rs | 13 ++++++++----- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/common/src/app/common/buffer.cljc b/common/src/app/common/buffer.cljc index 74514a3951..69413db7db 100644 --- a/common/src/app/common/buffer.cljc +++ b/common/src/app/common/buffer.cljc @@ -21,6 +21,13 @@ (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] `(long (.get ~target ~offset))))) +(defmacro read-unsigned-byte + [target offset] + (if (:ns &env) + `(.getUint8 ~target ~offset true) + (let [target (with-meta target {:tag 'java.nio.ByteBuffer})] + `(bit-and (long (.get ~target ~offset)) 0xff)))) + (defmacro read-bool [target offset] (if (:ns &env) diff --git a/common/src/app/common/types/fills/impl.cljc b/common/src/app/common/types/fills/impl.cljc index fb41340a45..73cdb2eec0 100644 --- a/common/src/app/common/types/fills/impl.cljc +++ b/common/src/app/common/types/fills/impl.cljc @@ -121,13 +121,16 @@ (let [image-id (get image :id) image-width (get image :width) image-height (get image :height) - keep-aspect-ratio (get image :keep-aspect-ratio false)] + opacity (mth/floor (* opacity 0xff)) + keep-aspect-ratio (if (get image :keep-aspect-ratio false) 0x01 0x00) + flags (bit-or keep-aspect-ratio 0x00)] (buf/write-byte buffer (+ offset 0) 0x03) (buf/write-uuid buffer (+ offset 4) image-id) - (buf/write-float buffer (+ offset 20) opacity) + (buf/write-byte buffer (+ offset 20) opacity) + (buf/write-byte buffer (+ offset 21) flags) + (buf/write-short buffer (+ offset 22) 0) ;; 2-byte padding (reserved for future use) (buf/write-int buffer (+ offset 24) image-width) (buf/write-int buffer (+ offset 28) image-height) - (buf/write-bool buffer (+ offset 32) keep-aspect-ratio) (+ offset FILL-BYTE-SIZE))) (defn- write-metadata @@ -202,10 +205,12 @@ :stops stops :type type}}) - 3 - (let [ratio (buf/read-bool dbuffer (+ doffset 32)) - id (buf/read-uuid dbuffer (+ doffset 4)) - alpha (buf/read-float dbuffer (+ doffset 20)) + 3 ;; image fill + (let [id (buf/read-uuid dbuffer (+ doffset 4)) + alpha (buf/read-unsigned-byte dbuffer (+ doffset 20)) + opacity (mth/precision (/ alpha 0xff) 2) + flags (buf/read-unsigned-byte dbuffer (+ doffset 21)) + ratio (boolean (bit-and flags 0x01)) width (buf/read-int dbuffer (+ doffset 24)) height (buf/read-int dbuffer (+ doffset 28)) mtype (buf/read-short mbuffer (+ moffset 2)) @@ -215,7 +220,7 @@ 0x03 "image/gif" 0x04 "image/webp" 0x05 "image/svg+xml")] - {:fill-opacity alpha + {:fill-opacity opacity :fill-image {:id id :width width :height height diff --git a/render-wasm/src/wasm/fills/image.rs b/render-wasm/src/wasm/fills/image.rs index 07ae137d37..ab059d3ecd 100644 --- a/render-wasm/src/wasm/fills/image.rs +++ b/render-wasm/src/wasm/fills/image.rs @@ -1,5 +1,7 @@ use crate::{shapes::ImageFill, utils::uuid_from_u32_quartet}; +const FLAG_KEEP_ASPECT_RATIO: u8 = 1 << 0; + #[derive(Debug, Clone, Copy, PartialEq)] #[repr(C)] #[repr(align(4))] @@ -8,23 +10,24 @@ pub struct RawImageFillData { b: u32, c: u32, d: u32, - opacity: f32, + opacity: u8, + flags: u8, + _padding: u16, // reserved for future use width: i32, height: i32, - keep_aspect_ratio: i32, } impl From for ImageFill { fn from(value: RawImageFillData) -> Self { let id = uuid_from_u32_quartet(value.a, value.b, value.c, value.d); - let opacity = (value.opacity * 255.).floor() as u8; + let keep_aspect_ratio = value.flags & FLAG_KEEP_ASPECT_RATIO != 0; Self::new( id, - opacity, + value.opacity, value.width, value.height, - value.keep_aspect_ratio != 0, + keep_aspect_ratio, ) } } From 6fc949844d1d68e4a7e2c4b0ab3f5e1de09ac373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Mon, 4 Aug 2025 14:13:40 +0200 Subject: [PATCH 2/2] :sparkles: Use 1 byte to store opacity in gradient fills --- common/src/app/common/types/fills/impl.cljc | 40 +++++++++++---------- render-wasm/src/wasm/fills/gradient.rs | 5 +-- render-wasm/src/wasm/fills/image.rs | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/common/src/app/common/types/fills/impl.cljc b/common/src/app/common/types/fills/impl.cljc index 73cdb2eec0..eec8271c52 100644 --- a/common/src/app/common/types/fills/impl.cljc +++ b/common/src/app/common/types/fills/impl.cljc @@ -86,6 +86,7 @@ start-y (:start-y gradient) end-x (:end-x gradient) end-y (:end-y gradient) + alpha (mth/floor (* opacity 0xff)) width (:width gradient 0) stops (into [] xf:take-stops (:stops gradient)) type (if (= (:type gradient) :linear) @@ -97,7 +98,7 @@ (buf/write-float buffer (+ offset 8) start-y) (buf/write-float buffer (+ offset 12) end-x) (buf/write-float buffer (+ offset 16) end-y) - (buf/write-float buffer (+ offset 20) opacity) + (buf/write-byte buffer (+ offset 20) alpha) (buf/write-float buffer (+ offset 24) width) (buf/write-byte buffer (+ offset 28) (count stops)) @@ -121,12 +122,12 @@ (let [image-id (get image :id) image-width (get image :width) image-height (get image :height) - opacity (mth/floor (* opacity 0xff)) + alpha (mth/floor (* opacity 0xff)) keep-aspect-ratio (if (get image :keep-aspect-ratio false) 0x01 0x00) flags (bit-or keep-aspect-ratio 0x00)] (buf/write-byte buffer (+ offset 0) 0x03) (buf/write-uuid buffer (+ offset 4) image-id) - (buf/write-byte buffer (+ offset 20) opacity) + (buf/write-byte buffer (+ offset 20) alpha) (buf/write-byte buffer (+ offset 21) flags) (buf/write-short buffer (+ offset 22) 0) ;; 2-byte padding (reserved for future use) (buf/write-int buffer (+ offset 24) image-width) @@ -183,9 +184,10 @@ start-y (buf/read-float dbuffer (+ doffset 8)) end-x (buf/read-float dbuffer (+ doffset 12)) end-y (buf/read-float dbuffer (+ doffset 16)) - alpha (buf/read-float dbuffer (+ doffset 20)) + alpha (buf/read-unsigned-byte dbuffer (+ doffset 20)) width (buf/read-float dbuffer (+ doffset 24)) stops (buf/read-byte dbuffer (+ doffset 28)) + opacity (mth/precision (/ alpha 0xff) 2) type (if (= type 1) :linear :radial) @@ -196,7 +198,7 @@ (conj result (read-stop dbuffer (+ doffset 32 (* GRADIENT-STOP-SIZE index))))) result))] - {:fill-opacity alpha + {:fill-opacity opacity :fill-color-gradient {:start-x start-x :start-y start-y :end-x end-x @@ -206,20 +208,20 @@ :type type}}) 3 ;; image fill - (let [id (buf/read-uuid dbuffer (+ doffset 4)) - alpha (buf/read-unsigned-byte dbuffer (+ doffset 20)) - opacity (mth/precision (/ alpha 0xff) 2) - flags (buf/read-unsigned-byte dbuffer (+ doffset 21)) - ratio (boolean (bit-and flags 0x01)) - width (buf/read-int dbuffer (+ doffset 24)) - height (buf/read-int dbuffer (+ doffset 28)) - mtype (buf/read-short mbuffer (+ moffset 2)) - mtype (case mtype - 0x01 "image/jpeg" - 0x02 "image/png" - 0x03 "image/gif" - 0x04 "image/webp" - 0x05 "image/svg+xml")] + (let [id (buf/read-uuid dbuffer (+ doffset 4)) + alpha (buf/read-unsigned-byte dbuffer (+ doffset 20)) + opacity (mth/precision (/ alpha 0xff) 2) + flags (buf/read-unsigned-byte dbuffer (+ doffset 21)) + ratio (boolean (bit-and flags 0x01)) + width (buf/read-int dbuffer (+ doffset 24)) + height (buf/read-int dbuffer (+ doffset 28)) + mtype (buf/read-short mbuffer (+ moffset 2)) + mtype (case mtype + 0x01 "image/jpeg" + 0x02 "image/png" + 0x03 "image/gif" + 0x04 "image/webp" + 0x05 "image/svg+xml")] {:fill-opacity opacity :fill-image {:id id :width width diff --git a/render-wasm/src/wasm/fills/gradient.rs b/render-wasm/src/wasm/fills/gradient.rs index dcb2a81fa9..c656cf197e 100644 --- a/render-wasm/src/wasm/fills/gradient.rs +++ b/render-wasm/src/wasm/fills/gradient.rs @@ -10,7 +10,8 @@ pub struct RawGradientData { start_y: f32, end_x: f32, end_y: f32, - opacity: f32, + opacity: u8, + // 24-bit padding here, reserved for future use width: f32, stop_count: u8, stops: [RawStopData; MAX_GRADIENT_STOPS], @@ -55,7 +56,7 @@ impl From for Gradient { Gradient::new( raw_gradient.start(), raw_gradient.end(), - (raw_gradient.opacity * 255.) as u8, + raw_gradient.opacity, raw_gradient.width, &stops, ) diff --git a/render-wasm/src/wasm/fills/image.rs b/render-wasm/src/wasm/fills/image.rs index ab059d3ecd..15ab5e037f 100644 --- a/render-wasm/src/wasm/fills/image.rs +++ b/render-wasm/src/wasm/fills/image.rs @@ -12,7 +12,7 @@ pub struct RawImageFillData { d: u32, opacity: u8, flags: u8, - _padding: u16, // reserved for future use + // 16-bit padding here, reserved for future use width: i32, height: i32, }