plotters_bitmap/bitmap_pixel/
rgb.rs

1use super::pixel_format::blend;
2use super::PixelFormat;
3use crate::BitMapBackend;
4use plotters_backend::DrawingBackend;
5
6/// The marker type that indicates we are currently using a RGB888 pixel format
7pub struct RGBPixel;
8
9impl PixelFormat for RGBPixel {
10    const PIXEL_SIZE: usize = 3;
11    const EFFECTIVE_PIXEL_SIZE: usize = 3;
12
13    #[inline(always)]
14    fn byte_at(r: u8, g: u8, b: u8, _a: u64, idx: usize) -> u8 {
15        match idx {
16            0 => r,
17            1 => g,
18            2 => b,
19            _ => 0xff,
20        }
21    }
22
23    #[inline(always)]
24    fn decode_pixel(data: &[u8]) -> (u8, u8, u8, u64) {
25        (data[0], data[1], data[2], 0x255)
26    }
27
28    fn can_be_saved() -> bool {
29        true
30    }
31
32    #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
33    fn blend_rect_fast(
34        target: &mut BitMapBackend<'_, Self>,
35        upper_left: (i32, i32),
36        bottom_right: (i32, i32),
37        r: u8,
38        g: u8,
39        b: u8,
40        a: f64,
41    ) {
42        let (w, h) = target.get_size();
43        let a = a.clamp(0.0, 1.0);
44        if a == 0.0 {
45            return;
46        }
47
48        let (x0, y0) = (
49            upper_left.0.min(bottom_right.0).max(0),
50            upper_left.1.min(bottom_right.1).max(0),
51        );
52        let (x1, y1) = (
53            upper_left.0.max(bottom_right.0).min(w as i32),
54            upper_left.1.max(bottom_right.1).min(h as i32),
55        );
56
57        // This may happen when the minimal value is larger than the limit.
58        // Thus we just have something that is completely out-of-range
59        if x0 >= x1 || y0 >= y1 {
60            return;
61        }
62
63        let dst = target.get_raw_pixel_buffer();
64
65        let a = (256.0 * a).floor() as u64;
66
67        // Since we should always make sure the RGB payload occupies the logic lower bits
68        // thus, this type purning should work for both LE and BE CPUs
69        #[rustfmt::skip]
70        let [p1, p2, p3]: [u64; 3] = unsafe {
71            std::mem::transmute([
72                u16::from(r), u16::from(b), u16::from(g), u16::from(r), // QW1
73                u16::from(b), u16::from(g), u16::from(r), u16::from(b), // QW2
74                u16::from(g), u16::from(r), u16::from(b), u16::from(g), // QW3
75            ])
76        };
77
78        #[rustfmt::skip]
79        let [q1, q2, q3]: [u64; 3] = unsafe {
80            std::mem::transmute([
81                u16::from(g), u16::from(r), u16::from(b), u16::from(g), // QW1
82                u16::from(r), u16::from(b), u16::from(g), u16::from(r), // QW2
83                u16::from(b), u16::from(g), u16::from(r), u16::from(b), // QW3
84            ])
85        };
86
87        const N: u64 = 0xff00_ff00_ff00_ff00;
88        const M: u64 = 0x00ff_00ff_00ff_00ff;
89
90        for y in y0..y1 {
91            let start = (y * w as i32 + x0) as usize;
92            let count = (x1 - x0) as usize;
93
94            let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
95            let slice = unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
96            for p in slice.iter_mut() {
97                let ptr = p as *mut [u8; 24] as *mut [u64; 3];
98                let [d1, d2, d3] = unsafe { ptr.read_unaligned() };
99                let (mut h1, mut h2, mut h3) = ((d1 >> 8) & M, (d2 >> 8) & M, (d3 >> 8) & M);
100                let (mut l1, mut l2, mut l3) = (d1 & M, d2 & M, d3 & M);
101
102                #[cfg(target_endian = "little")]
103                {
104                    h1 = (h1 * (256 - a) + q1 * a) & N;
105                    h2 = (h2 * (256 - a) + q2 * a) & N;
106                    h3 = (h3 * (256 - a) + q3 * a) & N;
107                    l1 = ((l1 * (256 - a) + p1 * a) & N) >> 8;
108                    l2 = ((l2 * (256 - a) + p2 * a) & N) >> 8;
109                    l3 = ((l3 * (256 - a) + p3 * a) & N) >> 8;
110                }
111
112                #[cfg(target_endian = "big")]
113                {
114                    h1 = (h1 * (256 - a) + p1 * a) & N;
115                    h2 = (h2 * (256 - a) + p2 * a) & N;
116                    h3 = (h3 * (256 - a) + p3 * a) & N;
117                    l1 = ((l1 * (256 - a) + q1 * a) & N) >> 8;
118                    l2 = ((l2 * (256 - a) + q2 * a) & N) >> 8;
119                    l3 = ((l3 * (256 - a) + q3 * a) & N) >> 8;
120                }
121
122                unsafe {
123                    ptr.write_unaligned([h1 | l1, h2 | l2, h3 | l3]);
124                }
125            }
126
127            let mut iter = dst[((start + slice.len() * 8) * Self::PIXEL_SIZE)
128                ..((start + count) * Self::PIXEL_SIZE)]
129                .iter_mut();
130            for _ in (slice.len() * 8)..count {
131                blend(iter.next().unwrap(), r, a);
132                blend(iter.next().unwrap(), g, a);
133                blend(iter.next().unwrap(), b, a);
134            }
135        }
136    }
137
138    #[allow(clippy::many_single_char_names, clippy::cast_ptr_alignment)]
139    fn fill_rect_fast(
140        target: &mut BitMapBackend<'_, Self>,
141        upper_left: (i32, i32),
142        bottom_right: (i32, i32),
143        r: u8,
144        g: u8,
145        b: u8,
146    ) {
147        let (w, h) = target.get_size();
148        let (x0, y0) = (
149            upper_left.0.min(bottom_right.0).max(0),
150            upper_left.1.min(bottom_right.1).max(0),
151        );
152        let (x1, y1) = (
153            upper_left.0.max(bottom_right.0).min(w as i32),
154            upper_left.1.max(bottom_right.1).min(h as i32),
155        );
156
157        // This may happen when the minimal value is larger than the limit.
158        // Thus we just have something that is completely out-of-range
159        if x0 >= x1 || y0 >= y1 {
160            return;
161        }
162
163        let dst = target.get_raw_pixel_buffer();
164
165        if r == g && g == b {
166            // If r == g == b, then we can use memset
167            if x0 != 0 || x1 != w as i32 {
168                // If it's not the entire row is filled, we can only do
169                // memset per row
170                for y in y0..y1 {
171                    let start = (y * w as i32 + x0) as usize;
172                    let count = (x1 - x0) as usize;
173                    dst[(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
174                        .iter_mut()
175                        .for_each(|e| *e = r);
176                }
177            } else {
178                // If the entire memory block is going to be filled, just use single memset
179                dst[Self::PIXEL_SIZE * (y0 * w as i32) as usize
180                    ..(y1 * w as i32) as usize * Self::PIXEL_SIZE]
181                    .iter_mut()
182                    .for_each(|e| *e = r);
183            }
184        } else {
185            let count = (x1 - x0) as usize;
186            if count < 8 {
187                for y in y0..y1 {
188                    let start = (y * w as i32 + x0) as usize;
189                    let mut iter = dst
190                        [(start * Self::PIXEL_SIZE)..((start + count) * Self::PIXEL_SIZE)]
191                        .iter_mut();
192                    for _ in 0..count {
193                        *iter.next().unwrap() = r;
194                        *iter.next().unwrap() = g;
195                        *iter.next().unwrap() = b;
196                    }
197                }
198            } else {
199                for y in y0..y1 {
200                    let start = (y * w as i32 + x0) as usize;
201                    let start_ptr = &mut dst[start * Self::PIXEL_SIZE] as *mut u8 as *mut [u8; 24];
202                    let slice =
203                        unsafe { std::slice::from_raw_parts_mut(start_ptr, (count - 1) / 8) };
204                    for p in slice.iter_mut() {
205                        // In this case, we can actually fill 8 pixels in one iteration with
206                        // only 3 movq instructions.
207                        // TODO: Consider using AVX instructions when possible
208                        let ptr = p as *mut [u8; 24] as *mut u64;
209                        unsafe {
210                            let [d1, d2, d3]: [u64; 3] = std::mem::transmute([
211                                r, g, b, r, g, b, r, g, // QW1
212                                b, r, g, b, r, g, b, r, // QW2
213                                g, b, r, g, b, r, g, b, // QW3
214                            ]);
215                            ptr.write_unaligned(d1);
216                            ptr.offset(1).write_unaligned(d2);
217                            ptr.offset(2).write_unaligned(d3);
218                        }
219                    }
220
221                    for idx in (slice.len() * 8)..count {
222                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE] = r;
223                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 1] = g;
224                        dst[start * Self::PIXEL_SIZE + idx * Self::PIXEL_SIZE + 2] = b;
225                    }
226                }
227            }
228        }
229    }
230}