surpass/painter/
style.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::sync::Arc;
7use std::{fmt, hash};
8
9use crate::simd::{f32x8, u32x8};
10use crate::{AffineTransform, CanonBits, Point};
11
12pub(crate) trait Ratio {
13    fn zero() -> Self;
14    fn one() -> Self;
15}
16
17impl Ratio for f32x8 {
18    #[inline]
19    fn zero() -> Self {
20        f32x8::splat(0.0)
21    }
22
23    #[inline]
24    fn one() -> Self {
25        f32x8::splat(1.0)
26    }
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum Channel {
31    Red,
32    Green,
33    Blue,
34    Alpha,
35    Zero,
36    One,
37}
38
39impl Channel {
40    pub(crate) fn select<R: Ratio>(self, r: R, g: R, b: R, a: R) -> R {
41        match self {
42            Channel::Red => r,
43            Channel::Green => g,
44            Channel::Blue => b,
45            Channel::Alpha => a,
46            Channel::Zero => R::zero(),
47            Channel::One => R::one(),
48        }
49    }
50}
51
52pub const RGBA: [Channel; 4] = [Channel::Red, Channel::Green, Channel::Blue, Channel::Alpha];
53pub const BGRA: [Channel; 4] = [Channel::Blue, Channel::Green, Channel::Red, Channel::Alpha];
54pub const RGB0: [Channel; 4] = [Channel::Red, Channel::Green, Channel::Blue, Channel::Zero];
55pub const BGR0: [Channel; 4] = [Channel::Blue, Channel::Green, Channel::Red, Channel::Zero];
56pub const RGB1: [Channel; 4] = [Channel::Red, Channel::Green, Channel::Blue, Channel::One];
57pub const BGR1: [Channel; 4] = [Channel::Blue, Channel::Green, Channel::Red, Channel::One];
58
59#[derive(Clone, Copy, Debug)]
60pub struct Color {
61    pub r: f32,
62    pub g: f32,
63    pub b: f32,
64    pub a: f32,
65}
66
67impl Eq for Color {}
68
69impl PartialEq for Color {
70    fn eq(&self, other: &Self) -> bool {
71        self.r == other.r && self.g == other.g && self.b == other.b && self.a == other.a
72    }
73}
74
75impl hash::Hash for Color {
76    fn hash<H: hash::Hasher>(&self, state: &mut H) {
77        self.r.to_canon_bits().hash(state);
78        self.g.to_canon_bits().hash(state);
79        self.b.to_canon_bits().hash(state);
80        self.a.to_canon_bits().hash(state);
81    }
82}
83
84impl Color {
85    pub const fn to_array(self) -> [f32; 4] {
86        [self.r, self.g, self.b, self.a]
87    }
88
89    pub(crate) fn max(&self) -> f32 {
90        self.r.max(self.g.max(self.b))
91    }
92    pub(crate) fn min(&self) -> f32 {
93        self.r.min(self.g.min(self.b))
94    }
95
96    pub(crate) fn sorted(&mut self) -> [&mut f32; 3] {
97        let c = [self.r, self.g, self.b];
98
99        match (c[0] < c[1], c[0] < c[2], c[1] < c[2]) {
100            (true, true, true) => [&mut self.r, &mut self.g, &mut self.b],
101            (true, true, false) => [&mut self.r, &mut self.b, &mut self.g],
102            (true, false, _) => [&mut self.b, &mut self.r, &mut self.g],
103            (false, true, true) => [&mut self.g, &mut self.r, &mut self.b],
104            (false, _, false) => [&mut self.b, &mut self.g, &mut self.r],
105            (false, false, true) => [&mut self.g, &mut self.b, &mut self.r],
106        }
107    }
108
109    pub(crate) fn channel(&self, c: Channel) -> f32 {
110        match c {
111            Channel::Red => self.r,
112            Channel::Green => self.g,
113            Channel::Blue => self.b,
114            Channel::Alpha => self.a,
115            Channel::Zero => 0.0,
116            Channel::One => 1.0,
117        }
118    }
119}
120
121impl Default for Color {
122    fn default() -> Self {
123        Self { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }
124    }
125}
126
127#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
128pub enum FillRule {
129    NonZero,
130    EvenOdd,
131}
132
133impl Default for FillRule {
134    fn default() -> Self {
135        Self::NonZero
136    }
137}
138
139#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
140pub enum GradientType {
141    Linear,
142    Radial,
143}
144
145const NO_STOP: f32 = -0.0;
146
147#[derive(Clone, Debug)]
148pub struct GradientBuilder {
149    r#type: GradientType,
150    start: Point,
151    end: Point,
152    stops: Vec<(Color, f32)>,
153}
154
155impl GradientBuilder {
156    pub fn new(start: Point, end: Point) -> Self {
157        Self { r#type: GradientType::Linear, start, end, stops: Vec::new() }
158    }
159
160    pub fn r#type(&mut self, r#type: GradientType) -> &mut Self {
161        self.r#type = r#type;
162        self
163    }
164
165    pub fn color(&mut self, color: Color) -> &mut Self {
166        self.stops.push((color, NO_STOP));
167        self
168    }
169
170    pub fn color_with_stop(&mut self, color: Color, stop: f32) -> &mut Self {
171        if !(0.0..=1.0).contains(&stop) {
172            panic!("gradient stops must be between 0.0 and 1.0");
173        }
174
175        self.stops.push((color, stop));
176        self
177    }
178
179    pub fn build(mut self) -> Option<Gradient> {
180        if self.stops.len() < 2 {
181            return None;
182        }
183
184        let stop_increment = 1.0 / (self.stops.len() - 1) as f32;
185        for (i, (_, stop)) in self.stops.iter_mut().enumerate() {
186            if *stop == NO_STOP {
187                *stop = i as f32 * stop_increment;
188            }
189        }
190
191        Some(Gradient {
192            r#type: self.r#type,
193            start: self.start,
194            end: self.end,
195            stops: self.stops.into(),
196        })
197    }
198}
199
200#[derive(Clone, Debug)]
201pub struct Gradient {
202    r#type: GradientType,
203    start: Point,
204    end: Point,
205    stops: Arc<[(Color, f32)]>,
206}
207
208impl Eq for Gradient {}
209
210impl PartialEq for Gradient {
211    fn eq(&self, other: &Self) -> bool {
212        self.r#type == other.r#type
213            && self.start == other.start
214            && self.end == other.end
215            && self.stops == other.stops
216    }
217}
218
219impl hash::Hash for Gradient {
220    fn hash<H: hash::Hasher>(&self, state: &mut H) {
221        self.r#type.hash(state);
222        self.start.hash(state);
223        self.end.hash(state);
224
225        self.stops.len().hash(state);
226        for (color, stop) in self.stops.iter() {
227            (color, stop.to_canon_bits()).hash(state);
228        }
229    }
230}
231
232impl Gradient {
233    pub fn r#type(&self) -> GradientType {
234        self.r#type
235    }
236
237    pub fn start(&self) -> Point {
238        self.start
239    }
240
241    pub fn end(&self) -> Point {
242        self.end
243    }
244
245    #[inline]
246    pub fn colors_with_stops(&self) -> &[(Color, f32)] {
247        &self.stops
248    }
249
250    fn get_t(&self, x: f32, y: f32) -> f32x8 {
251        let dx = self.end.x - self.start.x;
252        let dy = self.end.y - self.start.y;
253
254        let dot = dx * dx + dy * dy;
255        let dot_recip = dot.recip();
256
257        match self.r#type {
258            GradientType::Linear => {
259                let tx = (x - self.start.x) * dx * dot_recip;
260                let ty = y - self.start.y;
261
262                ((f32x8::indexed() + f32x8::splat(ty)) * f32x8::splat(dy))
263                    .mul_add(f32x8::splat(dot_recip), f32x8::splat(tx))
264            }
265            GradientType::Radial => {
266                let px = x - self.start.x;
267                let px2 = f32x8::splat(px * px);
268                let py = f32x8::indexed() + f32x8::splat(y - self.start.y);
269
270                (py.mul_add(py, px2) * f32x8::splat(dot_recip)).sqrt()
271            }
272        }
273    }
274
275    pub(crate) fn color_at(&self, x: f32, y: f32) -> [f32x8; 4] {
276        let mut channels = [f32x8::splat(0.0); 4];
277
278        let t = self.get_t(x, y);
279
280        let mask = t.le(f32x8::splat(self.stops[0].1));
281        if mask.any() {
282            let stop = self.stops[0].0;
283            for (channel, &stop_channel) in
284                channels.iter_mut().zip([stop.r, stop.g, stop.b, stop.a].iter())
285            {
286                *channel |= f32x8::splat(stop_channel).select(f32x8::splat(0.0), mask);
287            }
288        }
289
290        let mut start_stop = 0.0;
291        let mut start_color = self.stops[0].0;
292        let mut acc_mask = mask;
293
294        for &(color, end_stop) in self.stops.iter().skip(1) {
295            let mask = acc_mask ^ t.lt(f32x8::splat(end_stop));
296            if mask.any() {
297                let d = end_stop - start_stop;
298                let local_t = (t - f32x8::splat(start_stop)) * f32x8::splat(d.recip());
299
300                for (channel, (&start_channel, &end_channel)) in channels.iter_mut().zip(
301                    [start_color.r, start_color.g, start_color.b, start_color.a]
302                        .iter()
303                        .zip([color.r, color.g, color.b, color.a].iter()),
304                ) {
305                    *channel |= local_t
306                        .mul_add(
307                            f32x8::splat(end_channel),
308                            (-local_t)
309                                .mul_add(f32x8::splat(start_channel), f32x8::splat(start_channel)),
310                        )
311                        .select(f32x8::splat(0.0), mask);
312                }
313
314                acc_mask |= mask;
315            }
316
317            start_stop = end_stop;
318            start_color = color;
319        }
320
321        let mask = !acc_mask;
322        if mask.any() {
323            let stop = self.stops[self.stops.len() - 1].0;
324            for (channel, &stop_channel) in
325                channels.iter_mut().zip([stop.r, stop.g, stop.b, stop.a].iter())
326            {
327                *channel |= f32x8::splat(stop_channel).select(f32x8::splat(0.0), mask);
328            }
329        }
330
331        channels
332    }
333}
334#[derive(Debug)]
335pub enum ImageError {
336    SizeMismatch { len: usize, width: usize, height: usize },
337    TooLarge,
338}
339
340impl fmt::Display for ImageError {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        match self {
343            Self::SizeMismatch { len, width, height } => {
344                write!(
345                    f,
346                    "buffer has {} pixels, which does not match \
347                     the specified width ({}) and height ({})",
348                    len, width, height
349                )
350            }
351            Self::TooLarge => {
352                write!(
353                    f,
354                    "image dimensions exceed what is addressable \
355                     with f32; try to reduce the image size."
356                )
357            }
358        }
359    }
360}
361
362/// f16 value without denormals and within 0 and one.
363#[allow(non_camel_case_types)]
364#[repr(C)]
365#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
366pub struct f16(u16);
367
368impl f16 {
369    #[inline]
370    fn to_f32(self) -> f32 {
371        if self.0 != 0 {
372            f32::from_bits(0x3800_0000 + (u32::from(self.0) << 13))
373        } else {
374            0.0
375        }
376    }
377}
378
379impl From<f32> for f16 {
380    fn from(val: f32) -> Self {
381        if val != 0.0 {
382            f16(((val.to_bits() - 0x3800_0000) >> 13) as u16)
383        } else {
384            f16(0)
385        }
386    }
387}
388
389/// Transforms `sRGB` component into linear space.
390fn to_linear(l: u8) -> f32 {
391    let l = f32::from(l) * 255.0f32.recip();
392    if l <= 0.04045 {
393        l * 12.92f32.recip()
394    } else {
395        ((l + 0.055) * 1.055f32.recip()).powf(2.4)
396    }
397}
398
399#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
400pub struct ImageId(usize);
401impl ImageId {
402    fn new() -> ImageId {
403        static GENERATOR: AtomicUsize = AtomicUsize::new(0);
404
405        ImageId(GENERATOR.fetch_add(1, Ordering::SeqCst))
406    }
407}
408
409#[derive(Clone, Debug)]
410pub struct Image {
411    /// Pixels RGBA.
412    data: Box<[[f16; 4]]>,
413    /// Largest x coordinate within the Image.
414    max_x: f32,
415    /// Largest y coordinate within the Image.
416    max_y: f32,
417    /// Width of the image in pixels.
418    width: u32,
419    /// Unique identifier for this image.
420    id: ImageId,
421}
422
423impl Eq for Image {}
424
425impl PartialEq for Image {
426    fn eq(&self, other: &Self) -> bool {
427        self.data.as_ptr() == other.data.as_ptr()
428            && self.max_x == other.max_x
429            && self.max_y == other.max_y
430    }
431}
432
433impl hash::Hash for Image {
434    fn hash<H: hash::Hasher>(&self, state: &mut H) {
435        self.data.as_ptr().hash(state);
436        self.max_x.to_canon_bits().hash(state);
437        self.max_y.to_canon_bits().hash(state);
438    }
439}
440
441impl Image {
442    /// Creates an image from `sRGB` color channels and linear alpha.
443    /// The boxed array size must match the image dimensions.
444    pub fn from_srgba(data: &[[u8; 4]], width: usize, height: usize) -> Result<Self, ImageError> {
445        let to_alpha = |a| f32::from(a) * f32::from(u8::MAX).recip();
446        let data = data
447            .iter()
448            .map(|c| {
449                [to_linear(c[0]), to_linear(c[1]), to_linear(c[2]), to_alpha(c[3])].map(f16::from)
450            })
451            .collect();
452        Self::new(data, width, height)
453    }
454
455    pub fn from_linear_rgba(
456        data: &[[f32; 4]],
457        width: usize,
458        height: usize,
459    ) -> Result<Self, ImageError> {
460        let data = data.iter().map(|c| c.map(f16::from)).collect();
461        Self::new(data, width, height)
462    }
463
464    fn new(data: Box<[[f16; 4]]>, width: usize, height: usize) -> Result<Self, ImageError> {
465        match width * height {
466            len if len > u32::MAX as usize => Err(ImageError::TooLarge),
467            len if len != data.len() => {
468                Err(ImageError::SizeMismatch { len: data.len(), width, height })
469            }
470            _ => Ok(Image {
471                data,
472                max_x: width as f32 - 1.0,
473                max_y: height as f32 - 1.0,
474                width: width as u32,
475                id: ImageId::new(),
476            }),
477        }
478    }
479
480    pub fn id(&self) -> ImageId {
481        self.id
482    }
483
484    pub fn width(&self) -> u32 {
485        self.width
486    }
487
488    pub fn height(&self) -> u32 {
489        self.max_y as u32 + 1
490    }
491
492    pub fn data(&self) -> &[[f16; 4]] {
493        self.data.as_ref()
494    }
495}
496
497/// Describes how to shade a surface using a bitmap image.
498#[derive(Clone, Debug, Eq, Hash, PartialEq)]
499pub struct Texture {
500    /// Transformation from screen-space to texture-space.
501    pub transform: AffineTransform,
502    /// Image shared with zero or more textures.
503    pub image: Arc<Image>,
504}
505
506impl Texture {
507    #[inline]
508    pub(crate) fn color_at(&self, x: f32, y: f32) -> [f32x8; 4] {
509        let x = f32x8::splat(x);
510        let y = f32x8::splat(y) + f32x8::indexed();
511        // Apply affine transformation.
512        let t = self.transform;
513        let tx = x.mul_add(f32x8::splat(t.ux), f32x8::splat(t.vx).mul_add(y, f32x8::splat(t.tx)));
514        let ty = x.mul_add(f32x8::splat(t.uy), f32x8::splat(t.vy).mul_add(y, f32x8::splat(t.ty)));
515
516        // Assume that conversion clamp to 0.
517        let tx = u32x8::from(tx.min(f32x8::splat(self.image.max_x)));
518        let ty = u32x8::from(ty.min(f32x8::splat(self.image.max_y)));
519        // Compute texture offsets.
520        // Largest consecutive integer is 2^24
521        let offsets = ty.mul_add(u32x8::splat(self.image.width), tx);
522
523        let data = &*self.image.data;
524        let pixels = offsets.to_array().map(|o| unsafe { data.get_unchecked(o as usize) });
525
526        let get_channel = |c: usize| {
527            f32x8::from_array([
528                pixels[0][c].to_f32(),
529                pixels[1][c].to_f32(),
530                pixels[2][c].to_f32(),
531                pixels[3][c].to_f32(),
532                pixels[4][c].to_f32(),
533                pixels[5][c].to_f32(),
534                pixels[6][c].to_f32(),
535                pixels[7][c].to_f32(),
536            ])
537        };
538        [get_channel(0), get_channel(1), get_channel(2), get_channel(3)]
539    }
540}
541
542#[derive(Clone, Debug, Eq, Hash, PartialEq)]
543pub enum Fill {
544    Solid(Color),
545    Gradient(Gradient),
546    Texture(Texture),
547}
548
549impl Default for Fill {
550    fn default() -> Self {
551        Self::Solid(Color::default())
552    }
553}
554
555#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
556pub enum BlendMode {
557    Over,
558    Multiply,
559    Screen,
560    Overlay,
561    Darken,
562    Lighten,
563    ColorDodge,
564    ColorBurn,
565    HardLight,
566    SoftLight,
567    Difference,
568    Exclusion,
569    Hue,
570    Saturation,
571    Color,
572    Luminosity,
573}
574
575impl BlendMode {
576    pub(crate) fn blend_fn(self) -> fn(Channel, Color, Color) -> f32 {
577        fn multiply(dst: f32, src: f32) -> f32 {
578            dst * src
579        }
580
581        fn screen(dst: f32, src: f32) -> f32 {
582            dst + src - (dst * src)
583        }
584
585        fn hard_light(dst: f32, src: f32) -> f32 {
586            if src <= 0.5 {
587                multiply(dst, 2.0 * src)
588            } else {
589                screen(dst, 2.0 * src - 1.0)
590            }
591        }
592
593        fn lum(color: Color) -> f32 {
594            color.r.mul_add(0.3, color.g.mul_add(0.59, color.b * 0.11))
595        }
596
597        fn clip_color(c: Channel, color: Color) -> f32 {
598            let l = lum(color);
599            let n = color.min();
600            let x = color.max();
601            let mut c = color.channel(c);
602
603            if n < 0.0 {
604                let l_n_recip_l = (l - n).recip() * l;
605                c = l_n_recip_l.mul_add(c - l, l);
606            }
607
608            if x > 1.0 {
609                let l_1 = l - 1.0;
610                let x_l_recip = (x - l).recip();
611                c = x_l_recip.mul_add(l.mul_add(l_1 - c, c), l);
612            }
613            c
614        }
615
616        fn set_lum(c: Channel, mut color: Color, l: f32) -> f32 {
617            let d = l - lum(color);
618            color.r += d;
619            color.g += d;
620            color.b += d;
621            clip_color(c, color)
622        }
623
624        fn sat(color: Color) -> f32 {
625            color.max() - color.min()
626        }
627
628        fn set_sat(mut color: Color, s: f32) -> Color {
629            let [c_min, c_mid, c_max] = color.sorted();
630            if c_max > c_min {
631                *c_mid = s.mul_add(*c_mid, -s * *c_min) / (*c_max - *c_min);
632                *c_max = s;
633            } else {
634                *c_mid = 0.0;
635                *c_max = 0.0;
636            }
637            *c_min = 0.0;
638            color
639        }
640
641        match self {
642            Self::Over => |c, _, src| src.channel(c),
643            Self::Multiply => |c, dst, src| multiply(dst.channel(c), src.channel(c)),
644            Self::Screen => |c, dst, src| screen(dst.channel(c), src.channel(c)),
645            Self::Overlay => |c, dst, src| hard_light(src.channel(c), dst.channel(c)),
646            Self::Darken => |c, dst, src| dst.channel(c).min(src.channel(c)),
647            Self::Lighten => |c, dst, src| dst.channel(c).max(src.channel(c)),
648            Self::ColorDodge => |c, dst, src| {
649                if dst.channel(c) == 0.0 {
650                    0.0
651                } else if src.channel(c) == 1.0 {
652                    1.0
653                } else {
654                    1.0f32.min(dst.channel(c) / (1.0 - src.channel(c)))
655                }
656            },
657            Self::ColorBurn => |c, dst, src| {
658                if dst.channel(c) == 1.0 {
659                    1.0
660                } else if src.channel(c) == 0.0 {
661                    0.0
662                } else {
663                    1.0 - 1.0f32.min((1.0 - dst.channel(c)) / src.channel(c))
664                }
665            },
666            Self::HardLight => |c, dst, src| hard_light(dst.channel(c), src.channel(c)),
667            Self::SoftLight => |c, dst, src| {
668                fn d(dst: f32) -> f32 {
669                    if dst <= 0.25 {
670                        ((16.0 * dst - 12.0) * dst + 4.0) * dst
671                    } else {
672                        dst.sqrt()
673                    }
674                }
675
676                if src.channel(c) <= 0.5 {
677                    dst.channel(c)
678                        - (1.0 - 2.0 * src.channel(c)) * dst.channel(c) * (1.0 - dst.channel(c))
679                } else {
680                    dst.channel(c)
681                        + (2.0 * src.channel(c) - 1.0) * (d(dst.channel(c)) - dst.channel(c))
682                }
683            },
684            Self::Difference => |c, dst, src| (dst.channel(c) - src.channel(c)).abs(),
685            Self::Exclusion => |c, dst, src| {
686                dst.channel(c) + src.channel(c) - 2.0 * dst.channel(c) * src.channel(c)
687            },
688            Self::Color => |c, dst, src| set_lum(c, src, lum(dst)),
689            Self::Luminosity => |c, dst, src| set_lum(c, dst, lum(src)),
690            Self::Hue => |c, dst, src| set_lum(c, set_sat(src, sat(dst)), lum(dst)),
691            Self::Saturation => |c, dst, src| set_lum(c, set_sat(dst, sat(src)), lum(dst)),
692        }
693    }
694
695    pub(crate) fn blend(self, dst: Color, src: Color) -> Color {
696        let f = self.blend_fn();
697
698        let inv_dst_a = 1.0 - dst.a;
699        let inv_dst_a_src_a = inv_dst_a * src.a;
700        let inv_src_a = 1.0 - src.a;
701        let dst_a_src_a = dst.a * src.a;
702
703        let current_r = src.r.mul_add(inv_dst_a_src_a, f(Channel::Red, dst, src) * dst_a_src_a);
704        let current_g = src.g.mul_add(inv_dst_a_src_a, f(Channel::Green, dst, src) * dst_a_src_a);
705        let current_b = src.b.mul_add(inv_dst_a_src_a, f(Channel::Blue, dst, src) * dst_a_src_a);
706
707        Color {
708            r: dst.r.mul_add(inv_src_a, current_r),
709            g: dst.g.mul_add(inv_src_a, current_g),
710            b: dst.b.mul_add(inv_src_a, current_b),
711            a: dst.a.mul_add(inv_src_a, src.a),
712        }
713    }
714}
715
716macro_rules! blend_function {
717    (
718        $mode:expr,
719        $dst_r:expr,
720        $dst_g:expr,
721        $dst_b:expr,
722        $src_r:expr,
723        $src_g:expr,
724        $src_b:expr
725        $( , )?
726    ) => {{
727        macro_rules! lum {
728            ($r:expr, $g:expr, $b:expr) => {
729                $r.mul_add(
730                    f32x8::splat(0.3),
731                    $g.mul_add(f32x8::splat(0.59), $b * f32x8::splat(0.11)),
732                )
733            };
734        }
735
736        macro_rules! sat {
737            ($r:expr, $g:expr, $b:expr) => {
738                $r.max($g.max($b)) - $r.min($g.min($b))
739            };
740        }
741
742        macro_rules! clip_color {
743            ($r:expr, $g:expr, $b:expr) => {{
744                let l = lum!($r, $g, $b);
745                let n = $r.min($g.min($b));
746                let x = $r.max($g.max($b));
747                let l_1 = l - f32x8::splat(1.0);
748                let x_l_recip = (x - l).recip();
749                let l_n_recip_l = (l - n).recip() * l;
750
751                [
752                    x_l_recip.mul_add(l.mul_add(l_1 - $r, $r), l).select(
753                        l_n_recip_l.mul_add($r - l, l).select($r, n.lt(f32x8::splat(0.0))),
754                        f32x8::splat(1.0).lt(x),
755                    ),
756                    x_l_recip.mul_add(l.mul_add(l_1 - $g, $g), l).select(
757                        l_n_recip_l.mul_add($g - l, l).select($g, n.lt(f32x8::splat(0.0))),
758                        f32x8::splat(1.0).lt(x),
759                    ),
760                    x_l_recip.mul_add(l.mul_add(l_1 - $b, $b), l).select(
761                        l_n_recip_l.mul_add($b - l, l).select($b, n.lt(f32x8::splat(0.0))),
762                        f32x8::splat(1.0).lt(x),
763                    ),
764                ]
765            }};
766        }
767
768        macro_rules! set_lum {
769            ($r:expr, $g:expr, $b:expr, $l:expr) => {{
770                let d = $l - lum!($r, $g, $b);
771                $r += d;
772                $g += d;
773                $b += d;
774                clip_color!($r, $g, $b)
775            }};
776        }
777
778        macro_rules! set_sat {
779            ($sat_dst:expr, $s_r:expr, $s_g:expr, $s_b:expr) => {{
780                let src_min = $s_r.min($s_g.min($s_b));
781                let src_max = $s_r.max($s_g.max($s_b));
782                let src_mid = $s_r + $s_g + $s_b - src_min - src_max;
783                let min_lt_max = src_min.lt(src_max);
784                let sat_mid = ($sat_dst.mul_add(-src_min, $sat_dst * src_mid)
785                    / (src_max - src_min))
786                    .select(f32x8::splat(0.0), min_lt_max);
787                let sat_max = $sat_dst.select(f32x8::splat(0.0), min_lt_max);
788
789                [
790                    sat_max.select(
791                        f32x8::splat(0.0).select(sat_mid, $s_r.eq(src_min)),
792                        $s_r.eq(src_max),
793                    ),
794                    sat_max.select(
795                        f32x8::splat(0.0).select(sat_mid, $s_g.eq(src_min)),
796                        $s_g.eq(src_max),
797                    ),
798                    sat_max.select(
799                        f32x8::splat(0.0).select(sat_mid, $s_b.eq(src_min)),
800                        $s_b.eq(src_max),
801                    ),
802                ]
803            }};
804        }
805
806        match $mode {
807            BlendMode::Over => [$src_r, $src_g, $src_b],
808            BlendMode::Multiply => [$dst_r * $src_r, $dst_g * $src_g, $dst_b * $src_b],
809            BlendMode::Screen => [
810                $dst_r.mul_add(-$src_r, $dst_r) + $src_r,
811                $dst_g.mul_add(-$src_g, $dst_g) + $src_g,
812                $dst_b.mul_add(-$src_b, $dst_b) + $src_b,
813            ],
814            BlendMode::Overlay => [
815                ($dst_r * $src_r * f32x8::splat(2.0)).select(
816                    f32x8::splat(2.0)
817                        * ($dst_r + $src_r - $dst_r.mul_add($src_r, f32x8::splat(0.5))),
818                    $dst_r.le(f32x8::splat(0.5)),
819                ),
820                ($dst_g * $src_g * f32x8::splat(2.0)).select(
821                    f32x8::splat(2.0)
822                        * ($dst_g + $src_g - $dst_g.mul_add($src_g, f32x8::splat(0.5))),
823                    $dst_g.le(f32x8::splat(0.5)),
824                ),
825                ($dst_b * $src_b * f32x8::splat(2.0)).select(
826                    f32x8::splat(2.0)
827                        * ($dst_b + $src_b - $dst_b.mul_add($src_b, f32x8::splat(0.5))),
828                    $dst_b.le(f32x8::splat(0.5)),
829                ),
830            ],
831            BlendMode::Darken => [$dst_r.min($src_r), $dst_g.min($src_g), $dst_b.min($src_b)],
832            BlendMode::Lighten => [$dst_r.max($src_r), $dst_g.max($src_g), $dst_b.max($src_b)],
833            BlendMode::ColorDodge => [
834                f32x8::splat(1.0).select(
835                    f32x8::splat(1.0).min($dst_r / (f32x8::splat(1.0) - $src_r)),
836                    $src_r.eq(f32x8::splat(1.0)),
837                ),
838                f32x8::splat(1.0).select(
839                    f32x8::splat(1.0).min($dst_g / (f32x8::splat(1.0) - $src_g)),
840                    $src_g.eq(f32x8::splat(1.0)),
841                ),
842                f32x8::splat(1.0).select(
843                    f32x8::splat(1.0).min($dst_b / (f32x8::splat(1.0) - $src_b)),
844                    $src_b.eq(f32x8::splat(1.0)),
845                ),
846            ],
847            BlendMode::ColorBurn => [
848                f32x8::splat(0.0).select(
849                    f32x8::splat(1.0)
850                        - f32x8::splat(1.0).min((f32x8::splat(1.0) - $dst_r) / $src_r),
851                    $src_r.eq(f32x8::splat(0.0)),
852                ),
853                f32x8::splat(0.0).select(
854                    f32x8::splat(1.0)
855                        - f32x8::splat(1.0).min((f32x8::splat(1.0) - $dst_g) / $src_g),
856                    $src_g.eq(f32x8::splat(0.0)),
857                ),
858                f32x8::splat(0.0).select(
859                    f32x8::splat(1.0)
860                        - f32x8::splat(1.0).min((f32x8::splat(1.0) - $dst_b) / $src_b),
861                    $src_b.eq(f32x8::splat(0.0)),
862                ),
863            ],
864            BlendMode::HardLight => [
865                ($dst_r * $src_r * f32x8::splat(2.0)).select(
866                    f32x8::splat(2.0)
867                        * ($dst_r + $src_r - $dst_r.mul_add($src_r, f32x8::splat(0.5))),
868                    $src_r.le(f32x8::splat(0.5)),
869                ),
870                ($dst_g * $src_g * f32x8::splat(2.0)).select(
871                    f32x8::splat(2.0)
872                        * ($dst_g + $src_g - $dst_g.mul_add($src_g, f32x8::splat(0.5))),
873                    $src_g.le(f32x8::splat(0.5)),
874                ),
875                ($dst_b * $src_b * f32x8::splat(2.0)).select(
876                    f32x8::splat(2.0)
877                        * ($dst_b + $src_b - $dst_b.mul_add($src_b, f32x8::splat(0.5))),
878                    $src_b.le(f32x8::splat(0.5)),
879                ),
880            ],
881            BlendMode::SoftLight => {
882                let d0 = (f32x8::splat(16.0)
883                    .mul_add($dst_r, f32x8::splat(-12.0))
884                    .mul_add($dst_r, f32x8::splat(4.0))
885                    * $dst_r)
886                    .select($dst_r.sqrt(), $dst_r.le(f32x8::splat(0.25)));
887                let d1 = (f32x8::splat(16.0)
888                    .mul_add($dst_g, f32x8::splat(-12.0))
889                    .mul_add($dst_g, f32x8::splat(4.0))
890                    * $dst_g)
891                    .select($dst_g.sqrt(), $dst_g.le(f32x8::splat(0.25)));
892                let d2 = (f32x8::splat(16.0)
893                    .mul_add($dst_b, f32x8::splat(-12.0))
894                    .mul_add($dst_b, f32x8::splat(4.0))
895                    * $dst_b)
896                    .select($dst_b.sqrt(), $dst_b.le(f32x8::splat(0.25)));
897
898                [
899                    (($dst_r * (f32x8::splat(1.0) - $dst_r))
900                        .mul_add(f32x8::splat(2.0).mul_add($src_r, f32x8::splat(-1.0)), $dst_r))
901                    .select(
902                        (d0 - $dst_r)
903                            .mul_add(f32x8::splat(2.0).mul_add($src_r, f32x8::splat(-1.0)), $dst_r),
904                        $src_r.le(f32x8::splat(0.5)),
905                    ),
906                    (($dst_g * (f32x8::splat(1.0) - $dst_g))
907                        .mul_add(f32x8::splat(2.0).mul_add($src_g, f32x8::splat(-1.0)), $dst_g))
908                    .select(
909                        (d1 - $dst_g)
910                            .mul_add(f32x8::splat(2.0).mul_add($src_g, f32x8::splat(-1.0)), $dst_g),
911                        $src_g.le(f32x8::splat(0.5)),
912                    ),
913                    (($dst_b * (f32x8::splat(1.0) - $dst_b))
914                        .mul_add(f32x8::splat(2.0).mul_add($src_b, f32x8::splat(-1.0)), $dst_b))
915                    .select(
916                        (d2 - $dst_b)
917                            .mul_add(f32x8::splat(2.0).mul_add($src_b, f32x8::splat(-1.0)), $dst_b),
918                        $src_b.le(f32x8::splat(0.5)),
919                    ),
920                ]
921            }
922            BlendMode::Difference => {
923                [($dst_r - $src_r).abs(), ($dst_g - $src_g).abs(), ($dst_b - $src_b).abs()]
924            }
925            BlendMode::Exclusion => [
926                (f32x8::splat(-2.0) * $dst_r).mul_add($src_r, $dst_r) + $src_r,
927                (f32x8::splat(-2.0) * $dst_g).mul_add($src_g, $dst_g) + $src_g,
928                (f32x8::splat(-2.0) * $dst_b).mul_add($src_b, $dst_b) + $src_b,
929            ],
930            BlendMode::Hue => {
931                let mut src = set_sat!(sat!($dst_r, $dst_g, $dst_b), $src_r, $src_g, $src_b);
932                set_lum!(src[0], src[1], src[2], lum!($dst_r, $dst_g, $dst_b))
933            }
934            BlendMode::Saturation => {
935                let mut dst = set_sat!(sat!($src_r, $src_g, $src_b), $dst_r, $dst_g, $dst_b);
936                set_lum!(dst[0], dst[1], dst[2], lum!($dst_r, $dst_g, $dst_b))
937            }
938            BlendMode::Color => {
939                let mut src = [$src_r, $src_g, $src_b];
940                set_lum!(src[0], src[1], src[2], lum!($dst_r, $dst_g, $dst_b))
941            }
942            BlendMode::Luminosity => {
943                let mut dst = [$dst_r, $dst_g, $dst_b];
944                set_lum!(dst[0], dst[1], dst[2], lum!($src_r, $src_g, $src_b))
945            }
946        }
947    }};
948}
949
950impl Default for BlendMode {
951    fn default() -> Self {
952        Self::Over
953    }
954}
955
956#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
957pub struct Style {
958    pub is_clipped: bool,
959    pub fill: Fill,
960    pub blend_mode: BlendMode,
961}
962
963#[cfg(test)]
964mod tests {
965    use std::collections::HashSet;
966
967    use super::*;
968
969    const EPSILON: f32 = 0.001;
970
971    macro_rules! color {
972        ( $val:expr ) => {
973            Color { r: $val, g: $val, b: $val, a: $val }
974        };
975    }
976
977    macro_rules! color_array {
978        ( $val:expr ) => {
979            [$val, $val, $val, $val]
980        };
981    }
982
983    macro_rules! color_eq {
984        ( $val:expr ) => {{
985            assert_eq!($val[0], $val[1]);
986            assert_eq!($val[1], $val[2]);
987            assert_eq!($val[2], $val[3]);
988
989            $val[0]
990        }};
991    }
992
993    macro_rules! simd_assert_approx {
994        ( $left:expr, $right:expr ) => {{
995            assert!(
996                ($left - $right).abs().le(f32x8::splat(EPSILON)).all(),
997                "{:?} != {:?}",
998                $left,
999                $right,
1000            );
1001        }};
1002    }
1003
1004    impl Ratio for [f32; 8] {
1005        fn zero() -> Self {
1006            [0.0; 8]
1007        }
1008
1009        fn one() -> Self {
1010            [1.0; 8]
1011        }
1012    }
1013
1014    fn colors(separate: &[f32x8; 4]) -> [[f32; 4]; 8] {
1015        let mut colors = [[0.0, 0.0, 0.0, 0.0]; 8];
1016
1017        for (i, color) in colors.iter_mut().enumerate() {
1018            *color = [
1019                separate[0].to_array()[i],
1020                separate[1].to_array()[i],
1021                separate[2].to_array()[i],
1022                separate[3].to_array()[i],
1023            ];
1024        }
1025
1026        colors
1027    }
1028
1029    fn test_blend_mode(blend_mode: BlendMode) {
1030        let color_values = [
1031            Color { r: 0.125, g: 0.25, b: 0.625, a: 0.5 },
1032            Color { r: 0.25, g: 0.125, b: 0.75, a: 0.5 },
1033            Color { r: 0.625, g: 0.5, b: 0.125, a: 0.5 },
1034            Color { r: 0.375, g: 1.0, b: 0.875, a: 0.5 },
1035            Color { r: 0.5, g: 0.5, b: 0.5, a: 0.5 },
1036            Color { r: 0.875, g: 0.125, b: 0.0, a: 0.5 },
1037        ];
1038        let f = blend_mode.blend_fn();
1039
1040        for &dst in &color_values {
1041            for &src in &color_values {
1042                let [r, g, b] = blend_function!(
1043                    blend_mode,
1044                    f32x8::splat(dst.r),
1045                    f32x8::splat(dst.g),
1046                    f32x8::splat(dst.b),
1047                    f32x8::splat(src.r),
1048                    f32x8::splat(src.g),
1049                    f32x8::splat(src.b),
1050                );
1051
1052                simd_assert_approx!(r, f32x8::splat(f(Channel::Red, dst, src)));
1053                simd_assert_approx!(g, f32x8::splat(f(Channel::Green, dst, src)));
1054                simd_assert_approx!(b, f32x8::splat(f(Channel::Blue, dst, src)));
1055            }
1056        }
1057    }
1058
1059    #[test]
1060    fn linear_gradient() {
1061        let mut builder = GradientBuilder::new(Point::new(0.0, 7.0), Point::new(7.0, 0.0));
1062
1063        builder
1064            .color(color!(0.25))
1065            .color(color!(0.75))
1066            .color(color!(0.25))
1067            .color(color!(0.75))
1068            .color(color!(0.25));
1069
1070        let gradient = builder.build().unwrap();
1071
1072        let col = colors(&gradient.color_at(0.0, 0.0));
1073        assert_eq!(col[0], color_array!(0.25));
1074        assert!(color_eq!(col[1]) < color_eq!(col[2]));
1075        assert!(color_eq!(col[2]) < color_eq!(col[3]));
1076        assert!(color_eq!(col[4]) > color_eq!(col[5]));
1077        assert!(color_eq!(col[5]) > color_eq!(col[6]));
1078        assert_eq!(col[7], color_array!(0.25));
1079
1080        let col = colors(&gradient.color_at(3.0, 0.0));
1081        assert!(color_eq!(col[0]) < 0.75);
1082        assert!(color_eq!(col[1]) > color_eq!(col[2]));
1083        assert!(color_eq!(col[2]) > color_eq!(col[3]));
1084        assert_eq!(col[3], color_array!(0.25));
1085        assert!(color_eq!(col[3]) < color_eq!(col[4]));
1086        assert!(color_eq!(col[4]) < color_eq!(col[5]));
1087        assert!(color_eq!(col[5]) < color_eq!(col[6]));
1088        assert!(color_eq!(col[7]) < 0.75);
1089
1090        let col = colors(&gradient.color_at(7.0, 0.0));
1091        assert_eq!(col[0], color_array!(0.25));
1092        assert!(color_eq!(col[1]) < color_eq!(col[2]));
1093        assert!(color_eq!(col[2]) < color_eq!(col[3]));
1094        assert!(color_eq!(col[4]) > color_eq!(col[5]));
1095        assert!(color_eq!(col[5]) > color_eq!(col[6]));
1096        assert_eq!(col[7], color_array!(0.25));
1097    }
1098
1099    #[test]
1100    fn radial_gradient() {
1101        let mut builder = GradientBuilder::new(
1102            Point::new(0.0, 0.0),
1103            Point::new(7.0 * (1.0 / 2.0f32.sqrt()), 7.0 * (1.0 / 2.0f32.sqrt())),
1104        );
1105
1106        builder.r#type(GradientType::Radial).color(color!(0.25)).color(color!(0.75));
1107
1108        let gradient = builder.build().unwrap();
1109
1110        let col = colors(&gradient.color_at(0.0, 0.0));
1111        assert_eq!(col[0], color_array!(0.25));
1112        assert!(color_eq!(col[1]) < color_eq!(col[2]));
1113        assert!(color_eq!(col[2]) < color_eq!(col[3]));
1114        assert!(color_eq!(col[3]) < color_eq!(col[4]));
1115        assert!(color_eq!(col[4]) < color_eq!(col[5]));
1116        assert!(color_eq!(col[5]) < color_eq!(col[6]));
1117        assert_eq!(col[7], color_array!(0.75));
1118
1119        let col = colors(&gradient.color_at(3.0, 0.0));
1120        assert!(color_eq!(col[0]) < color_eq!(col[1]));
1121        assert!(color_eq!(col[1]) < color_eq!(col[2]));
1122        assert!(color_eq!(col[2]) < color_eq!(col[3]));
1123        assert!(color_eq!(col[3]) < color_eq!(col[4]));
1124        assert!(color_eq!(col[4]) < color_eq!(col[5]));
1125        assert!(color_eq!(col[5]) < color_eq!(col[6]));
1126        assert_eq!(col[7], color_array!(0.75));
1127
1128        let col = colors(&gradient.color_at(4.0, 0.0));
1129        assert!(color_eq!(col[0]) < color_eq!(col[1]));
1130        assert!(color_eq!(col[1]) < color_eq!(col[2]));
1131        assert!(color_eq!(col[2]) < color_eq!(col[3]));
1132        assert!(color_eq!(col[3]) < color_eq!(col[4]));
1133        assert!(color_eq!(col[4]) < color_eq!(col[5]));
1134        assert_eq!(col[6], color_array!(0.75));
1135        assert_eq!(col[7], color_array!(0.75));
1136
1137        let col = colors(&gradient.color_at(7.0, 0.0));
1138        assert_eq!(col[0], color_array!(0.75));
1139        assert_eq!(col[1], color_array!(0.75));
1140        assert_eq!(col[2], color_array!(0.75));
1141        assert_eq!(col[3], color_array!(0.75));
1142        assert_eq!(col[4], color_array!(0.75));
1143        assert_eq!(col[5], color_array!(0.75));
1144        assert_eq!(col[6], color_array!(0.75));
1145        assert_eq!(col[7], color_array!(0.75));
1146    }
1147
1148    #[test]
1149    fn test_blend_mode_over() {
1150        test_blend_mode(BlendMode::Over);
1151    }
1152
1153    #[test]
1154    fn test_blend_mode_multiply() {
1155        test_blend_mode(BlendMode::Multiply);
1156    }
1157
1158    #[test]
1159    fn test_blend_mode_screen() {
1160        test_blend_mode(BlendMode::Screen);
1161    }
1162
1163    #[test]
1164    fn test_blend_mode_overlay() {
1165        test_blend_mode(BlendMode::Overlay);
1166    }
1167
1168    #[test]
1169    fn test_blend_mode_darken() {
1170        test_blend_mode(BlendMode::Darken);
1171    }
1172
1173    #[test]
1174    fn test_blend_mode_lighten() {
1175        test_blend_mode(BlendMode::Lighten);
1176    }
1177
1178    #[test]
1179    fn test_blend_mode_color_dodge() {
1180        test_blend_mode(BlendMode::ColorDodge);
1181    }
1182
1183    #[test]
1184    fn test_blend_mode_color_burn() {
1185        test_blend_mode(BlendMode::ColorBurn);
1186    }
1187
1188    #[test]
1189    fn test_blend_mode_hard_light() {
1190        test_blend_mode(BlendMode::HardLight);
1191    }
1192
1193    #[test]
1194    fn test_blend_mode_soft_light() {
1195        test_blend_mode(BlendMode::SoftLight);
1196    }
1197
1198    #[test]
1199    fn test_blend_mode_difference() {
1200        test_blend_mode(BlendMode::Difference);
1201    }
1202
1203    #[test]
1204    fn test_blend_mode_exclusion() {
1205        test_blend_mode(BlendMode::Exclusion);
1206    }
1207
1208    #[test]
1209    fn test_blend_mode_hue() {
1210        test_blend_mode(BlendMode::Hue);
1211    }
1212
1213    #[test]
1214    fn test_blend_mode_saturation() {
1215        test_blend_mode(BlendMode::Saturation);
1216    }
1217
1218    #[test]
1219    fn test_blend_mode_color() {
1220        test_blend_mode(BlendMode::Color);
1221    }
1222
1223    #[test]
1224    fn test_blend_mode_luminosity() {
1225        test_blend_mode(BlendMode::Luminosity);
1226    }
1227
1228    #[test]
1229    fn channel_select() {
1230        let channels: [Channel; 4] = [Channel::Blue, Channel::Green, Channel::Red, Channel::Alpha];
1231        let red = [3.0; 8];
1232        let green = [2.0; 8];
1233        let blue = [1.0; 8];
1234        let alpha = [1.0; 8];
1235        let color = channels.map(|c| c.select(red, green, blue, alpha));
1236        assert_eq!(color, [blue, green, red, alpha]);
1237    }
1238
1239    #[test]
1240    fn channel_select_zero() {
1241        let red = [4.0; 8];
1242        let green = [3.0; 8];
1243        let blue = [2.0; 8];
1244        let alpha = [2.0; 8];
1245        let color = BGR0.map(|c| c.select(red, green, blue, alpha));
1246        assert_eq!(color, [blue, green, red, [0.0; 8]]);
1247    }
1248
1249    #[test]
1250    fn channel_select_one() {
1251        let red = [4.0; 8];
1252        let green = [3.0; 8];
1253        let blue = [2.0; 8];
1254        let alpha = [2.0; 8];
1255        let color = BGR1.map(|c| c.select(red, green, blue, alpha));
1256        assert_eq!(color, [blue, green, red, [1.0; 8]]);
1257    }
1258
1259    #[test]
1260    fn channel_select_from_color() {
1261        let channels: [Channel; 4] = [Channel::Blue, Channel::Green, Channel::Red, Channel::Alpha];
1262        let color = Color { r: 3.0, g: 2.0, b: 1.0, a: 1.0 };
1263        let color = channels.map(|c| color.channel(c));
1264        assert_eq!(color, [1.0, 2.0, 3.0, 1.0]);
1265    }
1266
1267    #[test]
1268    fn channel_select_from_color_zero() {
1269        let color = Color { r: 4.0, g: 3.0, b: 2.0, a: 2.0 };
1270        let color = BGR0.map(|c| color.channel(c));
1271        assert_eq!(color, [2.0, 3.0, 4.0, 0.0]);
1272    }
1273
1274    #[test]
1275    fn channel_select_from_color_one() {
1276        let color = Color { r: 4.0, g: 3.0, b: 2.0, a: 2.0 };
1277        let color = BGR1.map(|c| color.channel(c));
1278        assert_eq!(color, [2.0, 3.0, 4.0, 1.0]);
1279    }
1280
1281    #[test]
1282    fn color_sorted() {
1283        let permutations = [
1284            (1.0, 2.0, 3.0),
1285            (1.0, 3.0, 2.0),
1286            (2.0, 1.0, 3.0),
1287            (2.0, 3.0, 1.0),
1288            (3.0, 1.0, 2.0),
1289            (3.0, 2.0, 1.0),
1290        ];
1291
1292        for (r, g, b) in permutations {
1293            let mut color = Color { r, g, b, a: 1.0 };
1294            let sorted = color.sorted();
1295            assert_eq!(sorted.map(|c| *c), [1.0, 2.0, 3.0]);
1296        }
1297    }
1298
1299    #[test]
1300    fn color_min() {
1301        let color = Color { r: 3.0, g: 2.0, b: 1.0, a: 1.0 };
1302        let min = color.min();
1303        assert_eq!(min, 1.0);
1304    }
1305
1306    #[test]
1307    fn color_max() {
1308        let color = Color { r: 3.0, g: 2.0, b: 1.0, a: 1.0 };
1309        let max = color.max();
1310        assert_eq!(max, 3.0);
1311    }
1312
1313    const C00: Color = Color { r: 0.0000, g: 0.03125, b: 0.0625, a: 0.09375 };
1314    const C01: Color = Color { r: 0.1250, g: 0.15625, b: 0.1875, a: 0.21875 };
1315    const C10: Color = Color { r: 0.2500, g: 0.28125, b: 0.3125, a: 0.34375 };
1316    const C11: Color = Color { r: 0.3750, g: 0.40625, b: 0.4375, a: 0.46875 };
1317    const C20: Color = Color { r: 0.5000, g: 0.53125, b: 0.5625, a: 0.59375 };
1318    const C21: Color = Color { r: 0.6250, g: 0.65625, b: 0.6875, a: 0.71875 };
1319
1320    fn apply_texture_color_at(transform: AffineTransform) -> Vec<[f32; 8]> {
1321        let data: Vec<_> = [C00, C01, C10, C11, C20, C21].iter().map(|c| c.to_array()).collect();
1322        let image = Arc::new(Image::from_linear_rgba(&data[..], 2, 3).unwrap());
1323        let texture = Texture { transform, image };
1324        texture.color_at(-2.0, -2.0).iter().map(|v| v.to_array()).collect()
1325    }
1326
1327    fn transpose(colors: [Color; 8]) -> Vec<[f32; 8]> {
1328        (0..4)
1329            .map(|i| {
1330                colors.iter().map(|c| c.to_array()[i]).collect::<Vec<f32>>().try_into().unwrap()
1331            })
1332            .collect()
1333    }
1334
1335    #[test]
1336    fn texture_color_at_with_identity() {
1337        assert_eq!(
1338            apply_texture_color_at(AffineTransform::default()),
1339            transpose([C00, C00, C00, C10, C20, C20, C20, C20])
1340        );
1341    }
1342
1343    #[test]
1344    fn texture_color_at_with_scale_x2() {
1345        assert_eq!(
1346            apply_texture_color_at(AffineTransform {
1347                ux: 0.5,
1348                uy: 0.0,
1349                vy: 0.5,
1350                vx: 0.0,
1351                tx: 0.0,
1352                ty: 0.0
1353            }),
1354            transpose([C00, C00, C00, C00, C10, C10, C20, C20])
1355        );
1356    }
1357
1358    #[test]
1359    fn texture_color_at_with_translation() {
1360        assert_eq!(
1361            apply_texture_color_at(AffineTransform {
1362                ux: 1.0,
1363                uy: 0.0,
1364                vx: 0.0,
1365                vy: 1.0,
1366                tx: 1.0,
1367                ty: 1.0
1368            }),
1369            transpose([C00, C00, C10, C20, C20, C20, C20, C20])
1370        );
1371    }
1372
1373    #[test]
1374    fn texture_color_at_with_axis_inverted() {
1375        assert_eq!(
1376            apply_texture_color_at(AffineTransform {
1377                ux: 0.0,
1378                uy: 1.0,
1379                vx: 1.0,
1380                vy: 0.0,
1381                tx: 0.0,
1382                ty: 0.0
1383            }),
1384            transpose([C00, C00, C00, C01, C01, C01, C01, C01])
1385        );
1386    }
1387
1388    #[test]
1389    fn f16_error() {
1390        // Error for the 256 values of u8 alpha is low.
1391        let alpha_mse = (0u8..=255u8)
1392            .map(|u| f32::from(u) / 255.0)
1393            .map(|v| (v - f16::from(v).to_f32()))
1394            .map(|d| d * d)
1395            .sum::<f32>()
1396            / 256.0;
1397        assert!(alpha_mse < 5e-8, "alpha_mse: {}", alpha_mse);
1398
1399        // Values for 256 values of u8 alpha are distinct.
1400        let alpha_distinct =
1401            (0u8..=255u8).map(|a| f16::from(f32::from(a) / 255.0)).collect::<HashSet<f16>>().len();
1402        assert_eq!(alpha_distinct, 256);
1403
1404        // Error for the 256 value of u8 sRGB is low.
1405        let component_mse = (0u8..=255u8)
1406            .map(to_linear)
1407            .map(|v| (v - f16::from(v).to_f32()))
1408            .map(|d| d * d)
1409            .sum::<f32>()
1410            / 256.0;
1411        assert!(component_mse < 3e-8, "component_mse: {}", component_mse);
1412
1413        // Values for 256 values of u8 sRGB are distinct.
1414        let component_distinct =
1415            (0u8..=255u8).map(|c| f16::from(to_linear(c))).collect::<HashSet<f16>>().len();
1416        assert_eq!(component_distinct, 256);
1417
1418        // Min and max values are intact.
1419        assert_eq!(f16::from(0.0).to_f32(), 0.0);
1420        assert_eq!(f16::from(1.0).to_f32(), 1.0);
1421    }
1422
1423    #[test]
1424    fn f16_conversion() {
1425        for i in 0..255 {
1426            let value = (i as f32) / 255.0;
1427            let value_f16 = f16::from(value);
1428            assert!(half::f16::from_f32(value).to_bits().abs_diff(value_f16.0) <= 1);
1429            assert_eq!(half::f16::from_bits(value_f16.0).to_f32(), value_f16.to_f32());
1430        }
1431    }
1432}