1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::color::PixelRgb;
#[cfg(not(feature = "std"))]
use crate::m_polyfill::*;

/// The trait for putting pixels into byte buffers.
pub trait PixelBuffer {
    /// Specifies how many bytes a single plasma pixel occupies.
    const PIXEL_BYTES: usize;
    /// Puts bytes from a `pixel` into the provided `buffer` using a provided writer.
    fn put_pixel<'a, I: Iterator<Item = &'a mut u8>>(writer: &mut I, pixel: PixelRgb);
}

/// A [PixelBuffer] tool for a RGB24 buffer (3 bytes/pixel: red, green, blue).
pub struct PixelBufRGB24;

impl PixelBuffer for PixelBufRGB24 {
    const PIXEL_BYTES: usize = 3;

    #[inline]
    fn put_pixel<'a, I>(writer: &mut I, pixel: PixelRgb)
        where I: Iterator<Item = &'a mut u8>
    {
        for (color, ptr) in pixel.iter_rgb_values().zip(writer) {
            *ptr = color.to_color_u8clamped();
        }
    }
}

/// A [PixelBuffer] tool for a RGBA32 buffer (4 bytes/pixel: red, green, blue, alpha).
pub struct PixelBufRGBA32;

impl PixelBuffer for PixelBufRGBA32 {
    const PIXEL_BYTES: usize = 4;

    #[inline]
    fn put_pixel<'a, I>(writer: &mut I, pixel: PixelRgb)
        where I: Iterator<Item = &'a mut u8>
    {
        for (color, ptr) in pixel.iter_rgba_values(1.0).zip(writer) {
            *ptr = color.to_color_u8clamped();
        }
    }
}

#[cfg(not(feature = "use-simd"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "use-simd"))))]
/// A [PixelBuffer] tool for a RGB16 buffer (5-6-5 bits per color channel: red, green, blue).
pub struct PixelBufRGB16;

#[cfg(not(feature = "use-simd"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "use-simd"))))]
/// A [PixelBuffer] tool for a RGB16 x2 buffer (5-6-5 bits per color channel: red, green, blue)
/// where each plasma pixel is being written to 2 consecutive pixels.
pub struct PixelBufRGB16x2;

#[cfg(not(feature = "use-simd"))]
impl PixelBuffer for PixelBufRGB16 {
    const PIXEL_BYTES: usize = 2;

    #[inline]
    fn put_pixel<'a, I>(writer: &mut I, pixel: PixelRgb)
        where I: Iterator<Item = &'a mut u8>
    {
        let PixelRgb { r, g, b } = pixel;
        let [r, g, b] = [r, g, b].map(ToColor8::to_color_u8clamped);
        let rgb16 = (((r & 0b11111000) as u16) << 8)|
                    (((g & 0b11111100) as u16) << 3)|
                    (((b & 0b11111000) as u16) >> 3);
        for (v, ptr) in rgb16.to_be_bytes().into_iter().zip(writer) {
            *ptr = v;
        }
    }
}

#[cfg(not(feature = "use-simd"))]
impl PixelBuffer for PixelBufRGB16x2 {
    const PIXEL_BYTES: usize = 4;

    #[inline]
    fn put_pixel<'a, I>(writer: &mut I, pixel: PixelRgb)
        where I: Iterator<Item = &'a mut u8>
    {
        let PixelRgb { r, g, b } = pixel;
        let [r, g, b] = [r, g, b].map(ToColor8::to_color_u8clamped);
        let rgb16 = (((r & 0b11111000) as u16) << 8)|
                    (((g & 0b11111100) as u16) << 3)|
                    (((b & 0b11111000) as u16) >> 3);
        let [hi, lo] = rgb16.to_be_bytes();
        for (v, ptr) in [hi, lo, hi, lo].into_iter().zip(writer) {
            *ptr = v;
        }
    }
}

/// Provides a method of converting color part from a `f32` type to a `u8`.
pub trait ToColor8 {
    fn to_color_u8clamped(self) -> u8;
}

impl ToColor8 for f32 {
    #[inline]
    fn to_color_u8clamped(self) -> u8 {
        // this is saturating conversion
        (self.abs() * 255.0) as u8
    }
}