1use std::{
16 fmt, hash,
17 sync::{
18 atomic::{AtomicUsize, Ordering},
19 Arc,
20 },
21};
22
23use crate::{
24 math::{AffineTransform, Point},
25 utils::CanonBits,
26};
27#[derive(Clone, Copy, Debug)]
28pub struct Color {
29 pub r: f32,
30 pub g: f32,
31 pub b: f32,
32 pub a: f32,
33}
34
35impl Eq for Color {}
36
37impl PartialEq for Color {
38 fn eq(&self, other: &Self) -> bool {
39 self.r == other.r && self.g == other.g && self.b == other.b && self.a == other.a
40 }
41}
42
43impl hash::Hash for Color {
44 fn hash<H: hash::Hasher>(&self, state: &mut H) {
45 self.r.to_canon_bits().hash(state);
46 self.g.to_canon_bits().hash(state);
47 self.b.to_canon_bits().hash(state);
48 self.a.to_canon_bits().hash(state);
49 }
50}
51
52impl Default for Color {
53 fn default() -> Self {
54 Self {
55 r: 0.0,
56 g: 0.0,
57 b: 0.0,
58 a: 1.0,
59 }
60 }
61}
62
63#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
64pub enum FillRule {
65 NonZero,
66 EvenOdd,
67}
68
69impl Default for FillRule {
70 fn default() -> Self {
71 Self::NonZero
72 }
73}
74
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76pub enum GradientType {
77 Linear,
78 Radial,
79}
80
81const NO_STOP: f32 = -1.0;
82
83#[derive(Clone, Debug)]
84pub struct GradientBuilder {
85 r#type: GradientType,
86 start: Point,
87 end: Point,
88 stops: Vec<(Color, f32)>,
89}
90
91impl GradientBuilder {
92 pub fn new(start: Point, end: Point) -> Self {
93 Self {
94 r#type: GradientType::Linear,
95 start,
96 end,
97 stops: Vec::new(),
98 }
99 }
100
101 pub fn r#type(&mut self, r#type: GradientType) -> &mut Self {
102 self.r#type = r#type;
103 self
104 }
105
106 pub fn color(&mut self, color: Color) -> &mut Self {
107 self.stops.push((color, NO_STOP));
108 self
109 }
110
111 pub fn color_with_stop(&mut self, color: Color, stop: f32) -> &mut Self {
112 if !(0.0..=1.0).contains(&stop) {
113 panic!("gradient stops must be between 0.0 and 1.0");
114 }
115
116 self.stops.push((color, stop));
117 self
118 }
119
120 pub fn build(mut self) -> Option<Gradient> {
121 if self.stops.len() < 2 {
122 return None;
123 }
124
125 let stop_increment = 1.0 / (self.stops.len() - 1) as f32;
126 for (i, (_, stop)) in self.stops.iter_mut().enumerate() {
127 if *stop == NO_STOP {
128 *stop = i as f32 * stop_increment;
129 }
130 }
131
132 Some(Gradient {
133 r#type: self.r#type,
134 start: self.start,
135 end: self.end,
136 stops: self.stops.into(),
137 })
138 }
139}
140
141#[derive(Clone, Debug)]
142pub struct Gradient {
143 pub(crate) r#type: GradientType,
144 pub(crate) start: Point,
145 pub(crate) end: Point,
146 pub(crate) stops: Arc<[(Color, f32)]>,
147}
148
149impl Eq for Gradient {}
150
151impl PartialEq for Gradient {
152 fn eq(&self, other: &Self) -> bool {
153 self.r#type == other.r#type
154 && self.start == other.start
155 && self.end == other.end
156 && self.stops == other.stops
157 }
158}
159
160impl hash::Hash for Gradient {
161 fn hash<H: hash::Hasher>(&self, state: &mut H) {
162 self.r#type.hash(state);
163 self.start.hash(state);
164 self.end.hash(state);
165
166 self.stops.len().hash(state);
167 for (color, stop) in self.stops.iter() {
168 (color, stop.to_canon_bits()).hash(state);
169 }
170 }
171}
172
173impl Gradient {
174 pub fn r#type(&self) -> GradientType {
175 self.r#type
176 }
177
178 pub fn start(&self) -> Point {
179 self.start
180 }
181
182 pub fn end(&self) -> Point {
183 self.end
184 }
185
186 #[inline]
187 pub fn colors_with_stops(&self) -> &[(Color, f32)] {
188 &self.stops
189 }
190}
191
192#[derive(Debug)]
193pub enum ImageError {
194 SizeMismatch {
195 len: usize,
196 width: usize,
197 height: usize,
198 },
199 TooLarge,
200}
201
202impl fmt::Display for ImageError {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match self {
205 Self::SizeMismatch { len, width, height } => {
206 write!(
207 f,
208 "buffer has {} pixels, which does not match \
209 the specified width ({}) and height ({})",
210 len, width, height
211 )
212 }
213 Self::TooLarge => {
214 write!(
215 f,
216 "image dimensions exceed what is addressable \
217 with f32; try to reduce the image size."
218 )
219 }
220 }
221 }
222}
223
224#[allow(non_camel_case_types)]
226#[repr(C)]
227#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
228pub(crate) struct f16(u16);
229
230impl f16 {
231 #[inline]
232 pub fn to_f32(self) -> f32 {
233 if self.0 != 0 {
234 f32::from_bits(0x3800_0000 + (u32::from(self.0) << 13))
235 } else {
236 0.0
237 }
238 }
239}
240
241impl From<f32> for f16 {
242 fn from(val: f32) -> Self {
243 if val != 0.0 {
244 f16(((val.to_bits() - 0x3800_0000) >> 13) as u16)
245 } else {
246 f16(0)
247 }
248 }
249}
250
251fn to_linear(l: u8) -> f32 {
253 let l = f32::from(l) * 255.0f32.recip();
254 if l <= 0.04045 {
255 l * 12.92f32.recip()
256 } else {
257 ((l + 0.055) * 1.055f32.recip()).powf(2.4)
258 }
259}
260
261#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
262pub struct ImageId(usize);
263
264impl ImageId {
265 fn new() -> ImageId {
266 static GENERATOR: AtomicUsize = AtomicUsize::new(0);
267
268 ImageId(GENERATOR.fetch_add(1, Ordering::SeqCst))
269 }
270}
271
272#[derive(Clone, Debug)]
273pub struct Image {
274 pub(crate) data: Arc<[[f16; 4]]>,
276 pub(crate) max_x: f32,
278 pub(crate) max_y: f32,
280 pub(crate) width: u32,
282 pub(crate) id: ImageId,
284}
285
286impl Eq for Image {}
287
288impl PartialEq for Image {
289 fn eq(&self, other: &Self) -> bool {
290 self.data.as_ptr() == other.data.as_ptr()
291 && self.max_x == other.max_x
292 && self.max_y == other.max_y
293 }
294}
295
296impl hash::Hash for Image {
297 fn hash<H: hash::Hasher>(&self, state: &mut H) {
298 self.data.as_ptr().hash(state);
299 self.max_x.to_canon_bits().hash(state);
300 self.max_y.to_canon_bits().hash(state);
301 }
302}
303
304impl Image {
305 pub fn from_srgba(data: &[[u8; 4]], width: usize, height: usize) -> Result<Self, ImageError> {
308 let to_alpha = |a| f32::from(a) * f32::from(u8::MAX).recip();
309 let data = data
310 .iter()
311 .map(|c| {
312 [
313 to_linear(c[0]),
314 to_linear(c[1]),
315 to_linear(c[2]),
316 to_alpha(c[3]),
317 ]
318 .map(f16::from)
319 })
320 .collect();
321 Self::new(data, width, height)
322 }
323
324 pub fn from_linear_rgba(
325 data: &[[f32; 4]],
326 width: usize,
327 height: usize,
328 ) -> Result<Self, ImageError> {
329 let data = data.iter().map(|c| c.map(f16::from)).collect();
330 Self::new(data, width, height)
331 }
332
333 fn new(data: Arc<[[f16; 4]]>, width: usize, height: usize) -> Result<Self, ImageError> {
334 match width * height {
335 len if len > u32::MAX as usize => Err(ImageError::TooLarge),
336 len if len != data.len() => Err(ImageError::SizeMismatch {
337 len: data.len(),
338 width,
339 height,
340 }),
341 _ => Ok(Image {
342 data,
343 max_x: width as f32 - 1.0,
344 max_y: height as f32 - 1.0,
345 width: width as u32,
346 id: ImageId::new(),
347 }),
348 }
349 }
350
351 pub fn id(&self) -> ImageId {
352 self.id
353 }
354
355 pub fn width(&self) -> u32 {
356 self.width
357 }
358
359 pub fn height(&self) -> u32 {
360 self.max_y as u32 + 1
361 }
362
363 pub(crate) fn data(&self) -> &[[f16; 4]] {
364 self.data.as_ref()
365 }
366}
367
368#[derive(Clone, Debug, Eq, Hash, PartialEq)]
370pub struct Texture {
371 pub transform: AffineTransform,
373 pub image: Image,
375}
376
377#[derive(Clone, Debug, Eq, Hash, PartialEq)]
378pub enum Fill {
379 Solid(Color),
380 Gradient(Gradient),
381 Texture(Texture),
382}
383
384impl Default for Fill {
385 fn default() -> Self {
386 Self::Solid(Color::default())
387 }
388}
389
390#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
391pub enum BlendMode {
392 Over,
393 Multiply,
394 Screen,
395 Overlay,
396 Darken,
397 Lighten,
398 ColorDodge,
399 ColorBurn,
400 HardLight,
401 SoftLight,
402 Difference,
403 Exclusion,
404 Hue,
405 Saturation,
406 Color,
407 Luminosity,
408}
409
410impl Default for BlendMode {
411 fn default() -> Self {
412 Self::Over
413 }
414}
415
416#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
417pub struct Style {
418 pub is_clipped: bool,
419 pub fill: Fill,
420 pub blend_mode: BlendMode,
421}
422
423#[derive(Clone, Debug, Eq, Hash, PartialEq)]
424pub enum Func {
425 Draw(Style),
426 Clip(usize),
430}
431
432impl Default for Func {
433 fn default() -> Self {
434 Self::Draw(Style::default())
435 }
436}
437
438#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
439pub struct Props {
440 pub fill_rule: FillRule,
441 pub func: Func,
442}
443
444#[cfg(test)]
445mod tests {
446 use std::collections::HashSet;
447
448 use super::*;
449
450 #[test]
451 fn f16_error() {
452 let alpha_mse = (0u8..=255u8)
454 .map(|u| f32::from(u) / 255.0)
455 .map(|v| (v - f16::from(v).to_f32()))
456 .map(|d| d * d)
457 .sum::<f32>()
458 / 256.0;
459 assert!(alpha_mse < 5e-8, "alpha_mse: {}", alpha_mse);
460
461 let alpha_distinct = (0u8..=255u8)
463 .map(|a| f16::from(f32::from(a) / 255.0))
464 .collect::<HashSet<f16>>()
465 .len();
466 assert_eq!(alpha_distinct, 256);
467
468 let component_mse = (0u8..=255u8)
470 .map(to_linear)
471 .map(|v| (v - f16::from(v).to_f32()))
472 .map(|d| d * d)
473 .sum::<f32>()
474 / 256.0;
475 assert!(component_mse < 3e-8, "component_mse: {}", component_mse);
476
477 let component_distinct = (0u8..=255u8)
479 .map(|c| f16::from(to_linear(c)))
480 .collect::<HashSet<f16>>()
481 .len();
482 assert_eq!(component_distinct, 256);
483
484 assert_eq!(f16::from(0.0).to_f32(), 0.0);
486 assert_eq!(f16::from(1.0).to_f32(), 1.0);
487 }
488
489 #[test]
490 fn f16_conversion() {
491 for i in 0..255 {
492 let value = (i as f32) / 255.0;
493 let value_f16 = f16::from(value);
494 assert!(half::f16::from_f32(value).to_bits().abs_diff(value_f16.0) <= 1);
495 assert_eq!(
496 half::f16::from_bits(value_f16.0).to_f32(),
497 value_f16.to_f32()
498 );
499 }
500 }
501}