1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use core::convert::TryFrom;

use crate::error::{Error, Result};
use crate::utils::unlikely;

/// Image color space.
///
/// Note: the color space is purely informative. Although it is saved to the
/// file header, it does not affect encoding/decoding in any way.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
#[repr(u8)]
pub enum ColorSpace {
    /// sRGB with linear alpha
    Srgb = 0,
    /// All channels are linear
    Linear = 1,
}

impl ColorSpace {
    /// Returns true if the color space is sRGB with linear alpha.
    pub const fn is_srgb(self) -> bool {
        matches!(self, Self::Srgb)
    }

    /// Returns true is all channels are linear.
    pub const fn is_linear(self) -> bool {
        matches!(self, Self::Linear)
    }

    /// Converts to an integer (0 if sRGB, 1 if all linear).
    pub const fn as_u8(self) -> u8 {
        self as u8
    }
}

impl Default for ColorSpace {
    fn default() -> Self {
        Self::Srgb
    }
}

impl From<ColorSpace> for u8 {
    #[inline]
    fn from(colorspace: ColorSpace) -> Self {
        colorspace as Self
    }
}

impl TryFrom<u8> for ColorSpace {
    type Error = Error;

    #[inline]
    fn try_from(colorspace: u8) -> Result<Self> {
        if unlikely(colorspace | 1 != 1) {
            Err(Error::InvalidColorSpace { colorspace })
        } else {
            Ok(if colorspace == 0 { Self::Srgb } else { Self::Linear })
        }
    }
}

/// Number of 8-bit channels in a pixel.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
#[repr(u8)]
pub enum Channels {
    /// Three 8-bit channels (RGB)
    Rgb = 3,
    /// Four 8-bit channels (RGBA)
    Rgba = 4,
}

impl Channels {
    /// Returns true if there are 3 channels (RGB).
    pub const fn is_rgb(self) -> bool {
        matches!(self, Self::Rgb)
    }

    /// Returns true if there are 4 channels (RGBA).
    pub const fn is_rgba(self) -> bool {
        matches!(self, Self::Rgba)
    }

    /// Converts to an integer (3 if RGB, 4 if RGBA).
    pub const fn as_u8(self) -> u8 {
        self as u8
    }
}

impl Default for Channels {
    fn default() -> Self {
        Self::Rgb
    }
}

impl From<Channels> for u8 {
    #[inline]
    fn from(channels: Channels) -> Self {
        channels as Self
    }
}

impl TryFrom<u8> for Channels {
    type Error = Error;

    #[inline]
    fn try_from(channels: u8) -> Result<Self> {
        if unlikely(channels != 3 && channels != 4) {
            Err(Error::InvalidChannels { channels })
        } else {
            Ok(if channels == 3 { Self::Rgb } else { Self::Rgba })
        }
    }
}