use tiny_skia_path::NormalizedF32;
use crate::{BlendMode, PixmapRef, Shader, SpreadMode, Transform};
use crate::pipeline;
use crate::pipeline::RasterPipelineBuilder;
#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
use tiny_skia_path::NoStdFloat;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum FilterQuality {
Nearest,
Bilinear,
Bicubic,
}
#[derive(Copy, Clone, Debug)]
pub struct PixmapPaint {
pub opacity: f32,
pub blend_mode: BlendMode,
pub quality: FilterQuality,
}
impl Default for PixmapPaint {
fn default() -> Self {
PixmapPaint {
opacity: 1.0,
blend_mode: BlendMode::default(),
quality: FilterQuality::Nearest,
}
}
}
#[derive(Clone, Debug)]
pub struct Pattern<'a> {
pub(crate) pixmap: PixmapRef<'a>,
quality: FilterQuality,
spread_mode: SpreadMode,
pub(crate) opacity: NormalizedF32,
pub(crate) transform: Transform,
}
impl<'a> Pattern<'a> {
#[allow(clippy::new_ret_no_self)]
pub fn new(
pixmap: PixmapRef<'a>,
spread_mode: SpreadMode,
quality: FilterQuality,
opacity: f32,
transform: Transform,
) -> Shader {
Shader::Pattern(Pattern {
pixmap,
spread_mode,
quality,
opacity: NormalizedF32::new_clamped(opacity),
transform,
})
}
pub(crate) fn push_stages(&self, p: &mut RasterPipelineBuilder) -> Option<()> {
let ts = self.transform.invert()?;
p.push(pipeline::Stage::SeedShader);
p.push_transform(ts);
let mut quality = self.quality;
if ts.is_identity() || ts.is_translate() {
quality = FilterQuality::Nearest;
}
if quality == FilterQuality::Bilinear {
if ts.is_translate() {
if ts.tx == ts.tx.trunc() && ts.ty == ts.ty.trunc() {
quality = FilterQuality::Nearest;
}
}
}
match quality {
FilterQuality::Nearest => {
p.ctx.limit_x = pipeline::TileCtx {
scale: self.pixmap.width() as f32,
inv_scale: 1.0 / self.pixmap.width() as f32,
};
p.ctx.limit_y = pipeline::TileCtx {
scale: self.pixmap.height() as f32,
inv_scale: 1.0 / self.pixmap.height() as f32,
};
match self.spread_mode {
SpreadMode::Pad => { }
SpreadMode::Repeat => p.push(pipeline::Stage::Repeat),
SpreadMode::Reflect => p.push(pipeline::Stage::Reflect),
}
p.push(pipeline::Stage::Gather);
}
FilterQuality::Bilinear => {
p.ctx.sampler = pipeline::SamplerCtx {
spread_mode: self.spread_mode,
inv_width: 1.0 / self.pixmap.width() as f32,
inv_height: 1.0 / self.pixmap.height() as f32,
};
p.push(pipeline::Stage::Bilinear);
}
FilterQuality::Bicubic => {
p.ctx.sampler = pipeline::SamplerCtx {
spread_mode: self.spread_mode,
inv_width: 1.0 / self.pixmap.width() as f32,
inv_height: 1.0 / self.pixmap.height() as f32,
};
p.push(pipeline::Stage::Bicubic);
p.push(pipeline::Stage::Clamp0);
p.push(pipeline::Stage::ClampA);
}
}
if self.opacity != NormalizedF32::ONE {
debug_assert_eq!(
core::mem::size_of_val(&self.opacity),
4,
"alpha must be f32"
);
p.ctx.current_coverage = self.opacity.get();
p.push(pipeline::Stage::Scale1Float);
}
Some(())
}
}