appthere_color/rendering_intent.rs
1//! Rendering intent selection for ICC color transforms.
2//!
3//! Defines the four ICC rendering intents that control how out-of-gamut colors
4//! are mapped during profile-to-profile conversion.
5
6/// ICC rendering intent controlling out-of-gamut color mapping.
7///
8/// When converting colors between profiles with different gamuts, the rendering
9/// intent determines how colors outside the destination gamut are handled.
10/// Each intent makes a different trade-off between accuracy and aesthetics.
11///
12/// # Examples
13///
14/// ```
15/// use appthere_color::RenderingIntent;
16///
17/// let intent = RenderingIntent::Perceptual;
18/// assert_eq!(intent, RenderingIntent::Perceptual);
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum RenderingIntent {
23 /// Perceptual intent — optimizes for pleasing visual appearance.
24 ///
25 /// Compresses the entire source gamut to fit within the destination gamut,
26 /// preserving the relationships between colors. Best for photographic images
27 /// where visual quality matters more than exact color matching.
28 Perceptual,
29
30 /// Relative colorimetric intent — matches in-gamut colors exactly.
31 ///
32 /// Maps the source white point to the destination white point and maps
33 /// in-gamut colors exactly. Out-of-gamut colors are clipped to the nearest
34 /// reproducible color. Best for proofing and logo colors.
35 RelativeColorimetric,
36
37 /// Saturation intent — maximizes color vividness.
38 ///
39 /// Maps saturated source colors to saturated destination colors, prioritizing
40 /// vividness over accuracy. Best for business graphics and charts where
41 /// impact matters more than fidelity.
42 Saturation,
43
44 /// Absolute colorimetric intent — preserves exact colorimetry.
45 ///
46 /// Like relative colorimetric but does not adjust for the destination
47 /// white point. Colors are reproduced with absolute fidelity, including
48 /// the media white point simulation. Best for exact color matching and
49 /// substrate simulation in proofing.
50 AbsoluteColorimetric,
51}
52
53impl RenderingIntent {
54 /// Converts this rendering intent to the corresponding `moxcms` intent.
55 ///
56 /// # Examples
57 ///
58 /// ```
59 /// use appthere_color::RenderingIntent;
60 ///
61 /// let intent = RenderingIntent::Perceptual;
62 /// let moxcms_intent = intent.to_moxcms();
63 /// ```
64 pub fn to_moxcms(self) -> moxcms::RenderingIntent {
65 match self {
66 RenderingIntent::Perceptual => moxcms::RenderingIntent::Perceptual,
67 RenderingIntent::RelativeColorimetric => {
68 moxcms::RenderingIntent::RelativeColorimetric
69 }
70 RenderingIntent::Saturation => moxcms::RenderingIntent::Saturation,
71 RenderingIntent::AbsoluteColorimetric => {
72 moxcms::RenderingIntent::AbsoluteColorimetric
73 }
74 }
75 }
76
77 /// Creates a rendering intent from the corresponding `moxcms` intent.
78 ///
79 /// # Examples
80 ///
81 /// ```
82 /// use appthere_color::RenderingIntent;
83 ///
84 /// let intent = RenderingIntent::from_moxcms(moxcms::RenderingIntent::Perceptual);
85 /// assert_eq!(intent, RenderingIntent::Perceptual);
86 /// ```
87 pub fn from_moxcms(intent: moxcms::RenderingIntent) -> Self {
88 match intent {
89 moxcms::RenderingIntent::Perceptual => RenderingIntent::Perceptual,
90 moxcms::RenderingIntent::RelativeColorimetric => {
91 RenderingIntent::RelativeColorimetric
92 }
93 moxcms::RenderingIntent::Saturation => RenderingIntent::Saturation,
94 moxcms::RenderingIntent::AbsoluteColorimetric => {
95 RenderingIntent::AbsoluteColorimetric
96 }
97 }
98 }
99}
100
101impl Default for RenderingIntent {
102 /// Returns [`RenderingIntent::Perceptual`] as the default.
103 fn default() -> Self {
104 RenderingIntent::Perceptual
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn default_is_perceptual() {
114 assert_eq!(RenderingIntent::default(), RenderingIntent::Perceptual);
115 }
116
117 #[test]
118 fn round_trip_through_moxcms() {
119 let intents = [
120 RenderingIntent::Perceptual,
121 RenderingIntent::RelativeColorimetric,
122 RenderingIntent::Saturation,
123 RenderingIntent::AbsoluteColorimetric,
124 ];
125 for intent in intents {
126 let mox = intent.to_moxcms();
127 let back = RenderingIntent::from_moxcms(mox);
128 assert_eq!(intent, back);
129 }
130 }
131}