pub mod draw;
mod font;
mod shape;
mod texture;
use ab_glyph::{Font, FontRef, Glyph, ScaleFont};
pub use draw::Draw;
pub use shape::Shape;
use wgpu::{util::DeviceExt, BindGroup, BindGroupLayout, Device, Queue};
use crate::{
draw::Color,
info::Info,
vertex::{vertex, Vertex},
};
const PI_2: f32 = std::f32::consts::PI * 2.0;
pub struct Builder<'a> {
device: &'a mut Device,
queue: &'a mut Queue,
texture_bind_group_layout: &'a BindGroupLayout,
font: &'a FontRef<'a>,
info: &'a Info<'a>,
}
impl<'a> Builder<'a> {
pub(crate) fn new(
device: &'a mut Device,
queue: &'a mut Queue,
texture_bind_group_layout: &'a BindGroupLayout,
font: &'a FontRef<'a>,
info: &'a Info,
) -> Self {
Self {
device,
queue,
texture_bind_group_layout,
font,
info,
}
}
pub fn ellipse(&mut self, pos: (f32, f32), radius: (f32, f32), res: u32, draw: Draw) -> Shape {
if res < 3 {
panic!("Failed to draw ellipse, Resolution can't be less then 3");
}
let (clr, texture) = match draw {
Draw::Color(clr) => (clr, None),
Draw::Texture { texture, color } => (color, Some(texture)),
};
let clr = [clr.r, clr.g, clr.b];
let mut vertices: Vec<Vertex> = vec![vertex([pos.0, pos.1, 0.0], clr, [0.0, 0.0])];
let mut indices: Vec<u16> = Vec::new();
for i in 0..res {
let fi = (i as f32 / res as f32) * PI_2;
let x = fi.cos() * radius.0 + pos.0;
let y = fi.sin() * radius.1 + pos.1;
vertices.push(vertex([x, y, 0.0], clr, [0.0, 0.0])); }
for i in 1..vertices.len() {
indices.push(i as u16);
indices.push(if i + 1 == vertices.len() {
1
} else {
(i + 1) as u16
});
indices.push(0);
}
self.raw(vertices, indices, texture)
}
pub fn rect(&mut self, pos0: (f32, f32), pos1: (f32, f32), draw: Draw) -> Shape {
let (clr, texture) = match draw {
Draw::Color(clr) => (clr, None),
Draw::Texture { texture, color } => (color, Some(texture)),
};
let clr = [clr.r, clr.g, clr.b];
self.raw(
vec![
vertex([pos0.0, pos0.1, 0.0], clr, [0.0, 1.0]),
vertex([pos1.0, pos0.1, 0.0], clr, [1.0, 1.0]),
vertex([pos1.0, pos1.1, 0.0], clr, [1.0, 0.0]),
vertex([pos0.0, pos1.1, 0.0], clr, [0.0, 0.0]),
],
vec![0, 1, 2, 2, 3, 0],
texture,
)
}
pub fn text(&mut self, pos: (f32, f32), scale: f32, text: String, clr: Color) -> Shape {
let scaled_font = self.font.as_scaled(scale);
let mut glyphs: Vec<Glyph> = Vec::new();
let (total_width, total_height) = font::handle(scaled_font, (0.0, 0.0), &text, &mut glyphs);
let mut texture_bytes: Vec<u8> = vec![0; (total_width * total_height * 4) as usize];
let clr = [
(clr.r * 255.0) as u8,
(clr.g * 255.0) as u8,
(clr.b * 255.0) as u8,
];
for glyph in glyphs {
if let Some(outlined) = scaled_font.outline_glyph(glyph) {
let bounds = outlined.px_bounds();
outlined.draw(|x, y, c| {
let x = x + bounds.min.x as u32;
let y = y + bounds.min.y as u32;
let ind = (x + y * total_width) as usize * 4;
texture_bytes[ind] = clr[0];
texture_bytes[ind + 1] = clr[1];
texture_bytes[ind + 2] = clr[2];
texture_bytes[ind + 3] = (c * 255.0) as u8;
});
}
}
let texture = self.create_texture(texture_bytes, (total_width, total_height));
let size = self
.info
.from_pixel((total_width as f32, total_height as f32));
self.rect(
pos,
(pos.0 + size.0, pos.1 + size.1),
Draw::Texture {
texture,
color: crate::draw::color::WHITE,
},
)
}
pub fn raw(
&mut self,
vertices: Vec<Vertex>,
indices: Vec<u16>,
texture: Option<BindGroup>,
) -> Shape {
let vertex_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::INDEX,
});
Shape {
vertex_buffer,
index_buffer,
index_count: indices.len() as u32,
bindgroup: texture,
}
}
pub fn create_texture(&mut self, bytes: Vec<u8>, size: (u32, u32)) -> BindGroup {
texture::create(
self.device,
self.queue,
self.texture_bind_group_layout,
bytes,
size,
)
}
}