use color::{color_blend, color_dist, Color};
use console::*;
use file::FileLoader;
use image;
pub struct Image {
file_loader: FileLoader,
img: Option<image::RgbaImage>,
}
impl Image {
pub fn new(file_path: &str) -> Self {
let mut file_loader = FileLoader::new();
file_loader.load_file(file_path).ok();
Self {
file_loader,
img: None,
}
}
pub fn is_loaded(&mut self) -> bool {
if self.img.is_some() {
return true;
}
if self.file_loader.is_file_ready(0) {
let buf = self.file_loader.get_file_content(0);
self.intialize_image(&buf);
return true;
}
return false;
}
fn intialize_image(&mut self, buf: &Vec<u8>) {
self.img = Some(image::load_from_memory(&buf).unwrap().to_rgba());
}
pub fn get_size(&mut self) -> Option<(u32, u32)> {
if self.is_loaded() {
if let Some(ref img) = self.img {
return Some((img.width(), img.height()));
}
}
return None;
}
pub fn blit(&mut self, con: &mut Console, x: i32, y: i32, transparent: Option<Color>) {
if !self.is_loaded() {
return;
}
if let Some(ref img) = self.img {
let width = img.width() as i32;
let height = img.height() as i32;
let minx = x.max(0);
let miny = y.max(0);
let maxx = (x + width).min(con.get_width() as i32);
let maxy = (y + height).min(con.get_height() as i32);
let mut offx = if x < 0 { -x } else { 0 };
let mut offy = if y < 0 { -y } else { 0 };
let con_width = con.get_pot_width();
let back = con.borrow_mut_background();
for cx in minx..maxx {
for cy in miny..maxy {
let pixel = img
.get_pixel((cx - minx + offx) as u32, (cy - miny + offy) as u32)
.data;
let color = (pixel[0], pixel[1], pixel[2], pixel[3]);
if let Some(ref t) = transparent {
if color == *t {
continue;
}
}
let offset = (cx as u32 + cy as u32 * con_width) as usize;
back[offset] = color;
}
}
}
}
pub fn blit_ex(
&mut self,
con: &mut Console,
x: f32,
y: f32,
scalex: f32,
scaley: f32,
angle: f32,
transparent: Option<Color>,
) {
if !self.is_loaded() || scalex == 0.0 || scaley == 0.0 {
return;
}
let size = self.get_size().unwrap();
let rx = x - size.0 as f32 * 0.5;
let ry = y - size.1 as f32 * 0.5;
if scalex == 1.0 && scaley == 1.0 && angle == 0.0 && rx.floor() == rx && ry.floor() == ry {
let ix = rx as i32;
let iy = ry as i32;
self.blit(con, ix, iy, transparent);
return;
}
let iw = (size.0 / 2) as f32 * scalex;
let ih = (size.1 / 2) as f32 * scaley;
let newx_x = angle.cos();
let newx_y = -angle.sin();
let newy_x = newx_y;
let newy_y = -newx_x;
let x0 = x - iw * newx_x + ih * newy_x;
let y0 = y - iw * newx_y + ih * newy_y;
let x1 = x + iw * newx_x + ih * newy_x;
let y1 = y + iw * newx_y + ih * newy_y;
let x2 = x + iw * newx_x - ih * newy_x;
let y2 = y + iw * newx_y - ih * newy_y;
let x3 = x - iw * newx_x - ih * newy_x;
let y3 = y - iw * newx_y - ih * newy_y;
let rx = x0.min(x1).min(x2).min(x3) as i32;
let ry = y0.min(y1).min(y2).min(y3) as i32;
let rw = x0.max(x1).max(x2).max(x3) as i32 - rx;
let rh = y0.max(y1).max(y2).max(y3) as i32 - ry;
let minx = rx.max(0);
let miny = ry.max(0);
let maxx = (rx + rw).min(con.get_width() as i32);
let maxy = (ry + rh).min(con.get_height() as i32);
let invscalex = 1.0 / scalex;
let invscaley = 1.0 / scaley;
let con_width = con.get_pot_width();
let back = con.borrow_mut_background();
if let Some(ref img) = self.img {
for cx in minx..maxx {
for cy in miny..maxy {
let ix =
(iw + (cx as f32 - x) * newx_x + (cy as f32 - y) * (-newy_x)) * invscalex;
let iy =
(ih + (cx as f32 - x) * (newx_y) - (cy as f32 - y) * newy_y) * invscaley;
let color = if ix as i32 >= size.0 as i32
|| ix < 0.0
|| iy as i32 >= size.1 as i32
|| iy < 0.0
{
(0, 0, 0, 255)
} else {
let pixel = img.get_pixel(ix as u32, iy as u32).data;
(pixel[0], pixel[1], pixel[2], pixel[3])
};
if let Some(ref t) = transparent {
if color == *t {
continue;
}
}
let offset = (cx as u32 + cy as u32 * con_width) as usize;
if scalex < 1.0 || scaley < 1.0 {
}
back[offset] = color;
}
}
}
}
pub fn blit_2x(
&mut self,
con: &mut Console,
dx: i32,
dy: i32,
sx: i32,
sy: i32,
w: Option<i32>,
h: Option<i32>,
transparent: Option<Color>,
) {
if !self.is_loaded() {
return;
}
if let Some(ref img) = self.img {
Image::blit_2x_image(img, con, dx, dy, sx, sy, w, h, transparent);
}
}
pub fn blit_2x_image(
img: &image::RgbaImage,
con: &mut Console,
dx: i32,
dy: i32,
sx: i32,
sy: i32,
w: Option<i32>,
h: Option<i32>,
transparent: Option<Color>,
) {
let mut grid: [Color; 4] = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)];
let mut back: Color = (0, 0, 0, 0);
let mut front: Option<Color> = None;
let mut ascii: i32 = ' ' as i32;
let width = img.width() as i32;
let height = img.height() as i32;
let con_width = con.get_width() as i32;
let con_height = con.get_height() as i32;
let mut blit_w = w.unwrap_or(width);
let mut blit_h = h.unwrap_or(height);
let minx = sx.max(0);
let miny = sy.max(0);
blit_w = blit_w.min(width - minx);
blit_h = blit_h.min(height - miny);
let mut maxx = if dx + blit_w / 2 <= con_width {
blit_w
} else {
(con_width - dx) * 2
};
let mut maxy = if dy + blit_h / 2 <= con_height {
blit_h
} else {
(con_height - dy) * 2
};
maxx += minx;
maxy += miny;
let mut cx = minx;
while cx < maxx {
let mut cy = miny;
while cy < maxy {
let conx = dx + (cx - minx) / 2;
let cony = dy + (cy - miny) / 2;
let console_back = con.unsafe_get_back(conx, cony);
let pixel = img.get_pixel(cx as u32, cy as u32).data;
grid[0] = (pixel[0], pixel[1], pixel[2], pixel[3]);
if let Some(ref t) = transparent {
if grid[0] == *t {
grid[0] = console_back;
}
}
if cx < maxx - 1 {
let pixel = img.get_pixel(cx as u32 + 1, cy as u32).data;
grid[1] = (pixel[0], pixel[1], pixel[2], pixel[3]);
if let Some(ref t) = transparent {
if grid[1] == *t {
grid[1] = console_back;
}
}
} else {
grid[1] = console_back;
}
if cy < maxy - 1 {
let pixel = img.get_pixel(cx as u32, cy as u32 + 1).data;
grid[2] = (pixel[0], pixel[1], pixel[2], pixel[3]);
if let Some(ref t) = transparent {
if grid[2] == *t {
grid[2] = console_back;
}
}
} else {
grid[2] = console_back;
}
if cx < maxx - 1 && cy < maxy - 1 {
let pixel = img.get_pixel(cx as u32 + 1, cy as u32 + 1).data;
grid[3] = (pixel[0], pixel[1], pixel[2], pixel[3]);
if let Some(ref t) = transparent {
if grid[3] == *t {
grid[3] = console_back;
}
}
} else {
grid[3] = console_back;
}
compute_pattern(&grid, &mut back, &mut front, &mut ascii);
if front.is_none() {
con.unsafe_back(conx, cony, back);
con.unsafe_ascii(conx, cony, ascii as u16);
} else {
if ascii >= 0 {
con.unsafe_back(conx, cony, back);
con.unsafe_fore(conx, cony, front.unwrap());
con.unsafe_ascii(conx, cony, ascii as u16);
} else {
con.unsafe_back(conx, cony, front.unwrap());
con.unsafe_fore(conx, cony, back);
con.unsafe_ascii(conx, cony, (-ascii) as u16);
}
}
cy += 2;
}
cx += 2;
}
}
}
const FLAG_TO_ASCII: [i32; 8] = [
0,
CHAR_SUBP_NE as i32,
CHAR_SUBP_SW as i32,
-(CHAR_SUBP_DIAG as i32),
CHAR_SUBP_SE as i32,
CHAR_SUBP_E as i32,
-(CHAR_SUBP_N as i32),
-(CHAR_SUBP_NW as i32),
];
fn compute_pattern(
desired: &[Color; 4],
back: &mut Color,
front: &mut Option<Color>,
ascii: &mut i32,
) {
let mut flag = 0;
let mut weight: [f32; 2] = [0.0, 0.0];
*back = desired[0];
let mut i = 1;
while i < 4 {
if desired[i].0 != back.0 || desired[i].1 != back.1 || desired[i].2 != back.2 {
break;
}
i += 1;
}
if i == 4 {
*front = None;
*ascii = ' ' as i32;
return;
}
weight[0] = i as f32;
let mut tmp_front = desired[i];
weight[1] = 1.0;
flag |= 1 << (i - 1);
i += 1;
while i < 4 {
if desired[i].0 == back.0 && desired[i].1 == back.1 && desired[i].2 == back.2 {
weight[0] += 1.0;
} else if desired[i].0 == tmp_front.0
&& desired[i].1 == tmp_front.1
&& desired[i].2 == tmp_front.2
{
flag |= 1 << (i - 1);
weight[1] += 1.0;
} else {
let dist0i = color_dist(&desired[i], back);
let dist1i = color_dist(&desired[i], &tmp_front);
let dist01 = color_dist(back, &tmp_front);
if dist0i < dist1i {
if dist0i <= dist01 {
*back = color_blend(&desired[i], back, weight[0] / (1.0 + weight[0]));
weight[0] += 1.0;
} else {
*back = color_blend(back, &tmp_front, weight[1] / (weight[0] + weight[1]));
weight[0] += 1.0;
tmp_front = desired[i];
flag = 1 << (i - 1);
}
} else {
if dist1i <= dist01 {
tmp_front = color_blend(&desired[i], &tmp_front, weight[1] / (1.0 + weight[1]));
weight[1] += 1.0;
flag |= 1 << (i - 1);
} else {
*back = color_blend(back, &tmp_front, weight[1] / (weight[0] + weight[1]));
weight[0] += 1.0;
tmp_front = desired[i];
flag = 1 << (i - 1);
}
}
}
i += 1;
}
*front = Some(tmp_front);
*ascii = FLAG_TO_ASCII[flag as usize];
}