use image as im;
use crate::ga::*;
use crate::pixels;
use itertools::{flatten, Itertools};
use std::collections::HashSet;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Mode {
Mode0,
Mode1,
Mode2,
Mode3,
}
impl From<u8> for Mode {
fn from(val: u8) -> Mode {
match val {
0 => Mode::Mode0,
1 => Mode::Mode1,
2 => Mode::Mode2,
3 => Mode::Mode3,
_ => panic!(format!("{} is not a valid mode.", val)),
}
}
}
impl Mode {
pub fn max_colors(&self) -> usize {
match self {
&Mode::Mode0 => 16,
&Mode::Mode1 => 4,
&Mode::Mode2 => 2,
&Mode::Mode3 => 4,
}
}
pub fn nbPixelsPerByte(&self) -> usize {
match self {
&Mode::Mode0 | &Mode::Mode3 => 2,
&Mode::Mode1 => 4,
&Mode::Mode2 => 8,
}
}
}
#[derive(Copy, Clone)]
pub enum ConversionRule {
AnyModeUseAllPixels,
Mode0SkipOddPixels,
}
fn get_unique_colors(img: &im::ImageBuffer<im::Rgb<u8>, Vec<u8>>) -> HashSet<im::Rgb<u8>> {
let mut set = HashSet::new();
for pixel in img.pixels() {
set.insert(pixel.clone());
}
set
}
fn extract_palette(img: &im::ImageBuffer<im::Rgb<u8>, Vec<u8>>) -> Palette {
let colors = get_unique_colors(img);
let mut p = Palette::empty();
assert!(colors.len() <= 16);
for (idx, color) in colors.iter().enumerate() {
let color = color.clone();
p.set(&Pen::from(idx as u8), Ink::from(color))
}
p
}
fn encode(pens: Vec<Vec<Pen>>, mode: Mode) -> Vec<Vec<u8>> {
let mut rows = Vec::new();
for input_row in pens.iter() {
let row = {
match mode {
Mode::Mode0 => pixels::mode0::pens_to_vec(input_row),
Mode::Mode1 => pixels::mode1::pens_to_vec(input_row),
_ => panic!("Unimplemented yet ..."),
}
};
rows.push(row);
}
rows
}
fn merge_mode0_mode3(line1: &Vec<u8>, line2: &Vec<u8>) -> Vec<u8> {
assert_eq!(line1.len(), line2.len());
eprintln!("Line 1 {:?}", line1);
eprintln!("Line 2 {:?}", line2);
line1
.iter()
.zip(line2.iter())
.map(|(u1, u2)| {
let (p10, p11) = pixels::mode0::byte_to_pens(u1.clone());
let (p20, p21) = pixels::mode0::byte_to_pens(u2.clone());
let p0 = pixels::mode0::mix_mode0_mode3(&p10, &p20);
let p1 = pixels::mode0::mix_mode0_mode3(&p11, &p21);
eprintln!("{}/{} {:?} + {:?} = {:?}", *u1, *u2, &p10, &p20, &p0);
eprintln!("{}/{} {:?} + {:?} = {:?}", *u1, *u2, &p11, &p21, &p1);
pixels::mode0::pens_to_byte(&p0, &p1)
})
.collect::<Vec<u8>>()
}
fn inks_to_pens(inks: &Vec<Vec<Ink>>, p: &Palette) -> Vec<Vec<Pen>> {
inks.iter().map( |line| {
line.iter().map(|ink|{
let pen = p.get_pen_for_ink(ink);
match pen {
Some(pen) => pen,
None => {
panic!("Unable to find a correspondance for ink {:?} in given palette {:?}", ink, p);
}
}
}).collect::<Vec<Pen>>()
}).collect::<Vec<_>>()
}
#[derive(Clone)]
pub struct ColorMatrix {
data: Vec<Vec<Ink>>,
}
impl ColorMatrix {
pub fn new(width: usize, height: usize) -> ColorMatrix {
ColorMatrix {
data: vec![vec![Ink::from(0); width]; height],
}
}
pub fn empty() -> ColorMatrix {
ColorMatrix { data: Vec::new() }
}
pub fn empty_like(&self) -> ColorMatrix {
ColorMatrix {
data: vec![vec![Ink::from(0); self.width() as usize]; self.height() as usize],
}
}
pub fn double_horizontally(&mut self) {
let mut new_data =
vec![vec![Ink::from(0); (2 * self.width()) as usize]; self.height() as usize];
for x in 0..(self.width() as usize) {
for y in 0..(self.height() as usize) {
let color = self.get_ink(x as _, y as _);
new_data[y][x * 2 + 0] = color.clone();
new_data[y][x * 2 + 1] = color.clone();
}
}
std::mem::swap(&mut self.data, &mut new_data)
}
pub fn remove_odd_columns(&mut self) {
let mut new_data =
vec![vec![Ink::from(0); (self.width() / 2) as usize]; self.height() as usize];
for x in 0..((self.width() / 2) as usize) {
for y in 0..(self.height() as usize) {
let color = self.get_ink((x * 2) as _, y as _);
new_data[y][x] = color.clone();
}
}
std::mem::swap(&mut self.data, &mut new_data)
}
pub fn height(&self) -> u32 {
self.data.len() as u32
}
pub fn get_ink(&self, x: usize, y: usize) -> &Ink {
&self.data[y][x]
}
pub fn set_ink(&mut self, x: usize, y: usize, ink: Ink) {
self.data[y][x] = ink;
}
pub fn get_line(&self, y: usize) -> &Vec<Ink> {
&self.data[y]
}
pub fn extract_palette(&self) -> Palette {
let mut p = Palette::empty();
for (idx, color) in flatten(self.data.iter()).unique().enumerate() {
if idx >= 16 {
panic!("[ERROR] your picture uses more than 16 different colors");
}
let color = color.clone();
p.set(&Pen::from(idx as u8), Ink::from(color));
}
p
}
pub fn width(&self) -> u32 {
match self.height() {
0 => 0 as u32,
_ => self.data[0].len() as u32,
}
}
pub fn convert_from_fname(
fname: &str,
conversion: ConversionRule,
) -> Result<ColorMatrix, im::ImageError> {
let img = im::open(fname)?;
Ok(ColorMatrix::convert(&img.to_rgb(), conversion))
}
pub fn convert(
img: &im::ImageBuffer<im::Rgb<u8>, Vec<u8>>,
conversion: ConversionRule,
) -> ColorMatrix {
let height = img.height();
let width = {
match conversion {
ConversionRule::AnyModeUseAllPixels => img.width(),
ConversionRule::Mode0SkipOddPixels => img.width() / 2,
}
};
let mut lines = Vec::new();
lines.reserve(height as usize);
for y in 0..height {
let src_y = y;
let mut line = Vec::new();
line.reserve(width as usize);
for x in 0..width {
let src_x = {
match conversion {
ConversionRule::AnyModeUseAllPixels => x,
ConversionRule::Mode0SkipOddPixels => x * 2,
}
};
let src_color = img.get_pixel(src_x, src_y);
let dest_ink = Ink::from(*src_color);
line.push(dest_ink);
}
lines.push(line);
}
ColorMatrix { data: lines }
}
pub fn diff(&self, other: &ColorMatrix) -> ColorMatrix {
let mut data = vec![vec![Ink::from(26); other.width() as usize]; other.height() as usize];
for x in 0..(self.width() as usize) {
for y in 0..(self.height() as usize) {
if self.data[y][x] != other.data[y][x] {
data[y][x] = Ink::from(0);
}
}
}
ColorMatrix { data }
}
pub fn as_image(&self) -> im::ImageBuffer<im::Rgba<u8>, Vec<u8>> {
let mut buffer: im::ImageBuffer<im::Rgba<u8>, Vec<u8>> =
im::ImageBuffer::new(self.width(), self.height());
for x in 0..(self.width()) {
for y in 0..(self.height()) {
buffer.put_pixel(x, y, self.get_ink(x as usize, y as usize).color());
}
}
buffer
}
pub fn as_sprite(&self, mode: Mode, palette: Option<Palette>) -> Sprite {
let palette = palette.unwrap_or(self.extract_palette());
let pens = inks_to_pens(&self.data, &palette);
Sprite {
mode: Some(mode),
palette: Some(palette),
data: encode(pens, mode.clone()),
}
}
pub fn as_mode1_sprite_with_different_inks_per_line(
&self,
palette: &Vec<(Ink, Ink, Ink, Ink)>,
dummy_palette: &Palette,
) -> Sprite {
let mut data: Vec<Vec<Pen>> = Vec::new();
for y in 0..self.height() {
let y = y as usize;
let line_palette = {
let mut p = Palette::new();
p.set(&Pen::from(0), palette[y].0);
p.set(&Pen::from(1), palette[y].1);
p.set(&Pen::from(2), palette[y].2);
p.set(&Pen::from(3), palette[y].3);
p
};
let pens = self.get_line(y).iter().enumerate().map(|(x, ink)|{
let pen = line_palette.get_pen_for_ink(ink);
match pen {
Some(pen) => pen,
None => {
eprintln!("
[ERROR] In line {}, pixel {} color ({:?}) is not in the palette {:?}. Background is used insted",
y,
x,
ink,
line_palette
);
Pen::from(0)
},
}
}).collect::<Vec<Pen>>();
data.push(pens);
}
let encoded_pixels = encode(data, Mode::Mode1);
Sprite {
mode: Some(Mode::Mode1),
palette: Some(dummy_palette.clone()),
data: encoded_pixels,
}
}
}
pub struct Sprite {
pub(crate) mode: Option<Mode>,
pub(crate) palette: Option<Palette>,
pub(crate) data: Vec<Vec<u8>>,
}
impl Sprite {
pub fn to_color_matrix(&self) -> Option<ColorMatrix> {
if self.mode.is_none() && self.palette.is_none() {
return None;
}
let mut data = Vec::with_capacity(self.data.len());
let p = self.palette.as_ref().unwrap();
for line in self.data.iter() {
let inks = match self.mode {
Some(Mode::Mode0) | Some(Mode::Mode3) => line
.iter()
.flat_map(|b: &u8| {
let pens = {
let mut pens = pixels::mode0::byte_to_pens(*b);
pens.0.limit(self.mode.unwrap());
pens.1.limit(self.mode.unwrap());
pens
};
vec![p.get(&pens.0).clone(), p.get(&pens.1).clone()]
})
.collect::<Vec<Ink>>(),
_ => unimplemented!(),
};
data.push(inks);
}
Some(ColorMatrix { data })
}
pub fn to_linear_vec(&self) -> Vec<u8> {
let size = self.height() * self.byte_width();
let mut bytes: Vec<u8> = Vec::with_capacity(size as usize);
for y in 0..self.height() {
bytes.extend_from_slice(&self.data[y as usize]);
}
bytes
}
pub fn palette(&self) -> Option<Palette> {
self.palette.clone()
}
pub fn set_palette(&mut self, palette: Palette) {
self.palette = Some(palette);
}
pub fn bytes(&self) -> &Vec<Vec<u8>> {
&self.data
}
pub fn mode(&self) -> Option<Mode> {
self.mode.clone()
}
pub fn height(&self) -> u32 {
self.data.len() as u32
}
pub fn byte_width(&self) -> u32 {
match self.height() {
0 => 0 as u32,
_ => self.data[0].len() as u32,
}
}
pub fn pixel_width(&self) -> u32 {
match self.mode {
None => panic!("Unable to get the pixel width when mode is not specified"),
Some(mode) => mode.nbPixelsPerByte() as u32 * self.byte_width(),
}
}
pub fn get_byte(&self, x: usize, y: usize) -> u8 {
let line = &self.data[y];
line[x]
}
pub fn get_byte_safe(&self, x: usize, y: usize) -> Option<u8> {
self.data
.get(y)
.and_then(|v| v.get(x))
.and_then(|v| Some(*v))
}
pub fn get_line(&self, y: usize) -> &Vec<u8> {
&self.data[y]
}
pub fn convert(
img: &im::ImageBuffer<im::Rgb<u8>, Vec<u8>>,
mode: Mode,
conversion: ConversionRule,
palette: Option<Palette>,
) -> Sprite {
let matrix = ColorMatrix::convert(img, conversion);
matrix.as_sprite(mode, palette)
}
pub fn convert_from_fname(
fname: &str,
mode: Mode,
conversion: ConversionRule,
palette: Option<Palette>,
) -> Result<Sprite, im::ImageError> {
let img = im::open(fname)?;
Ok(Sprite::convert(&img.to_rgb(), mode, conversion, palette))
}
pub fn horizontal_transform<F>(&mut self, f: F)
where
F: Fn(&Vec<u8>) -> Vec<u8>,
{
let mut transformed = self.data.iter().map(f).collect::<Vec<_>>();
::std::mem::swap(&mut transformed, &mut self.data);
}
}
#[derive(Clone)]
pub struct MultiModeSprite {
mode: Vec<Mode>,
palette: Palette,
data: Vec<Vec<u8>>,
}
pub enum MultiModeConversion {
FirstHalfSecondHalf,
OddEven,
}
impl MultiModeSprite {
pub fn new(p: Palette) -> MultiModeSprite {
MultiModeSprite {
palette: p,
mode: Vec::new(),
data: Vec::new(),
}
}
pub fn bytes(&self) -> &Vec<Vec<u8>> {
&self.data
}
pub fn height(&self) -> usize {
self.data.len()
}
pub fn width(&self) -> usize {
self.data[0].len()
}
pub fn to_mode0_sprite(self) -> Sprite {
Sprite {
mode: Some(Mode::Mode0),
palette: Some(self.palette),
data: self.data,
}
}
pub fn to_mode3_sprite(self) -> Sprite {
Sprite {
mode: Some(Mode::Mode3),
palette: Some(self.palette),
data: self.data,
}
}
pub fn mode0_mode3_mix_from_mode0(
sprite: &Sprite,
conversion: MultiModeConversion,
) -> MultiModeSprite {
let p_orig = sprite.palette().unwrap();
let p = {
let mut p = Palette::new();
for i in 0..4 {
p.set(i.into(), p_orig.get(i.into()).clone());
}
let lut = [
(0, [5, 6, 7]),
(1, [8, 10, 11]),
(2, [12, 13, 15]),
(3, [4, 9, 14]),
];
for (src, dsts) in lut.iter() {
dsts.iter().for_each(|dst| {
p.set((*dst).into(), p_orig.get((*src).into()).clone());
});
}
p
};
let (modes, lines) = match conversion {
MultiModeConversion::FirstHalfSecondHalf => {
let sprite_height = sprite.height() as usize;
let encoded_height = if sprite_height % 2 == 1 {
sprite_height / 2 + 1
} else {
sprite_height / 2 + 0
} as usize;
let mut modes = Vec::with_capacity(sprite_height);
let mut lines = Vec::with_capacity(encoded_height);
for i in 0..sprite_height {
let mode = if i < encoded_height {
Mode::Mode0
} else {
Mode::Mode3
};
modes.push(mode);
}
for i in 0..encoded_height {
let line1 = &sprite.data[i + 0];
let line2 = sprite.data.get(i + encoded_height);
let line = match line2 {
Some(line2) => merge_mode0_mode3(line1, line2),
None => line1.clone(),
};
lines.push(line);
}
(modes, lines)
}
_ => unimplemented!(),
};
MultiModeSprite {
palette: p,
mode: modes,
data: lines,
}
}
}