1use crate::basics::CoverType;
13use crate::color::Rgba8;
14use crate::pixfmt_rgba::PixelFormat;
15use crate::rendering_buffer::RowAccessor;
16
17pub struct LcdDistributionLut {
29 primary_lut: [u8; 256],
30 secondary_lut: [u8; 256],
31 tertiary_lut: [u8; 256],
32}
33
34impl LcdDistributionLut {
35 pub fn new(prim: f64, second: f64, tert: f64) -> Self {
44 let norm = 1.0 / (prim + second * 2.0 + tert * 2.0);
45 let prim = prim * norm;
46 let second = second * norm;
47 let tert = tert * norm;
48
49 let mut primary_lut = [0u8; 256];
50 let mut secondary_lut = [0u8; 256];
51 let mut tertiary_lut = [0u8; 256];
52
53 for i in 0..256 {
54 primary_lut[i] = (prim * i as f64).floor() as u8;
55 secondary_lut[i] = (second * i as f64).floor() as u8;
56 tertiary_lut[i] = (tert * i as f64).floor() as u8;
57 }
58
59 Self {
60 primary_lut,
61 secondary_lut,
62 tertiary_lut,
63 }
64 }
65
66 #[inline]
68 pub fn primary(&self, v: u8) -> u8 {
69 self.primary_lut[v as usize]
70 }
71
72 #[inline]
74 pub fn secondary(&self, v: u8) -> u8 {
75 self.secondary_lut[v as usize]
76 }
77
78 #[inline]
80 pub fn tertiary(&self, v: u8) -> u8 {
81 self.tertiary_lut[v as usize]
82 }
83}
84
85const BPP: usize = 4; pub struct PixfmtRgba32Lcd<'a> {
105 rbuf: &'a mut RowAccessor,
106 lut: &'a LcdDistributionLut,
107}
108
109impl<'a> PixfmtRgba32Lcd<'a> {
110 pub fn new(rbuf: &'a mut RowAccessor, lut: &'a LcdDistributionLut) -> Self {
112 Self { rbuf, lut }
113 }
114
115 #[inline]
117 fn actual_width(&self) -> u32 {
118 self.rbuf.width()
119 }
120
121 #[inline]
125 fn blend_byte(dst: u8, src: u8, alpha: i32) -> u8 {
126 (((src as i32 - dst as i32) * alpha + ((dst as i32) << 16)) >> 16) as u8
127 }
128}
129
130impl<'a> PixelFormat for PixfmtRgba32Lcd<'a> {
131 type ColorType = Rgba8;
132
133 fn width(&self) -> u32 {
134 self.rbuf.width() * 3
135 }
136
137 fn height(&self) -> u32 {
138 self.rbuf.height()
139 }
140
141 fn pixel(&self, x: i32, y: i32) -> Rgba8 {
142 let pixel = x as usize / 3;
144 let actual_w = self.actual_width() as usize;
145 if pixel >= actual_w {
146 return Rgba8::new(0, 0, 0, 0);
147 }
148 let row = unsafe {
149 let ptr = self.rbuf.row_ptr(y);
150 std::slice::from_raw_parts(ptr, actual_w * BPP)
151 };
152 let off = pixel * BPP;
153 Rgba8::new(
154 row[off] as u32,
155 row[off + 1] as u32,
156 row[off + 2] as u32,
157 row[off + 3] as u32,
158 )
159 }
160
161 fn copy_pixel(&mut self, x: i32, y: i32, c: &Rgba8) {
162 let pixel = x as usize / 3;
163 let actual_w = self.actual_width() as usize;
164 if pixel >= actual_w {
165 return;
166 }
167 let row = unsafe {
168 let ptr = self.rbuf.row_ptr(y);
169 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
170 };
171 let off = pixel * BPP;
172 row[off] = c.r;
173 row[off + 1] = c.g;
174 row[off + 2] = c.b;
175 row[off + 3] = c.a;
176 }
177
178 fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8) {
179 let actual_w = self.actual_width() as usize;
181 let row = unsafe {
182 let ptr = self.rbuf.row_ptr(y);
183 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
184 };
185 for k in 0..len as usize {
186 let sp = x as usize + k;
187 let pixel = sp / 3;
188 let channel = sp % 3;
189 if pixel >= actual_w {
190 break;
191 }
192 let byte_off = pixel * BPP + channel;
193 row[byte_off] = [c.r, c.g, c.b][channel];
194 row[pixel * BPP + 3] = 255;
196 }
197 }
198
199 fn blend_pixel(&mut self, x: i32, y: i32, c: &Rgba8, cover: CoverType) {
200 let sp = x as usize;
201 let pixel = sp / 3;
202 let channel = sp % 3;
203 let actual_w = self.actual_width() as usize;
204 if pixel >= actual_w {
205 return;
206 }
207 let row = unsafe {
208 let ptr = self.rbuf.row_ptr(y);
209 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
210 };
211 let byte_off = pixel * BPP + channel;
212 let rgb = [c.r, c.g, c.b];
213 let alpha = cover as i32 * c.a as i32;
214 if alpha != 0 {
215 if alpha == 255 * 255 {
216 row[byte_off] = rgb[channel];
217 } else {
218 row[byte_off] = Self::blend_byte(row[byte_off], rgb[channel], alpha);
219 }
220 row[pixel * BPP + 3] = 255;
221 }
222 }
223
224 fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, cover: CoverType) {
225 let actual_w = self.actual_width() as usize;
226 let row = unsafe {
227 let ptr = self.rbuf.row_ptr(y);
228 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
229 };
230 let alpha = cover as i32 * c.a as i32;
231 if alpha == 0 {
232 return;
233 }
234 let rgb = [c.r, c.g, c.b];
235
236 for k in 0..len as usize {
237 let sp = x as usize + k;
238 let pixel = sp / 3;
239 let channel = sp % 3;
240 if pixel >= actual_w {
241 break;
242 }
243 let byte_off = pixel * BPP + channel;
244 if alpha == 255 * 255 {
245 row[byte_off] = rgb[channel];
246 } else {
247 row[byte_off] = Self::blend_byte(row[byte_off], rgb[channel], alpha);
248 }
249 row[pixel * BPP + 3] = 255;
250 }
251 }
252
253 fn blend_solid_hspan(
262 &mut self,
263 x: i32,
264 y: i32,
265 len: u32,
266 c: &Rgba8,
267 covers: &[CoverType],
268 ) {
269 let len = len as usize;
270
271 let dist_len = len + 4;
276 let mut c3 = vec![0u8; dist_len];
277
278 for i in 0..len {
279 let cv = covers[i];
280 c3[i] = c3[i].wrapping_add(self.lut.tertiary(cv));
281 c3[i + 1] = c3[i + 1].wrapping_add(self.lut.secondary(cv));
282 c3[i + 2] = c3[i + 2].wrapping_add(self.lut.primary(cv));
283 c3[i + 3] = c3[i + 3].wrapping_add(self.lut.secondary(cv));
284 c3[i + 4] = c3[i + 4].wrapping_add(self.lut.tertiary(cv));
285 }
286
287 let mut sp_start = x as i32 - 2;
289 let mut c3_offset = 0usize;
290 let mut remaining = dist_len;
291
292 if sp_start < 0 {
293 let skip = (-sp_start) as usize;
294 c3_offset = skip;
295 if skip >= remaining {
296 return;
297 }
298 remaining -= skip;
299 sp_start = 0;
300 }
301
302 let actual_w = self.actual_width() as usize;
304 let row = unsafe {
305 let ptr = self.rbuf.row_ptr(y);
306 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
307 };
308
309 let rgb = [c.r, c.g, c.b];
310 for k in 0..remaining {
315 let sp = sp_start as usize + k;
316 let pixel = sp / 3;
317 let channel = sp % 3;
318
319 if pixel >= actual_w {
320 break;
321 }
322
323 let cover = c3[c3_offset + k];
324 let alpha = cover as i32 * c.a as i32;
325
326 if alpha != 0 {
327 let byte_off = pixel * BPP + channel;
328 if alpha == 255 * 255 {
329 row[byte_off] = rgb[channel];
330 } else {
331 row[byte_off] =
332 Self::blend_byte(row[byte_off], rgb[channel], alpha);
333 }
334 row[pixel * BPP + 3] = 255;
336 }
337 }
338 }
339
340 fn blend_color_hspan(
341 &mut self,
342 x: i32,
343 y: i32,
344 len: u32,
345 colors: &[Rgba8],
346 covers: &[CoverType],
347 cover: CoverType,
348 ) {
349 let actual_w = self.actual_width() as usize;
350 let row = unsafe {
351 let ptr = self.rbuf.row_ptr(y);
352 std::slice::from_raw_parts_mut(ptr, actual_w * BPP)
353 };
354
355 for k in 0..len as usize {
356 let sp = x as usize + k;
357 let pixel = sp / 3;
358 let channel = sp % 3;
359 if pixel >= actual_w {
360 break;
361 }
362
363 let c = &colors[k];
364 let cov = if !covers.is_empty() {
365 covers[k]
366 } else {
367 cover
368 };
369 let alpha = cov as i32 * c.a as i32;
370 if alpha != 0 {
371 let byte_off = pixel * BPP + channel;
372 let rgb = [c.r, c.g, c.b];
373 if alpha == 255 * 255 {
374 row[byte_off] = rgb[channel];
375 } else {
376 row[byte_off] =
377 Self::blend_byte(row[byte_off], rgb[channel], alpha);
378 }
379 row[pixel * BPP + 3] = 255;
380 }
381 }
382 }
383}
384
385#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_lcd_distribution_lut_construction() {
395 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
397
398 assert_eq!(lut.primary(0), 0);
400 assert_eq!(lut.secondary(0), 0);
401 assert_eq!(lut.tertiary(0), 0);
402
403 let total = lut.primary(255) as u32
406 + 2 * lut.secondary(255) as u32
407 + 2 * lut.tertiary(255) as u32;
408 assert!(total <= 255);
410 assert!(total >= 250, "total distribution = {}", total);
411 }
412
413 #[test]
414 fn test_lcd_distribution_lut_normalization() {
415 let lut = LcdDistributionLut::new(3.0, 2.0, 1.0);
417 assert_eq!(lut.primary(255), 85);
420 }
421
422 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
423 let stride = (w * BPP as u32) as i32;
424 let buf = vec![255u8; (h * w * BPP as u32) as usize];
425 let mut ra = RowAccessor::new();
426 unsafe {
427 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
428 }
429 (buf, ra)
430 }
431
432 #[test]
433 fn test_pixfmt_lcd_width_height() {
434 let (_buf, mut ra) = make_buffer(100, 50);
435 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
436 let pf = PixfmtRgba32Lcd::new(&mut ra, &lut);
437 assert_eq!(pf.width(), 300); assert_eq!(pf.height(), 50);
439 }
440
441 #[test]
442 fn test_lcd_blend_solid_hspan_black_on_white() {
443 let (_buf, mut ra) = make_buffer(100, 10);
445 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
446 let mut pf = PixfmtRgba32Lcd::new(&mut ra, &lut);
447
448 let covers = [255u8; 6];
450 let black = Rgba8::new(0, 0, 0, 255);
451 pf.blend_solid_hspan(30, 5, 6, &black, &covers);
452
453 let p = pf.pixel(30, 5); assert!(
456 p.r < 255 || p.g < 255 || p.b < 255,
457 "Expected darkened pixel, got {:?}",
458 (p.r, p.g, p.b)
459 );
460 }
461}