From 8e9ac4d7efdd3a0f3fd8767d347613e74695bb16 Mon Sep 17 00:00:00 2001 From: WR Updater Bot Date: Sat, 22 Dec 2018 08:57:10 +0000 Subject: [PATCH] Bug 1516012 - Update webrender to commit ca4ee825c0fecc459c8741f05acbd90761667b75 (WR PR #3442). r=kats https://github.com/servo/webrender/pull/3442 Differential Revision: https://phabricator.services.mozilla.com/D15247 --HG-- extra : moz-landing-system : lando --- gfx/webrender_bindings/revision.txt | 2 +- gfx/wr/Cargo.lock | 1 + gfx/wr/webrender/Cargo.toml | 1 + gfx/wr/webrender/src/lib.rs | 2 + gfx/wr/webrender/src/platform/macos/font.rs | 5 +- gfx/wr/webrender/src/platform/unix/font.rs | 274 +++++++++++++++----- 6 files changed, 211 insertions(+), 74 deletions(-) diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index f71364c4eaeb..6744dbf688ac 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -6bdd0d26afe3fc5a24f10c19d4ca8569d0182a37 +ca4ee825c0fecc459c8741f05acbd90761667b75 diff --git a/gfx/wr/Cargo.lock b/gfx/wr/Cargo.lock index 905b404fb43b..35794460e9aa 100644 --- a/gfx/wr/Cargo.lock +++ b/gfx/wr/Cargo.lock @@ -1547,6 +1547,7 @@ dependencies = [ "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mozangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/gfx/wr/webrender/Cargo.toml b/gfx/wr/webrender/Cargo.toml index d386b90be071..7dae53dcc89d 100644 --- a/gfx/wr/webrender/Cargo.toml +++ b/gfx/wr/webrender/Cargo.toml @@ -72,6 +72,7 @@ rand = "0.4" [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] freetype = { version = "0.4", default-features = false } +libc = "0.2" [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.6.3" diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs index f4c0cce0fb7c..d5a6c2cc61c8 100644 --- a/gfx/wr/webrender/src/lib.rs +++ b/gfx/wr/webrender/src/lib.rs @@ -153,6 +153,8 @@ extern crate core_text; #[cfg(all(unix, not(target_os = "macos")))] extern crate freetype; +#[cfg(all(unix, not(target_os = "macos")))] +extern crate libc; #[cfg(target_os = "windows")] extern crate dwrote; diff --git a/gfx/wr/webrender/src/platform/macos/font.rs b/gfx/wr/webrender/src/platform/macos/font.rs index 0543efba8ea4..770c94c851d5 100644 --- a/gfx/wr/webrender/src/platform/macos/font.rs +++ b/gfx/wr/webrender/src/platform/macos/font.rs @@ -339,10 +339,7 @@ impl FontContext { match self.ct_fonts.entry((font_key, size, variations.to_vec())) { Entry::Occupied(entry) => Some((*entry.get()).clone()), Entry::Vacant(entry) => { - let cg_font = match self.cg_fonts.get(&font_key) { - None => return None, - Some(cg_font) => cg_font, - }; + let cg_font = self.cg_fonts.get(&font_key)?; let ct_font = new_ct_font_with_variations(cg_font, size.to_f64_px(), variations); entry.insert(ct_font.clone()); Some(ct_font) diff --git a/gfx/wr/webrender/src/platform/unix/font.rs b/gfx/wr/webrender/src/platform/unix/font.rs index 6d1e98c60842..bec591256f8c 100644 --- a/gfx/wr/webrender/src/platform/unix/font.rs +++ b/gfx/wr/webrender/src/platform/unix/font.rs @@ -4,7 +4,7 @@ use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode}; use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting}; -use api::{FontInstanceFlags, NativeFontHandle}; +use api::{FontInstanceFlags, FontVariation, NativeFontHandle}; use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode}; use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32}; use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos}; @@ -12,20 +12,26 @@ use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt} use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face}; use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph}; use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size}; -use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform}; +use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong}; +use freetype::freetype::{FT_Err_Unimplemented_Feature}; use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT}; use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT}; use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT}; use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES}; +use freetype::freetype::{FT_FACE_FLAG_MULTIPLE_MASTERS}; use freetype::succeeded; use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterResult, RasterizedGlyph}; #[cfg(feature = "pathfinder")] use glyph_rasterizer::NativeFontHandleWrapper; use internal_types::{FastHashMap, ResourceCacheError}; +#[cfg(not(target_os = "android"))] +use libc::{dlsym, RTLD_DEFAULT}; +use libc::free; #[cfg(feature = "pathfinder")] use pathfinder_font_renderer::freetype as pf_freetype; use std::{cmp, mem, ptr, slice}; use std::cmp::max; +use std::collections::hash_map::Entry; use std::ffi::CString; use std::sync::Arc; @@ -38,16 +44,141 @@ const FT_LOAD_TARGET_MONO: FT_UInt = 2 << 16; const FT_LOAD_TARGET_LCD: FT_UInt = 3 << 16; const FT_LOAD_TARGET_LCD_V: FT_UInt = 4 << 16; -struct Face { - face: FT_Face, +#[repr(C)] +struct FT_Var_Axis { + pub name: *mut FT_String, + pub minimum: FT_Fixed, + pub def: FT_Fixed, + pub maximum: FT_Fixed, + pub tag: FT_ULong, + pub strid: FT_UInt, +} + +#[repr(C)] +struct FT_Var_Named_Style { + pub coords: *mut FT_Fixed, + pub strid: FT_UInt, + pub psid: FT_UInt, +} + +#[repr(C)] +struct FT_MM_Var { + pub num_axis: FT_UInt, + pub num_designs: FT_UInt, + pub num_namedstyles: FT_UInt, + pub axis: *mut FT_Var_Axis, + pub namedstyle: *mut FT_Var_Named_Style, +} + +#[inline] +pub fn unimplemented(error: FT_Error) -> bool { + error == FT_Err_Unimplemented_Feature as FT_Error +} + +// Use dlsym to check for symbols. If not available. just return an unimplemented error. +#[cfg(not(target_os = "android"))] +macro_rules! ft_dyn_fn { + ($func_name:ident($($arg_name:ident:$arg_type:ty),*) -> FT_Error) => { + #[allow(non_snake_case)] + unsafe fn $func_name($($arg_name:$arg_type),*) -> FT_Error { + extern "C" fn unimpl_func($(_:$arg_type),*) -> FT_Error { + FT_Err_Unimplemented_Feature as FT_Error + } + lazy_static! { + static ref func: unsafe extern "C" fn($($arg_type),*) -> FT_Error = { + unsafe { + let cname = CString::new(stringify!($func_name)).unwrap(); + let ptr = dlsym(RTLD_DEFAULT, cname.as_ptr()); + if !ptr.is_null() { mem::transmute(ptr) } else { unimpl_func } + } + }; + } + (*func)($($arg_name),*) + } + } +} + +// On Android, just statically link in the symbols... +#[cfg(target_os = "android")] +macro_rules! ft_dyn_fn { + ($($proto:tt)+) => { extern "C" { fn $($proto)+; } } +} + +ft_dyn_fn!(FT_Get_MM_Var(face: FT_Face, desc: *mut *mut FT_MM_Var) -> FT_Error); +ft_dyn_fn!(FT_Done_MM_Var(library: FT_Library, desc: *mut FT_MM_Var) -> FT_Error); +ft_dyn_fn!(FT_Set_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error); + +extern "C" { + fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot); +} + +enum FontFile { + Pathname(CString), + Data(Arc>), +} + +struct FontFace { // Raw byte data has to live until the font is deleted, according to // https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_New_Memory_Face - _bytes: Option>>, + file: FontFile, + index: u32, + face: FT_Face, + mm_var: *mut FT_MM_Var, +} + +impl Drop for FontFace { + fn drop(&mut self) { + unsafe { + if !self.mm_var.is_null() && + unimplemented(FT_Done_MM_Var((*(*self.face).glyph).library, self.mm_var)) { + free(self.mm_var as _); + } + + FT_Done_Face(self.face); + } + } +} + +struct VariationFace(FT_Face); + +impl Drop for VariationFace { + fn drop(&mut self) { + unsafe { FT_Done_Face(self.0) }; + } +} + +fn new_ft_face(font_key: &FontKey, lib: FT_Library, file: &FontFile, index: u32) -> Option { + unsafe { + let mut face: FT_Face = ptr::null_mut(); + let result = match file { + FontFile::Pathname(ref cstr) => FT_New_Face( + lib, + cstr.as_ptr(), + index as FT_Long, + &mut face, + ), + FontFile::Data(ref bytes) => FT_New_Memory_Face( + lib, + bytes.as_ptr(), + bytes.len() as FT_Long, + index as FT_Long, + &mut face, + ), + }; + if succeeded(result) && !face.is_null() { + Some(face) + } else { + warn!("WARN: webrender failed to load font"); + debug!("font={:?}, result={:?}", font_key, result); + None + } + } } pub struct FontContext { lib: FT_Library, - faces: FastHashMap, + faces: FastHashMap, + variations: FastHashMap<(FontKey, Vec), VariationFace>, lcd_extra_pixels: i64, } @@ -56,10 +187,6 @@ pub struct FontContext { // a given FontContext so it is safe to move the latter between threads. unsafe impl Send for FontContext {} -extern "C" { - fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot); -} - fn get_skew_bounds(bottom: i32, top: i32, skew_factor: f32) -> (f32, f32) { let skew_min = ((bottom as f32 + 0.5) * skew_factor).floor(); let skew_max = ((top as f32 - 0.5) * skew_factor).ceil(); @@ -165,6 +292,7 @@ impl FontContext { Ok(FontContext { lib, faces: FastHashMap::default(), + variations: FastHashMap::default(), lcd_extra_pixels, }) } else { @@ -181,72 +309,78 @@ impl FontContext { pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc>, index: u32) { if !self.faces.contains_key(&font_key) { - let mut face: FT_Face = ptr::null_mut(); - let result = unsafe { - FT_New_Memory_Face( - self.lib, - bytes.as_ptr(), - bytes.len() as FT_Long, - index as FT_Long, - &mut face, - ) - }; - if succeeded(result) && !face.is_null() { - self.faces.insert( - *font_key, - Face { - face, - _bytes: Some(bytes), - }, - ); - } else { - warn!("WARN: webrender failed to load font"); - debug!("font={:?}", font_key); + let file = FontFile::Data(bytes); + if let Some(face) = new_ft_face(font_key, self.lib, &file, index) { + self.faces.insert(*font_key, FontFace { file, index, face, mm_var: ptr::null_mut() }); } } } pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle) { if !self.faces.contains_key(&font_key) { - let mut face: FT_Face = ptr::null_mut(); - let pathname = CString::new(native_font_handle.pathname).unwrap(); - let result = unsafe { - FT_New_Face( - self.lib, - pathname.as_ptr(), - native_font_handle.index as FT_Long, - &mut face, - ) - }; - if succeeded(result) && !face.is_null() { - self.faces.insert( - *font_key, - Face { - face, - _bytes: None, - }, - ); - } else { - warn!("WARN: webrender failed to load font"); - debug!("font={:?}, path={:?}", font_key, pathname); + let file = FontFile::Pathname(CString::new(native_font_handle.pathname).unwrap()); + let index = native_font_handle.index; + if let Some(face) = new_ft_face(font_key, self.lib, &file, index) { + self.faces.insert(*font_key, FontFace { file, index, face, mm_var: ptr::null_mut() }); } } } pub fn delete_font(&mut self, font_key: &FontKey) { - if let Some(face) = self.faces.remove(font_key) { - let result = unsafe { FT_Done_Face(face.face) }; - assert!(succeeded(result)); + if let Some(_) = self.faces.remove(font_key) { + self.variations.retain(|k, _| k.0 != *font_key); } } - pub fn delete_font_instance(&mut self, _instance: &FontInstance) { - // This backend does not yet support variations, so there is nothing to do here. + pub fn delete_font_instance(&mut self, instance: &FontInstance) { + // Ensure we don't keep around excessive amounts of stale variations. + if !instance.variations.is_empty() { + self.variations.remove(&(instance.font_key, instance.variations.clone())); + } } - fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<(FT_GlyphSlot, f32)> { - debug_assert!(self.faces.contains_key(&font.font_key)); - let face = self.faces.get(&font.font_key).unwrap(); + fn get_ft_face(&mut self, font: &FontInstance) -> Option { + if font.variations.is_empty() { + return Some(self.faces.get(&font.font_key)?.face); + } + match self.variations.entry((font.font_key, font.variations.clone())) { + Entry::Occupied(entry) => Some(entry.get().0), + Entry::Vacant(entry) => unsafe { + let normal_face = self.faces.get_mut(&font.font_key)?; + if ((*normal_face.face).face_flags & (FT_FACE_FLAG_MULTIPLE_MASTERS as FT_Long)) == 0 { + return Some(normal_face.face); + } + // Clone a new FT face and attempt to set the variation values on it. + // Leave unspecified values at the defaults. + let var_face = new_ft_face(&font.font_key, self.lib, &normal_face.file, normal_face.index)?; + if !normal_face.mm_var.is_null() || + succeeded(FT_Get_MM_Var(normal_face.face, &mut normal_face.mm_var)) { + let mm_var = normal_face.mm_var; + let num_axis = (*mm_var).num_axis; + let mut coords: Vec = Vec::with_capacity(num_axis as usize); + for i in 0 .. num_axis { + let axis = (*mm_var).axis.offset(i as isize); + let mut value = (*axis).def; + for var in &font.variations { + if var.tag as FT_ULong == (*axis).tag { + value = (var.value * 65536.0 + 0.5) as FT_Fixed; + value = cmp::min(value, (*axis).maximum); + value = cmp::max(value, (*axis).minimum); + break; + } + } + coords.push(value); + } + FT_Set_Var_Design_Coordinates(var_face, num_axis, coords.as_mut_ptr()); + } + entry.insert(VariationFace(var_face)); + Some(var_face) + } + } + } + + fn load_glyph(&mut self, font: &FontInstance, glyph: &GlyphKey) -> Option<(FT_GlyphSlot, f32)> { + let face = self.get_ft_face(font)?; let mut load_flags = FT_LOAD_DEFAULT; let FontInstancePlatformOptions { mut hinting, .. } = font.platform_options.unwrap_or_default(); @@ -293,12 +427,12 @@ impl FontContext { let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let scale = font.oversized_scale_factor(x_scale, y_scale); let req_size = font.size.to_f64_px(); - let face_flags = unsafe { (*face.face).face_flags }; + let face_flags = unsafe { (*face).face_flags }; let mut result = if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 && (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 && (load_flags & FT_LOAD_NO_BITMAP) == 0 { - unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) }; - self.choose_bitmap_size(face.face, req_size * y_scale / scale) + unsafe { FT_Set_Transform(face, ptr::null_mut(), ptr::null_mut()) }; + self.choose_bitmap_size(face, req_size * y_scale / scale) } else { let mut shape = font.transform.invert_scale(x_scale, y_scale); if font.flags.contains(FontInstanceFlags::FLIP_X) { @@ -320,9 +454,9 @@ impl FontContext { yy: (shape.scale_y * 65536.0) as FT_Fixed, }; unsafe { - FT_Set_Transform(face.face, &mut ft_shape, ptr::null_mut()); + FT_Set_Transform(face, &mut ft_shape, ptr::null_mut()); FT_Set_Char_Size( - face.face, + face, (req_size * x_scale / scale * 64.0 + 0.5) as FT_F26Dot6, (req_size * y_scale / scale * 64.0 + 0.5) as FT_F26Dot6, 0, @@ -332,11 +466,11 @@ impl FontContext { }; if succeeded(result) { - result = unsafe { FT_Load_Glyph(face.face, glyph.index() as FT_UInt, load_flags as FT_Int32) }; + result = unsafe { FT_Load_Glyph(face, glyph.index() as FT_UInt, load_flags as FT_Int32) }; }; if succeeded(result) { - let slot = unsafe { (*face.face).glyph }; + let slot = unsafe { (*face).glyph }; assert!(slot != ptr::null_mut()); if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) { @@ -504,9 +638,9 @@ impl FontContext { } pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option { - let face = self.faces.get(&font_key).expect("Unknown font key!"); + let face = self.faces.get(&font_key)?.face; unsafe { - let idx = FT_Get_Char_Index(face.face, ch as _); + let idx = FT_Get_Char_Index(face, ch as _); if idx != 0 { Some(idx) } else { @@ -823,6 +957,8 @@ impl FontContext { impl Drop for FontContext { fn drop(&mut self) { + self.variations.clear(); + self.faces.clear(); unsafe { FT_Done_FreeType(self.lib); }