TODOs complete. More TODOs?

This commit is contained in:
Pyrex 2024-04-07 23:31:22 -07:00
parent de10e4b4b8
commit f047642f37
5 changed files with 122 additions and 80 deletions

View File

@ -1,6 +1,11 @@
Actually support terminating early for "progressive" loads TODO:
I still think we can interleave layers to get better progressive loading. But we need FC, FD, FE, and FF commands for "rerun 4/3/2/1 tiles ago."
Make a super simple one corresponding to quality 3 or so, controlled by constants?
Generally improve code quality.
Done: Done:
Actually support terminating early for "progressive" loads
Support an alpha channel. Support an alpha channel.
Improve the PNG interface. Improve the PNG interface.
Interleave layers instead of having them consecutively. (Bad idea, breaks RLE. Skipping) Interleave layers instead of having them consecutively. (Bad idea, breaks RLE. Skipping)

View File

@ -43,8 +43,8 @@ pub fn compress<W: Write>(
// build list of tiles // build list of tiles
let mut tiles = vec![]; let mut tiles = vec![];
for (quality, layer) in image.layers.iter() { for (quality, layer) in image.layers.iter() {
for x0 in (0..image.width).step_by(TILE_SZ) { for y0 in (0..image.height).step_by(TILE_SZ) {
for y0 in (0..image.height).step_by(TILE_SZ) { for x0 in (0..image.width).step_by(TILE_SZ) {
let pixel_tile = PixelTile::from_layer( let pixel_tile = PixelTile::from_layer(
x0 as usize, y0 as usize, *quality, layer, x0 as usize, y0 as usize, *quality, layer,
image.width as usize, image.height as usize image.width as usize, image.height as usize
@ -81,8 +81,8 @@ impl PixelTile {
height: usize height: usize
) -> PixelTile { ) -> PixelTile {
let mut pixels = [0; TILE_SZ2]; let mut pixels = [0; TILE_SZ2];
for x in 0..TILE_SZ { for y in 0..TILE_SZ {
for y in 0..TILE_SZ { for x in 0..TILE_SZ {
let src_x = x0 + x; let src_x = x0 + x;
let src_y = y0 + y; let src_y = y0 + y;
pixels[y * TILE_SZ + x] = pixels[y * TILE_SZ + x] =

View File

@ -1,4 +1,4 @@
use std::io::Read; use std::io::{ErrorKind, Read};
use crate::{constants::{MAGIC, TILE_SZ, TILE_SZ2}, image::Image, protocol::{ProtocolReader, ProtocolReaderError, ProtocolReaderResult}, quality_settings::{self, QualitySettings}, quantization, transform}; use crate::{constants::{MAGIC, TILE_SZ, TILE_SZ2}, image::Image, protocol::{ProtocolReader, ProtocolReaderError, ProtocolReaderResult}, quality_settings::{self, QualitySettings}, quantization, transform};
@ -17,58 +17,98 @@ struct QuantTile {
quants: [i16; TILE_SZ2] quants: [i16; TILE_SZ2]
} }
pub enum ProgressiveImage {
Present { image: Image, complete: bool },
NotHereYet
}
pub fn decompress<R: Read>( pub fn decompress<R: Read>(
reader: &mut ProtocolReader<R> reader: &mut ProtocolReader<R>
) -> ProtocolReaderResult<Image> { ) -> ProtocolReaderResult<ProgressiveImage> {
// read header // read header
let header = reader.read_header()?; let read_header_and_stuff: Result<_, ProtocolReaderError> = (|| {
if header.bytes != MAGIC.bytes { let header = reader.read_header()?;
return Err(ProtocolReaderError::WrongHeader); if header.bytes != MAGIC.bytes {
} return Err(ProtocolReaderError::WrongHeader);
if header.version != MAGIC.version { }
return Err(ProtocolReaderError::WrongVersion); if header.version != MAGIC.version {
} return Err(ProtocolReaderError::WrongVersion);
}
let width = reader.read_u32_wide()?; let width = reader.read_u32_wide()?;
let height = reader.read_u32_wide()?; let height = reader.read_u32_wide()?;
let n_layers = reader.read_u32_wide()?; let n_layers = reader.read_u32_wide()?;
// read quality settings for each layout
let mut quality_settings = vec![];
for _ in 0..n_layers {
quality_settings.push(reader.read_quality_settings()?);
}
Ok((width, height, n_layers, quality_settings))
})();
let Ok((width, height, n_layers, quality_settings)) = read_header_and_stuff else {
match read_header_and_stuff {
Ok(_) => unreachable!(),
Err(ProtocolReaderError::Io(io)) => {
if io.kind() == ErrorKind::UnexpectedEof {
return ProtocolReaderResult::Ok(ProgressiveImage::NotHereYet)
}
return Err(ProtocolReaderError::Io(io))
}
Err(e) => return Err(e),
}
};
let width_in_tiles = (width as usize + TILE_SZ - 1) / TILE_SZ; let width_in_tiles = (width as usize + TILE_SZ - 1) / TILE_SZ;
let height_in_tiles = (height as usize + TILE_SZ - 1) / TILE_SZ; let height_in_tiles = (height as usize + TILE_SZ - 1) / TILE_SZ;
let n_tiles_per_layer = width_in_tiles * height_in_tiles; let n_tiles_per_layer = width_in_tiles * height_in_tiles;
// read quality settings for each layout
let mut quality_settings = vec![];
for _ in 0..n_layers {
quality_settings.push(reader.read_quality_settings()?);
}
// read thumbnail block
let mut tiles = vec![QuantTile::new(); n_tiles_per_layer * n_layers as usize]; let mut tiles = vec![QuantTile::new(); n_tiles_per_layer * n_layers as usize];
for l in 0..n_layers { let read_tiles_and_stuff: Result<(), ProtocolReaderError> = (|tiles: &mut [QuantTile]| {
for i in 0..n_tiles_per_layer { // read thumbnail block
tiles[l as usize * n_tiles_per_layer + i].load_zero( for l in 0..n_layers {
quality_settings[l as usize], reader for i in 0..n_tiles_per_layer {
)?; tiles[l as usize * n_tiles_per_layer + i].load_zero(
quality_settings[l as usize], reader
)?;
}
} }
}
// read remaining tiles // read remaining tiles
let mut prev: Option<QuantTile> = None; let mut prev: Option<QuantTile> = None;
for i in 0..n_tiles_per_layer * n_layers as usize { for i in 0..n_tiles_per_layer * n_layers as usize {
tiles[i].load_rest( tiles[i].load_rest(
prev, prev,
reader reader
)?; )?;
prev = Some(tiles[i]); prev = Some(tiles[i]);
} }
Ok(())
})(&mut tiles);
let complete;
match read_tiles_and_stuff {
Err(ProtocolReaderError::Io(i)) => {
if i.kind() == ErrorKind::UnexpectedEof {
complete = false
} else {
return Err(ProtocolReaderError::Io(i))
}
}
Err(e) => return Err(e),
Ok(()) => {
complete = true;
}
};
let mut tile_i = 0; let mut tile_i = 0;
let mut layers = vec![vec![0; width as usize * height as usize]; n_layers as usize]; let mut layers = vec![vec![0; width as usize * height as usize]; n_layers as usize];
for layer in 0..n_layers { for layer in 0..n_layers {
for x0 in (0..width).step_by(TILE_SZ) { for y0 in (0..height).step_by(TILE_SZ) {
for y0 in (0..height).step_by(TILE_SZ) { for x0 in (0..width).step_by(TILE_SZ) {
let pixel_tile = tiles[tile_i].to_coef_tile().to_pixel_tile(); let pixel_tile = tiles[tile_i].to_coef_tile().to_pixel_tile();
tile_i += 1; tile_i += 1;
pixel_tile.to_layer( pixel_tile.to_layer(
@ -81,11 +121,10 @@ pub fn decompress<R: Read>(
let mut image = Image::new(width as usize, height as usize); let mut image = Image::new(width as usize, height as usize);
for (l, layer) in layers.into_iter().enumerate() { for (l, layer) in layers.into_iter().enumerate() {
image.add(quality_settings[l], layer) image.add(quality_settings[l], layer).expect("by construction, this layer should be OK");
.expect("by construction, this layer should be OK");
} }
return Ok(image) return Ok(ProgressiveImage::Present { image: image, complete });
} }
impl CoefTile { impl CoefTile {
@ -108,8 +147,8 @@ impl CoefTile {
impl PixelTile { impl PixelTile {
fn to_layer(&self, x0: usize, y0: usize, layer: &mut [u8], width: usize, height: usize) { fn to_layer(&self, x0: usize, y0: usize, layer: &mut [u8], width: usize, height: usize) {
for x in 0..TILE_SZ { for y in 0..TILE_SZ {
for y in 0..TILE_SZ { for x in 0..TILE_SZ {
let dst_x = x0 + x; let dst_x = x0 + x;
let dst_y = y0 + y; let dst_y = y0 + y;
if dst_x < width && dst_y < height { if dst_x < width && dst_y < height {

View File

@ -14,14 +14,15 @@ mod image;
fn main() { fn main() {
run_for("zonked".to_string()); // run_for("zonked".to_string(), 9, 2, Some(330000));
run_for("avatar2".to_string()); run_for("zonked".to_string(), 9, 2, Some(37000));
run_for("avatar2".to_string(), 9, 2, Some(100000));
} }
fn run_for(name: String) { fn run_for(name: String, quality_rgb: u8, quality_alpha: u8, bytes_to_chop: Option<usize>) {
let image = png_utils::load_image( let image = png_utils::load_image(
format!("inputs/{}.png", name), format!("inputs/{}.png", name),
QualitySettings::new(2), QualitySettings::new(quality_rgb),
QualitySettings::new(2), QualitySettings::new(quality_alpha),
).unwrap(); ).unwrap();
let mut writer = ProtocolWriter::new(vec![]); let mut writer = ProtocolWriter::new(vec![]);
@ -29,7 +30,11 @@ fn run_for(name: String) {
image, image,
&mut writer &mut writer
).unwrap(); ).unwrap();
let compressed = writer.destroy(); let mut compressed = writer.destroy();
if let Some(b2c) = bytes_to_chop {
compressed.drain(b2c..);
}
let mut output_file = File::create(format!("outputs/{}.rxi", name)).unwrap(); let mut output_file = File::create(format!("outputs/{}.rxi", name)).unwrap();
output_file.write_all(&compressed).unwrap(); output_file.write_all(&compressed).unwrap();
@ -37,8 +42,19 @@ fn run_for(name: String) {
let mut reader = ProtocolReader::new(Cursor::new(compressed)); let mut reader = ProtocolReader::new(Cursor::new(compressed));
let decompressed = decompression::decompress(&mut reader).unwrap(); let decompressed = decompression::decompress(&mut reader).unwrap();
let decompressed_image = match decompressed {
decompression::ProgressiveImage::Present { image, complete: _ } => {
image
}
decompression::ProgressiveImage::NotHereYet => {
// image not here yet
println!("not here yet: {}", name);
return
}
};
png_utils::save_image( png_utils::save_image(
format!("outputs/{}_out.png", name), format!("outputs/{}_out.png", name),
decompressed decompressed_image
).unwrap(); ).unwrap();
} }

View File

@ -1,4 +1,4 @@
use std::io::{self, ErrorKind, Read, Write}; use std::io::{self, Read, Write};
use thiserror::Error; use thiserror::Error;
@ -25,10 +25,6 @@ pub struct ProtocolWriter<W: Write> {
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ProtocolReaderError { pub enum ProtocolReaderError {
// this is explicitly supported: rxi images are progressive
#[error("EOF before end of image")]
EarlyEof,
#[error("wrong header")] #[error("wrong header")]
WrongHeader, WrongHeader,
@ -97,10 +93,10 @@ impl<R: Read> ProtocolReader<R> {
pub fn read_header(&mut self) -> ProtocolReaderResult<Header> { pub fn read_header(&mut self) -> ProtocolReaderResult<Header> {
let mut bytes_buf = [0; 3]; let mut bytes_buf = [0; 3];
self.read_exact(&mut bytes_buf)?; self.reader.read_exact(&mut bytes_buf)?;
let mut version_buf = [0; 1]; let mut version_buf = [0; 1];
self.read_exact(&mut version_buf)?; self.reader.read_exact(&mut version_buf)?;
Ok(Header { Ok(Header {
bytes: bytes_buf, bytes: bytes_buf,
@ -110,55 +106,41 @@ impl<R: Read> ProtocolReader<R> {
pub fn read_quality_settings(&mut self) -> ProtocolReaderResult<QualitySettings> { pub fn read_quality_settings(&mut self) -> ProtocolReaderResult<QualitySettings> {
let mut qsettings_buf = [0; 8]; let mut qsettings_buf = [0; 8];
self.read_exact(&mut qsettings_buf)?; self.reader.read_exact(&mut qsettings_buf)?;
Ok(QualitySettings { values: qsettings_buf }) Ok(QualitySettings { values: qsettings_buf })
} }
pub fn read_u32_wide(&mut self) -> ProtocolReaderResult<u32> { pub fn read_u32_wide(&mut self) -> ProtocolReaderResult<u32> {
let mut u32_buf = [0; 4]; let mut u32_buf = [0; 4];
self.read_exact(&mut u32_buf)?; self.reader.read_exact(&mut u32_buf)?;
Ok(u32::from_le_bytes(u32_buf)) Ok(u32::from_le_bytes(u32_buf))
} }
pub fn read_i16_wide(&mut self) -> ProtocolReaderResult<i16> { pub fn read_i16_wide(&mut self) -> ProtocolReaderResult<i16> {
let mut i16_buf = [0; 2]; let mut i16_buf = [0; 2];
self.read_exact(&mut i16_buf)?; self.reader.read_exact(&mut i16_buf)?;
Ok(i16::from_le_bytes(i16_buf)) Ok(i16::from_le_bytes(i16_buf))
} }
pub fn read_u8_wide(&mut self) -> ProtocolReaderResult<u8> { pub fn read_u8_wide(&mut self) -> ProtocolReaderResult<u8> {
let mut u8_buf = [0; 1]; let mut u8_buf = [0; 1];
self.read_exact(&mut u8_buf)?; self.reader.read_exact(&mut u8_buf)?;
Ok(u8::from_le_bytes(u8_buf)) Ok(u8::from_le_bytes(u8_buf))
} }
pub fn read_i16_packed(&mut self) -> ProtocolReaderResult<i16> { pub fn read_i16_packed(&mut self) -> ProtocolReaderResult<i16> {
let mut i8_buf = [0; 1]; let mut i8_buf = [0; 1];
self.read_exact(&mut i8_buf)?; self.reader.read_exact(&mut i8_buf)?;
let i8_value = i8::from_le_bytes(i8_buf); let i8_value = i8::from_le_bytes(i8_buf);
if i8_value != i8::MAX { return Ok(i8_value as i16) } if i8_value != i8::MAX { return Ok(i8_value as i16) }
let mut i16_buf = [0; 2]; let mut i16_buf = [0; 2];
self.read_exact(&mut i16_buf)?; self.reader.read_exact(&mut i16_buf)?;
let i16_value = i16::from_le_bytes(i16_buf); let i16_value = i16::from_le_bytes(i16_buf);
Ok(i16_value as i16) Ok(i16_value as i16)
} }
// wrap UnexpectedEof, since that's an error for other Io clients,
// but it's specifically not an error for us
fn read_exact(&mut self, buf: &mut [u8]) -> ProtocolReaderResult<()> {
match self.reader.read_exact(buf) {
Ok(_) => Ok(()),
Err(e) => {
if e.kind() == ErrorKind::UnexpectedEof {
return Err(ProtocolReaderError::EarlyEof)
}
return Err(ProtocolReaderError::Io(e));
}
}
}
} }