mod glyphs;
pub use crate::glyphs::*;
use std::{error, fmt, ops, path::Path};
use bit_vec::BitVec;
use graphics::{draw_state::DrawState, math::Matrix2d, types::Color, Graphics, ImageSize};
use image::{DynamicImage, GenericImageView, ImageResult, Rgba, RgbaImage};
#[cfg(feature = "piston_window_texture")]
use piston_window::{
texture::{CreateTexture, Format},
G2dTexture, GfxFactory, TextureSettings,
};
use rayon::prelude::*;
pub const IDENTITY: Matrix2d = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
#[derive(Debug, Clone)]
pub enum Error {
ContainerTooSmall(usize, usize),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::ContainerTooSmall(len, area) => write!(
f,
"Container is too small for the given dimensions. \
\nContainer has {} bytes, which encode {} pixels, \
\nbut the given demensions contain {} pixels",
len,
len / 4,
area
),
}
}
}
impl error::Error for Error {}
#[derive(Debug, Clone)]
pub struct RenderBuffer {
inner: RgbaImage,
used: Vec<BitVec>,
}
impl RenderBuffer {
pub fn new(width: u32, height: u32) -> RenderBuffer {
RenderBuffer {
inner: RgbaImage::new(width, height),
used: vec![BitVec::from_elem(height as usize, false); width as usize],
}
}
pub fn open<P: AsRef<Path>>(path: P) -> ImageResult<RenderBuffer> {
image::open(path).map(RenderBuffer::from)
}
pub fn decode_from_bytes(bytes: &[u8]) -> ImageResult<RenderBuffer> {
image::load_from_memory(bytes).map(RenderBuffer::from)
}
pub fn clear(&mut self, color: [f32; 4]) {
self.clear_color(color);
}
pub fn pixel(&self, x: u32, y: u32) -> [f32; 4] {
color_rgba_f32(*self.inner.get_pixel(x, y))
}
pub fn set_pixel(&mut self, x: u32, y: u32, color: [f32; 4]) {
self.inner.put_pixel(x, y, color_f32_rgba(&color));
}
fn reset_used(&mut self) {
let (width, height) = self.inner.dimensions();
self.used = vec![BitVec::from_elem(height as usize, false); width as usize];
}
#[cfg(feature = "piston_window_texture")]
pub fn to_g2d_texture(
&self,
factory: &mut GfxFactory,
settings: &TextureSettings,
) -> Result<G2dTexture, Box<error::Error>> {
Ok(G2dTexture::from_image(factory, &self.inner, settings)?)
}
}
#[cfg(feature = "piston_window_texture")]
impl CreateTexture<()> for RenderBuffer {
type Error = Box<error::Error>;
fn create<S: Into<[u32; 2]>>(
_factory: &mut (),
_format: Format,
memory: &[u8],
size: S,
_settings: &TextureSettings,
) -> Result<Self, Box<error::Error>> {
let size = size.into();
Ok(RenderBuffer::from(
RgbaImage::from_raw(size[0], size[1], memory.to_vec()).ok_or(
Error::ContainerTooSmall(memory.len(), (size[0] * size[1]) as usize),
)?,
))
}
}
impl From<RgbaImage> for RenderBuffer {
fn from(image: RgbaImage) -> Self {
let (width, height) = image.dimensions();
RenderBuffer {
inner: image,
used: vec![BitVec::from_elem(height as usize, false); width as usize],
}
}
}
impl From<DynamicImage> for RenderBuffer {
fn from(image: DynamicImage) -> Self {
let (width, height) = image.dimensions();
RenderBuffer {
inner: image.to_rgba(),
used: vec![BitVec::from_elem(height as usize, false); width as usize],
}
}
}
impl ops::Deref for RenderBuffer {
type Target = RgbaImage;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl ImageSize for RenderBuffer {
fn get_size(&self) -> (u32, u32) {
self.inner.dimensions()
}
}
impl Graphics for RenderBuffer {
type Texture = RenderBuffer;
fn clear_color(&mut self, color: Color) {
for (_, _, pixel) in self.inner.enumerate_pixels_mut() {
*pixel = color_f32_rgba(&color);
}
}
fn clear_stencil(&mut self, _value: u8) {}
fn tri_list<F>(&mut self, _draw_state: &DrawState, color: &[f32; 4], mut f: F)
where
F: FnMut(&mut dyn FnMut(&[[f32; 2]])),
{
self.reset_used();
f(&mut |vertices| {
for tri in vertices.chunks(3) {
let mut tl = [0f32, 0f32];
let mut br = [0f32, 0f32];
for v in tri {
tl[0] = tl[0].min(v[0]);
tl[1] = tl[1].min(v[1]);
br[0] = br[0].max(v[0]);
br[1] = br[1].max(v[1]);
}
let tl = [tl[0].floor().max(0.0) as i32, tl[1].floor().max(0.0) as i32];
let br = [
br[0].ceil().min(self.width() as f32) as i32,
br[1].ceil().min(self.height() as f32) as i32,
];
let inner = &self.inner;
let used = &self.used;
(tl[0]..br[0]).into_par_iter().for_each(|x| {
let mut entered = false;
for y in tl[1]..br[1] {
if triangle_contains(tri, [x as f32, y as f32]) {
entered = true;
if !used[x as usize].get(y as usize).unwrap_or(true) {
let under_color =
color_rgba_f32(*inner.get_pixel(x as u32, y as u32));
let layered_color = layer_color(&color, &under_color);
unsafe {
(inner as *const RgbaImage as *mut RgbaImage)
.as_mut()
.unwrap()
.put_pixel(
x as u32,
y as u32,
color_f32_rgba(&layered_color),
);
(used as *const Vec<BitVec> as *mut Vec<BitVec>)
.as_mut()
.unwrap()[x as usize]
.set(y as usize, true);
}
}
} else if entered {
break;
}
}
});
}
});
}
fn tri_list_uv<F>(
&mut self,
_draw_state: &DrawState,
color: &[f32; 4],
texture: &Self::Texture,
mut f: F,
) where
F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])),
{
println!("tri_list_uv");
self.reset_used();
f(&mut |vertices, tex_vertices| {
for (tri, tex_tri) in vertices.chunks(3).zip(tex_vertices.chunks(3)) {
println!("\ttri: {:?}", tri);
let mut tl = [0f32, 0f32];
let mut br = [0f32, 0f32];
for v in tri {
tl[0] = tl[0].min(v[0]);
tl[1] = tl[1].min(v[1]);
br[0] = br[0].max(v[0]);
br[1] = br[1].max(v[1]);
}
let tl = [tl[0].floor().max(0.0) as i32, tl[1].floor().max(0.0) as i32];
let br = [
br[0].ceil().min((self.width() - 1) as f32) as i32,
br[1].ceil().min((self.height() - 1) as f32) as i32,
];
let avg_y = ((tri[0][1] + tri[1][1] + tri[2][1]) / 3.0) as i32;
let vert_center = (br[1] - tl[1]) / 2;
let vertical_balance_top = avg_y < vert_center;
let scaled_tex_tri = tri_image_scale(tex_tri, texture.get_size());
let inner = &self.inner;
let used = &self.used;
(tl[0]..br[0]).into_par_iter().for_each(|x| {
let mut entered = false;
let range: Box<Iterator<Item = i32>> = if vertical_balance_top {
Box::new(tl[1]..br[1])
} else {
Box::new((tl[1]..br[1]).rev())
};
for y in range {
if triangle_contains(tri, [x as f32, y as f32]) {
entered = true;
let mapped_point =
map_to_triangle([x as f32, y as f32], tri, &scaled_tex_tri);
let texel = color_rgba_f32(*texture.get_pixel(
(mapped_point[0].round() as u32).min(texture.width() - 1),
(mapped_point[1].round() as u32).min(texture.height() - 1),
));
let over_color = color_mul(color, &texel);
let under_color = color_rgba_f32(*inner.get_pixel(x as u32, y as u32));
let layered_color = layer_color(&over_color, &under_color);
unsafe {
(inner as *const RgbaImage as *mut RgbaImage)
.as_mut()
.unwrap()
.put_pixel(x as u32, y as u32, color_f32_rgba(&layered_color));
(used as *const Vec<BitVec> as *mut Vec<BitVec>)
.as_mut()
.unwrap()[x as usize]
.set(y as usize, true);
}
} else if entered {
break;
}
}
});
}
});
}
}
fn color_f32_rgba(color: &[f32; 4]) -> Rgba<u8> {
Rgba {
data: [
(color[0] * 255.0) as u8,
(color[1] * 255.0) as u8,
(color[2] * 255.0) as u8,
(color[3] * 255.0) as u8,
],
}
}
fn color_rgba_f32(color: Rgba<u8>) -> [f32; 4] {
[
f32::from(color.data[0]) / 255.0,
f32::from(color.data[1]) / 255.0,
f32::from(color.data[2]) / 255.0,
f32::from(color.data[3]) / 255.0,
]
}
fn color_mul(a: &[f32; 4], b: &[f32; 4]) -> [f32; 4] {
[a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]
}
fn layer_color(over: &[f32; 4], under: &[f32; 4]) -> [f32; 4] {
let over_weight = 1.0 - (1.0 - over[3]).powf(2.0);
let under_weight = 1.0 - over_weight;
[
over_weight * over[0] + under_weight * under[0],
over_weight * over[1] + under_weight * under[1],
over_weight * over[2] + under_weight * under[2],
(over[3].powf(2.0) + under[3].powf(2.0)).sqrt().min(1.0),
]
}
fn sign(p1: [f32; 2], p2: [f32; 2], p3: [f32; 2]) -> f32 {
(p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
}
fn triangle_contains(tri: &[[f32; 2]], point: [f32; 2]) -> bool {
let b1 = sign(point, tri[0], tri[1]) < 0.0;
let b2 = sign(point, tri[1], tri[2]) < 0.0;
let b3 = sign(point, tri[2], tri[0]) < 0.0;
b1 == b2 && b2 == b3
}
fn map_to_triangle(point: [f32; 2], from_tri: &[[f32; 2]], to_tri: &[[f32; 2]]) -> [f32; 2] {
let t = from_tri;
let p = point;
let a = t[1][1] - t[2][1];
let b = p[0] - t[2][0];
let c = t[2][0] - t[1][0];
let d = p[1] - t[2][1];
let e = t[0][0] - t[2][0];
let f = t[0][1] - t[2][1];
let g = t[2][1] - t[0][1];
let ae_cf = a * e + c * f;
let bary_a = (a * b + c * d) / ae_cf;
let bary_b = (g * b + e * d) / ae_cf;
let bary_c = 1.0 - bary_a - bary_b;
[
bary_a * to_tri[0][0] + bary_b * to_tri[1][0] + bary_c * to_tri[2][0],
bary_a * to_tri[0][1] + bary_b * to_tri[1][1] + bary_c * to_tri[2][1],
]
}
fn point_image_scale(point: [f32; 2], size: (u32, u32)) -> [f32; 2] {
[point[0] * size.0 as f32, point[1] * size.1 as f32]
}
fn tri_image_scale(tri: &[[f32; 2]], size: (u32, u32)) -> [[f32; 2]; 3] {
[
point_image_scale(tri[0], size),
point_image_scale(tri[1], size),
point_image_scale(tri[2], size),
]
}