1#![allow(unsafe_code, reason = "unsafe is required for bytemuck unsafe impls")]
5
6use crate::{
7 cache_key::CacheKey, AlphaColor, ColorSpace, ColorSpaceTag, HueDirection, OpaqueColor,
8 PremulColor, PremulRgba8, Rgba8,
9};
10
11unsafe impl<CS: ColorSpace> bytemuck::Pod for AlphaColor<CS> {}
13
14unsafe impl<CS: ColorSpace> bytemuck::TransparentWrapper<[f32; 4]> for AlphaColor<CS> {}
16
17unsafe impl<CS: ColorSpace> bytemuck::Zeroable for AlphaColor<CS> {}
19
20unsafe impl<CS: ColorSpace> bytemuck::Pod for OpaqueColor<CS> {}
22
23unsafe impl<CS: ColorSpace> bytemuck::TransparentWrapper<[f32; 3]> for OpaqueColor<CS> {}
25
26unsafe impl<CS: ColorSpace> bytemuck::Zeroable for OpaqueColor<CS> {}
28
29unsafe impl<CS: ColorSpace> bytemuck::Pod for PremulColor<CS> {}
31
32unsafe impl<CS: ColorSpace> bytemuck::TransparentWrapper<[f32; 4]> for PremulColor<CS> {}
34
35unsafe impl<CS: ColorSpace> bytemuck::Zeroable for PremulColor<CS> {}
37
38unsafe impl bytemuck::Pod for PremulRgba8 {}
40
41unsafe impl bytemuck::Zeroable for PremulRgba8 {}
43
44unsafe impl bytemuck::Pod for Rgba8 {}
46
47unsafe impl bytemuck::Zeroable for Rgba8 {}
49
50unsafe impl bytemuck::NoUninit for ColorSpaceTag {}
52
53unsafe impl bytemuck::Zeroable for ColorSpaceTag {}
55
56unsafe impl bytemuck::checked::CheckedBitPattern for ColorSpaceTag {
58 type Bits = u8;
59
60 fn is_valid_bit_pattern(bits: &u8) -> bool {
61 use bytemuck::Contiguous;
62 *bits <= Self::MAX_VALUE
64 }
65}
66
67unsafe impl bytemuck::Contiguous for ColorSpaceTag {
70 type Int = u8;
71 const MIN_VALUE: u8 = Self::Srgb as u8;
72 const MAX_VALUE: u8 = Self::Aces2065_1 as u8;
73}
74
75unsafe impl bytemuck::NoUninit for HueDirection {}
77
78unsafe impl bytemuck::Zeroable for HueDirection {}
80
81unsafe impl bytemuck::checked::CheckedBitPattern for HueDirection {
83 type Bits = u8;
84
85 fn is_valid_bit_pattern(bits: &u8) -> bool {
86 use bytemuck::Contiguous;
87 *bits <= Self::MAX_VALUE
89 }
90}
91
92unsafe impl bytemuck::Contiguous for HueDirection {
95 type Int = u8;
96 const MIN_VALUE: u8 = Self::Shorter as u8;
97 const MAX_VALUE: u8 = Self::Decreasing as u8;
98}
99
100unsafe impl<T> bytemuck::TransparentWrapper<T> for CacheKey<T> {}
102
103#[cfg(test)]
104mod tests {
105 use crate::{
106 cache_key::CacheKey, AlphaColor, ColorSpaceTag, HueDirection, OpaqueColor, PremulColor,
107 PremulRgba8, Rgba8, Srgb,
108 };
109 use bytemuck::{checked::try_from_bytes, Contiguous, TransparentWrapper, Zeroable};
110 use core::{marker::PhantomData, ptr};
111
112 fn assert_is_pod(_pod: impl bytemuck::Pod) {}
113
114 #[test]
115 fn alphacolor_is_pod() {
116 let AlphaColor {
117 components,
118 cs: PhantomData,
119 } = AlphaColor::<Srgb>::new([1., 2., 3., 0.]);
120 assert_is_pod(components);
121 }
122
123 #[test]
124 fn opaquecolor_is_pod() {
125 let OpaqueColor {
126 components,
127 cs: PhantomData,
128 } = OpaqueColor::<Srgb>::new([1., 2., 3.]);
129 assert_is_pod(components);
130 }
131
132 #[test]
133 fn premulcolor_is_pod() {
134 let PremulColor {
135 components,
136 cs: PhantomData,
137 } = PremulColor::<Srgb>::new([1., 2., 3., 0.]);
138 assert_is_pod(components);
139 }
140
141 #[test]
142 fn premulrgba8_is_pod() {
143 let rgba8 = PremulRgba8 {
144 r: 0,
145 b: 0,
146 g: 0,
147 a: 0,
148 };
149 let PremulRgba8 { r, g, b, a } = rgba8;
150 assert_is_pod(r);
151 assert_is_pod(g);
152 assert_is_pod(b);
153 assert_is_pod(a);
154 }
155
156 #[test]
157 fn rgba8_is_pod() {
158 let rgba8 = Rgba8 {
159 r: 0,
160 b: 0,
161 g: 0,
162 a: 0,
163 };
164 let Rgba8 { r, g, b, a } = rgba8;
165 assert_is_pod(r);
166 assert_is_pod(g);
167 assert_is_pod(b);
168 assert_is_pod(a);
169 }
170
171 #[test]
172 fn checked_bit_pattern() {
173 let valid = bytemuck::bytes_of(&2_u8);
174 let invalid = bytemuck::bytes_of(&200_u8);
175
176 assert_eq!(
177 Ok(&ColorSpaceTag::Lab),
178 try_from_bytes::<ColorSpaceTag>(valid)
179 );
180
181 assert!(try_from_bytes::<ColorSpaceTag>(invalid).is_err());
182
183 assert_eq!(
184 Ok(&HueDirection::Increasing),
185 try_from_bytes::<HueDirection>(valid)
186 );
187
188 assert!(try_from_bytes::<HueDirection>(invalid).is_err());
189 }
190
191 #[test]
192 fn contiguous() {
193 let cst1 = ColorSpaceTag::LinearSrgb;
194 let cst2 = ColorSpaceTag::from_integer(cst1.into_integer());
195 assert_eq!(Some(cst1), cst2);
196
197 assert_eq!(None, ColorSpaceTag::from_integer(255));
198
199 let hd1 = HueDirection::Decreasing;
200 let hd2 = HueDirection::from_integer(hd1.into_integer());
201 assert_eq!(Some(hd1), hd2);
202
203 assert_eq!(None, HueDirection::from_integer(255));
204 }
205
206 #[test]
210 fn transparent_wrapper() {
211 let ac = AlphaColor::<Srgb>::new([1., 2., 3., 0.]);
212 let ai: [f32; 4] = AlphaColor::<Srgb>::peel(ac);
213 assert_eq!(ai, [1., 2., 3., 0.]);
214
215 let oc = OpaqueColor::<Srgb>::new([1., 2., 3.]);
216 let oi: [f32; 3] = OpaqueColor::<Srgb>::peel(oc);
217 assert_eq!(oi, [1., 2., 3.]);
218
219 let pc = PremulColor::<Srgb>::new([1., 2., 3., 0.]);
220 let pi: [f32; 4] = PremulColor::<Srgb>::peel(pc);
221 assert_eq!(pi, [1., 2., 3., 0.]);
222
223 let ck = CacheKey::<f32>::new(1.);
224 let ci: f32 = CacheKey::<f32>::peel(ck);
225 assert_eq!(ci, 1.);
226 }
227
228 #[test]
229 fn zeroable() {
230 let ac = AlphaColor::<Srgb>::zeroed();
231 assert_eq!(ac.components, [0., 0., 0., 0.]);
232
233 let oc = OpaqueColor::<Srgb>::zeroed();
234 assert_eq!(oc.components, [0., 0., 0.]);
235
236 let pc = PremulColor::<Srgb>::zeroed();
237 assert_eq!(pc.components, [0., 0., 0., 0.]);
238
239 let rgba8 = Rgba8::zeroed();
240 assert_eq!(
241 rgba8,
242 Rgba8 {
243 r: 0,
244 g: 0,
245 b: 0,
246 a: 0
247 }
248 );
249
250 let cst = ColorSpaceTag::zeroed();
251 assert_eq!(cst, ColorSpaceTag::Srgb);
252
253 let hd = HueDirection::zeroed();
254 assert_eq!(hd, HueDirection::Shorter);
255 }
256
257 const _: () = {
259 let mut value = 0;
260 while value <= HueDirection::MAX_VALUE {
261 let it: HueDirection = unsafe { ptr::read((&raw const value).cast()) };
263 if it as u8 != value {
265 unreachable!();
266 }
267 value += 1;
268 }
269 };
270
271 const _: () = {
273 let mut value = 0;
274 while value <= ColorSpaceTag::MAX_VALUE {
275 let it: ColorSpaceTag = unsafe { ptr::read((&raw const value).cast()) };
277 if it as u8 != value {
279 unreachable!();
280 }
281 value += 1;
282 }
283 };
284}
285
286#[cfg(doctest)]
287mod doctests {
289 const _HUE_DIRECTION: () = {};
308
309 const _COLOR_SPACE_TAG: () = {};
326}