@@ -51,7 +51,7 @@ pub enum NodeGraphUpdate {
51
51
pub struct NodeGraphExecutor {
52
52
runtime_io : NodeRuntimeIO ,
53
53
current_execution_id : u64 ,
54
- futures : HashMap < u64 , ExecutionContext > ,
54
+ futures : VecDeque < ( u64 , ExecutionContext ) > ,
55
55
node_graph_hash : u64 ,
56
56
previous_node_to_inspect : Option < NodeId > ,
57
57
}
@@ -157,7 +157,7 @@ impl NodeGraphExecutor {
157
157
// Execute the node graph
158
158
let execution_id = self . queue_execution ( render_config) ;
159
159
160
- self . futures . insert ( execution_id, ExecutionContext { export_config : None , document_id } ) ;
160
+ self . futures . push_back ( ( execution_id, ExecutionContext { export_config : None , document_id } ) ) ;
161
161
162
162
Ok ( DeferMessage :: SetGraphSubmissionIndex { execution_id } . into ( ) )
163
163
}
@@ -190,14 +190,20 @@ impl NodeGraphExecutor {
190
190
let size = bounds[ 1 ] - bounds[ 0 ] ;
191
191
let transform = DAffine2 :: from_translation ( bounds[ 0 ] ) . inverse ( ) ;
192
192
193
+ let export_format = if export_config. file_type == FileType :: Svg || cfg ! ( not( feature = "gpu" ) ) {
194
+ graphene_std:: application_io:: ExportFormat :: Svg
195
+ } else {
196
+ graphene_std:: application_io:: ExportFormat :: Texture
197
+ } ;
198
+
193
199
let render_config = RenderConfig {
194
200
viewport : Footprint {
195
201
transform : DAffine2 :: from_scale ( DVec2 :: splat ( export_config. scale_factor ) ) * transform,
196
202
resolution : ( size * export_config. scale_factor ) . as_uvec2 ( ) ,
197
203
..Default :: default ( )
198
204
} ,
199
205
time : Default :: default ( ) ,
200
- export_format : graphene_std :: application_io :: ExportFormat :: Svg ,
206
+ export_format,
201
207
render_mode : document. render_mode ,
202
208
hide_artboards : export_config. transparent_background ,
203
209
for_export : true ,
@@ -213,34 +219,87 @@ impl NodeGraphExecutor {
213
219
export_config : Some ( export_config) ,
214
220
document_id,
215
221
} ;
216
- self . futures . insert ( execution_id, execution_context) ;
222
+ self . futures . push_back ( ( execution_id, execution_context) ) ;
217
223
218
224
Ok ( ( ) )
219
225
}
220
226
221
227
fn export ( & self , node_graph_output : TaggedValue , export_config : ExportConfig , responses : & mut VecDeque < Message > ) -> Result < ( ) , String > {
222
- let TaggedValue :: RenderOutput ( RenderOutput {
223
- data : RenderOutputType :: Svg { svg, .. } ,
224
- ..
225
- } ) = node_graph_output
226
- else {
227
- return Err ( "Incorrect render type for exporting (expected RenderOutput::Svg)" . to_string ( ) ) ;
228
- } ;
229
-
230
228
let ExportConfig {
231
- file_type, name, size, scale_factor, ..
229
+ file_type,
230
+ name,
231
+ size,
232
+ scale_factor,
233
+ #[ cfg( feature = "gpu" ) ]
234
+ transparent_background,
235
+ ..
232
236
} = export_config;
233
237
234
- let file_suffix = & format ! ( ".{file_type:?}" ) . to_lowercase ( ) ;
235
- let name = name + file_suffix;
238
+ let file_extension = match file_type {
239
+ FileType :: Svg => "svg" ,
240
+ FileType :: Png => "png" ,
241
+ FileType :: Jpg => "jpg" ,
242
+ } ;
243
+ let name = format ! ( "{name}.{file_extension}" ) ;
244
+
245
+ match node_graph_output {
246
+ TaggedValue :: RenderOutput ( RenderOutput {
247
+ data : RenderOutputType :: Svg { svg, .. } ,
248
+ ..
249
+ } ) => {
250
+ if file_type == FileType :: Svg {
251
+ responses. add ( FrontendMessage :: TriggerSaveFile { name, content : svg. into_bytes ( ) } ) ;
252
+ } else {
253
+ let mime = file_type. to_mime ( ) . to_string ( ) ;
254
+ let size = ( size * scale_factor) . into ( ) ;
255
+ responses. add ( FrontendMessage :: TriggerExportImage { svg, name, mime, size } ) ;
256
+ }
257
+ }
258
+ #[ cfg( feature = "gpu" ) ]
259
+ TaggedValue :: RenderOutput ( RenderOutput {
260
+ data : RenderOutputType :: Buffer { data, width, height } ,
261
+ ..
262
+ } ) if file_type != FileType :: Svg => {
263
+ use image:: buffer:: ConvertBuffer ;
264
+ use image:: { ImageFormat , RgbImage , RgbaImage } ;
265
+
266
+ let Some ( image) = RgbaImage :: from_raw ( width, height, data) else {
267
+ return Err ( format ! ( "Failed to create image buffer for export" ) ) ;
268
+ } ;
236
269
237
- if file_type == FileType :: Svg {
238
- responses. add ( FrontendMessage :: TriggerSaveFile { name, content : svg. into_bytes ( ) } ) ;
239
- } else {
240
- let mime = file_type. to_mime ( ) . to_string ( ) ;
241
- let size = ( size * scale_factor) . into ( ) ;
242
- responses. add ( FrontendMessage :: TriggerExportImage { svg, name, mime, size } ) ;
243
- }
270
+ let mut encoded = Vec :: new ( ) ;
271
+ let mut cursor = std:: io:: Cursor :: new ( & mut encoded) ;
272
+
273
+ match file_type {
274
+ FileType :: Png => {
275
+ let result = if transparent_background {
276
+ image. write_to ( & mut cursor, ImageFormat :: Png )
277
+ } else {
278
+ let image: RgbImage = image. convert ( ) ;
279
+ image. write_to ( & mut cursor, ImageFormat :: Png )
280
+ } ;
281
+ if let Err ( err) = result {
282
+ return Err ( format ! ( "Failed to encode PNG: {err}" ) ) ;
283
+ }
284
+ }
285
+ FileType :: Jpg => {
286
+ let image: RgbImage = image. convert ( ) ;
287
+ let result = image. write_to ( & mut cursor, ImageFormat :: Jpeg ) ;
288
+ if let Err ( err) = result {
289
+ return Err ( format ! ( "Failed to encode JPG: {err}" ) ) ;
290
+ }
291
+ }
292
+ FileType :: Svg => {
293
+ return Err ( format ! ( "SVG cannot be exported from an image buffer" ) ) ;
294
+ }
295
+ }
296
+
297
+ responses. add ( FrontendMessage :: TriggerSaveFile { name, content : encoded } ) ;
298
+ }
299
+ _ => {
300
+ return Err ( format ! ( "Incorrect render type for exporting to an SVG ({file_type:?}, {node_graph_output})" ) ) ;
301
+ }
302
+ } ;
244
303
245
304
Ok ( ( ) )
246
305
}
@@ -273,7 +332,19 @@ impl NodeGraphExecutor {
273
332
responses. extend ( existing_responses. into_iter ( ) . map ( Into :: into) ) ;
274
333
document. network_interface . update_vector_modify ( vector_modify) ;
275
334
276
- let execution_context = self . futures . remove ( & execution_id) . ok_or_else ( || "Invalid generation ID" . to_string ( ) ) ?;
335
+ while let Some ( & ( fid, _) ) = self . futures . front ( ) {
336
+ if fid < execution_id {
337
+ self . futures . pop_front ( ) ;
338
+ } else {
339
+ break ;
340
+ }
341
+ }
342
+
343
+ let Some ( ( fid, execution_context) ) = self . futures . pop_front ( ) else {
344
+ panic ! ( "InvalidGenerationId" )
345
+ } ;
346
+ assert_eq ! ( fid, execution_id, "Missmatch in execution id" ) ;
347
+
277
348
if let Some ( export_config) = execution_context. export_config {
278
349
// Special handling for exporting the artwork
279
350
self . export ( node_graph_output, export_config, responses) ?;
0 commit comments