|
1 | | -#[cfg(target_family = "wasm")] |
2 | | -use base64::Engine; |
3 | | -#[cfg(target_family = "wasm")] |
4 | | -use canvas_utils::{Canvas, CanvasHandle}; |
| 1 | +use ::wgpu_executor::VelloCanvasHandle; |
5 | 2 | use core_types::color::SRGBA8; |
6 | 3 | use core_types::list::Item; |
7 | | -#[cfg(target_family = "wasm")] |
8 | 4 | use core_types::list::List; |
9 | | -#[cfg(target_family = "wasm")] |
10 | 5 | use core_types::math::bbox::Bbox; |
11 | 6 | use core_types::ops::{Convert, ConvertAsync}; |
12 | 7 | use core_types::transform::Footprint; |
13 | | -#[cfg(target_family = "wasm")] |
14 | | -use core_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM}; |
15 | | -use core_types::{Ctx, Interrupt, NodeError}; |
| 8 | +use core_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, Ctx, Interrupt, NodeError}; |
| 9 | +use glam::DAffine2; |
16 | 10 | pub use graph_craft::application_io::resource::{Resource, ResourceHash}; |
17 | 11 | pub use graph_craft::application_io::*; |
18 | 12 | pub use graph_craft::document::value::RenderOutputType; |
19 | 13 | #[cfg(target_family = "wasm")] |
20 | 14 | pub use graphene_canvas_utils as canvas_utils; |
21 | | -#[cfg(target_family = "wasm")] |
22 | 15 | use graphic_types::Graphic; |
23 | 16 | use graphic_types::raster_types::Image; |
24 | 17 | use graphic_types::raster_types::{CPU, GPU, Raster, Storage}; |
25 | 18 | use raster_nodes::raster_kernel::raster_kernel_pipeline; |
26 | | -#[cfg(target_family = "wasm")] |
27 | | -use rendering::{Render, RenderParams, RenderSvgSegmentList, SvgRender}; |
| 19 | +use rendering::{Render, RenderParams}; |
28 | 20 | use std::sync::Arc; |
29 | 21 |
|
30 | 22 | fn parse_headers(headers: &str) -> reqwest::header::HeaderMap { |
@@ -182,85 +174,50 @@ fn bytes_to_image(_: impl Ctx, data: Item<Resource>, #[scope(raster_kernel_pipel |
182 | 174 | Ok(Item::new_from_element(Raster::new_cpu(image).convert(Footprint::DEFAULT, executor))) |
183 | 175 | } |
184 | 176 |
|
185 | | -#[cfg(target_family = "wasm")] |
186 | 177 | #[node_macro::node(category(""))] |
187 | | -fn create_canvas(_: impl Ctx) -> Item<CanvasHandle> { |
188 | | - Item::new_from_element(CanvasHandle::new()) |
| 178 | +fn create_canvas(_: impl Ctx) -> Item<VelloCanvasHandle> { |
| 179 | + Item::new_from_element(VelloCanvasHandle::default()) |
189 | 180 | } |
190 | 181 |
|
191 | 182 | /// Renders a view of the input graphic within an area defined by the *Footprint*. |
192 | | -#[cfg(target_family = "wasm")] |
193 | | -#[node_macro::node(category(""), placeholder = pending_raster)] |
194 | | -async fn rasterize( |
| 183 | +#[node_macro::node(category(""))] |
| 184 | +fn rasterize( |
195 | 185 | _: impl Ctx, |
196 | 186 | data: List<Graphic>, |
197 | 187 | footprint: Item<Footprint>, |
198 | | - canvas: Item<CanvasHandle>, |
| 188 | + canvas: Item<VelloCanvasHandle>, |
199 | 189 | #[scope(raster_kernel_pipeline::IDENTIFIER)] pipeline: Item<::wgpu_executor::WgpuPipelineCache>, |
200 | | -) -> Result<List<Raster<GPU>>, NodeError> { |
201 | | - let mut data = data; |
202 | | - let mut canvas = canvas.into_element(); |
203 | | - use glam::{DAffine2, DVec2}; |
204 | | - |
| 190 | +) -> Result<List<Raster<GPU>>, Interrupt> { |
205 | 191 | let footprint = footprint.into_element(); |
| 192 | + let resolution = footprint.resolution; |
206 | 193 |
|
207 | | - if footprint.transform.matrix2.determinant() == 0. { |
| 194 | + if footprint.transform.matrix2.determinant() == 0. || resolution.x == 0 || resolution.y == 0 { |
208 | 195 | log::trace!("Invalid footprint received for rasterization"); |
209 | 196 | return Ok(List::new()); |
210 | 197 | } |
211 | 198 |
|
212 | | - // Snapshot the input as a List<Graphic> so the renderer can recurse into the original child layers |
213 | | - // when collecting metadata, exposing their click targets to editor tools (same mechanism as Boolean Operation). |
214 | | - let upstream_graphic_list = data.clone(); |
215 | | - |
216 | | - let mut render = SvgRender::new(); |
217 | 199 | let aabb = Bbox::from_transform(footprint.transform).to_axis_aligned_bbox(); |
218 | | - let size = aabb.size(); |
219 | | - let resolution = footprint.resolution; |
| 200 | + let view = DAffine2::from_scale(resolution.as_dvec2() / aabb.size()) * DAffine2::from_translation(-aabb.start); |
220 | 201 | let render_params = RenderParams { |
221 | | - footprint, |
| 202 | + footprint: Footprint { transform: view, ..footprint }, |
| 203 | + render_output_type: rendering::RenderOutputType::Vello, |
222 | 204 | for_export: true, |
223 | 205 | ..Default::default() |
224 | 206 | }; |
225 | 207 |
|
226 | | - for transform in data.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) { |
227 | | - *transform = DAffine2::from_translation(-aabb.start) * *transform; |
228 | | - } |
229 | | - data.render_svg(&mut render, &render_params); |
230 | | - render.format_svg(DVec2::ZERO, size); |
231 | | - let svg_string = render.svg.to_svg_string(); |
232 | | - |
233 | | - canvas.set_resolution(resolution); |
234 | | - let context = canvas.context(); |
235 | | - |
236 | | - let preamble = "data:image/svg+xml;base64,"; |
237 | | - let mut base64_string = String::with_capacity(preamble.len() + svg_string.len() * 4); |
238 | | - base64_string.push_str(preamble); |
239 | | - base64::engine::general_purpose::STANDARD.encode_string(svg_string, &mut base64_string); |
| 208 | + let executor = pipeline.element().executor().ok_or_else(|| NodeError::new("A GPU is required to rasterize"))?; |
| 209 | + let mut canvas = canvas.element().lock(); |
| 210 | + let mut scene = executor.vello_scene_on_canvas(&mut canvas, resolution, view).map_err(NodeError::new)?; |
| 211 | + data.render_to_vello(&mut scene, DAffine2::IDENTITY, &render_params); |
| 212 | + let texture = executor.render_vello_raster_on_canvas(&mut canvas, scene).map_err(NodeError::new)?; |
240 | 213 |
|
241 | | - let image_data = web_sys::HtmlImageElement::new().unwrap(); |
242 | | - image_data.set_src(base64_string.as_str()); |
243 | | - wasm_bindgen_futures::JsFuture::from(image_data.decode()).await.unwrap(); |
244 | | - context |
245 | | - .draw_image_with_html_image_element_and_dw_and_dh(&image_data, 0., 0., resolution.x as f64, resolution.y as f64) |
246 | | - .unwrap(); |
247 | | - |
248 | | - let rasterized = context.get_image_data(0, 0, resolution.x as i32, resolution.y as i32).unwrap(); |
249 | | - |
250 | | - let image = Image::from_image_data(&rasterized.data().0, resolution.x as u32, resolution.y as u32); |
251 | | - let executor = pipeline.element().executor().cloned().ok_or_else(|| NodeError::new("A GPU is required to rasterize"))?; |
252 | 214 | Ok(List::new_from_item( |
253 | | - Item::new_from_element(Raster::new_cpu(image).convert(Footprint::DEFAULT, executor)) |
| 215 | + Item::new_from_element(Raster::new_gpu(texture)) |
254 | 216 | .with_attribute(ATTR_TRANSFORM, footprint.transform) |
255 | | - .with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_list), |
| 217 | + .with_attribute(ATTR_EDITOR_MERGED_LAYERS, data), |
256 | 218 | )) |
257 | 219 | } |
258 | 220 |
|
259 | | -#[cfg(target_family = "wasm")] |
260 | | -fn pending_raster(_data: &List<Graphic>, _footprint: &Item<Footprint>, _canvas: &Item<CanvasHandle>, _pipeline: &Item<::wgpu_executor::WgpuPipelineCache>) -> List<Raster<GPU>> { |
261 | | - List::new() |
262 | | -} |
263 | | - |
264 | 221 | #[node_macro::node(category(""), inject_scope)] |
265 | 222 | fn editor_api(_: impl Ctx, #[scope("editor-api")] editor_api: Item<Arc<PlatformEditorApi>>) -> Item<Arc<PlatformEditorApi>> { |
266 | 223 | editor_api |
|
0 commit comments