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}