diff options
| author | Dylan <boss@tehbox.org> | 2026-06-20 23:29:58 +1200 |
|---|---|---|
| committer | Dylan <boss@tehbox.org> | 2026-06-21 15:03:02 +1200 |
| commit | 14de6db28fb689cdb066c5b01a2885b6a6a35157 (patch) | |
| tree | 54aeeaa7859bed134b944a0d23bb30814015512d | |
| parent | 2d467c488943cea159876b35e3f5c1ba05433189 (diff) | |
| download | tehimage-rs-14de6db28fb689cdb066c5b01a2885b6a6a35157.tar.gz tehimage-rs-14de6db28fb689cdb066c5b01a2885b6a6a35157.zip | |
feat: Initial commit
| -rw-r--r-- | .envrc | 1 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 133 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | flake.lock | 61 | ||||
| -rw-r--r-- | flake.nix | 24 | ||||
| -rw-r--r-- | smiley.bmp | bin | 0 -> 120054 bytes | |||
| -rw-r--r-- | src/main.rs | 185 | ||||
| -rw-r--r-- | src/reader.rs | 56 |
9 files changed, 469 insertions, 0 deletions
@@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d5df85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.direnv diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4dafa25 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,133 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "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 = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "testing" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8b225fa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "testing" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.8.5" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..41c37ee --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1764521362, + "narHash": "sha256-M101xMtWdF1eSD0xhiR8nG8CXRlHmv6V+VoY65Smwf4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "871b9fd269ff6246794583ce4ee1031e1da71895", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..095f049 --- /dev/null +++ b/flake.nix @@ -0,0 +1,24 @@ +{ + description = "Rust flake"; + inputs = + { + nixpkgs.url = "github:nixos/nixpkgs/25.11"; + flake-utils.url = "github:numtide/flake-utils"; + # flake-utils.follows = "nixpkgs"; + }; + + outputs = { self, nixpkgs, ... }@inputs: + inputs.flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.default = pkgs.mkShell + { + packages = with pkgs; [ + cargo + rustc + rust-analyzer + ]; + }; + }); +} diff --git a/smiley.bmp b/smiley.bmp Binary files differnew file mode 100644 index 0000000..6e29d80 --- /dev/null +++ b/smiley.bmp diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c348e77 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,185 @@ +// #![allow(dead_code)] + +use crate::reader::FileReader; +use std::{fs::File, io::BufReader, ops::{self}}; + +mod reader; + +// #[derive(Debug)] +// struct RGB<T: Add<Output = T> + Copy> +// { +// r: T, +// g: T, +// b: T +// } + +trait ColorDepth: Clone +{ + const DEFAULT: Self; +} +impl ColorDepth for u8 { + const DEFAULT: Self = 0; +} +impl ColorDepth for u16 { + const DEFAULT: Self = 0; +} +impl ColorDepth for u32 { + const DEFAULT: Self = 0; +} + +#[derive(Debug, Clone)] +struct RGBA<T: ColorDepth> +{ + r: T, + g: T, + b: T, + a: T +} +#[derive(Debug, Clone)] +struct RGB<T: ColorDepth> +{ + r: T, + g: T, + b: T, +} + +trait ColorType: Clone +{ + const DEFAULT: Self; +} +impl<T: ColorDepth> ColorType for RGBA<T> +{ + const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT, a: T::DEFAULT }; +} +impl<T: ColorDepth> ColorType for RGB<T> +{ + const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT}; +} + +#[derive(Debug)] +struct PixelArr<T: ColorType> +{ + width: u32, + height: u32, + raw: Vec<T> +} +impl<T: ColorType> PixelArr<T> { + fn new(width: u32, height: u32) -> Self { + let raw = vec![T::DEFAULT;(width*height) as usize]; + Self { width, height, raw } + } +} +impl<T: ColorType> ops::Index<(usize, usize)> for PixelArr<T> +{ + type Output = T; + + fn index(&self, index: (usize, usize)) -> &Self::Output { + let (x, y) = index; + &self.raw[x + y*(self.width as usize)] + } +} +impl<T: ColorType> ops::IndexMut<(usize, usize)> for PixelArr<T> +{ + fn index_mut(&mut self, index: (usize, usize)) -> &mut T { + let (x, y) = index; + &mut self.raw[x + y*(self.width as usize)] + } +} + +#[derive(Debug)] +struct ImageBase +{ + width: u32, + height: u32, + bit_depth: u8, + pixels: PixelArr<RGB<u8>> +} + +struct BMPImage +{ +} + + +trait Image +{ + fn read_image(reader: &mut FileReader) -> Result<ImageBase, &'static str>; +} + +impl Image for BMPImage +{ + fn read_image(reader: &mut FileReader) -> Result<ImageBase, &'static str> + { + let magic: [u8; 2] = reader.read_array(); + if magic != [0x42, 0x4d] + { + return Err("Not a bitmap!"); + } + + let _file_size: u32 = reader.read(); + reader.skip(4); + let _offset: u32 = reader.read(); + + let header_size: u32 = reader.read(); + + // println!("File header: {file_size} {offset} {header_size}"); + + if header_size == 40 + { + + let width: u32 = reader.read(); + let height: u32 = reader.read(); + let _color_planes: u16 = reader.read(); + let bpp = reader.read::<2, u16>() as u8; + let compression_method: u32 = reader.read(); + let _image_size: u32 = reader.read(); + let _horr_res: u32 = reader.read(); + let _vert_res: u32 = reader.read(); + let _color_count: u32 = reader.read(); + let _impotant_color_count: u32 = reader.read(); + + if + bpp != 24 + || compression_method != 0 + { + return Err("bpp or compresssion method not supported") + } + + let row_size = (((bpp as u32) * width + 31)/32) * 4; + let skip: usize = (row_size - width * 3) as usize; + let mut pixel_arr = PixelArr::new(width, height); + for y in (0..height).rev() + { + for x in 0..width + { + let mut pixel = RGB::DEFAULT; + pixel.b = reader.read(); + pixel.g = reader.read(); + pixel.r = reader.read(); + pixel_arr[(x as usize, y as usize)] = pixel; + } + reader.skip(skip); + } + + return Ok(ImageBase + { + width, + height, + bit_depth: bpp, + pixels: pixel_arr, + }) + } + else + { + return Err("Header size not supported yet"); + } + + + } +} + +fn main() { + let file = File::open("smiley.bmp").expect("File does not exist"); + let mut reader = FileReader::new(BufReader::new(file)); + let _bmp = BMPImage::read_image(&mut reader).expect("Bitmap had error"); + // println!("{bmp:?}"); +} diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 0000000..affd2da --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,56 @@ +use std::{fs::File, io::{BufReader, Read}}; + +pub struct FileReader<const BUFF_SIZE: usize = 1024> +{ + buf_reader: BufReader<File> +} + +impl<const BUFF_SIZE: usize> FileReader<BUFF_SIZE> { + pub fn new(buf_reader: BufReader<File>) -> Self + { + Self + { + buf_reader + } + } + + pub fn skip(&mut self, c: usize) + { + self.buf_reader.seek_relative(c as i64).expect("Cannot seek!"); + } + pub fn read_array<const N: usize, T: FromBytes<N> + Sized, const C: usize>(&mut self) -> [T; C] + { + std::array::from_fn(|_| self.read()) + } + + pub fn read<const N: usize, T: FromBytes<N> + Sized>(&mut self) -> T + { + let mut data = [0u8; N]; + self.buf_reader.read_exact(&mut data).expect("Bad read"); + T::from_le_bytes(&data) + } +} + +pub trait FromBytes<const N: usize>: Sized +{ + fn from_le_bytes(buffer: &[u8; N]) -> Self; +} + +impl FromBytes<4> for u32 +{ + fn from_le_bytes(buffer: &[u8; 4]) -> Self { + u32::from_le_bytes(*buffer) + } +} +impl FromBytes<2> for u16 +{ + fn from_le_bytes(buffer: &[u8; 2]) -> Self { + u16::from_le_bytes(*buffer) + } +} +impl FromBytes<1> for u8 +{ + fn from_le_bytes(buffer: &[u8; 1]) -> Self { + u8::from_le_bytes(*buffer) + } +} |
