master
elfeiin 2024-01-05 22:06:51 -08:00
commit 31e2f1ea56
Signed by: elfein
GPG Key ID: A53FDD4FD091A276
4 changed files with 269 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

16
Cargo.lock generated Normal file
View File

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitstream-io"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da"
[[package]]
name = "pictext"
version = "0.1.0"
dependencies = [
"bitstream-io",
]

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "pictext"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitstream-io = "2.2.0"

243
src/lib.rs Normal file
View File

@ -0,0 +1,243 @@
/*
All graphics are 64x64 points.
First bit of every octet indicates next byte is part of current character.
- Control?
- - 0
- - - 7 bit control code
- - 1
- - - Graphical Data
- - - - bit X
- - - - bit Y
- - - - X? 7 bit signed int horizontal offset in points
- - - - Y? 7 bit signed int vertical offset in points
- - - - Bit Quadtree that caches homogeneity
- - - - - Homogenous?
- - - - - - 0
- - - - - - - Read next level
- - - - - - 1
- - - - - - - Fill quadrant with next bit.
Empty graphical data (all 0s) indicates whitespace. Interpretation left to
renderer. May utilize offsets for extra data or interpretation of whitespace,
eg "x > 0" means space or "y < 0" means newline.
*/
use bitstream_io::{BigEndian, BitRead, BitReader, Endianness};
use std::io::Read;
pub struct BitReaderWrapper<T: Read, E: Endianness> {
bit_count: usize,
bitreader: BitReader<T, E>,
}
// Subject to change
pub enum ControlCode {
DirectionRightDown = 4,
DirectionLeftDown = 5,
DirectionRightUp = 6,
DirectionLeftUp = 7,
}
impl<T: Read, E: Endianness> BitRead for BitReaderWrapper<T, E> {
fn read_bit(&mut self) -> std::io::Result<bool> {
if self.bit_count % 8 == 0 {
self.bitreader.read_bit().ok();
self.bit_count += 1;
}
self.bit_count += 1;
self.bitreader.read_bit()
}
fn read<U>(&mut self, mut bits: u32) -> std::io::Result<U>
where
U: bitstream_io::Numeric,
{
let mut n = U::default();
while bits > 0 {
if self.bit_count % 8 == 0 {
self.bitreader.read_bit().ok();
self.bit_count += 1;
}
n <<= 1;
if self.bitreader.read_bit().unwrap() {
n |= U::ONE;
}
self.bit_count += 1;
bits -= 1;
}
Ok(n)
}
fn read_signed<S>(&mut self, bits: u32) -> std::io::Result<S>
where
S: bitstream_io::SignedNumeric,
{
if self.bit_count % 8 == 0 {
self.bitreader.read_bit().ok();
self.bit_count += 1;
}
let sign = self.bitreader.read_bit().unwrap();
self.bit_count += 1;
let n = self.read::<S>(bits - 1).unwrap();
if sign {
Ok(n.as_negative(bits))
} else {
Ok(n)
}
}
fn read_to<V>(&mut self) -> std::io::Result<V>
where
V: bitstream_io::Primitive,
{
unimplemented![]
}
fn read_as_to<F, V>(&mut self) -> std::io::Result<V>
where
F: Endianness,
V: bitstream_io::Primitive,
{
unimplemented![]
}
fn skip(&mut self, bits: u32) -> std::io::Result<()> {
self.bit_count += bits as usize;
self.bitreader.skip(bits)
}
fn byte_aligned(&self) -> bool {
unimplemented![]
}
fn byte_align(&mut self) {
unimplemented![]
}
}
pub struct Character {
pub x_offset: i8,
pub y_offset: i8,
pub control_code: Option<u8>,
pub graphical_data: Option<[u8; 512]>,
}
pub fn decode(input: &[u8]) -> Vec<Character> {
let mut input = BitReaderWrapper {
bit_count: 0,
bitreader: BitReader::<&[u8], BigEndian>::new(input),
};
let mut output = vec![];
while let Ok(graphical) = input.bitreader.read_bit() {
input.bit_count += 1;
if graphical {
let offset_x = input.read_bit().unwrap();
let offset_y = input.read_bit().unwrap();
let mut x_offset = 0i8;
let mut y_offset = 0i8;
if offset_x {
x_offset = input.read::<i8>(7).unwrap();
}
if offset_y {
y_offset = input.read_signed::<i8>(7).unwrap();
}
let mut map = [0u64; 64];
let mut value = None;
for q0 in 0..4 {
let mut erase = false;
if input.read_bit().unwrap() {
erase = true;
value = Some(input.read_bit().unwrap());
}
for q1 in 0..4 {
let mut erase = false;
if value.is_none() && input.read_bit().unwrap() {
erase = true;
value = Some(input.read_bit().unwrap());
}
for q2 in 0..4 {
let mut erase = false;
if value.is_none() && input.read_bit().unwrap() {
erase = true;
value = Some(input.read_bit().unwrap());
}
for q3 in 0..4 {
let mut erase = false;
if value.is_none() && input.read_bit().unwrap() {
erase = true;
value = Some(input.read_bit().unwrap());
}
for q4 in 0..4 {
let mut erase = false;
if value.is_none() && input.read_bit().unwrap() {
erase = true;
value = Some(input.read_bit().unwrap());
}
for q5 in 0..4 {
let index = (q0 / 2 * 32)
+ (q1 / 2 * 16)
+ (q2 / 2 * 8)
+ (q3 / 2 * 4)
+ (q4 / 2 * 2)
+ (q5 / 2);
let shift = (q0 % 2 * 32)
+ (q1 % 2 * 16)
+ (q2 % 2 * 8)
+ (q3 % 2 * 4)
+ (q4 % 2 * 2)
+ (q5 % 2);
map[index] |= if let Some(v) = value {
if v {
1 << shift
} else {
0
}
} else if input.read_bit().unwrap() {
1 << shift
} else {
0
};
}
if erase {
value = None;
}
}
if erase {
value = None;
}
}
if erase {
value = None;
}
}
if erase {
value = None;
}
}
if erase {
value = None;
}
}
let mut raster_data: [u8; 512] = [0; 512];
let mut iter = map.iter().map(|t6| t6.to_le_bytes()).flatten();
raster_data.fill_with(|| iter.next().unwrap());
output.push(Character {
x_offset,
y_offset,
control_code: None,
graphical_data: Some(raster_data),
});
input.skip(8 - input.bit_count as u32 % 8).ok();
} else {
output.push(Character {
x_offset: 0,
y_offset: 0,
control_code: input.read::<u8>(7).unwrap().into(),
graphical_data: None,
})
}
}
output
}