@@ -16,13 +16,27 @@ use editor::messages::portfolio::utility_types::Platform;
1616use editor:: messages:: prelude:: * ;
1717use editor:: messages:: tool:: tool_messages:: tool_prelude:: WidgetId ;
1818use graph_craft:: document:: NodeId ;
19+ use graphene_std:: raster:: Image ;
1920use graphene_std:: raster:: color:: Color ;
21+ use js_sys:: { Object , Reflect } ;
2022use serde:: Serialize ;
2123use serde_wasm_bindgen:: { self , from_value} ;
2224use std:: cell:: RefCell ;
23- use std:: sync:: atomic:: Ordering ;
25+ use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
2426use std:: time:: Duration ;
27+ use wasm_bindgen:: JsCast ;
2528use wasm_bindgen:: prelude:: * ;
29+ use web_sys:: { CanvasRenderingContext2d , HtmlCanvasElement , ImageData , window} ;
30+
31+ static IMAGE_DATA_HASH : AtomicU64 = AtomicU64 :: new ( 0 ) ;
32+
33+ fn calculate_hash < T : std:: hash:: Hash > ( t : & T ) -> u64 {
34+ use std:: collections:: hash_map:: DefaultHasher ;
35+ use std:: hash:: Hasher ;
36+ let mut hasher = DefaultHasher :: new ( ) ;
37+ t. hash ( & mut hasher) ;
38+ hasher. finish ( )
39+ }
2640
2741/// Set the random seed used by the editor by calling this from JS upon initialization.
2842/// This is necessary because WASM doesn't have a random number generator.
@@ -37,6 +51,75 @@ pub fn wasm_memory() -> JsValue {
3751 wasm_bindgen:: memory ( )
3852}
3953
54+ fn render_image_data_to_canvases ( image_data : & [ ( u64 , Image < Color > ) ] ) {
55+ let window = match window ( ) {
56+ Some ( window) => window,
57+ None => {
58+ error ! ( "Cannot render canvas: window object not found" ) ;
59+ return ;
60+ }
61+ } ;
62+ let document = window. document ( ) . expect ( "window should have a document" ) ;
63+ let window_obj = Object :: from ( window) ;
64+ let image_canvases_key = JsValue :: from_str ( "imageCanvases" ) ;
65+
66+ let canvases_obj = match Reflect :: get ( & window_obj, & image_canvases_key) {
67+ Ok ( obj) if !obj. is_undefined ( ) && !obj. is_null ( ) => obj,
68+ _ => {
69+ let new_obj = Object :: new ( ) ;
70+ if Reflect :: set ( & window_obj, & image_canvases_key, & new_obj) . is_err ( ) {
71+ error ! ( "Failed to create and set imageCanvases object on window" ) ;
72+ return ;
73+ }
74+ new_obj. into ( )
75+ }
76+ } ;
77+ let canvases_obj = Object :: from ( canvases_obj) ;
78+
79+ for ( placeholder_id, image) in image_data. iter ( ) {
80+ let canvas_name = placeholder_id. to_string ( ) ;
81+ let js_key = JsValue :: from_str ( & canvas_name) ;
82+
83+ if Reflect :: has ( & canvases_obj, & js_key) . unwrap_or ( false ) || image. width == 0 || image. height == 0 {
84+ continue ;
85+ }
86+
87+ let canvas: HtmlCanvasElement = document
88+ . create_element ( "canvas" )
89+ . expect ( "Failed to create canvas element" )
90+ . dyn_into :: < HtmlCanvasElement > ( )
91+ . expect ( "Failed to cast element to HtmlCanvasElement" ) ;
92+
93+ canvas. set_width ( image. width ) ;
94+ canvas. set_height ( image. height ) ;
95+
96+ let context: CanvasRenderingContext2d = canvas
97+ . get_context ( "2d" )
98+ . expect ( "Failed to get 2d context" )
99+ . expect ( "2d context was not found" )
100+ . dyn_into :: < CanvasRenderingContext2d > ( )
101+ . expect ( "Failed to cast context to CanvasRenderingContext2d" ) ;
102+ let u8_data: Vec < u8 > = image. data . iter ( ) . flat_map ( |color| color. to_rgba8_srgb ( ) ) . collect ( ) ;
103+ let clamped_u8_data = wasm_bindgen:: Clamped ( & u8_data[ ..] ) ;
104+ match ImageData :: new_with_u8_clamped_array_and_sh ( clamped_u8_data, image. width , image. height ) {
105+ Ok ( image_data_obj) => {
106+ if context. put_image_data ( & image_data_obj, 0. , 0. ) . is_err ( ) {
107+ error ! ( "Failed to put image data on canvas for id: {placeholder_id}" ) ;
108+ }
109+ }
110+ Err ( e) => {
111+ error ! ( "Failed to create ImageData for id: {placeholder_id}: {e:?}" ) ;
112+ }
113+ }
114+
115+ let js_value = JsValue :: from ( canvas) ;
116+
117+ if Reflect :: set ( & canvases_obj, & js_key, & js_value) . is_err ( ) {
118+ error ! ( "Failed to set canvas '{canvas_name}' on imageCanvases object" ) ;
119+ }
120+ }
121+ }
122+
40123// ============================================================================
41124
42125/// This struct is, via wasm-bindgen, used by JS to interact with the editor backend. It does this by calling functions, which are `impl`ed
@@ -88,6 +171,17 @@ impl EditorHandle {
88171
89172 // Sends a FrontendMessage to JavaScript
90173 fn send_frontend_message_to_js ( & self , mut message : FrontendMessage ) {
174+ if let FrontendMessage :: UpdateImageData { ref image_data } = message {
175+ let new_hash = calculate_hash ( image_data) ;
176+ let prev_hash = IMAGE_DATA_HASH . load ( Ordering :: Relaxed ) ;
177+
178+ if new_hash != prev_hash {
179+ render_image_data_to_canvases ( image_data. as_slice ( ) ) ;
180+ IMAGE_DATA_HASH . store ( new_hash, Ordering :: Relaxed ) ;
181+ }
182+ return ;
183+ }
184+
91185 if let FrontendMessage :: UpdateDocumentLayerStructure { data_buffer } = message {
92186 message = FrontendMessage :: UpdateDocumentLayerStructureJs { data_buffer : data_buffer. into ( ) } ;
93187 }
0 commit comments