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.min(1.0).max(0.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}
231