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