From f8ffb92f2857677e4a9986ef154800ddb2e233fd Mon Sep 17 00:00:00 2001 From: Dylan Date: Thu, 25 Jun 2026 13:42:49 +1200 Subject: feat: Added conversion between different color types and depths --- src/bmp.rs | 86 +++++++++++++++++++++++++++++++----------------------- src/byte_encode.rs | 5 ++++ src/image.rs | 63 +++++++++++++++++++++++++++++++++++---- src/main.rs | 16 ++++++---- 4 files changed, 123 insertions(+), 47 deletions(-) diff --git a/src/bmp.rs b/src/bmp.rs index f6c0c7e..8592894 100644 --- a/src/bmp.rs +++ b/src/bmp.rs @@ -1,8 +1,8 @@ -use crate::{image::{ColorType, Image, ImageBase, PixelArr, RGB}, reader::FileReader, writer::FileWriter}; +use crate::{image::{ColorType, Image, ImageBase, PixelArr, RGB, RGBA}, reader::FileReader, writer::FileWriter}; -impl Image for BMPImage +impl Image for BMPImage { - fn read_image(reader: &mut FileReader) -> Result + fn read_image(reader: &mut FileReader) -> Result, &'static str> { let magic: [u8; 2] = reader.read_array(); if magic != [0x42, 0x4d] @@ -18,13 +18,16 @@ impl Image for BMPImage // println!("File header: {file_size} {offset} {header_size}"); + let width: u32; + let height: u32; + let bpp: u8; + if header_size == 40 { - - let width: u32 = reader.read(); - let height: u32 = reader.read(); + width = reader.read(); + height = reader.read(); let _color_planes: u16 = reader.read(); - let bpp = reader.read::<2, u16>() as u8; + 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(); @@ -32,44 +35,53 @@ impl Image for BMPImage 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() + if compression_method != 0 { - 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 Err("Compresssion method not supported"); } - - return Ok(ImageBase - { - bit_depth: bpp, - pixels: pixel_arr, - }) + } + else if header_size == 12 + { + width = reader.read::<2, u16>() as u32; + height = reader.read::<2, u16>() as u32; + let _color_planes: u16 = reader.read(); + bpp = reader.read::<2, u16>() as u8; } else { return Err("Header size not supported yet"); } - - + + if bpp != 24 + { + return Err("Only bpp of 24 is supported so far"); + } + + 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 = RGBA::::DEFAULT; + pixel.b = reader.read(); + pixel.g = reader.read(); + pixel.r = reader.read(); + pixel.a = 255; + pixel_arr[(x as usize, y as usize)] = pixel.convert(); + } + reader.skip(skip); + } + + return Ok(ImageBase + { + bit_depth: bpp, + pixels: pixel_arr, + }) } - fn write_image(image: &ImageBase, writer: &mut FileWriter) -> () { + fn write_image(image: &ImageBase, writer: &mut FileWriter) -> () { writer.write_array([0x42, 0x4d] as [u8; 2]); let row_size = (((24) * image.pixels.width + 31)/32) * 4; @@ -90,7 +102,7 @@ impl Image for BMPImage { for x in 0..image.pixels.width { - let pixel = image.pixels[(x as usize, y as usize)]; + let pixel: RGB = image.pixels[(x as usize, y as usize)].clone().convert(); writer.write(pixel.b); writer.write(pixel.g); writer.write(pixel.r); diff --git a/src/byte_encode.rs b/src/byte_encode.rs index 4be57b2..f5cad19 100644 --- a/src/byte_encode.rs +++ b/src/byte_encode.rs @@ -2,6 +2,11 @@ pub trait ByteEncode: Sized + Copy { fn from_le_bytes(buffer: &[u8; N]) -> Self; fn to_le_bytes(&self) -> [u8; N]; + + // fn from_be_bytes(buffer: &[u8; N]) -> Self + // { + // Self::from_le_bytes(buffer.reversed()) + // } } impl ByteEncode<4> for u32 diff --git a/src/image.rs b/src/image.rs index ab5dc99..9b42b27 100644 --- a/src/image.rs +++ b/src/image.rs @@ -2,18 +2,31 @@ use std::ops; use crate::{reader::FileReader, writer::FileWriter}; +type FullColorDepth = u32; pub trait ColorDepth: Clone { const DEFAULT: Self; + fn from_full_color(x: FullColorDepth) -> Self; + fn to_full_color(&self) -> FullColorDepth; + fn convert(&self) -> T + { + T::from_full_color(self.to_full_color()) + } } impl ColorDepth for u8 { const DEFAULT: Self = 0; + fn from_full_color(x: FullColorDepth) -> Self {(x>>24) as Self} + fn to_full_color(&self) -> FullColorDepth { (*self as FullColorDepth) << 24 } } impl ColorDepth for u16 { const DEFAULT: Self = 0; + fn from_full_color(x: FullColorDepth) -> Self {(x>>16) as Self} + fn to_full_color(&self) -> FullColorDepth { (*self as FullColorDepth) << 16 } } impl ColorDepth for u32 { const DEFAULT: Self = 0; + fn from_full_color(x: FullColorDepth) -> Self {x} + fn to_full_color(&self) -> FullColorDepth { *self } } #[derive(Debug, Clone, Copy)] @@ -32,17 +45,57 @@ pub struct RGB pub b: T, } +type FullColorPixel = RGBA; pub trait ColorType: Clone { const DEFAULT: Self; + fn from_full_color(x: FullColorPixel) -> Self; + fn to_full_color(&self) -> FullColorPixel; + fn convert(&self) -> T + { + T::from_full_color(self.to_full_color()) + } } impl ColorType for RGBA { const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT, a: T::DEFAULT }; + fn from_full_color(x: FullColorPixel) -> Self { + Self + { + r: T::from_full_color(x.r), + g: T::from_full_color(x.g), + b: T::from_full_color(x.b), + a: T::from_full_color(x.a), + } + } + fn to_full_color(&self) -> FullColorPixel { + RGBA { + r: T::to_full_color(&self.r), + g: T::to_full_color(&self.g), + b: T::to_full_color(&self.b), + a: T::to_full_color(&self.a), + } + } } impl ColorType for RGB { const DEFAULT: Self = Self { r: T::DEFAULT, g: T::DEFAULT, b: T::DEFAULT}; + fn from_full_color(x: FullColorPixel) -> Self { + Self + { + r: T::from_full_color(x.r), + g: T::from_full_color(x.g), + b: T::from_full_color(x.b) + } + } + fn to_full_color(&self) -> FullColorPixel { + RGBA { + r: T::to_full_color(&self.r), + g: T::to_full_color(&self.g), + b: T::to_full_color(&self.b), + a: u32::max_value() + } + } } #[derive(Debug)] @@ -76,14 +129,14 @@ impl ops::IndexMut<(usize, usize)> for PixelArr } #[derive(Debug)] -pub struct ImageBase +pub struct ImageBase { pub bit_depth: u8, - pub pixels: PixelArr> + pub pixels: PixelArr } -pub trait Image +pub trait Image { - fn read_image(reader: &mut FileReader) -> Result; - fn write_image(image: &ImageBase, writer: &mut FileWriter)-> (); + fn read_image(reader: &mut FileReader) -> Result, &'static str>; + fn write_image(image: &ImageBase, writer: &mut FileWriter)-> (); } diff --git a/src/main.rs b/src/main.rs index 1e78abb..0da4f13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use crate::{bmp::BMPImage, image::Image, reader::FileReader, writer::FileWriter}; -use std::{fs::File, io::{BufReader, BufWriter}}; +use crate::{bmp::BMPImage, image::{Image, ImageBase, RGB}, reader::FileReader, writer::FileWriter}; +use std::{env, fs::File, io::{BufReader, BufWriter}}; mod reader; mod image; @@ -8,11 +8,17 @@ mod writer; mod byte_encode; fn main() { - let file = File::open("smiley.bmp").expect("File does not exist"); + let args: Vec = env::args().collect(); + if args.len() != 3 + { + panic!("USAGE: infline.bmp outfile.bmp"); + } + + let file = File::open(args[1].clone()).expect("File does not exist"); let mut reader = FileReader::new(BufReader::new(file)); - let _bmp = BMPImage::read_image(&mut reader).expect("Bitmap had error"); + let _bmp: ImageBase> = BMPImage::read_image(&mut reader).expect("Bitmap had error"); - let out_file = File::create("tmp.bmp").expect("Cannot create fiel"); + let out_file = File::create(args[2].clone()).expect("Cannot create file"); let mut writer = FileWriter::new(BufWriter::new(out_file)); BMPImage::write_image(&_bmp, &mut writer); // println!("{_bmp:?}"); -- cgit v1.3.1