First technically working code
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/target
 | 
			
		||||
/outputs
 | 
			
		||||
							
								
								
									
										85
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
# This file is automatically @generated by Cargo.
 | 
			
		||||
# It is not intended for manual editing.
 | 
			
		||||
version = 3
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "adler"
 | 
			
		||||
version = "1.0.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitflags"
 | 
			
		||||
version = "1.3.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crc32fast"
 | 
			
		||||
version = "1.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fdeflate"
 | 
			
		||||
version = "0.3.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "simd-adler32",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "flate2"
 | 
			
		||||
version = "1.0.28"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crc32fast",
 | 
			
		||||
 "miniz_oxide",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "miniz_oxide"
 | 
			
		||||
version = "0.7.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "adler",
 | 
			
		||||
 "simd-adler32",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "png"
 | 
			
		||||
version = "0.17.13"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitflags",
 | 
			
		||||
 "crc32fast",
 | 
			
		||||
 "fdeflate",
 | 
			
		||||
 "flate2",
 | 
			
		||||
 "miniz_oxide",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rximg"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "png",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "simd-adler32"
 | 
			
		||||
version = "0.3.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
 | 
			
		||||
							
								
								
									
										9
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "rximg"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
png = "0.17.13"
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								inputs/avatar2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								inputs/avatar2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 761 KiB  | 
							
								
								
									
										111
									
								
								src/compression.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/compression.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
 | 
			
		||||
use crate::{constants::{MAGIC, TILE_SZ}, transform};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct PixelTile {
 | 
			
		||||
    // i32: representation that supports Walsh-Hadamard
 | 
			
		||||
    pixels: [i32; TILE_SZ * TILE_SZ]
 | 
			
		||||
}
 | 
			
		||||
struct CoefTile {
 | 
			
		||||
    coefs: [i32; TILE_SZ * TILE_SZ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub fn compress(
 | 
			
		||||
    width: u16,
 | 
			
		||||
    height: u16,
 | 
			
		||||
    // n_components: u16,
 | 
			
		||||
    layers: &[&[u8]],
 | 
			
		||||
) -> Vec<u8> {
 | 
			
		||||
    let mut output = vec![];
 | 
			
		||||
 | 
			
		||||
    // validation
 | 
			
		||||
    for l in 0..layers.len() {
 | 
			
		||||
        assert!(layers[l].len() == width as usize * height as usize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // write header
 | 
			
		||||
    output.write(&MAGIC).unwrap();
 | 
			
		||||
    output.write(&(width as u16).to_le_bytes()).unwrap();
 | 
			
		||||
    output.write(&(height as u16).to_le_bytes()).unwrap();
 | 
			
		||||
    // output.write(&n_components.to_le_bytes()).unwrap();
 | 
			
		||||
    output.write(&(layers.len() as u16).to_le_bytes()).unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut tiles = vec![];
 | 
			
		||||
    for layer in layers.iter() {
 | 
			
		||||
        for x0 in (0..width).step_by(TILE_SZ) {
 | 
			
		||||
            for y0 in (0..height).step_by(TILE_SZ) {
 | 
			
		||||
                let pixel_tile = PixelTile::from_layer(
 | 
			
		||||
                    x0 as usize, y0 as usize, layer, 
 | 
			
		||||
                    width as usize, height as usize
 | 
			
		||||
                );
 | 
			
		||||
                let coef_tile = CoefTile::from_pixel_tile(&pixel_tile);
 | 
			
		||||
                tiles.push(coef_tile);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for t in tiles.iter() {
 | 
			
		||||
        t.write_zero(&mut output);
 | 
			
		||||
    }
 | 
			
		||||
    for t in tiles.iter() {
 | 
			
		||||
        t.write_rest(&mut output);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PixelTile {
 | 
			
		||||
    fn from_layer(
 | 
			
		||||
        x0: usize,
 | 
			
		||||
        y0: usize,
 | 
			
		||||
        layer: &[u8],
 | 
			
		||||
        width: usize,
 | 
			
		||||
        height: usize
 | 
			
		||||
    ) -> PixelTile {
 | 
			
		||||
        let mut pixels = [0; TILE_SZ * TILE_SZ];
 | 
			
		||||
        for x in 0..TILE_SZ {
 | 
			
		||||
            for y in 0..TILE_SZ {
 | 
			
		||||
                let src_x = x0 + x;
 | 
			
		||||
                let src_y = y0 + y;
 | 
			
		||||
                pixels[y * TILE_SZ + x] = 
 | 
			
		||||
                    if src_x < width && src_y < height { 
 | 
			
		||||
                        layer[src_y * width + src_x] as i32
 | 
			
		||||
                    } else {
 | 
			
		||||
                        0
 | 
			
		||||
                    };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return PixelTile { pixels };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CoefTile {
 | 
			
		||||
    fn from_pixel_tile(pt: &PixelTile) -> CoefTile {
 | 
			
		||||
        let mut coefs = pt.pixels.clone();
 | 
			
		||||
 | 
			
		||||
        // rows
 | 
			
		||||
        for y in 0..TILE_SZ {
 | 
			
		||||
            transform::encode(&mut coefs, y * TILE_SZ, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // columns
 | 
			
		||||
        for x in 0..TILE_SZ {
 | 
			
		||||
            transform::encode(&mut coefs, x * TILE_SZ, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoefTile { coefs }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_zero(&self, output: &mut Vec<u8>) {
 | 
			
		||||
        output.write(&self.coefs[0].to_le_bytes()).unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_rest(&self, output: &mut Vec<u8>) {
 | 
			
		||||
        for i in 1..self.coefs.len() {
 | 
			
		||||
            output.write(&self.coefs[i].to_le_bytes()).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								src/constants.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/constants.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
pub const MAGIC: [u8; 4] = *b"rxi\0";
 | 
			
		||||
pub const TILE_SZ: usize = 8;
 | 
			
		||||
							
								
								
									
										111
									
								
								src/decompression.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/decompression.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
use std::io::{BufReader, Read};
 | 
			
		||||
 | 
			
		||||
use crate::{constants::{MAGIC, TILE_SZ}, transform};
 | 
			
		||||
 | 
			
		||||
struct PixelTile {
 | 
			
		||||
    // i32: representation that supports Walsh-Hadamard
 | 
			
		||||
    pixels: [i32; TILE_SZ * TILE_SZ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct CoefTile {
 | 
			
		||||
    coefs: [i32; TILE_SZ * TILE_SZ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn decompress(
 | 
			
		||||
    input: &[u8]
 | 
			
		||||
) -> (u16, u16, Vec<Vec<u8>>) {
 | 
			
		||||
    let mut reader = BufReader::new(input);
 | 
			
		||||
 | 
			
		||||
    // read header
 | 
			
		||||
    let mut magic_buffer = [0; MAGIC.len()];
 | 
			
		||||
    reader.read_exact(&mut magic_buffer).unwrap();
 | 
			
		||||
 | 
			
		||||
    assert_eq!(MAGIC, magic_buffer);
 | 
			
		||||
 | 
			
		||||
    let mut u16_buffer = [0; 2];
 | 
			
		||||
    reader.read_exact(&mut u16_buffer).unwrap();
 | 
			
		||||
    let width = u16::from_le_bytes(u16_buffer);
 | 
			
		||||
    reader.read_exact(&mut u16_buffer).unwrap();
 | 
			
		||||
    let height = u16::from_le_bytes(u16_buffer);
 | 
			
		||||
    reader.read_exact(&mut u16_buffer).unwrap();
 | 
			
		||||
    let n_layers = u16::from_le_bytes(u16_buffer);
 | 
			
		||||
 | 
			
		||||
    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 = width_in_tiles * height_in_tiles * (n_layers as usize);
 | 
			
		||||
    let mut tiles = vec![CoefTile::new(); n_tiles];
 | 
			
		||||
    for i in 0..n_tiles {
 | 
			
		||||
        tiles[i].load_zero(&mut reader);
 | 
			
		||||
    }
 | 
			
		||||
    for i in 0..n_tiles {
 | 
			
		||||
        tiles[i].load_rest(&mut reader);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut tile_i = 0;
 | 
			
		||||
    let mut layers = vec![vec![0; width as usize * height as usize]; n_layers as usize];
 | 
			
		||||
    for layer in 0..n_layers {
 | 
			
		||||
        for x0 in (0..width).step_by(TILE_SZ) {
 | 
			
		||||
            for y0 in (0..height).step_by(TILE_SZ) {
 | 
			
		||||
                let pixel_tile = tiles[tile_i].to_pixel_tile();
 | 
			
		||||
                tile_i += 1;
 | 
			
		||||
                pixel_tile.to_layer(
 | 
			
		||||
                    x0 as usize, y0 as usize, &mut layers[layer as usize], 
 | 
			
		||||
                    width as usize, height as usize
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return (width, height, layers)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CoefTile {
 | 
			
		||||
    fn new() -> CoefTile {
 | 
			
		||||
        CoefTile { coefs: [0; TILE_SZ * TILE_SZ] }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn load_zero(&mut self, reader: &mut BufReader<&[u8]>) {
 | 
			
		||||
        let mut coef_zero_buffer = [0; 4];
 | 
			
		||||
        reader.read_exact(&mut coef_zero_buffer).unwrap();
 | 
			
		||||
        self.coefs[0] = i32::from_le_bytes(coef_zero_buffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn load_rest(&mut self, reader: &mut BufReader<&[u8]>) {
 | 
			
		||||
        let mut coef_buffer = [0; 4];
 | 
			
		||||
        for i in 1..self.coefs.len() {
 | 
			
		||||
            reader.read_exact(&mut coef_buffer).unwrap();
 | 
			
		||||
            self.coefs[i] = i32::from_le_bytes(coef_buffer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn to_pixel_tile(&self) -> PixelTile {
 | 
			
		||||
        let mut pixels = self.coefs.clone();
 | 
			
		||||
 | 
			
		||||
        // columns
 | 
			
		||||
        for x in 0..TILE_SZ {
 | 
			
		||||
            transform::decode(&mut pixels, x * TILE_SZ, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // rows
 | 
			
		||||
        for y in 0..TILE_SZ {
 | 
			
		||||
            transform::decode(&mut pixels, y * TILE_SZ, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return PixelTile { pixels };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PixelTile {
 | 
			
		||||
    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 {
 | 
			
		||||
                let dst_x = x0 + x;
 | 
			
		||||
                let dst_y = y0 + y;
 | 
			
		||||
                if dst_x < width && dst_y < height { 
 | 
			
		||||
                    layer[dst_y * width + dst_x] = self.pixels[y * TILE_SZ + x] as u8;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
 | 
			
		||||
use png::{BitDepth, ColorType};
 | 
			
		||||
 | 
			
		||||
mod compression;
 | 
			
		||||
mod constants;
 | 
			
		||||
mod decompression;
 | 
			
		||||
mod transform;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    for chunk in vec![
 | 
			
		||||
        [0, 0, 0, 0, 0, 0, 0, 0],
 | 
			
		||||
        [0, 1, 2, 3, 4, 5, 6, 7]
 | 
			
		||||
    ] {
 | 
			
		||||
        let orig = chunk;
 | 
			
		||||
        let mut enc = chunk.clone();
 | 
			
		||||
        transform::encode(&mut enc, 0, 1);
 | 
			
		||||
        let mut dec = enc.clone();
 | 
			
		||||
        transform::decode(&mut dec, 0, 1);
 | 
			
		||||
        dbg!(orig, enc, dec);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hard_main();
 | 
			
		||||
}
 | 
			
		||||
fn hard_main() {
 | 
			
		||||
    let (width, height, r, g, b) = load_image();
 | 
			
		||||
    let compressed = 
 | 
			
		||||
        compression::compress(width as u16, height as u16, &[&r, &g, &b]);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    let mut output_file = File::create("outputs/avatar2.rxi").unwrap();
 | 
			
		||||
    output_file.write_all(&compressed).unwrap();
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    let (width2, height2, decompressed) =
 | 
			
		||||
        decompression::decompress(&compressed);
 | 
			
		||||
 | 
			
		||||
    assert_eq!(3, decompressed.len());
 | 
			
		||||
    save_image(width2 as usize, height2 as usize, decompressed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn load_image() -> (usize, usize, Vec<u8>, Vec<u8>, Vec<u8>) {
 | 
			
		||||
    let decoder = png::Decoder::new(File::open("inputs/avatar2.png").unwrap());
 | 
			
		||||
    let mut reader = decoder.read_info().unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut buf = vec![0; reader.output_buffer_size()];
 | 
			
		||||
    let info = reader.next_frame(&mut buf).unwrap();
 | 
			
		||||
    let bytes = &buf[..info.buffer_size()];
 | 
			
		||||
 | 
			
		||||
    let width = reader.info().width;
 | 
			
		||||
    let height = reader.info().height;
 | 
			
		||||
    assert_eq!(BitDepth::Eight, reader.info().bit_depth);
 | 
			
		||||
    assert_eq!(3, reader.info().bytes_per_pixel());
 | 
			
		||||
    assert_eq!(ColorType::Rgb, reader.info().color_type);
 | 
			
		||||
 | 
			
		||||
    let r: Vec<u8> = bytes[0..].iter().cloned().step_by(3).collect();
 | 
			
		||||
    let g: Vec<u8> = bytes[1..].iter().cloned().step_by(3).collect();
 | 
			
		||||
    let b: Vec<u8> = bytes[2..].iter().cloned().step_by(3).collect();
 | 
			
		||||
 | 
			
		||||
    assert_eq!(r.len(), (width * height) as usize);
 | 
			
		||||
    assert_eq!(g.len(), (width * height) as usize);
 | 
			
		||||
    assert_eq!(b.len(), (width * height) as usize);
 | 
			
		||||
 | 
			
		||||
    (width as usize, height as usize, r, g, b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn save_image(width: usize, height: usize, image: Vec<Vec<u8>>) {
 | 
			
		||||
    assert_eq!(image.len(), 3);
 | 
			
		||||
 | 
			
		||||
    let mut encoder = png::Encoder::new(
 | 
			
		||||
        File::create("outputs/avatar2.png").unwrap(),
 | 
			
		||||
        width as u32, height as u32, 
 | 
			
		||||
    );
 | 
			
		||||
    encoder.set_color(ColorType::Rgb);
 | 
			
		||||
 | 
			
		||||
    let mut idata: Vec<u8> = vec![0; width * height * 3];
 | 
			
		||||
    for i in [0, 1, 2] {
 | 
			
		||||
        for (dst, src) in 
 | 
			
		||||
            idata[i..].iter_mut().step_by(3).zip(image[i].iter()) 
 | 
			
		||||
        {  
 | 
			
		||||
            *dst = *src;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut writer = encoder.write_header().unwrap();
 | 
			
		||||
    writer.write_image_data(&idata).unwrap();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								src/transform.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/transform.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
use crate::constants::TILE_SZ;
 | 
			
		||||
 | 
			
		||||
// This is the Walsh-Hadamard transform, specialized to 8px
 | 
			
		||||
// Ported from: 
 | 
			
		||||
// - https://colab.research.google.com/drive/1WjtKwUcqxWafAumFO9ET74RsckZSsw6n#scrollTo=e3r9-RBpcgwH
 | 
			
		||||
pub fn encode(data: &mut [i32], zero: usize, stride: usize) {
 | 
			
		||||
    transform(data, zero, stride, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn decode(data: &mut [i32], zero: usize, stride: usize) {
 | 
			
		||||
    transform(data, zero, stride, 8)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn transform(data: &mut [i32], zero: usize, stride: usize, divisor: i32) {
 | 
			
		||||
    let ix = |t| zero + t * stride;
 | 
			
		||||
 | 
			
		||||
    let mut row = [0; TILE_SZ];
 | 
			
		||||
    for i in 0..TILE_SZ {
 | 
			
		||||
        row[i] = data[ix(i)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for bit in [4, 2, 1] {
 | 
			
		||||
        for i in 0..TILE_SZ {
 | 
			
		||||
            if i & bit == 0 {
 | 
			
		||||
                let j = i | bit;
 | 
			
		||||
 | 
			
		||||
                let tmp = row[i];
 | 
			
		||||
                row[i] = row[i].wrapping_add(row[j]);
 | 
			
		||||
                row[j] = tmp.wrapping_sub(row[j]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // reorder according to gray code
 | 
			
		||||
    // basically, this moves important stuff towards the left
 | 
			
		||||
    const GRAY: [usize; TILE_SZ] = [0, 4, 6, 2, 3, 7, 5, 1];
 | 
			
		||||
    for i in 0..TILE_SZ {
 | 
			
		||||
        data[ix(GRAY[i])] = row[i] / divisor;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user