use binrw::{binread, helpers::until_eof, BinRead, BinReaderExt};
use fourcc_rs::{fourcc, FourCC};
use pict::{
decode_bitmap,
drawing_context::decode_pixmap,
shared::{ColorTable, PixMap},
};
use resource_fork::{Resource, ResourceReading};
enum IconSize {
Small,
Large,
Huge,
Mega,
}
impl IconSize {
fn width(&self) -> u32 {
match self {
IconSize::Small => 16,
IconSize::Large => 32,
IconSize::Huge => 48,
IconSize::Mega => 128,
}
}
fn height(&self) -> u32 {
self.width()
}
}
pub const FOUR_BIT_COLOR_TABLE: [u32; 16] = [
0xFFFFFF, 0xFFFF00, 0xFF6600, 0xDD0000, 0xFF0099, 0x330099, 0x0000DD, 0x0099FF, 0x00BB00,
0x006600, 0x663300, 0x996633, 0xCCCCCC, 0x888888, 0x444444, 0x000000,
];
pub const EIGH_BIT_COLOR_TABLE: [u32; 256] = [
0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00, 0xFFCCFF, 0xFFCCCC, 0xFFCC99,
0xFFCC66, 0xFFCC33, 0xFFCC00, 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966, 0xFF9933, 0xFF9900,
0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600, 0xFF33FF, 0xFF33CC, 0xFF3399,
0xFF3366, 0xFF3333, 0xFF3300, 0xFF00FF, 0xFF00CC, 0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000,
0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66, 0xCCFF33, 0xCCFF00, 0xCCCCFF, 0xCCCCCC, 0xCCCC99,
0xCCCC66, 0xCCCC33, 0xCCCC00, 0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900,
0xCC66FF, 0xCC66CC, 0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600, 0xCC33FF, 0xCC33CC, 0xCC3399,
0xCC3366, 0xCC3333, 0xCC3300, 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00, 0x99CCFF, 0x99CCCC, 0x99CC99,
0x99CC66, 0x99CC33, 0x99CC00, 0x9999FF, 0x9999CC, 0x999999, 0x999966, 0x999933, 0x999900,
0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600, 0x9933FF, 0x9933CC, 0x993399,
0x993366, 0x993333, 0x993300, 0x9900FF, 0x9900CC, 0x990099, 0x990066, 0x990033, 0x990000,
0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66, 0x66FF33, 0x66FF00, 0x66CCFF, 0x66CCCC, 0x66CC99,
0x66CC66, 0x66CC33, 0x66CC00, 0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900,
0x6666FF, 0x6666CC, 0x666699, 0x666666, 0x666633, 0x666600, 0x6633FF, 0x6633CC, 0x663399,
0x663366, 0x663333, 0x663300, 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00, 0x33CCFF, 0x33CCCC, 0x33CC99,
0x33CC66, 0x33CC33, 0x33CC00, 0x3399FF, 0x3399CC, 0x339999, 0x339966, 0x339933, 0x339900,
0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600, 0x3333FF, 0x3333CC, 0x333399,
0x333366, 0x333333, 0x333300, 0x3300FF, 0x3300CC, 0x330099, 0x330066, 0x330033, 0x330000,
0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66, 0x00FF33, 0x00FF00, 0x00CCFF, 0x00CCCC, 0x00CC99,
0x00CC66, 0x00CC33, 0x00CC00, 0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900,
0x0066FF, 0x0066CC, 0x006699, 0x006666, 0x006633, 0x006600, 0x0033FF, 0x0033CC, 0x003399,
0x003366, 0x003333, 0x003300, 0x0000FF, 0x0000CC, 0x000099, 0x000066,
0x000033, 0xEE0000, 0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000, 0x440000, 0x220000,
0x110000, 0x00EE00, 0x00DD00, 0x00BB00, 0x00AA00, 0x008800, 0x007700, 0x005500, 0x004400,
0x002200, 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088, 0x000077, 0x000055,
0x000044, 0x000022, 0x000011, 0xEEEEEE, 0xDDDDDD, 0xBBBBBB, 0xAAAAAA, 0x888888, 0x777777,
0x555555, 0x444444, 0x222222, 0x111111, 0x000000,
];
#[binread]
#[derive(Debug, Resource)]
#[resource(code = "ICON")]
#[br(big)]
pub struct Icon([u8; Self::DATA_LEN]);
impl Icon {
pub const DATA_LEN: usize = Self::HEIGHT * Self::WIDTH / 8;
pub const HEIGHT: usize = 32;
pub const WIDTH: usize = 32;
pub fn data(&self) -> &[u8; Self::DATA_LEN] {
&self.0
}
pub fn at(&self, x: usize, y: usize) -> bool {
let bytes_per_row = self.width() / 8;
let chosen_byte = y * bytes_per_row + x / 8;
let bit = 7 - (x % 8);
(self.0[chosen_byte] & (1 << bit)) != 0
}
pub fn into_data(self) -> [u8; Self::DATA_LEN] {
self.0
}
pub const fn width(&self) -> usize {
Self::WIDTH
}
pub const fn height(&self) -> usize {
Self::HEIGHT
}
}
#[derive(Debug)]
pub struct Bitmap {
row_bytes: u16,
bounds: pict::shared::Rect,
}
impl BinRead for Bitmap {
type Args<'a> = ();
fn read_options<R: std::io::Read + std::io::Seek>(
reader: &mut R,
_endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<Self> {
let row_bytes_and_flags_hi: u8 = reader.read_be()?;
let data_is_pixmap = row_bytes_and_flags_hi & 0x80 != 0;
assert!(!data_is_pixmap);
let row_bytes_lo: u8 = reader.read_be()?;
let flags_and_row_bytes = ((row_bytes_and_flags_hi as u16) << 8) | row_bytes_lo as u16;
let bounds: pict::shared::Rect = reader.read_be()?;
Ok(Self {
row_bytes: flags_and_row_bytes,
bounds,
})
}
}
#[binread]
#[derive(Debug)]
#[br(big)]
pub struct ColorIconHeader {
_pix_map_unused: u32,
#[br(temp)]
pub row_bytes_and_flags_low_byte: u8,
#[br(args(row_bytes_and_flags_low_byte,))]
pub pix_map: PixMap,
_mask_unused: u32,
pub mask_header: Bitmap,
_bitmap_unused: u32,
pub bitmap_header: Bitmap,
pub icon_data: u32,
}
#[derive(Debug, Resource)]
#[resource(code = "cicn")]
pub struct ColorIcon {
pub image: image::ImageBuffer<image::Rgba<u8>, Vec<u8>>,
pub bitmap: image::ImageBuffer<image::Rgba<u8>, Vec<u8>>,
}
impl BinRead for ColorIcon {
type Args<'a> = ();
fn read_options<R: std::io::Read + std::io::Seek>(
reader: &mut R,
_endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<Self> {
let header: ColorIconHeader = reader.read_be()?;
let mask_size =
header.mask_header.bounds.height() as usize * header.mask_header.row_bytes as usize;
let mut mask_bytes = vec![0u8; mask_size];
reader.read_exact(&mut mask_bytes)?;
let bitmap_size =
header.bitmap_header.bounds.height() as usize * header.bitmap_header.row_bytes as usize;
let mut bitmap_bytes = vec![0u8; bitmap_size];
reader.read_exact(&mut bitmap_bytes)?;
let color_table: ColorTable = reader.read_be()?;
let pixmap_size =
header.pix_map.bounds.height() as usize * header.pix_map.bytes_per_row() as usize;
let mut pixmap_bytes = vec![0u8; pixmap_size];
reader.read_exact(&mut pixmap_bytes)?;
let mut image = decode_pixmap(&header.pix_map, &color_table, &pixmap_bytes);
let mut bitmap = decode_bitmap(
&header.bitmap_header.bounds,
header.bitmap_header.row_bytes as usize,
bitmap_bytes,
);
let mask = decode_bitmap(
&header.mask_header.bounds,
header.mask_header.row_bytes as usize,
mask_bytes,
);
for (x, y, mask) in mask.enumerate_pixels() {
if mask.0[0] != 0 {
image.get_pixel_mut(x, y).0[3] = 0x00;
bitmap.get_pixel_mut(x, y).0[3] = 0x00;
} else {
image.get_pixel_mut(x, y).0[3] = 0xFF;
bitmap.get_pixel_mut(x, y).0[3] = 0xFF;
}
}
Ok(Self { image, bitmap })
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "ics4")]
pub struct ICS4 {
bytes: [u8; 128],
}
impl ICS4 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_nibble_indexed_icon(IconSize::Small, &FOUR_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "icl4")]
pub struct ICL4 {
bytes: [u8; 512],
}
impl ICL4 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_nibble_indexed_icon(IconSize::Large, &FOUR_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "ich4")]
pub struct ICH4 {
bytes: [u8; 48 * 48 / 2],
}
impl ICH4 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_nibble_indexed_icon(IconSize::Huge, &FOUR_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "ics8")]
pub struct ICS8 {
bytes: [u8; 256],
}
impl ICS8 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_byte_indexed_icon(IconSize::Small, &EIGH_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "icl8")]
pub struct ICL8 {
bytes: [u8; 1024],
}
impl ICL8 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_byte_indexed_icon(IconSize::Large, &EIGH_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "ich8")]
pub struct ICH8 {
bytes: [u8; 48 * 48],
}
impl ICH8 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_byte_indexed_icon(IconSize::Huge, &EIGH_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "ics#", include_size = true)]
pub struct ICSN {
#[br(count(size))]
bytes: Vec<u8>,
}
impl ICSN {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_bitmap_image_list(16, 16, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "ICN#", include_size = true)]
pub struct ICNN {
#[br(count(size))]
bytes: Vec<u8>,
}
impl ICNN {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_bitmap_image_list(32, 32, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "ich#", include_size = true)]
pub struct ICHN {
#[br(count(size))]
bytes: Vec<u8>,
}
impl ICHN {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_bitmap_image_list(48, 48, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "s8mk", include_size = true)]
pub struct S8MK {
#[br(count(size))]
bytes: Vec<u8>,
}
impl S8MK {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_single_channel_icon(IconSize::Small, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "l8mk", include_size = true)]
pub struct L8MK {
#[br(count(size))]
bytes: Vec<u8>,
}
impl L8MK {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_single_channel_icon(IconSize::Large, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "h8mk", include_size = true)]
pub struct H8MK {
#[br(count(size))]
bytes: Vec<u8>,
}
impl H8MK {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_single_channel_icon(IconSize::Huge, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "t8mk", include_size = true)]
pub struct T8MK {
#[br(count(size))]
bytes: Vec<u8>,
}
impl T8MK {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_single_channel_icon(IconSize::Mega, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "is32", include_size = true)]
pub struct IS32 {
#[br(count(size))]
bytes: Vec<u8>,
}
impl IS32 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_full_color_icon(IconSize::Small, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "il32", include_size = true)]
pub struct IL32 {
#[br(count(size))]
bytes: Vec<u8>,
}
impl IL32 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_full_color_icon(IconSize::Large, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "ih32", include_size = true)]
pub struct IH32 {
#[br(count(size))]
bytes: Vec<u8>,
}
impl IH32 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_full_color_icon(IconSize::Huge, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "it32", include_size = true)]
pub struct IT32 {
#[br(count(size))]
bytes: Vec<u8>,
}
impl IT32 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_full_color_icon(IconSize::Mega, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big, import(size: usize))]
#[resource(code = "icm#", include_size = true)]
pub struct ICMN {
#[br(count(size))]
bytes: Vec<u8>,
}
impl ICMN {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_bitmap_image_list(16, 12, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "icm4")]
pub struct ICM4 {
bytes: [u8; 96],
}
impl ICM4 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_nibble_indexed_image(16, 12, &FOUR_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug, BinRead, Resource)]
#[br(big)]
#[resource(code = "icm8")]
pub struct ICM8 {
bytes: [u8; 192],
}
impl ICM8 {
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_byte_indexed_image(16, 12, &EIGH_BIT_COLOR_TABLE, &self.bytes)
}
}
#[derive(Debug)]
pub enum IconFamilyElement {
ICMN(ICMN),
ICM4(ICM4),
ICM8(ICM8),
ICS4(ICS4),
ICL4(ICL4),
ICH4(ICH4),
ICS8(ICS8),
ICL8(ICL8),
ICH8(ICH8),
ICSN(ICSN),
ICNN(ICNN),
ICHN(ICHN),
IS32(IS32),
IL32(IL32),
IH32(IH32),
IT32(IT32),
S8MK(S8MK),
L8MK(L8MK),
H8MK(H8MK),
T8MK(T8MK),
Unknown { element_type: FourCC, data: Vec<u8> },
}
impl BinRead for IconFamilyElement {
type Args<'a> = ();
fn read_options<R: std::io::Read + std::io::Seek>(
reader: &mut R,
_endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<Self> {
let element_type: FourCC = reader.read_be()?;
let element_size: u32 = reader.read_be()?;
let pos = reader.stream_position()?;
if element_size < 8 {
return Err(binrw::Error::Custom {
pos,
err: Box::new("Icon family element is too short"),
});
}
let remaining = element_size as usize - 8;
let result = Ok(match element_type {
S8MK::FOURCC => Self::S8MK(reader.read_be_args((remaining,))?),
L8MK::FOURCC => Self::L8MK(reader.read_be_args((remaining,))?),
H8MK::FOURCC => Self::H8MK(reader.read_be_args((remaining,))?),
T8MK::FOURCC => Self::T8MK(reader.read_be_args((remaining,))?),
ICMN::FOURCC => Self::ICMN(reader.read_be_args((remaining,))?),
ICM4::FOURCC => Self::ICM4(reader.read_be_args(())?),
ICM8::FOURCC => Self::ICM8(reader.read_be_args(())?),
ICS4::FOURCC => Self::ICS4(reader.read_be_args(())?),
ICL4::FOURCC => Self::ICL4(reader.read_be_args(())?),
ICH4::FOURCC => Self::ICH4(reader.read_be_args(())?),
ICS8::FOURCC => Self::ICS8(reader.read_be_args(())?),
ICL8::FOURCC => Self::ICL8(reader.read_be_args(())?),
ICH8::FOURCC => Self::ICH8(reader.read_be_args(())?),
ICSN::FOURCC => Self::ICSN(reader.read_be_args((remaining,))?),
ICNN::FOURCC => Self::ICNN(reader.read_be_args((remaining,))?),
ICHN::FOURCC => Self::ICHN(reader.read_be_args((remaining,))?),
IS32::FOURCC => Self::IS32(reader.read_be_args((remaining,))?),
IL32::FOURCC => Self::IL32(reader.read_be_args((remaining,))?),
IH32::FOURCC => Self::IH32(reader.read_be_args((remaining,))?),
IT32::FOURCC => Self::IT32(reader.read_be_args((remaining,))?),
element_type => {
let mut data = vec![0u8; remaining];
reader.read_exact(&mut data)?;
Self::Unknown { element_type, data }
}
});
let end = pos + element_size as u64 - 8;
let current = reader.stream_position()?;
if end != current {
log::warn!(
"End position mismatch after reading {}, cursor is at {} but should be at {} ({} byte difference)",
element_type,
current,
end,
(current as i64) - (end as i64)
);
reader.seek(std::io::SeekFrom::Start(end))?;
}
result
}
}
impl IconFamilyElement {
fn fourcc(&self) -> FourCC {
match self {
IconFamilyElement::ICMN(_) => ICMN::FOURCC,
IconFamilyElement::ICM4(_) => ICM4::FOURCC,
IconFamilyElement::ICM8(_) => ICM8::FOURCC,
IconFamilyElement::ICS4(_) => ICS4::FOURCC,
IconFamilyElement::ICL4(_) => ICL4::FOURCC,
IconFamilyElement::ICH4(_) => ICH4::FOURCC,
IconFamilyElement::ICS8(_) => ICS8::FOURCC,
IconFamilyElement::ICL8(_) => ICL8::FOURCC,
IconFamilyElement::ICH8(_) => ICH8::FOURCC,
IconFamilyElement::ICSN(_) => ICSN::FOURCC,
IconFamilyElement::ICNN(_) => ICNN::FOURCC,
IconFamilyElement::ICHN(_) => ICHN::FOURCC,
IconFamilyElement::IS32(_) => IS32::FOURCC,
IconFamilyElement::IL32(_) => IL32::FOURCC,
IconFamilyElement::IH32(_) => IH32::FOURCC,
IconFamilyElement::IT32(_) => IT32::FOURCC,
IconFamilyElement::S8MK(_) => S8MK::FOURCC,
IconFamilyElement::L8MK(_) => L8MK::FOURCC,
IconFamilyElement::H8MK(_) => H8MK::FOURCC,
IconFamilyElement::T8MK(_) => T8MK::FOURCC,
IconFamilyElement::Unknown { element_type, .. } => *element_type,
}
}
pub fn decode(
&self,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
match self {
IconFamilyElement::S8MK(icon) => icon.decode(),
IconFamilyElement::L8MK(icon) => icon.decode(),
IconFamilyElement::H8MK(icon) => icon.decode(),
IconFamilyElement::T8MK(icon) => icon.decode(),
IconFamilyElement::ICMN(icon) => icon.decode(),
IconFamilyElement::ICM4(icon) => icon.decode(),
IconFamilyElement::ICM8(icon) => icon.decode(),
IconFamilyElement::ICS4(icon) => icon.decode(),
IconFamilyElement::ICL4(icon) => icon.decode(),
IconFamilyElement::ICH4(icon) => icon.decode(),
IconFamilyElement::ICS8(icon) => icon.decode(),
IconFamilyElement::ICL8(icon) => icon.decode(),
IconFamilyElement::ICH8(icon) => icon.decode(),
IconFamilyElement::ICSN(icon) => icon.decode(),
IconFamilyElement::ICNN(icon) => icon.decode(),
IconFamilyElement::ICHN(icon) => icon.decode(),
IconFamilyElement::IS32(icon) => icon.decode(),
IconFamilyElement::IL32(icon) => icon.decode(),
IconFamilyElement::IH32(icon) => icon.decode(),
IconFamilyElement::IT32(icon) => icon.decode(),
_ => {
log::error!(
"Decoding {} icons is not supported right now",
self.fourcc().name()
);
Err(packbits_rle::Error::TooMuchInputData)
}
}
}
}
#[binread]
#[derive(Debug, Resource)]
#[resource(code = "icns")]
#[br(big)]
pub struct IconFamily {
#[br(temp, assert(resource_type==fourcc!("icns")))]
resource_type: FourCC,
#[br(temp)]
_resource_size: u32,
#[br(parse_with = until_eof)]
icons: Vec<IconFamilyElement>,
}
impl IconFamily {
pub fn len(&self) -> usize {
self.icons.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn types(&self) -> Vec<FourCC> {
self.icons.iter().map(|t| t.fourcc()).collect()
}
pub fn fourcc(&self, idx: usize) -> FourCC {
self.icons[idx].fourcc()
}
pub fn decode(
&self,
item: usize,
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
self.icons[item].decode()
}
}
fn decode_byte_indexed_icon(
size: IconSize,
color_table: &[u32; 256],
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_byte_indexed_image(size.width(), size.height(), color_table, data)
}
fn decode_byte_indexed_image(
width: u32,
height: u32,
color_table: &[u32; 256],
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
let mut image = image::ImageBuffer::new(width, height);
let width = width as usize;
let height = height as usize;
for y in 0..height {
for x in 0..width {
let value = data[y * width + x];
let color = color_table[value as usize];
*image.get_pixel_mut(x as u32, y as u32) = image::Rgba([
((color & 0x00FF0000) >> 16) as u8,
((color & 0x0000FF00) >> 8) as u8,
(color & 0x0000FF) as u8,
0xFF_u8,
]);
}
}
Ok(image)
}
fn decode_nibble_indexed_icon(
icon: IconSize,
color_table: &[u32; 16],
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_nibble_indexed_image(icon.width(), icon.height(), color_table, data)
}
fn decode_nibble_indexed_image(
width: u32,
height: u32,
color_table: &[u32; 16],
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
let mut image = image::ImageBuffer::new(width, height);
let width = width as usize;
let height = height as usize;
if data.len() < height * width / 2 {
return Err(packbits_rle::Error::NotEnoughInputData);
}
for y in 0..height {
for x in 0..(width / 2) {
let value = data[y * width / 2 + x];
let left_color = (value & 0xF0) >> 4;
let right_color = value & 0x0F;
let color = color_table[left_color as usize];
*image.get_pixel_mut(2 * x as u32, y as u32) = image::Rgba([
((color & 0xFF0000) >> 16) as u8,
((color & 0x00FF00) >> 8) as u8,
(color & 0x0000FF) as u8,
0xFF_u8,
]);
let color = color_table[right_color as usize];
*image.get_pixel_mut(2 * x as u32 + 1, y as u32) = image::Rgba([
((color & 0xFF0000) >> 16) as u8,
((color & 0x00FF00) >> 8) as u8,
(color & 0x0000FF) as u8,
0xFF_u8,
]);
}
}
Ok(image)
}
fn decode_full_color_icon(
size: IconSize,
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_full_color_image(size.width(), size.height(), data)
}
fn decode_full_color_image(
width: u32,
height: u32,
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
let mut buffer = image::ImageBuffer::new(height, width);
let width = width as usize;
let height = height as usize;
let unpacked_size = width * height * 4;
let looks_uncompressed = data.len() >= unpacked_size;
if looks_uncompressed {
for (x, y, px) in buffer.enumerate_pixels_mut() {
let idx = y as usize * width + x as usize;
let _a = data[idx];
let r = data[idx + 1];
let g = data[idx + 2];
let b = data[idx + 3];
*px = image::Rgba([r, g, b, 0xFF])
}
} else {
let data = unpack_rgb_planes(data)?;
let plane_size = width * height;
if data.len() < plane_size * 3 {
return Err(packbits_rle::Error::NotEnoughInputData);
}
for (x, y, px) in buffer.enumerate_pixels_mut() {
let idx = y as usize * width + x as usize;
let r = data[idx];
let g = data[plane_size + idx];
let b = data[plane_size * 2 + idx];
*px = image::Rgba([r, g, b, 0xFF])
}
}
Ok(buffer)
}
fn unpack_rgb_planes(input: &[u8]) -> Result<Vec<u8>, packbits_rle::Error> {
let mut result = Vec::with_capacity(input.len() * 2);
let mut i = 0;
loop {
if i >= input.len() {
return Ok(result);
}
let cmd = input[i] as u16;
i += 1;
if cmd < 0x80 {
let chunk_size = cmd as usize + 1;
if i + chunk_size > input.len() {
return Err(packbits_rle::Error::NotEnoughInputData);
}
let chunk = &input[i..(i + chunk_size)];
result.extend_from_slice(chunk);
i += chunk_size;
} else {
let chunk_size = (cmd - 0x80 + 3) as usize;
let value = input[i];
for _ in 0..chunk_size {
result.push(value);
}
i += 1;
}
}
}
fn decode_bitmap_image_list(
width: u32,
height: u32,
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
let bytes_per_row = (width >> 3) as usize;
let size = data.len();
let image_byte_count = ((width >> 3) * height) as usize;
let image_count = size / image_byte_count;
if image_count == 2 {
let mut image = image::ImageBuffer::new(width, height);
for y in 0..height {
for x in (0..width).step_by(8) {
let mut row = data[y as usize * bytes_per_row + (x >> 3) as usize];
let mut mask_row =
data[y as usize * bytes_per_row + (x >> 3) as usize + image_byte_count];
let z_limit = if (x + 8) <= width { 8 } else { width - x };
for z in 0..z_limit {
let value: u8 = if (row & 0x80) != 0 { 0 } else { 0xff };
let mask_value: u8 = if (mask_row & 0x80) != 0 { 0xff } else { 0x00 };
row <<= 1;
mask_row <<= 1;
*image.get_pixel_mut(x + z, y) = image::Rgba([value, value, value, mask_value]);
}
}
}
Ok(image)
} else {
let mut image = image::ImageBuffer::new(width, height);
for y in 0..height {
for x in (0..width).step_by(8) {
let mut row = data[y as usize * bytes_per_row + (x >> 3) as usize];
let z_limit = if (x + 8) <= width { 8 } else { width - x };
for z in 0..z_limit {
let value: u8 = if (row & 0x80) != 0 { 0 } else { 0xff };
row <<= 1;
*image.get_pixel_mut(x + z, y) = image::Rgba([value, value, value, 0xFF]);
}
}
}
Ok(image)
}
}
fn decode_single_channel_icon(
size: IconSize,
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
decode_single_channel_image(size.width(), size.height(), data)
}
fn decode_single_channel_image(
width: u32,
height: u32,
data: &[u8],
) -> Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>, packbits_rle::Error> {
let mut buffer = image::ImageBuffer::new(width, height);
let width = width as usize;
for (x, y, px) in buffer.enumerate_pixels_mut() {
let b = data[y as usize * width + x as usize];
*px = image::Rgba([b, b, b, 0xFF])
}
Ok(buffer)
}