Skip to main content

wr_glyph_rasterizer/platform/unix/
font.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#![allow(non_camel_case_types)]
6
7use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode};
8use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
9use api::{FontInstanceFlags, FontTemplate, FontVariation, NativeFontHandle};
10use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
11use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
12use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
13use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
14use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
15use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
16use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
17use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong, FT_Vector};
18use freetype::freetype::{FT_Err_Unimplemented_Feature, FT_MulFix, FT_Outline_Embolden};
19use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
20use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
21use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING};
22use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
23use freetype::freetype::FT_FACE_FLAG_MULTIPLE_MASTERS;
24use freetype::succeeded;
25use crate::gamma_lut::{ColorLut, GammaLut};
26use crate::rasterizer::{FontInstance, GlyphFormat, GlyphKey};
27use crate::rasterizer::{GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
28use crate::types::FastHashMap;
29#[cfg(any(not(any(target_os = "android", target_env = "ohos")), feature = "dynamic_freetype"))]
30use libc::{dlsym, RTLD_DEFAULT};
31use libc::free;
32use std::{cmp, mem, ptr, slice};
33use std::cmp::max;
34use std::ffi::CString;
35use std::sync::{Arc, Condvar, Mutex, MutexGuard};
36
37
38// These constants are not present in the freetype
39// bindings due to bindgen not handling the way
40// the macros are defined.
41//const FT_LOAD_TARGET_NORMAL: FT_UInt = 0 << 16;
42const FT_LOAD_TARGET_LIGHT: FT_UInt  = 1 << 16;
43const FT_LOAD_TARGET_MONO: FT_UInt   = 2 << 16;
44const FT_LOAD_TARGET_LCD: FT_UInt    = 3 << 16;
45const FT_LOAD_TARGET_LCD_V: FT_UInt  = 4 << 16;
46
47#[repr(C)]
48struct FT_Var_Axis {
49    pub name: *mut FT_String,
50    pub minimum: FT_Fixed,
51    pub def: FT_Fixed,
52    pub maximum: FT_Fixed,
53    pub tag: FT_ULong,
54    pub strid: FT_UInt,
55}
56
57#[repr(C)]
58struct FT_Var_Named_Style {
59    pub coords: *mut FT_Fixed,
60    pub strid: FT_UInt,
61    pub psid: FT_UInt,
62}
63
64#[repr(C)]
65struct FT_MM_Var {
66    pub num_axis: FT_UInt,
67    pub num_designs: FT_UInt,
68    pub num_namedstyles: FT_UInt,
69    pub axis: *mut FT_Var_Axis,
70    pub namedstyle: *mut FT_Var_Named_Style,
71}
72
73#[inline]
74pub fn unimplemented(error: FT_Error) -> bool {
75    error == FT_Err_Unimplemented_Feature as FT_Error
76}
77
78// Use dlsym to check for symbols. If not available. just return an unimplemented error.
79#[cfg(any(not(any(target_os = "android", target_env = "ohos")), feature = "dynamic_freetype"))]
80macro_rules! ft_dyn_fn {
81    ($func_name:ident($($arg_name:ident:$arg_type:ty),*) -> FT_Error) => {
82        #[allow(non_snake_case)]
83        unsafe fn $func_name($($arg_name:$arg_type),*) -> FT_Error {
84            extern "C" fn unimpl_func($(_:$arg_type),*) -> FT_Error {
85                FT_Err_Unimplemented_Feature as FT_Error
86            }
87            lazy_static! {
88                static ref FUNC: unsafe extern "C" fn($($arg_type),*) -> FT_Error = {
89                    unsafe {
90                        let cname = CString::new(stringify!($func_name)).unwrap();
91                        let ptr = dlsym(RTLD_DEFAULT, cname.as_ptr());
92                        if !ptr.is_null() { mem::transmute(ptr) } else { unimpl_func }
93                    }
94                };
95            }
96            (*FUNC)($($arg_name),*)
97        }
98    }
99}
100
101// On Android and OHOS, just statically link in the symbols...
102#[cfg(all(any(target_os = "android", target_env = "ohos"), not(feature = "dynamic_freetype")))]
103macro_rules! ft_dyn_fn {
104    ($($proto:tt)+) => { extern "C" { fn $($proto)+; } }
105}
106
107ft_dyn_fn!(FT_Get_MM_Var(face: FT_Face, desc: *mut *mut FT_MM_Var) -> FT_Error);
108ft_dyn_fn!(FT_Done_MM_Var(library: FT_Library, desc: *mut FT_MM_Var) -> FT_Error);
109ft_dyn_fn!(FT_Set_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error);
110ft_dyn_fn!(FT_Get_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error);
111
112extern "C" {
113    fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
114}
115
116// Custom version of FT_GlyphSlot_Embolden to be less aggressive with outline
117// fonts than the default implementation in FreeType.
118#[no_mangle]
119pub extern "C" fn mozilla_glyphslot_embolden_less(slot: FT_GlyphSlot) {
120    if slot.is_null() {
121        return;
122    }
123
124    let slot_ = unsafe { &mut *slot };
125    let format = slot_.format;
126    if format != FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE {
127        // For non-outline glyphs, just fall back to FreeType's function.
128        unsafe { FT_GlyphSlot_Embolden(slot) };
129        return;
130    }
131
132    let face_ = unsafe { *slot_.face };
133
134    // FT_GlyphSlot_Embolden uses a divisor of 24 here; we'll be only half as
135    // bold.
136    let size_ = unsafe { *face_.size };
137    let strength =
138        unsafe { FT_MulFix(face_.units_per_EM as FT_Long,
139                           size_.metrics.y_scale) / 48 };
140    unsafe { FT_Outline_Embolden(&mut slot_.outline, strength) };
141
142    // Adjust metrics to suit the fattened glyph.
143    if slot_.advance.x != 0 {
144        slot_.advance.x += strength;
145    }
146    if slot_.advance.y != 0 {
147        slot_.advance.y += strength;
148    }
149    slot_.metrics.width += strength;
150    slot_.metrics.height += strength;
151    slot_.metrics.horiAdvance += strength;
152    slot_.metrics.vertAdvance += strength;
153    slot_.metrics.horiBearingY += strength;
154}
155
156struct CachedFont {
157    template: FontTemplate,
158    face: FT_Face,
159    mm_var: *mut FT_MM_Var,
160    variations: Vec<FontVariation>,
161}
162
163impl Drop for CachedFont {
164    fn drop(&mut self) {
165        unsafe {
166            if !self.mm_var.is_null() &&
167                unimplemented(FT_Done_MM_Var((*(*self.face).glyph).library, self.mm_var)) {
168                free(self.mm_var as _);
169            }
170
171            FT_Done_Face(self.face);
172        }
173    }
174}
175
176struct FontCache {
177    lib: FT_Library,
178    // Maps a template to a cached font that may be used across all threads.
179    fonts: FastHashMap<FontTemplate, Arc<Mutex<CachedFont>>>,
180    // The current LCD filter installed in the library.
181    lcd_filter: FontLCDFilter,
182    // The number of threads currently relying on the LCD filter state.
183    lcd_filter_uses: usize,
184}
185
186// FreeType resources are safe to move between threads as long as they
187// are not concurrently accessed. In our case, everything is behind a
188// Mutex so it is safe to move them between threads.
189unsafe impl Send for CachedFont {}
190unsafe impl Send for FontCache {}
191
192impl FontCache {
193    fn new() -> Self {
194        let mut lib: FT_Library = ptr::null_mut();
195        let result = unsafe { FT_Init_FreeType(&mut lib) };
196        if succeeded(result) {
197            // Ensure the library uses the default LCD filter initially.
198            unsafe { FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT) };
199        } else {
200            panic!("Failed to initialize FreeType - {}", result)
201        }
202
203        FontCache {
204            lib,
205            fonts: FastHashMap::default(),
206            lcd_filter: FontLCDFilter::Default,
207            lcd_filter_uses: 0,
208        }
209    }
210
211    fn add_font(&mut self, template: FontTemplate) -> Result<Arc<Mutex<CachedFont>>, FT_Error> {
212        if let Some(cached) = self.fonts.get(&template) {
213            return Ok(cached.clone());
214        }
215        unsafe {
216            let mut face: FT_Face = ptr::null_mut();
217            let result = match template {
218                FontTemplate::Raw(ref bytes, index) => {
219                    FT_New_Memory_Face(
220                        self.lib,
221                        bytes.as_ptr(),
222                        bytes.len() as FT_Long,
223                        index as FT_Long,
224                        &mut face,
225                    )
226                }
227                FontTemplate::Native(NativeFontHandle { ref path, index }) => {
228                    let str = path.as_os_str().to_str().unwrap();
229                    let cstr = CString::new(str).unwrap();
230                    FT_New_Face(
231                        self.lib,
232                        cstr.as_ptr(),
233                        index as FT_Long,
234                        &mut face,
235                    )
236                }
237            };
238            if !succeeded(result) || face.is_null() {
239                return Err(result);
240            }
241            let mut mm_var = ptr::null_mut();
242            if ((*face).face_flags & (FT_FACE_FLAG_MULTIPLE_MASTERS as FT_Long)) != 0 &&
243               succeeded(FT_Get_MM_Var(face, &mut mm_var)) {
244                // Calling this before FT_Set_Var_Design_Coordinates avoids a bug with font variations
245                // not initialized properly in the font face, even if we ignore the result.
246                // See bug 1647035.
247                let mut tmp = [0; 16];
248                let res = FT_Get_Var_Design_Coordinates(
249                    face,
250                    (*mm_var).num_axis.min(16),
251                    tmp.as_mut_ptr()
252                );
253                debug_assert!(succeeded(res));
254            }
255            let cached = Arc::new(Mutex::new(CachedFont {
256                template: template.clone(),
257                face,
258                mm_var,
259                variations: Vec::new(),
260            }));
261            self.fonts.insert(template, cached.clone());
262            Ok(cached)
263        }
264    }
265
266    fn delete_font(&mut self, cached: Arc<Mutex<CachedFont>>) {
267        self.fonts.remove(&cached.lock().unwrap().template);
268    }
269}
270
271impl Drop for FontCache {
272    fn drop(&mut self) {
273        self.fonts.clear();
274        unsafe {
275            FT_Done_FreeType(self.lib);
276        }
277    }
278}
279
280lazy_static! {
281    static ref FONT_CACHE: Mutex<FontCache> = Mutex::new(FontCache::new());
282    static ref LCD_FILTER_UNUSED: Condvar = Condvar::new();
283}
284
285pub struct FontContext {
286    fonts: FastHashMap<FontKey, Arc<Mutex<CachedFont>>>,
287    gamma_luts: FastHashMap<(i16, i16), GammaLut>,
288}
289
290fn get_skew_bounds(bottom: i32, top: i32, skew_factor: f32, _vertical: bool) -> (f32, f32) {
291    let skew_min = (bottom as f32 + 0.5) * skew_factor;
292    let skew_max = (top as f32 - 0.5) * skew_factor;
293    // Negative skew factor may switch the sense of skew_min and skew_max.
294    (skew_min.min(skew_max).floor(), skew_min.max(skew_max).ceil())
295}
296
297fn skew_bitmap(
298    bitmap: &[u8],
299    width: usize,
300    height: usize,
301    left: i32,
302    top: i32,
303    skew_factor: f32,
304    vertical: bool, // TODO: vertical skew not yet implemented!
305) -> (Vec<u8>, usize, i32) {
306    let stride = width * 4;
307    // Calculate the skewed horizontal offsets of the bottom and top of the glyph.
308    let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top, skew_factor, vertical);
309    // Allocate enough extra width for the min/max skew offsets.
310    let skew_width = width + (skew_max - skew_min) as usize;
311    let mut skew_buffer = vec![0u8; skew_width * height * 4];
312    for y in 0 .. height {
313        // Calculate a skew offset at the vertical center of the current row.
314        let offset = (top as f32 - y as f32 - 0.5) * skew_factor - skew_min;
315        // Get a blend factor in 0..256 constant across all pixels in the row.
316        let blend = (offset.fract() * 256.0) as u32;
317        let src_row = y * stride;
318        let dest_row = (y * skew_width + offset.floor() as usize) * 4;
319        let mut prev_px = [0u32; 4];
320        for (src, dest) in
321            bitmap[src_row .. src_row + stride].chunks(4).zip(
322                skew_buffer[dest_row .. dest_row + stride].chunks_mut(4)
323            ) {
324            let px = [src[0] as u32, src[1] as u32, src[2] as u32, src[3] as u32];
325            // Blend current pixel with previous pixel based on blend factor.
326            let next_px = [px[0] * blend, px[1] * blend, px[2] * blend, px[3] * blend];
327            dest[0] = ((((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8) as u8;
328            dest[1] = ((((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8) as u8;
329            dest[2] = ((((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8) as u8;
330            dest[3] = ((((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8) as u8;
331            // Save the remainder for blending onto the next pixel.
332            prev_px = next_px;
333        }
334        // If the skew misaligns the final pixel, write out the remainder.
335        if blend > 0 {
336            let dest = &mut skew_buffer[dest_row + stride .. dest_row + stride + 4];
337            dest[0] = ((prev_px[0] + 128) >> 8) as u8;
338            dest[1] = ((prev_px[1] + 128) >> 8) as u8;
339            dest[2] = ((prev_px[2] + 128) >> 8) as u8;
340            dest[3] = ((prev_px[3] + 128) >> 8) as u8;
341        }
342    }
343    (skew_buffer, skew_width, left + skew_min as i32)
344}
345
346fn transpose_bitmap(bitmap: &[u8], width: usize, height: usize) -> Vec<u8> {
347    let mut transposed = vec![0u8; width * height * 4];
348    for (y, row) in bitmap.chunks(width * 4).enumerate() {
349        let mut offset = y * 4;
350        for src in row.chunks(4) {
351            transposed[offset .. offset + 4].copy_from_slice(src);
352            offset += height * 4;
353        }
354    }
355    transposed
356}
357
358fn flip_bitmap_x(bitmap: &mut [u8], width: usize, height: usize) {
359    assert!(bitmap.len() == width * height * 4);
360    let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) };
361    for row in pixels.chunks_mut(width) {
362        row.reverse();
363    }
364}
365
366fn flip_bitmap_y(bitmap: &mut [u8], width: usize, height: usize) {
367    assert!(bitmap.len() == width * height * 4);
368    let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) };
369    for y in 0 .. height / 2 {
370        let low_row = y * width;
371        let high_row = (height - 1 - y) * width;
372        for x in 0 .. width {
373            pixels.swap(low_row + x, high_row + x);
374        }
375    }
376}
377
378impl FontContext {
379    pub fn distribute_across_threads() -> bool {
380        false
381    }
382
383    pub fn new() -> FontContext {
384        FontContext {
385            fonts: FastHashMap::default(),
386            gamma_luts: FastHashMap::default(),
387        }
388    }
389
390    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) {
391        if !self.fonts.contains_key(font_key) {
392            let len = bytes.len();
393            match FONT_CACHE.lock().unwrap().add_font(FontTemplate::Raw(bytes, index)) {
394                Ok(font) => self.fonts.insert(*font_key, font),
395                Err(result) => panic!("adding raw font failed: {} bytes, err={:?}", len, result),
396            };
397        }
398    }
399
400    pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle) {
401        if !self.fonts.contains_key(font_key) {
402            let path = native_font_handle.path.to_string_lossy().into_owned();
403            match FONT_CACHE.lock().unwrap().add_font(FontTemplate::Native(native_font_handle)) {
404                Ok(font) => self.fonts.insert(*font_key, font),
405                Err(result) => panic!("adding native font failed: file={} err={:?}", path, result),
406            };
407        }
408    }
409
410    /// Returns whether the font described by `template` actually contains
411    /// embedded bitmap strikes (FreeType `FT_FACE_FLAG_FIXED_SIZES`). The
412    /// `EMBEDDED_BITMAPS` instance flag is unreliable here because on unix it
413    /// only reflects a fontconfig preference and is set even for ordinary
414    /// outline fonts, so anything that needs to know whether a glyph will really
415    /// rasterize to a bitmap must consult the face itself. The face is loaded
416    /// into the shared cache that the rasterizing worker threads reuse, so this
417    /// does not duplicate work.
418    pub fn has_bitmap_strikes(template: &FontTemplate) -> bool {
419        let cached = match FONT_CACHE.lock().unwrap().add_font(template.clone()) {
420            Ok(font) => font,
421            Err(_) => return false,
422        };
423        let cached = cached.lock().unwrap();
424        (unsafe { (*cached.face).face_flags } & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0
425    }
426
427    pub fn delete_font(&mut self, font_key: &FontKey) {
428        if let Some(cached) = self.fonts.remove(font_key) {
429            // If the only references to this font are the FontCache and this FontContext,
430            // then delete the font as there are no other existing users.
431            if Arc::strong_count(&cached) <= 2 {
432                FONT_CACHE.lock().unwrap().delete_font(cached);
433            }
434        }
435    }
436
437    pub fn delete_font_instance(&mut self, _instance: &FontInstance) {
438    }
439
440    fn load_glyph<'a>(
441        fonts: &'a FastHashMap<FontKey, Arc<Mutex<CachedFont>>>,
442        font: &FontInstance,
443        glyph: &GlyphKey,
444    ) -> Option<(MutexGuard<'a, CachedFont>, FT_GlyphSlot, f32)> {
445        let mut cached = fonts.get(&font.font_key)?.lock().ok()?;
446        let face = cached.face;
447
448        let mm_var = cached.mm_var;
449        if !mm_var.is_null() && font.variations != cached.variations {
450            cached.variations.clear();
451            cached.variations.extend_from_slice(&font.variations);
452
453            unsafe {
454                let num_axis = (*mm_var).num_axis;
455                let mut coords: Vec<FT_Fixed> = Vec::with_capacity(num_axis as usize);
456                for i in 0 .. num_axis {
457                    let axis = (*mm_var).axis.offset(i as isize);
458                    let mut value = (*axis).def;
459                    for var in &font.variations {
460                        if var.tag as FT_ULong == (*axis).tag {
461                            value = (var.value * 65536.0 + 0.5) as FT_Fixed;
462                            value = cmp::min(value, (*axis).maximum);
463                            value = cmp::max(value, (*axis).minimum);
464                            break;
465                        }
466                    }
467                    coords.push(value);
468                }
469                let res = FT_Set_Var_Design_Coordinates(face, num_axis, coords.as_mut_ptr());
470                debug_assert!(succeeded(res));
471            }
472        }
473
474        let mut load_flags = FT_LOAD_DEFAULT;
475        let FontInstancePlatformOptions { mut hinting, .. } = font.platform_options.unwrap_or_default();
476        // Disable hinting if there is a non-axis-aligned transform.
477        if font.synthetic_italics.is_enabled() ||
478           ((font.transform.scale_x != 0.0 || font.transform.scale_y != 0.0) &&
479            (font.transform.skew_x != 0.0 || font.transform.skew_y != 0.0)) {
480            hinting = FontHinting::None;
481        }
482        match (hinting, font.render_mode) {
483            (FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING,
484            (FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO,
485            (FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT,
486            (FontHinting::LCD, FontRenderMode::Subpixel) => {
487                load_flags = if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
488                    FT_LOAD_TARGET_LCD_V
489                } else {
490                    FT_LOAD_TARGET_LCD
491                };
492                if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) {
493                    load_flags |= FT_LOAD_FORCE_AUTOHINT;
494                }
495            }
496            _ => {
497                if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) {
498                    load_flags |= FT_LOAD_FORCE_AUTOHINT;
499                }
500            }
501        }
502
503        if font.flags.contains(FontInstanceFlags::NO_AUTOHINT) {
504            load_flags |= FT_LOAD_NO_AUTOHINT;
505        }
506        if !font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS) {
507            load_flags |= FT_LOAD_NO_BITMAP;
508        }
509
510        let face_flags = unsafe { (*face).face_flags };
511        if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
512          // We only set FT_LOAD_COLOR if there are bitmap strikes;
513          // COLR (color-layer) fonts are handled internally by Gecko, and
514          // WebRender is just asked to paint individual layers.
515          load_flags |= FT_LOAD_COLOR;
516        }
517
518        load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
519
520        let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
521        let req_size = font.size.to_f64_px();
522
523        let mut result = if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 &&
524                            (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 &&
525                            (load_flags & FT_LOAD_NO_BITMAP) == 0 {
526            unsafe { FT_Set_Transform(face, ptr::null_mut(), ptr::null_mut()) };
527            Self::choose_bitmap_size(face, req_size * y_scale)
528        } else {
529            let mut shape = font.transform.invert_scale(x_scale, y_scale);
530            if font.flags.contains(FontInstanceFlags::FLIP_X) {
531                shape = shape.flip_x();
532            }
533            if font.flags.contains(FontInstanceFlags::FLIP_Y) {
534                shape = shape.flip_y();
535            }
536            if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
537                shape = shape.swap_xy();
538            }
539            let (mut tx, mut ty) = (0.0, 0.0);
540            if font.synthetic_italics.is_enabled() {
541                let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, y_scale * req_size);
542                shape = shape_;
543                tx = tx_;
544                ty = ty_;
545            };
546            let mut ft_shape = FT_Matrix {
547                xx: (shape.scale_x * 65536.0) as FT_Fixed,
548                xy: (shape.skew_x * -65536.0) as FT_Fixed,
549                yx: (shape.skew_y * -65536.0) as FT_Fixed,
550                yy: (shape.scale_y * 65536.0) as FT_Fixed,
551            };
552            // The delta vector for FT_Set_Transform is in units of 1/64 pixel.
553            let mut ft_delta = FT_Vector {
554                x: (tx * 64.0) as FT_F26Dot6,
555                y: (ty * -64.0) as FT_F26Dot6,
556            };
557            unsafe {
558                FT_Set_Transform(face, &mut ft_shape, &mut ft_delta);
559                FT_Set_Char_Size(
560                    face,
561                    (req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6,
562                    (req_size * y_scale * 64.0 + 0.5) as FT_F26Dot6,
563                    0,
564                    0,
565                )
566            }
567        };
568
569        if !succeeded(result) {
570            error!("Unable to set glyph size and transform: {}", result);
571            //let raw_error = unsafe { FT_Error_String(result) };
572            //if !raw_error.is_ptr() {
573            //    error!("\tcode {:?}", CStr::from_ptr(raw_error));
574            //}
575            debug!(
576                "\t[{}] for size {:?} and scale {:?} from font {:?}",
577                glyph.index(),
578                req_size,
579                (x_scale, y_scale),
580                font.font_key,
581            );
582            return None;
583        }
584
585        result = unsafe { FT_Load_Glyph(face, glyph.index() as FT_UInt, load_flags as FT_Int32) };
586        if !succeeded(result) {
587            error!("Unable to load glyph: {}", result);
588            //let raw_error = unsafe { FT_Error_String(result) };
589            //if !raw_error.is_ptr() {
590            //    error!("\tcode {:?}", CStr::from_ptr(raw_error));
591            //}
592            debug!(
593                "\t[{}] with flags {:?} from font {:?}",
594                glyph.index(),
595                load_flags,
596                font.font_key,
597            );
598            return None;
599        }
600
601        let slot = unsafe { (*face).glyph };
602        assert!(slot != ptr::null_mut());
603
604        if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
605            mozilla_glyphslot_embolden_less(slot);
606        }
607
608        let format = unsafe { (*slot).format };
609        match format {
610            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
611                let bitmap_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
612                Some((cached, slot, req_size as f32 / bitmap_size as f32))
613            }
614            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some((cached, slot, 1.0)),
615            _ => {
616                error!("Unsupported format");
617                debug!("format={:?}", format);
618                None
619            }
620        }
621    }
622
623    fn pad_bounding_box(font: &FontInstance, cbox: &mut FT_BBox) {
624        // Apply extra pixel of padding for subpixel AA, due to the filter.
625        if font.render_mode == FontRenderMode::Subpixel {
626            // Using an LCD filter may add one full pixel to each side if support is built in.
627            // As of FreeType 2.8.1, an LCD filter is always used regardless of settings
628            // if support for the patent-encumbered LCD filter algorithms is not built in.
629            // Thus, the only reasonable way to guess padding is to unconditonally add it if
630            // subpixel AA is used.
631            let lcd_extra_pixels = 1;
632            let padding = (lcd_extra_pixels * 64) as FT_Pos;
633            if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
634                cbox.yMin -= padding;
635                cbox.yMax += padding;
636            } else {
637                cbox.xMin -= padding;
638                cbox.xMax += padding;
639            }
640        }
641    }
642
643    // Get the bounding box for a glyph, accounting for sub-pixel positioning.
644    fn get_bounding_box(
645        slot: FT_GlyphSlot,
646        font: &FontInstance,
647        glyph: &GlyphKey,
648        scale: f32,
649    ) -> FT_BBox {
650        // Get the estimated bounding box from FT (control points).
651        let mut cbox = FT_BBox { xMin: 0, yMin: 0, xMax: 0, yMax: 0 };
652
653        unsafe {
654            FT_Outline_Get_CBox(&(*slot).outline, &mut cbox);
655        }
656
657        // For spaces and other non-printable characters, early out.
658        if unsafe { (*slot).outline.n_contours } == 0 {
659            return cbox;
660        }
661
662        Self::pad_bounding_box(font, &mut cbox);
663
664        // Offset the bounding box by subpixel positioning.
665        // Convert to 26.6 fixed point format for FT.
666        let (dx, dy) = font.get_subpx_offset(glyph);
667        let (dx, dy) = (
668            (dx / scale as f64 * 64.0 + 0.5) as FT_Pos,
669            -(dy / scale as f64 * 64.0 + 0.5) as FT_Pos,
670        );
671        cbox.xMin += dx;
672        cbox.xMax += dx;
673        cbox.yMin += dy;
674        cbox.yMax += dy;
675
676        // Outset the box to device pixel boundaries
677        cbox.xMin &= !63;
678        cbox.yMin &= !63;
679        cbox.xMax = (cbox.xMax + 63) & !63;
680        cbox.yMax = (cbox.yMax + 63) & !63;
681
682        cbox
683    }
684
685    fn get_glyph_dimensions_impl(
686        slot: FT_GlyphSlot,
687        font: &FontInstance,
688        glyph: &GlyphKey,
689        scale: f32,
690        use_transform: bool,
691    ) -> Option<GlyphDimensions> {
692        let format = unsafe { (*slot).format };
693        let (mut left, mut top, mut width, mut height) = match format {
694            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
695                unsafe { (
696                    (*slot).bitmap_left as i32,
697                    (*slot).bitmap_top as i32,
698                    (*slot).bitmap.width as i32,
699                    (*slot).bitmap.rows as i32,
700                ) }
701            }
702            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
703                let cbox = Self::get_bounding_box(slot, font, glyph, scale);
704                (
705                    (cbox.xMin >> 6) as i32,
706                    (cbox.yMax >> 6) as i32,
707                    ((cbox.xMax - cbox.xMin) >> 6) as i32,
708                    ((cbox.yMax - cbox.yMin) >> 6) as i32,
709                )
710            }
711            _ => return None,
712        };
713        let mut advance = unsafe { (*slot).metrics.horiAdvance as f32 / 64.0 };
714        if use_transform {
715            if scale != 1.0 {
716                let x0 = left as f32 * scale;
717                let x1 = width as f32 * scale + x0;
718                let y1 = top as f32 * scale;
719                let y0 = y1 - height as f32 * scale;
720                left = x0.round() as i32;
721                top = y1.round() as i32;
722                width = (x1.ceil() - x0.floor()) as i32;
723                height = (y1.ceil() - y0.floor()) as i32;
724                advance *= scale;
725            }
726            // An outline glyph's cbox would have already been transformed inside FT_Load_Glyph,
727            // so only handle bitmap glyphs which are not handled by FT_Load_Glyph.
728            if format == FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP {
729                if font.synthetic_italics.is_enabled() {
730                    let (skew_min, skew_max) = get_skew_bounds(
731                        top - height as i32,
732                        top,
733                        font.synthetic_italics.to_skew(),
734                        font.flags.contains(FontInstanceFlags::VERTICAL),
735                    );
736                    left += skew_min as i32;
737                    width += (skew_max - skew_min) as i32;
738                }
739                if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
740                    mem::swap(&mut width, &mut height);
741                    mem::swap(&mut left, &mut top);
742                    left -= width as i32;
743                    top += height as i32;
744                }
745                if font.flags.contains(FontInstanceFlags::FLIP_X) {
746                    left = -(left + width as i32);
747                }
748                if font.flags.contains(FontInstanceFlags::FLIP_Y) {
749                    top = -(top - height as i32);
750                }
751            }
752        }
753        Some(GlyphDimensions {
754            left,
755            top,
756            width,
757            height,
758            advance,
759        })
760    }
761
762    pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
763        let cached = self.fonts.get(&font_key)?.lock().ok()?;
764        let face = cached.face;
765        unsafe {
766            let idx = FT_Get_Char_Index(face, ch as _);
767            if idx != 0 {
768                Some(idx)
769            } else {
770                None
771            }
772        }
773    }
774
775    pub fn get_glyph_dimensions(
776        &mut self,
777        font: &FontInstance,
778        key: &GlyphKey,
779    ) -> Option<GlyphDimensions> {
780        let (_cached, slot, scale) = Self::load_glyph(&self.fonts, font, key)?;
781        Self::get_glyph_dimensions_impl(slot, &font, key, scale, true)
782    }
783
784    fn choose_bitmap_size(face: FT_Face, requested_size: f64) -> FT_Error {
785        let mut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size;
786        let mut best_size = 0;
787        let num_fixed_sizes = unsafe { (*face).num_fixed_sizes };
788        for i in 1 .. num_fixed_sizes {
789            // Distance is positive if strike is larger than desired size,
790            // or negative if smaller. If previously a found smaller strike,
791            // then prefer a larger strike. Otherwise, minimize distance.
792            let dist = unsafe { *(*face).available_sizes.offset(i as isize) }.y_ppem as f64 / 64.0 - requested_size;
793            if (best_dist < 0.0 && dist >= best_dist) || dist.abs() <= best_dist {
794                best_dist = dist;
795                best_size = i;
796            }
797        }
798        unsafe { FT_Select_Size(face, best_size) }
799    }
800
801    pub fn prepare_font(font: &mut FontInstance) {
802        let preblend_enabled = font.platform_options.map_or(false, |o| o.gamma >= 0 || o.enhanced_contrast > 0);
803        match font.render_mode {
804            FontRenderMode::Mono => {
805                // In mono mode the color of the font is irrelevant.
806                font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
807                // Subpixel positioning is disabled in mono mode.
808                font.disable_subpixel_position();
809            }
810            FontRenderMode::Alpha => {
811                if preblend_enabled {
812                    font.color = font.color.luminance_color().quantize();
813                } else {
814                    // Color is unused if there is no preblend.
815                    font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
816                }
817            }
818            FontRenderMode::Subpixel => {
819                if preblend_enabled {
820                    font.color = font.color.quantize();
821                } else {
822                    // Color is unused if there is no preblend.
823                    font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
824                }
825            }
826        }
827    }
828
829    fn rasterize_glyph_outline(
830        slot: FT_GlyphSlot,
831        font: &FontInstance,
832        key: &GlyphKey,
833        scale: f32,
834    ) -> bool {
835        // Get the subpixel offsets in FT 26.6 format.
836        let (dx, dy) = font.get_subpx_offset(key);
837        let (dx, dy) = (
838            (dx / scale as f64 * 64.0 + 0.5) as FT_Pos,
839            -(dy / scale as f64 * 64.0 + 0.5) as FT_Pos,
840        );
841
842        // Move the outline curves to be at the origin, taking
843        // into account the subpixel positioning.
844        unsafe {
845            let outline = &(*slot).outline;
846            let mut cbox = FT_BBox { xMin: 0, yMin: 0, xMax: 0, yMax: 0 };
847            FT_Outline_Get_CBox(outline, &mut cbox);
848            Self::pad_bounding_box(font, &mut cbox);
849            FT_Outline_Translate(
850                outline,
851                dx - ((cbox.xMin + dx) & !63),
852                dy - ((cbox.yMin + dy) & !63),
853            );
854        }
855
856        let render_mode = match font.render_mode {
857            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
858            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
859            FontRenderMode::Subpixel => if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
860                FT_Render_Mode::FT_RENDER_MODE_LCD_V
861            } else {
862                FT_Render_Mode::FT_RENDER_MODE_LCD
863            },
864        };
865        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
866        if !succeeded(result) {
867            error!("Unable to rasterize");
868            debug!(
869                "{:?} with {:?}, {:?}",
870                key,
871                render_mode,
872                result
873            );
874            false
875        } else {
876            true
877        }
878    }
879
880    pub fn begin_rasterize(font: &FontInstance) {
881        // The global LCD filter state is only used in subpixel rendering modes.
882        if font.render_mode == FontRenderMode::Subpixel {
883            let mut cache = FONT_CACHE.lock().unwrap();
884            let FontInstancePlatformOptions { lcd_filter, .. } = font.platform_options.unwrap_or_default();
885            // Check if the current LCD filter matches the requested one.
886            if cache.lcd_filter != lcd_filter {
887                // If the filter doesn't match, we have to wait for all other currently rasterizing threads
888                // that may use the LCD filter state to finish before we can override it.
889                while cache.lcd_filter_uses != 0 {
890                    cache = LCD_FILTER_UNUSED.wait(cache).unwrap();
891                }
892                // Finally set the LCD filter to the requested one now that the library is unused.
893                cache.lcd_filter = lcd_filter;
894                let filter = match lcd_filter {
895                    FontLCDFilter::None => FT_LcdFilter::FT_LCD_FILTER_NONE,
896                    FontLCDFilter::Default => FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
897                    FontLCDFilter::Light => FT_LcdFilter::FT_LCD_FILTER_LIGHT,
898                    FontLCDFilter::Legacy => FT_LcdFilter::FT_LCD_FILTER_LEGACY,
899                };
900                unsafe {
901                    let result = FT_Library_SetLcdFilter(cache.lib, filter);
902                    // Setting the legacy filter may fail, so just use the default filter instead.
903                    if !succeeded(result) {
904                        FT_Library_SetLcdFilter(cache.lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
905                    }
906                }
907            }
908            cache.lcd_filter_uses += 1;
909        }
910    }
911
912    pub fn end_rasterize(font: &FontInstance) {
913        if font.render_mode == FontRenderMode::Subpixel {
914            let mut cache = FONT_CACHE.lock().unwrap();
915            // If this is the last use of the LCD filter, then signal that the LCD filter isn't used.
916            cache.lcd_filter_uses -= 1;
917            if cache.lcd_filter_uses == 0 {
918                LCD_FILTER_UNUSED.notify_all();
919            }
920        }
921    }
922
923    pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
924        let (_cached, slot, scale) = Self::load_glyph(&self.fonts, font, key)
925                                         .ok_or(GlyphRasterError::LoadFailed)?;
926
927        // Get dimensions of the glyph, to see if we need to rasterize it.
928        // Don't apply scaling to the dimensions, as the glyph cache needs to know the actual
929        // footprint of the glyph.
930        let dimensions = Self::get_glyph_dimensions_impl(slot, font, key, scale, false)
931                             .ok_or(GlyphRasterError::LoadFailed)?;
932        let GlyphDimensions { mut left, mut top, width, height, .. } = dimensions;
933
934        // For spaces and other non-printable characters, early out.
935        if width == 0 || height == 0 {
936            return Err(GlyphRasterError::LoadFailed);
937        }
938
939        let format = unsafe { (*slot).format };
940        match format {
941            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {}
942            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
943                if !Self::rasterize_glyph_outline(slot, font, key, scale) {
944                    return Err(GlyphRasterError::LoadFailed);
945                }
946            }
947            _ => {
948                error!("Unsupported format");
949                debug!("format={:?}", format);
950                return Err(GlyphRasterError::LoadFailed);
951            }
952        };
953
954        debug!(
955            "Rasterizing {:?} as {:?} with dimensions {:?}",
956            key,
957            font.render_mode,
958            dimensions
959        );
960
961        let bitmap = unsafe { &(*slot).bitmap };
962        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
963        let (mut actual_width, mut actual_height) = match pixel_mode {
964            FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
965                assert!(bitmap.width % 3 == 0);
966                ((bitmap.width / 3) as usize, bitmap.rows as usize)
967            }
968            FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
969                assert!(bitmap.rows % 3 == 0);
970                (bitmap.width as usize, (bitmap.rows / 3) as usize)
971            }
972            FT_Pixel_Mode::FT_PIXEL_MODE_MONO |
973            FT_Pixel_Mode::FT_PIXEL_MODE_GRAY |
974            FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
975                (bitmap.width as usize, bitmap.rows as usize)
976            }
977            _ => panic!("Unsupported mode"),
978        };
979
980        // If we need padding, we will need to expand the buffer size.
981        let (buffer_width, buffer_height, padding) = if font.use_texture_padding() {
982            (actual_width + 2, actual_height + 2, 1)
983        } else {
984            (actual_width, actual_height, 0)
985        };
986
987        let mut final_buffer = vec![0u8; buffer_width * buffer_height * 4];
988
989        // Extract the final glyph from FT format into BGRA8 format, which is
990        // what WR expects.
991        let subpixel_bgr = font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR);
992        let mut src_row = bitmap.buffer;
993        let mut dest = 4 * padding * (padding + buffer_width);
994        let actual_end = final_buffer.len() - 4 * padding * (buffer_width + 1);
995        while dest < actual_end {
996            let mut src = src_row;
997            let row_end = dest + actual_width * 4;
998            match pixel_mode {
999                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
1000                    while dest < row_end {
1001                        // Cast the byte to signed so that we can left shift each bit into
1002                        // the top bit, then right shift to fill out the bits with 0s or 1s.
1003                        let mut byte: i8 = unsafe { *src as i8 };
1004                        src = unsafe { src.offset(1) };
1005                        let byte_end = cmp::min(row_end, dest + 8 * 4);
1006                        while dest < byte_end {
1007                            let alpha = (byte >> 7) as u8;
1008                            final_buffer[dest + 0] = alpha;
1009                            final_buffer[dest + 1] = alpha;
1010                            final_buffer[dest + 2] = alpha;
1011                            final_buffer[dest + 3] = alpha;
1012                            dest += 4;
1013                            byte <<= 1;
1014                        }
1015                    }
1016                }
1017                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
1018                    while dest < row_end {
1019                        let alpha = unsafe { *src };
1020                        final_buffer[dest + 0] = alpha;
1021                        final_buffer[dest + 1] = alpha;
1022                        final_buffer[dest + 2] = alpha;
1023                        final_buffer[dest + 3] = alpha;
1024                        src = unsafe { src.offset(1) };
1025                        dest += 4;
1026                    }
1027                }
1028                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
1029                    while dest < row_end {
1030                        let (mut r, g, mut b) = unsafe { (*src, *src.offset(1), *src.offset(2)) };
1031                        if subpixel_bgr {
1032                            mem::swap(&mut r, &mut b);
1033                        }
1034                        final_buffer[dest + 0] = b;
1035                        final_buffer[dest + 1] = g;
1036                        final_buffer[dest + 2] = r;
1037                        final_buffer[dest + 3] = max(max(b, g), r);
1038                        src = unsafe { src.offset(3) };
1039                        dest += 4;
1040                    }
1041                }
1042                FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
1043                    while dest < row_end {
1044                        let (mut r, g, mut b) =
1045                            unsafe { (*src, *src.offset(bitmap.pitch as isize),
1046                                      *src.offset((2 * bitmap.pitch) as isize)) };
1047                        if subpixel_bgr {
1048                            mem::swap(&mut r, &mut b);
1049                        }
1050                        final_buffer[dest + 0] = b;
1051                        final_buffer[dest + 1] = g;
1052                        final_buffer[dest + 2] = r;
1053                        final_buffer[dest + 3] = max(max(b, g), r);
1054                        src = unsafe { src.offset(1) };
1055                        dest += 4;
1056                    }
1057                    src_row = unsafe { src_row.offset((2 * bitmap.pitch) as isize) };
1058                }
1059                FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
1060                    // The source is premultiplied BGRA data.
1061                    let dest_slice = &mut final_buffer[dest .. row_end];
1062                    let src_slice = unsafe { slice::from_raw_parts(src, dest_slice.len()) };
1063                    dest_slice.copy_from_slice(src_slice);
1064                }
1065                _ => panic!("Unsupported mode"),
1066            }
1067            src_row = unsafe { src_row.offset(bitmap.pitch as isize) };
1068            dest = row_end + 8 * padding;
1069        }
1070
1071        if font.use_texture_padding() {
1072            left -= padding as i32;
1073            top += padding as i32;
1074            actual_width = buffer_width;
1075            actual_height = buffer_height;
1076        }
1077
1078        match format {
1079            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
1080                if font.synthetic_italics.is_enabled() {
1081                    let (skew_buffer, skew_width, skew_left) = skew_bitmap(
1082                        &final_buffer,
1083                        actual_width,
1084                        actual_height,
1085                        left,
1086                        top,
1087                        font.synthetic_italics.to_skew(),
1088                        font.flags.contains(FontInstanceFlags::VERTICAL),
1089                    );
1090                    final_buffer = skew_buffer;
1091                    actual_width = skew_width;
1092                    left = skew_left;
1093                }
1094                if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
1095                    final_buffer = transpose_bitmap(&final_buffer, actual_width, actual_height);
1096                    mem::swap(&mut actual_width, &mut actual_height);
1097                    mem::swap(&mut left, &mut top);
1098                    left -= actual_width as i32;
1099                    top += actual_height as i32;
1100                }
1101                if font.flags.contains(FontInstanceFlags::FLIP_X) {
1102                    flip_bitmap_x(&mut final_buffer, actual_width, actual_height);
1103                    left = -(left + actual_width as i32);
1104                }
1105                if font.flags.contains(FontInstanceFlags::FLIP_Y) {
1106                    flip_bitmap_y(&mut final_buffer, actual_width, actual_height);
1107                    top = -(top - actual_height as i32);
1108                }
1109            }
1110            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
1111                unsafe {
1112                    left += (*slot).bitmap_left;
1113                    top += (*slot).bitmap_top - height as i32;
1114                }
1115            }
1116            _ => {}
1117        }
1118
1119        let glyph_format = match (pixel_mode, format) {
1120            (FT_Pixel_Mode::FT_PIXEL_MODE_LCD, _) |
1121            (FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V, _) => font.get_subpixel_glyph_format(),
1122            (FT_Pixel_Mode::FT_PIXEL_MODE_BGRA, _) => GlyphFormat::ColorBitmap,
1123            (_, FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP) => GlyphFormat::Bitmap,
1124            _ => font.get_alpha_glyph_format(),
1125        };
1126
1127        Self::gamma_correct_pixels(font, &mut self.gamma_luts, &mut final_buffer, glyph_format);
1128
1129        Ok(RasterizedGlyph {
1130            left: left as f32,
1131            top: top as f32,
1132            width: actual_width as i32,
1133            height: actual_height as i32,
1134            scale,
1135            format: glyph_format,
1136            bytes: final_buffer,
1137            is_packed_glyph: false,
1138        })
1139    }
1140
1141    fn gamma_correct_pixels(
1142        font: &FontInstance,
1143        gamma_luts: &mut FastHashMap<(i16, i16), GammaLut>,
1144        pixels: &mut Vec<u8>,
1145        glyph_format: GlyphFormat,
1146    ) {
1147        let FontInstancePlatformOptions { gamma, enhanced_contrast, .. } =
1148            font.platform_options.unwrap_or_default();
1149
1150        if gamma < 0 && enhanced_contrast <= 0 {
1151            return;
1152        }
1153
1154        match glyph_format {
1155            GlyphFormat::ColorBitmap | GlyphFormat::Bitmap => return,
1156            _ => {}
1157        }
1158
1159        let gamma = gamma.min(400);
1160        let enhanced_contrast = enhanced_contrast.clamp(0, 100);
1161        let g = if gamma < 0 { 1.0 } else { gamma as f32 / 100.0 };
1162        let c = enhanced_contrast as f32 / 100.0;
1163
1164        if gamma_luts.len() > 4 {
1165            gamma_luts.clear();
1166        }
1167        let gamma_lut = gamma_luts
1168            .entry((gamma, enhanced_contrast))
1169            .or_insert_with(|| GammaLut::new(c, g, g));
1170
1171        match font.render_mode {
1172            FontRenderMode::Alpha => {
1173                gamma_lut.preblend_grayscale(pixels, font.color);
1174            }
1175            FontRenderMode::Subpixel => {
1176                gamma_lut.preblend(pixels, font.color);
1177            }
1178            _ => {}
1179        }
1180    }
1181}
1182