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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//! Contains various type needed across the crate.

use crate::{MAX_COLORS, MAX_PIXELS};

use std::{
    error::Error,
    fmt::{Debug, Display},
    ops::Deref,
};

#[cfg(feature = "image")]
use image::RgbImage;
#[cfg(feature = "image")]
use palette::{cast::ComponentsAs, Srgb};

/// An error type for when the length of an input (e.g., `Vec` or slice)
/// is above the maximum supported value.
///
/// The inner value is the maximum supported value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct AboveMaxLen<T>(pub T);

impl<T: Display> Display for AboveMaxLen<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "above the maximum length of {}", self.0)
    }
}

impl<T: Debug + Display> Error for AboveMaxLen<T> {}

/// A simple new type wrapper around `&'a [Color]` with the invariant that the length of the
/// inner slice must not be greater than [`MAX_PIXELS`].
///
/// # Examples
/// Use `try_into` or [`ColorSlice::from_truncated`] to create [`ColorSlice`]s.
///
/// From a raw color slice:
/// ```
/// # use quantette::{ColorSlice, AboveMaxLen};
/// # use palette::Srgb;
/// # fn main() -> Result<(), AboveMaxLen<u32>> {
/// let srgb = vec![Srgb::new(0, 0, 0)];
/// let colors: ColorSlice<_> = srgb.as_slice().try_into()?;
/// # Ok(())
/// # }
/// ```
///
/// From an image (needs the `image` feature to be enabled):
/// ```no_run
/// # use quantette::ColorSlice;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let img = image::open("some image")?.into_rgb8();
/// let colors = ColorSlice::try_from(&img)?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
#[repr(transparent)]
pub struct ColorSlice<'a, Color>(&'a [Color]);

impl<'a, Color> Clone for ColorSlice<'a, Color> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a, Color> Copy for ColorSlice<'a, Color> {}

impl<'a, Color> ColorSlice<'a, Color> {
    /// Creates a [`ColorSlice`] without ensuring that its length
    /// is less than or equal to [`MAX_PIXELS`].
    #[allow(unused)]
    pub(crate) const fn new_unchecked(colors: &'a [Color]) -> Self {
        Self(colors)
    }

    /// Creates a new [`ColorSlice`] by truncating the input slice to a max length of [`MAX_PIXELS`].
    pub fn from_truncated(colors: &'a [Color]) -> Self {
        Self(&colors[..colors.len().min(MAX_PIXELS as usize)])
    }

    /// Returns the length of the slice as a `u32`.
    #[must_use]
    #[allow(clippy::cast_possible_truncation)]
    pub fn num_colors(&self) -> u32 {
        self.0.len() as u32
    }
}

impl<'a, Color> AsRef<[Color]> for ColorSlice<'a, Color> {
    fn as_ref(&self) -> &[Color] {
        self
    }
}

impl<'a, Color> Deref for ColorSlice<'a, Color> {
    type Target = [Color];

    fn deref(&self) -> &Self::Target {
        self.0
    }
}

impl<'a, Color> From<ColorSlice<'a, Color>> for &'a [Color] {
    fn from(val: ColorSlice<'a, Color>) -> Self {
        val.0
    }
}

impl<'a, Color> TryFrom<&'a [Color]> for ColorSlice<'a, Color> {
    type Error = AboveMaxLen<u32>;

    fn try_from(value: &'a [Color]) -> Result<Self, Self::Error> {
        if value.len() <= MAX_PIXELS as usize {
            Ok(Self(value))
        } else {
            Err(AboveMaxLen(MAX_PIXELS))
        }
    }
}

#[cfg(feature = "image")]
impl<'a> TryFrom<&'a RgbImage> for ColorSlice<'a, Srgb<u8>> {
    type Error = AboveMaxLen<u32>;

    fn try_from(image: &'a RgbImage) -> Result<Self, Self::Error> {
        if image.pixels().len() <= MAX_PIXELS as usize {
            Ok(Self(image.components_as()))
        } else {
            Err(AboveMaxLen(MAX_PIXELS))
        }
    }
}

/// This type is used to specify the (maximum) number of colors to include in a palette.
///
/// This is a simple new type wrapper around `u16` with the invariant that it must be
/// less than or equal to [`MAX_COLORS`].
///
/// If a [`PaletteSize`] of `0` is provided to a quantization function,
/// an empty [`QuantizeOutput`] will be returned.
///
/// # Examples
/// Use `into` to create [`PaletteSize`]s from `u8`s.
/// For `u16`s, use `try_into` or [`PaletteSize::from_clamped`].
/// You can also use the [`PaletteSize::MAX`] constant.
///
/// From a `u8`:
/// ```
/// # use quantette::PaletteSize;
/// let size = PaletteSize::from(16);
/// ```
///
/// From a `u16`:
/// ```
/// # use quantette::{PaletteSize, AboveMaxLen};
/// # fn main() -> Result<(), AboveMaxLen<u16>> {
/// let size: PaletteSize = 128u16.try_into()?;
/// let size = PaletteSize::from_clamped(1024);
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct PaletteSize(u16);

impl PaletteSize {
    /// The maximum supported palette size (given by [`MAX_COLORS`]).
    pub const MAX: Self = Self(MAX_COLORS);

    /// Gets the inner `u16` value.
    #[must_use]
    pub const fn into_inner(self) -> u16 {
        self.0
    }

    /// Creates a [`PaletteSize`] directly from the given `u16`
    /// without ensuring that it is less than or equal to [`MAX_COLORS`].
    #[allow(unused)]
    pub(crate) const fn new_unchecked(value: u16) -> Self {
        Self(value)
    }

    /// Creates a [`PaletteSize`] by clamping the given `u16` to be less than or equal to [`MAX_COLORS`].
    #[must_use]
    pub fn from_clamped(value: u16) -> Self {
        Self(u16::min(value, MAX_COLORS))
    }
}

impl Default for PaletteSize {
    fn default() -> Self {
        Self::MAX
    }
}

impl From<PaletteSize> for u16 {
    fn from(val: PaletteSize) -> Self {
        val.into_inner()
    }
}

impl From<u8> for PaletteSize {
    fn from(value: u8) -> Self {
        Self(value.into())
    }
}

impl TryFrom<u16> for PaletteSize {
    type Error = AboveMaxLen<u16>;

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        if value <= MAX_COLORS {
            Ok(PaletteSize(value))
        } else {
            Err(AboveMaxLen(MAX_COLORS))
        }
    }
}

impl Display for PaletteSize {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.into_inner())
    }
}

/// The output struct returned by quantization functions.
///
/// It contains the color `palette` for the image, alongside `counts` which has
/// the number of pixels/samples assigned to each palette color.
/// Additionally, `indices` will contain a index into `palette` for each pixel,
/// but only if the quantization function computes an indexed palette
/// (e.g., [`wu::indexed_palette`](crate::wu::indexed_palette)).
/// Otherwise, `indices` will be empty (e.g., [`wu::palette`](crate::wu::palette)).
///
/// Note that all fields will be empty if a [`PaletteSize`] of `0` was provided to the quantization function.
#[derive(Debug, Clone)]
pub struct QuantizeOutput<Color> {
    /// The computed color palette that is representative of the colors in the image.
    ///
    /// The colors in the palette are not guaranteed to be unique.
    pub palette: Vec<Color>,
    /// The number of pixels or samples that were assigned to each color in `palette`.
    ///
    /// Each count is not guaranteed to be non-zero.
    pub counts: Vec<u32>,
    /// The remapped image, where each pixel is replaced with an index into `palette`.
    ///
    /// This will be empty if the quantization function does not compute an indexed palette.
    pub indices: Vec<u8>,
}

impl<Color> Default for QuantizeOutput<Color> {
    fn default() -> Self {
        Self {
            palette: Vec::new(),
            counts: Vec::new(),
            indices: Vec::new(),
        }
    }
}