1use alloc::vec;
32use alloc::vec::Vec;
33
34use gfx::{Dc, Surface};
35
36use crate::glyphcache::GcEntry;
37use crate::Text;
38
39pub const SUBPIXEL_NONE: u8 = 0;
41pub const SUBPIXEL_RGB: u8 = 1;
43pub const SUBPIXEL_BGR: u8 = 2;
45pub const SUBPIXEL_VRGB: u8 = 3;
47pub const SUBPIXEL_VBGR: u8 = 4;
49
50const FIR: [u32; 5] = [0x08, 0x4D, 0x56, 0x4D, 0x08];
52const LCD_GAMMA: f64 = 1.6;
54const LCD_DARK: f64 = 1.0;
56const LCD_LIGHT: f64 = 0.35;
58
59pub(crate) struct Lut {
61 to_lin: [u16; 256],
63 from_lo: Vec<u8>,
65 from_hi: Vec<u8>,
67 adj: [u8; 256],
69 adj_for: u32,
70}
71
72impl Lut {
73 fn new() -> Lut {
74 let g = LCD_GAMMA;
75 let mut to_lin = [0u16; 256];
76 for (v, o) in to_lin.iter_mut().enumerate() {
77 *o = rt::fx::cvtd(rt::x87::pow(v as f64 / 255.0, g) * 65535.0).clamp(0, 65535) as u16;
78 }
79 let back = |l: f64| rt::fx::cvtd(rt::x87::pow(l / 65535.0, 1.0 / g) * 255.0).clamp(0, 255) as u8;
80 let from_lo = (0..4096).map(|i| back(i as f64)).collect();
81 let from_hi = (0..4096).map(|i| back(i as f64 * 16.0 + 7.5)).collect();
82 let mut l = Lut { to_lin, from_lo, from_hi, adj: [0; 256], adj_for: u32::MAX };
83 l.prepare(0);
84 l
85 }
86
87 pub(crate) fn adj(&self) -> &[u8; 256] {
89 &self.adj
90 }
91
92 #[inline]
93 fn from_lin(&self, l: i32) -> u8 {
94 if l < 4096 { self.from_lo[l.max(0) as usize] } else { self.from_hi[(l.min(65535) >> 4) as usize] }
95 }
96
97 fn prepare(&mut self, rgb: u32) {
100 let rgb = rgb & 0xFF_FFFF;
101 if self.adj_for == rgb {
102 return;
103 }
104 self.adj_for = rgb;
105 let (r, g, b) = ((rgb >> 16) & 255, (rgb >> 8) & 255, rgb & 255);
106 let luma = (r * 54 + g * 183 + b * 19) as f64 / (255.0 * 256.0);
107 let k = LCD_DARK * (1.0 - luma) - LCD_LIGHT * luma;
108 for (c, o) in self.adj.iter_mut().enumerate() {
109 let x = c as f64 / 255.0;
110 *o = rt::fx::cvtd(x * (1.0 + k) / (1.0 + k * x) * 255.0).clamp(0, 255) as u8;
111 }
112 }
113
114 pub(crate) fn round_trip(&self, v: u8) -> u8 {
116 self.from_lin(self.to_lin[v as usize] as i32)
117 }
118}
119
120fn fir(src: &[u8], n: usize, step: usize, out: &mut [u8]) {
122 for j in 0..n {
123 let mut s = 128u32;
124 for (k, w) in FIR.iter().enumerate() {
125 let t = j as isize + k as isize - 2;
126 if t >= 0 && (t as usize) < n {
127 s += w * src[t as usize * step] as u32;
128 }
129 }
130 out[j * step] = (s >> 8).min(255) as u8;
131 }
132}
133
134pub(crate) fn filter(px: &[u8], w: usize, h: usize, vertical: bool) -> Vec<u8> {
137 let mut out = vec![0u8; w * h * 3];
138 if !vertical {
139 let w3 = w * 3;
140 for r in 0..h {
141 fir(&px[r * w3..r * w3 + w3], w3, 1, &mut out[r * w3..r * w3 + w3]);
142 }
143 return out;
144 }
145 let h3 = h * 3;
146 let mut col = vec![0u8; h3 * w];
147 for x in 0..w {
148 fir(&px[x..], h3, w, &mut col[x..]);
149 }
150 for y in 0..h {
151 for x in 0..w {
152 for i in 0..3 {
153 out[(y * w + x) * 3 + i] = col[(y * 3 + i) * w + x];
154 }
155 }
156 }
157 out
158}
159
160#[allow(clippy::too_many_arguments)]
164pub(crate) fn blit(dc: &Dc, sf: &mut Surface, pen_x: i32, base: i32, e: &GcEntry, bgr: bool, lut: &Lut, adj: &[u8; 256]) {
165 let mut x0 = pen_x.wrapping_add(e.left);
166 let mut y0 = base.wrapping_add(e.top);
167 let mut sx = 0i32;
168 let mut sy = 0i32;
169 let x1 = x0.wrapping_add(e.w).min(dc.clip[2]);
170 let y1 = y0.wrapping_add(e.h).min(dc.clip[3]);
171 if dc.clip[0] > x0 {
172 sx = dc.clip[0] - x0;
173 x0 = dc.clip[0];
174 }
175 if dc.clip[1] > y0 {
176 sy = dc.clip[1] - y0;
177 y0 = dc.clip[1];
178 }
179 let w = x1 - x0;
180 let h = y1 - y0;
181 let alpha = dc.pcol[3];
182 if w <= 0 || h <= 0 || alpha == 0 {
183 return;
184 }
185 let c = dc.color;
187 let src = [c & 255, (c >> 8) & 255, (c >> 16) & 255];
188 let src_lin = src.map(|v| lut.to_lin[v as usize] as i32);
189 let pick: [usize; 3] = if bgr { [0, 1, 2] } else { [2, 1, 0] };
191 let stride = sf.stride as usize;
192 for r in 0..h as usize {
193 let mo = ((sy as usize + r) * e.w as usize + sx as usize) * 3;
194 let o = ((y0 - dc.row0) as usize + r) * stride + x0 as usize * 4;
195 let row = &mut sf.px[o..o + w as usize * 4];
196 let mask = &e.px[mo..mo + w as usize * 3];
197 for (p, m) in row.chunks_exact_mut(4).zip(mask.chunks_exact(3)) {
198 if m[0] | m[1] | m[2] == 0 {
199 continue;
200 }
201 for ch in 0..3 {
202 let cov = adj[m[pick[ch]] as usize] as u32;
203 let a = (cov * alpha + 127) / 255;
204 if a == 0 {
205 continue;
206 }
207 if a >= 255 {
208 p[ch] = src[ch] as u8;
209 continue;
210 }
211 let d = lut.to_lin[p[ch] as usize] as i32;
212 let l = d + ((src_lin[ch] - d) * a as i32 + if src_lin[ch] >= d { 127 } else { -127 }) / 255;
213 p[ch] = lut.from_lin(l);
214 }
215 }
216 }
217}
218
219impl Text {
220 pub fn set_subpixel(&mut self, order: u8) {
224 let order = if order <= SUBPIXEL_VBGR { order } else { SUBPIXEL_NONE };
225 self.lcd = order;
226 if order != SUBPIXEL_NONE && self.lcd_lut.is_none() {
227 self.lcd_lut = Some(alloc::boxed::Box::new(Lut::new()));
228 }
229 }
230
231 pub fn subpixel(&self) -> u8 {
233 self.lcd
234 }
235
236 pub(crate) fn lcd_for(&self, sf: &Surface, font: u32) -> bool {
238 self.lcd != SUBPIXEL_NONE && sf.fmt == gfx::FMT_XRGB && font != crate::FONT_ICON && self.antialias()
239 }
240
241 pub(crate) fn lcd_prepare(&mut self, argb: u32) {
243 if let Some(l) = self.lcd_lut.as_mut() {
244 l.prepare(argb);
245 }
246 }
247
248 pub fn lcd_tables_exact(&mut self) -> bool {
250 let l = Lut::new();
251 (0..=255u8).all(|v| l.round_trip(v) == v)
252 }
253}