use kurbo::{Affine, BezPath, Rect, Stroke};
use pdfrum_page::BlendMode;
use crate::pixmap::{AlphaMask, Pixmap};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AntiAlias {
#[default]
On,
Off,
FullCover,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FillRule {
#[default]
Winding,
EvenOdd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ImageQuality {
#[default]
Nearest,
Bilinear,
}
pub type RasterImage = Pixmap;
#[derive(Debug, Clone, Copy)]
pub enum Brush<'a> {
Solid(peniko::Color),
Image(&'a RasterImage),
}
pub trait RenderDevice {
fn fill_path(
&mut self,
path: &BezPath,
t: Affine,
brush: &Brush<'_>,
rule: FillRule,
aa: AntiAlias,
);
fn stroke_path(
&mut self,
path: &BezPath,
t: Affine,
brush: &Brush<'_>,
stroke: &Stroke,
aa: AntiAlias,
);
fn draw_image(&mut self, img: &RasterImage, t: Affine, quality: ImageQuality, alpha: f32);
fn draw_glyph_lcd(
&mut self,
glyph: &crate::glyph::SubpixelBitmap,
origin: (f64, f64),
colour: peniko::Color,
) {
let Some(gray) = crate::glyph::average_to_gray(glyph) else {
return;
};
let Some(pixels) = crate::glyph::recolour(&gray, colour) else {
return;
};
self.draw_image(
&pixels,
Affine::translate(origin),
ImageQuality::Nearest,
1.0,
);
}
fn push_clip(&mut self, path: &BezPath, rule: FillRule);
fn push_clip_rect(&mut self, rect: Rect);
fn push_layer(&mut self, blend: BlendMode, alpha: f32, mask: Option<&AlphaMask>);
fn pop(&mut self);
}
pub trait RasterBackend {
type Device: RenderDevice;
fn new_target(&self, w: u32, h: u32, clear: peniko::Color) -> Self::Device;
fn new_target_with_backdrop(&self, base: &Pixmap) -> Self::Device;
fn snapshot(&self, d: &Self::Device) -> Pixmap;
fn finish(&self, d: Self::Device) -> Pixmap;
fn snapshot_rect(
&self,
d: &Self::Device,
origin_x: u32,
origin_y: u32,
width: u32,
height: u32,
) -> Pixmap {
self.snapshot(d).cropped(origin_x, origin_y, width, height)
}
fn composite_isolated_groups_as_layers(&self) -> bool {
false
}
}
impl<T: RasterBackend + ?Sized> RasterBackend for &T {
type Device = T::Device;
fn new_target(&self, w: u32, h: u32, clear: peniko::Color) -> Self::Device {
(**self).new_target(w, h, clear)
}
fn new_target_with_backdrop(&self, base: &Pixmap) -> Self::Device {
(**self).new_target_with_backdrop(base)
}
fn snapshot(&self, d: &Self::Device) -> Pixmap {
(**self).snapshot(d)
}
fn finish(&self, d: Self::Device) -> Pixmap {
(**self).finish(d)
}
fn snapshot_rect(
&self,
d: &Self::Device,
origin_x: u32,
origin_y: u32,
width: u32,
height: u32,
) -> Pixmap {
(**self).snapshot_rect(d, origin_x, origin_y, width, height)
}
fn composite_isolated_groups_as_layers(&self) -> bool {
(**self).composite_isolated_groups_as_layers()
}
}
pub const MAX_TARGET_DIMENSION: u32 = u16::MAX as u32;
impl From<pdfrum_page::FillRule> for FillRule {
fn from(value: pdfrum_page::FillRule) -> Self {
match value {
pdfrum_page::FillRule::Winding => Self::Winding,
pdfrum_page::FillRule::EvenOdd | pdfrum_page::FillRule::None => Self::EvenOdd,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_fill_rule_reads_as_even_odd() {
assert_eq!(
FillRule::from(pdfrum_page::FillRule::None),
FillRule::EvenOdd
);
assert_eq!(
FillRule::from(pdfrum_page::FillRule::EvenOdd),
FillRule::EvenOdd
);
assert_eq!(
FillRule::from(pdfrum_page::FillRule::Winding),
FillRule::Winding
);
}
}