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]
123 fn row(&self, y: i32) -> &[u8] {
124 unsafe {
125 let ptr = self.rbuf.row_ptr(y);
126 std::slice::from_raw_parts(ptr, self.rbuf.width() as usize * BPP)
127 }
128 }
129
130 #[inline]
132 fn row_mut(&mut self, y: i32) -> &mut [u8] {
133 unsafe {
134 let ptr = self.rbuf.row_ptr(y);
135 std::slice::from_raw_parts_mut(ptr, self.rbuf.width() as usize * BPP)
136 }
137 }
138
139 #[inline]
143 fn blend_byte(dst: u8, src: u8, alpha: i32) -> u8 {
144 (((src as i32 - dst as i32) * alpha + ((dst as i32) << 16)) >> 16) as u8
145 }
146}
147
148impl<'a> PixelFormat for PixfmtRgba32Lcd<'a> {
149 type ColorType = Rgba8;
150
151 fn width(&self) -> u32 {
152 self.rbuf.width() * 3
153 }
154
155 fn height(&self) -> u32 {
156 self.rbuf.height()
157 }
158
159 fn pixel(&self, x: i32, y: i32) -> Rgba8 {
160 let pixel = x as usize / 3;
162 let actual_w = self.actual_width() as usize;
163 if pixel >= actual_w {
164 return Rgba8::new(0, 0, 0, 0);
165 }
166 let row = self.row(y);
167 let off = pixel * BPP;
168 Rgba8::new(
169 row[off] as u32,
170 row[off + 1] as u32,
171 row[off + 2] as u32,
172 row[off + 3] as u32,
173 )
174 }
175
176 fn copy_pixel(&mut self, x: i32, y: i32, c: &Rgba8) {
177 let pixel = x as usize / 3;
178 let actual_w = self.actual_width() as usize;
179 if pixel >= actual_w {
180 return;
181 }
182 let row = self.row_mut(y);
183 let off = pixel * BPP;
184 row[off] = c.r;
185 row[off + 1] = c.g;
186 row[off + 2] = c.b;
187 row[off + 3] = c.a;
188 }
189
190 fn copy_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8) {
191 let actual_w = self.actual_width() as usize;
193 let row = self.row_mut(y);
194 for k in 0..len as usize {
195 let sp = x as usize + k;
196 let pixel = sp / 3;
197 let channel = sp % 3;
198 if pixel >= actual_w {
199 break;
200 }
201 let byte_off = pixel * BPP + channel;
202 row[byte_off] = [c.r, c.g, c.b][channel];
203 row[pixel * BPP + 3] = 255;
205 }
206 }
207
208 fn blend_pixel(&mut self, x: i32, y: i32, c: &Rgba8, cover: CoverType) {
209 let sp = x as usize;
210 let pixel = sp / 3;
211 let channel = sp % 3;
212 let actual_w = self.actual_width() as usize;
213 if pixel >= actual_w {
214 return;
215 }
216 let row = self.row_mut(y);
217 let byte_off = pixel * BPP + channel;
218 let rgb = [c.r, c.g, c.b];
219 let alpha = cover as i32 * c.a as i32;
220 if alpha != 0 {
221 if alpha == 255 * 255 {
222 row[byte_off] = rgb[channel];
223 } else {
224 row[byte_off] = Self::blend_byte(row[byte_off], rgb[channel], alpha);
225 }
226 row[pixel * BPP + 3] = 255;
227 }
228 }
229
230 fn blend_hline(&mut self, x: i32, y: i32, len: u32, c: &Rgba8, cover: CoverType) {
231 let actual_w = self.actual_width() as usize;
232 let row = self.row_mut(y);
233 let alpha = cover as i32 * c.a as i32;
234 if alpha == 0 {
235 return;
236 }
237 let rgb = [c.r, c.g, c.b];
238
239 for k in 0..len as usize {
240 let sp = x as usize + k;
241 let pixel = sp / 3;
242 let channel = sp % 3;
243 if pixel >= actual_w {
244 break;
245 }
246 let byte_off = pixel * BPP + channel;
247 if alpha == 255 * 255 {
248 row[byte_off] = rgb[channel];
249 } else {
250 row[byte_off] = Self::blend_byte(row[byte_off], rgb[channel], alpha);
251 }
252 row[pixel * BPP + 3] = 255;
253 }
254 }
255
256 fn blend_solid_hspan(
265 &mut self,
266 x: i32,
267 y: i32,
268 len: u32,
269 c: &Rgba8,
270 covers: &[CoverType],
271 ) {
272 let len = len as usize;
273
274 let dist_len = len + 4;
279 let mut c3 = vec![0u8; dist_len];
280
281 for i in 0..len {
282 let cv = covers[i];
283 c3[i] = c3[i].wrapping_add(self.lut.tertiary(cv));
284 c3[i + 1] = c3[i + 1].wrapping_add(self.lut.secondary(cv));
285 c3[i + 2] = c3[i + 2].wrapping_add(self.lut.primary(cv));
286 c3[i + 3] = c3[i + 3].wrapping_add(self.lut.secondary(cv));
287 c3[i + 4] = c3[i + 4].wrapping_add(self.lut.tertiary(cv));
288 }
289
290 let mut sp_start = x as i32 - 2;
292 let mut c3_offset = 0usize;
293 let mut remaining = dist_len;
294
295 if sp_start < 0 {
296 let skip = (-sp_start) as usize;
297 c3_offset = skip;
298 if skip >= remaining {
299 return;
300 }
301 remaining -= skip;
302 sp_start = 0;
303 }
304
305 let actual_w = self.actual_width() as usize;
307 let row = self.row_mut(y);
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 = self.row_mut(y);
351
352 for k in 0..len as usize {
353 let sp = x as usize + k;
354 let pixel = sp / 3;
355 let channel = sp % 3;
356 if pixel >= actual_w {
357 break;
358 }
359
360 let c = &colors[k];
361 let cov = if !covers.is_empty() {
362 covers[k]
363 } else {
364 cover
365 };
366 let alpha = cov as i32 * c.a as i32;
367 if alpha != 0 {
368 let byte_off = pixel * BPP + channel;
369 let rgb = [c.r, c.g, c.b];
370 if alpha == 255 * 255 {
371 row[byte_off] = rgb[channel];
372 } else {
373 row[byte_off] =
374 Self::blend_byte(row[byte_off], rgb[channel], alpha);
375 }
376 row[pixel * BPP + 3] = 255;
377 }
378 }
379 }
380}
381
382#[cfg(test)]
387mod tests {
388 use super::*;
389
390 #[test]
391 fn test_lcd_distribution_lut_construction() {
392 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
394
395 assert_eq!(lut.primary(0), 0);
397 assert_eq!(lut.secondary(0), 0);
398 assert_eq!(lut.tertiary(0), 0);
399
400 let total = lut.primary(255) as u32
403 + 2 * lut.secondary(255) as u32
404 + 2 * lut.tertiary(255) as u32;
405 assert!(total <= 255);
407 assert!(total >= 250, "total distribution = {}", total);
408 }
409
410 #[test]
411 fn test_lcd_distribution_lut_normalization() {
412 let lut = LcdDistributionLut::new(3.0, 2.0, 1.0);
414 assert_eq!(lut.primary(255), 85);
417 }
418
419 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
420 let stride = (w * BPP as u32) as i32;
421 let buf = vec![255u8; (h * w * BPP as u32) as usize];
422 let mut ra = RowAccessor::new();
423 unsafe {
424 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
425 }
426 (buf, ra)
427 }
428
429 #[test]
430 fn test_pixfmt_lcd_width_height() {
431 let (_buf, mut ra) = make_buffer(100, 50);
432 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
433 let pf = PixfmtRgba32Lcd::new(&mut ra, &lut);
434 assert_eq!(pf.width(), 300); assert_eq!(pf.height(), 50);
436 }
437
438 #[test]
439 fn test_lcd_blend_solid_hspan_black_on_white() {
440 let (_buf, mut ra) = make_buffer(100, 10);
442 let lut = LcdDistributionLut::new(1.0 / 3.0, 2.0 / 9.0, 1.0 / 9.0);
443 let mut pf = PixfmtRgba32Lcd::new(&mut ra, &lut);
444
445 let covers = [255u8; 6];
447 let black = Rgba8::new(0, 0, 0, 255);
448 pf.blend_solid_hspan(30, 5, 6, &black, &covers);
449
450 let p = pf.pixel(30, 5); assert!(
453 p.r < 255 || p.g < 255 || p.b < 255,
454 "Expected darkened pixel, got {:?}",
455 (p.r, p.g, p.b)
456 );
457 }
458}