From a49a9bbb907244403e4d9d695e56bba2bd4ca40d Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Tue, 7 Nov 2023 06:09:14 +0200 Subject: [PATCH] Initial commit --- .gitignore | 6 + Cargo.lock | 23 +++ Cargo.toml | 10 ++ src/main.rs | 393 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 432 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9dcf89d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target +*.bin +*.bak +*.txt +*.bmp +*.jpg \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c7bdda3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "lab2" +version = "0.1.0" +dependencies = [ + "anyhow", + "byteorder", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f835ae9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "lab2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" +byteorder = "1.5.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..beac217 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,393 @@ +use anyhow::{anyhow, Result}; +use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use std::{ + fs::{File, OpenOptions}, + io::{BufReader, BufWriter, Seek, SeekFrom}, + path::Path, +}; + +#[derive(Debug)] +struct Pixel { + pub r: u8, + pub g: u8, + pub b: u8, +} + +trait Image { + fn width(&self) -> u32; + fn height(&self) -> u32; + fn pixel(&self, x: u32, y: u32) -> &Pixel; + fn pixel_mut(&mut self, x: u32, y: u32) -> &mut Pixel; +} + +#[derive(Debug)] +struct BMP { + magic: u16, + size: u32, + offset: u32, + hdr_size: u32, + width: u32, + height: u32, + num_planes: u16, + bpp: u16, + compression: u32, + image_size: u32, + h_ppm: i32, + v_ppm: i32, + num_colors: u32, + used_colors: u32, + + pixels: Vec, +} + +impl BMP { + fn read(path: &Path) -> Result { + let file = OpenOptions::new().read(true).open(path)?; + let mut rd = BufReader::new(file); + + let magic = rd.read_u16::()?; + let size = rd.read_u32::()?; + let _ = rd.read_u32::()?; + let offset = rd.read_u32::()?; + let hdr_size = rd.read_u32::()?; + let width = rd.read_u32::()?; + let height = rd.read_u32::()?; + let num_planes = rd.read_u16::()?; + let bpp = rd.read_u16::()?; + let compression = rd.read_u32::()?; + let image_size = rd.read_u32::()?; + let h_ppm = rd.read_i32::()?; + let v_ppm = rd.read_i32::()?; + let num_colors = rd.read_u32::()?; + let used_colors = rd.read_u32::()?; + + let row_size = { + let row_bytes = (bpp as u32 / 8) * width; + 4 * ((row_bytes / 4) + if row_bytes % 4 != 0 { 1 } else { 0 }) + }; + + let mut pixels: Vec = Vec::new(); + for y in (0..height as u32).rev() { + rd.seek(SeekFrom::Start((offset + y as u32 * row_size) as u64))?; + for _ in 0..width { + let b = rd.read_u8()?; + let g = rd.read_u8()?; + let r = rd.read_u8()?; + pixels.push(Pixel { r, g, b }); + } + } + + Ok(BMP { + magic, + size, + offset, + hdr_size, + width, + height, + num_planes, + num_colors, + bpp, + compression, + image_size, + h_ppm, + v_ppm, + used_colors, + pixels, + }) + } + + fn write(&self, path: &Path) -> Result<()> { + let file = OpenOptions::new().write(true).create(true).open(path)?; + let mut wd = BufWriter::new(file); + + wd.write_u16::(self.magic)?; + wd.write_u32::(self.size)?; + wd.write_u32::(0)?; + wd.write_u32::(self.offset)?; + wd.write_u32::(self.hdr_size)?; + wd.write_u32::(self.width)?; + wd.write_u32::(self.height)?; + wd.write_u16::(self.num_planes)?; + wd.write_u16::(self.bpp)?; + wd.write_u32::(self.compression)?; + wd.write_u32::(self.image_size)?; + wd.write_i32::(self.h_ppm)?; + wd.write_i32::(self.v_ppm)?; + wd.write_u32::(self.num_colors)?; + wd.write_u32::(self.used_colors)?; + + let pad = { + let row_bytes = (self.bpp as u32 / 8) * self.width; + let row_size = 4 * ((row_bytes / 4) + if row_bytes % 4 != 0 { 1 } else { 0 }); + + row_size - row_bytes + }; + + for y in (0..self.height).rev() { + for x in 0..self.width { + let pixel = self.pixel(x, y); + wd.write_u8(pixel.b)?; + wd.write_u8(pixel.g)?; + wd.write_u8(pixel.r)?; + } + + for _ in 0..pad { + wd.write_u8(0)?; + } + } + + Ok(()) + } +} + +impl Image for BMP { + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } + + fn pixel(&self, x: u32, y: u32) -> &Pixel { + &self.pixels[(x + y * self.width) as usize] + } + + fn pixel_mut(&mut self, x: u32, y: u32) -> &mut Pixel { + &mut self.pixels[(x + y * self.width) as usize] + } +} + +struct FileBitReader { + pub size: u64, + rd: BufReader, + bit_position: u64, +} + +impl FileBitReader { + pub fn open(path: &Path) -> Result { + let file = OpenOptions::new().read(true).open(path)?; + let size = file.metadata()?.len(); + + let rd = BufReader::new(file); + let bit_position: u64 = 0; + + Ok(Self { + rd, + size, + bit_position, + }) + } + + pub fn read_bit(&mut self) -> Result { + self.rd.seek(SeekFrom::Start(self.bit_position / 8))?; + let bit = { + let byte = self.rd.read_u8()?; + + (byte >> (self.bit_position % 8)) & 1 + }; + + self.bit_position += 1; + Ok(bit) + } + + pub fn read_bits(&mut self, len: u8) -> Result { + assert!(len > 0 && len <= 8); + + let mut out: u8 = 0; + for i in 0..len { + out |= self.read_bit()? << i; + } + Ok(out) + } +} + +struct FileBitWriter { + wd: BufWriter, + bit_position: u64, + byte: Option, +} + +impl FileBitWriter { + pub fn open(path: &Path) -> Result { + let file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path)?; + let wd = BufWriter::new(file); + let bit_position: u64 = 0; + let byte = None; + + Ok(Self { + wd, + bit_position, + byte, + }) + } + + fn flush(&mut self) -> Result<()> { + if let Some(byte) = self.byte { + self.wd.write_u8(byte)?; + self.byte = None; + } + Ok(()) + } + + pub fn write_bit(&mut self, bit: u8) -> Result<()> { + self.byte = if let Some(byte) = self.byte { + let value = byte | (bit << (self.bit_position % 8)); + Some(value) + } else { + Some(bit) + }; + + self.bit_position += 1; + if self.bit_position != 0 && self.bit_position % 8 == 0 { + self.flush()?; + } + + Ok(()) + } + + pub fn write_bits(&mut self, mut bits: u8, len: u8) -> Result<()> { + assert!(len > 0 && len <= 8); + + for i in 0..len { + self.write_bit(bits & 1)?; + bits >>= 1; + } + Ok(()) + } +} + +impl Drop for FileBitWriter { + fn drop(&mut self) { + self.flush().expect("flush before drop"); + } +} + +struct ImageDataStream { + image: T, +} + +impl ImageDataStream { + pub fn new(image: T) -> Self { + Self { image } + } + + fn pixel(&self, addr: u32) -> &Pixel { + self.image + .pixel(addr % self.image.width(), addr / self.image.height()) + } + + fn pixel_mut(&mut self, addr: u32) -> &mut Pixel { + self.image + .pixel_mut(addr % self.image.width(), addr / self.image.height()) + } + + const R_BITS: u8 = 3; + const G_BITS: u8 = 2; + const B_BITS: u8 = 2; + + const R_MASK: u8 = (1 << Self::R_BITS) - 1; + const G_MASK: u8 = (1 << Self::G_BITS) - 1; + const B_MASK: u8 = (1 << Self::B_BITS) - 1; + + const B_POS: u8 = 0; + const G_POS: u8 = Self::B_BITS; + const R_POS: u8 = Self::G_BITS + Self::B_BITS; + + const WORD_SIZE: u8 = Self::R_BITS + Self::G_BITS + Self::B_BITS; + const WORD_MASK: u8 = (1 << Self::WORD_SIZE) - 1; + + pub fn read_word(&self, addr: u32) -> u8 { + let pixel = self.pixel(addr); + + (pixel.r & Self::R_MASK) << Self::R_POS + | (pixel.g & Self::G_MASK) << Self::G_POS + | (pixel.b & Self::B_MASK) << Self::B_POS + } + + pub fn write_word(&mut self, addr: u32, value: u8) { + let pixel = self.pixel_mut(addr); + + pixel.r = (pixel.r & !Self::R_MASK) | ((value >> Self::R_POS) & Self::R_MASK); + pixel.g = (pixel.g & !Self::G_MASK) | ((value >> Self::G_POS) & Self::G_MASK); + pixel.b = (pixel.b & !Self::B_MASK) | (value & Self::B_MASK); + } + + const HEADER_SIZE: u8 = 63; + const HEADER_WORDS: u8 = Self::HEADER_SIZE / Self::WORD_SIZE; + + fn read_header(&self) -> u64 { + let mut header: u64 = 0; + for i in 0..Self::HEADER_WORDS { + header |= (self.read_word(i as u32) as u64) << (i * Self::WORD_SIZE); + } + + header + } + + fn write_header(&mut self, header: u64) { + for i in 0..Self::HEADER_WORDS { + let value = (header >> (i * Self::WORD_SIZE)) as u8 & Self::WORD_MASK; + self.write_word(i as u32, value); + } + } + + const DATA_START: u64 = Self::HEADER_WORDS as u64; + pub fn read_stream(&self, output: &mut FileBitWriter) -> Result<()> { + let bytes = self.read_header(); + let bits = bytes * 8; + let count = bits / Self::WORD_SIZE as u64; + let rem = bits % Self::WORD_SIZE as u64; + + for i in Self::DATA_START..Self::DATA_START + count { + output.write_bits(self.read_word(i as u32), Self::WORD_SIZE)?; + } + if rem != 0 { + let value = self.read_word((Self::DATA_START + count) as u32); + output.write_bits(value, rem as u8)?; // & !((1< Result<()> { + let bytes = input.size; + let bits = bytes * 8; + let count = bits / Self::WORD_SIZE as u64; + let rem = bits % Self::WORD_SIZE as u64; + + self.write_header(bytes); + + for i in Self::DATA_START..Self::DATA_START + count { + self.write_word(i as u32, input.read_bits(Self::WORD_SIZE)?); + } + if rem != 0 { + let value = input.read_bits(rem as u8)?; + self.write_word((Self::DATA_START + count) as u32, value); + } + + Ok(()) + } + + pub fn into_inner(self) -> T { + self.image + } +} + +fn main() { + let bmp = BMP::read(Path::new("blank.bmp")).expect("read"); + let mut test = ImageDataStream::new(bmp); + + let mut input = FileBitReader::open(Path::new("input.jpg")).expect("open"); + test.write_stream(&mut input).expect("write_stream"); + + let mut output = FileBitWriter::open(Path::new("output.jpg")).expect("open"); + test.read_stream(&mut output).expect("read_stream"); + + let out = test.into_inner(); + out.write(Path::new("test2.bmp")).expect("write"); +}