use std::fmt;
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum IconType {
Mono_32x32,
MonoA_32x32,
MonoA_16x12,
Palette4_16x12,
Palette8_16x12,
MonoA_16x16,
Palette4_16x16,
Palette8_16x16,
RGB24_16x16,
Mask8_16x16,
Palette4_32x32,
Palette8_32x32,
RGB24_32x32,
Mask8_32x32,
MonoA_48x48,
Palette4_48x48,
Palette8_48x48,
RGB24_48x48,
Mask8_48x48,
RGB24_128x128,
Mask8_128x128,
RGBA32_16x16,
RGBA32_16x16_2x,
RGBA32_32x32,
RGBA32_32x32_2x,
RGBA32_64x64,
RGBA32_128x128,
RGBA32_128x128_2x,
RGBA32_256x256,
RGBA32_256x256_2x,
RGBA32_512x512,
RGBA32_512x512_2x,
}
impl IconType {
pub fn from_ostype(ostype: OSType) -> Option<IconType> {
let OSType(raw_ostype) = ostype;
match &raw_ostype {
b"ICON" => Some(IconType::Mono_32x32),
b"ICN#" => Some(IconType::MonoA_32x32),
b"icm#" => Some(IconType::MonoA_16x12),
b"icm4" => Some(IconType::Palette4_16x12),
b"icm8" => Some(IconType::Palette8_16x12),
b"ics#" => Some(IconType::MonoA_16x16),
b"ics4" => Some(IconType::Palette4_16x16),
b"ics8" => Some(IconType::Palette8_16x16),
b"is32" => Some(IconType::RGB24_16x16),
b"s8mk" => Some(IconType::Mask8_16x16),
b"icl4" => Some(IconType::Palette4_32x32),
b"icl8" => Some(IconType::Palette8_32x32),
b"il32" => Some(IconType::RGB24_32x32),
b"l8mk" => Some(IconType::Mask8_32x32),
b"ich#" => Some(IconType::MonoA_48x48),
b"ich4" => Some(IconType::Palette4_48x48),
b"ich8" => Some(IconType::Palette8_48x48),
b"ih32" => Some(IconType::RGB24_48x48),
b"h8mk" => Some(IconType::Mask8_48x48),
b"it32" => Some(IconType::RGB24_128x128),
b"t8mk" => Some(IconType::Mask8_128x128),
b"icp4" => Some(IconType::RGBA32_16x16),
b"ic11" => Some(IconType::RGBA32_16x16_2x),
b"icp5" => Some(IconType::RGBA32_32x32),
b"ic12" => Some(IconType::RGBA32_32x32_2x),
b"icp6" => Some(IconType::RGBA32_64x64),
b"ic07" => Some(IconType::RGBA32_128x128),
b"ic13" => Some(IconType::RGBA32_128x128_2x),
b"ic08" => Some(IconType::RGBA32_256x256),
b"ic14" => Some(IconType::RGBA32_256x256_2x),
b"ic09" => Some(IconType::RGBA32_512x512),
b"ic10" => Some(IconType::RGBA32_512x512_2x),
_ => None,
}
}
pub fn from_pixel_size(width: u32, height: u32) -> Option<IconType> {
match (width, height) {
(16, 12) => Some(IconType::MonoA_16x12),
(16, 16) => Some(IconType::RGB24_16x16),
(32, 32) => Some(IconType::RGB24_32x32),
(48, 48) => Some(IconType::RGB24_48x48),
(64, 64) => Some(IconType::RGBA32_64x64),
(128, 128) => Some(IconType::RGB24_128x128),
(256, 256) => Some(IconType::RGBA32_256x256),
(512, 512) => Some(IconType::RGBA32_512x512),
(1024, 1024) => Some(IconType::RGBA32_512x512_2x),
_ => None,
}
}
pub fn from_pixel_size_and_density(width: u32,
height: u32,
density: u32)
-> Option<IconType> {
match (width, height, density) {
(16, 12, 1) => Some(IconType::MonoA_16x12),
(16, 16, 1) => Some(IconType::RGB24_16x16),
(32, 32, 1) => Some(IconType::RGB24_32x32),
(32, 32, 2) => Some(IconType::RGBA32_16x16_2x),
(48, 48, 1) => Some(IconType::RGB24_48x48),
(64, 64, 1) => Some(IconType::RGBA32_64x64),
(64, 64, 2) => Some(IconType::RGBA32_32x32_2x),
(128, 128, 1) => Some(IconType::RGB24_128x128),
(256, 256, 1) => Some(IconType::RGBA32_256x256),
(256, 256, 2) => Some(IconType::RGBA32_128x128_2x),
(512, 512, 1) => Some(IconType::RGBA32_512x512),
(512, 512, 2) => Some(IconType::RGBA32_256x256_2x),
(1024, 1024, 2) => Some(IconType::RGBA32_512x512_2x),
_ => None,
}
}
pub fn ostype(self) -> OSType {
match self {
IconType::Mono_32x32 => OSType(*b"ICON"),
IconType::MonoA_32x32 => OSType(*b"ICN#"),
IconType::MonoA_16x12 => OSType(*b"icm#"),
IconType::Palette4_16x12 => OSType(*b"icm4"),
IconType::Palette8_16x12 => OSType(*b"icm8"),
IconType::MonoA_16x16 => OSType(*b"ics#"),
IconType::Palette4_16x16 => OSType(*b"ics4"),
IconType::Palette8_16x16 => OSType(*b"ics8"),
IconType::RGB24_16x16 => OSType(*b"is32"),
IconType::Mask8_16x16 => OSType(*b"s8mk"),
IconType::Palette4_32x32 => OSType(*b"icl4"),
IconType::Palette8_32x32 => OSType(*b"icl8"),
IconType::RGB24_32x32 => OSType(*b"il32"),
IconType::Mask8_32x32 => OSType(*b"l8mk"),
IconType::MonoA_48x48 => OSType(*b"ich#"),
IconType::Palette4_48x48 => OSType(*b"ich4"),
IconType::Palette8_48x48 => OSType(*b"ich8"),
IconType::RGB24_48x48 => OSType(*b"ih32"),
IconType::Mask8_48x48 => OSType(*b"h8mk"),
IconType::RGB24_128x128 => OSType(*b"it32"),
IconType::Mask8_128x128 => OSType(*b"t8mk"),
IconType::RGBA32_16x16 => OSType(*b"icp4"),
IconType::RGBA32_16x16_2x => OSType(*b"ic11"),
IconType::RGBA32_32x32 => OSType(*b"icp5"),
IconType::RGBA32_32x32_2x => OSType(*b"ic12"),
IconType::RGBA32_64x64 => OSType(*b"icp6"),
IconType::RGBA32_128x128 => OSType(*b"ic07"),
IconType::RGBA32_128x128_2x => OSType(*b"ic13"),
IconType::RGBA32_256x256 => OSType(*b"ic08"),
IconType::RGBA32_256x256_2x => OSType(*b"ic14"),
IconType::RGBA32_512x512 => OSType(*b"ic09"),
IconType::RGBA32_512x512_2x => OSType(*b"ic10"),
}
}
pub fn is_mask(self) -> bool {
matches!(self,
IconType::MonoA_16x12 |
IconType::MonoA_16x16 |
IconType::MonoA_32x32 |
IconType::MonoA_48x48 |
IconType::Mask8_16x16 |
IconType::Mask8_32x32 |
IconType::Mask8_48x48 |
IconType::Mask8_128x128)
}
pub fn mask_type(self) -> Option<IconType> {
match self {
IconType::Palette4_16x12 | IconType::Palette8_16x12 => {
Some(IconType::MonoA_16x12)
}
IconType::Palette4_16x16 | IconType::Palette8_16x16 => {
Some(IconType::MonoA_16x16)
}
IconType::RGB24_16x16 => Some(IconType::Mask8_16x16),
IconType::Palette4_32x32 | IconType::Palette8_32x32 => {
Some(IconType::MonoA_32x32)
}
IconType::RGB24_32x32 => Some(IconType::Mask8_32x32),
IconType::Palette4_48x48 | IconType::Palette8_48x48 => {
Some(IconType::MonoA_48x48)
}
IconType::RGB24_48x48 => Some(IconType::Mask8_48x48),
IconType::RGB24_128x128 => Some(IconType::Mask8_128x128),
_ => None,
}
}
pub fn pixel_width(self) -> u32 {
self.screen_width() * self.pixel_density()
}
pub fn pixel_height(self) -> u32 {
self.screen_height() * self.pixel_density()
}
pub fn pixel_density(self) -> u32 {
match self {
IconType::RGBA32_16x16_2x |
IconType::RGBA32_32x32_2x |
IconType::RGBA32_128x128_2x |
IconType::RGBA32_256x256_2x |
IconType::RGBA32_512x512_2x => 2,
_ => 1,
}
}
pub fn screen_width(self) -> u32 {
match self {
IconType::Mono_32x32 => 32,
IconType::MonoA_32x32 => 32,
IconType::MonoA_16x12 => 16,
IconType::Palette4_16x12 => 16,
IconType::Palette8_16x12 => 16,
IconType::MonoA_16x16 => 16,
IconType::Palette4_16x16 => 16,
IconType::Palette8_16x16 => 16,
IconType::RGB24_16x16 => 16,
IconType::Mask8_16x16 => 16,
IconType::Palette4_32x32 => 32,
IconType::Palette8_32x32 => 32,
IconType::RGB24_32x32 => 32,
IconType::Mask8_32x32 => 32,
IconType::MonoA_48x48 => 48,
IconType::Palette4_48x48 => 48,
IconType::Palette8_48x48 => 48,
IconType::RGB24_48x48 => 48,
IconType::Mask8_48x48 => 48,
IconType::RGB24_128x128 => 128,
IconType::Mask8_128x128 => 128,
IconType::RGBA32_16x16 => 16,
IconType::RGBA32_16x16_2x => 16,
IconType::RGBA32_32x32 => 32,
IconType::RGBA32_32x32_2x => 32,
IconType::RGBA32_64x64 => 64,
IconType::RGBA32_128x128 => 128,
IconType::RGBA32_128x128_2x => 128,
IconType::RGBA32_256x256 => 256,
IconType::RGBA32_256x256_2x => 256,
IconType::RGBA32_512x512 => 512,
IconType::RGBA32_512x512_2x => 512,
}
}
pub fn screen_height(self) -> u32 {
match self {
IconType::Mono_32x32 => 32,
IconType::MonoA_32x32 => 32,
IconType::MonoA_16x12 => 12,
IconType::Palette4_16x12 => 12,
IconType::Palette8_16x12 => 12,
IconType::MonoA_16x16 => 16,
IconType::Palette4_16x16 => 16,
IconType::Palette8_16x16 => 16,
IconType::RGB24_16x16 => 16,
IconType::Mask8_16x16 => 16,
IconType::Palette4_32x32 => 32,
IconType::Palette8_32x32 => 32,
IconType::RGB24_32x32 => 32,
IconType::Mask8_32x32 => 32,
IconType::Palette4_48x48 => 48,
IconType::Palette8_48x48 => 48,
IconType::MonoA_48x48 => 48,
IconType::RGB24_48x48 => 48,
IconType::Mask8_48x48 => 48,
IconType::RGB24_128x128 => 128,
IconType::Mask8_128x128 => 128,
IconType::RGBA32_16x16 => 16,
IconType::RGBA32_16x16_2x => 16,
IconType::RGBA32_32x32 => 32,
IconType::RGBA32_32x32_2x => 32,
IconType::RGBA32_64x64 => 64,
IconType::RGBA32_128x128 => 128,
IconType::RGBA32_128x128_2x => 128,
IconType::RGBA32_256x256 => 256,
IconType::RGBA32_256x256_2x => 256,
IconType::RGBA32_512x512 => 512,
IconType::RGBA32_512x512_2x => 512,
}
}
pub fn encoding(self) -> Encoding {
match self {
IconType::Mono_32x32 => Encoding::Mono,
IconType::MonoA_32x32 |
IconType::MonoA_16x12 |
IconType::MonoA_16x16 |
IconType::MonoA_48x48 => Encoding::MonoA,
IconType::Palette4_16x12 |
IconType::Palette4_16x16 |
IconType::Palette4_32x32 |
IconType::Palette4_48x48 => Encoding::Palette4,
IconType::Palette8_16x12 |
IconType::Palette8_16x16 |
IconType::Palette8_32x32 |
IconType::Palette8_48x48 => Encoding::Palette8,
IconType::RGB24_16x16 |
IconType::RGB24_32x32 |
IconType::RGB24_48x48 |
IconType::RGB24_128x128 => Encoding::RLE24,
IconType::Mask8_16x16 |
IconType::Mask8_32x32 |
IconType::Mask8_48x48 |
IconType::Mask8_128x128 => Encoding::Mask8,
IconType::RGBA32_16x16 |
IconType::RGBA32_16x16_2x |
IconType::RGBA32_32x32 |
IconType::RGBA32_32x32_2x |
IconType::RGBA32_64x64 |
IconType::RGBA32_128x128 |
IconType::RGBA32_128x128_2x |
IconType::RGBA32_256x256 |
IconType::RGBA32_256x256_2x |
IconType::RGBA32_512x512 |
IconType::RGBA32_512x512_2x => Encoding::JP2PNG,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct OSType(pub [u8; 4]);
impl fmt::Display for OSType {
fn fmt(&self, out: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let &OSType(raw) = self;
for &byte in &raw {
let character = std::char::from_u32(u32::from(byte)).unwrap();
write!(out, "{}", character)?;
}
Ok(())
}
}
impl std::str::FromStr for OSType {
type Err = String;
fn from_str(input: &str) -> Result<OSType, String> {
let chars: Vec<char> = input.chars().collect();
if chars.len() != 4 {
return Err(format!("OSType string must be 4 chars (was {})",
chars.len()));
}
let mut bytes = [0u8; 4];
for (i, &ch) in chars.iter().enumerate() {
let value = ch as u32;
if value > u8::MAX as u32 {
return Err(format!("OSType chars must have value of at \
most 0x{:X} (found 0x{:X})",
u8::MAX,
value));
}
bytes[i] = value as u8;
}
Ok(OSType(bytes))
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Encoding {
Mono,
MonoA,
Palette4,
Palette8,
Mask8,
RLE24,
JP2PNG,
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
const ALL_ICON_TYPES: [IconType; 32] = [
IconType::Mono_32x32,
IconType::MonoA_32x32,
IconType::MonoA_16x12,
IconType::Palette4_16x12,
IconType::Palette8_16x12,
IconType::MonoA_16x16,
IconType::Palette4_16x16,
IconType::Palette8_16x16,
IconType::RGB24_16x16,
IconType::Mask8_16x16,
IconType::Palette4_32x32,
IconType::Palette8_32x32,
IconType::RGB24_32x32,
IconType::Mask8_32x32,
IconType::MonoA_48x48,
IconType::Palette4_48x48,
IconType::Palette8_48x48,
IconType::RGB24_48x48,
IconType::Mask8_48x48,
IconType::RGB24_128x128,
IconType::Mask8_128x128,
IconType::RGBA32_16x16,
IconType::RGBA32_16x16_2x,
IconType::RGBA32_32x32,
IconType::RGBA32_32x32_2x,
IconType::RGBA32_64x64,
IconType::RGBA32_128x128,
IconType::RGBA32_128x128_2x,
IconType::RGBA32_256x256,
IconType::RGBA32_256x256_2x,
IconType::RGBA32_512x512,
IconType::RGBA32_512x512_2x,
];
#[test]
fn icon_type_ostype_round_trip() {
for icon_type in &ALL_ICON_TYPES {
let ostype = icon_type.ostype();
let from = IconType::from_ostype(ostype);
assert_eq!(Some(*icon_type), from);
}
}
#[test]
fn icon_type_size_round_trip() {
for icon_type in &ALL_ICON_TYPES {
let width = icon_type.pixel_width();
let height = icon_type.pixel_height();
let from = IconType::from_pixel_size(width, height).unwrap();
assert_eq!(from.pixel_width(), width);
assert_eq!(from.pixel_height(), height);
}
}
#[test]
fn icon_type_size_and_density_round_trip() {
for icon_type in &ALL_ICON_TYPES {
let width = icon_type.pixel_width();
let height = icon_type.pixel_height();
let density = icon_type.pixel_density();
let from =
IconType::from_pixel_size_and_density(width, height, density)
.unwrap();
assert_eq!(from.pixel_width(), width);
assert_eq!(from.pixel_height(), height);
assert_eq!(from.pixel_density(), density);
}
}
#[test]
fn icon_type_mask_type() {
for icon_type in &ALL_ICON_TYPES {
match icon_type.encoding() {
Encoding::MonoA | Encoding::Mask8 => {
assert!(icon_type.is_mask());
assert_eq!(icon_type.mask_type(), None);
}
Encoding::RLE24 => {
assert!(!icon_type.is_mask());
if let Some(mask_type) = icon_type.mask_type() {
assert_eq!(mask_type.encoding(), Encoding::Mask8);
assert_eq!(icon_type.pixel_width(),
mask_type.pixel_width());
assert_eq!(icon_type.pixel_height(),
mask_type.pixel_height());
} else {
panic!("{:?} is missing a mask type", icon_type);
}
}
Encoding::Mono | Encoding::JP2PNG => {
assert!(!icon_type.is_mask());
assert_eq!(icon_type.mask_type(), None);
}
Encoding::Palette4 | Encoding::Palette8 => {
assert!(!icon_type.is_mask());
if let Some(mask_type) = icon_type.mask_type() {
assert_eq!(mask_type.encoding(), Encoding::MonoA);
assert_eq!(
icon_type.pixel_width(),
mask_type.pixel_width()
);
assert_eq!(
icon_type.pixel_height(),
mask_type.pixel_height()
);
} else {
panic!("{:?} is missing a mask type", icon_type);
}
}
}
}
}
#[test]
fn ostype_to_and_from_str() {
let ostype = OSType::from_str("abcd").expect("failed to parse OSType");
assert_eq!(ostype.to_string(), "abcd".to_string());
}
#[test]
fn ostype_to_and_from_str_non_ascii() {
let ostype = OSType(*b"sp\xf6b");
let string = ostype.to_string();
assert_eq!(string, "sp\u{f6}b".to_string());
assert_eq!(OSType::from_str(&string), Ok(ostype));
}
#[test]
fn ostype_from_str_failure() {
assert_eq!(OSType::from_str("abc"),
Err("OSType string must be 4 chars (was 3)".to_string()));
assert_eq!(OSType::from_str("abcde"),
Err("OSType string must be 4 chars (was 5)".to_string()));
assert_eq!(OSType::from_str("ab\u{2603}d"),
Err("OSType chars must have value of at most 0xFF \
(found 0x2603)"
.to_string()));
}
}