use crate::texture::Texture;
use crate::{Arc, Ellipse, ImageTexture, Pixels};
use euclid::{Length, Point2D, Rect, Size2D};
use qwac_sys::graphics;
pub use qwac_sys::graphics::CompositeOperation;
pub trait Canvas {
fn id(&self) -> i32;
fn draw_texture<T: Texture>(
&mut self,
texture: &T,
source: Rect<f32, Pixels>,
destination: Rect<f32, Pixels>,
) {
unsafe {
graphics::canvas_draw_texture(
self.id(),
texture.id(),
source.origin.x,
source.origin.y,
source.size.width,
source.size.height,
destination.origin.x,
destination.origin.y,
destination.size.width,
destination.size.height,
);
}
}
fn draw_canvas<C: Canvas>(
&mut self,
canvas: &C,
source: Rect<f32, Pixels>,
destination: Rect<f32, Pixels>,
) {
unsafe {
graphics::canvas_draw_canvas(
self.id(),
canvas.id(),
source.origin.x,
source.origin.y,
source.size.width,
source.size.height,
destination.origin.x,
destination.origin.y,
destination.size.width,
destination.size.height,
);
}
}
fn resize(&mut self, size: Size2D<u16, Pixels>) {
unsafe {
graphics::canvas_resize(self.id(), size.width.into(), size.height.into());
}
}
fn clear(&mut self) {
unsafe {
graphics::canvas_clear(self.id());
}
}
fn set_global_alpha(&mut self, alpha: f32) {
unsafe {
graphics::canvas_set_global_alpha(self.id(), alpha);
}
}
fn set_global_composite_operation(&mut self, operation: CompositeOperation) {
unsafe {
graphics::canvas_set_global_composite_operation(self.id(), operation);
}
}
fn size(&self) -> Size2D<u16, Pixels> {
let width = unsafe { graphics::canvas_width(self.id()) }
.try_into()
.expect("out of range");
let height = unsafe { graphics::canvas_height(self.id()) }
.try_into()
.expect("out of range");
Size2D::new(width, height)
}
fn set_fill_style<S>(&mut self, style: S)
where
S: AsRef<str>,
{
let style = style.as_ref();
let style_ptr = style.as_ptr();
let style_len = style.len().try_into().expect("out of range");
unsafe {
graphics::canvas_set_fill_style(self.id(), style_ptr, style_len);
}
}
fn set_stroke_style<S>(&mut self, style: S)
where
S: AsRef<str>,
{
let style = style.as_ref();
let style_ptr = style.as_ptr();
let style_len = style.len().try_into().expect("out of range");
unsafe {
graphics::canvas_set_stroke_style(self.id(), style_ptr, style_len);
}
}
fn set_font<S>(&mut self, font: S)
where
S: AsRef<str>,
{
let font = font.as_ref();
let font_ptr = font.as_ptr();
let font_len = font.len().try_into().expect("out of range");
unsafe {
graphics::canvas_set_font(self.id(), font_ptr, font_len);
}
}
fn set_line_width(&mut self, line_width: Length<f32, Pixels>) {
unsafe {
graphics::canvas_set_line_width(self.id(), line_width.get());
}
}
fn fill_text<S>(
&mut self,
text: S,
position: Point2D<f32, Pixels>,
max_width: Option<Length<f32, Pixels>>,
) where
S: AsRef<str>,
{
let text = text.as_ref();
let text_ptr = text.as_ptr();
let text_len = text.len().try_into().expect("out of range");
unsafe {
graphics::canvas_fill_text(
self.id(),
text_ptr,
text_len,
position.x,
position.y,
max_width.unwrap_or_default().get(),
);
}
}
fn stroke_text<S>(
&mut self,
text: S,
position: Point2D<f32, Pixels>,
max_width: Option<Length<f32, Pixels>>,
) where
S: AsRef<str>,
{
let text = text.as_ref();
let text_ptr = text.as_ptr();
let text_len = text.len().try_into().expect("out of range");
unsafe {
graphics::canvas_stroke_text(
self.id(),
text_ptr,
text_len,
position.x,
position.y,
max_width.unwrap_or_default().get(),
);
}
}
fn fill_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
unsafe {
graphics::canvas_fill_rectangle(
self.id(),
rectangle.origin.x,
rectangle.origin.y,
rectangle.size.width,
rectangle.size.height,
);
}
}
fn stroke_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
unsafe {
graphics::canvas_stroke_rectangle(
self.id(),
rectangle.origin.x,
rectangle.origin.y,
rectangle.size.width,
rectangle.size.height,
);
}
}
fn clear_rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
unsafe {
graphics::canvas_clear_rectangle(
self.id(),
rectangle.origin.x,
rectangle.origin.y,
rectangle.size.width,
rectangle.size.height,
);
}
}
}
impl<D: Canvas> Canvas for &mut D {
fn id(&self) -> i32 {
(**self).id()
}
}
impl<D: Canvas> Canvas for &D {
fn id(&self) -> i32 {
(**self).id()
}
}
#[derive(Debug)]
pub struct OffscreenCanvas {
pub(crate) id: i32,
}
impl OffscreenCanvas {
pub fn new() -> Self {
unsafe {
Self {
id: graphics::canvas_create(),
}
}
}
pub fn from_canvas<C>(canvas: &C) -> Self
where
C: Canvas,
{
let mut new = OffscreenCanvas::new();
let size = canvas.size();
let rect = Rect::new(
Point2D::zero(),
Size2D::new(
size.width.try_into().expect("out of range"),
size.height.try_into().expect("out of range"),
),
);
new.resize(size);
new.draw_canvas(canvas, rect, rect);
new
}
pub fn from_texture<T>(texture: &T) -> Self
where
T: Texture,
{
let mut new = OffscreenCanvas::new();
let size = texture.size();
let rect = Rect::new(
Point2D::zero(),
Size2D::new(
size.width.try_into().expect("out of range"),
size.height.try_into().expect("out of range"),
),
);
new.resize(size);
new.draw_texture(texture, rect, rect);
new
}
}
impl Canvas for OffscreenCanvas {
fn id(&self) -> i32 {
self.id
}
}
impl Drop for OffscreenCanvas {
fn drop(&mut self) {
unsafe {
graphics::canvas_drop(self.id);
}
}
}
impl Clone for OffscreenCanvas {
fn clone(&self) -> Self {
Self::from_canvas(&self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Screen;
impl Canvas for Screen {
fn id(&self) -> i32 {
0
}
}
impl From<&Screen> for OffscreenCanvas {
fn from(value: &Screen) -> Self {
Self::from_canvas(value)
}
}
impl From<Screen> for OffscreenCanvas {
fn from(value: Screen) -> Self {
Self::from_canvas(&value)
}
}
impl From<&ImageTexture> for OffscreenCanvas {
fn from(value: &ImageTexture) -> Self {
Self::from_texture(value)
}
}
impl From<ImageTexture> for OffscreenCanvas {
fn from(value: ImageTexture) -> Self {
Self::from_texture(&value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum PathType {
Fill,
Stroke,
}
#[derive(Debug)]
pub struct Path<'a, C>
where
C: Canvas + std::fmt::Debug,
{
canvas: &'a mut C,
path_type: PathType,
}
impl<'a, C> Path<'a, C>
where
C: Canvas + std::fmt::Debug,
{
fn new(canvas: &'a mut C, path_type: PathType) -> Self {
unsafe {
graphics::canvas_path_begin(canvas.id());
}
Self { canvas, path_type }
}
pub fn fill(canvas: &'a mut C) -> Self {
Self::new(canvas, PathType::Fill)
}
pub fn stroke(canvas: &'a mut C) -> Self {
Self::new(canvas, PathType::Stroke)
}
pub fn close(&mut self) {
unsafe {
graphics::canvas_path_close(self.canvas.id());
}
}
pub fn move_to(&mut self, point: Point2D<f32, Pixels>) {
unsafe {
graphics::canvas_path_move_to(self.canvas.id(), point.x, point.y);
}
}
pub fn line_to(&mut self, point: Point2D<f32, Pixels>) {
unsafe {
graphics::canvas_path_line_to(self.canvas.id(), point.x, point.y);
}
}
pub fn rectangle(&mut self, rectangle: Rect<f32, Pixels>) {
unsafe {
graphics::canvas_path_rectangle(
self.canvas.id(),
rectangle.origin.x,
rectangle.origin.y,
rectangle.size.width,
rectangle.size.height,
);
}
}
pub fn ellipse(&mut self, ellipse: Ellipse) {
unsafe {
graphics::canvas_path_ellipse(
self.canvas.id(),
ellipse.center.x,
ellipse.center.y,
ellipse.radii.0.get(),
ellipse.radii.1.get(),
ellipse.rotation.radians,
ellipse.range.start().radians,
ellipse.range.end().radians,
ellipse.counter_clockwise.into(),
);
}
}
pub fn arc(&mut self, arc: Arc) {
unsafe {
graphics::canvas_path_arc(
self.canvas.id(),
arc.center.x,
arc.center.y,
arc.radius.get(),
arc.range.start().radians,
arc.range.end().radians,
arc.counter_clockwise.into(),
);
}
}
pub fn arc_to(
&mut self,
from: Point2D<f32, Pixels>,
to: Point2D<f32, Pixels>,
radius: Length<f32, Pixels>,
) {
unsafe {
graphics::canvas_path_arc_to(
self.canvas.id(),
from.x,
from.y,
to.x,
to.y,
radius.get(),
);
}
}
}
impl<'a, C> Canvas for Path<'a, C>
where
C: Canvas + std::fmt::Debug,
{
fn id(&self) -> i32 {
self.canvas.id()
}
}
impl<'a, C> Drop for Path<'a, C>
where
C: Canvas + std::fmt::Debug,
{
fn drop(&mut self) {
unsafe {
match self.path_type {
PathType::Fill => graphics::canvas_path_fill(self.canvas.id()),
PathType::Stroke => graphics::canvas_path_stroke(self.canvas.id()),
}
}
}
}
#[derive(Debug)]
pub struct CanvasState<'a, C>
where
C: Canvas + std::fmt::Debug,
{
canvas: &'a mut C,
}
impl<'a, C> CanvasState<'a, C>
where
C: Canvas + std::fmt::Debug,
{
pub fn new(canvas: &'a mut C) -> Self {
unsafe {
graphics::canvas_save(canvas.id());
}
Self { canvas }
}
}
impl<'a, C> Canvas for CanvasState<'a, C>
where
C: Canvas + std::fmt::Debug,
{
fn id(&self) -> i32 {
self.canvas.id()
}
}
impl<'a, C> Drop for CanvasState<'a, C>
where
C: Canvas + std::fmt::Debug,
{
fn drop(&mut self) {
unsafe {
graphics::canvas_restore(self.canvas.id());
}
}
}