From 14de6db28fb689cdb066c5b01a2885b6a6a35157 Mon Sep 17 00:00:00 2001 From: Dylan Date: Sat, 20 Jun 2026 23:29:58 +1200 Subject: feat: Initial commit --- .envrc | 1 + .gitignore | 2 + Cargo.lock | 133 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +++ flake.lock | 61 +++++++++++++++++++ flake.nix | 24 ++++++++ smiley.bmp | Bin 0 -> 120054 bytes src/main.rs | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/reader.rs | 56 ++++++++++++++++++ 9 files changed, 469 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 smiley.bmp create mode 100644 src/main.rs create mode 100644 src/reader.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -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 new file mode 100644 index 0000000..6e29d80 Binary files /dev/null and b/smiley.bmp differ 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 + 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 +{ + r: T, + g: T, + b: T, + a: T +} +#[derive(Debug, Clone)] +struct RGB +{ + r: T, + g: T, + b: T, +} + +trait ColorType: Clone +{ + const DEFAULT: Self; +} +impl ColorType for RGBA +{ + const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT, a: T::DEFAULT }; +} +impl ColorType for RGB +{ + const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT}; +} + +#[derive(Debug)] +struct PixelArr +{ + width: u32, + height: u32, + raw: Vec +} +impl PixelArr { + fn new(width: u32, height: u32) -> Self { + let raw = vec![T::DEFAULT;(width*height) as usize]; + Self { width, height, raw } + } +} +impl ops::Index<(usize, usize)> for PixelArr +{ + type Output = T; + + fn index(&self, index: (usize, usize)) -> &Self::Output { + let (x, y) = index; + &self.raw[x + y*(self.width as usize)] + } +} +impl ops::IndexMut<(usize, usize)> for PixelArr +{ + 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> +} + +struct BMPImage +{ +} + + +trait Image +{ + fn read_image(reader: &mut FileReader) -> Result; +} + +impl Image for BMPImage +{ + fn read_image(reader: &mut FileReader) -> Result + { + 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 +{ + buf_reader: BufReader +} + +impl FileReader { + pub fn new(buf_reader: BufReader) -> 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 + Sized, const C: usize>(&mut self) -> [T; C] + { + std::array::from_fn(|_| self.read()) + } + + pub fn read + 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: 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) + } +} -- cgit v1.3.1