use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::cmp;
use std::io::{self, Error, ErrorKind, Read, Write};
use super::icontype::{Encoding, IconType, OSType};
use super::image::{Image, PixelFormat};
use super::palette::{PALETTE16, PALETTE256};
const ICON_ELEMENT_HEADER_LENGTH: u32 = 8;
const JPEG_2000_FILE_MAGIC_NUMBER: [u8; 12] = [
0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
];
const PNG_FILE_MAGIC_NUMBER: [u8; 8] =
[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
pub struct IconElement {
pub ostype: OSType,
pub data: Vec<u8>,
}
impl IconElement {
pub fn new(ostype: OSType, data: Vec<u8>) -> IconElement {
IconElement { ostype, data }
}
pub fn encode_image_with_type(image: &Image,
icon_type: IconType)
-> io::Result<IconElement> {
let width = icon_type.pixel_width();
let height = icon_type.pixel_height();
if image.width() != width || image.height() != height {
let msg = format!("image has wrong dimensions for {:?} ({}x{} \
instead of {}x{}))",
icon_type,
image.width(),
image.height(),
width,
height);
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let mut data: Vec<u8>;
match icon_type.encoding() {
#[cfg(feature = "pngio")]
Encoding::JP2PNG => {
data = Vec::new();
image.write_png(&mut data)?;
}
#[cfg(not(feature = "pngio"))]
Encoding::JP2PNG => unimplemented!(),
Encoding::RLE24 => {
let num_pixels = (width * height) as usize;
match image.pixel_format() {
PixelFormat::RGBA => {
data = encode_rle(image.data(), 4, num_pixels);
}
PixelFormat::RGB => {
data = encode_rle(image.data(), 3, num_pixels);
}
_ => {
let image = image.convert_to(PixelFormat::RGB);
data = encode_rle(image.data(), 3, num_pixels);
}
}
}
Encoding::Mask8 => {
let image = image.convert_to(PixelFormat::Alpha);
data = image.into_data().into_vec();
}
Encoding::Mono => {
let image = image.convert_to(PixelFormat::Gray);
assert!(image.data().len().is_multiple_of(8));
data = vec![0; image.data.len() / 8];
for (i, e) in image.into_data().iter().enumerate() {
if *e < 128 {
data[i / 8] |= 1 << (7 - (i % 8));
}
}
}
Encoding::MonoA => {
let image = image.convert_to(PixelFormat::GrayAlpha);
assert!(image.data().len().is_multiple_of(16));
data = vec![0; image.data.len() / 8];
let (mono, alpha) = data.split_at_mut(image.data.len() / 16);
for (i, e) in image.into_data().chunks_exact(2).enumerate() {
if e[0] < 128 {
mono[i / 8] |= 1 << (7 - (i % 8));
}
if e[1] >= 128 {
alpha[i / 8] |= 1 << (7 - (i % 8));
}
}
}
Encoding::Palette4 => {
let image = image.convert_to(PixelFormat::RGB);
data = vec![0; image.data.len() / 6];
for (i, pix) in image.data().chunks_exact(3).enumerate() {
let pix: [u8; 3] = [pix[0], pix[1], pix[2]];
let best_index = nearest_match(pix, &PALETTE16[..]);
data[i / 2] |= best_index << (4 * (1 - (i % 2)));
}
}
Encoding::Palette8 => {
let image = image.convert_to(PixelFormat::RGB);
data = vec![0; image.data.len() / 3];
for (i, pix) in image.data().chunks_exact(3).enumerate() {
let pix: [u8; 3] = [pix[0], pix[1], pix[2]];
let best_index = nearest_match(pix, &PALETTE256[..]);
data[i] = best_index;
}
}
}
Ok(IconElement::new(icon_type.ostype(), data))
}
pub fn decode_image(&self) -> io::Result<Image> {
let icon_type = self.icon_type().ok_or_else(|| {
Error::new(ErrorKind::InvalidInput,
format!("unsupported OSType: {}", self.ostype))
})?;
let width = icon_type.pixel_width();
let height = icon_type.pixel_height();
match icon_type.encoding() {
Encoding::JP2PNG => {
#[cfg(feature = "jp2io")]
if self.data.starts_with(&JPEG_2000_FILE_MAGIC_NUMBER) {
let image = Image::read_jp2(&self.data)?;
if image.width() != width || image.height() != height {
let msg = format!(
"decoded JPEG 2000 has wrong dimensions \
({}x{} instead of {}x{})",
image.width(),
image.height(),
width,
height
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
return Ok(image);
}
#[cfg(not(feature = "jp2io"))]
if self.data.starts_with(&JPEG_2000_FILE_MAGIC_NUMBER) {
let msg = "element to be decoded contains JPEG 2000 \
data, which requires the feature `jp2io` to be decoded";
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
#[cfg(feature = "pngio")]
if self.data.starts_with(&PNG_FILE_MAGIC_NUMBER) {
let image = Image::read_png(io::Cursor::new(&self.data))?;
if image.width() != width || image.height() != height {
let msg = format!(
"decoded PNG has wrong dimensions \
({}x{} instead of {}x{})",
image.width(),
image.height(),
width,
height
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
return Ok(image);
}
#[cfg(not(feature = "pngio"))]
if self.data.starts_with(&PNG_FILE_MAGIC_NUMBER) {
let msg = "element to be decoded contains PNG \
data, which requires the feature `pngio` to be decoded";
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let msg = "element to be decoded contains neither JPEG 2000 \
nor PNG data";
Err(Error::new(ErrorKind::InvalidInput, msg))
}
Encoding::RLE24 => {
let mut image = Image::new(PixelFormat::RGB, width, height);
decode_rle(&self.data, 3, image.data_mut())?;
Ok(image)
}
Encoding::Mask8 => {
let num_pixels = width * height;
if self.data.len() != num_pixels as usize {
let msg = format!(
"wrong data payload length ({} \
instead of {})",
self.data.len(),
num_pixels
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
let mut image = Image::new(PixelFormat::Alpha, width, height);
image.data_mut().copy_from_slice(&self.data);
Ok(image)
}
Encoding::Mono => {
assert!((width * height).is_multiple_of(8));
let num_bytes = (width * height) / 8;
if self.data.len() != num_bytes as usize {
let msg = format!(
"wrong data payload length ({} \
instead of {})",
self.data.len(),
num_bytes
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
let mut image = Image::new(PixelFormat::Gray, width, height);
let out = image.data_mut();
for (i, b) in self.data.iter().enumerate() {
for d in 0..8 {
out[8 * i + d] = 0xff - 0xff * ((b >> (7 - d)) & 0x1)
}
}
Ok(image)
}
Encoding::MonoA => {
assert!((width * height).is_multiple_of(8));
let num_bytes = (width * height) / 4;
if self.data.len() != num_bytes as usize {
let msg = format!(
"wrong data payload length ({} \
instead of {})",
self.data.len(),
num_bytes
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
let mut image =
Image::new(PixelFormat::GrayAlpha, width, height);
let out = image.data_mut();
let (mono, alpha) =
self.data.split_at((num_bytes / 2) as usize);
for (i, b) in mono.iter().enumerate() {
for d in 0..8 {
out[16 * i + 2 * d] =
0xff - 0xff * ((b >> (7 - d)) & 0x1)
}
}
for (i, b) in alpha.iter().enumerate() {
for d in 0..8 {
out[16 * i + 2 * d + 1] = 0xff * ((b >> (7 - d)) & 0x1)
}
}
Ok(image)
}
Encoding::Palette4 => {
let num_bytes = (width * height) / 2;
if self.data.len() != num_bytes as usize {
let msg = format!(
"wrong data payload length ({} instead of {})",
self.data.len(),
num_bytes
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
let mut image = Image::new(PixelFormat::RGB, width, height);
let out = image.data_mut();
for (i, b) in self.data.iter().enumerate() {
let (hi, lo) = ((*b >> 4) & 0xf, *b & 0xf);
out[6 * i..6 * i + 3]
.copy_from_slice(&PALETTE16[hi as usize]);
out[6 * i + 3..6 * i + 6]
.copy_from_slice(&PALETTE16[lo as usize]);
}
Ok(image)
}
Encoding::Palette8 => {
let num_bytes = width * height;
if self.data.len() != num_bytes as usize {
let msg = format!(
"wrong data payload length ({} instead of {})",
self.data.len(),
num_bytes
);
return Err(Error::new(ErrorKind::InvalidData, msg));
}
let mut image = Image::new(PixelFormat::RGB, width, height);
let out = image.data_mut();
for (o, b) in out.chunks_exact_mut(3).zip(self.data.iter()) {
o.copy_from_slice(&PALETTE256[*b as usize]);
}
Ok(image)
}
}
}
pub fn decode_image_with_mask(&self,
mask: &IconElement)
-> io::Result<Image> {
let icon_type = self.icon_type().ok_or_else(|| {
Error::new(ErrorKind::InvalidInput,
format!("unsupported OSType: {}", self.ostype))
})?;
let mask_type = icon_type.mask_type().ok_or_else(|| {
let msg = format!("icon type {:?} does not use a mask", icon_type);
Error::new(ErrorKind::InvalidInput, msg)
})?;
if mask.ostype != mask_type.ostype() {
let msg = format!("wrong OSType for mask ('{}' instead of '{}')",
mask.ostype,
mask_type.ostype());
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let width = icon_type.pixel_width();
let height = icon_type.pixel_height();
match mask_type.encoding() {
Encoding::Mask8 => {
let num_pixels = (width * height) as usize;
if mask.data.len() != num_pixels {
let msg = format!(
"wrong mask data payload length ({} instead \
of {})",
mask.data.len(),
num_pixels
);
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let mut image = Image::new(PixelFormat::RGBA, width, height);
decode_rle(&self.data, 4, image.data_mut())?;
for (i, &alpha) in mask.data.iter().enumerate() {
image.data_mut()[4 * i + 3] = alpha;
}
Ok(image)
}
Encoding::MonoA => {
let num_bytes = ((width * height) / 4) as usize;
if mask.data.len() != num_bytes {
let msg = format!(
"wrong mask data payload length ({} instead of {})",
mask.data.len(),
num_bytes
);
return Err(Error::new(ErrorKind::InvalidInput, msg));
}
let image = self.decode_image()?;
let mut image = image.convert_to(PixelFormat::RGBA);
let (_, alpha) = mask.data.split_at(num_bytes / 2);
let image_data = image.data_mut();
for (i, b) in alpha.iter().enumerate() {
for d in 0..8 {
image_data[32 * i + 4 * d + 3] =
0xff * ((b >> (7 - d)) & 0x1)
}
}
Ok(image)
}
_ => unreachable!(),
}
}
pub fn icon_type(&self) -> Option<IconType> {
IconType::from_ostype(self.ostype)
}
pub fn read<R: Read>(mut reader: R) -> io::Result<IconElement> {
let mut raw_ostype = [0u8; 4];
reader.read_exact(&mut raw_ostype)?;
let element_length = reader.read_u32::<BigEndian>()?;
if element_length < ICON_ELEMENT_HEADER_LENGTH {
return Err(Error::new(ErrorKind::InvalidData,
"invalid element length"));
}
let data_length = element_length - ICON_ELEMENT_HEADER_LENGTH;
let mut data = vec![0u8; data_length as usize];
reader.read_exact(&mut data)?;
Ok(IconElement::new(OSType(raw_ostype), data))
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
let OSType(ref raw_ostype) = self.ostype;
writer.write_all(raw_ostype)?;
writer.write_u32::<BigEndian>(self.total_length())?;
writer.write_all(&self.data)?;
Ok(())
}
pub fn total_length(&self) -> u32 {
ICON_ELEMENT_HEADER_LENGTH + (self.data.len() as u32)
}
}
fn encode_rle(input: &[u8],
num_input_channels: usize,
num_pixels: usize)
-> Vec<u8> {
assert!(num_input_channels == 3 || num_input_channels == 4);
let mut output = Vec::new();
if num_pixels == 128 * 128 {
output.extend_from_slice(&[0, 0, 0, 0]);
}
for channel in 0..3 {
let mut pixel: usize = 0;
let mut literal_start: usize = 0;
while pixel < num_pixels {
let value = input[num_input_channels * pixel + channel];
let mut run_length = 1;
while pixel + run_length < num_pixels &&
input[num_input_channels * (pixel + run_length) +
channel] == value && run_length < 130 {
run_length += 1;
}
if run_length >= 3 {
while literal_start < pixel {
let literal_length = cmp::min(128, pixel - literal_start);
output.push((literal_length - 1) as u8);
for i in 0..literal_length {
output.push(input[num_input_channels *
(literal_start + i) +
channel]);
}
literal_start += literal_length;
}
output.push((run_length + 125) as u8);
output.push(value);
pixel += run_length;
literal_start = pixel;
} else {
pixel += run_length;
}
}
while literal_start < pixel {
let literal_length = cmp::min(128, pixel - literal_start);
output.push((literal_length - 1) as u8);
for i in 0..literal_length {
output.push(input[num_input_channels * (literal_start + i) +
channel]);
}
literal_start += literal_length;
}
}
output
}
fn decode_rle(input: &[u8],
num_output_channels: usize,
output: &mut [u8])
-> io::Result<()> {
assert!(num_output_channels == 3 || num_output_channels == 4);
assert_eq!(output.len() % num_output_channels, 0);
let num_pixels = output.len() / num_output_channels;
let skip: usize = if input.starts_with(&[0, 0, 0, 0]) {
4
} else {
0
};
let input = &input[skip..input.len()];
let mut iter = input.iter();
let mut remaining: usize = 0;
let mut within_run = false;
let mut run_value: u8 = 0;
for channel in 0..3 {
for pixel in 0..num_pixels {
if remaining == 0 {
let next: u8 = *iter.next().ok_or_else(rle_error)?;
if next < 128 {
remaining = (next as usize) + 1;
within_run = false;
} else {
remaining = (next as usize) - 125;
within_run = true;
run_value = *iter.next().ok_or_else(rle_error)?;
}
}
output[num_output_channels * pixel + channel] = if within_run {
run_value
} else {
*iter.next().ok_or_else(rle_error)?
};
remaining -= 1;
}
if remaining != 0 {
return Err(rle_error());
}
}
if iter.next().is_some() {
Err(rle_error())
} else {
Ok(())
}
}
fn rle_error() -> Error {
Error::new(ErrorKind::InvalidData, "invalid RLE-compressed data")
}
fn nearest_match(rgb: [u8; 3], palette: &[[u8; 3]]) -> u8 {
fn color_distance(rgb: [u8; 3], pal: [u8; 3]) -> u16 {
u16::from(rgb[0].abs_diff(pal[0]))
+ u16::from(rgb[1].abs_diff(pal[1]))
+ u16::from(rgb[2].abs_diff(pal[2]))
}
let mut best_idx = 0;
let mut best_dist = color_distance(rgb, palette[0]);
for (i, c) in palette.iter().enumerate().skip(1) {
let dist = color_distance(rgb, *c);
if dist < best_dist {
best_idx = i;
best_dist = dist;
}
}
best_idx as u8
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::icontype::{IconType, OSType};
use super::super::image::{Image, PixelFormat};
#[test]
fn encode_rle() {
let mut image = Image::new(PixelFormat::Gray, 16, 16);
image.data_mut()[0] = 44;
image.data_mut()[1] = 55;
image.data_mut()[2] = 66;
image.data_mut()[3] = 66;
image.data_mut()[4] = 66;
let element =
IconElement::encode_image_with_type(&image, IconType::RGB24_16x16)
.expect("failed to encode image");
assert_eq!(element.ostype, OSType(*b"is32"));
assert_eq!(element.data[0..5], [1, 44, 55, 128, 66]);
}
#[test]
fn decode_rle() {
let data: Vec<u8> = vec![0, 12, 255, 0, 250, 0, 128, 34, 255, 0, 248,
0, 1, 56, 99, 255, 0, 249, 0];
let element = IconElement::new(OSType(*b"is32"), data);
let image = element.decode_image().expect("failed to decode image");
assert_eq!(image.pixel_format(), PixelFormat::RGB);
assert_eq!(image.width(), 16);
assert_eq!(image.height(), 16);
assert_eq!(image.data()[0], 12);
assert_eq!(image.data()[1], 34);
assert_eq!(image.data()[2], 56);
}
#[test]
fn decode_rle_skip_extra_zeros() {
let data: Vec<u8> = vec![0, 0, 0, 0, 0, 12, 255, 0, 250, 0, 128, 34,
255, 0, 248, 0, 1, 56, 99, 255, 0, 249, 0];
let element = IconElement::new(OSType(*b"is32"), data);
let image = element.decode_image().expect("failed to decode image");
assert_eq!(image.data()[0], 12);
assert_eq!(image.data()[1], 34);
assert_eq!(image.data()[2], 56);
}
#[test]
fn encode_mask() {
let mut image = Image::new(PixelFormat::Alpha, 16, 16);
image.data_mut()[2] = 127;
let element =
IconElement::encode_image_with_type(&image, IconType::Mask8_16x16)
.expect("failed to encode image");
assert_eq!(element.ostype, OSType(*b"s8mk"));
assert_eq!(element.data[2], 127);
}
#[test]
fn decode_mask() {
let mut data = vec![0u8; 256];
data[2] = 127;
let element = IconElement::new(OSType(*b"s8mk"), data);
let image = element.decode_image().expect("failed to decode image");
assert_eq!(image.pixel_format(), PixelFormat::Alpha);
assert_eq!(image.width(), 16);
assert_eq!(image.height(), 16);
assert_eq!(image.data()[2], 127);
}
#[test]
fn decode_rle_with_mask() {
let color_data: Vec<u8> = vec![0, 12, 255, 0, 250, 0, 128, 34, 255,
0, 248, 0, 1, 56, 99, 255, 0, 249, 0];
let color_element = IconElement::new(OSType(*b"is32"), color_data);
let mask_data = vec![78u8; 256];
let mask_element = IconElement::new(OSType(*b"s8mk"), mask_data);
let image = color_element.decode_image_with_mask(&mask_element)
.expect("failed to decode image");
assert_eq!(image.pixel_format(), PixelFormat::RGBA);
assert_eq!(image.width(), 16);
assert_eq!(image.height(), 16);
assert_eq!(image.data()[0], 12);
assert_eq!(image.data()[1], 34);
assert_eq!(image.data()[2], 56);
assert_eq!(image.data()[3], 78);
}
}