Skip to content

Instantly share code, notes, and snippets.

@wareya
Created January 9, 2023 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wareya/335951cbe71a67cc7f7cc7832e45e031 to your computer and use it in GitHub Desktop.
Save wareya/335951cbe71a67cc7f7cc7832e45e031 to your computer and use it in GitHub Desktop.
ultima 1 ega row-planar encoder and decoder in rust
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