use crate::Color;
use crate::geom::{Point, Rect, Size};
use crate::icon::Icon;
use crate::paint::Paint;
use crate::pixels::{AtlasPage, ImageRef, Mask, PixelView};
use crate::surface::PixelFormat;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ClipToken(Rect);
impl ClipToken {
#[doc(hidden)]
#[inline]
pub const fn restoring(previous: Rect) -> Self {
Self(previous)
}
#[inline]
pub const fn previous(self) -> Rect {
self.0
}
}
pub trait Painter {
fn size(&self) -> Size;
fn format(&self) -> PixelFormat;
fn clip(&self) -> Rect;
fn push_clip(&mut self, rect: Rect) -> ClipToken;
fn pop_clip(&mut self, token: ClipToken);
fn clear(&mut self, color: Color);
fn fill_rect(&mut self, rect: Rect, paint: Paint);
fn fill_rounded_rect(&mut self, rect: Rect, radius: i32, paint: Paint);
fn stroke_rounded_rect(&mut self, rect: Rect, radius: i32, thickness: i32, paint: Paint);
fn fill_circle(&mut self, centre: Point, radius: i32, paint: Paint);
fn stroke_circle(&mut self, centre: Point, radius: i32, thickness: i32, paint: Paint);
fn stroke_arc(
&mut self,
centre: Point,
radius: i32,
thickness: i32,
start: i32,
sweep: i32,
paint: Paint,
);
fn draw_line(&mut self, a: Point, b: Point, paint: Paint);
fn scroll_rows(&mut self, rect: Rect, dy: i32) -> bool {
let _ = (rect, dy);
false
}
fn fill_polygon_fx(&mut self, points: &[(i32, i32)], paint: Paint);
fn blit_mask(&mut self, at: Point, mask: &Mask<'_>, paint: Paint);
fn blit_glyph(&mut self, at: Point, page: &AtlasPage<'_>, rect: Rect, paint: Paint) {
if let Some(mask) = page.mask.sub(rect) {
self.blit_mask(at, &mask, paint);
}
}
fn blit(&mut self, src: &PixelView<'_>, at: Point);
fn blit_scaled(&mut self, src: &PixelView<'_>, dest: Rect);
fn blit_rounded(&mut self, src: &PixelView<'_>, dest: Rect, shape: Rect, radius: i32);
fn blit_image(&mut self, src: &ImageRef<'_>, dest: Rect) {
self.blit_scaled(&src.view, dest);
}
fn blit_image_rounded(&mut self, src: &ImageRef<'_>, dest: Rect, shape: Rect, radius: i32) {
self.blit_rounded(&src.view, dest, shape, radius);
}
fn is_clipped_out(&self) -> bool {
self.clip().is_empty()
}
fn visible(&self, rect: Rect) -> Option<Rect> {
self.clip().intersect(&rect)
}
fn fill_rects(&mut self, rects: &[Rect], paint: Paint) {
for rect in rects {
self.fill_rect(*rect, paint);
}
}
fn stroke_rect(&mut self, rect: Rect, thickness: i32, paint: Paint) {
let t = thickness.max(0);
if t == 0 || rect.is_empty() || paint.is_invisible() {
return;
}
if t * 2 >= rect.width.min(rect.height) {
self.fill_rect(rect, paint);
return;
}
let inner_height = rect.height - 2 * t;
self.fill_rect(Rect::new(rect.x, rect.y, rect.width, t), paint);
self.fill_rect(Rect::new(rect.x, rect.bottom() - t, rect.width, t), paint);
self.fill_rect(Rect::new(rect.x, rect.y + t, t, inner_height), paint);
self.fill_rect(
Rect::new(rect.right() - t, rect.y + t, t, inner_height),
paint,
);
}
fn fill_star(
&mut self,
centre: Point,
outer_radius: i32,
inner_radius: i32,
points: u32,
rotation: i32,
paint: Paint,
) {
use crate::angle::{COORD_LIMIT, direction, to_fx};
use crate::icon::MAX_ICON_VERTICES;
if points < 2 || outer_radius <= 0 || paint.is_invisible() {
return;
}
let count = (points as usize) * 2;
if count > MAX_ICON_VERTICES {
return;
}
let outer = outer_radius.clamp(0, COORD_LIMIT) as i64;
let inner = inner_radius.clamp(0, outer_radius) as i64;
let mut vertices = [(0i32, 0i32); MAX_ICON_VERTICES];
let (cx, cy) = (to_fx(centre.x), to_fx(centre.y));
for (i, vertex) in vertices.iter_mut().enumerate().take(count) {
let step = (i as i64 * crate::angle::TURN as i64 + count as i64 / 2) / count as i64;
let angle = rotation.wrapping_add(step as i32);
let (dx, dy) = direction(angle);
let r = if i % 2 == 0 { outer } else { inner };
*vertex = (
cx + ((dx as i64 * r) >> 8) as i32,
cy + ((dy as i64 * r) >> 8) as i32,
);
}
self.fill_polygon_fx(&vertices[..count], paint);
}
fn draw_icon(&mut self, icon: &Icon, rect: Rect, fore: Color, back: Color) {
use crate::icon::{GRID, Ink, MAX_SHAPES, fx_along};
if rect.is_empty() || GRID <= 0 {
return;
}
for shape in icon.shapes.iter().take(MAX_SHAPES) {
let mut points = [(0i32, 0i32); crate::icon::MAX_ICON_VERTICES];
let n = shape.points.len().min(crate::icon::MAX_ICON_VERTICES);
if n < 3 {
continue;
}
for (slot, &(gx, gy)) in points.iter_mut().zip(shape.points).take(n) {
*slot = (
fx_along(rect.x, rect.width, gx),
fx_along(rect.y, rect.height, gy),
);
}
let paint = match shape.ink {
Ink::Fore => fore,
Ink::Back => back,
};
self.fill_polygon_fx(&points[..n], paint.into());
}
}
}
pub struct Pen<'a> {
painter: &'a mut dyn Painter,
token: Option<ClipToken>,
}
impl<'a> Pen<'a> {
#[inline]
pub fn new(painter: &'a mut dyn Painter) -> Self {
Self {
painter,
token: None,
}
}
#[inline]
pub fn painter(&mut self) -> &mut dyn Painter {
self.painter
}
pub fn with_clip(&mut self, rect: Rect) -> Pen<'_> {
let token = self.painter.push_clip(rect);
Pen {
painter: self.painter,
token: Some(token),
}
}
#[inline]
pub fn size(&self) -> Size {
self.painter.size()
}
#[inline]
pub fn format(&self) -> PixelFormat {
self.painter.format()
}
#[inline]
pub fn clip(&self) -> Rect {
self.painter.clip()
}
#[inline]
pub fn is_clipped_out(&self) -> bool {
self.painter.is_clipped_out()
}
#[inline]
pub fn visible(&self, rect: Rect) -> Option<Rect> {
self.painter.visible(rect)
}
#[inline]
pub fn clear(&mut self, color: Color) {
self.painter.clear(color);
}
#[inline]
pub fn fill_rect(&mut self, rect: Rect, color: impl Into<Paint>) {
self.painter.fill_rect(rect, color.into());
}
#[inline]
pub fn fill_rects(&mut self, rects: &[Rect], color: impl Into<Paint>) {
self.painter.fill_rects(rects, color.into());
}
#[inline]
pub fn stroke_rect(&mut self, rect: Rect, thickness: i32, color: impl Into<Paint>) {
self.painter.stroke_rect(rect, thickness, color.into());
}
#[inline]
pub fn fill_rounded_rect(&mut self, rect: Rect, radius: i32, color: impl Into<Paint>) {
self.painter.fill_rounded_rect(rect, radius, color.into());
}
#[inline]
pub fn stroke_rounded_rect(
&mut self,
rect: Rect,
radius: i32,
thickness: i32,
color: impl Into<Paint>,
) {
self.painter
.stroke_rounded_rect(rect, radius, thickness, color.into());
}
#[inline]
pub fn fill_circle(&mut self, centre: Point, radius: i32, color: impl Into<Paint>) {
self.painter.fill_circle(centre, radius, color.into());
}
#[inline]
pub fn stroke_circle(
&mut self,
centre: Point,
radius: i32,
thickness: i32,
color: impl Into<Paint>,
) {
self.painter
.stroke_circle(centre, radius, thickness, color.into());
}
#[inline]
pub fn stroke_arc(
&mut self,
centre: Point,
radius: i32,
thickness: i32,
start: i32,
sweep: i32,
color: impl Into<Paint>,
) {
self.painter
.stroke_arc(centre, radius, thickness, start, sweep, color.into());
}
#[inline]
pub fn draw_line(&mut self, a: Point, b: Point, color: impl Into<Paint>) {
self.painter.draw_line(a, b, color.into());
}
#[inline]
pub fn fill_star(
&mut self,
centre: Point,
outer_radius: i32,
inner_radius: i32,
points: u32,
rotation: i32,
color: impl Into<Paint>,
) {
self.painter.fill_star(
centre,
outer_radius,
inner_radius,
points,
rotation,
color.into(),
);
}
#[inline]
pub fn fill_polygon_fx(&mut self, points: &[(i32, i32)], color: impl Into<Paint>) {
self.painter.fill_polygon_fx(points, color.into());
}
#[inline]
pub fn draw_icon(&mut self, icon: &Icon, rect: Rect, fore: Color, back: Color) {
self.painter.draw_icon(icon, rect, fore, back);
}
#[inline]
pub fn blit_mask(&mut self, at: Point, mask: &Mask<'_>, color: impl Into<Paint>) {
self.painter.blit_mask(at, mask, color.into());
}
#[inline]
pub fn blit_glyph(
&mut self,
at: Point,
page: &AtlasPage<'_>,
rect: Rect,
color: impl Into<Paint>,
) {
self.painter.blit_glyph(at, page, rect, color.into());
}
#[inline]
pub fn blit(&mut self, src: &PixelView<'_>, at: Point) {
self.painter.blit(src, at);
}
#[inline]
pub fn blit_scaled(&mut self, src: &PixelView<'_>, dest: Rect) {
self.painter.blit_scaled(src, dest);
}
#[inline]
pub fn blit_rounded(&mut self, src: &PixelView<'_>, dest: Rect, shape: Rect, radius: i32) {
self.painter.blit_rounded(src, dest, shape, radius);
}
#[inline]
pub fn blit_image(&mut self, src: &ImageRef<'_>, dest: Rect) {
self.painter.blit_image(src, dest);
}
#[inline]
pub fn blit_image_rounded(&mut self, src: &ImageRef<'_>, dest: Rect, shape: Rect, radius: i32) {
self.painter.blit_image_rounded(src, dest, shape, radius);
}
#[inline]
pub fn scroll_rows(&mut self, rect: Rect, dy: i32) -> bool {
self.painter.scroll_rows(rect, dy)
}
}
impl Drop for Pen<'_> {
fn drop(&mut self) {
if let Some(token) = self.token.take() {
self.painter.pop_clip(token);
}
}
}
impl core::fmt::Debug for Pen<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Pen")
.field("size", &self.size())
.field("clip", &self.clip())
.finish_non_exhaustive()
}
}
pub trait PainterExt: Painter {
fn with_clip(&mut self, rect: Rect) -> Clipped<'_, Self> {
let token = self.push_clip(rect);
Clipped {
painter: self,
token,
}
}
}
impl<P: Painter + ?Sized> PainterExt for P {}
pub struct Clipped<'p, P: Painter + ?Sized> {
painter: &'p mut P,
token: ClipToken,
}
impl<P: Painter + ?Sized> Drop for Clipped<'_, P> {
fn drop(&mut self) {
self.painter.pop_clip(self.token);
}
}
impl<P: Painter + ?Sized> core::ops::Deref for Clipped<'_, P> {
type Target = P;
#[inline]
fn deref(&self) -> &P {
self.painter
}
}
impl<P: Painter + ?Sized> core::ops::DerefMut for Clipped<'_, P> {
#[inline]
fn deref_mut(&mut self) -> &mut P {
self.painter
}
}