Skip to content

Commit

Permalink
Merge pull request #2 from orph3usLyre/ls/try-rework-bitmap-width-height
Browse files Browse the repository at this point in the history
chore/remove-width-height-bitmap
  • Loading branch information
orph3usLyre authored Oct 15, 2024
2 parents 4a5e8ad + 1b54147 commit ae93fe9
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 62 deletions.
13 changes: 6 additions & 7 deletions chartr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use std::{collections::HashSet, fs::File, path::Path};
use anyhow::Result;
use image::{codecs::png::PngEncoder, GenericImageView, ImageEncoder};
use libbsb::{
image::{
raw::header::{GeneralParameters, ImageHeader},
BitMap,
},
image::raw::header::{GeneralParameters, ImageHeader},
ColorPalette, KapImageFile,
};
use tracing::{debug, info, instrument};
Expand Down Expand Up @@ -78,16 +75,18 @@ pub fn image_to_kap(image_file: &Path, output_name: &Path) -> Result<()> {
.build();

let rgbs = header.rgb.as_ref().unwrap();
let mut bitmap = BitMap::empty(width, height);
// let mut bitmap = BitMap::empty(width, height);
let mut raster_data = Vec::with_capacity((width * height) as usize);
for (x, y, p) in img.pixels() {
if let Some(index) = rgbs.iter().position(|rgb| rgb.eq(&(p[0], p[1], p[2]))) {
// BSB indexes start from 1
bitmap.set_pixel_index(x as u16, y as u16, (index + 1) as u8)
// bitmap.set_pixel_index(x as u16, y as u16, (index + 1) as u8);
raster_data.insert((x * y) as usize, (index + 1) as u8);
} else {
eprintln!("Unable to find pos for pixel");
}
}
let bsb = KapImageFile::new(header, bitmap)?;
let bsb = KapImageFile::new(header, raster_data)?;
bsb.into_file("test_assets/bsb_from_png.kap")?;
Ok(())
}
11 changes: 4 additions & 7 deletions libbsb/examples/png_to_kap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ use anyhow::Context;
use image::GenericImageView;
use itertools::Itertools;
use libbsb::{
image::{
raw::header::{GeneralParameters, ImageHeader},
BitMap,
},
image::raw::header::{GeneralParameters, ImageHeader},
Depth, KapImageFile,
};
use std::collections::HashMap;
Expand All @@ -22,7 +19,7 @@ fn main() -> anyhow::Result<()> {
img.height().try_into().expect("height is too big"),
);

let mut bitmap = BitMap::empty(width, height);
let mut raster_data = Vec::with_capacity((width * height) as usize);
let mut map: HashMap<(u8, u8, u8), usize> = HashMap::new();
for (x, y, p) in img.pixels() {
let index = map.len();
Expand All @@ -33,7 +30,7 @@ fn main() -> anyhow::Result<()> {
debug_assert!(i <= 127);

// BSB indexes start from 1
bitmap.set_pixel_index(x as u16, y as u16, i + 1)
raster_data.insert((x * y) as usize, (index + 1) as u8);
}

let header = ImageHeader::builder()
Expand All @@ -51,7 +48,7 @@ fn main() -> anyhow::Result<()> {
.collect(),
)
.build();
let bsb = KapImageFile::new(header, bitmap)?;
let bsb = KapImageFile::new(header, raster_data)?;
bsb.into_file("kap_from_png_example.kap")?;
Ok(())
}
4 changes: 2 additions & 2 deletions libbsb/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ pub enum Error {
ParseError(#[from] serde::error::Error),
/// Error returned if width/height of header do not match
/// the width/height of the bitmap
#[error("header width/height does not match bitmap width/height. header: {header:?}, bitmap: {bitmap:?}")]
#[error("header width/height does not match bitmap width/height. header: {header:?}, raster_length: {raster_length:?}")]
MismatchWidthHeight {
/// header width/height
header: (u16, u16),
/// bitmap width/height
bitmap: (u16, u16),
raster_length: usize,
},
}
25 changes: 13 additions & 12 deletions libbsb/src/image/bitmap.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use bon::Builder;

/// Decompressed bitmap of KAP/BSB image embedded raster data
#[derive(Builder, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct BitMap {
Expand All @@ -12,10 +11,19 @@ pub struct BitMap {
}

impl BitMap {
pub(crate) fn new(width: u16, height: u16, data: Vec<u8>) -> Self {
debug_assert_eq!(usize::from(width) * usize::from(height), data.len());
Self {
width,
height,
pixels: data,
}
}

/// Creates a new [`BitMap`]
// TODO:
#[must_use]
pub fn empty(width: u16, height: u16) -> Self {
pub(crate) fn empty(width: u16, height: u16) -> Self {
Self {
width,
height,
Expand All @@ -37,26 +45,19 @@ impl BitMap {

/// Returns the pixel indexes of the image
#[must_use]
pub fn pixel_indexes(&self) -> &[u8] {
pub fn pixel_indices(&self) -> &[u8] {
&self.pixels
}

/// set the value of a specific pixel
pub fn set_pixel_index(&mut self, x: u16, y: u16, value: u8) {
pub(crate) fn _set_pixel_index(&mut self, x: u16, y: u16, value: u8) {
if x < self.width && y < self.height {
self.pixels[usize::from(y) * usize::from(self.width) + usize::from(x)] = value;
}
}

// clear the bitmap (set all pixels to 0)
fn _clear(&mut self) {
for pixel in &mut self.pixels {
*pixel = 0;
}
}

// get an entire row of the bitmap
fn _get_row(&self, y: u16) -> Option<&[u8]> {
pub(crate) fn _get_row(&self, y: u16) -> Option<&[u8]> {
if y < self.height {
let start_index = usize::from(y) * usize::from(self.width);
let end_index = start_index + usize::from(self.width);
Expand Down
8 changes: 8 additions & 0 deletions libbsb/src/image/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ impl ImageHeader {
pub(crate) fn empty() -> Self {
Self::default()
}

pub(crate) const fn width(&self) -> u16 {
self.general_parameters.image_width_height.0
}

pub(crate) const fn height(&self) -> u16 {
self.general_parameters.image_width_height.1
}
}

/// identifier: BSB
Expand Down
36 changes: 18 additions & 18 deletions libbsb/src/image/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ pub mod raw {
};
}
}
pub use crate::image::bitmap::BitMap;

use crate::{error::Error, CTRL_Z};
use anyhow::{ensure, Context, Result};
use bitmap::BitMap;
use compress::compress_bsb_row;
use decompress::{BsbDecompressor, Decompressor};
use header::ImageHeader;
Expand Down Expand Up @@ -84,14 +84,19 @@ impl KapImageFile {
/// This function errors if the width and height of the image header don't match
/// the width and height of the bitmap
///
pub fn new(header: ImageHeader, bitmap: BitMap) -> Result<Self, Error> {
if header.general_parameters.image_width_height != (bitmap.width(), bitmap.height()) {
pub fn new(header: ImageHeader, raster_data: Vec<u8>) -> Result<Self, Error> {
let width = header.width();
let height = header.height();
if raster_data.len() != usize::from(width) * usize::from(height) {
return Err(Error::MismatchWidthHeight {
header: header.general_parameters.image_width_height,
bitmap: (bitmap.width(), bitmap.height()),
raster_length: raster_data.len(),
});
}
Ok(Self { header, bitmap })
Ok(Self {
header,
bitmap: BitMap::new(width, height, raster_data),
})
}

/// Returns a reference to the [`ImageHeader`]
Expand Down Expand Up @@ -245,6 +250,13 @@ impl KapImageFile {
Ok(())
}

/// Returns an array of the pixel indices raster data. See the [`KapImageFile`] documentation
/// for more information.
#[must_use]
pub fn pixel_indices(&self) -> &[u8] {
self.bitmap.pixel_indices()
}

/// Returns an iterator over the palette colors the pixel indexes correspond to
/// (defined in [`ImageHeader::rgb`])
///
Expand All @@ -267,7 +279,7 @@ impl KapImageFile {
}
.context("get color palette")?;
// let rgbs = self.header.rgb.as_ref().context("RGB not found")?;
let out = self.bitmap().pixel_indexes().iter().map(|bsb_p| {
let out = self.bitmap.pixel_indices().iter().map(|bsb_p| {
// NOTE: we subtract one since bsb file indexes start at 1
<[u8; 3]>::from(rgbs[(*bsb_p as usize).saturating_sub(1)])
});
Expand All @@ -287,18 +299,6 @@ impl KapImageFile {
pub const fn height(&self) -> u16 {
self.header.general_parameters.image_width_height.1
}

/// Returns a reference to the [`BitMap`]
#[must_use]
pub const fn bitmap(&self) -> &BitMap {
&self.bitmap
}

/// Returns a mutable reference to the [`BitMap`]
#[must_use]
pub fn bitmap_mut(&mut self) -> &mut BitMap {
&mut self.bitmap
}
}

impl From<Depth> for u8 {
Expand Down
9 changes: 5 additions & 4 deletions libbsb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
//!
//! ```rust
//! use image::GenericImageView;
//! use libbsb::{KapImageFile, image::raw::header::{ImageHeader, GeneralParameters}, image::BitMap, Depth};
//! use libbsb::{KapImageFile, image::raw::header::{ImageHeader, GeneralParameters}, Depth};
//! use std::collections::HashMap;
//!
//! fn main() -> anyhow::Result<()> {
Expand All @@ -87,7 +87,8 @@
//! img.height().try_into().expect("height is too big"),
//! );
//!
//! let mut bitmap = BitMap::empty(width, height);
//!
//! let mut raster_data = vec![0; width as usize * height as usize];
//! let mut map = HashMap::new();
//! for (x, y, p) in img.pixels() {
//! let index = map.len();
Expand All @@ -98,7 +99,7 @@
//! debug_assert!(i <= 127);
//!
//! // BSB indexes start from 1
//! bitmap.set_pixel_index(x as u16, y as u16, i + 1)
//! raster_data[y as usize * width as usize + x as usize] = (index + 1) as u8;
//! }
//! let mut palette = map.into_iter().collect::<Vec<_>>();
//! palette.sort_by_key(|(_, i)| *i);
Expand All @@ -117,7 +118,7 @@
//! .collect(),
//! )
//! .build();
//! let bsb = KapImageFile::new(header, bitmap)?;
//! let bsb = KapImageFile::new(header, raster_data)?;
//! bsb.into_file("kap_from_png_example.kap")?;
//! # std::fs::remove_file("kap_from_png_example.kap")?;
//! Ok(())
Expand Down
22 changes: 10 additions & 12 deletions libbsb/tests/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ mod common;
use common::{CONVERTED_PNG_MAPTECH_TEST_KAP_4_DEPTH, TEST_KAP_TO_PNG};
use image::{codecs::png::PngEncoder, GenericImageView, ImageEncoder};
use libbsb::{
image::{
raw::header::{GeneralParameters, ImageHeader},
BitMap,
},
image::raw::header::{GeneralParameters, ImageHeader},
ColorPalette, Depth, KapImageFile,
};
use mktemp::Temp;
Expand Down Expand Up @@ -68,16 +65,16 @@ fn create_bsb_from_converted_png() -> anyhow::Result<()> {
.build();

let rgbs = header.rgb.as_ref().unwrap();
let mut bitmap = BitMap::empty(width, height);
let mut raster_data = vec![0; width as usize * height as usize];
for (x, y, p) in img.pixels() {
if let Some(index) = rgbs.iter().position(|rgb| rgb.eq(&(p[0], p[1], p[2]))) {
// BSB indexes start from 1
bitmap.set_pixel_index(x as u16, y as u16, (index + 1) as u8)
raster_data[y as usize * width as usize + x as usize] = (index + 1) as u8;
} else {
eprintln!("Unable to find pos for pixel");
}
}
let bsb = KapImageFile::new(header, bitmap)?;
let bsb = KapImageFile::new(header, raster_data)?;
bsb.into_file(Temp::new_file()?)?;
Ok(())
}
Expand Down Expand Up @@ -106,17 +103,19 @@ fn recreate_png_from_converted_png() -> anyhow::Result<()> {
.rgb(unique_colors.into_iter().collect())
.build();
let rgbs = header.rgb.as_ref().unwrap();
let mut bitmap = BitMap::empty(width, height);
let mut raster_data = vec![0; width as usize * height as usize];
for (x, y, p) in img.pixels() {
if let Some(index) = rgbs.iter().position(|rgb| rgb.eq(&(p[0], p[1], p[2]))) {
// BSB indexes start from 1
bitmap.set_pixel_index(x as u16, y as u16, (index + 1) as u8)
if (x as usize) < (width as usize) && (y as usize) < height as usize {
// BSB indexes start from 1
raster_data[y as usize * width as usize + x as usize] = (index + 1) as u8;
}
} else {
eprintln!("Unable to find pos for pixel");
}
}
let tmp_kap = Temp::new_file()?;
let bsb = KapImageFile::new(header, bitmap)?;
let bsb = KapImageFile::new(header, raster_data)?;
bsb.into_file(&tmp_kap)?;

// now load it again
Expand All @@ -138,7 +137,6 @@ fn recreate_png_from_converted_png() -> anyhow::Result<()> {
image::ExtendedColorType::Rgb8,
)?;

// assert hashes
let hash_1 = sha256::try_digest(Path::new(CONVERTED_PNG_MAPTECH_TEST_KAP_4_DEPTH)).unwrap();
let hash_2 = sha256::try_digest(tmp_png).unwrap();
assert_eq!(hash_1, hash_2);
Expand Down

0 comments on commit ae93fe9

Please sign in to comment.