use crate::{
graphics::{BlendMode, DrawParam, Drawable, FilterMode, Image, Rect},
Context, GameResult,
};
use crate::graphics::drawparam::Transform;
use miniquad::{RenderPass, Texture, TextureFormat, TextureParams};
#[derive(Clone, Debug)]
pub struct Canvas {
image: Image,
pub(crate) offscreen_pass: RenderPass,
}
impl Canvas {
pub fn new(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
width: u16,
height: u16,
) -> GameResult<Canvas> {
let texture = Texture::new_render_texture(
quad_ctx,
TextureParams {
width: width as u32,
height: height as u32,
format: TextureFormat::RGBA8,
..Default::default()
},
);
let image = Image::from_texture(quad_ctx, texture, ctx.gfx_context.default_filter)?;
let offscreen_pass = RenderPass::new(quad_ctx, texture, None);
Ok(Canvas {
image,
offscreen_pass,
})
}
pub fn with_window_size(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
) -> GameResult<Canvas> {
use crate::graphics;
let (w, h) = graphics::drawable_size(quad_ctx);
Canvas::new(ctx, quad_ctx, w as u16, h as u16)
}
pub fn image(&self) -> &Image {
&self.image
}
pub fn width(&self) -> u16 {
self.image.width
}
pub fn height(&self) -> u16 {
self.image.height
}
pub fn dimensions(&self) -> Rect {
Rect::new(0.0, 0.0, f32::from(self.width()), f32::from(self.height()))
}
pub fn filter(&self) -> FilterMode {
self.image.filter()
}
pub fn set_filter(&mut self, mode: FilterMode) {
self.image.set_filter(mode)
}
pub fn into_inner(self) -> Image {
self.image
}
}
impl Drawable for Canvas {
fn draw(
&self,
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
param: DrawParam,
) -> GameResult {
let scale_x = param.src.w * f32::from(self.width());
let scale_y = param.src.h * f32::from(self.height());
let scaled_param = match param.trans {
Transform::Values { scale, .. } => {
let s_param = param.scale(mint::Vector2 {
x: scale.x * scale_x,
y: scale.y * scale_y,
});
s_param.transform(s_param.trans.to_bare_matrix())
}
Transform::Matrix(m) => param.transform(
cgmath::Matrix4::from(m)
* cgmath::Matrix4::from_nonuniform_scale(scale_x, scale_y, 1.0),
),
};
let new_param = flip_draw_param_vertical(scaled_param);
self.image.draw_image_raw(ctx, quad_ctx, new_param)
}
fn set_blend_mode(&mut self, blend_mode: Option<BlendMode>) {
self.image.set_blend_mode(blend_mode);
}
fn blend_mode(&self) -> Option<BlendMode> {
self.image.blend_mode()
}
fn dimensions(&self, _: &mut Context) -> Option<Rect> {
Some(self.image.dimensions())
}
}
#[rustfmt::skip]
const FLIP_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
1.0, 0.0, 0.0, 0.0,
0.0, -1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 1.0,
);
fn flip_draw_param_vertical(param: DrawParam) -> DrawParam {
let param = if let Transform::Matrix(mat) = param.trans {
param.transform(cgmath::Matrix4::from(mat) * FLIP_MATRIX)
} else {
panic!("Can not be called with a non-matrix DrawParam");
};
let new_src = Rect {
x: param.src.x,
y: (1.0 - param.src.h) - param.src.y,
w: param.src.w,
h: param.src.h,
};
param.src(new_src)
}
pub fn set_canvas(ctx: &mut Context, target: Option<&Canvas>) {
ctx.gfx_context.canvas = target.cloned();
}