atmos/os_lib/web_engine/font_gamma.rs
1//! グリフ被覆率(アルファ)のガンマ・コントラスト補正。
2//!
3//! ## 背景と課題
4//! ディスプレイのガンマ特性(γ ≈ 2.2)により、線形色空間でのアルファブレンド
5//! (`C = C_fg * a + C_bg * (1 - a)`)では、暗い背景に白い文字を描く場合、
6//! 被覆率 50%(a=128)の縁ピクセルが知覚的にわずか 22% の明るさに沈んでしまう。
7//! これにより文字の縁が急激に暗く落ち込み、Chrome(Skia)等のガンマ補正エンジンに
8//! 比べて文字全体が細く見える「暗背景での細字現象」が発生する。
9//!
10//! ## 解決策
11//! 前景色(文字)と背景色の明度差を計算し、文字が背景より明るい場合(白文字 on 暗背景など)に
12//! 被覆率(アルファ)を持ち上げるガンマ・コントラスト補正(Parabolic boost)を適用する。
13//! 整数演算のみで高速に計算し、浮動小数点や外部ライブラリへの依存を持たない純粋モジュールとする。
14
15/// 前景色と背景色の明度差に基づき、グリフ被覆率(アルファ 0..255)を補正する。
16///
17/// - `alpha == 0` または `alpha == 255` は不変(端点保存)。
18/// - 前景が背景より明るい場合、中間の被覆率(縁)を持ち上げて太さと濃度を保つ。
19/// - 前景が背景と同等または暗い場合(黒文字 on 白背景など)は補正せず元の値を返す。
20#[inline]
21pub fn correct_glyph_coverage(alpha: u8, fg_color: u32, bg_color: u32) -> u8 {
22 if alpha == 0 || alpha == 255 {
23 return alpha;
24 }
25
26 let fg_r = (fg_color >> 16) & 0xFF;
27 let fg_g = (fg_color >> 8) & 0xFF;
28 let fg_b = fg_color & 0xFF;
29
30 let bg_r = (bg_color >> 16) & 0xFF;
31 let bg_g = (bg_color >> 8) & 0xFF;
32 let bg_b = bg_color & 0xFF;
33
34 // Rec. 601 輝度近似 (0..255)
35 let fg_lum = (fg_r * 299 + fg_g * 587 + fg_b * 114) / 1000;
36 let bg_lum = (bg_r * 299 + bg_g * 587 + bg_b * 114) / 1000;
37
38 if fg_lum > bg_lum {
39 let diff = fg_lum - bg_lum; // 1..255
40 let a = alpha as u32;
41 // 中間アルファ(128付近)を滑らかに持ち上げるパラボラ曲線: a * (255 - a)
42 let parabola = a * (255 - a);
43 // 最大差分(255)で約 +35〜45 程度のアルファ持ち上げ
44 let boost = (parabola * diff) / (255 * 350);
45 let corrected = (a + boost).min(255);
46 corrected as u8
47 } else {
48 alpha
49 }
50}