#![allow(clippy::collapsible_if)]
use kurbo::{Affine, Rect, Shape, Stroke};
use peniko::{BlendMode, Brush, Color, Fill, FontData, ImageBrushRef, StyleRef};
use recording::RenderCommand;
use std::sync::Arc;
pub mod wasm_send_sync;
pub use wasm_send_sync::*;
pub mod types;
pub use types::*;
mod null_backend;
pub use null_backend::*;
pub mod recording;
pub use recording::Scene;
#[cfg(feature = "serde")]
mod svg_path_parser;
pub trait WindowRenderer {
type ScenePainter<'a>: PaintScene
where
Self: 'a;
fn resume(&mut self, window: Arc<dyn WindowHandle>, width: u32, height: u32);
fn suspend(&mut self);
fn is_active(&self) -> bool;
fn set_size(&mut self, width: u32, height: u32);
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F);
}
pub trait ImageRenderer {
type ScenePainter<'a>: PaintScene
where
Self: 'a;
fn new(width: u32, height: u32) -> Self;
fn resize(&mut self, width: u32, height: u32);
fn reset(&mut self);
fn render_to_vec<F: FnOnce(&mut Self::ScenePainter<'_>)>(
&mut self,
draw_fn: F,
vec: &mut Vec<u8>,
);
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F, buffer: &mut [u8]);
}
pub fn render_to_buffer<R: ImageRenderer, F: FnOnce(&mut R::ScenePainter<'_>)>(
draw_fn: F,
width: u32,
height: u32,
) -> Vec<u8> {
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);
buf
}
pub trait PaintScene {
fn reset(&mut self);
fn push_layer(
&mut self,
blend: impl Into<BlendMode>,
alpha: f32,
transform: Affine,
clip: &impl Shape,
);
fn push_clip_layer(&mut self, transform: Affine, clip: &impl Shape);
fn pop_layer(&mut self);
fn stroke<'a>(
&mut self,
style: &Stroke,
transform: Affine,
brush: impl Into<PaintRef<'a>>,
brush_transform: Option<Affine>,
shape: &impl Shape,
);
fn fill<'a>(
&mut self,
style: Fill,
transform: Affine,
brush: impl Into<PaintRef<'a>>,
brush_transform: Option<Affine>,
shape: &impl Shape,
);
#[allow(clippy::too_many_arguments)]
fn draw_glyphs<'a, 's: 'a>(
&'s mut self,
font: &'a FontData,
font_size: f32,
hint: bool,
normalized_coords: &'a [NormalizedCoord],
style: impl Into<StyleRef<'a>>,
brush: impl Into<PaintRef<'a>>,
brush_alpha: f32,
transform: Affine,
glyph_transform: Option<Affine>,
glyphs: impl Iterator<Item = Glyph>,
);
fn draw_box_shadow(
&mut self,
transform: Affine,
rect: Rect,
brush: Color,
radius: f64,
std_dev: f64,
);
fn append_scene(&mut self, scene: Scene, scene_transform: Affine) {
for cmd in scene.commands {
match cmd {
RenderCommand::PushLayer(cmd) => self.push_layer(
cmd.blend,
cmd.alpha,
scene_transform * cmd.transform,
&cmd.clip,
),
RenderCommand::PushClipLayer(cmd) => {
self.push_clip_layer(scene_transform * cmd.transform, &cmd.clip)
}
RenderCommand::PopLayer => self.pop_layer(),
RenderCommand::Stroke(cmd) => self.stroke(
&cmd.style,
scene_transform * cmd.transform,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
},
cmd.brush_transform,
&cmd.shape,
),
RenderCommand::Fill(cmd) => self.fill(
cmd.fill,
scene_transform * cmd.transform,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
},
cmd.brush_transform,
&cmd.shape,
),
RenderCommand::GlyphRun(cmd) => self.draw_glyphs(
&cmd.font_data,
cmd.font_size,
cmd.hint,
&cmd.normalized_coords,
&cmd.style,
match cmd.brush {
Brush::Solid(alpha_color) => Brush::Solid(alpha_color),
Brush::Gradient(ref gradient) => Brush::Gradient(gradient),
Brush::Image(ref image) => Brush::Image(image.as_ref()),
},
cmd.brush_alpha,
scene_transform * cmd.transform,
cmd.glyph_transform,
cmd.glyphs.into_iter(),
),
RenderCommand::BoxShadow(cmd) => self.draw_box_shadow(
scene_transform * cmd.transform,
cmd.rect,
cmd.brush,
cmd.radius,
cmd.std_dev,
),
}
}
}
fn draw_image(&mut self, image: ImageBrushRef, transform: Affine) {
self.fill(
Fill::NonZero,
transform,
image,
None,
&Rect::new(
0.0,
0.0,
image.image.width as f64,
image.image.height as f64,
),
);
}
}