Created
January 9, 2023 04:10
-
-
Save wareya/335951cbe71a67cc7f7cc7832e45e031 to your computer and use it in GitHub Desktop.
ultima 1 ega row-planar encoder and decoder in rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use image; | |
fn ega_to_image(bytes : &Vec<u8>) -> image::RgbaImage | |
{ | |
let tile_size = [16, 16]; | |
let bits_per_value = 1; | |
let channel_count = 4; | |
let palette = [ | |
[0x00, 0x00, 0x00, 0xFF], | |
[0x00, 0x00, 0xAA, 0xFF], | |
[0x00, 0xAA, 0x00, 0xFF], | |
[0x00, 0xAA, 0xAA, 0xFF], | |
[0xAA, 0x00, 0x00, 0xFF], | |
[0xAA, 0x00, 0xAA, 0xFF], | |
[0xAA, 0x55, 0x00, 0xFF], | |
[0xAA, 0xAA, 0xAA, 0xFF], | |
[0x55, 0x55, 0x55, 0xFF], | |
[0x55, 0x55, 0xFF, 0xFF], | |
[0x55, 0xFF, 0x55, 0xFF], | |
[0x55, 0xFF, 0xFF, 0xFF], | |
[0xFF, 0x55, 0x55, 0xFF], | |
[0xFF, 0x55, 0xFF, 0xFF], | |
[0xFF, 0xFF, 0x55, 0xFF], | |
[0xFF, 0xFF, 0xFF, 0xFF], | |
]; | |
let mut tiles = vec!(); | |
let mut rows = vec!(); | |
let mut channels = vec!(); | |
let mut channel = vec!(); | |
let mut byte_cursor = 0; | |
let mut bit_cursor = 0; | |
let mut i = 0; // bit within value within channel | |
let mut value = 0; | |
while byte_cursor < bytes.len() | |
{ | |
let bit = bytes[byte_cursor] & (0x80>>bit_cursor); | |
value <<= 1; | |
value |= if bit != 0 { 1 } else { 0 }; | |
bit_cursor += 1; | |
if bit_cursor >= 8 | |
{ | |
bit_cursor = 0; | |
byte_cursor += 1; | |
} | |
i += 1; | |
if i >= bits_per_value | |
{ | |
i = 0; | |
channel.push(value); | |
value = 0; | |
} | |
if channel.len() >= tile_size[0] | |
{ | |
channels.push(channel); | |
channel = vec!(); | |
} | |
if channels.len() >= channel_count | |
{ | |
rows.push(channels); | |
channels = vec!(); | |
} | |
if rows.len() >= tile_size[1] | |
{ | |
tiles.push(rows); | |
rows = vec!(); | |
} | |
} | |
let output_tiles_per_row = 9; | |
let w = tile_size[0] * output_tiles_per_row; | |
let h = tile_size[1] * ((tiles.len()+output_tiles_per_row-1)/output_tiles_per_row); | |
let mut img : image::RgbaImage = image::ImageBuffer::new(w as u32, h as u32); | |
println!("tile count {}", tiles.len()); | |
for (tile_num, tile) in tiles.into_iter().enumerate() | |
{ | |
let t_x = (tile_num % output_tiles_per_row)*tile_size[0]; | |
let t_y = (tile_num / output_tiles_per_row)*tile_size[1]; | |
for y in 0..tile_size[1] | |
{ | |
for x in 0..tile_size[0] | |
{ | |
let _i = tile[y][3][x]; | |
let _b = tile[y][0][x]; | |
let _g = tile[y][1][x]; | |
let _r = tile[y][2][x]; | |
let c = _b | _g<<1 | _r<<2 | _i<<3; | |
img.put_pixel((t_x + x) as u32, (t_y + y) as u32, palette[c].into()); | |
} | |
} | |
} | |
img | |
} | |
fn sqdist(n : [u8; 4], m : [u8; 4]) -> i32 | |
{ | |
let a = n[0] as i32 - m[0] as i32; | |
let b = n[1] as i32 - m[1] as i32; | |
let c = n[2] as i32 - m[2] as i32; | |
a*a + b*b + c*c | |
} | |
fn image_to_ega(image : &image::RgbaImage) -> Vec<u8> | |
{ | |
let tile_size = [16, 16]; | |
let bits_per_value = 1; | |
let channel_count = 4; | |
let palette = [ | |
[0x00, 0x00, 0x00, 0xFF], | |
[0x00, 0x00, 0xAA, 0xFF], | |
[0x00, 0xAA, 0x00, 0xFF], | |
[0x00, 0xAA, 0xAA, 0xFF], | |
[0xAA, 0x00, 0x00, 0xFF], | |
[0xAA, 0x00, 0xAA, 0xFF], | |
[0xAA, 0x55, 0x00, 0xFF], | |
[0xAA, 0xAA, 0xAA, 0xFF], | |
[0x55, 0x55, 0x55, 0xFF], | |
[0x55, 0x55, 0xFF, 0xFF], | |
[0x55, 0xFF, 0x55, 0xFF], | |
[0x55, 0xFF, 0xFF, 0xFF], | |
[0xFF, 0x55, 0x55, 0xFF], | |
[0xFF, 0x55, 0xFF, 0xFF], | |
[0xFF, 0xFF, 0x55, 0xFF], | |
[0xFF, 0xFF, 0xFF, 0xFF], | |
]; | |
let (w, h) = image.dimensions(); | |
let tiles_per_row = w/tile_size[0]; | |
let tile_count = (w/tile_size[0]) as usize * (h/tile_size[1]) as usize; | |
let mut bytes = vec!(0; tile_size[0] as usize * tile_size[1] as usize * tile_count * channel_count as usize * bits_per_value as usize / 8); | |
for tile_num in 0..tile_count as u32 | |
{ | |
let t_x = (tile_num % tiles_per_row)*tile_size[0]; | |
let t_y = (tile_num / tiles_per_row)*tile_size[1]; | |
for y in 0..tile_size[1] as u32 | |
{ | |
for x in 0..tile_size[0] as u32 | |
{ | |
let color = image.get_pixel((t_x + x) as u32, (t_y + y) as u32).0; | |
let mut found_i = 0; | |
let mut found_distance = sqdist(color, [0, 0, 0, 0]); | |
for i in 1..palette.len() | |
{ | |
let cost = sqdist(color, palette[i]); | |
if cost < found_distance | |
{ | |
found_i = i; | |
found_distance = cost; | |
} | |
} | |
for c in 0..channel_count | |
{ | |
let mut bit_address = tile_num * tile_size[0] * tile_size[1] * channel_count; | |
bit_address += y * tile_size[0] * channel_count; | |
bit_address += c * tile_size[0]; | |
bit_address += x; | |
let byte_address = (bit_address / 8) as usize; | |
let bit = bit_address % 8; | |
let value = if (found_i & (1<<c)) != 0 { 1 } else { 0 }; | |
bytes[byte_address] |= (value << 7 >> bit) as u8; | |
} | |
} | |
} | |
} | |
bytes | |
} | |
fn main() | |
{ | |
let f1 = std::env::args().nth(1).unwrap(); | |
let f2 = std::env::args().nth(2).unwrap(); | |
if f1.to_lowercase().ends_with(".bin") | |
{ | |
let bytes = std::fs::read(f1).unwrap(); | |
let img = ega_to_image(&bytes); | |
img.save(f2).unwrap(); | |
} | |
else if f1.to_lowercase().ends_with(".png") | |
{ | |
let img = image::open(f1).unwrap().to_rgba8(); | |
let bytes = image_to_ega(&img); | |
std::fs::write(f2, bytes).unwrap(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment