simd_blit/
sequential.rs

1use rgb::{RGBA, FromSlice};
2use super::PixelArray;
3
4/// A pack of 8 pixels (SIMD alignment)
5#[derive(Copy, Clone, Debug)]
6#[repr(transparent)]
7pub struct EightPixels([u16; 32]);
8
9impl EightPixels {
10    /// Read up to 8 pixels from a byte slice (4 bytes per pixel)
11    pub fn new(src: &[u8]) -> Self {
12        let mut array = [0; 32];
13        array[..src.len()].copy_from_slice(src);
14        Self(array.map(|item| item as u16))
15    }
16
17    /// Write up to 8 pixels to a byte slice (4 bytes per pixel)
18    pub fn write(&self, dst: &mut [u8]) {
19        dst.copy_from_slice(&self.0.map(|item| item as u8)[..dst.len()]);
20    }
21}
22
23/// Supported Alpha configurations
24#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25#[repr(usize)]
26pub enum AlphaConfig {
27    FirstByte,
28    SecondByte,
29    ThirdByte,
30    FourthByte,
31    /// The pixels will be directly copied, no blending
32    None,
33}
34
35/// Perform alpha compositing on up to eight pixels
36#[inline(always)]
37pub fn blend8(
38    src: EightPixels,
39    dst: &mut [u8],
40    alpha_config: AlphaConfig,
41) {
42    let result = if alpha_config != AlphaConfig::None {
43        let dst_p = EightPixels::new(dst);
44
45        let alpha_channel = match alpha_config {
46            AlphaConfig::FirstByte  => 0,
47            AlphaConfig::SecondByte => 1,
48            AlphaConfig::ThirdByte  => 2,
49            AlphaConfig::FourthByte => 3,
50            _ => unreachable!(),
51        };
52        let u8_max = u8::MAX as u16;
53
54        let mut result = [0; 32];
55        for i in 0..32 {
56            let p = i & !3;
57            let src_a = src.0[p + alpha_channel];
58            let dst_a = u8_max - src_a;
59            result[i] = ((src.0[i] * src_a) + (dst_p.0[i] * dst_a)) / u8_max;
60        }
61
62        EightPixels(result)
63    } else {
64        src
65    };
66
67    result.write(dst);
68}
69
70/// An aligned structure storing `SSAA_SQ` (x, y) subpixel coordinates for up to eight pixels
71pub struct SsaaCoords<const SSAA_SQ: usize> {
72    src_o: [[usize; 8]; SSAA_SQ],
73    src_x: [[usize; 8]; SSAA_SQ],
74    src_y: [[usize; 8]; SSAA_SQ],
75}
76
77impl<const SSAA_SQ: usize> SsaaCoords<SSAA_SQ> {
78    pub fn new() -> Self {
79        const FULL_USIZE_MAX: [usize; 8] = [usize::MAX; 8];
80        Self {
81            src_o: [FULL_USIZE_MAX; SSAA_SQ],
82            src_x: [FULL_USIZE_MAX; SSAA_SQ],
83            src_y: [FULL_USIZE_MAX; SSAA_SQ],
84        }
85    }
86
87    /// Insert coordinates (pixel < 8 && sub_pixel < SSAA_SQ)
88    #[inline(always)]
89    pub fn set(&mut self, pixel: usize, sub_pixel: usize, x: usize, y: usize) {
90        assert!(pixel < 8);
91        self.src_o[sub_pixel][pixel] = pixel;
92        self.src_x[sub_pixel][pixel] = x;
93        self.src_y[sub_pixel][pixel] = y;
94    }
95}
96
97/// Performs SSAA on up to 8 pixels
98#[inline(always)]
99pub fn ssaa8<P: PixelArray, const SSAA_SQ: usize>(
100    src_coords: SsaaCoords<SSAA_SQ>,
101    src: &P,
102) -> EightPixels {
103    let src_w = src.width();
104    let src_h = src.height();
105    let src_l = src.length();
106
107    // SUM SUBPIXELS
108
109    let mut ssaa_px = [0; 8];
110    let mut result = EightPixels::new(&[]);
111
112    for i in 0..SSAA_SQ {
113        for j in 0..8 {
114            let src_o = src_coords.src_o[i][j];
115            let src_x = src_coords.src_x[i][j];
116            let src_y = src_coords.src_y[i][j];
117            let src_i = src_y * src_w + src_x;
118
119            let usable_x = src_x < src_w;
120            let usable_y = src_y < src_h;
121            let usable_l = src_i < src_l;
122            let usable = usable_x & usable_y & usable_l;
123
124            if usable {
125                let rgba: RGBA<u16> = src.get(src_i).into();
126                result.0.as_rgba_mut()[src_o] += rgba;
127                ssaa_px[src_o] += 1;
128            }
129        }
130    }
131
132    // DIVIDE BY NUMBER OF SUBPIXELS
133
134    for i in 0..8 {
135        result.0.as_rgba_mut()[i] /= if true {
136            // better perf but some weird line SouthEast
137            SSAA_SQ as u16
138        } else {
139            match ssaa_px[i] {
140                0 => 1,
141                n => n,
142            }
143        };
144    }
145
146    result
147}