mirror of
https://github.com/penpot/penpot.git
synced 2026-05-26 10:23:45 +00:00
🔧 Batch blur and shadow effects into single WASM call
This commit is contained in:
parent
c372b1f668
commit
0873a2fbc4
@ -17,6 +17,7 @@
|
|||||||
[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.wasm :as wasm]))
|
[app.render-wasm.wasm :as wasm]))
|
||||||
|
|
||||||
;; Binary layout constants matching Rust implementation:
|
;; Binary layout constants matching Rust implementation:
|
||||||
@ -76,7 +77,7 @@
|
|||||||
[0.0 0.0 0.0 0.0]))
|
[0.0 0.0 0.0 0.0]))
|
||||||
|
|
||||||
(defn set-shape-base-props
|
(defn set-shape-base-props
|
||||||
"Set all base shape properties in a single WASM call.
|
"Set all base shape properties (and optionally children) in a single WASM call.
|
||||||
|
|
||||||
This replaces the following individual calls:
|
This replaces the following individual calls:
|
||||||
- use-shape
|
- use-shape
|
||||||
@ -91,103 +92,206 @@
|
|||||||
- set-shape-selrect
|
- set-shape-selrect
|
||||||
- set-shape-corners
|
- set-shape-corners
|
||||||
- set-shape-constraints (clear + h + v)
|
- set-shape-constraints (clear + h + v)
|
||||||
|
- set-shape-children (when include-children? is true)
|
||||||
|
|
||||||
Returns nil."
|
Returns nil."
|
||||||
[shape]
|
([shape] (set-shape-base-props shape false))
|
||||||
|
([shape include-children?]
|
||||||
|
(when wasm/context-initialized?
|
||||||
|
(let [id (dm/get-prop shape :id)
|
||||||
|
parent-id (get shape :parent-id)
|
||||||
|
shape-type (dm/get-prop shape :type)
|
||||||
|
|
||||||
|
clip-content (if (= shape-type :frame)
|
||||||
|
(not (get shape :show-content))
|
||||||
|
false)
|
||||||
|
hidden (get shape :hidden false)
|
||||||
|
|
||||||
|
flags (cond-> 0
|
||||||
|
clip-content (bit-or FLAG-CLIP-CONTENT)
|
||||||
|
hidden (bit-or FLAG-HIDDEN))
|
||||||
|
|
||||||
|
blend-mode (sr/translate-blend-mode (get shape :blend-mode))
|
||||||
|
constraint-h (let [c (get shape :constraints-h)]
|
||||||
|
(if (some? c)
|
||||||
|
(sr/translate-constraint-h c)
|
||||||
|
CONSTRAINT-NONE))
|
||||||
|
constraint-v (let [c (get shape :constraints-v)]
|
||||||
|
(if (some? c)
|
||||||
|
(sr/translate-constraint-v c)
|
||||||
|
CONSTRAINT-NONE))
|
||||||
|
|
||||||
|
opacity (d/nilv (get shape :opacity) 1.0)
|
||||||
|
rotation (d/nilv (get shape :rotation) 0.0)
|
||||||
|
|
||||||
|
;; Transform matrix
|
||||||
|
[ta tb tc td te tf] (serialize-transform (get shape :transform))
|
||||||
|
|
||||||
|
;; Selrect
|
||||||
|
selrect (get shape :selrect)
|
||||||
|
[sx1 sy1 sx2 sy2] (serialize-selrect selrect)
|
||||||
|
|
||||||
|
;; Corners
|
||||||
|
r1 (d/nilv (get shape :r1) 0.0)
|
||||||
|
r2 (d/nilv (get shape :r2) 0.0)
|
||||||
|
r3 (d/nilv (get shape :r3) 0.0)
|
||||||
|
r4 (d/nilv (get shape :r4) 0.0)
|
||||||
|
|
||||||
|
;; Children (when batched)
|
||||||
|
children (when include-children?
|
||||||
|
(into [] (filter uuid?) (get shape :shapes)))
|
||||||
|
child-count (if include-children? (count children) 0)
|
||||||
|
|
||||||
|
;; Total buffer: 104 base + (4 child_count + 16*N child UUIDs) when batched
|
||||||
|
total-size (if include-children?
|
||||||
|
(+ BASE-PROPS-SIZE 4 (* child-count 16))
|
||||||
|
BASE-PROPS-SIZE)
|
||||||
|
|
||||||
|
;; Allocate buffer and get DataView
|
||||||
|
offset (mem/alloc total-size)
|
||||||
|
heap (mem/get-heap-u8)
|
||||||
|
dview (js/DataView. (.-buffer heap))]
|
||||||
|
|
||||||
|
;; Write id (offset 0, 16 bytes)
|
||||||
|
(write-uuid-to-heap dview offset id)
|
||||||
|
|
||||||
|
;; Write parent_id (offset 16, 16 bytes)
|
||||||
|
(write-uuid-to-heap dview (+ offset 16) (d/nilv parent-id uuid/zero))
|
||||||
|
|
||||||
|
;; Write shape_type (offset 32, 1 byte)
|
||||||
|
(.setUint8 dview (+ offset 32) (sr/translate-shape-type shape-type))
|
||||||
|
|
||||||
|
;; Write flags (offset 33, 1 byte)
|
||||||
|
(.setUint8 dview (+ offset 33) flags)
|
||||||
|
|
||||||
|
;; Write blend_mode (offset 34, 1 byte)
|
||||||
|
(.setUint8 dview (+ offset 34) blend-mode)
|
||||||
|
|
||||||
|
;; Write constraint_h (offset 35, 1 byte)
|
||||||
|
(.setUint8 dview (+ offset 35) constraint-h)
|
||||||
|
|
||||||
|
;; Write constraint_v (offset 36, 1 byte)
|
||||||
|
(.setUint8 dview (+ offset 36) constraint-v)
|
||||||
|
|
||||||
|
;; Padding at offset 37-39 (already zero from alloc)
|
||||||
|
|
||||||
|
;; Write opacity (offset 40, f32)
|
||||||
|
(.setFloat32 dview (+ offset 40) opacity true)
|
||||||
|
|
||||||
|
;; Write rotation (offset 44, f32)
|
||||||
|
(.setFloat32 dview (+ offset 44) rotation true)
|
||||||
|
|
||||||
|
;; Write transform matrix (offset 48, 6 × f32)
|
||||||
|
(.setFloat32 dview (+ offset 48) ta true)
|
||||||
|
(.setFloat32 dview (+ offset 52) tb true)
|
||||||
|
(.setFloat32 dview (+ offset 56) tc true)
|
||||||
|
(.setFloat32 dview (+ offset 60) td true)
|
||||||
|
(.setFloat32 dview (+ offset 64) te true)
|
||||||
|
(.setFloat32 dview (+ offset 68) tf true)
|
||||||
|
|
||||||
|
;; Write selrect (offset 72, 4 × f32)
|
||||||
|
(.setFloat32 dview (+ offset 72) sx1 true)
|
||||||
|
(.setFloat32 dview (+ offset 76) sy1 true)
|
||||||
|
(.setFloat32 dview (+ offset 80) sx2 true)
|
||||||
|
(.setFloat32 dview (+ offset 84) sy2 true)
|
||||||
|
|
||||||
|
;; Write corners (offset 88, 4 × f32)
|
||||||
|
(.setFloat32 dview (+ offset 88) r1 true)
|
||||||
|
(.setFloat32 dview (+ offset 92) r2 true)
|
||||||
|
(.setFloat32 dview (+ offset 96) r3 true)
|
||||||
|
(.setFloat32 dview (+ offset 100) r4 true)
|
||||||
|
|
||||||
|
;; Write children (offset 104+) when batched
|
||||||
|
(when include-children?
|
||||||
|
(.setUint32 dview (+ offset 104) child-count true)
|
||||||
|
(loop [i 0
|
||||||
|
cs (seq children)]
|
||||||
|
(when cs
|
||||||
|
(write-uuid-to-heap dview (+ offset 108 (* i 16)) (first cs))
|
||||||
|
(recur (inc i) (next cs)))))
|
||||||
|
|
||||||
|
(h/call wasm/internal-module "_set_shape_base_props")
|
||||||
|
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
;; Binary layout for batched blur + shadows:
|
||||||
|
;;
|
||||||
|
;; Header (12 bytes):
|
||||||
|
;; | Offset | Size | Field | Type |
|
||||||
|
;; |--------|------|---------------|------------|
|
||||||
|
;; | 0 | 1 | blur_present | u8 |
|
||||||
|
;; | 1 | 1 | blur_type | u8 |
|
||||||
|
;; | 2 | 1 | blur_hidden | u8 |
|
||||||
|
;; | 3 | 1 | padding | - |
|
||||||
|
;; | 4 | 4 | blur_value | f32 LE |
|
||||||
|
;; | 8 | 4 | shadow_count | u32 LE |
|
||||||
|
;;
|
||||||
|
;; Per shadow (24 bytes each):
|
||||||
|
;; | Offset | Size | Field | Type |
|
||||||
|
;; |--------|------|----------|------------|
|
||||||
|
;; | 0 | 4 | color | u32 LE |
|
||||||
|
;; | 4 | 4 | blur | f32 LE |
|
||||||
|
;; | 8 | 4 | spread | f32 LE |
|
||||||
|
;; | 12 | 4 | offset_x | f32 LE |
|
||||||
|
;; | 16 | 4 | offset_y | f32 LE |
|
||||||
|
;; | 20 | 1 | style | u8 |
|
||||||
|
;; | 21 | 1 | hidden | u8 |
|
||||||
|
;; | 22 | 2 | padding | - |
|
||||||
|
|
||||||
|
(def ^:const EFFECTS-HEADER-SIZE 12)
|
||||||
|
(def ^:const SHADOW-ENTRY-SIZE 24)
|
||||||
|
|
||||||
|
(defn set-shape-effects
|
||||||
|
"Set blur and shadows in a single WASM call.
|
||||||
|
|
||||||
|
Replaces:
|
||||||
|
- set-shape-blur / clear-shape-blur
|
||||||
|
- clear-shape-shadows + N × add-shape-shadow
|
||||||
|
|
||||||
|
Returns nil."
|
||||||
|
[blur shadows]
|
||||||
(when wasm/context-initialized?
|
(when wasm/context-initialized?
|
||||||
(let [id (dm/get-prop shape :id)
|
(let [shadow-count (count shadows)
|
||||||
parent-id (get shape :parent-id)
|
total-size (+ EFFECTS-HEADER-SIZE (* shadow-count SHADOW-ENTRY-SIZE))
|
||||||
shape-type (dm/get-prop shape :type)
|
offset (mem/alloc total-size)
|
||||||
|
heap (mem/get-heap-u8)
|
||||||
|
dview (js/DataView. (.-buffer heap))]
|
||||||
|
|
||||||
clip-content (if (= shape-type :frame)
|
;; Write blur header
|
||||||
(not (get shape :show-content))
|
(if (some? blur)
|
||||||
false)
|
(let [type (-> blur :type sr/translate-blur-type)
|
||||||
hidden (get shape :hidden false)
|
hidden (if (:hidden blur) 1 0)
|
||||||
|
value (:value blur)]
|
||||||
|
(.setUint8 dview offset 1) ;; blur_present
|
||||||
|
(.setUint8 dview (+ offset 1) type) ;; blur_type
|
||||||
|
(.setUint8 dview (+ offset 2) hidden) ;; blur_hidden
|
||||||
|
(.setFloat32 dview (+ offset 4) value true)) ;; blur_value
|
||||||
|
(do
|
||||||
|
(.setUint8 dview offset 0) ;; blur_present = 0
|
||||||
|
(.setUint8 dview (+ offset 1) 0)
|
||||||
|
(.setUint8 dview (+ offset 2) 0)
|
||||||
|
(.setFloat32 dview (+ offset 4) 0.0 true)))
|
||||||
|
|
||||||
flags (cond-> 0
|
;; Write shadow count
|
||||||
clip-content (bit-or FLAG-CLIP-CONTENT)
|
(.setUint32 dview (+ offset 8) shadow-count true)
|
||||||
hidden (bit-or FLAG-HIDDEN))
|
|
||||||
|
|
||||||
blend-mode (sr/translate-blend-mode (get shape :blend-mode))
|
;; Write shadow entries
|
||||||
constraint-h (let [c (get shape :constraints-h)]
|
(loop [i 0
|
||||||
(if (some? c)
|
shadows-seq (seq shadows)]
|
||||||
(sr/translate-constraint-h c)
|
(when shadows-seq
|
||||||
CONSTRAINT-NONE))
|
(let [shadow (first shadows-seq)
|
||||||
constraint-v (let [c (get shape :constraints-v)]
|
entry-offset (+ offset EFFECTS-HEADER-SIZE (* i SHADOW-ENTRY-SIZE))
|
||||||
(if (some? c)
|
color (get shadow :color)
|
||||||
(sr/translate-constraint-v c)
|
rgba (sr-clr/hex->u32argb (get color :color)
|
||||||
CONSTRAINT-NONE))
|
(get color :opacity))]
|
||||||
|
(.setUint32 dview entry-offset rgba true)
|
||||||
opacity (d/nilv (get shape :opacity) 1.0)
|
(.setFloat32 dview (+ entry-offset 4) (get shadow :blur) true)
|
||||||
rotation (d/nilv (get shape :rotation) 0.0)
|
(.setFloat32 dview (+ entry-offset 8) (get shadow :spread) true)
|
||||||
|
(.setFloat32 dview (+ entry-offset 12) (get shadow :offset-x) true)
|
||||||
;; Transform matrix
|
(.setFloat32 dview (+ entry-offset 16) (get shadow :offset-y) true)
|
||||||
[ta tb tc td te tf] (serialize-transform (get shape :transform))
|
(.setUint8 dview (+ entry-offset 20) (sr/translate-shadow-style (get shadow :style)))
|
||||||
|
(.setUint8 dview (+ entry-offset 21) (if (get shadow :hidden) 1 0))
|
||||||
;; Selrect
|
(recur (inc i) (next shadows-seq)))))
|
||||||
selrect (get shape :selrect)
|
|
||||||
[sx1 sy1 sx2 sy2] (serialize-selrect selrect)
|
|
||||||
|
|
||||||
;; Corners
|
|
||||||
r1 (d/nilv (get shape :r1) 0.0)
|
|
||||||
r2 (d/nilv (get shape :r2) 0.0)
|
|
||||||
r3 (d/nilv (get shape :r3) 0.0)
|
|
||||||
r4 (d/nilv (get shape :r4) 0.0)
|
|
||||||
|
|
||||||
;; Allocate buffer and get DataView
|
|
||||||
offset (mem/alloc BASE-PROPS-SIZE)
|
|
||||||
heap (mem/get-heap-u8)
|
|
||||||
dview (js/DataView. (.-buffer heap))]
|
|
||||||
|
|
||||||
;; Write id (offset 0, 16 bytes)
|
|
||||||
(write-uuid-to-heap dview offset id)
|
|
||||||
|
|
||||||
;; Write parent_id (offset 16, 16 bytes)
|
|
||||||
(write-uuid-to-heap dview (+ offset 16) (d/nilv parent-id uuid/zero))
|
|
||||||
|
|
||||||
;; Write shape_type (offset 32, 1 byte)
|
|
||||||
(.setUint8 dview (+ offset 32) (sr/translate-shape-type shape-type))
|
|
||||||
|
|
||||||
;; Write flags (offset 33, 1 byte)
|
|
||||||
(.setUint8 dview (+ offset 33) flags)
|
|
||||||
|
|
||||||
;; Write blend_mode (offset 34, 1 byte)
|
|
||||||
(.setUint8 dview (+ offset 34) blend-mode)
|
|
||||||
|
|
||||||
;; Write constraint_h (offset 35, 1 byte)
|
|
||||||
(.setUint8 dview (+ offset 35) constraint-h)
|
|
||||||
|
|
||||||
;; Write constraint_v (offset 36, 1 byte)
|
|
||||||
(.setUint8 dview (+ offset 36) constraint-v)
|
|
||||||
|
|
||||||
;; Padding at offset 37-39 (already zero from alloc)
|
|
||||||
|
|
||||||
;; Write opacity (offset 40, f32)
|
|
||||||
(.setFloat32 dview (+ offset 40) opacity true)
|
|
||||||
|
|
||||||
;; Write rotation (offset 44, f32)
|
|
||||||
(.setFloat32 dview (+ offset 44) rotation true)
|
|
||||||
|
|
||||||
;; Write transform matrix (offset 48, 6 × f32)
|
|
||||||
(.setFloat32 dview (+ offset 48) ta true)
|
|
||||||
(.setFloat32 dview (+ offset 52) tb true)
|
|
||||||
(.setFloat32 dview (+ offset 56) tc true)
|
|
||||||
(.setFloat32 dview (+ offset 60) td true)
|
|
||||||
(.setFloat32 dview (+ offset 64) te true)
|
|
||||||
(.setFloat32 dview (+ offset 68) tf true)
|
|
||||||
|
|
||||||
;; Write selrect (offset 72, 4 × f32)
|
|
||||||
(.setFloat32 dview (+ offset 72) sx1 true)
|
|
||||||
(.setFloat32 dview (+ offset 76) sy1 true)
|
|
||||||
(.setFloat32 dview (+ offset 80) sx2 true)
|
|
||||||
(.setFloat32 dview (+ offset 84) sy2 true)
|
|
||||||
|
|
||||||
;; Write corners (offset 88, 4 × f32)
|
|
||||||
(.setFloat32 dview (+ offset 88) r1 true)
|
|
||||||
(.setFloat32 dview (+ offset 92) r2 true)
|
|
||||||
(.setFloat32 dview (+ offset 96) r3 true)
|
|
||||||
(.setFloat32 dview (+ offset 100) r4 true)
|
|
||||||
|
|
||||||
(h/call wasm/internal-module "_set_shape_base_props")
|
|
||||||
|
|
||||||
|
(h/call wasm/internal-module "_set_shape_effects")
|
||||||
nil)))
|
nil)))
|
||||||
|
|||||||
198
render-wasm/src/wasm/shapes/effect_props.rs
Normal file
198
render-wasm/src/wasm/shapes/effect_props.rs
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
use skia_safe as skia;
|
||||||
|
|
||||||
|
use crate::mem;
|
||||||
|
use crate::shapes::{Blur, Shadow};
|
||||||
|
use crate::wasm::blurs::RawBlurType;
|
||||||
|
use crate::wasm::shadows::RawShadowStyle;
|
||||||
|
use crate::{with_current_shape_mut, STATE};
|
||||||
|
|
||||||
|
const RAW_EFFECT_HEADER_SIZE: usize = std::mem::size_of::<RawEffectHeader>();
|
||||||
|
const RAW_SHADOW_ENTRY_SIZE: usize = std::mem::size_of::<RawShadowEntry>();
|
||||||
|
|
||||||
|
/// Binary layout for the effect header (blur + shadow count).
|
||||||
|
///
|
||||||
|
/// The struct fields directly mirror the binary protocol — the layout
|
||||||
|
/// documentation lives in the struct definition itself via `#[repr(C)]`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(align(4))]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct RawEffectHeader {
|
||||||
|
blur_present: u8,
|
||||||
|
blur_type: u8,
|
||||||
|
blur_hidden: u8,
|
||||||
|
padding: u8,
|
||||||
|
blur_value: f32,
|
||||||
|
shadow_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawEffectHeader {
|
||||||
|
fn has_blur(&self) -> bool {
|
||||||
|
self.blur_present != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blur_hidden(&self) -> bool {
|
||||||
|
self.blur_hidden != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; RAW_EFFECT_HEADER_SIZE]> for RawEffectHeader {
|
||||||
|
fn from(bytes: [u8; RAW_EFFECT_HEADER_SIZE]) -> Self {
|
||||||
|
unsafe { std::mem::transmute(bytes) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary layout for a single shadow entry.
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(align(4))]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct RawShadowEntry {
|
||||||
|
color: u32,
|
||||||
|
blur: f32,
|
||||||
|
spread: f32,
|
||||||
|
offset_x: f32,
|
||||||
|
offset_y: f32,
|
||||||
|
style: u8,
|
||||||
|
hidden: u8,
|
||||||
|
padding: [u8; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawShadowEntry {
|
||||||
|
fn is_hidden(&self) -> bool {
|
||||||
|
self.hidden != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; RAW_SHADOW_ENTRY_SIZE]> for RawShadowEntry {
|
||||||
|
fn from(bytes: [u8; RAW_SHADOW_ENTRY_SIZE]) -> Self {
|
||||||
|
unsafe { std::mem::transmute(bytes) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn set_shape_effects() {
|
||||||
|
let bytes = mem::bytes();
|
||||||
|
|
||||||
|
if bytes.len() < RAW_EFFECT_HEADER_SIZE {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let header_bytes: [u8; RAW_EFFECT_HEADER_SIZE] =
|
||||||
|
bytes[..RAW_EFFECT_HEADER_SIZE].try_into().unwrap();
|
||||||
|
let header = RawEffectHeader::from(header_bytes);
|
||||||
|
|
||||||
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||||
|
// Parse blur
|
||||||
|
if header.has_blur() {
|
||||||
|
let blur_type = RawBlurType::from(header.blur_type);
|
||||||
|
shape.set_blur(Some(Blur::new(
|
||||||
|
blur_type.into(),
|
||||||
|
header.is_blur_hidden(),
|
||||||
|
header.blur_value,
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
shape.set_blur(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse shadows
|
||||||
|
let shadow_count = header.shadow_count as usize;
|
||||||
|
shape.clear_shadows();
|
||||||
|
let shadows_data = &bytes[RAW_EFFECT_HEADER_SIZE..];
|
||||||
|
for i in 0..shadow_count {
|
||||||
|
let offset = i * RAW_SHADOW_ENTRY_SIZE;
|
||||||
|
if offset + RAW_SHADOW_ENTRY_SIZE > shadows_data.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry_bytes: [u8; RAW_SHADOW_ENTRY_SIZE] = shadows_data
|
||||||
|
[offset..offset + RAW_SHADOW_ENTRY_SIZE]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let entry = RawShadowEntry::from(entry_bytes);
|
||||||
|
|
||||||
|
let shadow = Shadow::new(
|
||||||
|
skia::Color::new(entry.color),
|
||||||
|
entry.blur,
|
||||||
|
entry.spread,
|
||||||
|
(entry.offset_x, entry.offset_y),
|
||||||
|
RawShadowStyle::from(entry.style).into(),
|
||||||
|
entry.is_hidden(),
|
||||||
|
);
|
||||||
|
shape.add_shadow(shadow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_raw_effect_header_layout() {
|
||||||
|
assert_eq!(RAW_EFFECT_HEADER_SIZE, 12);
|
||||||
|
assert_eq!(std::mem::align_of::<RawEffectHeader>(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_raw_shadow_entry_layout() {
|
||||||
|
assert_eq!(RAW_SHADOW_ENTRY_SIZE, 24);
|
||||||
|
assert_eq!(std::mem::align_of::<RawShadowEntry>(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_field_offsets() {
|
||||||
|
assert_eq!(std::mem::offset_of!(RawEffectHeader, blur_present), 0);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawEffectHeader, blur_type), 1);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawEffectHeader, blur_hidden), 2);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawEffectHeader, blur_value), 4);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawEffectHeader, shadow_count), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shadow_entry_field_offsets() {
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, color), 0);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, blur), 4);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, spread), 8);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, offset_x), 12);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, offset_y), 16);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, style), 20);
|
||||||
|
assert_eq!(std::mem::offset_of!(RawShadowEntry, hidden), 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_deserialization() {
|
||||||
|
let mut bytes = [0u8; RAW_EFFECT_HEADER_SIZE];
|
||||||
|
bytes[0] = 1; // blur_present
|
||||||
|
bytes[1] = 1; // blur_type = LayerBlur
|
||||||
|
bytes[2] = 0; // blur_hidden = false
|
||||||
|
bytes[4..8].copy_from_slice(&5.0_f32.to_le_bytes()); // blur_value
|
||||||
|
bytes[8..12].copy_from_slice(&3_u32.to_le_bytes()); // shadow_count
|
||||||
|
|
||||||
|
let header = RawEffectHeader::from(bytes);
|
||||||
|
assert!(header.has_blur());
|
||||||
|
assert!(!header.is_blur_hidden());
|
||||||
|
assert_eq!(header.blur_type, 1);
|
||||||
|
assert_eq!(header.blur_value, 5.0);
|
||||||
|
assert_eq!(header.shadow_count, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shadow_entry_deserialization() {
|
||||||
|
let mut bytes = [0u8; RAW_SHADOW_ENTRY_SIZE];
|
||||||
|
bytes[0..4].copy_from_slice(&0xFF0000FF_u32.to_le_bytes()); // color
|
||||||
|
bytes[4..8].copy_from_slice(&4.0_f32.to_le_bytes()); // blur
|
||||||
|
bytes[8..12].copy_from_slice(&2.0_f32.to_le_bytes()); // spread
|
||||||
|
bytes[12..16].copy_from_slice(&10.0_f32.to_le_bytes()); // offset_x
|
||||||
|
bytes[16..20].copy_from_slice(&20.0_f32.to_le_bytes()); // offset_y
|
||||||
|
bytes[20] = 0; // style = DropShadow
|
||||||
|
bytes[21] = 1; // hidden = true
|
||||||
|
|
||||||
|
let entry = RawShadowEntry::from(bytes);
|
||||||
|
assert_eq!(entry.color, 0xFF0000FF);
|
||||||
|
assert_eq!(entry.blur, 4.0);
|
||||||
|
assert_eq!(entry.spread, 2.0);
|
||||||
|
assert_eq!(entry.offset_x, 10.0);
|
||||||
|
assert_eq!(entry.offset_y, 20.0);
|
||||||
|
assert_eq!(entry.style, 0);
|
||||||
|
assert!(entry.is_hidden());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
mod base_props;
|
mod base_props;
|
||||||
|
mod effect_props;
|
||||||
|
|
||||||
use macros::ToJs;
|
use macros::ToJs;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user