diff --git a/crates/anyrender/src/lib.rs b/crates/anyrender/src/lib.rs index 27b457ef..dac76be6 100644 --- a/crates/anyrender/src/lib.rs +++ b/crates/anyrender/src/lib.rs @@ -111,6 +111,17 @@ pub trait RenderContext { } } +pub enum Backdrop { + Preserve, + Clear(Color), +} + +impl Default for Backdrop { + fn default() -> Self { + Self::Clear(Color::TRANSPARENT) + } +} + /// Abstraction for rendering a scene to a window pub trait WindowRenderer: RenderContext { type ScenePainter<'a>: PaintScene @@ -149,7 +160,7 @@ pub trait WindowRenderer: RenderContext { false } fn set_size(&mut self, width: u32, height: u32); - fn render)>(&mut self, draw_fn: F); + fn render)>(&mut self, backdrop: Backdrop, draw_fn: F); } /// Abstraction for rendering a scene to an image buffer @@ -162,21 +173,28 @@ pub trait ImageRenderer: RenderContext { fn reset(&mut self); fn render_to_vec)>( &mut self, + backdrop: Backdrop, draw_fn: F, vec: &mut Vec, ); - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]); + fn render)>( + &mut self, + backdrop: Backdrop, + draw_fn: F, + buffer: &mut [u8], + ); } /// Draw a scene to a buffer using an `ImageRenderer` pub fn render_to_buffer)>( + backdrop: Backdrop, draw_fn: F, width: u32, height: u32, ) -> Vec { let mut buf = Vec::with_capacity((width * height * 4) as usize); let mut renderer = R::new(width, height); - renderer.render_to_vec(draw_fn, &mut buf); + renderer.render_to_vec(backdrop, draw_fn, &mut buf); buf } diff --git a/crates/anyrender/src/null_backend.rs b/crates/anyrender/src/null_backend.rs index 9047aa80..fe80e8e2 100644 --- a/crates/anyrender/src/null_backend.rs +++ b/crates/anyrender/src/null_backend.rs @@ -1,6 +1,8 @@ //! A dummy implementation of the AnyRender traits while simply ignores all commands -use crate::{Filter, ImageRenderer, PaintScene, RenderContext, WindowHandle, WindowRenderer}; +use crate::{ + Backdrop, Filter, ImageRenderer, PaintScene, RenderContext, WindowHandle, WindowRenderer, +}; use std::sync::Arc; #[derive(Copy, Clone, Default)] @@ -46,7 +48,8 @@ impl WindowRenderer for NullWindowRenderer { fn set_size(&mut self, _width: u32, _height: u32) {} - fn render)>(&mut self, _draw_fn: F) {} + fn render)>(&mut self, _backdrop: Backdrop, _draw_fn: F) { + } } #[derive(Copy, Clone, Default)] @@ -75,12 +78,19 @@ impl ImageRenderer for NullImageRenderer { fn render_to_vec)>( &mut self, + _backdrop: Backdrop, _draw_fn: F, _vec: &mut Vec, ) { } - fn render)>(&mut self, _draw_fn: F, _buffer: &mut [u8]) {} + fn render)>( + &mut self, + _backdrop: Backdrop, + _draw_fn: F, + _buffer: &mut [u8], + ) { + } } #[derive(Copy, Clone, Default)] diff --git a/crates/anyrender_skia/src/image_renderer.rs b/crates/anyrender_skia/src/image_renderer.rs index ef06a643..e3ad2d63 100644 --- a/crates/anyrender_skia/src/image_renderer.rs +++ b/crates/anyrender_skia/src/image_renderer.rs @@ -1,6 +1,6 @@ -use anyrender::{ImageRenderer, RenderContext}; +use anyrender::{Backdrop, ImageRenderer, RenderContext}; use debug_timer::debug_timer; -use skia_safe::{AlphaType, Color, ColorType, ImageInfo, SurfaceProps, graphics, surfaces}; +use skia_safe::{AlphaType, ColorType, ImageInfo, SurfaceProps, graphics, surfaces}; use crate::{SkiaScenePainter, scene::SkiaSceneCache}; @@ -47,6 +47,7 @@ impl ImageRenderer for SkiaImageRenderer { fn render_to_vec)>( &mut self, + backdrop: Backdrop, draw_fn: F, buffer: &mut Vec, ) { @@ -62,8 +63,11 @@ impl ImageRenderer for SkiaImageRenderer { ) .unwrap(); - // Clear surface with transparent background to allow transparency in rendered images - surface.canvas().clear(Color::TRANSPARENT); + if let Backdrop::Clear(color) = backdrop { + surface + .canvas() + .clear(crate::scene::sk_peniko::color4f_from_alpha_color(color)); + } draw_fn(&mut SkiaScenePainter { inner: surface.canvas(), @@ -77,7 +81,12 @@ impl ImageRenderer for SkiaImageRenderer { timer.print_times("skia_raster: "); } - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]) { + fn render)>( + &mut self, + backdrop: Backdrop, + draw_fn: F, + buffer: &mut [u8], + ) { debug_timer!(timer, feature = "log_frame_times"); let mut surface = surfaces::wrap_pixels( @@ -88,8 +97,11 @@ impl ImageRenderer for SkiaImageRenderer { ) .unwrap(); - // Clear surface with transparent background to allow transparency in rendered images - surface.canvas().clear(Color::TRANSPARENT); + if let Backdrop::Clear(color) = backdrop { + surface + .canvas() + .clear(crate::scene::sk_peniko::color4f_from_alpha_color(color)); + } draw_fn(&mut SkiaScenePainter { inner: surface.canvas(), diff --git a/crates/anyrender_skia/src/scene.rs b/crates/anyrender_skia/src/scene.rs index c3fb2f04..7da51266 100644 --- a/crates/anyrender_skia/src/scene.rs +++ b/crates/anyrender_skia/src/scene.rs @@ -917,7 +917,7 @@ fn distant_light_direction(light: &DistantLightSource) -> Point3 { ) } -mod sk_peniko { +pub(crate) mod sk_peniko { use peniko::color::{AlphaColor, ColorSpaceTag, HueDirection, Srgb}; use peniko::{ BlendMode, Compose, Extend, Gradient, GradientKind, ImageAlphaType, ImageBrush, ImageData, @@ -1140,7 +1140,7 @@ mod sk_peniko { } } - pub(super) fn color4f_from_alpha_color(color: AlphaColor) -> SkColor4f { + pub(crate) fn color4f_from_alpha_color(color: AlphaColor) -> SkColor4f { SkColor4f::new( color.components[0], color.components[1], diff --git a/crates/anyrender_skia/src/window_renderer.rs b/crates/anyrender_skia/src/window_renderer.rs index 82431d7e..bb27e0b4 100644 --- a/crates/anyrender_skia/src/window_renderer.rs +++ b/crates/anyrender_skia/src/window_renderer.rs @@ -1,6 +1,6 @@ -use anyrender::{RenderContext, WindowRenderer}; +use anyrender::{Backdrop, RenderContext, WindowRenderer}; use debug_timer::debug_timer; -use skia_safe::{Color, Surface, graphics}; +use skia_safe::{Surface, graphics}; use std::any::Any; use std::sync::Arc; @@ -28,8 +28,6 @@ struct ActiveRenderState { #[derive(Debug, Clone)] #[non_exhaustive] pub struct SkiaRendererOptions { - /// Background color used to clear the canvas. - pub base_color: Color, /// Alpha mode used when compositing the window surface. pub composite_alpha_mode: anyrender::CompositeAlphaMode, } @@ -43,15 +41,10 @@ impl Default for SkiaRendererOptions { impl SkiaRendererOptions { pub const fn new() -> Self { Self { - base_color: Color::WHITE, composite_alpha_mode: anyrender::CompositeAlphaMode::Auto, } } - pub const fn base_color(self, base_color: Color) -> Self { - Self { base_color, ..self } - } - pub const fn composite_alpha_mode( self, composite_alpha_mode: anyrender::CompositeAlphaMode, @@ -66,10 +59,6 @@ impl SkiaRendererOptions { impl From for SkiaRendererOptions { fn from(config: anyrender::RendererConfig) -> Self { let mut options = Self::default(); - if let Some(color) = config.base_color { - let rgba8 = color.to_rgba8(); - options.base_color = skia_safe::Color::from_argb(rgba8.a, rgba8.r, rgba8.g, rgba8.b); - } if let Some(mode) = config.composite_alpha_mode { options.composite_alpha_mode = mode; } @@ -163,7 +152,7 @@ impl WindowRenderer for SkiaWindowRenderer { } } - fn render)>(&mut self, draw_fn: F) { + fn render)>(&mut self, backdrop: Backdrop, draw_fn: F) { let RenderState::Active(state) = &mut self.render_state else { return; }; @@ -176,7 +165,11 @@ impl WindowRenderer for SkiaWindowRenderer { }; surface.canvas().restore_to_count(1); - surface.canvas().clear(self.options.base_color); + if let Backdrop::Clear(color) = backdrop { + surface + .canvas() + .clear(crate::scene::sk_peniko::color4f_from_alpha_color(color)); + } draw_fn(&mut SkiaScenePainter { inner: surface.canvas(), diff --git a/crates/anyrender_vello/src/image_renderer.rs b/crates/anyrender_vello/src/image_renderer.rs index 2e5faeef..0b140de2 100644 --- a/crates/anyrender_vello/src/image_renderer.rs +++ b/crates/anyrender_vello/src/image_renderer.rs @@ -1,4 +1,4 @@ -use anyrender::{ImageRenderer, RenderContext, ResourceId}; +use anyrender::{Backdrop, ImageRenderer, RenderContext, ResourceId}; use peniko::ImageData; use rustc_hash::FxHashMap; use vello::{Renderer as VelloRenderer, RendererOptions, Scene as VelloScene}; @@ -77,16 +77,18 @@ impl ImageRenderer for VelloImageRenderer { fn render_to_vec)>( &mut self, + backdrop: Backdrop, draw_fn: F, cpu_buffer: &mut Vec, ) { let size = self.buffer_renderer.size(); cpu_buffer.resize((size.width * size.height * 4) as usize, 0); - self.render(draw_fn, cpu_buffer); + self.render(backdrop, draw_fn, cpu_buffer); } fn render)>( &mut self, + backdrop: Backdrop, draw_fn: F, cpu_buffer: &mut [u8], ) { @@ -96,6 +98,20 @@ impl ImageRenderer for VelloImageRenderer { device_handle: None, texture_handles: Some(&mut self.texture_handles), }); + let texture_view = self.buffer_renderer.target_texture_view(); + let backdrop_image = if matches!(backdrop, Backdrop::Preserve) { + let mut scene = VelloScene::new(); + let image_data = self + .vello_renderer + .register_texture(texture_view.texture().clone()); + let image = peniko::ImageBrush::new(image_data.clone()); + scene.draw_image(&image, kurbo::Affine::IDENTITY); + std::mem::swap(&mut self.scene, &mut scene); + self.scene.append(&scene, None); + Some(image_data) + } else { + None + }; let size = self.buffer_renderer.size(); self.vello_renderer @@ -103,9 +119,12 @@ impl ImageRenderer for VelloImageRenderer { self.buffer_renderer.device(), self.buffer_renderer.queue(), &self.scene, - &self.buffer_renderer.target_texture_view(), + &texture_view, &vello::RenderParams { - base_color: vello::peniko::Color::TRANSPARENT, + base_color: match backdrop { + Backdrop::Preserve => vello::peniko::Color::TRANSPARENT, + Backdrop::Clear(color) => color, + }, width: size.width, height: size.height, antialiasing_method: vello::AaConfig::Area, @@ -113,6 +132,10 @@ impl ImageRenderer for VelloImageRenderer { ) .expect("Got non-Send/Sync error from rendering"); + if let Some(image) = backdrop_image { + self.vello_renderer.unregister_texture(image); + } + self.buffer_renderer.copy_texture_to_buffer(cpu_buffer); // Empty the Vello scene (memory optimisation) diff --git a/crates/anyrender_vello/src/window_renderer.rs b/crates/anyrender_vello/src/window_renderer.rs index 13b80246..e0661a06 100644 --- a/crates/anyrender_vello/src/window_renderer.rs +++ b/crates/anyrender_vello/src/window_renderer.rs @@ -1,5 +1,5 @@ use anyrender::{ - RegisterResourceErrorKind, RenderContext, ResourceId, WindowHandle, WindowRenderer, + Backdrop, RegisterResourceErrorKind, RenderContext, ResourceId, WindowHandle, WindowRenderer, }; use debug_timer::debug_timer; use futures_channel::oneshot; @@ -61,7 +61,6 @@ enum RenderState { pub struct VelloRendererOptions { pub features: Option, pub limits: Option, - pub base_color: Color, pub antialiasing_method: AaConfig, /// Alpha mode used when compositing the window surface. pub composite_alpha_mode: anyrender::CompositeAlphaMode, @@ -87,7 +86,6 @@ impl VelloRendererOptions { Self { features: None, limits: None, - base_color: Color::WHITE, antialiasing_method: AaConfig::Msaa16, composite_alpha_mode: anyrender::CompositeAlphaMode::Auto, pipeline_cache: None, @@ -109,10 +107,6 @@ impl VelloRendererOptions { } } - pub fn base_color(self, base_color: Color) -> Self { - Self { base_color, ..self } - } - pub fn antialiasing_method(self, antialiasing_method: AaConfig) -> Self { Self { antialiasing_method, @@ -148,7 +142,6 @@ impl VelloRendererOptions { impl From for VelloRendererOptions { fn from(config: anyrender::RendererConfig) -> Self { Self { - base_color: config.base_color.unwrap_or(Color::WHITE), composite_alpha_mode: config.composite_alpha_mode.unwrap_or_default(), ..Default::default() } @@ -435,7 +428,7 @@ impl WindowRenderer for VelloWindowRenderer { }; } - fn render)>(&mut self, draw_fn: F) { + fn render)>(&mut self, backdrop: Backdrop, draw_fn: F) { let RenderState::Active(state) = &mut self.render_state else { return; }; @@ -459,6 +452,20 @@ impl WindowRenderer for VelloWindowRenderer { return; }; + let backdrop_image = if matches!(backdrop, Backdrop::Preserve) { + let mut scene = VelloScene::new(); + let image_data = state + .renderer + .register_texture(texture_view.texture().clone()); + let image = peniko::ImageBrush::new(image_data.clone()); + scene.draw_image(&image, kurbo::Affine::IDENTITY); + std::mem::swap(&mut self.scene, &mut scene); + self.scene.append(&scene, None); + Some(image_data) + } else { + None + }; + for handle in self.texture_handles.values() { state.renderer.mark_override_image_dirty(handle); } @@ -471,7 +478,10 @@ impl WindowRenderer for VelloWindowRenderer { &self.scene, &texture_view, &RenderParams { - base_color: self.config.base_color, + base_color: match backdrop { + Backdrop::Preserve => Color::TRANSPARENT, + Backdrop::Clear(color) => color, + }, width: render_surface.config.width, height: render_surface.config.height, antialiasing_method: self.config.antialiasing_method, @@ -480,6 +490,10 @@ impl WindowRenderer for VelloWindowRenderer { .expect("failed to render to texture"); timer.record_time("render"); + if let Some(image) = backdrop_image { + state.renderer.unregister_texture(image); + } + drop(texture_view); if render_surface.maybe_blit_and_present().is_err() { diff --git a/crates/anyrender_vello_cpu/src/image_renderer.rs b/crates/anyrender_vello_cpu/src/image_renderer.rs index 619d5d4c..343b6025 100644 --- a/crates/anyrender_vello_cpu/src/image_renderer.rs +++ b/crates/anyrender_vello_cpu/src/image_renderer.rs @@ -1,7 +1,8 @@ use crate::{ImageCacheConfig, VelloCpuScenePainter}; -use anyrender::{ImageRenderer, RenderContext as AnyRenderContext}; +use anyrender::{Backdrop, ImageRenderer, RenderContext as AnyRenderContext}; use debug_timer::debug_timer; -use vello_cpu::{PixmapMut, RenderContext}; +use kurbo::{Point, Rect, Shape as _, Size}; +use vello_cpu::{CompositeMode, PixmapMut, RenderContext}; pub struct VelloCpuImageRenderer { scene: VelloCpuScenePainter, @@ -43,16 +44,34 @@ impl ImageRenderer for VelloCpuImageRenderer { self.scene.render_ctx.reset(); } - fn render)>(&mut self, draw_fn: F, buffer: &mut [u8]) { + fn render)>( + &mut self, + backdrop: Backdrop, + draw_fn: F, + buffer: &mut [u8], + ) { debug_timer!(timer, feature = "log_frame_times"); + if let Backdrop::Clear(color) = backdrop { + self.scene.render_ctx.set_paint(color); + self.scene.render_ctx.fill_path( + &Rect::from_origin_size( + Point::ORIGIN, + Size::new( + self.scene.render_ctx.width() as f64, + self.scene.render_ctx.height() as f64, + ), + ) + .to_path(0.1), + ); + } draw_fn(&mut self.scene); timer.record_time("cmds"); self.scene.render_ctx.flush(); timer.record_time("flush"); - self.scene.render_ctx.render( + self.scene.render_ctx.render_with( PixmapMut::new( self.scene.render_ctx.width(), self.scene.render_ctx.height(), @@ -60,6 +79,13 @@ impl ImageRenderer for VelloCpuImageRenderer { ) .unwrap(), &mut self.scene.resources, + vello_cpu::RasterizerSettings { + composite_mode: match backdrop { + Backdrop::Preserve => CompositeMode::SrcOver, + Backdrop::Clear(_) => CompositeMode::Replace, + }, + ..Default::default() + }, ); timer.record_time("render"); @@ -71,12 +97,13 @@ impl ImageRenderer for VelloCpuImageRenderer { fn render_to_vec)>( &mut self, + backdrop: Backdrop, draw_fn: F, buffer: &mut Vec, ) { let width = self.scene.render_ctx.width(); let height = self.scene.render_ctx.height(); buffer.resize(width as usize * height as usize * 4, 0); - self.render(draw_fn, buffer); + self.render(backdrop, draw_fn, buffer); } } diff --git a/crates/anyrender_vello_cpu/src/window_renderer.rs b/crates/anyrender_vello_cpu/src/window_renderer.rs index f50c90aa..868d07f0 100644 --- a/crates/anyrender_vello_cpu/src/window_renderer.rs +++ b/crates/anyrender_vello_cpu/src/window_renderer.rs @@ -1,5 +1,5 @@ #[cfg(feature = "softbuffer_window_renderer")] -pub use softbuffer_window_renderer::{SoftbufferRendererOptions, SoftbufferWindowRenderer}; +pub use softbuffer_window_renderer::SoftbufferWindowRenderer; #[cfg(feature = "pixels_window_renderer")] pub use pixels_window_renderer::PixelsRendererOptions; diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs index c3266500..1f979c4d 100644 --- a/crates/anyrender_vello_hybrid/src/window_renderer.rs +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -1,9 +1,11 @@ use anyrender::{ - PaintScene, RegisterResourceErrorKind, RenderContext, ResourceId, WindowHandle, WindowRenderer, + Backdrop, Paint, PaintScene, RegisterResourceErrorKind, RenderContext, ResourceId, + WindowHandle, WindowRenderer, }; use debug_timer::debug_timer; use futures_channel::oneshot; -use peniko::Color; +use kurbo::Point; +use peniko::{Color, ImageBrush, ImageSampler}; use rustc_hash::FxHashMap; use std::future::Future; use std::sync::Arc; @@ -62,7 +64,6 @@ pub struct VelloHybridRendererOptions { pub features: Option, pub limits: Option, pub render_settings: RenderSettings, - pub base_color: Color, /// Alpha mode used when compositing the window surface. pub composite_alpha_mode: anyrender::CompositeAlphaMode, /// Maximum number of frames the presentation engine may queue ahead of the @@ -78,7 +79,6 @@ impl Default for VelloHybridRendererOptions { features: None, limits: None, render_settings: RenderSettings::default(), - base_color: Color::WHITE, composite_alpha_mode: anyrender::CompositeAlphaMode::Auto, desired_maximum_frame_latency: 1, } @@ -112,10 +112,6 @@ impl VelloHybridRendererOptions { } } - pub const fn base_color(self, base_color: Color) -> Self { - Self { base_color, ..self } - } - pub const fn composite_alpha_mode( self, composite_alpha_mode: anyrender::CompositeAlphaMode, @@ -137,7 +133,6 @@ impl VelloHybridRendererOptions { impl From for VelloHybridRendererOptions { fn from(config: anyrender::RendererConfig) -> Self { Self { - base_color: config.base_color.unwrap_or(Color::WHITE), composite_alpha_mode: config.composite_alpha_mode.unwrap_or_default(), ..Default::default() } @@ -451,13 +446,15 @@ impl WindowRenderer for VelloHybridWindowRenderer { } } - fn render)>(&mut self, draw_fn: F) { + fn render)>(&mut self, backdrop: Backdrop, draw_fn: F) { let RenderState::Active(state) = &mut self.render_state else { return; }; let render_surface = &mut state.render_surface; + let target_texture = render_surface.target_texture_view().clone(); + debug_timer!(timer, feature = "log_frame_times"); let mut encoder = @@ -483,25 +480,51 @@ impl WindowRenderer for VelloHybridWindowRenderer { texture_bindings: &mut state.texture_bindings, device_handle: &render_surface.device_handle, }; - if self.config.base_color != Color::TRANSPARENT { - scene_painter.fill( - peniko::Fill::NonZero, - kurbo::Affine::IDENTITY, - self.config.base_color, - None, - &kurbo::Rect::new( - 0., - 0., + match backdrop { + Backdrop::Preserve => { + let size = kurbo::Size::new( render_surface.config.width as f64, render_surface.config.height as f64, - ), - ); + ); + if let Ok(tv) = &target_texture { + let resource = scene_painter + .try_register_custom_resource(Box::new(tv.clone())) + .unwrap(); + let paint = Paint::Resource(ImageBrush { + image: resource, + sampler: ImageSampler::default(), + }); + scene_painter.fill( + peniko::Fill::NonZero, + kurbo::Affine::IDENTITY, + &paint, + None, + &kurbo::Rect::from_origin_size(Point::ORIGIN, size), + ); + } + } + Backdrop::Clear(color) => { + if color != Color::TRANSPARENT { + scene_painter.fill( + peniko::Fill::NonZero, + kurbo::Affine::IDENTITY, + color, + None, + &kurbo::Rect::new( + 0., + 0., + render_surface.config.width as f64, + render_surface.config.height as f64, + ), + ); + } + } } // Regenerate the vello scene draw_fn(&mut scene_painter); timer.record_time("cmd"); - let Ok(texture_view) = render_surface.target_texture_view() else { + let Ok(texture_view) = target_texture else { // Skip frame in case of error getting surface texture render_surface.clear_surface_texture(); return; diff --git a/crates/pixels_window_renderer/src/lib.rs b/crates/pixels_window_renderer/src/lib.rs index 3cd6bdff..689e850a 100644 --- a/crates/pixels_window_renderer/src/lib.rs +++ b/crates/pixels_window_renderer/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] -use anyrender::{ImageRenderer, RenderContext, WindowHandle, WindowRenderer}; +use anyrender::{Backdrop, ImageRenderer, RenderContext, WindowHandle, WindowRenderer}; use debug_timer::debug_timer; use pixels::{ Pixels, PixelsBuilder, SurfaceTexture, @@ -206,7 +206,11 @@ impl WindowRenderer for PixelsWindowRenderer }; } - fn render)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + backdrop: Backdrop, + draw_fn: F, + ) { let RenderState::Active(state) = &mut self.render_state else { return; }; @@ -214,7 +218,8 @@ impl WindowRenderer for PixelsWindowRenderer debug_timer!(timer, feature = "log_frame_times"); // Paint - self.renderer.render(draw_fn, state.pixels.frame_mut()); + self.renderer + .render(backdrop, draw_fn, state.pixels.frame_mut()); timer.record_time("render"); state.pixels.render().unwrap(); timer.record_time("present"); diff --git a/crates/softbuffer_window_renderer/src/lib.rs b/crates/softbuffer_window_renderer/src/lib.rs index 8a846fb3..676693a3 100644 --- a/crates/softbuffer_window_renderer/src/lib.rs +++ b/crates/softbuffer_window_renderer/src/lib.rs @@ -2,50 +2,11 @@ #![cfg_attr(docsrs, feature(doc_cfg))] -use anyrender::{ImageRenderer, PaintScene, RenderContext, WindowHandle, WindowRenderer}; +use anyrender::{Backdrop, ImageRenderer, RenderContext, WindowHandle, WindowRenderer}; use debug_timer::debug_timer; -use kurbo::{Affine, Rect}; use softbuffer::{Context, Surface}; use std::{num::NonZero, sync::Arc}; -/// Configuration options for the Softbuffer renderer. -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct SoftbufferRendererOptions { - /// Background color used to clear the frame. - pub base_color: peniko::Color, -} - -impl Default for SoftbufferRendererOptions { - fn default() -> Self { - Self::new() - } -} - -impl SoftbufferRendererOptions { - pub const fn new() -> Self { - Self { - base_color: peniko::Color::WHITE, - } - } - - pub const fn base_color(self, base_color: peniko::Color) -> Self { - Self { base_color, ..self } - } -} - -impl From for SoftbufferRendererOptions { - fn from(config: anyrender::RendererConfig) -> Self { - let mut options = Self::default(); - if let Some(color) = config.base_color { - options.base_color = color; - } - // `composite_alpha_mode` is intentionally ignored: softbuffer does not - // support transparency, so any requested alpha mode degrades to opaque. - options - } -} - // Simple struct to hold the state of the renderer pub struct ActiveRenderState { _context: Context>, @@ -65,7 +26,6 @@ pub struct SoftbufferWindowRenderer { window_handle: Option>, renderer: Renderer, buffer: Vec, - config: SoftbufferRendererOptions, width: u32, height: u32, } @@ -82,50 +42,6 @@ impl SoftbufferWindowRenderer { window_handle: None, renderer, buffer: Vec::new(), - config: SoftbufferRendererOptions::default(), - width: 0, - height: 0, - } - } - - pub fn with_options(config: impl Into) -> Self { - Self { - render_state: RenderState::Suspended, - window_handle: None, - renderer: Renderer::new(0, 0), - buffer: Vec::new(), - config: config.into(), - width: 0, - height: 0, - } - } - - pub fn try_with_options( - config: impl TryInto, - ) -> Result { - Ok(Self { - render_state: RenderState::Suspended, - window_handle: None, - renderer: Renderer::new(0, 0), - buffer: Vec::new(), - config: config.try_into()?, - width: 0, - height: 0, - }) - } - - pub fn with_options_and_renderer( - renderer: R, - config: impl TryInto, - ) -> SoftbufferWindowRenderer { - SoftbufferWindowRenderer { - render_state: RenderState::Suspended, - window_handle: None, - renderer, - buffer: Vec::new(), - config: config - .try_into() - .expect("Invalid Softbuffer renderer configuration"), width: 0, height: 0, } @@ -196,7 +112,11 @@ impl WindowRenderer for SoftbufferWindowRenderer)>(&mut self, draw_fn: F) { + fn render)>( + &mut self, + backdrop: Backdrop, + draw_fn: F, + ) { let RenderState::Active(state) = &mut self.render_state else { return; }; @@ -209,23 +129,8 @@ impl WindowRenderer for SoftbufferWindowRenderer| { - painter.fill( - peniko::Fill::NonZero, - Affine::IDENTITY, - base_color, - None, - &Rect::new(0.0, 0.0, width, height), - ); - draw_fn(painter); - }; - self.renderer - .render_to_vec(wrapped_draw_fn, &mut self.buffer); + .render_to_vec(backdrop, draw_fn, &mut self.buffer); timer.record_time("render"); let out = surface_buffer.as_mut(); diff --git a/examples/bunnymark/src/main.rs b/examples/bunnymark/src/main.rs index 94014108..dd73f46e 100644 --- a/examples/bunnymark/src/main.rs +++ b/examples/bunnymark/src/main.rs @@ -1,4 +1,4 @@ -use anyrender::{PaintScene, WindowRenderer}; +use anyrender::{Backdrop, PaintScene, WindowRenderer}; use anyrender_skia::{SkiaImageRenderer, SkiaWindowRenderer}; use anyrender_vello::VelloWindowRenderer; use anyrender_vello_cpu::VelloCpuWindowRenderer; @@ -225,7 +225,7 @@ impl ApplicationHandler for App { self.bunny_manager.count(), ); match renderer { - Renderer::Skia(r) => r.render(|scene_painter| { + Renderer::Skia(r) => r.render(Backdrop::default(), |scene_painter| { App::draw_scene( scene_painter, self.logical_width, @@ -235,7 +235,7 @@ impl ApplicationHandler for App { Color::from_rgb8(255, 0, 0), ); }), - Renderer::SkiaRaster(r) => r.render(|scene_painter| { + Renderer::SkiaRaster(r) => r.render(Backdrop::default(), |scene_painter| { App::draw_scene( scene_painter, self.logical_width, @@ -245,7 +245,7 @@ impl ApplicationHandler for App { Color::from_rgb8(255, 0, 0), ); }), - Renderer::Gpu(r) => r.render(|scene_painter| { + Renderer::Gpu(r) => r.render(Backdrop::default(), |scene_painter| { App::draw_scene( scene_painter, self.logical_width, @@ -255,7 +255,7 @@ impl ApplicationHandler for App { Color::from_rgb8(255, 0, 0), ); }), - Renderer::Hybrid(r) => r.render(|scene_painter| { + Renderer::Hybrid(r) => r.render(Backdrop::default(), |scene_painter| { App::draw_scene( scene_painter, self.logical_width, @@ -265,7 +265,7 @@ impl ApplicationHandler for App { Color::from_rgb8(255, 0, 0), ); }), - Renderer::Cpu(r) => r.render(|scene_painter| { + Renderer::Cpu(r) => r.render(Backdrop::default(), |scene_painter| { App::draw_scene( scene_painter, self.logical_width, diff --git a/examples/player/src/main.rs b/examples/player/src/main.rs index 4641733d..dd353964 100644 --- a/examples/player/src/main.rs +++ b/examples/player/src/main.rs @@ -1,4 +1,4 @@ -use anyrender::{NullWindowRenderer, PaintScene, Scene, WindowRenderer}; +use anyrender::{Backdrop, NullWindowRenderer, PaintScene, Scene, WindowRenderer}; use anyrender_serialize::SceneArchive; use anyrender_skia::SkiaWindowRenderer; use anyrender_vello::VelloWindowRenderer; @@ -182,24 +182,24 @@ impl ApplicationHandler for App { self.request_redraw(); } WindowEvent::RedrawRequested => match renderer { - Renderer::Skia(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } - Renderer::Gpu(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } - Renderer::Hybrid(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } - Renderer::Cpu(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } - Renderer::CpuSoftbuffer(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } - Renderer::Null(r) => { - r.render(|painter| painter.append_scene(self.scene.clone(), Affine::IDENTITY)) - } + Renderer::Skia(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), + Renderer::Gpu(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), + Renderer::Hybrid(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), + Renderer::Cpu(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), + Renderer::CpuSoftbuffer(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), + Renderer::Null(r) => r.render(Backdrop::default(), |painter| { + painter.append_scene(self.scene.clone(), Affine::IDENTITY) + }), }, WindowEvent::KeyboardInput { event: diff --git a/examples/serialize/src/main.rs b/examples/serialize/src/main.rs index 961782e0..1b8911c3 100644 --- a/examples/serialize/src/main.rs +++ b/examples/serialize/src/main.rs @@ -5,7 +5,7 @@ use std::io::BufWriter; use std::path::Path; use anyrender::recording::Scene; -use anyrender::{Glyph, PaintScene, render_to_buffer}; +use anyrender::{Backdrop, Glyph, PaintScene, render_to_buffer}; use anyrender_serialize::{SceneArchive, SerializeConfig}; use anyrender_vello_cpu::VelloCpuImageRenderer; use image::{ImageBuffer, RgbaImage}; @@ -305,6 +305,7 @@ fn create_checkerboard_image(width: u32, height: u32) -> ImageData { /// Render a scene to an RGBA buffer using Vello CPU. fn render_scene_to_buffer(scene: &Scene) -> Vec { render_to_buffer::( + Backdrop::default(), |painter| { painter.append_scene(scene.clone(), Affine::IDENTITY); },