TODOs complete. More TODOs?
This commit is contained in:
parent
de10e4b4b8
commit
f047642f37
7
TODO.md
7
TODO.md
@ -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)
|
||||||
|
@ -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] =
|
||||||
|
@ -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,10 +17,16 @@ 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 read_header_and_stuff: Result<_, ProtocolReaderError> = (|| {
|
||||||
let header = reader.read_header()?;
|
let header = reader.read_header()?;
|
||||||
if header.bytes != MAGIC.bytes {
|
if header.bytes != MAGIC.bytes {
|
||||||
return Err(ProtocolReaderError::WrongHeader);
|
return Err(ProtocolReaderError::WrongHeader);
|
||||||
@ -33,19 +39,36 @@ pub fn decompress<R: Read>(
|
|||||||
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()?;
|
||||||
|
|
||||||
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 n_tiles_per_layer = width_in_tiles * height_in_tiles;
|
|
||||||
|
|
||||||
// read quality settings for each layout
|
// read quality settings for each layout
|
||||||
let mut quality_settings = vec![];
|
let mut quality_settings = vec![];
|
||||||
for _ in 0..n_layers {
|
for _ in 0..n_layers {
|
||||||
quality_settings.push(reader.read_quality_settings()?);
|
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 height_in_tiles = (height as usize + TILE_SZ - 1) / TILE_SZ;
|
||||||
|
|
||||||
|
let n_tiles_per_layer = width_in_tiles * height_in_tiles;
|
||||||
|
|
||||||
// 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];
|
||||||
|
let read_tiles_and_stuff: Result<(), ProtocolReaderError> = (|tiles: &mut [QuantTile]| {
|
||||||
|
// read thumbnail block
|
||||||
for l in 0..n_layers {
|
for l in 0..n_layers {
|
||||||
for i in 0..n_tiles_per_layer {
|
for i in 0..n_tiles_per_layer {
|
||||||
tiles[l as usize * n_tiles_per_layer + i].load_zero(
|
tiles[l as usize * n_tiles_per_layer + i].load_zero(
|
||||||
@ -63,12 +86,29 @@ pub fn decompress<R: Read>(
|
|||||||
)?;
|
)?;
|
||||||
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 {
|
||||||
|
30
src/main.rs
30
src/main.rs
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user