quantette/types/palette_counts.rs
1use alloc::vec::Vec;
2use core::{
3 error::Error,
4 fmt::{self, Debug},
5};
6
7/// The reason a [`PaletteCounts`] failed to be created.
8#[non_exhaustive]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum CreatePaletteCountsErrorReason {
11 /// The length of the provided palette and the provided counts did not match.
12 LengthMismatch,
13 /// The sum of counts overflowed a `u32`.
14 Overflow,
15}
16
17impl fmt::Display for CreatePaletteCountsErrorReason {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::LengthMismatch => write!(
21 f,
22 "the length of the provided palette and the provided counts do not match",
23 ),
24 Self::Overflow => {
25 write!(f, "the sum of counts overflowed a u32")
26 }
27 }
28 }
29}
30
31impl Error for CreatePaletteCountsErrorReason {}
32
33/// The error returned when a [`PaletteCounts`] failed to be created.
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct CreatePaletteCountsError<Color> {
36 /// The reason the [`PaletteCounts`] failed to be created.
37 pub reason: CreatePaletteCountsErrorReason,
38 /// The provided palette of colors.
39 pub palette: Vec<Color>,
40 /// The provided counts for each color.
41 pub counts: Vec<u32>,
42}
43
44impl<Color> fmt::Display for CreatePaletteCountsError<Color> {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "{}", self.reason)
47 }
48}
49
50impl<Color: Debug> Error for CreatePaletteCountsError<Color> {}
51
52/// A palette of colors and their corresponding counts.
53///
54/// Certain algorithms are faster when operating on deduplicated data,
55/// in which case [`PaletteCounts`] will be a supported input. To create
56/// a [`PaletteCounts`], see the [`dedup`](crate::dedup) module or [`PaletteCounts::new`].
57#[derive(Clone, Debug, PartialEq, Eq, Hash)]
58pub struct PaletteCounts<Color> {
59 /// The palette of colors.
60 palette: Vec<Color>,
61 /// The counts corresponding to each `palette` color.
62 counts: Vec<u32>,
63 /// The sum of `counts`.
64 total_count: u32,
65}
66
67impl<Color> PaletteCounts<Color> {
68 /// Create a new [`PaletteCounts`] without validating invariants.
69 #[inline]
70 pub(crate) fn new_unchecked(palette: Vec<Color>, counts: Vec<u32>, total_count: u32) -> Self {
71 debug_assert_eq!(palette.len(), counts.len());
72 debug_assert_eq!(total_count, counts.iter().copied().sum::<u32>());
73 Self { palette, counts, total_count }
74 }
75
76 /// Create a new [`PaletteCounts`] from a [`Vec`] of palette colors
77 /// and a [`Vec`] of corresponding counts.
78 ///
79 /// # Errors
80 ///
81 /// The provided `palette` and `counts` are returned as an `Err` if any of the following are true:
82 /// - The length of `palette` and `counts` do not match.
83 /// - The sum of `counts` is greater than [`MAX_PIXELS`](crate::MAX_PIXELS) (overflows a `u32`).
84 #[inline]
85 pub fn new(
86 palette: Vec<Color>,
87 counts: Vec<u32>,
88 ) -> Result<Self, CreatePaletteCountsError<Color>> {
89 let reason = if palette.len() == counts.len() {
90 if let Some(total_count) = counts.iter().copied().try_fold(0, u32::checked_add) {
91 return Ok(Self::new_unchecked(palette, counts, total_count));
92 }
93 CreatePaletteCountsErrorReason::Overflow
94 } else {
95 CreatePaletteCountsErrorReason::LengthMismatch
96 };
97 Err(CreatePaletteCountsError { reason, palette, counts })
98 }
99
100 /// Consume a [`PaletteCounts`] and return the inner [`Vec`] of palette colors
101 /// and [`Vec`] of counts.
102 #[must_use]
103 #[inline]
104 pub fn into_parts(self) -> (Vec<Color>, Vec<u32>) {
105 let Self { palette, counts, .. } = self;
106 (palette, counts)
107 }
108
109 /// Returns a slice of the inner palette colors.
110 ///
111 /// To get a mutable slice, see [`palette_mut`](Self::palette_mut).
112 /// To get an owned [`Vec`], see [`into_parts`](Self::into_parts).
113 #[inline]
114 pub fn palette(&self) -> &[Color] {
115 &self.palette
116 }
117
118 /// Returns a mutable slice of the inner palette colors.
119 ///
120 /// To get an immutable slice, see [`palette`](Self::palette).
121 /// To get an owned [`Vec`], see [`into_parts`](Self::into_parts).
122 #[inline]
123 pub fn palette_mut(&mut self) -> &mut [Color] {
124 &mut self.palette
125 }
126
127 /// Returns a slice of the inner counts.
128 ///
129 /// To get an owned [`Vec`], see [`into_parts`](Self::into_parts).
130 #[inline]
131 pub fn counts(&self) -> &[u32] {
132 &self.counts
133 }
134
135 /// Returns the sum of the [`counts`](Self::counts) for all palette colors.
136 ///
137 /// This operation is `O(1)`. The total count is calculated on creation and never changes.
138 #[inline]
139 pub fn total_count(&self) -> u32 {
140 self.total_count
141 }
142
143 /// Replace the palette of a [`PaletteCounts`] to a different color type (or to new colors
144 /// of the same type).
145 ///
146 /// # Errors
147 ///
148 /// If the length of the provided `palette` does not match the length of the current palette
149 /// in the [`PaletteCounts`], then `self` and `palette` are returned as an `Err`. Otherwise,
150 /// the new [`PaletteCounts`] is returned alongside the old palette.
151 #[allow(clippy::type_complexity)]
152 #[inline]
153 pub fn replace_palette<NewColor>(
154 self,
155 palette: Vec<NewColor>,
156 ) -> Result<(PaletteCounts<NewColor>, Vec<Color>), (Self, Vec<NewColor>)> {
157 if self.palette.len() == palette.len() {
158 let Self {
159 palette: old_palette,
160 counts,
161 total_count,
162 } = self;
163 Ok((
164 PaletteCounts::new_unchecked(palette, counts, total_count),
165 old_palette,
166 ))
167 } else {
168 Err((self, palette))
169 }
170 }
171
172 /// Map the palette of a [`PaletteCounts`], reusing the existing `counts`.
173 ///
174 /// See [`map_ref`](PaletteCounts::map_ref) to instead clone the existing `counts`
175 /// and retain the original [`PaletteCounts`].
176 ///
177 /// Rather than being a function from `Color -> NewColor`, `mapping` takes the whole palette
178 /// as input and returns a new palette. This is to allow batch or parallel mappings.
179 ///
180 /// # Examples
181 ///
182 /// It is recommended to do batch mappings for efficiency where it makes sense. E.g., using the
183 /// color space conversion functions from the [`color_space`](crate::color_space) module.
184 ///
185 /// ```
186 /// # use quantette::PaletteCounts;
187 /// # use palette::{Srgb, LinSrgb};
188 /// use quantette::color_space::srgb8_to_oklab;
189 /// let srgb_counts = PaletteCounts::<Srgb<u8>>::default();
190 /// let oklab_counts = srgb_counts.map(|palette| srgb8_to_oklab(&palette));
191 /// ```
192 ///
193 /// To instead map each color one at a time, use `into_iter`, `map`, and `collect` like normal:
194 ///
195 /// ```
196 /// # use quantette::PaletteCounts;
197 /// # use palette::{Srgb, LinSrgb};
198 /// let srgb_counts = PaletteCounts::<Srgb<u8>>::default();
199 /// let lin_srgb_counts: PaletteCounts<LinSrgb> =
200 /// srgb_counts.map(|palette| palette.into_iter().map(|srgb| srgb.into_linear()).collect());
201 /// ```
202 ///
203 /// # Panics
204 ///
205 /// Panics if `mapping` returns a palette with a different length than the original palette.
206 #[must_use]
207 #[inline]
208 pub fn map<NewColor>(
209 self,
210 mapping: impl FnOnce(Vec<Color>) -> Vec<NewColor>,
211 ) -> PaletteCounts<NewColor> {
212 let Self { palette, counts, total_count, .. } = self;
213 let len = palette.len();
214 let palette = mapping(palette);
215 assert_eq!(palette.len(), len);
216 PaletteCounts::new_unchecked(palette, counts, total_count)
217 }
218
219 /// Map the palette colors of a [`PaletteCounts`] to a new [`PaletteCounts`],
220 /// cloning the `counts` in the process.
221 ///
222 /// See [`map`](PaletteCounts::map) to instead consume the original [`PaletteCounts`]
223 /// and avoid a clone of the `counts`.
224 ///
225 /// Rather than being a function from `Color -> NewColor`, `mapping` takes the whole palette
226 /// as input and returns a new palette. This is to allow batch or parallel mappings.
227 ///
228 /// # Examples
229 ///
230 /// It is recommended to do batch mappings for efficiency where it makes sense. E.g., using the
231 /// color space conversion functions from the [`color_space`](crate::color_space) module.
232 ///
233 /// ```
234 /// # use quantette::PaletteCounts;
235 /// # use palette::{Srgb, LinSrgb};
236 /// use quantette::color_space::srgb8_to_oklab;
237 /// let srgb_counts = PaletteCounts::<Srgb<u8>>::default();
238 /// let oklab_counts = srgb_counts.map_ref(srgb8_to_oklab);
239 /// ```
240 ///
241 /// To instead map each color one at a time, use `iter`, `map`, and `collect` like normal:
242 ///
243 /// ```
244 /// # use quantette::PaletteCounts;
245 /// # use palette::{Srgb, LinSrgb};
246 /// let srgb_counts = PaletteCounts::<Srgb<u8>>::default();
247 /// let lin_srgb_counts: PaletteCounts<LinSrgb> =
248 /// srgb_counts.map_ref(|palette| palette.iter().map(|srgb| srgb.into_linear()).collect());
249 /// ```
250 ///
251 /// # Panics
252 ///
253 /// Panics if `mapping` returns a palette with a different length than the original palette.
254 #[must_use]
255 #[inline]
256 pub fn map_ref<NewColor>(
257 &self,
258 mapping: impl FnOnce(&[Color]) -> Vec<NewColor>,
259 ) -> PaletteCounts<NewColor> {
260 let palette = mapping(self.palette());
261 PaletteCounts::new_unchecked(palette, self.counts.clone(), self.total_count)
262 }
263}
264
265impl<Color> Default for PaletteCounts<Color> {
266 #[inline]
267 fn default() -> Self {
268 Self::new_unchecked(Vec::new(), Vec::new(), 0)
269 }
270}