use std::ops::{Deref, DerefMut};
use sdl2::{
rect::Point,
render::{Canvas as SdlCanvas, RenderTarget, TextureCreator},
surface::{Surface, SurfaceContext},
video::{Window, WindowContext},
};
#[cfg(feature = "unifont")]
use sdl2::{
pixels::Color,
render::Texture,
};
#[cfg(feature = "unifont")]
use sdl2_unifont::renderer::SurfaceRenderer as TextRenderer;
pub type SurfaceCanvas<'a> = Canvas<Surface<'a>, SurfaceContext<'a>>;
pub type WindowCanvas = Canvas<Window, WindowContext>;
pub struct Canvas<T: RenderTarget, U> {
inner: SdlCanvas<T>,
texture_creator: TextureCreator<U>,
#[cfg(feature = "unifont")]
text_renderer: TextRenderer,
#[cfg(feature = "unifont")]
synced_colors: bool,
}
impl WindowCanvas {
pub fn new(inner: SdlCanvas<Window>) -> Self {
let texture_creator = inner.texture_creator();
#[cfg(feature = "unifont")]
let text_renderer = TextRenderer::new(inner.draw_color(), Color::RGBA(0, 0, 0, 0));
Self {
inner,
texture_creator,
#[cfg(feature = "unifont")]
text_renderer,
#[cfg(feature = "unifont")]
synced_colors: true,
}
}
}
impl<'a> SurfaceCanvas<'a> {
pub fn new(inner: SdlCanvas<Surface<'a>>) -> Self {
let texture_creator = inner.texture_creator();
#[cfg(feature = "unifont")]
let text_renderer = TextRenderer::new(inner.draw_color(), Color::RGBA(0, 0, 0, 0));
Self {
inner,
texture_creator,
#[cfg(feature = "unifont")]
text_renderer,
#[cfg(feature = "unifont")]
synced_colors: true,
}
}
#[cfg(feature = "unifont")]
pub fn draw_text_surface<P: Into<Point>>(
&mut self,
text: &str,
pos: P,
) -> Result<Surface, String> {
let pos = pos.into();
let surface = self.text_renderer.draw(text)?;
let mut rect = surface.rect();
rect.set_x(pos.x());
rect.set_y(pos.y());
surface.blit(None, self.inner.surface_mut(), rect)?;
Ok(surface)
}
}
impl<T: RenderTarget, U> Canvas<T, U> {
pub fn texture_creator(&self) -> &TextureCreator<U> {
&self.texture_creator
}
pub fn texture_creator_mut(&mut self) -> &mut TextureCreator<U> {
&mut self.texture_creator
}
#[cfg(feature = "unifont")]
pub fn text_renderer(&self) -> &TextRenderer {
&self.text_renderer
}
#[cfg(feature = "unifont")]
pub fn text_renderer_mut(&mut self) -> &mut TextRenderer {
&mut self.text_renderer
}
#[cfg(feature = "unifont")]
pub fn set_draw_color<C: Into<Color>>(&mut self, color: C) {
let color = color.into();
if self.synced_colors {
self.text_renderer.fg_color = color;
}
self.inner.set_draw_color(color)
}
#[cfg(feature = "unifont")]
pub fn set_text_color<C>(&mut self, color: C)
where C: Into<Option<Color>> {
self.synced_colors = if let Some(color) = color.into() {
self.text_renderer.fg_color = color;
false
} else {
self.text_renderer.fg_color = self.inner.draw_color();
true
};
}
#[cfg(feature = "unifont")]
pub fn draw_text<P: Into<Point>>(&mut self, text: &str, pos: P) -> Result<Texture, String> {
let pos = pos.into();
let surface = self.text_renderer.draw(text)?;
let texture = surface.as_texture(&mut self.texture_creator).unwrap();
let mut rect = surface.rect();
rect.set_x(pos.x());
rect.set_y(pos.y());
self.inner.copy(&texture, None, rect)?;
Ok(texture)
}
fn draw_circle_points(&mut self, center: Point, point: Point) -> Result<(), String> {
let points = &[
Point::new(center.x() + point.x(), center.y() + point.y()),
Point::new(center.x() - point.x(), center.y() + point.y()),
Point::new(center.x() + point.x(), center.y() - point.y()),
Point::new(center.x() - point.x(), center.y() - point.y()),
Point::new(center.x() + point.y(), center.y() + point.x()),
Point::new(center.x() - point.y(), center.y() + point.x()),
Point::new(center.x() + point.y(), center.y() - point.x()),
Point::new(center.x() - point.y(), center.y() - point.x()),
];
self.inner.draw_points(points.as_ref())
}
fn fill_circle_lines(&mut self, center: Point, point: Point) -> Result<(), String> {
let points = &[
Point::new(center.x() + point.x(), center.y() + point.y()),
Point::new(center.x() - point.x(), center.y() + point.y()),
Point::new(center.x() + point.x(), center.y() - point.y()),
Point::new(center.x() - point.x(), center.y() - point.y()),
Point::new(center.x() + point.y(), center.y() + point.x()),
Point::new(center.x() - point.y(), center.y() + point.x()),
Point::new(center.x() + point.y(), center.y() - point.x()),
Point::new(center.x() - point.y(), center.y() - point.x()),
];
self.inner.draw_lines(points.as_ref())
}
pub fn draw_circle<P>(&mut self, center: P, radius: i32) -> Result<(), String>
where
P: Into<Point>{
let center = center.into();
let mut current = Point::new(0, radius);
let mut d = 3 - 2 * radius;
self.draw_circle_points(center, current)?;
while current.y() >= current.x() {
current = current.offset(1, 0);
if d > 0 {
current = current.offset(0, -1);
d += 4 * (current.x() - current.y()) + 10;
} else {
d += 4 * current.x() + 6;
self.draw_circle_points(center, current)?;
}
}
Ok(())
}
pub fn fill_circle<P>(&mut self, center: P, radius: i32) -> Result<(), String>
where
P: Into<Point>{
let center = center.into();
let mut current = Point::new(0, radius);
let mut d = 3 - 2 * radius;
self.fill_circle_lines(center, current)?;
while current.y() >= current.x() {
current = current.offset(1, 0);
if d > 0 {
current = current.offset(0, -1);
d += 4 * (current.x() - current.y()) + 10;
} else {
d += 4 * current.x() + 6;
self.fill_circle_lines(center, current)?;
}
}
Ok(())
}
}
impl<T: RenderTarget, U> Deref for Canvas<T, U> {
type Target = SdlCanvas<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: RenderTarget, U> DerefMut for Canvas<T, U> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}