pub mod image;
pub mod prof;
pub mod skia;
pub use image::{DecodedImage, Fit, Image, ImageDecoder, ImageError, VisualState};
pub use skia::SkiaCanvas;
use crate::geometry::{Color, Rect};
use crate::spec::Align;
use crate::text::TextEngine;
#[derive(Debug, Clone, Copy)]
pub struct GradientStop {
pub offset: f32,
pub color: Color,
}
#[derive(Debug, Clone)]
pub enum Gradient {
Linear {
start: (f32, f32),
end: (f32, f32),
stops: Vec<GradientStop>,
},
Radial {
center: (f32, f32),
radius: f32,
stops: Vec<GradientStop>,
},
}
impl Gradient {
fn to_stops(stops: Vec<(f32, Color)>) -> Vec<GradientStop> {
stops
.into_iter()
.map(|(offset, color)| GradientStop { offset, color })
.collect()
}
pub fn linear(start: (f32, f32), end: (f32, f32), stops: Vec<(f32, Color)>) -> Self {
Gradient::Linear {
start,
end,
stops: Self::to_stops(stops),
}
}
pub fn radial(center: (f32, f32), radius: f32, stops: Vec<(f32, Color)>) -> Self {
Gradient::Radial {
center,
radius,
stops: Self::to_stops(stops),
}
}
pub fn stops(&self) -> &[GradientStop] {
match self {
Gradient::Linear { stops, .. } | Gradient::Radial { stops, .. } => stops,
}
}
}
#[derive(Debug, Clone)]
pub struct Paint {
pub color: Color,
pub anti_alias: bool,
pub gradient: Option<Gradient>,
}
impl Paint {
pub fn fill(color: Color) -> Self {
Self {
color,
anti_alias: true,
gradient: None,
}
}
pub fn gradient(g: Gradient) -> Self {
let color = g
.stops()
.first()
.map(|s| s.color)
.unwrap_or(Color::TRANSPARENT);
Self {
color,
anti_alias: true,
gradient: Some(g),
}
}
}
pub trait Canvas {
fn fill_rect(&mut self, x: f32, y: f32, w: f32, h: f32, paint: &Paint);
fn fill_round_rect(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32, paint: &Paint);
fn stroke_round_rect(
&mut self,
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
width: f32,
paint: &Paint,
);
fn draw_line(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, width: f32, paint: &Paint);
fn fill_circle(&mut self, cx: f32, cy: f32, r: f32, paint: &Paint);
fn draw_shadow(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32, blur: f32, color: Color);
fn draw_image(
&mut self,
img: &image::Image,
dst: Rect,
fit: image::Fit,
radius: f32,
opacity: f32,
);
fn draw_text(
&mut self,
text: &str,
rect: Rect,
color: Color,
align: Align,
family: Option<&str>,
size: f32,
);
fn measure_text(
&mut self,
text: &str,
family: Option<&str>,
size: f32,
) -> crate::geometry::Size;
fn push_layer(&mut self, opacity: f32);
fn pop_layer(&mut self);
fn save(&mut self);
fn restore(&mut self);
fn clip_rect(&mut self, r: Rect);
}
pub trait RenderTarget {
fn make_canvas<'a>(
&'a mut self,
engine: &'a mut dyn TextEngine,
scale: f32,
) -> Box<dyn Canvas + 'a>;
fn as_pixmap(&mut self) -> Option<&mut tiny_skia::Pixmap> {
None
}
}
pub struct PixmapTarget<'p> {
pub pixmap: &'p mut tiny_skia::Pixmap,
}
impl RenderTarget for PixmapTarget<'_> {
fn make_canvas<'a>(
&'a mut self,
engine: &'a mut dyn TextEngine,
scale: f32,
) -> Box<dyn Canvas + 'a> {
Box::new(SkiaCanvas::with_text(&mut *self.pixmap, engine, scale))
}
fn as_pixmap(&mut self) -> Option<&mut tiny_skia::Pixmap> {
Some(self.pixmap)
}
}
pub(crate) fn rounded_rect_path(
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
) -> Option<tiny_skia::Path> {
use tiny_skia::PathBuilder;
if w <= 0.0 || h <= 0.0 {
return None;
}
let r = radius.min(w / 2.0).min(h / 2.0).max(0.0);
let mut pb = PathBuilder::new();
if r <= 0.0 {
pb.push_rect(tiny_skia::Rect::from_xywh(x, y, w, h)?);
return pb.finish();
}
let k = 0.552_284_8 * r; let (l, t, rt, b) = (x, y, x + w, y + h);
pb.move_to(l + r, t);
pb.line_to(rt - r, t);
pb.cubic_to(rt - r + k, t, rt, t + r - k, rt, t + r);
pb.line_to(rt, b - r);
pb.cubic_to(rt, b - r + k, rt - r + k, b, rt - r, b);
pb.line_to(l + r, b);
pb.cubic_to(l + r - k, b, l, b - r + k, l, b - r);
pb.line_to(l, t + r);
pb.cubic_to(l, t + r - k, l + r - k, t, l + r, t);
pb.close();
pb.finish()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixmap_target_make_canvas_paints() {
use crate::text::NullTextEngine;
let mut pixmap = tiny_skia::Pixmap::new(10, 10).unwrap();
let mut engine = NullTextEngine;
{
let mut target = PixmapTarget {
pixmap: &mut pixmap,
};
let mut canvas = target.make_canvas(&mut engine, 1.0);
canvas.fill_rect(0.0, 0.0, 10.0, 10.0, &Paint::fill(Color::rgb(255, 0, 0)));
}
let px = pixmap.data();
assert_eq!(&px[0..4], &[255, 0, 0, 255]);
}
}