diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 51a73c860..58f132982 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -19,7 +19,7 @@ use crate::shapes::{Corners, Shape, Type}; use cache::CachedSurfaceImage; use gpu_state::GpuState; use options::RenderOptions; -use surfaces::Surfaces; +use surfaces::{SurfaceId, Surfaces}; pub use blend::BlendMode; pub use images::*; @@ -56,7 +56,6 @@ pub(crate) struct RenderState { gpu_state: GpuState, pub options: RenderOptions, pub surfaces: Surfaces, - pub sampling_options: skia::SamplingOptions, pub font_provider: skia::textlayout::TypefaceFontProvider, pub cached_surface_image: Option, pub viewbox: Viewbox, @@ -75,7 +74,10 @@ impl RenderState { pub fn new(width: i32, height: i32) -> RenderState { // This needs to be done once per WebGL context. let mut gpu_state = GpuState::new(); - let surfaces = Surfaces::new(&mut gpu_state, (width, height)); + + let sampling_options = + skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest); + let surfaces = Surfaces::new(&mut gpu_state, (width, height), sampling_options); let mut font_provider = skia::textlayout::TypefaceFontProvider::new(); let default_font = skia::FontMgr::default() .new_from_data(DEFAULT_FONT_BYTES, None) @@ -84,15 +86,12 @@ impl RenderState { // This is used multiple times everywhere so instead of creating new instances every // time we reuse this one. - let sampling_options = - skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest); RenderState { gpu_state, surfaces, cached_surface_image: None, font_provider, - sampling_options, options: RenderOptions::default(), viewbox: Viewbox::new(width as f32, height as f32), images: ImageStore::new(), @@ -150,88 +149,64 @@ impl RenderState { } pub fn flush(&mut self) { - self.gpu_state - .context - .flush_and_submit_surface(&mut self.surfaces.target, None); + self.surfaces + .flush_and_submit(&mut self.gpu_state, SurfaceId::Target); } pub fn reset_canvas(&mut self) { - self.surfaces.shape.canvas().restore_to_count(1); - self.surfaces.current.canvas().restore_to_count(1); + self.surfaces.canvas(SurfaceId::Shape).restore_to_count(1); + self.surfaces.canvas(SurfaceId::Current).restore_to_count(1); self.surfaces - .shape - .canvas() + .canvas(SurfaceId::Shape) .clear(self.background_color) .reset_matrix(); self.surfaces - .current - .canvas() + .canvas(SurfaceId::Current) .clear(self.background_color) .reset_matrix(); self.surfaces - .shadow - .canvas() + .canvas(SurfaceId::Shadow) .clear(self.background_color) .reset_matrix(); self.surfaces - .overlay - .canvas() + .canvas(SurfaceId::Overlay) .clear(self.background_color) .reset_matrix(); self.surfaces - .debug - .canvas() + .canvas(SurfaceId::Debug) .clear(skia::Color::TRANSPARENT) .reset_matrix(); } pub fn apply_render_to_final_canvas(&mut self) { - self.surfaces.current.draw( - &mut self.surfaces.target.canvas(), - (0.0, 0.0), - self.sampling_options, - Some(&skia::Paint::default()), - ); + self.surfaces + .draw_into(SurfaceId::Current, SurfaceId::Target, None); } pub fn apply_drawing_to_render_canvas(&mut self) { - self.gpu_state - .context - .flush_and_submit_surface(&mut self.surfaces.shape, None); - - self.surfaces.shape.draw( - &mut self.surfaces.current.canvas(), - (0.0, 0.0), - self.sampling_options, - Some(&skia::Paint::default()), - ); - - self.gpu_state - .context - .flush_and_submit_surface(&mut self.surfaces.current, None); - - self.gpu_state - .context - .flush_and_submit_surface(&mut self.surfaces.overlay, None); - - self.surfaces.overlay.draw( - &mut self.surfaces.current.canvas(), - (0.0, 0.0), - self.sampling_options, - None, - ); + self.surfaces + .flush_and_submit(&mut self.gpu_state, SurfaceId::Shape); + self.surfaces + .draw_into(SurfaceId::Shape, SurfaceId::Current, None); self.surfaces - .shadow - .canvas() + .flush_and_submit(&mut self.gpu_state, SurfaceId::Current); + self.surfaces + .flush_and_submit(&mut self.gpu_state, SurfaceId::Overlay); + self.surfaces + .draw_into(SurfaceId::Overlay, SurfaceId::Current, None); + + self.surfaces + .canvas(SurfaceId::Shadow) .clear(skia::Color::TRANSPARENT); self.surfaces - .overlay - .canvas() + .canvas(SurfaceId::Overlay) .clear(skia::Color::TRANSPARENT); - self.surfaces.shape.canvas().clear(skia::Color::TRANSPARENT); + self.surfaces + .canvas(SurfaceId::Shape) + .clear(skia::Color::TRANSPARENT); } pub fn invalidate_cache_if_needed(&mut self) { @@ -246,20 +221,22 @@ impl RenderState { modifiers: Option<&Matrix>, clip_bounds: Option<(Rect, Option, Matrix)>, ) { - self.surfaces.shape.canvas().save(); + self.surfaces.canvas(SurfaceId::Shape).save(); if let Some((bounds, corners, transform)) = clip_bounds { - self.surfaces.shape.canvas().concat(&transform); + self.surfaces.canvas(SurfaceId::Shape).concat(&transform); if let Some(corners) = corners { let rrect = RRect::new_rect_radii(bounds, &corners); - self.surfaces - .shape - .canvas() - .clip_rrect(rrect, skia::ClipOp::Intersect, true); + self.surfaces.canvas(SurfaceId::Shape).clip_rrect( + rrect, + skia::ClipOp::Intersect, + true, + ); } else { - self.surfaces - .shape - .canvas() - .clip_rect(bounds, skia::ClipOp::Intersect, true); + self.surfaces.canvas(SurfaceId::Shape).clip_rect( + bounds, + skia::ClipOp::Intersect, + true, + ); } if self.options.is_debug_visible() { @@ -267,12 +244,13 @@ impl RenderState { paint.set_style(skia::PaintStyle::Stroke); paint.set_color(skia::Color::from_argb(255, 255, 0, 0)); paint.set_stroke_width(4.); - self.surfaces.shape.canvas().draw_rect(bounds, &paint); + self.surfaces + .canvas(SurfaceId::Shape) + .draw_rect(bounds, &paint); } self.surfaces - .shape - .canvas() + .canvas(SurfaceId::Shape) .concat(&transform.invert().unwrap_or(Matrix::default())); } @@ -292,17 +270,17 @@ impl RenderState { match &shape.shape_type { Type::SVGRaw(sr) => { if let Some(modifiers) = modifiers { - self.surfaces.shape.canvas().concat(&modifiers); + self.surfaces.canvas(SurfaceId::Shape).concat(&modifiers); } - self.surfaces.shape.canvas().concat(&matrix); + self.surfaces.canvas(SurfaceId::Shape).concat(&matrix); if let Some(svg) = shape.svg.as_ref() { - svg.render(self.surfaces.shape.canvas()) + svg.render(self.surfaces.canvas(SurfaceId::Shape)) } else { let font_manager = skia::FontMgr::from(self.font_provider.clone()); let dom_result = skia::svg::Dom::from_str(sr.content.to_string(), font_manager); match dom_result { Ok(dom) => { - dom.render(self.surfaces.shape.canvas()); + dom.render(self.surfaces.canvas(SurfaceId::Shape)); shape.set_svg(dom); } Err(e) => { @@ -312,7 +290,7 @@ impl RenderState { } } _ => { - self.surfaces.shape.canvas().concat(&matrix); + self.surfaces.canvas(SurfaceId::Shape).concat(&matrix); for fill in shape.fills().rev() { fills::render(self, &shape, fill); @@ -341,7 +319,7 @@ impl RenderState { }; self.apply_drawing_to_render_canvas(); - self.surfaces.shape.canvas().restore(); + self.surfaces.canvas(SurfaceId::Shape).restore(); } pub fn start_render_loop( @@ -356,13 +334,12 @@ impl RenderState { } } self.reset_canvas(); - self.surfaces.shape.canvas().scale(( + self.surfaces.canvas(SurfaceId::Shape).scale(( self.viewbox.zoom * self.options.dpr(), self.viewbox.zoom * self.options.dpr(), )); self.surfaces - .shape - .canvas() + .canvas(SurfaceId::Shape) .translate((self.viewbox.pan_x, self.viewbox.pan_y)); // self.pending_nodes = vec![NodeRenderState { @@ -423,7 +400,7 @@ impl RenderState { .map_or(true, |img| img.invalid) { self.cached_surface_image = Some(CachedSurfaceImage { - image: self.surfaces.current.image_snapshot(), + image: self.surfaces.snapshot(SurfaceId::Current), viewbox: self.viewbox, invalid: false, has_all_shapes: self.render_complete, @@ -452,29 +429,29 @@ impl RenderState { let image = &cached.image; let paint = skia::Paint::default(); - self.surfaces.target.canvas().save(); - self.surfaces.shape.canvas().save(); + self.surfaces.canvas(SurfaceId::Target).save(); + self.surfaces.canvas(SurfaceId::Shape).save(); let navigate_zoom = self.viewbox.zoom / cached.viewbox.zoom; let navigate_x = cached.viewbox.zoom * (self.viewbox.pan_x - cached.viewbox.pan_x); let navigate_y = cached.viewbox.zoom * (self.viewbox.pan_y - cached.viewbox.pan_y); self.surfaces - .target - .canvas() + .canvas(SurfaceId::Target) .scale((navigate_zoom, navigate_zoom)); - self.surfaces.target.canvas().translate(( + self.surfaces.canvas(SurfaceId::Target).translate(( navigate_x * self.options.dpr(), navigate_y * self.options.dpr(), )); - self.surfaces.target.canvas().clear(self.background_color); self.surfaces - .target - .canvas() + .canvas(SurfaceId::Target) + .clear(self.background_color); + self.surfaces + .canvas(SurfaceId::Target) .draw_image(image, (0, 0), Some(&paint)); - self.surfaces.target.canvas().restore(); - self.surfaces.shape.canvas().restore(); + self.surfaces.canvas(SurfaceId::Target).restore(); + self.surfaces.canvas(SurfaceId::Shape).restore(); self.flush(); @@ -495,7 +472,9 @@ impl RenderState { if group.masked { let paint = skia::Paint::default(); let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint); - self.surfaces.current.canvas().save_layer(&layer_rec); + self.surfaces + .canvas(SurfaceId::Current) + .save_layer(&layer_rec); } } _ => {} @@ -512,7 +491,9 @@ impl RenderState { let mut mask_paint = skia::Paint::default(); mask_paint.set_blend_mode(skia::BlendMode::DstIn); let mask_rec = skia::canvas::SaveLayerRec::default().paint(&mask_paint); - self.surfaces.current.canvas().save_layer(&mask_rec); + self.surfaces + .canvas(SurfaceId::Current) + .save_layer(&mask_rec); } if let Some(image_filter) = element.image_filter(self.viewbox.zoom * self.options.dpr()) { @@ -520,7 +501,9 @@ impl RenderState { } let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint); - self.surfaces.current.canvas().save_layer(&layer_rec); + self.surfaces + .canvas(SurfaceId::Current) + .save_layer(&layer_rec); } pub fn render_shape_exit(&mut self, element: &mut Shape, visited_mask: bool) { @@ -531,13 +514,13 @@ impl RenderState { match element.shape_type { Type::Group(group) => { if group.masked { - self.surfaces.current.canvas().restore(); + self.surfaces.canvas(SurfaceId::Current).restore(); } } _ => {} } } - self.surfaces.current.canvas().restore(); + self.surfaces.canvas(SurfaceId::Current).restore(); } pub fn render_shape_tree( diff --git a/render-wasm/src/render/debug.rs b/render-wasm/src/render/debug.rs index 345f02642..033f7c90e 100644 --- a/render-wasm/src/render/debug.rs +++ b/render-wasm/src/render/debug.rs @@ -1,7 +1,7 @@ use crate::shapes::Shape; use skia_safe as skia; -use super::RenderState; +use super::{RenderState, SurfaceId}; fn render_debug_view(render_state: &mut RenderState) { let mut paint = skia::Paint::default(); @@ -18,13 +18,12 @@ fn render_debug_view(render_state: &mut RenderState) { render_state .surfaces - .debug - .canvas() + .canvas(SurfaceId::Debug) .draw_rect(scaled_rect, &paint); } pub fn render_wasm_label(render_state: &mut RenderState) { - let canvas = render_state.surfaces.current.canvas(); + let canvas = render_state.surfaces.canvas(SurfaceId::Current); let skia::ISize { width, height } = canvas.base_layer_size(); let p = skia::Point::new(width as f32 - 100.0, height as f32 - 25.0); @@ -59,18 +58,13 @@ pub fn render_debug_shape(render_state: &mut RenderState, element: &Shape, inter render_state .surfaces - .debug - .canvas() + .canvas(SurfaceId::Debug) .draw_rect(scaled_rect, &paint); } pub fn render(render_state: &mut RenderState) { - let paint = skia::Paint::default(); render_debug_view(render_state); - render_state.surfaces.debug.draw( - &mut render_state.surfaces.current.canvas(), - (0.0, 0.0), - render_state.sampling_options, - Some(&paint), - ); + render_state + .surfaces + .draw_into(SurfaceId::Debug, SurfaceId::Current, None); } diff --git a/render-wasm/src/render/fills.rs b/render-wasm/src/render/fills.rs index 82aa8837c..e182e8473 100644 --- a/render-wasm/src/render/fills.rs +++ b/render-wasm/src/render/fills.rs @@ -1,8 +1,8 @@ -use crate::shapes::{Fill, ImageFill, Shape, Type}; use skia_safe::{self as skia, RRect}; -use super::RenderState; +use super::{RenderState, SurfaceId}; use crate::math::Rect; +use crate::shapes::{Fill, ImageFill, Shape, Type}; fn draw_image_fill_in_container( render_state: &mut RenderState, @@ -16,7 +16,7 @@ fn draw_image_fill_in_container( } let size = image_fill.size(); - let canvas = render_state.surfaces.shape.canvas(); + let canvas = render_state.surfaces.canvas(SurfaceId::Shape); let container = &shape.selrect; let path_transform = shape.to_path_transform(); let paint = fill.to_paint(container); @@ -96,7 +96,7 @@ fn draw_image_fill_in_container( * This SHOULD be the only public function in this module. */ pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill) { - let canvas = render_state.surfaces.shape.canvas(); + let canvas = render_state.surfaces.canvas(SurfaceId::Shape); let selrect = shape.selrect; let path_transform = shape.to_path_transform(); diff --git a/render-wasm/src/render/shadows.rs b/render-wasm/src/render/shadows.rs index 24ed0cfdd..f0af52698 100644 --- a/render-wasm/src/render/shadows.rs +++ b/render-wasm/src/render/shadows.rs @@ -1,51 +1,37 @@ use skia_safe::{self as skia}; -use super::RenderState; +use super::{RenderState, SurfaceId}; use crate::shapes::Shadow; pub fn render_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { let shadow_paint = shadow.to_paint(scale); - render_state.surfaces.shape.draw( - &mut render_state.surfaces.shadow.canvas(), - (0.0, 0.0), - render_state.sampling_options, - Some(&shadow_paint), - ); - - render_state.surfaces.shadow.draw( - &mut render_state.surfaces.current.canvas(), - (0.0, 0.0), - render_state.sampling_options, - Some(&skia::Paint::default()), - ); + render_state + .surfaces + .draw_into(SurfaceId::Shape, SurfaceId::Shadow, Some(&shadow_paint)); render_state .surfaces - .shadow - .canvas() + .draw_into(SurfaceId::Shadow, SurfaceId::Current, None); + + render_state + .surfaces + .canvas(SurfaceId::Shadow) .clear(skia::Color::TRANSPARENT); } pub fn render_inner_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { let shadow_paint = shadow.to_paint(scale); - render_state.surfaces.shape.draw( - render_state.surfaces.shadow.canvas(), - (0.0, 0.0), - render_state.sampling_options, - Some(&shadow_paint), - ); - - render_state.surfaces.shadow.draw( - &mut render_state.surfaces.overlay.canvas(), - (0.0, 0.0), - render_state.sampling_options, - None, - ); + render_state + .surfaces + .draw_into(SurfaceId::Shape, SurfaceId::Shadow, Some(&shadow_paint)); // , ShadowPaint render_state .surfaces - .shadow - .canvas() + .draw_into(SurfaceId::Shadow, SurfaceId::Overlay, None); // , None + + render_state + .surfaces + .canvas(SurfaceId::Shadow) .clear(skia::Color::TRANSPARENT); } diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index 6d37b6d4d..69121a01d 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -5,7 +5,7 @@ use crate::math::{Matrix, Point, Rect}; use crate::shapes::{Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, Type}; use skia_safe::{self as skia, RRect}; -use super::RenderState; +use super::{RenderState, SurfaceId}; fn draw_stroke_on_rect( canvas: &skia::Canvas, @@ -330,7 +330,7 @@ fn draw_image_stroke_in_container( } let size = image_fill.size(); - let canvas = render_state.surfaces.shape.canvas(); + let canvas = render_state.surfaces.canvas(SurfaceId::Shape); let container = &shape.selrect; let path_transform = shape.to_path_transform(); let svg_attrs = &shape.svg_attrs; @@ -432,7 +432,7 @@ fn draw_image_stroke_in_container( * This SHOULD be the only public function in this module. */ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) { - let canvas = render_state.surfaces.shape.canvas(); + let canvas = render_state.surfaces.canvas(SurfaceId::Shape); let dpr_scale = render_state.viewbox.zoom * render_state.options.dpr(); let selrect = shape.selrect; let path_transform = shape.to_path_transform(); diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index b7afb7059..3d7f10126 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -1,23 +1,38 @@ use super::gpu_state::GpuState; use skia_safe as skia; +pub enum SurfaceId { + Target, + Current, + Shape, + Shadow, + Overlay, + Debug, +} + pub struct Surfaces { // is the final destination surface, the one that it is represented in the canvas element. - pub target: skia::Surface, + target: skia::Surface, // keeps the current render - pub current: skia::Surface, + current: skia::Surface, // keeps the current shape - pub shape: skia::Surface, + shape: skia::Surface, // used for rendering shadows - pub shadow: skia::Surface, + shadow: skia::Surface, // for drawing the things that are over shadows. - pub overlay: skia::Surface, + overlay: skia::Surface, // for drawing debug info. - pub debug: skia::Surface, + debug: skia::Surface, + + sampling_options: skia::SamplingOptions, } impl Surfaces { - pub fn new(gpu_state: &mut GpuState, (width, height): (i32, i32)) -> Self { + pub fn new( + gpu_state: &mut GpuState, + (width, height): (i32, i32), + sampling_options: skia::SamplingOptions, + ) -> Self { let mut target = gpu_state.create_target_surface(width, height); let current = target.new_surface_with_dimensions((width, height)).unwrap(); let shadow = target.new_surface_with_dimensions((width, height)).unwrap(); @@ -32,20 +47,53 @@ impl Surfaces { overlay, shape, debug, + sampling_options, } } - pub fn set(&mut self, new_surface: skia::Surface) { - let dim = (new_surface.width(), new_surface.height()); - self.target = new_surface; + pub fn resize(&mut self, gpu_state: &mut GpuState, new_width: i32, new_height: i32) { + self.reset_from_target(gpu_state.create_target_surface(new_width, new_height)); + } + + pub fn snapshot(&mut self, id: SurfaceId) -> skia::Image { + self.get_mut(id).image_snapshot() + } + + pub fn canvas(&mut self, id: SurfaceId) -> &skia::Canvas { + self.get_mut(id).canvas() + } + + pub fn flush_and_submit(&mut self, gpu_state: &mut GpuState, id: SurfaceId) { + let surface = self.get_mut(id); + gpu_state.context.flush_and_submit_surface(surface, None); + } + + pub fn draw_into(&mut self, from: SurfaceId, to: SurfaceId, paint: Option<&skia::Paint>) { + let sampling_options = self.sampling_options; + + self.get_mut(from) + .clone() + .draw(self.canvas(to), (0.0, 0.0), sampling_options, paint); + } + + fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface { + match id { + SurfaceId::Target => &mut self.target, + SurfaceId::Current => &mut self.current, + SurfaceId::Shadow => &mut self.shadow, + SurfaceId::Overlay => &mut self.overlay, + SurfaceId::Shape => &mut self.shape, + SurfaceId::Debug => &mut self.debug, + } + } + + fn reset_from_target(&mut self, target: skia::Surface) { + let dim = (target.width(), target.height()); + self.target = target; self.current = self.target.new_surface_with_dimensions(dim).unwrap(); self.overlay = self.target.new_surface_with_dimensions(dim).unwrap(); self.shadow = self.target.new_surface_with_dimensions(dim).unwrap(); self.shape = self.target.new_surface_with_dimensions(dim).unwrap(); self.debug = self.target.new_surface_with_dimensions(dim).unwrap(); } - - pub fn resize(&mut self, gpu_state: &mut GpuState, new_width: i32, new_height: i32) { - self.set(gpu_state.create_target_surface(new_width, new_height)); - } }