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