use std::sync::Arc;
use valo_dl::{ColorFilter, GlyphPos, Image, MaskBlur, Paint, PaintStyle, Sampling, TileMode};
use valo_geometry::{Color, FillRule, Matrix, Path, Rect, Stroke};
use valo_text::Font;
use super::filter_tree::{DrawMaskBlur, Fill, MaskedDraw};
#[derive(Clone, Copy, Debug, PartialEq)]
pub(super) enum Shape<'a> {
Rect(Rect),
Path { path: &'a Arc<Path>, rule: FillRule },
Stroke {
path: &'a Arc<Path>,
stroke: &'a Stroke,
},
}
impl<'a> Shape<'a> {
pub fn of_path(path: &'a Arc<Path>, rule: FillRule, style: &'a PaintStyle) -> Self {
match style {
PaintStyle::Fill => Shape::Path { path, rule },
PaintStyle::Stroke(stroke) => Shape::Stroke { path, stroke },
}
}
}
#[derive(Clone, Copy)]
pub(super) enum DrawSource<'a> {
Shape {
shape: Shape<'a>,
ink: Rect,
},
Image {
image: &'a Image,
src: Rect,
dst: Rect,
sampling: Sampling,
},
RRectBlur {
rect: Rect,
radii: [f32; 4],
blur: MaskBlur,
},
Glyphs(GlyphRun<'a>),
}
#[derive(Clone, Copy)]
pub(super) struct GlyphRun<'a> {
pub font: &'a Arc<Font>,
pub size: f32,
pub glyphs: &'a Arc<Vec<GlyphPos>>,
pub content_bounds: Rect,
pub device_bounds: Rect,
pub colour_filter: Option<ColorFilter>,
}
impl<'a> DrawSource<'a> {
pub fn blur_tile_mode(&self) -> TileMode {
match self {
DrawSource::Image { .. } => TileMode::Clamp,
_ => TileMode::Decal,
}
}
pub fn local_bounds(&self) -> Rect {
match self {
DrawSource::Shape { ink, .. } => *ink,
DrawSource::Image { dst, .. } => *dst,
DrawSource::RRectBlur { rect, .. } => *rect,
DrawSource::Glyphs(run) => run.content_bounds,
}
}
pub fn mask_blur(&self, paint: &Paint, blur: MaskBlur) -> Option<DrawMaskBlur<'a>> {
let masked = match *self {
DrawSource::Shape { shape, ink } if paint.shader.is_some() => MaskedDraw::Filled {
shape,
fill: Box::new(Fill::shader(paint, &ink, blur)),
},
DrawSource::Shape { shape, .. } => MaskedDraw::Colours(shape),
DrawSource::Image {
image,
src,
dst,
sampling,
} => MaskedDraw::Filled {
shape: Shape::Rect(dst),
fill: Box::new(Fill::image(image, &src, &dst, sampling, paint, blur)),
},
DrawSource::Glyphs(_) => MaskedDraw::GlyphRun,
DrawSource::RRectBlur { .. } => return None,
};
Some(DrawMaskBlur { blur, masked })
}
pub fn in_effect_layer(
self,
paint: &Paint,
mask_blur: Option<&DrawMaskBlur<'a>>,
) -> (Self, Paint) {
match mask_blur.map(|mask_blur| &mask_blur.masked) {
Some(MaskedDraw::Filled { shape, .. }) => {
let mask = DrawSource::Shape {
shape: *shape,
ink: self.local_bounds(),
};
(mask, white_mask(paint))
}
_ => (self, plain(paint)),
}
}
pub fn whole_image(&self) -> Option<(&'a Image, Rect)> {
match *self {
DrawSource::Image {
image, src, dst, ..
} if src == Rect::new(0.0, 0.0, image.width(), image.height()) => Some((image, dst)),
_ => None,
}
}
pub fn device_bounds(&self, transform: &Matrix) -> Rect {
let local = match self {
DrawSource::RRectBlur { rect, blur, .. } => rect.expand((blur.sigma * 3.0).ceil()),
DrawSource::Glyphs(run) => return run.device_bounds,
source => source.local_bounds(),
};
transform.map_rect(&local)
}
}
pub(super) fn white_mask(paint: &Paint) -> Paint {
Paint {
style: paint.style.clone(),
..Paint::from_color(Color::WHITE)
}
}
pub(super) fn fill_paint(paint: &Paint) -> Paint {
Paint {
shader: paint.shader.clone(),
color: paint.color,
color_filter: paint.color_filter,
..Paint::default()
}
}
pub(super) fn plain(paint: &Paint) -> Paint {
Paint {
mask_blur: None,
color_filter: None,
image_filter: None,
..paint.clone()
}
}