use alloc::vec;
use alloc::vec::Vec;
use gfx::{Dc, Surface};
use crate::glyphcache::GcEntry;
use crate::Text;
pub const SUBPIXEL_NONE: u8 = 0;
pub const SUBPIXEL_RGB: u8 = 1;
pub const SUBPIXEL_BGR: u8 = 2;
pub const SUBPIXEL_VRGB: u8 = 3;
pub const SUBPIXEL_VBGR: u8 = 4;
const FIR: [u32; 5] = [0x08, 0x4D, 0x56, 0x4D, 0x08];
const LCD_GAMMA: f64 = 1.6;
const LCD_DARK: f64 = 1.0;
const LCD_LIGHT: f64 = 0.35;
pub(crate) struct Lut {
to_lin: [u16; 256],
from_lo: Vec<u8>,
from_hi: Vec<u8>,
adj: [u8; 256],
adj_for: u32,
}
impl Lut {
fn new() -> Lut {
let g = LCD_GAMMA;
let mut to_lin = [0u16; 256];
for (v, o) in to_lin.iter_mut().enumerate() {
*o = rt::fx::cvtd(rt::x87::pow(v as f64 / 255.0, g) * 65535.0).clamp(0, 65535) as u16;
}
let back = |l: f64| rt::fx::cvtd(rt::x87::pow(l / 65535.0, 1.0 / g) * 255.0).clamp(0, 255) as u8;
let from_lo = (0..4096).map(|i| back(i as f64)).collect();
let from_hi = (0..4096).map(|i| back(i as f64 * 16.0 + 7.5)).collect();
let mut l = Lut { to_lin, from_lo, from_hi, adj: [0; 256], adj_for: u32::MAX };
l.prepare(0);
l
}
pub(crate) fn adj(&self) -> &[u8; 256] {
&self.adj
}
#[inline]
fn from_lin(&self, l: i32) -> u8 {
if l < 4096 { self.from_lo[l.max(0) as usize] } else { self.from_hi[(l.min(65535) >> 4) as usize] }
}
fn prepare(&mut self, rgb: u32) {
let rgb = rgb & 0xFF_FFFF;
if self.adj_for == rgb {
return;
}
self.adj_for = rgb;
let (r, g, b) = ((rgb >> 16) & 255, (rgb >> 8) & 255, rgb & 255);
let luma = (r * 54 + g * 183 + b * 19) as f64 / (255.0 * 256.0);
let k = LCD_DARK * (1.0 - luma) - LCD_LIGHT * luma;
for (c, o) in self.adj.iter_mut().enumerate() {
let x = c as f64 / 255.0;
*o = rt::fx::cvtd(x * (1.0 + k) / (1.0 + k * x) * 255.0).clamp(0, 255) as u8;
}
}
pub(crate) fn round_trip(&self, v: u8) -> u8 {
self.from_lin(self.to_lin[v as usize] as i32)
}
}
fn fir(src: &[u8], n: usize, step: usize, out: &mut [u8]) {
for j in 0..n {
let mut s = 128u32;
for (k, w) in FIR.iter().enumerate() {
let t = j as isize + k as isize - 2;
if t >= 0 && (t as usize) < n {
s += w * src[t as usize * step] as u32;
}
}
out[j * step] = (s >> 8).min(255) as u8;
}
}
pub(crate) fn filter(px: &[u8], w: usize, h: usize, vertical: bool) -> Vec<u8> {
let mut out = vec![0u8; w * h * 3];
if !vertical {
let w3 = w * 3;
for r in 0..h {
fir(&px[r * w3..r * w3 + w3], w3, 1, &mut out[r * w3..r * w3 + w3]);
}
return out;
}
let h3 = h * 3;
let mut col = vec![0u8; h3 * w];
for x in 0..w {
fir(&px[x..], h3, w, &mut col[x..]);
}
for y in 0..h {
for x in 0..w {
for i in 0..3 {
out[(y * w + x) * 3 + i] = col[(y * 3 + i) * w + x];
}
}
}
out
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn blit(dc: &Dc, sf: &mut Surface, pen_x: i32, base: i32, e: &GcEntry, bgr: bool, lut: &Lut, adj: &[u8; 256]) {
let mut x0 = pen_x.wrapping_add(e.left);
let mut y0 = base.wrapping_add(e.top);
let mut sx = 0i32;
let mut sy = 0i32;
let x1 = x0.wrapping_add(e.w).min(dc.clip[2]);
let y1 = y0.wrapping_add(e.h).min(dc.clip[3]);
if dc.clip[0] > x0 {
sx = dc.clip[0] - x0;
x0 = dc.clip[0];
}
if dc.clip[1] > y0 {
sy = dc.clip[1] - y0;
y0 = dc.clip[1];
}
let w = x1 - x0;
let h = y1 - y0;
let alpha = dc.pcol[3];
if w <= 0 || h <= 0 || alpha == 0 {
return;
}
let c = dc.color;
let src = [c & 255, (c >> 8) & 255, (c >> 16) & 255];
let src_lin = src.map(|v| lut.to_lin[v as usize] as i32);
let pick: [usize; 3] = if bgr { [0, 1, 2] } else { [2, 1, 0] };
let stride = sf.stride as usize;
for r in 0..h as usize {
let mo = ((sy as usize + r) * e.w as usize + sx as usize) * 3;
let o = ((y0 - dc.row0) as usize + r) * stride + x0 as usize * 4;
let row = &mut sf.px[o..o + w as usize * 4];
let mask = &e.px[mo..mo + w as usize * 3];
for (p, m) in row.chunks_exact_mut(4).zip(mask.chunks_exact(3)) {
if m[0] | m[1] | m[2] == 0 {
continue;
}
for ch in 0..3 {
let cov = adj[m[pick[ch]] as usize] as u32;
let a = (cov * alpha + 127) / 255;
if a == 0 {
continue;
}
if a >= 255 {
p[ch] = src[ch] as u8;
continue;
}
let d = lut.to_lin[p[ch] as usize] as i32;
let l = d + ((src_lin[ch] - d) * a as i32 + if src_lin[ch] >= d { 127 } else { -127 }) / 255;
p[ch] = lut.from_lin(l);
}
}
}
}
impl Text {
pub fn set_subpixel(&mut self, order: u8) {
let order = if order <= SUBPIXEL_VBGR { order } else { SUBPIXEL_NONE };
self.lcd = order;
if order != SUBPIXEL_NONE && self.lcd_lut.is_none() {
self.lcd_lut = Some(alloc::boxed::Box::new(Lut::new()));
}
}
pub fn subpixel(&self) -> u8 {
self.lcd
}
pub(crate) fn lcd_for(&self, sf: &Surface, font: u32) -> bool {
self.lcd != SUBPIXEL_NONE && sf.fmt == gfx::FMT_XRGB && font != crate::FONT_ICON && self.antialias()
}
pub(crate) fn lcd_prepare(&mut self, argb: u32) {
if let Some(l) = self.lcd_lut.as_mut() {
l.prepare(argb);
}
}
pub fn lcd_tables_exact(&mut self) -> bool {
let l = Lut::new();
(0..=255u8).all(|v| l.round_trip(v) == v)
}
}