Skip to content

Commit a8317f6

Browse files
committedJan 28, 2016
Auto merge of #25 - Ogeon:color_ranges, r=Ogeon
Fix or relax some color ranges and clamping The `ColorSpace` trait has been repurposed as a trait for checking limits and clamping, and is now called `Limited`. The limits of `Xyz` has been changed to use the white point as its maximum (closing #10), and the maximum of `Lch.chroma` has been removed, since it's not very well defined. The ranges of `Lch` stays as they are, for the sake of consistency. Extra clamping is also added when converting RGB colors to `u8` based pixel representation, to prevent float shenanigans when multiplying values with 255.0. It's impossible to know what a type `T` will do when it's asked to convert `255.12312312` or whatever to `u8`. This closes #19. This is a breaking change, since it changes the ranges of some color spaces and renames the `ColorSpace` trait to `Limited`.
2 parents c082bab + b7dd6ae commit a8317f6

File tree

11 files changed

+348
-58
lines changed

11 files changed

+348
-58
lines changed
 

‎src/alpha.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut, Add, Sub, Mul, Div};
22

33
use num::Float;
44

5-
use {Mix, Shade, GetHue, Hue, Saturate};
5+
use {Mix, Shade, GetHue, Hue, Saturate, Limited, clamp};
66

77
///An alpha component wrapper for colors.
88
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -86,6 +86,24 @@ impl<C: Saturate> Saturate for Alpha<C, C::Scalar> {
8686
}
8787
}
8888

89+
impl<C: Limited, T: Float> Limited for Alpha<C, T> {
90+
fn is_valid(&self) -> bool {
91+
self.color.is_valid() && self.alpha >= T::zero() && self.alpha <= T::one()
92+
}
93+
94+
fn clamp(&self) -> Alpha<C, T> {
95+
Alpha {
96+
color: self.color.clamp(),
97+
alpha: clamp(self.alpha, T::zero(), T::one()),
98+
}
99+
}
100+
101+
fn clamp_self(&mut self) {
102+
self.color.clamp_self();
103+
self.alpha = clamp(self.alpha, T::zero(), T::one());
104+
}
105+
}
106+
89107
impl<C: Default, T: Float> Default for Alpha<C, T> {
90108
fn default() -> Alpha<C, T> {
91109
Alpha {

‎src/hsl.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub};
44

5-
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsv, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
5+
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsv, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
66

77
///Linear HSL with an alpha component. See the [`Hsla` implementation in `Alpha`](struct.Alpha.html#Hsla).
88
pub type Hsla<T = f32> = Alpha<Hsl<T>, T>;
@@ -54,7 +54,7 @@ impl<T: Float> Alpha<Hsl<T>, T> {
5454
}
5555
}
5656

57-
impl<T: Float> ColorSpace for Hsl<T> {
57+
impl<T: Float> Limited for Hsl<T> {
5858
fn is_valid(&self) -> bool {
5959
self.saturation >= T::zero() && self.saturation <= T::one() &&
6060
self.lightness >= T::zero() && self.lightness <= T::one()
@@ -290,7 +290,7 @@ impl<T: Float> From<Hsv<T>> for Hsl<T> {
290290
#[cfg(test)]
291291
mod test {
292292
use super::Hsl;
293-
use ::{Rgb, Hsv};
293+
use {Rgb, Hsv};
294294

295295
#[test]
296296
fn red() {
@@ -341,4 +341,19 @@ mod test {
341341
assert_approx_eq!(a, b, [hue, saturation, lightness]);
342342
assert_approx_eq!(a, c, [hue, saturation, lightness]);
343343
}
344+
345+
#[test]
346+
fn ranges() {
347+
assert_ranges!{
348+
Hsl;
349+
limited {
350+
saturation: 0.0 => 1.0,
351+
lightness: 0.0 => 1.0
352+
}
353+
limited_min {}
354+
unlimited {
355+
hue: -360.0 => 360.0
356+
}
357+
}
358+
}
344359
}

‎src/hsv.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub};
44

5-
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsl, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
5+
use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsl, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp};
66

77
///Linear HSV with an alpha component. See the [`Hsva` implementation in `Alpha`](struct.Alpha.html#Hsva).
88
pub type Hsva<T = f32> = Alpha<Hsv<T>, T>;
@@ -53,7 +53,7 @@ impl<T: Float> Alpha<Hsv<T>, T> {
5353
}
5454
}
5555

56-
impl<T: Float> ColorSpace for Hsv<T> {
56+
impl<T: Float> Limited for Hsv<T> {
5757
fn is_valid(&self) -> bool {
5858
self.saturation >= T::zero() && self.saturation <= T::one() &&
5959
self.value >= T::zero() && self.value <= T::one()
@@ -337,4 +337,19 @@ mod test {
337337
assert_approx_eq!(a, b, [hue, saturation, value]);
338338
assert_approx_eq!(a, c, [hue, saturation, value]);
339339
}
340+
341+
#[test]
342+
fn ranges() {
343+
assert_ranges!{
344+
Hsv;
345+
limited {
346+
saturation: 0.0 => 1.0,
347+
value: 0.0 => 1.0
348+
}
349+
limited_min {}
350+
unlimited {
351+
hue: -360.0 => 360.0
352+
}
353+
}
354+
}
340355
}

‎src/hues.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ fn normalize_angle<T: Float>(mut deg: T) -> T {
126126
}
127127

128128
while deg <= -T::from(180.0).unwrap() {
129-
deg = deg - T::from(360.0).unwrap();
129+
deg = deg + T::from(360.0).unwrap();
130130
}
131131

132132
deg

‎src/lab.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub, Mul, Div};
44

5-
use {Color, Alpha, Rgb, Luma, Xyz, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, LabHue, clamp};
5+
use {Color, Alpha, Rgb, Luma, Xyz, Lch, Hsv, Hsl, Limited, Mix, Shade, GetHue, LabHue, clamp};
66

77
use tristimulus::{X_N, Y_N, Z_N};
88

@@ -55,7 +55,7 @@ impl<T: Float> Alpha<Lab<T>, T> {
5555
}
5656
}
5757

58-
impl<T: Float> ColorSpace for Lab<T> {
58+
impl<T: Float> Limited for Lab<T> {
5959
fn is_valid(&self) -> bool {
6060
self.l >= T::zero() && self.l <= T::one() &&
6161
self.a >= -T::one() && self.a <= T::one() &&
@@ -307,4 +307,18 @@ mod test {
307307
let b = Lab::new(32.302586 / 100.0, 79.19668 / 128.0, -107.863686 / 128.0);
308308
assert_approx_eq!(a, b, [l, a, b]);
309309
}
310+
311+
#[test]
312+
fn ranges() {
313+
assert_ranges!{
314+
Lab;
315+
limited {
316+
l: 0.0 => 1.0,
317+
a: -1.0 => 1.0,
318+
b: -1.0 => 1.0
319+
}
320+
limited_min {}
321+
unlimited {}
322+
}
323+
}
310324
}

‎src/lch.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub};
44

5-
use {Color, Alpha, ColorSpace, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp};
5+
use {Color, Alpha, Limited, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp};
66

77
///CIE L*C*h° with an alpha component. See the [`Lcha` implementation in `Alpha`](struct.Alpha.html#Lcha).
88
pub type Lcha<T = f32> = Alpha<Lch<T>, T>;
@@ -52,10 +52,10 @@ impl<T: Float> Alpha<Lch<T>, T> {
5252
}
5353
}
5454

55-
impl<T: Float> ColorSpace for Lch<T> {
55+
impl<T: Float> Limited for Lch<T> {
5656
fn is_valid(&self) -> bool {
5757
self.l >= T::zero() && self.l <= T::one() &&
58-
self.chroma >= T::zero() && self.chroma <= T::from(1.41421356).unwrap() //should include all of L*a*b*, but will also overshoot...
58+
self.chroma >= T::zero()
5959
}
6060

6161
fn clamp(&self) -> Lch<T> {
@@ -66,7 +66,7 @@ impl<T: Float> ColorSpace for Lch<T> {
6666

6767
fn clamp_self(&mut self) {
6868
self.l = clamp(self.l, T::zero(), T::one());
69-
self.chroma = clamp(self.chroma, T::zero(), T::from(1.41421356).unwrap()); //should include all of L*a*b*, but will also overshoot...
69+
self.chroma = self.chroma.max(T::zero())
7070
}
7171
}
7272

@@ -235,3 +235,24 @@ impl<T: Float> From<Hsl<T>> for Lch<T> {
235235
Lab::from(hsl).into()
236236
}
237237
}
238+
239+
#[cfg(test)]
240+
mod test {
241+
use Lch;
242+
243+
#[test]
244+
fn ranges() {
245+
assert_ranges!{
246+
Lch;
247+
limited {
248+
l: 0.0 => 1.0
249+
}
250+
limited_min {
251+
chroma: 0.0 => 2.0
252+
}
253+
unlimited {
254+
hue: -360.0 => 360.0
255+
}
256+
}
257+
}
258+
}

‎src/lib.rs

+148-2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,152 @@ macro_rules! assert_approx_eq {
126126
})
127127
}
128128

129+
//Helper macro for checking ranges and clamping.
130+
#[cfg(test)]
131+
macro_rules! assert_ranges {
132+
(@make_tuple $first:pat, $next:ident,) => (($first, $next));
133+
134+
(@make_tuple $first:pat, $next:ident, $($rest:ident,)*) => (
135+
assert_ranges!(@make_tuple ($first, $next), $($rest,)*)
136+
);
137+
138+
(
139+
$ty:ident;
140+
limited {$($limited:ident: $limited_from:expr => $limited_to:expr),+}
141+
limited_min {$($limited_min:ident: $limited_min_from:expr => $limited_min_to:expr),*}
142+
unlimited {$($unlimited:ident: $unlimited_from:expr => $unlimited_to:expr),*}
143+
) => (
144+
{
145+
use std::iter::repeat;
146+
use Limited;
147+
148+
{
149+
print!("checking below limits ... ");
150+
$(
151+
let from = $limited_from;
152+
let to = $limited_to;
153+
let diff = to - from;
154+
let $limited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
155+
)+
156+
157+
$(
158+
let from = $limited_min_from;
159+
let to = $limited_min_to;
160+
let diff = to - from;
161+
let $limited_min = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
162+
)*
163+
164+
$(
165+
let from = $unlimited_from;
166+
let to = $unlimited_to;
167+
let diff = to - from;
168+
let $unlimited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
169+
)*
170+
171+
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
172+
let c = $ty::<f64> {
173+
$($limited: $limited.into(),)+
174+
$($limited_min: $limited_min.into(),)*
175+
$($unlimited: $unlimited.into(),)*
176+
};
177+
let clamped = c.clamp();
178+
let expected = $ty {
179+
$($limited: $limited_from.into(),)+
180+
$($limited_min: $limited_min_from.into(),)*
181+
$($unlimited: $unlimited.into(),)*
182+
};
183+
184+
assert!(!c.is_valid());
185+
assert_eq!(clamped, expected);
186+
}
187+
188+
println!("ok")
189+
}
190+
191+
{
192+
print!("checking within limits ... ");
193+
$(
194+
let from = $limited_from;
195+
let to = $limited_to;
196+
let diff = to - from;
197+
let $limited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
198+
)+
199+
200+
$(
201+
let from = $limited_min_from;
202+
let to = $limited_min_to;
203+
let diff = to - from;
204+
let $limited_min = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
205+
)*
206+
207+
$(
208+
let from = $unlimited_from;
209+
let to = $unlimited_to;
210+
let diff = to - from;
211+
let $unlimited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
212+
)*
213+
214+
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
215+
let c = $ty::<f64> {
216+
$($limited: $limited.into(),)+
217+
$($limited_min: $limited_min.into(),)*
218+
$($unlimited: $unlimited.into(),)*
219+
};
220+
let clamped = c.clamp();
221+
222+
assert!(c.is_valid());
223+
assert_eq!(clamped, c);
224+
}
225+
226+
println!("ok")
227+
}
228+
229+
{
230+
print!("checking above limits ... ");
231+
$(
232+
let from = $limited_from;
233+
let to = $limited_to;
234+
let diff = to - from;
235+
let $limited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
236+
)+
237+
238+
$(
239+
let from = $limited_min_from;
240+
let to = $limited_min_to;
241+
let diff = to - from;
242+
let $limited_min = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
243+
)*
244+
245+
$(
246+
let from = $unlimited_from;
247+
let to = $unlimited_to;
248+
let diff = to - from;
249+
let $unlimited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
250+
)*
251+
252+
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
253+
let c = $ty::<f64> {
254+
$($limited: $limited.into(),)+
255+
$($limited_min: $limited_min.into(),)*
256+
$($unlimited: $unlimited.into(),)*
257+
};
258+
let clamped = c.clamp();
259+
let expected = $ty {
260+
$($limited: $limited_to.into(),)+
261+
$($limited_min: $limited_min.into(),)*
262+
$($unlimited: $unlimited.into(),)*
263+
};
264+
265+
assert!(!c.is_valid());
266+
assert_eq!(clamped, expected);
267+
}
268+
269+
println!("ok")
270+
}
271+
}
272+
);
273+
}
274+
129275
pub mod gradient;
130276
pub mod pixel;
131277

@@ -320,8 +466,8 @@ make_color! {
320466
}
321467
}
322468

323-
///Common functionality for color spaces.
324-
pub trait ColorSpace {
469+
///A trait for clamping and checking if colors are within their ranges.
470+
pub trait Limited {
325471
///Check if the color's components are within the expected ranges.
326472
fn is_valid(&self) -> bool;
327473

‎src/luma.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub, Mul, Div};
44

5-
use {Color, Alpha, Rgb, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp};
5+
use {Color, Alpha, Rgb, Xyz, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, clamp};
66

77
///Linear luminance with an alpha component. See the [`Lumaa` implementation in `Alpha`](struct.Alpha.html#Lumaa).
88
pub type Lumaa<T = f32> = Alpha<Luma<T>, T>;
@@ -55,7 +55,7 @@ impl<T: Float> Alpha<Luma<T>, T> {
5555
}
5656
}
5757

58-
impl<T: Float> ColorSpace for Luma<T> {
58+
impl<T: Float> Limited for Luma<T> {
5959
fn is_valid(&self) -> bool {
6060
self.luma >= T::zero() && self.luma <= T::one()
6161
}
@@ -222,3 +222,20 @@ impl<T: Float> From<Hsl<T>> for Luma<T> {
222222
Rgb::from(hsl).into()
223223
}
224224
}
225+
226+
#[cfg(test)]
227+
mod test {
228+
use Luma;
229+
230+
#[test]
231+
fn ranges() {
232+
assert_ranges!{
233+
Luma;
234+
limited {
235+
luma: 0.0 => 1.0
236+
}
237+
limited_min {}
238+
unlimited {}
239+
}
240+
}
241+
}

‎src/pixel/mod.rs

+38-28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use num::Float;
44

5+
use clamp;
6+
57
pub use self::srgb::Srgb;
68
pub use self::gamma_rgb::GammaRgb;
79

@@ -69,40 +71,44 @@ impl<T: Float> RgbPixel<T> for (f64, f64, f64) {
6971

7072
impl<T: Float> RgbPixel<T> for (u8, u8, u8, u8) {
7173
fn from_rgba(red: T, green: T, blue: T, alpha: T) -> (u8, u8, u8, u8) {
74+
let c255 = T::from(255.0).unwrap();
7275
(
73-
(red * T::from(255.0).unwrap()).to_u8().unwrap(),
74-
(green * T::from(255.0).unwrap()).to_u8().unwrap(),
75-
(blue * T::from(255.0).unwrap()).to_u8().unwrap(),
76-
(alpha * T::from(255.0).unwrap()).to_u8().unwrap(),
76+
clamp(red * c255, T::zero(), c255).to_u8().unwrap(),
77+
clamp(green * c255, T::zero(), c255).to_u8().unwrap(),
78+
clamp(blue * c255, T::zero(), c255).to_u8().unwrap(),
79+
clamp(alpha * c255, T::zero(), c255).to_u8().unwrap(),
7780
)
7881
}
7982

8083
fn to_rgba(&self) -> (T, T, T, T) {
8184
let (r, g, b, a) = *self;
85+
let c255 = T::from(255.0).unwrap();
8286
(
83-
T::from(r).unwrap() / T::from(255.0).unwrap(),
84-
T::from(g).unwrap() / T::from(255.0).unwrap(),
85-
T::from(b).unwrap() / T::from(255.0).unwrap(),
86-
T::from(a).unwrap() / T::from(255.0).unwrap(),
87+
T::from(r).unwrap() / c255,
88+
T::from(g).unwrap() / c255,
89+
T::from(b).unwrap() / c255,
90+
T::from(a).unwrap() / c255,
8791
)
8892
}
8993
}
9094

9195
impl<T: Float> RgbPixel<T> for (u8, u8, u8) {
9296
fn from_rgba(red: T, green: T, blue: T, _alpha: T) -> (u8, u8, u8) {
97+
let c255 = T::from(255.0).unwrap();
9398
(
94-
(red * T::from(255.0).unwrap()).to_u8().unwrap(),
95-
(green * T::from(255.0).unwrap()).to_u8().unwrap(),
96-
(blue * T::from(255.0).unwrap()).to_u8().unwrap(),
99+
clamp(red * c255, T::zero(), c255).to_u8().unwrap(),
100+
clamp(green * c255, T::zero(), c255).to_u8().unwrap(),
101+
clamp(blue * c255, T::zero(), c255).to_u8().unwrap(),
97102
)
98103
}
99104

100105
fn to_rgba(&self) -> (T, T, T, T) {
101106
let (r, g, b) = *self;
107+
let c255 = T::from(255.0).unwrap();
102108
(
103-
T::from(r).unwrap() / T::from(255.0).unwrap(),
104-
T::from(g).unwrap() / T::from(255.0).unwrap(),
105-
T::from(b).unwrap() / T::from(255.0).unwrap(),
109+
T::from(r).unwrap() / c255,
110+
T::from(g).unwrap() / c255,
111+
T::from(b).unwrap() / c255,
106112
T::one(),
107113
)
108114
}
@@ -149,38 +155,42 @@ impl<T: Float> RgbPixel<T> for [f64; 3] {
149155

150156
impl<T: Float> RgbPixel<T> for [u8; 4] {
151157
fn from_rgba(red: T, green: T, blue: T, alpha: T) -> [u8; 4] {
158+
let c255 = T::from(255.0).unwrap();
152159
[
153-
(red * T::from(255.0).unwrap()).to_u8().unwrap(),
154-
(green * T::from(255.0).unwrap()).to_u8().unwrap(),
155-
(blue * T::from(255.0).unwrap()).to_u8().unwrap(),
156-
(alpha * T::from(255.0).unwrap()).to_u8().unwrap(),
160+
clamp(red * c255, T::zero(), c255).to_u8().unwrap(),
161+
clamp(green * c255, T::zero(), c255).to_u8().unwrap(),
162+
clamp(blue * c255, T::zero(), c255).to_u8().unwrap(),
163+
clamp(alpha * c255, T::zero(), c255).to_u8().unwrap(),
157164
]
158165
}
159166

160167
fn to_rgba(&self) -> (T, T, T, T) {
168+
let c255 = T::from(255.0).unwrap();
161169
(
162-
T::from(self[0]).unwrap() / T::from(255.0).unwrap(),
163-
T::from(self[1]).unwrap() / T::from(255.0).unwrap(),
164-
T::from(self[2]).unwrap() / T::from(255.0).unwrap(),
165-
T::from(self[3]).unwrap() / T::from(255.0).unwrap(),
170+
T::from(self[0]).unwrap() / c255,
171+
T::from(self[1]).unwrap() / c255,
172+
T::from(self[2]).unwrap() / c255,
173+
T::from(self[3]).unwrap() / c255,
166174
)
167175
}
168176
}
169177

170178
impl<T: Float> RgbPixel<T> for [u8; 3] {
171179
fn from_rgba(red: T, green: T, blue: T, _alpha: T) -> [u8; 3] {
180+
let c255 = T::from(255.0).unwrap();
172181
[
173-
(red * T::from(255.0).unwrap()).to_u8().unwrap(),
174-
(green * T::from(255.0).unwrap()).to_u8().unwrap(),
175-
(blue * T::from(255.0).unwrap()).to_u8().unwrap(),
182+
clamp(red * c255, T::zero(), c255).to_u8().unwrap(),
183+
clamp(green * c255, T::zero(), c255).to_u8().unwrap(),
184+
clamp(blue * c255, T::zero(), c255).to_u8().unwrap(),
176185
]
177186
}
178187

179188
fn to_rgba(&self) -> (T, T, T, T) {
189+
let c255 = T::from(255.0).unwrap();
180190
(
181-
T::from(self[0]).unwrap() / T::from(255.0).unwrap(),
182-
T::from(self[1]).unwrap() / T::from(255.0).unwrap(),
183-
T::from(self[2]).unwrap() / T::from(255.0).unwrap(),
191+
T::from(self[0]).unwrap() / c255,
192+
T::from(self[1]).unwrap() / c255,
193+
T::from(self[2]).unwrap() / c255,
184194
T::one(),
185195
)
186196
}

‎src/rgb.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub, Mul, Div};
44

5-
use {Color, Alpha, Luma, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, RgbHue, clamp};
5+
use {Color, Alpha, Luma, Xyz, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, GetHue, RgbHue, clamp};
66
use pixel::{RgbPixel, Srgb, GammaRgb};
77

88
///Linear RGB with an alpha component. See the [`Rgba` implementation in `Alpha`](struct.Alpha.html#Rgba).
@@ -124,7 +124,7 @@ impl<T: Float> Alpha<Rgb<T>, T> {
124124
}
125125
}
126126

127-
impl<T: Float> ColorSpace for Rgb<T> {
127+
impl<T: Float> Limited for Rgb<T> {
128128
fn is_valid(&self) -> bool {
129129
self.red >= T::zero() && self.red <= T::one() &&
130130
self.green >= T::zero() && self.green <= T::one() &&
@@ -405,3 +405,22 @@ impl<T: Float> From<GammaRgb<T>> for Alpha<Rgb<T>, T> {
405405
gamma_rgb.to_linear()
406406
}
407407
}
408+
409+
#[cfg(test)]
410+
mod test {
411+
use Rgb;
412+
413+
#[test]
414+
fn ranges() {
415+
assert_ranges!{
416+
Rgb;
417+
limited {
418+
red: 0.0 => 1.0,
419+
green: 0.0 => 1.0,
420+
blue: 0.0 => 1.0
421+
}
422+
limited_min {}
423+
unlimited {}
424+
}
425+
}
426+
}

‎src/xyz.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use num::traits::Float;
22

33
use std::ops::{Add, Sub, Mul, Div};
44

5-
use {Color, Alpha, Rgb, Luma, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp};
5+
use {Color, Alpha, Rgb, Luma, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, clamp};
66

77
use tristimulus::{X_N, Y_N, Z_N};
88

@@ -22,14 +22,14 @@ pub type Xyza<T = f32> = Alpha<Xyz<T>, T>;
2222
#[derive(Clone, Copy, Debug, PartialEq)]
2323
pub struct Xyz<T: Float = f32> {
2424
///X is the scale of what can be seen as a response curve for the cone
25-
///cells in the human eye. It goes from 0.0 to 1.0.
25+
///cells in the human eye. It goes from 0.0 to 0.95047.
2626
pub x: T,
2727

2828
///Y is the luminance of the color, where 0.0 is black and 1.0 is white.
2929
pub y: T,
3030

3131
///Z is the scale of what can be seen as the blue stimulation. It goes
32-
///from 0.0 to 1.0.
32+
///from 0.0 to 1.08883.
3333
pub z: T,
3434
}
3535

@@ -55,11 +55,11 @@ impl<T: Float> Alpha<Xyz<T>, T> {
5555
}
5656
}
5757

58-
impl<T: Float> ColorSpace for Xyz<T> {
58+
impl<T: Float> Limited for Xyz<T> {
5959
fn is_valid(&self) -> bool {
60-
self.x >= T::zero() && self.x <= T::one() &&
61-
self.y >= T::zero() && self.y <= T::one() &&
62-
self.z >= T::zero() && self.z <= T::one()
60+
self.x >= T::zero() && self.x <= T::from(X_N).unwrap() &&
61+
self.y >= T::zero() && self.y <= T::from(Y_N).unwrap() &&
62+
self.z >= T::zero() && self.z <= T::from(Z_N).unwrap()
6363
}
6464

6565
fn clamp(&self) -> Xyz<T> {
@@ -69,9 +69,9 @@ impl<T: Float> ColorSpace for Xyz<T> {
6969
}
7070

7171
fn clamp_self(&mut self) {
72-
self.x = clamp(self.x, T::zero(), T::one());
73-
self.y = clamp(self.y, T::zero(), T::one());
74-
self.z = clamp(self.z, T::zero(), T::one());
72+
self.x = clamp(self.x, T::zero(), T::from(X_N).unwrap());
73+
self.y = clamp(self.y, T::zero(), T::from(Y_N).unwrap());
74+
self.z = clamp(self.z, T::zero(), T::from(Z_N).unwrap());
7575
}
7676
}
7777

@@ -275,7 +275,8 @@ fn f_inv<T: Float>(t: T) -> T {
275275
#[cfg(test)]
276276
mod test {
277277
use super::Xyz;
278-
use ::Rgb;
278+
use Rgb;
279+
use tristimulus::{X_N, Y_N, Z_N};
279280

280281
#[test]
281282
fn red() {
@@ -297,4 +298,18 @@ mod test {
297298
let b = Xyz::new(0.18050, 0.07220, 0.95050);
298299
assert_approx_eq!(a, b, [x, y, z]);
299300
}
301+
302+
#[test]
303+
fn ranges() {
304+
assert_ranges!{
305+
Xyz;
306+
limited {
307+
x: 0.0 => X_N,
308+
y: 0.0 => Y_N,
309+
z: 0.0 => Z_N
310+
}
311+
limited_min {}
312+
unlimited {}
313+
}
314+
}
300315
}

0 commit comments

Comments
 (0)
Please sign in to comment.