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