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