1use alloc::collections::BTreeMap;
25use alloc::vec::Vec;
26
27use gfx::{Dc, Surface};
28use rt::fx::{ceil_i, cvt, floor_i};
29
30use crate::font::{Gp, SGH_CBDT};
31use crate::registry::Face;
32use crate::Text;
33
34const GC_SLOTS: u32 = 4096;
35const GC_ARENA: usize = 2 * 1024 * 1024;
36const GC_MAXSIDE: i32 = 1024;
37const GC_MAXPIX: i32 = 256 * 1024; const GC_SIZE_LIMIT: i32 = 1 << 20; const GC_FONT_MASK: u32 = 0x3F_FFFF;
40const GC_BIGBMP: usize = GC_ARENA / 4; #[derive(Clone, Default)]
45pub struct GcEntry {
46 pub left: i32,
47 pub top: i32,
48 pub w: i32,
49 pub h: i32,
50 pub px: Vec<u8>,
51}
52
53#[non_exhaustive]
54pub enum GcRef<'a> {
55 None,
56 Direct,
57 Entry(&'a GcEntry),
58}
59
60#[derive(Default)]
61pub(crate) struct Gc {
62 pub(crate) map: BTreeMap<u64, GcEntry>,
63 bytes: usize,
64 big: Option<GcEntry>,
65 sharp: bool,
67 pub(crate) epoch: u32,
69}
70
71impl Gc {
72 fn charge(&mut self, n: usize) -> bool {
74 let n = (n + 15) & !15;
75 if self.bytes + n > GC_ARENA {
76 return false;
77 }
78 self.bytes += n;
79 true
80 }
81}
82
83pub(crate) fn gc_key(font: u32, glyph: u32, size: i32, phase: i32) -> u64 {
85 1u64 << 63
86 | ((font & GC_FONT_MASK) as u64) << 40
87 | ((phase & 3) as u64) << 36
88 | ((size as u32 & 0xF_FFFF) as u64) << 16
89 | (glyph & 0xFFFF) as u64
90}
91
92pub(crate) fn lcd_key(font: u32, glyph: u32, size: i32, phase: i32, vertical: bool) -> u64 {
94 gc_key(font, glyph, size, phase) | (if vertical { 2u64 } else { 1 }) << 38
95}
96
97pub(crate) fn bitmap_key(font: u32, glyph: u32, size: i32) -> u64 {
99 gc_key(font, glyph, size, 0) | 1 << 62
100}
101
102impl Text {
103 pub fn gc_flush(&mut self) {
105 self.gc.map.clear();
106 self.gc.bytes = 0;
107 self.gc.big = None;
108 self.gc.epoch = self.gc.epoch.wrapping_add(1);
109 }
110
111 pub fn gc_usage(&self) -> (usize, usize, usize, usize) {
113 (self.gc.map.len(), (GC_SLOTS / 2) as usize, self.gc.bytes, GC_ARENA)
114 }
115
116 pub fn gc_scale(&self, font: u32, size_fx: i32) -> (f32, f32) {
119 let h = self.hdr(font);
120 let sx = (size_fx as f32 * (1.0 / 256.0)) / h.upem as f32;
121 let mut sy = sx;
122 if self.font_raster(font) == crate::FontRaster::Legacy && size_fx <= 24 * 256 && h.xh > 0 {
123 let xh = h.xh as f32;
124 let t = cvt(xh * sx);
125 if t > 0 {
126 sy = t as f32 / xh;
127 }
128 }
129 (sx, sy)
130 }
131
132 fn gc_place(&mut self, font: u32, g: u32, size: i32, phase: i32) -> Option<(i32, i32, i32, i32, f32, f32)> {
134 let (sx, sy) = self.gc_scale(font, size);
135 let b = self.glyph_box(font, g)?;
136 let ph = (phase & 3) as f32 * 0.25;
137 let left = floor_i(b[0] as f32 * sx + ph);
138 let w = ceil_i(b[2] as f32 * sx + ph).wrapping_sub(left);
139 let top = floor_i(b[3].wrapping_neg() as f32 * sy);
140 let h = ceil_i(b[1].wrapping_neg() as f32 * sy).wrapping_sub(top);
141 if w <= 0 || h <= 0 {
142 return None;
143 }
144 Some((left, top, w, h, sx, sy))
145 }
146
147 pub fn gc_box(&mut self, font: u32, g: u32, size: i32, phase: i32) -> Option<[i32; 4]> {
149 self.gc_place(font, g, size, phase).map(|p| [p.0, p.1, p.2, p.3])
150 }
151
152 pub fn set_antialias(&mut self, on: bool) {
155 if self.gc.sharp == on {
156 self.gc.sharp = !on;
157 self.gc_flush();
158 }
159 }
160
161 pub fn antialias(&self) -> bool {
163 !self.gc.sharp
164 }
165
166 pub fn gc_get(&mut self, font: u32, g: u32, size: i32, phase: i32) -> GcRef<'_> {
168 let phase = phase & 3;
169 if size >= GC_SIZE_LIMIT || size < 0 {
170 return GcRef::Direct;
171 }
172 let key = gc_key(font, g, size, phase);
173 if self.gc.map.contains_key(&key) {
174 return GcRef::Entry(&self.gc.map[&key]);
175 }
176 if self.gc.map.len() as u32 >= GC_SLOTS / 2 {
178 self.gc_flush();
179 }
180 let Some((left, top, w, h, sx, sy)) = self.gc_place(font, g, size, phase) else {
182 self.gc.map.insert(key, GcEntry::default());
183 return GcRef::Entry(&self.gc.map[&key]);
184 };
185 if w > GC_MAXSIDE || h > GC_MAXSIDE || w * h > GC_MAXPIX {
186 return GcRef::Direct;
187 }
188 if !self.gc.charge((w * h + 8) as usize) {
190 self.gc_flush();
191 if !self.gc.charge((w * h + 8) as usize) {
192 return GcRef::None;
193 }
194 }
195 let mut sf = Surface::new_a8_packed(w, h);
197 let dc = Dc::new(w, h);
198 self.gp = Gp { x: phase as f32 * 0.25 + left.wrapping_neg() as f32, y: top.wrapping_neg() as f32, sx, sy, dx: 0, dy: 0 };
199 self.path.begin(&dc);
200 self.glyph_path(font, g);
201 self.path.fill(&mut self.ras, &dc, &mut sf, 0);
202 if self.gc.sharp {
203 for c in sf.px.iter_mut() {
204 *c = if *c >= 128 { 255 } else { 0 };
205 }
206 }
207 self.gc.map.insert(key, GcEntry { left, top, w, h, px: sf.px });
208 GcRef::Entry(&self.gc.map[&key])
209 }
210
211 pub fn gc_get_lcd(&mut self, font: u32, g: u32, size: i32, phase: i32, vertical: bool) -> GcRef<'_> {
215 let phase = phase & 3;
216 if size >= GC_SIZE_LIMIT || size < 0 {
217 return GcRef::Direct;
218 }
219 let key = lcd_key(font, g, size, phase, vertical);
220 if self.gc.map.contains_key(&key) {
221 return GcRef::Entry(&self.gc.map[&key]);
222 }
223 if self.gc.map.len() as u32 >= GC_SLOTS / 2 {
224 self.gc_flush();
225 }
226 let Some((left, top, w, h, sx, sy)) = self.gc_place(font, g, size, phase) else {
227 self.gc.map.insert(key, GcEntry::default());
228 return GcRef::Entry(&self.gc.map[&key]);
229 };
230 let (left, top, w, h) = if vertical { (left, top - 1, w, h + 2) } else { (left - 1, top, w + 2, h) };
232 if w > GC_MAXSIDE || h > GC_MAXSIDE || w * h * 3 > GC_MAXPIX {
233 return GcRef::Direct;
234 }
235 let n = (w * h * 3 + 8) as usize;
236 if !self.gc.charge(n) {
237 self.gc_flush();
238 if !self.gc.charge(n) {
239 return GcRef::None;
240 }
241 }
242 let (rw, rh) = if vertical { (w, h * 3) } else { (w * 3, h) };
244 let mut sf = Surface::new_a8_packed(rw, rh);
245 let dc = Dc::new(rw, rh);
246 let ph = phase as f32 * 0.25;
247 self.gp = if vertical {
248 Gp { x: ph - left as f32, y: (top * 3).wrapping_neg() as f32, sx, sy: sy * 3.0, dx: 0, dy: 0 }
249 } else {
250 Gp { x: (ph - left as f32) * 3.0, y: top.wrapping_neg() as f32, sx: sx * 3.0, sy, dx: 0, dy: 0 }
251 };
252 self.path.begin(&dc);
253 self.glyph_path(font, g);
254 self.path.fill(&mut self.ras, &dc, &mut sf, 0);
255 let px = crate::lcd::filter(&sf.px, w as usize, h as usize, vertical);
256 self.gc.map.insert(key, GcEntry { left, top, w, h, px });
257 GcRef::Entry(&self.gc.map[&key])
258 }
259
260 pub fn gc_get_bitmap(&mut self, font: u32, g: u32, size: i32) -> Option<&GcEntry> {
262 if !(0..GC_SIZE_LIMIT).contains(&size) {
263 return None;
264 }
265 match self.face(font)? {
266 Face::Tt(_) | Face::Syn(_) | Face::Var(_) => {}
267 _ => return None,
268 }
269 if self.hdr(font).flags & SGH_CBDT == 0 {
270 return None;
271 }
272 let key = bitmap_key(font, g, size);
273 if !self.gc.map.contains_key(&key) {
274 if self.gc.map.len() as u32 >= GC_SLOTS / 2 {
275 self.gc_flush();
276 }
277 match self.op_bitmap(font, g, size) {
278 None => {
279 self.gc.map.insert(key, GcEntry::default());
280 }
281 Some(b) => {
282 let n = (b.w * b.h * 4 + 8) as usize;
283 let e = GcEntry { left: b.left, top: b.top, w: b.w, h: b.h, px: b.px };
284 if n > GC_BIGBMP {
285 self.gc.big = Some(e);
287 return self.gc.big.as_ref();
288 }
289 if !self.gc.charge(n) {
290 self.gc_flush();
291 if !self.gc.charge(n) {
292 return None;
293 }
294 }
295 self.gc.map.insert(key, e);
296 }
297 }
298 }
299 let e = &self.gc.map[&key];
300 if e.w == 0 { None } else { Some(e) }
301 }
302}