const FILTER_WEIGHTS: [u32; 5] = [1, 2, 3, 2, 1];
const FILTER_SUM: u32 = 9;
fn lcd_filter_weights() -> [f64; 5] {
let p_units = crate::font_settings::current_primary_weight() * 9.0;
let weights = [1.0, 2.0, p_units, 2.0, 1.0];
let sum = weights.iter().sum::<f64>().max(1e-9);
[
weights[0] / sum,
weights[1] / sum,
weights[2] / sum,
weights[3] / sum,
weights[4] / sum,
]
}
pub(super) fn apply_5_tap_filter(gray: &[u8], gray_w: u32, mask_w: u32, mask_h: u32) -> Vec<u8> {
let primary = crate::font_settings::current_primary_weight();
let gamma = crate::font_settings::current_gamma();
let is_default_primary = ((primary - 1.0 / 3.0).abs()) < 1e-6;
let is_default_gamma = ((gamma - 1.0).abs()) < 1e-6;
if is_default_primary && is_default_gamma {
return apply_5_tap_filter_legacy(gray, gray_w, mask_w, mask_h);
}
let mut data = vec![0u8; (mask_w as usize) * (mask_h as usize) * 3];
let gw = gray_w as i32;
let w = lcd_filter_weights();
let inv_g = 1.0 / gamma.max(1e-3);
let need_gamma = !is_default_gamma;
let apply_gamma = |c: f64| -> f64 {
if !need_gamma {
return c;
}
let t = (c / 255.0).clamp(0.0, 1.0);
t.powf(inv_g) * 255.0
};
for py in 0..mask_h {
let row_start = (py as usize) * (gray_w as usize);
let row = &gray[row_start..row_start + gray_w as usize];
for px in 0..mask_w {
let base = (px as i32) * 3;
let sample = |off: i32| -> f64 {
let pos = base + off;
if pos < 0 || pos >= gw {
0.0
} else {
row[pos as usize] as f64
}
};
let cov_r = w[0] * sample(-2)
+ w[1] * sample(-1)
+ w[2] * sample(0)
+ w[3] * sample(1)
+ w[4] * sample(2);
let cov_g = w[0] * sample(-1)
+ w[1] * sample(0)
+ w[2] * sample(1)
+ w[3] * sample(2)
+ w[4] * sample(3);
let cov_b = w[0] * sample(0)
+ w[1] * sample(1)
+ w[2] * sample(2)
+ w[3] * sample(3)
+ w[4] * sample(4);
let mi = ((py as usize) * (mask_w as usize) + (px as usize)) * 3;
data[mi] = apply_gamma(cov_r).round().clamp(0.0, 255.0) as u8;
data[mi + 1] = apply_gamma(cov_g).round().clamp(0.0, 255.0) as u8;
data[mi + 2] = apply_gamma(cov_b).round().clamp(0.0, 255.0) as u8;
}
}
data
}
fn apply_5_tap_filter_legacy(gray: &[u8], gray_w: u32, mask_w: u32, mask_h: u32) -> Vec<u8> {
let mut data = vec![0u8; (mask_w as usize) * (mask_h as usize) * 3];
let gw = gray_w as i32;
for py in 0..mask_h {
let row_start = (py as usize) * (gray_w as usize);
let row = &gray[row_start..row_start + gray_w as usize];
for px in 0..mask_w {
let base = (px as i32) * 3;
let sample = |off: i32| -> u32 {
let pos = base + off;
if pos < 0 || pos >= gw {
0
} else {
row[pos as usize] as u32
}
};
let cov_r = (FILTER_WEIGHTS[0] * sample(-2)
+ FILTER_WEIGHTS[1] * sample(-1)
+ FILTER_WEIGHTS[2] * sample(0)
+ FILTER_WEIGHTS[3] * sample(1)
+ FILTER_WEIGHTS[4] * sample(2))
/ FILTER_SUM;
let cov_g = (FILTER_WEIGHTS[0] * sample(-1)
+ FILTER_WEIGHTS[1] * sample(0)
+ FILTER_WEIGHTS[2] * sample(1)
+ FILTER_WEIGHTS[3] * sample(2)
+ FILTER_WEIGHTS[4] * sample(3))
/ FILTER_SUM;
let cov_b = (FILTER_WEIGHTS[0] * sample(0)
+ FILTER_WEIGHTS[1] * sample(1)
+ FILTER_WEIGHTS[2] * sample(2)
+ FILTER_WEIGHTS[3] * sample(3)
+ FILTER_WEIGHTS[4] * sample(4))
/ FILTER_SUM;
let mi = ((py as usize) * (mask_w as usize) + (px as usize)) * 3;
data[mi] = cov_r.min(255) as u8;
data[mi + 1] = cov_g.min(255) as u8;
data[mi + 2] = cov_b.min(255) as u8;
}
}
data
}
pub(super) fn apply_gray_collapse(gray: &[u8], gray_w: u32, mask_w: u32, mask_h: u32) -> Vec<u8> {
let mut data = vec![0u8; (mask_w as usize) * (mask_h as usize) * 3];
for py in 0..mask_h as usize {
let row_start = py * gray_w as usize;
let out_row = py * mask_w as usize * 3;
for px in 0..mask_w as usize {
let g = row_start + px * 3;
let cov = ((gray[g] as u32 + gray[g + 1] as u32 + gray[g + 2] as u32 + 1) / 3) as u8;
let o = out_row + px * 3;
data[o] = cov;
data[o + 1] = cov;
data[o + 2] = cov;
}
}
data
}