use crate::filter;
use std::{convert::TryFrom, fmt};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ColorType {
Grayscale = 0,
RGB = 2,
Indexed = 3,
GrayscaleAlpha = 4,
RGBA = 6,
}
impl ColorType {
pub fn samples(&self) -> usize {
self.samples_u8().into()
}
pub(crate) fn samples_u8(self) -> u8 {
use self::ColorType::*;
match self {
Grayscale | Indexed => 1,
RGB => 3,
GrayscaleAlpha => 2,
RGBA => 4,
}
}
pub fn from_u8(n: u8) -> Option<ColorType> {
match n {
0 => Some(ColorType::Grayscale),
2 => Some(ColorType::RGB),
3 => Some(ColorType::Indexed),
4 => Some(ColorType::GrayscaleAlpha),
6 => Some(ColorType::RGBA),
_ => None,
}
}
pub(crate) fn checked_raw_row_length(&self, depth: BitDepth, width: u32) -> Option<usize> {
let bits = u64::from(width) * u64::from(self.samples_u8()) * u64::from(depth.into_u8());
TryFrom::try_from(1 + (bits + 7) / 8).ok()
}
pub(crate) fn raw_row_length_from_width(&self, depth: BitDepth, width: u32) -> usize {
let samples = width as usize * self.samples();
1 + match depth {
BitDepth::Sixteen => samples * 2,
BitDepth::Eight => samples,
subbyte => {
let samples_per_byte = 8 / subbyte as usize;
let whole = samples / samples_per_byte;
let fract = usize::from(samples % samples_per_byte > 0);
whole + fract
}
}
}
pub(crate) fn is_combination_invalid(self, bit_depth: BitDepth) -> bool {
((bit_depth == BitDepth::One || bit_depth == BitDepth::Two || bit_depth == BitDepth::Four)
&& (self == ColorType::RGB
|| self == ColorType::GrayscaleAlpha
|| self == ColorType::RGBA))
|| (bit_depth == BitDepth::Sixteen && self == ColorType::Indexed)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BitDepth {
One = 1,
Two = 2,
Four = 4,
Eight = 8,
Sixteen = 16,
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub(crate) enum BytesPerPixel {
One = 1,
Two = 2,
Three = 3,
Four = 4,
Six = 6,
Eight = 8,
}
impl BitDepth {
pub fn from_u8(n: u8) -> Option<BitDepth> {
match n {
1 => Some(BitDepth::One),
2 => Some(BitDepth::Two),
4 => Some(BitDepth::Four),
8 => Some(BitDepth::Eight),
16 => Some(BitDepth::Sixteen),
_ => None,
}
}
pub(crate) fn into_u8(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy, Debug)]
pub struct PixelDimensions {
pub xppu: u32,
pub yppu: u32,
pub unit: Unit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Unit {
Unspecified = 0,
Meter = 1,
}
impl Unit {
pub fn from_u8(n: u8) -> Option<Unit> {
match n {
0 => Some(Unit::Unspecified),
1 => Some(Unit::Meter),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DisposeOp {
None = 0,
Background = 1,
Previous = 2,
}
impl DisposeOp {
pub fn from_u8(n: u8) -> Option<DisposeOp> {
match n {
0 => Some(DisposeOp::None),
1 => Some(DisposeOp::Background),
2 => Some(DisposeOp::Previous),
_ => None,
}
}
}
impl fmt::Display for DisposeOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
DisposeOp::None => "DISPOSE_OP_NONE",
DisposeOp::Background => "DISPOSE_OP_BACKGROUND",
DisposeOp::Previous => "DISPOSE_OP_PREVIOUS",
};
write!(f, "{}", name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BlendOp {
Source = 0,
Over = 1,
}
impl BlendOp {
pub fn from_u8(n: u8) -> Option<BlendOp> {
match n {
0 => Some(BlendOp::Source),
1 => Some(BlendOp::Over),
_ => None,
}
}
}
impl fmt::Display for BlendOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
BlendOp::Source => "BLEND_OP_SOURCE",
BlendOp::Over => "BLEND_OP_OVER",
};
write!(f, "{}", name)
}
}
#[derive(Clone, Copy, Debug)]
pub struct FrameControl {
pub sequence_number: u32,
pub width: u32,
pub height: u32,
pub x_offset: u32,
pub y_offset: u32,
pub delay_num: u16,
pub delay_den: u16,
pub dispose_op: DisposeOp,
pub blend_op: BlendOp,
}
impl Default for FrameControl {
fn default() -> FrameControl {
FrameControl {
sequence_number: 0,
width: 0,
height: 0,
x_offset: 0,
y_offset: 0,
delay_num: 1,
delay_den: 30,
dispose_op: DisposeOp::None,
blend_op: BlendOp::Source,
}
}
}
impl FrameControl {
pub fn set_seq_num(&mut self, s: u32) {
self.sequence_number = s;
}
pub fn inc_seq_num(&mut self, i: u32) {
self.sequence_number += i;
}
}
#[derive(Clone, Copy, Debug)]
pub struct AnimationControl {
pub num_frames: u32,
pub num_plays: u32,
}
#[derive(Debug, Clone)]
pub enum Compression {
Default,
Fast,
Best,
Huffman,
Rle,
}
#[derive(Clone, Debug)]
pub struct Info {
pub width: u32,
pub height: u32,
pub bit_depth: BitDepth,
pub color_type: ColorType,
pub interlaced: bool,
pub trns: Option<Vec<u8>>,
pub pixel_dims: Option<PixelDimensions>,
pub palette: Option<Vec<u8>>,
pub frame_control: Option<FrameControl>,
pub animation_control: Option<AnimationControl>,
pub compression: Compression,
pub filter: filter::FilterType,
}
impl Default for Info {
fn default() -> Info {
Info {
width: 0,
height: 0,
bit_depth: BitDepth::Eight,
color_type: ColorType::Grayscale,
interlaced: false,
palette: None,
trns: None,
pixel_dims: None,
frame_control: None,
animation_control: None,
compression: Compression::Fast,
filter: filter::FilterType::Sub,
}
}
}
impl Info {
pub fn size(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn is_animated(&self) -> bool {
self.frame_control.is_some() && self.animation_control.is_some()
}
pub fn animation_control(&self) -> Option<&AnimationControl> {
self.animation_control.as_ref()
}
pub fn frame_control(&self) -> Option<&FrameControl> {
self.frame_control.as_ref()
}
pub fn bits_per_pixel(&self) -> usize {
self.color_type.samples() * self.bit_depth as usize
}
pub fn bytes_per_pixel(&self) -> usize {
self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3)
}
pub(crate) fn bpp_in_prediction(&self) -> BytesPerPixel {
match self.bytes_per_pixel() {
1 => BytesPerPixel::One,
2 => BytesPerPixel::Two,
3 => BytesPerPixel::Three,
4 => BytesPerPixel::Four,
6 => BytesPerPixel::Six,
8 => BytesPerPixel::Eight,
_ => unreachable!("Not a possible byte rounded pixel width"),
}
}
pub fn raw_bytes(&self) -> usize {
self.height as usize * self.raw_row_length()
}
pub fn raw_row_length(&self) -> usize {
self.raw_row_length_from_width(self.width)
}
pub(crate) fn checked_raw_row_length(&self) -> Option<usize> {
self.color_type
.checked_raw_row_length(self.bit_depth, self.width)
}
pub fn raw_row_length_from_width(&self, width: u32) -> usize {
self.color_type
.raw_row_length_from_width(self.bit_depth, width)
}
}
impl BytesPerPixel {
pub(crate) fn into_usize(self) -> usize {
self as usize
}
}
bitflags! {
pub struct Transformations: u32 {
const IDENTITY = 0x0000;
const STRIP_16 = 0x0001;
const STRIP_ALPHA = 0x0002;
const PACKING = 0x0004;
const PACKSWAP = 0x0008;
const EXPAND = 0x0010;
const INVERT_MONO = 0x0020;
const SHIFT = 0x0040;
const BGR = 0x0080;
const SWAP_ALPHA = 0x0100;
const SWAP_ENDIAN = 0x0200;
const INVERT_ALPHA = 0x0400;
const STRIP_FILLER = 0x0800;
const STRIP_FILLER_BEFORE = 0x0800;
const STRIP_FILLER_AFTER = 0x1000;
const GRAY_TO_RGB = 0x2000;
const EXPAND_16 = 0x4000;
const SCALE_16 = 0x8000;
}
}
#[cfg(feature = "png-encoding")]
mod deflate_convert {
extern crate deflate;
use super::Compression;
impl From<deflate::Compression> for Compression {
fn from(c: deflate::Compression) -> Self {
match c {
deflate::Compression::Default => Compression::Default,
deflate::Compression::Fast => Compression::Fast,
deflate::Compression::Best => Compression::Best,
}
}
}
impl From<Compression> for deflate::CompressionOptions {
fn from(c: Compression) -> Self {
match c {
Compression::Default => deflate::CompressionOptions::default(),
Compression::Fast => deflate::CompressionOptions::fast(),
Compression::Best => deflate::CompressionOptions::high(),
Compression::Huffman => deflate::CompressionOptions::huffman_only(),
Compression::Rle => deflate::CompressionOptions::rle(),
}
}
}
}