what the fuck help
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| /target | ||||
| outputs/ | ||||
							
								
								
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|     "rust-analyzer.linkedProjects": [ | ||||
|         ".\\Cargo.toml" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										277
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | ||||
| # 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 = "aho-corasick" | ||||
| version = "1.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
|  | ||||
| [[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 = "env_logger" | ||||
| version = "0.8.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" | ||||
| dependencies = [ | ||||
|  "log", | ||||
|  "regex", | ||||
| ] | ||||
|  | ||||
| [[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 = "getrandom" | ||||
| version = "0.2.14" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  "wasi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.153" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" | ||||
|  | ||||
| [[package]] | ||||
| name = "log" | ||||
| version = "0.4.21" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" | ||||
|  | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| version = "2.7.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" | ||||
|  | ||||
| [[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 = "proc-macro2" | ||||
| version = "1.0.79" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "quickcheck" | ||||
| version = "1.0.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" | ||||
| dependencies = [ | ||||
|  "env_logger", | ||||
|  "log", | ||||
|  "rand", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "quickcheck_macros" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 1.0.109", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.36" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand" | ||||
| version = "0.8.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" | ||||
| dependencies = [ | ||||
|  "rand_core", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_core" | ||||
| version = "0.6.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" | ||||
| dependencies = [ | ||||
|  "getrandom", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "regex" | ||||
| version = "1.10.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" | ||||
| dependencies = [ | ||||
|  "aho-corasick", | ||||
|  "memchr", | ||||
|  "regex-automata", | ||||
|  "regex-syntax", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "regex-automata" | ||||
| version = "0.4.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" | ||||
| dependencies = [ | ||||
|  "aho-corasick", | ||||
|  "memchr", | ||||
|  "regex-syntax", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "regex-syntax" | ||||
| version = "0.8.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" | ||||
|  | ||||
| [[package]] | ||||
| name = "rx0" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "png", | ||||
|  "quickcheck", | ||||
|  "quickcheck_macros", | ||||
|  "thiserror", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "simd-adler32" | ||||
| version = "0.3.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.109" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.58" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror" | ||||
| version = "1.0.58" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" | ||||
| dependencies = [ | ||||
|  "thiserror-impl", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror-impl" | ||||
| version = "1.0.58" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.58", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||||
|  | ||||
| [[package]] | ||||
| name = "wasi" | ||||
| version = "0.11.0+wasi-snapshot-preview1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||||
							
								
								
									
										14
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| [package] | ||||
| name = "rx0" | ||||
| 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" | ||||
| thiserror = "1.0.58" | ||||
|  | ||||
| [dev-dependencies] | ||||
| quickcheck = "1.0.3" | ||||
| quickcheck_macros = "1.0.0" | ||||
							
								
								
									
										
											BIN
										
									
								
								inputs/avatar2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								inputs/avatar2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 761 KiB | 
							
								
								
									
										
											BIN
										
									
								
								inputs/zonked.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								inputs/zonked.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 518 KiB | 
							
								
								
									
										56
									
								
								src/decode_image.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/decode_image.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| use std::io::Read; | ||||
|  | ||||
| use crate::{decode_tile, image::Image, protocol::{self, ProtocolReaderResult}}; | ||||
|  | ||||
| pub fn decode_image<R: Read>( | ||||
|     reader: &mut protocol::ProtocolReader<R> | ||||
| ) -> ProtocolReaderResult<Image> { | ||||
|     let width = reader.read_u16()? as usize; | ||||
|     let height = reader.read_u16()? as usize; | ||||
|  | ||||
|     let mut layer_quality = [0_u8; 4]; | ||||
|     for i in 0..4 { | ||||
|         layer_quality[i] = reader.read_u8()?; | ||||
|     } | ||||
|  | ||||
|     let mut n_layers = 0; | ||||
|     for i in (0..4).rev() { | ||||
|         if layer_quality[i] != 0 { | ||||
|             n_layers = i + 1; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut layer_pixels = vec![]; | ||||
|     for _ in 0..n_layers { | ||||
|         layer_pixels.push(vec![0_u8; width as usize * height as usize]) | ||||
|     } | ||||
|  | ||||
|     for y0 in (0..height).step_by(16) { | ||||
|         for x0 in (0..width).step_by(16) { | ||||
|             for layer in 0..n_layers { | ||||
|                 let mut data = [0; 256]; | ||||
|  | ||||
|                 decode_tile::decode_tile(&mut data, layer_quality[layer], reader)?; | ||||
|  | ||||
|                 for y in 0..16 { | ||||
|                     for x in 0..16 { | ||||
|                         let src_x = x0 + x; | ||||
|                         let src_y = y0 + y; | ||||
|                         if src_x < width && src_y < height { | ||||
|                             layer_pixels[layer][src_y * width + src_x] =  | ||||
|                                 data[y * 16 + x]; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut image = Image::new(width, height); | ||||
|     for (layer, pixels) in layer_pixels.into_iter().enumerate() { | ||||
|         image.add(layer_quality[layer], pixels) | ||||
|             .expect("should be correct by construction"); | ||||
|     } | ||||
|     Ok(image) | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/decode_tile.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/decode_tile.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| use std::io::Read; | ||||
|  | ||||
| use crate::protocol::{ProtocolReader, ProtocolReaderResult}; | ||||
|  | ||||
| pub fn decode_tile<R: Read>( | ||||
|     data: &mut [u8; 256],  | ||||
|     quality: u8,  | ||||
|     reader: &mut ProtocolReader<R> | ||||
| ) -> ProtocolReaderResult<()> { | ||||
|     let mut coefs = [0_i32; 256]; | ||||
|  | ||||
|     for _ in 0..quality { | ||||
|         let ix = reader.read_u8()?; | ||||
|         let val = reader.read_u16()? as i32; | ||||
|         coefs[ix as usize] = val; | ||||
|     } | ||||
|  | ||||
|     let mut transform = |zero: usize, stride: usize| { | ||||
|         let mut ixs = [0; 16]; | ||||
|         ixs[0] = zero; | ||||
|         for i in 1..16 { ixs[i] = ixs[i - 1] + stride; } | ||||
|  | ||||
|         for bit in [8, 4, 2, 1] { | ||||
|             for i in 0..16 { | ||||
|                 if i & bit == 0 { | ||||
|                     let j = i | bit; | ||||
|  | ||||
|                     let ival = coefs[ixs[i]]; | ||||
|                     let jval = coefs[ixs[j]]; | ||||
|  | ||||
|                     coefs[ixs[i]] = ival.wrapping_add(jval); | ||||
|                     coefs[ixs[j]] = ival.wrapping_sub(jval); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for i in 0..16 { | ||||
|             coefs[ixs[i]] /= 16; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     for row in 0..16 { transform(row * 16, 1); } | ||||
|     for col in 0..16 { transform(col, 16); } | ||||
|  | ||||
|     for i in 0..256 { | ||||
|         data[i] = coefs[i].max(0).min(255) as u8; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub fn decode_value(enc: u8) -> i32 { | ||||
|     let sign = enc & 0x80 != 0; | ||||
|     let exponent = (enc >> 4) & 0x7; | ||||
|     let mantissa = enc & 0xf; | ||||
|  | ||||
|     let magnitude = ((((mantissa as i32) << 1) | 0b100001) << exponent) - 33; | ||||
|     if sign { return -magnitude * 16 } | ||||
|     return magnitude * 16; | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/encode_image.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/encode_image.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| use std::io::Write; | ||||
|  | ||||
| use crate::{encode_tile, image::Image, protocol::{self, ProtocolWriterResult}}; | ||||
|  | ||||
| pub fn encode_image<W: Write>( | ||||
|     image: Image, | ||||
|     writer: &mut protocol::ProtocolWriter<W> | ||||
| ) -> ProtocolWriterResult<()> { | ||||
|     assert!(image.layers.len() <= 4); // TODO: Enforce in Image | ||||
|  | ||||
|     writer.write_u16(image.width as u16)?; | ||||
|     writer.write_u16(image.height as u16)?; | ||||
|  | ||||
|     for i in 0..4 { | ||||
|         writer.write_u8(image.layers.get(i).map(|l| l.0).unwrap_or(0))?; | ||||
|     } | ||||
|  | ||||
|     for y0 in (0..image.height).step_by(16) { | ||||
|         for x0 in (0..image.width).step_by(16) { | ||||
|             for (quality, layer) in image.layers.iter() { | ||||
|                 let mut data = [0; 256]; | ||||
|  | ||||
|                 for y in 0..16 { | ||||
|                     for x in 0..16 { | ||||
|                         let src_x = x0 + x; | ||||
|                         let src_y = y0 + y; | ||||
|                         data[y * 16 + x] =  | ||||
|                             if src_x < image.width && src_y < image.height {  | ||||
|                                 layer[src_y * image.width + src_x]  | ||||
|                             } else { | ||||
|                                 0 | ||||
|                             }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 encode_tile::encode_tile(data, *quality, writer)? | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										151
									
								
								src/encode_tile.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/encode_tile.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| use std::io::Write; | ||||
|  | ||||
| use crate::protocol::{ProtocolWriter, ProtocolWriterResult}; | ||||
|  | ||||
| pub fn encode_tile<W: Write>( | ||||
|     data: [u8; 256],  | ||||
|     quality: u8,  | ||||
|     writer: &mut ProtocolWriter<W> | ||||
| ) -> ProtocolWriterResult<()> { | ||||
|     let mut coefs: [i32; 256] = [0; 256]; | ||||
|  | ||||
|     for i in 0..256 { | ||||
|         coefs[i] = data[i] as i32; | ||||
|     } | ||||
|  | ||||
|     let mut transform = |zero: usize, stride: usize| { | ||||
|         let mut ixs = [0; 16]; | ||||
|         ixs[0] = zero; | ||||
|         for i in 1..16 { | ||||
|             ixs[i] = ixs[i - 1] + stride; | ||||
|         } | ||||
|  | ||||
|         for bit in [8, 4, 2, 1] { | ||||
|             for i in 0..16 { | ||||
|                 if i & bit == 0 { | ||||
|                     let j = i | bit; | ||||
|  | ||||
|                     let ival = coefs[ixs[i]]; | ||||
|                     let jval = coefs[ixs[j]]; | ||||
|  | ||||
|                     coefs[ixs[i]] = ival.wrapping_add(jval); | ||||
|                     coefs[ixs[j]] = ival.wrapping_sub(jval); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     for col in 0..16 { | ||||
|         transform(col, 16); | ||||
|     } | ||||
|  | ||||
|     for row in 0..16 { | ||||
|         transform(row * 16, 1); | ||||
|     } | ||||
|  | ||||
|     // this operation can be efficiently implemented as a heapsort in C | ||||
|     // but the code is ugly | ||||
|     // so just use Rust's default sort | ||||
|     let mut indices = [0; 256]; | ||||
|     for i in 0..256 { | ||||
|         indices[i] = i; | ||||
|     } | ||||
|     indices.sort_by_key(|ix| -(coefs[*ix]).abs()); | ||||
|  | ||||
|     // write indices and values | ||||
|     // (note that the first index will almost always be zero) | ||||
|     for i in 0..quality { | ||||
|         let ix = indices[i as usize]; | ||||
|         writer.write_u8(ix as u8)?; | ||||
|         writer.write_u16(coefs[ix] as u16)?; | ||||
|     }; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub fn encode_value(value: i32) -> u8 { | ||||
|     let value = value / 16; | ||||
|     let sign = value < 0; | ||||
|     let mut rescaled = value.abs() + 33; | ||||
|     let mut exponent = 0; | ||||
|  | ||||
|     while rescaled > 64 { | ||||
|         exponent += 1; | ||||
|         rescaled >>= 1; | ||||
|     } | ||||
|  | ||||
|     let mantissa = (rescaled >> 1) & 0xf; | ||||
|  | ||||
|     return ( | ||||
|         if sign { 128 } else { 0 } | | ||||
|         exponent << 4 | | ||||
|         mantissa | ||||
|     ) as u8; | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use quickcheck_macros::quickcheck; | ||||
|  | ||||
|     use crate::decode_tile::decode_value; | ||||
|  | ||||
|     use super::encode_value; | ||||
|  | ||||
|     #[quickcheck] | ||||
|     fn test_encode_value_2(val: u16) -> bool { | ||||
|         let val = (val & (0x8000 | 0x3fff)) as i16; | ||||
|         let canon = decode_value(encode_value(val as i32)); | ||||
|  | ||||
|         canon == decode_value(encode_value(canon as i32)) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_encode_value_1() { | ||||
|         // ...01abcdx cases | ||||
|         assert_eq!(0b00001111, encode_value(0 + (0b111110 - 33))); | ||||
|         assert_eq!(0b00001110, encode_value(0 + (0b111100 - 33))); | ||||
|         assert_eq!(0b10001111, encode_value(0 - (0b111110 - 33))); | ||||
|         assert_eq!(0b10001110, encode_value(0 - (0b111100 - 33))); | ||||
|  | ||||
|         // ...01abcdxx cases | ||||
|         assert_eq!(0b00011111, encode_value(0 + (0b1111101 - 33))); | ||||
|         assert_eq!(0b00011110, encode_value(0 + (0b1111001 - 33))); | ||||
|         assert_eq!(0b10011111, encode_value(0 - (0b1111101 - 33))); | ||||
|         assert_eq!(0b10011110, encode_value(0 - (0b1111001 - 33))); | ||||
|  | ||||
|         // ...01abcdxxx cases | ||||
|         assert_eq!(0b00101111, encode_value(0 + (0b11111000 - 33))); | ||||
|         assert_eq!(0b00101110, encode_value(0 + (0b11110001 - 33))); | ||||
|         assert_eq!(0b10101111, encode_value(0 - (0b11111010 - 33))); | ||||
|         assert_eq!(0b10101110, encode_value(0 - (0b11110011 - 33))); | ||||
|  | ||||
|         // ...01abcdxxxx cases | ||||
|         assert_eq!(0b00111111, encode_value(0 + (0b111110000 - 33))); | ||||
|         assert_eq!(0b00111110, encode_value(0 + (0b111100010 - 33))); | ||||
|         assert_eq!(0b10111111, encode_value(0 - (0b111110100 - 33))); | ||||
|         assert_eq!(0b10111110, encode_value(0 - (0b111100110 - 33))); | ||||
|  | ||||
|         // ...01abcdxxxxx cases | ||||
|         assert_eq!(0b01001111, encode_value(0 + (0b1111100000 - 33))); | ||||
|         assert_eq!(0b01001110, encode_value(0 + (0b1111000100 - 33))); | ||||
|         assert_eq!(0b11001111, encode_value(0 - (0b1111101000 - 33))); | ||||
|         assert_eq!(0b11001110, encode_value(0 - (0b1111001100 - 33))); | ||||
|  | ||||
|         // ...01abcdxxxxxx cases | ||||
|         assert_eq!(0b01011111, encode_value(0 + (0b11111000000 - 33))); | ||||
|         assert_eq!(0b01011110, encode_value(0 + (0b11110001000 - 33))); | ||||
|         assert_eq!(0b11011111, encode_value(0 - (0b11111010000 - 33))); | ||||
|         assert_eq!(0b11011110, encode_value(0 - (0b11110011000 - 33))); | ||||
|  | ||||
|         // ...01abcdxxxxxxx cases | ||||
|         assert_eq!(0b01101111, encode_value(0 + (0b111110000000 - 33))); | ||||
|         assert_eq!(0b01101110, encode_value(0 + (0b111100010000 - 33))); | ||||
|         assert_eq!(0b11101111, encode_value(0 - (0b111110100000 - 33))); | ||||
|         assert_eq!(0b11101110, encode_value(0 - (0b111100110000 - 33))); | ||||
|  | ||||
|         // ...01abcdxxxxxxx cases | ||||
|         assert_eq!(0b01111111, encode_value(0 + (0b1111100000000 - 33))); | ||||
|         assert_eq!(0b01111110, encode_value(0 + (0b1111000100001 - 33))); | ||||
|         assert_eq!(0b11111111, encode_value(0 - (0b1111101000010 - 33))); | ||||
|         assert_eq!(0b11111110, encode_value(0 - (0b1111001100011 - 33))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/image.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/image.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| use std::io; | ||||
|  | ||||
| use png::ColorType; | ||||
| use thiserror::Error; | ||||
|  | ||||
| #[derive(Error, Debug)] | ||||
| pub enum ImageError { | ||||
|     #[error("can't make the layer fit {0}x{1}")] | ||||
|     WrongSize(usize, usize), | ||||
|     #[error("unsupported color type {0:?}")] | ||||
|     InvalidInputColorType(ColorType), | ||||
|     #[error("can't save an image with {0} layers as png")] | ||||
|     WrongNumberOfLayersToSave(usize), | ||||
|  | ||||
|     #[error("general IO error")] | ||||
|     IoError(#[from] io::Error), | ||||
|     #[error("error in encoding PNG")] | ||||
|     PngEncodingError(#[from] png::EncodingError), | ||||
|     #[error("error in decoding PNG")] | ||||
|     PngDecodingError(#[from] png::DecodingError), | ||||
| } | ||||
|  | ||||
| pub struct Image { | ||||
|     pub width: usize, | ||||
|     pub height: usize, | ||||
|     pub layers: Vec<(u8, Vec<u8>)> | ||||
| } | ||||
|  | ||||
| impl Image { | ||||
|     pub fn new(width: usize, height: usize) -> Self { | ||||
|         Image { | ||||
|             width, height,  | ||||
|             layers: vec![] | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn add(&mut self, quality: u8, pixels: Vec<u8>) -> Result<(), ImageError> { | ||||
|         if pixels.len() != self.width * self.height { | ||||
|             return Err(ImageError::WrongSize(self.width, self.height)); | ||||
|         } | ||||
|         self.layers.push((quality, pixels)); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| use std::{fs::File, io::{Cursor, Write}}; | ||||
|  | ||||
| use protocol::{ProtocolReader, ProtocolWriter}; | ||||
|  | ||||
| mod decode_image; | ||||
| mod decode_tile; | ||||
| mod encode_image; | ||||
| mod encode_tile; | ||||
| mod image; | ||||
| mod png_utils; | ||||
| mod protocol; | ||||
|  | ||||
| fn main() { | ||||
|     // run_for("zonked".to_string(), 9, 2, Some(330000)); | ||||
|     run_for("zonked".to_string()); | ||||
|     run_for("avatar2".to_string()); | ||||
| } | ||||
| fn run_for(name: String) { | ||||
|     let image = png_utils::load_image( | ||||
|         format!("inputs/{}.png", name), | ||||
|         [5, 6, 5, 0] | ||||
|     ).unwrap(); | ||||
|     let mut writer = ProtocolWriter::new(vec![]); | ||||
|  | ||||
|     encode_image::encode_image( | ||||
|         image, | ||||
|         &mut writer | ||||
|     ).unwrap(); | ||||
|     let compressed = writer.destroy(); | ||||
|  | ||||
|     let mut output_file = File::create(format!("outputs/{}.rx0", name)).unwrap(); | ||||
|     output_file.write_all(&compressed).unwrap(); | ||||
|  | ||||
|     let mut reader = ProtocolReader::new(Cursor::new(compressed)); | ||||
|     let decompressed_image = decode_image::decode_image(&mut reader).unwrap(); | ||||
|  | ||||
|     png_utils::save_image( | ||||
|         format!("outputs/{}_out.png", name),  | ||||
|         decompressed_image | ||||
|     ).unwrap(); | ||||
| } | ||||
|  | ||||
							
								
								
									
										83
									
								
								src/png_utils.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/png_utils.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| use std::fs::File; | ||||
|  | ||||
| use png::{BitDepth, ColorType}; | ||||
|  | ||||
| use crate::image::{Image, ImageError}; | ||||
|  | ||||
| pub fn load_image(filename: String, quality: [u8; 4]) -> Result<Image, ImageError> { | ||||
|     let decoder = png::Decoder::new(File::open(filename).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()]; | ||||
|  | ||||
|     assert_eq!(BitDepth::Eight, reader.info().bit_depth); | ||||
|     assert_eq!(ColorType::Rgb, reader.info().color_type); | ||||
|  | ||||
|     let width = reader.info().width; | ||||
|     let height = reader.info().height; | ||||
|  | ||||
|     match reader.info().color_type { | ||||
|         ColorType::Rgb => { | ||||
|             assert_eq!(3, reader.info().bytes_per_pixel()); | ||||
|  | ||||
|             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(); | ||||
|  | ||||
|             let mut image = Image::new(width as usize, height as usize); | ||||
|             image.add(quality[0], r)?; | ||||
|             image.add(quality[1], g)?; | ||||
|             image.add(quality[2], b)?; | ||||
|             return Ok(image); | ||||
|         } | ||||
|         ColorType::Rgba => { | ||||
|             assert_eq!(4, reader.info().bytes_per_pixel()); | ||||
|  | ||||
|             let r: Vec<u8> = bytes[0..].iter().cloned().step_by(4).collect(); | ||||
|             let g: Vec<u8> = bytes[1..].iter().cloned().step_by(4).collect(); | ||||
|             let b: Vec<u8> = bytes[2..].iter().cloned().step_by(4).collect(); | ||||
|             let a: Vec<u8> = bytes[3..].iter().cloned().step_by(4).collect(); | ||||
|  | ||||
|             let mut image = Image::new(width as usize, height as usize); | ||||
|             image.add(quality[0], r)?; | ||||
|             image.add(quality[1], g)?; | ||||
|             image.add(quality[2], b)?; | ||||
|             image.add(quality[3], a)?; | ||||
|             return Ok(image); | ||||
|         } | ||||
|         ct => { | ||||
|             return Err(ImageError::InvalidInputColorType(ct)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn save_image(filename: String, image: Image) -> Result<(), ImageError> { | ||||
|     let n_layers = image.layers.len(); | ||||
|  | ||||
|     let color_type = match n_layers { | ||||
|         3 => ColorType::Rgb, | ||||
|         4 => ColorType::Rgba, | ||||
|         _ => return Err(ImageError::WrongNumberOfLayersToSave(n_layers)), | ||||
|     }; | ||||
|  | ||||
|     let mut encoder = png::Encoder::new( | ||||
|         File::create(filename)?, | ||||
|         image.width as u32, image.height as u32,  | ||||
|     ); | ||||
|     encoder.set_color(color_type); | ||||
|  | ||||
|     let mut idata: Vec<u8> = vec![0; image.width * image.height * n_layers]; | ||||
|     for i in 0..n_layers { | ||||
|         for (dst, src) in  | ||||
|             idata[i..].iter_mut().step_by(n_layers).zip(image.layers[i].1.iter())  | ||||
|         {   | ||||
|             *dst = *src; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut writer = encoder.write_header()?; | ||||
|     writer.write_image_data(&idata)?; | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										67
									
								
								src/protocol.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/protocol.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use thiserror::Error; | ||||
|  | ||||
| #[derive(Error, Debug)] | ||||
| pub enum ProtocolWriterError { | ||||
|     #[error("error using underlying writer")] | ||||
|     Io(#[from] io::Error) | ||||
| } | ||||
|  | ||||
| pub type ProtocolWriterResult<T> = Result<T, ProtocolWriterError>; | ||||
|  | ||||
| pub struct ProtocolWriter<W: Write> { | ||||
|     writer: W | ||||
| } | ||||
|  | ||||
| #[derive(Error, Debug)] | ||||
| pub enum ProtocolReaderError { | ||||
|     #[error("error using underlying reader")] | ||||
|     Io(#[from] io::Error), | ||||
| } | ||||
|  | ||||
| pub type ProtocolReaderResult<T> = Result<T, ProtocolReaderError>; | ||||
|  | ||||
| pub struct ProtocolReader<R: Read> { | ||||
|     reader: R | ||||
| } | ||||
|  | ||||
| impl<W: Write> ProtocolWriter<W> { | ||||
|     pub fn new(writer: W) -> Self { | ||||
|         Self { writer } | ||||
|     } | ||||
|  | ||||
|     pub fn destroy(self) -> W { | ||||
|         self.writer | ||||
|     } | ||||
|  | ||||
|     pub fn write_u8(&mut self, value: u8) -> ProtocolWriterResult<()> { | ||||
|         self.writer.write_all(&value.to_le_bytes())?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn write_u16(&mut self, value: u16) -> ProtocolWriterResult<()> { | ||||
|         self.writer.write_all(&value.to_le_bytes())?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| } | ||||
| impl<R: Read> ProtocolReader<R> { | ||||
|     pub fn new(reader: R) -> Self { | ||||
|         Self { reader } | ||||
|     } | ||||
|  | ||||
|     pub fn read_u8(&mut self) -> ProtocolReaderResult<u8> { | ||||
|         let mut u8_buf = [0; 1]; | ||||
|         self.reader.read_exact(&mut u8_buf)?; | ||||
|  | ||||
|         Ok(u8::from_le_bytes(u8_buf)) | ||||
|     } | ||||
|  | ||||
|     pub fn read_u16(&mut self) -> ProtocolReaderResult<u16> { | ||||
|         let mut u16_buf = [0; 2]; | ||||
|         self.reader.read_exact(&mut u16_buf)?; | ||||
|  | ||||
|         Ok(u16::from_le_bytes(u16_buf)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										66
									
								
								src/transform.rs.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/transform.rs.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // Ported from:  | ||||
| // - https://colab.research.google.com/drive/1WjtKwUcqxWafAumFO9ET74RsckZSsw6n#scrollTo=e3r9-RBpcgwH | ||||
|  | ||||
| // Specialized for: | ||||
| // | ||||
| // - Input domain: 0-255 | ||||
| // - Tile size: 16x16 | ||||
| // | ||||
| // Rather than dividing by 16 at the end of a decode phase, | ||||
| // we divide by 16 at the end of the _encode_ phase | ||||
| // | ||||
| // The normal range would be something like -255 * 256 to 255 * 256 | ||||
| // (-65280 to 65280) but this reduces it (now it's something like -4080 to 4080) | ||||
| // | ||||
| // This simplifies the decoder (which can be i16) and means it can be written | ||||
| // using i16s instead of i32s | ||||
| // | ||||
| // We could probably shrink the range more by recentering to -128-127, but does | ||||
| // it even matter? Reasonable encodings like A-law and mu-law can handle both. | ||||
| // | ||||
| // (Take my opinions on the range with a grain of salt: I've calculated it but I | ||||
| //  haven't measured it) | ||||
| // | ||||
| struct Transform<'d> { | ||||
|     data: &'d mut [i32], | ||||
|     zero: usize, | ||||
|     stride: usize, | ||||
| } | ||||
|  | ||||
| impl<'d> Transform<'d> { | ||||
|     pub fn encode(&mut self) { | ||||
|         self.transform(); | ||||
|         self.divide(16); | ||||
|     } | ||||
|  | ||||
|     pub fn decode(&mut self) { | ||||
|         self.transform(); | ||||
|     } | ||||
|  | ||||
|     pub fn ix(&self, i: usize) -> usize { | ||||
|         self.zero + t * self.stride | ||||
|     } | ||||
|  | ||||
|     fn transform(&mut self) { | ||||
|         for bit in [8, 4, 2, 1] { | ||||
|             for i in 0..16 { | ||||
|                 if i & bit == 0 { | ||||
|                     let j = i | bit; | ||||
|  | ||||
|                     let ival = self.data[self.ix(i)]; | ||||
|                     let jval = self.data[self.ix(j)]; | ||||
|  | ||||
|                     self.data[self.ix(i)] = ival.wrapping_add(jval); | ||||
|                     self.data[self.ix(j)] = ival.wrapping_sub(jval); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn divide(&mut self, divisor: i32) { | ||||
|         for i in 0..16 { | ||||
|             self.data[self.ix(i)] = self.data[self.ix(j)] / divisor; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user