summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan <boss@tehbox.org>2026-06-20 23:29:58 +1200
committerDylan <boss@tehbox.org>2026-06-21 15:03:02 +1200
commit14de6db28fb689cdb066c5b01a2885b6a6a35157 (patch)
tree54aeeaa7859bed134b944a0d23bb30814015512d
parent2d467c488943cea159876b35e3f5c1ba05433189 (diff)
downloadtehimage-rs-14de6db28fb689cdb066c5b01a2885b6a6a35157.tar.gz
tehimage-rs-14de6db28fb689cdb066c5b01a2885b6a6a35157.zip
feat: Initial commit
-rw-r--r--.envrc1
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock133
-rw-r--r--Cargo.toml7
-rw-r--r--flake.lock61
-rw-r--r--flake.nix24
-rw-r--r--smiley.bmpbin0 -> 120054 bytes
-rw-r--r--src/main.rs185
-rw-r--r--src/reader.rs56
9 files changed, 469 insertions, 0 deletions
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
--- /dev/null
+++ b/smiley.bmp
Binary files 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<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)
+ }
+}