use crate::{Color, graphics::DrawCommand, vector::Vec2};
use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
use winit::dpi::{LogicalSize, PhysicalSize};
pub struct PixelsBackend {
pixels: Pixels,
logic_width: u32,
logic_height: u32,
}
impl PixelsBackend {
pub(crate) fn new(
window: &winit::window::Window,
window_size: PhysicalSize<u32>,
logic_size: LogicalSize<u32>,
) -> Self {
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window);
let pixels: Pixels =
PixelsBuilder::new(logic_size.width, logic_size.height, surface_texture)
.enable_vsync(true)
.build()
.expect("Error While Creating Pixels");
Self {
pixels,
logic_height: logic_size.height,
logic_width: logic_size.width,
}
}
fn draw_text(&mut self, pos: Vec2, text: &str, color: Color) {
let (x, y) = pos.as_u32_tuple();
let mut cursor_x = x;
let cursor_y = y;
for c in text.chars() {
let char_code = c as usize;
if char_code >= crate::text::FONT8X8_BASIC.len() {
cursor_x += 8;
continue;
}
let glyph = &crate::text::FONT8X8_BASIC[char_code];
for row in 0..8 {
let byte = glyph[row];
for col in 0..8 {
if (byte & (1 << col)) != 0 {
let px = cursor_x + col;
let py = cursor_y + row as u32;
if (px as u32) < self.logic_width
&& (py as u32) < self.logic_height
{
self.set_pixel(px as u32, py as u32, color);
}
}
}
}
cursor_x += 8;
}
}
fn draw_rect(&mut self, pos: Vec2, size: Vec2, color: Color) {
let (x, y) = pos.as_u32_tuple();
let (w, h) = size.as_u32_tuple();
let x1 = x;
let y1 = y;
let x2 = x.saturating_add(w);
let y2 = y.saturating_add(h);
let start_x = x1.min(self.logic_width);
let start_y = y1.min(self.logic_height);
let end_x = x2.min(self.logic_width);
let end_y = y2.min(self.logic_height);
if start_x >= end_x || start_y >= end_y {
return;
}
let frame = self.pixels.frame_mut();
let color_slice = [color.r, color.g, color.b, color.a];
for row in start_y..end_y {
let offset = (row * self.logic_width + start_x) as usize * 4;
let row_pixels = (end_x - start_x) as usize;
let target_row = &mut frame[offset..offset + (row_pixels * 4)];
for px in target_row.chunks_exact_mut(4) {
px.copy_from_slice(&color_slice);
}
}
}
fn draw_line(&mut self, p1: Vec2, p2: Vec2, color: Color) {
let mut x0 = p1.x;
let mut y0 = p1.y;
let x1 = p2.x;
let y1 = p2.y;
let dx = (x1 - x0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let dy = -(y1 - y0).abs();
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx + dy;
loop {
if x0 >= 0 && y0 >= 0 && (x0 as u32) < self.logic_width && (y0 as u32) < self.logic_height {
self.set_pixel(x0 as u32, y0 as u32, color);
}
if x0 == x1 && y0 == y1 { break; }
let e2 = 2 * err;
if e2 >= dy { err += dy; x0 += sx; }
if e2 <= dx { err += dx; y0 += sy; }
}
}
fn draw_circle(&mut self, center: Vec2, radius: i32, color: Color) {
let mut x = radius;
let mut y = 0;
let mut err = 0;
while x >= y {
let pts = [
(center.x + x, center.y + y),
(center.x + y, center.y + x),
(center.x - y, center.y + x),
(center.x - x, center.y + y),
(center.x - x, center.y - y),
(center.x - y, center.y - x),
(center.x + y, center.y - x),
(center.x + x, center.y - y),
];
for (px, py) in pts {
if px >= 0 && py >= 0 {
self.set_pixel(px as u32, py as u32, color);
}
}
y += 1;
if err <= 0 {
err += 2 * y + 1;
}
if err > 0 {
x -= 1;
err -= 2 * x + 1;
}
}
}
fn draw_triangle(&mut self, p1: Vec2, p2: Vec2, p3: Vec2, color: Color) {
self.draw_line(p1, p2, color);
self.draw_line(p2, p3, color);
self.draw_line(p3, p1, color);
}
fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
if x >= self.logic_width || y >= self.logic_height {
return;
}
let idx = ((y * self.logic_width + x) * 4) as usize;
let frame = self.pixels.frame_mut();
frame[idx..idx + 4].copy_from_slice(&[color.r, color.g, color.b, color.a]);
}
fn clear(&mut self, color: Color) {
let frame = self.pixels.frame_mut();
let color_slice = [color.r, color.g, color.b, color.a];
for px in frame.chunks_exact_mut(4) {
px.copy_from_slice(&color_slice);
}
}
pub fn resize_window(&mut self, size: PhysicalSize<u32>) {
if let Err(err) = self.pixels.resize_surface(size.width, size.height) {
eprintln!("Pixels resize_surface failed: {}", err);
}
}
pub fn render(&mut self, commands: &[DrawCommand]) {
for cmd in commands {
match cmd {
DrawCommand::Clear(color) => self.clear(*color),
DrawCommand::Pixel { pos, color } => {
let (x, y) = pos.as_u32_tuple();
self.set_pixel(x, y, *color);
}
DrawCommand::Circle { center, radius, color } => self.draw_circle(*center, *radius, *color),
DrawCommand::Rect { pos, size, color } => self.draw_rect(*pos, *size, *color),
DrawCommand::Text { pos, text, color } => self.draw_text(*pos, text, *color),
DrawCommand::Line { start, end, color } => self.draw_line(*start, *end, *color),
DrawCommand::Triangle { p1, p2, p3, color } => self.draw_triangle(*p1, *p2, *p3, *color),
}
}
if let Err(err) = self.pixels.render() {
eprintln!("Pixels render failed: {}", err);
}
}
}