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, FontKey, FontRenderMode, FontSize, GlyphDimensions};
6 use api::{FontInstanceFlags, FontVariation, NativeFontHandle};
7 use core_foundation::array::{CFArray, CFArrayRef};
8 use core_foundation::base::TCFType;
9 use core_foundation::dictionary::CFDictionary;
10 use core_foundation::number::{CFNumber, CFNumberRef};
11 use core_foundation::string::{CFString, CFStringRef};
12 use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
13 use core_graphics::base::{kCGBitmapByteOrder32Little};
14 use core_graphics::color_space::CGColorSpace;
15 use core_graphics::context::CGContext;
16 use core_graphics::context::{CGBlendMode, CGTextDrawingMode};
17 use core_graphics::data_provider::CGDataProvider;
18 use core_graphics::font::{CGFont, CGGlyph};
19 use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize};
20 use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect};
21 use core_text;
22 use core_text::font::{CTFont, CTFontRef};
23 use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
24 use euclid::default::Size2D;
25 use crate::gamma_lut::{ColorLut, GammaLut};
26 use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
27 use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
28 use crate::internal_types::{FastHashMap, ResourceCacheError};
29 use std::collections::hash_map::Entry;
30 use std::sync::Arc;
31 
32 const INITIAL_CG_CONTEXT_SIDE_LENGTH: u32 = 32;
33 
34 pub struct FontContext {
35     cg_fonts: FastHashMap<FontKey, CGFont>,
36     ct_fonts: FastHashMap<(FontKey, FontSize, Vec<FontVariation>), CTFont>,
37     #[allow(dead_code)]
38     graphics_context: GraphicsContext,
39     #[allow(dead_code)]
40     gamma_lut: GammaLut,
41 }
42 
43 // core text is safe to use on multiple threads and non-shareable resources are
44 // all hidden inside their font context.
45 unsafe impl Send for FontContext {}
46 
47 struct GlyphMetrics {
48     rasterized_left: i32,
49     #[allow(dead_code)]
50     rasterized_descent: i32,
51     rasterized_ascent: i32,
52     rasterized_width: i32,
53     rasterized_height: i32,
54     advance: f32,
55 }
56 
57 // According to the Skia source code, there's no public API to
58 // determine if subpixel AA is supported. So jrmuizel ported
59 // this function from Skia which is used to check if a glyph
60 // can be rendered with subpixel AA.
supports_subpixel_aa() -> bool61 fn supports_subpixel_aa() -> bool {
62     let mut cg_context = CGContext::create_bitmap_context(
63         None,
64         1,
65         1,
66         8,
67         4,
68         &CGColorSpace::create_device_rgb(),
69         kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
70     );
71     let ct_font = core_text::font::new_from_name("Helvetica", 16.).unwrap();
72     cg_context.set_should_smooth_fonts(true);
73     cg_context.set_should_antialias(true);
74     cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
75     let point = CGPoint { x: -1., y: 0. };
76     let glyph = '|' as CGGlyph;
77     ct_font.draw_glyphs(&[glyph], &[point], cg_context.clone());
78     let data = cg_context.data();
79     data[0] != data[1] || data[1] != data[2]
80 }
81 
should_use_white_on_black(color: ColorU) -> bool82 fn should_use_white_on_black(color: ColorU) -> bool {
83     let (r, g, b) = (color.r as u32, color.g as u32, color.b as u32);
84     // These thresholds were determined on 10.12 by observing what CG does.
85     r >= 85 && g >= 85 && b >= 85 && r + g + b >= 2 * 255
86 }
87 
get_glyph_metrics( ct_font: &CTFont, transform: Option<&CGAffineTransform>, glyph: CGGlyph, x_offset: f64, y_offset: f64, extra_width: f64, ) -> GlyphMetrics88 fn get_glyph_metrics(
89     ct_font: &CTFont,
90     transform: Option<&CGAffineTransform>,
91     glyph: CGGlyph,
92     x_offset: f64,
93     y_offset: f64,
94     extra_width: f64,
95 ) -> GlyphMetrics {
96     let mut bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);
97 
98     if bounds.origin.x.is_nan() || bounds.origin.y.is_nan() || bounds.size.width.is_nan() ||
99         bounds.size.height.is_nan()
100     {
101         // If an unexpected glyph index is requested, core text will return NaN values
102         // which causes us to do bad thing as the value is cast into an integer and
103         // overflow when expanding the bounds a few lines below.
104         // Instead we are better off returning zero-sized metrics because this special
105         // case is handled by the callers of this method.
106         return GlyphMetrics {
107             rasterized_left: 0,
108             rasterized_width: 0,
109             rasterized_height: 0,
110             rasterized_ascent: 0,
111             rasterized_descent: 0,
112             advance: 0.0,
113         };
114     }
115 
116     let mut advance = CGSize { width: 0.0, height: 0.0 };
117     unsafe {
118         ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1);
119     }
120 
121     if bounds.size.width > 0.0 {
122         bounds.size.width += extra_width;
123     }
124     if advance.width > 0.0 {
125         advance.width += extra_width;
126     }
127 
128     if let Some(transform) = transform {
129         bounds = bounds.apply_transform(transform);
130     }
131 
132     // First round out to pixel boundaries
133     // CG Origin is bottom left
134     let mut left = bounds.origin.x.floor() as i32;
135     let mut bottom = bounds.origin.y.floor() as i32;
136     let mut right = (bounds.origin.x + bounds.size.width + x_offset).ceil() as i32;
137     let mut top = (bounds.origin.y + bounds.size.height + y_offset).ceil() as i32;
138 
139     // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
140     // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
141     // is not currently known, as CG dilates the outlines by some percentage.
142     // This is taken from Skia.
143     left -= 1;
144     bottom -= 1;
145     right += 1;
146     top += 1;
147 
148     let width = right - left;
149     let height = top - bottom;
150 
151     GlyphMetrics {
152         rasterized_left: left,
153         rasterized_width: width,
154         rasterized_height: height,
155         rasterized_ascent: top,
156         rasterized_descent: -bottom,
157         advance: advance.width as f32,
158     }
159 }
160 
161 #[link(name = "ApplicationServices", kind = "framework")]
162 extern {
163     static kCTFontVariationAxisIdentifierKey: CFStringRef;
164     static kCTFontVariationAxisNameKey: CFStringRef;
165     static kCTFontVariationAxisMinimumValueKey: CFStringRef;
166     static kCTFontVariationAxisMaximumValueKey: CFStringRef;
167     static kCTFontVariationAxisDefaultValueKey: CFStringRef;
168 
CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef169     fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef;
170 }
171 
new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVariation]) -> CTFont172 fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVariation]) -> CTFont {
173     unsafe {
174         let ct_font = core_text::font::new_from_CGFont(cg_font, size);
175         if variations.is_empty() {
176             return ct_font;
177         }
178         let axes_ref = CTFontCopyVariationAxes(ct_font.as_concrete_TypeRef());
179         if axes_ref.is_null() {
180             return ct_font;
181         }
182         let axes: CFArray<CFDictionary> = TCFType::wrap_under_create_rule(axes_ref);
183         let mut vals: Vec<(CFString, CFNumber)> = Vec::with_capacity(variations.len() as usize);
184         for axis in axes.iter() {
185             if !axis.instance_of::<CFDictionary>() {
186                 return ct_font;
187             }
188             let tag_val = match axis.find(kCTFontVariationAxisIdentifierKey as *const _) {
189                 Some(tag_ptr) => {
190                     let tag: CFNumber = TCFType::wrap_under_get_rule(*tag_ptr as CFNumberRef);
191                     if !tag.instance_of::<CFNumber>() {
192                         return ct_font;
193                     }
194                     match tag.to_i64() {
195                         Some(val) => val,
196                         None => return ct_font,
197                     }
198                 }
199                 None => return ct_font,
200             };
201             let mut val = match variations.iter().find(|variation| (variation.tag as i64) == tag_val) {
202                 Some(variation) => variation.value as f64,
203                 None => continue,
204             };
205 
206             let name: CFString = match axis.find(kCTFontVariationAxisNameKey as *const _) {
207                 Some(name_ptr) => TCFType::wrap_under_get_rule(*name_ptr as CFStringRef),
208                 None => return ct_font,
209             };
210             if !name.instance_of::<CFString>() {
211                 return ct_font;
212             }
213 
214             let min_val = match axis.find(kCTFontVariationAxisMinimumValueKey as *const _) {
215                 Some(min_ptr) => {
216                     let min: CFNumber = TCFType::wrap_under_get_rule(*min_ptr as CFNumberRef);
217                     if !min.instance_of::<CFNumber>() {
218                         return ct_font;
219                     }
220                     match min.to_f64() {
221                         Some(val) => val,
222                         None => return ct_font,
223                     }
224                 }
225                 None => return ct_font,
226             };
227             let max_val = match axis.find(kCTFontVariationAxisMaximumValueKey as *const _) {
228                 Some(max_ptr) => {
229                     let max: CFNumber = TCFType::wrap_under_get_rule(*max_ptr as CFNumberRef);
230                     if !max.instance_of::<CFNumber>() {
231                         return ct_font;
232                     }
233                     match max.to_f64() {
234                         Some(val) => val,
235                         None => return ct_font,
236                     }
237                 }
238                 None => return ct_font,
239             };
240             let def_val = match axis.find(kCTFontVariationAxisDefaultValueKey as *const _) {
241                 Some(def_ptr) => {
242                     let def: CFNumber = TCFType::wrap_under_get_rule(*def_ptr as CFNumberRef);
243                     if !def.instance_of::<CFNumber>() {
244                         return ct_font;
245                     }
246                     match def.to_f64() {
247                         Some(val) => val,
248                         None => return ct_font,
249                     }
250                 }
251                 None => return ct_font,
252             };
253 
254             val = val.max(min_val).min(max_val);
255             if val != def_val {
256                 vals.push((name, CFNumber::from(val)));
257             }
258         }
259         if vals.is_empty() {
260             return ct_font;
261         }
262         let vals_dict = CFDictionary::from_CFType_pairs(&vals);
263         let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap();
264         core_text::font::new_from_CGFont_with_variations(&cg_var_font, size, &vals_dict)
265     }
266 }
267 
is_bitmap_font(ct_font: &CTFont) -> bool268 fn is_bitmap_font(ct_font: &CTFont) -> bool {
269     let traits = ct_font.symbolic_traits();
270     (traits & kCTFontColorGlyphsTrait) != 0
271 }
272 
273 impl FontContext {
new() -> Result<FontContext, ResourceCacheError>274     pub fn new() -> Result<FontContext, ResourceCacheError> {
275         debug!("Test for subpixel AA support: {}", supports_subpixel_aa());
276 
277         // Force CG to use sRGB color space to gamma correct.
278         let contrast = 0.0;
279         let gamma = 0.0;
280 
281         Ok(FontContext {
282             cg_fonts: FastHashMap::default(),
283             ct_fonts: FastHashMap::default(),
284             graphics_context: GraphicsContext::new(),
285             gamma_lut: GammaLut::new(contrast, gamma, gamma),
286         })
287     }
288 
has_font(&self, font_key: &FontKey) -> bool289     pub fn has_font(&self, font_key: &FontKey) -> bool {
290         self.cg_fonts.contains_key(font_key)
291     }
292 
add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32)293     pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) {
294         if self.cg_fonts.contains_key(font_key) {
295             return;
296         }
297 
298         assert_eq!(index, 0);
299         let data_provider = CGDataProvider::from_buffer(bytes);
300         let cg_font = match CGFont::from_data_provider(data_provider) {
301             Err(_) => return,
302             Ok(cg_font) => cg_font,
303         };
304         self.cg_fonts.insert(*font_key, cg_font);
305     }
306 
add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle)307     pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle) {
308         if self.cg_fonts.contains_key(font_key) {
309             return;
310         }
311 
312         self.cg_fonts
313             .insert(*font_key, native_font_handle.0);
314     }
315 
delete_font(&mut self, font_key: &FontKey)316     pub fn delete_font(&mut self, font_key: &FontKey) {
317         if let Some(_) = self.cg_fonts.remove(font_key) {
318             self.ct_fonts.retain(|k, _| k.0 != *font_key);
319         }
320     }
321 
delete_font_instance(&mut self, instance: &FontInstance)322     pub fn delete_font_instance(&mut self, instance: &FontInstance) {
323         // Remove the CoreText font corresponding to this instance.
324         let size = FontSize::from_f64_px(instance.get_transformed_size());
325         self.ct_fonts.remove(&(instance.font_key, size, instance.variations.clone()));
326     }
327 
get_ct_font( &mut self, font_key: FontKey, size: f64, variations: &[FontVariation], ) -> Option<CTFont>328     fn get_ct_font(
329         &mut self,
330         font_key: FontKey,
331         size: f64,
332         variations: &[FontVariation],
333     ) -> Option<CTFont> {
334         match self.ct_fonts.entry((font_key, FontSize::from_f64_px(size), variations.to_vec())) {
335             Entry::Occupied(entry) => Some((*entry.get()).clone()),
336             Entry::Vacant(entry) => {
337                 let cg_font = self.cg_fonts.get(&font_key)?;
338                 let ct_font = new_ct_font_with_variations(cg_font, size, variations);
339                 entry.insert(ct_font.clone());
340                 Some(ct_font)
341             }
342         }
343     }
344 
get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32>345     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
346         let character = ch as u16;
347         let mut glyph = 0;
348 
349         self.get_ct_font(font_key, 16.0, &[])
350             .and_then(|ref ct_font| {
351                 unsafe {
352                     let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
353 
354                     if result {
355                         Some(glyph as u32)
356                     } else {
357                         None
358                     }
359                 }
360             })
361     }
362 
get_glyph_dimensions( &mut self, font: &FontInstance, key: &GlyphKey, ) -> Option<GlyphDimensions>363     pub fn get_glyph_dimensions(
364         &mut self,
365         font: &FontInstance,
366         key: &GlyphKey,
367     ) -> Option<GlyphDimensions> {
368         let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
369         let size = font.size.to_f64_px() * y_scale;
370         self.get_ct_font(font.font_key, size, &font.variations)
371             .and_then(|ref ct_font| {
372                 let glyph = key.index() as CGGlyph;
373                 let bitmap = is_bitmap_font(ct_font);
374                 let (mut shape, (x_offset, y_offset)) = if bitmap {
375                     (FontTransform::identity(), (0.0, 0.0))
376                 } else {
377                     (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
378                 };
379                 if font.flags.contains(FontInstanceFlags::FLIP_X) {
380                     shape = shape.flip_x();
381                 }
382                 if font.flags.contains(FontInstanceFlags::FLIP_Y) {
383                     shape = shape.flip_y();
384                 }
385                 if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
386                     shape = shape.swap_xy();
387                 }
388                 let (mut tx, mut ty) = (0.0, 0.0);
389                 if font.synthetic_italics.is_enabled() {
390                     let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, size);
391                     shape = shape_;
392                     tx = tx_;
393                     ty = ty_;
394                 }
395                 let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) {
396                     Some(CGAffineTransform {
397                         a: shape.scale_x as f64,
398                         b: -shape.skew_y as f64,
399                         c: -shape.skew_x as f64,
400                         d: shape.scale_y as f64,
401                         tx: tx,
402                         ty: -ty,
403                     })
404                 } else {
405                     None
406                 };
407                 let (strike_scale, pixel_step) = if bitmap {
408                     (y_scale, 1.0)
409                 } else {
410                     (x_scale, y_scale / x_scale)
411                 };
412                 let extra_strikes = font.get_extra_strikes(strike_scale);
413                 let metrics = get_glyph_metrics(
414                     ct_font,
415                     transform.as_ref(),
416                     glyph,
417                     x_offset,
418                     y_offset,
419                     extra_strikes as f64 * pixel_step,
420                 );
421                 if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
422                     None
423                 } else {
424                     Some(GlyphDimensions {
425                         left: metrics.rasterized_left,
426                         top: metrics.rasterized_ascent,
427                         width: metrics.rasterized_width,
428                         height: metrics.rasterized_height,
429                         advance: metrics.advance,
430                     })
431                 }
432             })
433     }
434 
435     // Assumes the pixels here are linear values from CG
gamma_correct_pixels( &self, pixels: &mut Vec<u8>, render_mode: FontRenderMode, color: ColorU, )436     fn gamma_correct_pixels(
437         &self,
438         pixels: &mut Vec<u8>,
439         render_mode: FontRenderMode,
440         color: ColorU,
441     ) {
442         // Then convert back to gamma corrected values.
443         match render_mode {
444             FontRenderMode::Alpha => {
445                 self.gamma_lut.preblend_grayscale(pixels, color);
446             }
447             FontRenderMode::Subpixel => {
448                 self.gamma_lut.preblend(pixels, color);
449             }
450             _ => {} // Again, give mono untouched since only the alpha matters.
451         }
452     }
453 
454     #[allow(dead_code)]
print_glyph_data(&mut self, data: &[u8], width: usize, height: usize)455     fn print_glyph_data(&mut self, data: &[u8], width: usize, height: usize) {
456         // Rust doesn't have step_by support on stable :(
457         println!("Width is: {:?} height: {:?}", width, height);
458         for i in 0 .. height {
459             let current_height = i * width * 4;
460 
461             for pixel in data[current_height .. current_height + (width * 4)].chunks(4) {
462                 let b = pixel[0];
463                 let g = pixel[1];
464                 let r = pixel[2];
465                 let a = pixel[3];
466                 print!("({}, {}, {}, {}) ", r, g, b, a);
467             }
468             println!();
469         }
470     }
471 
prepare_font(font: &mut FontInstance)472     pub fn prepare_font(font: &mut FontInstance) {
473         match font.render_mode {
474             FontRenderMode::Mono => {
475                 // In mono mode the color of the font is irrelevant.
476                 font.color = ColorU::new(255, 255, 255, 255);
477                 // Subpixel positioning is disabled in mono mode.
478                 font.disable_subpixel_position();
479             }
480             FontRenderMode::Alpha => {
481                 font.color = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) {
482                     // Only the G channel is used to index grayscale tables,
483                     // so use R and B to preserve light/dark determination.
484                     let ColorU { g, a, .. } = font.color.luminance_color().quantized_ceil();
485                     let rb = if should_use_white_on_black(font.color) { 255 } else { 0 };
486                     ColorU::new(rb, g, rb, a)
487                 } else {
488                     ColorU::new(255, 255, 255, 255)
489                 };
490             }
491             FontRenderMode::Subpixel => {
492                 // Quantization may change the light/dark determination, so quantize in the
493                 // direction necessary to respect the threshold.
494                 font.color = if should_use_white_on_black(font.color) {
495                     font.color.quantized_ceil()
496                 } else {
497                     font.color.quantized_floor()
498                 };
499             }
500         }
501     }
502 
rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult503     pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
504         let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
505         let size = font.size.to_f64_px() * y_scale;
506         let ct_font = self.get_ct_font(font.font_key, size, &font.variations).ok_or(GlyphRasterError::LoadFailed)?;
507         let glyph_type = if is_bitmap_font(&ct_font) {
508             GlyphType::Bitmap
509         } else {
510             GlyphType::Vector
511         };
512 
513         let (mut shape, (x_offset, y_offset)) = match glyph_type {
514             GlyphType::Bitmap => (FontTransform::identity(), (0.0, 0.0)),
515             GlyphType::Vector => {
516                 (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
517             }
518         };
519         if font.flags.contains(FontInstanceFlags::FLIP_X) {
520             shape = shape.flip_x();
521         }
522         if font.flags.contains(FontInstanceFlags::FLIP_Y) {
523             shape = shape.flip_y();
524         }
525         if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
526             shape = shape.swap_xy();
527         }
528         let (mut tx, mut ty) = (0.0, 0.0);
529         if font.synthetic_italics.is_enabled() {
530             let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, size);
531             shape = shape_;
532             tx = tx_;
533             ty = ty_;
534         }
535         let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) {
536             Some(CGAffineTransform {
537                 a: shape.scale_x as f64,
538                 b: -shape.skew_y as f64,
539                 c: -shape.skew_x as f64,
540                 d: shape.scale_y as f64,
541                 tx: tx,
542                 ty: -ty,
543             })
544         } else {
545             None
546         };
547 
548         let glyph = key.index() as CGGlyph;
549         let (strike_scale, pixel_step) = if glyph_type == GlyphType::Bitmap {
550             (y_scale, 1.0)
551         } else {
552             (x_scale, y_scale / x_scale)
553         };
554 
555         let extra_strikes = font.get_extra_strikes(strike_scale);
556         let metrics = get_glyph_metrics(
557             &ct_font,
558             transform.as_ref(),
559             glyph,
560             x_offset,
561             y_offset,
562             extra_strikes as f64 * pixel_step,
563         );
564         if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
565             return Err(GlyphRasterError::LoadFailed);
566         }
567 
568         let raster_size = Size2D::new(
569             metrics.rasterized_width as u32,
570             metrics.rasterized_height as u32
571         );
572 
573         // If the font render mode is Alpha, we support two different ways to
574         // compute the grayscale mask, depending on the value of the platform
575         // options' font_smoothing flag:
576         //  - Alpha + smoothing:
577         //    We will recover a grayscale mask from a subpixel rasterization, in
578         //    such a way that the result looks as close to subpixel text
579         //    blending as we can make it. This involves gamma correction,
580         //    luminance computations and preblending based on the text color,
581         //    just like with the Subpixel render mode.
582         //  - Alpha without smoothing:
583         //    We will ask CoreGraphics to rasterize the text with font_smoothing
584         //    off. This will cause it to use grayscale anti-aliasing with
585         //    comparatively thin text. This method of text rendering is not
586         //    gamma-aware.
587         //
588         // For subpixel rasterization, starting with macOS 10.11, CoreGraphics
589         // uses different glyph dilation based on the text color. Bright text
590         // uses less font dilation (looks thinner) than dark text.
591         // As a consequence, when we ask CG to rasterize with subpixel AA, we
592         // will render white-on-black text as opposed to black-on-white text if
593         // the text color brightness exceeds a certain threshold. This applies
594         // to both the Subpixel and the "Alpha + smoothing" modes, but not to
595         // the "Alpha without smoothing" and Mono modes.
596         let use_white_on_black = should_use_white_on_black(font.color);
597         let use_font_smoothing = font.flags.contains(FontInstanceFlags::FONT_SMOOTHING);
598         let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match glyph_type {
599             GlyphType::Bitmap => (true, false, 0.0, 0.0, 0.0, false),
600             GlyphType::Vector => {
601                 match (font.render_mode, use_font_smoothing) {
602                     (FontRenderMode::Subpixel, _) |
603                     (FontRenderMode::Alpha, true) => if use_white_on_black {
604                         (true, true, 1.0, 0.0, 1.0, false)
605                     } else {
606                         (true, true, 0.0, 1.0, 1.0, true)
607                     },
608                     (FontRenderMode::Alpha, false) => (true, false, 0.0, 1.0, 1.0, true),
609                     (FontRenderMode::Mono, _) => (false, false, 0.0, 1.0, 1.0, true),
610                 }
611             }
612         };
613 
614         {
615             let cg_context = self.graphics_context.get_context(&raster_size, glyph_type);
616 
617             // These are always true in Gecko, even for non-AA fonts
618             cg_context.set_allows_font_subpixel_positioning(true);
619             cg_context.set_should_subpixel_position_fonts(true);
620 
621             // Don't quantize because we're doing it already.
622             cg_context.set_allows_font_subpixel_quantization(false);
623             cg_context.set_should_subpixel_quantize_fonts(false);
624 
625             cg_context.set_should_smooth_fonts(smooth);
626             cg_context.set_should_antialias(antialias);
627 
628             // Fill the background. This could be opaque white, opaque black, or
629             // transparency.
630             cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
631             let rect = CGRect {
632                 origin: CGPoint { x: 0.0, y: 0.0 },
633                 size: CGSize {
634                     width: metrics.rasterized_width as f64,
635                     height: metrics.rasterized_height as f64,
636                 },
637             };
638 
639             // Make sure we use the Copy blend mode, or else we'll get the Porter-Duff OVER
640             // operator, which can't clear to the transparent color!
641             cg_context.set_blend_mode(CGBlendMode::Copy);
642             cg_context.fill_rect(rect);
643             cg_context.set_blend_mode(CGBlendMode::Normal);
644 
645             // Set the text color and draw the glyphs.
646             cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
647             cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
648 
649             // CG Origin is bottom left, WR is top left. Need -y offset
650             let mut draw_origin = CGPoint {
651                 x: -metrics.rasterized_left as f64 + x_offset + tx,
652                 y: metrics.rasterized_descent as f64 - y_offset - ty,
653             };
654 
655             if let Some(transform) = transform {
656                 cg_context.set_text_matrix(&transform);
657 
658                 draw_origin = draw_origin.apply_transform(&transform.invert());
659             } else {
660                 // Make sure to reset this because some previous glyph rasterization might have
661                 // changed it.
662                 cg_context.set_text_matrix(&CG_AFFINE_TRANSFORM_IDENTITY);
663             }
664 
665             ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone());
666 
667             // We'd like to render all the strikes in a single ct_font.draw_glyphs call,
668             // passing an array of glyph IDs and an array of origins, but unfortunately
669             // with some fonts, Core Text may inappropriately pixel-snap the rasterization,
670             // such that the strikes overprint instead of being offset. Rendering the
671             // strikes with individual draw_glyphs calls avoids this.
672             // (See https://bugzilla.mozilla.org/show_bug.cgi?id=1633397 for details.)
673             for i in 1 ..= extra_strikes {
674                 let origin = CGPoint {
675                     x: draw_origin.x + i as f64 * pixel_step,
676                     y: draw_origin.y,
677                 };
678                 ct_font.draw_glyphs(&[glyph], &[origin], cg_context.clone());
679             }
680         }
681 
682         let mut rasterized_pixels = self.graphics_context
683                                         .get_rasterized_pixels(&raster_size, glyph_type);
684 
685         if glyph_type == GlyphType::Vector {
686             // We rendered text into an opaque surface. The code below needs to
687             // ignore the current value of each pixel's alpha channel. But it's
688             // allowed to write to the alpha channel, because we're done calling
689             // CG functions now.
690 
691             if smooth {
692                 // Convert to linear space for subpixel AA.
693                 // We explicitly do not do this for grayscale AA ("Alpha without
694                 // smoothing" or Mono) because those rendering modes are not
695                 // gamma-aware in CoreGraphics.
696                 self.gamma_lut.coregraphics_convert_to_linear(
697                     &mut rasterized_pixels,
698                 );
699             }
700 
701             for pixel in rasterized_pixels.chunks_mut(4) {
702                 if invert {
703                     pixel[0] = 255 - pixel[0];
704                     pixel[1] = 255 - pixel[1];
705                     pixel[2] = 255 - pixel[2];
706                 }
707 
708                 // Set alpha to the value of the green channel. For grayscale
709                 // text, all three channels have the same value anyway.
710                 // For subpixel text, the mask's alpha only makes a difference
711                 // when computing the destination alpha on destination pixels
712                 // that are not completely opaque. Picking an alpha value
713                 // that's somehow based on the mask at least ensures that text
714                 // blending doesn't modify the destination alpha on pixels where
715                 // the mask is entirely zero.
716                 pixel[3] = pixel[1];
717             }
718 
719             if smooth {
720                 // Convert back from linear space into device space, and perform
721                 // some "preblending" based on the text color.
722                 // In Alpha + smoothing mode, this will also convert subpixel AA
723                 // into grayscale AA.
724                 self.gamma_correct_pixels(
725                     &mut rasterized_pixels,
726                     font.render_mode,
727                     font.color,
728                 );
729             }
730         }
731 
732         Ok(RasterizedGlyph {
733             left: metrics.rasterized_left as f32,
734             top: metrics.rasterized_ascent as f32,
735             width: metrics.rasterized_width,
736             height: metrics.rasterized_height,
737             scale: match glyph_type {
738                 GlyphType::Bitmap => y_scale.recip() as f32,
739                 GlyphType::Vector => 1.0,
740             },
741             format: match glyph_type {
742                 GlyphType::Bitmap => GlyphFormat::ColorBitmap,
743                 GlyphType::Vector => font.get_glyph_format(),
744             },
745             bytes: rasterized_pixels,
746         })
747     }
748 }
749 
750 // Avoids taking locks by recycling Core Graphics contexts.
751 #[allow(dead_code)]
752 struct GraphicsContext {
753     vector_context: CGContext,
754     vector_context_size: Size2D<u32>,
755     bitmap_context: CGContext,
756     bitmap_context_size: Size2D<u32>,
757 }
758 
759 impl GraphicsContext {
new() -> GraphicsContext760     fn new() -> GraphicsContext {
761         let size = Size2D::new(INITIAL_CG_CONTEXT_SIDE_LENGTH, INITIAL_CG_CONTEXT_SIDE_LENGTH);
762         GraphicsContext {
763             vector_context: GraphicsContext::create_cg_context(&size, GlyphType::Vector),
764             vector_context_size: size,
765             bitmap_context: GraphicsContext::create_cg_context(&size, GlyphType::Bitmap),
766             bitmap_context_size: size,
767         }
768     }
769 
770     #[allow(dead_code)]
get_context(&mut self, size: &Size2D<u32>, glyph_type: GlyphType) -> &mut CGContext771     fn get_context(&mut self, size: &Size2D<u32>, glyph_type: GlyphType)
772                    -> &mut CGContext {
773         let (cached_context, cached_size) = match glyph_type {
774             GlyphType::Vector => {
775                 (&mut self.vector_context, &mut self.vector_context_size)
776             }
777             GlyphType::Bitmap => {
778                 (&mut self.bitmap_context, &mut self.bitmap_context_size)
779             }
780         };
781         let rounded_size = Size2D::new(size.width.next_power_of_two(),
782                                        size.height.next_power_of_two());
783         if rounded_size.width > cached_size.width || rounded_size.height > cached_size.height {
784             *cached_size = Size2D::new(u32::max(cached_size.width, rounded_size.width),
785                                        u32::max(cached_size.height, rounded_size.height));
786             *cached_context = GraphicsContext::create_cg_context(cached_size, glyph_type);
787         }
788         cached_context
789     }
790 
791     #[allow(dead_code)]
get_rasterized_pixels(&mut self, size: &Size2D<u32>, glyph_type: GlyphType) -> Vec<u8>792     fn get_rasterized_pixels(&mut self, size: &Size2D<u32>, glyph_type: GlyphType)
793                              -> Vec<u8> {
794         let (cached_context, cached_size) = match glyph_type {
795             GlyphType::Vector => (&mut self.vector_context, &self.vector_context_size),
796             GlyphType::Bitmap => (&mut self.bitmap_context, &self.bitmap_context_size),
797         };
798         let cached_data = cached_context.data();
799         let cached_stride = cached_size.width as usize * 4;
800 
801         let result_len = size.width as usize * size.height as usize * 4;
802         let mut result = Vec::with_capacity(result_len);
803         for y in (cached_size.height - size.height)..cached_size.height {
804             let cached_start = y as usize * cached_stride;
805             let cached_end = cached_start + size.width as usize * 4;
806             result.extend_from_slice(&cached_data[cached_start..cached_end]);
807         }
808         debug_assert_eq!(result.len(), result_len);
809         result
810     }
811 
create_cg_context(size: &Size2D<u32>, glyph_type: GlyphType) -> CGContext812     fn create_cg_context(size: &Size2D<u32>, glyph_type: GlyphType) -> CGContext {
813         // The result of rasterization, in all render modes, is going to be a
814         // BGRA surface with white text on transparency using premultiplied
815         // alpha. For subpixel text, the RGB values will be the mask value for
816         // the individual components. For bitmap glyphs, the RGB values will be
817         // the (premultiplied) color of the pixel. For Alpha and Mono, each
818         // pixel will have R==G==B==A at the end of this function.
819         // We access the color channels in little-endian order.
820         // The CGContext will create and own our pixel buffer.
821         // In the non-Bitmap cases, we will ask CoreGraphics to draw text onto
822         // an opaque background. In order to hit the most efficient path in CG
823         // for this, we will tell CG that the CGContext is opaque, by passing
824         // an "[...]AlphaNone[...]" context flag. This creates a slight
825         // contradiction to the way we use the buffer after CG is done with it,
826         // because we will convert it into text-on-transparency. But that's ok;
827         // we still get four bytes per pixel and CG won't mess with the alpha
828         // channel after we've stopped calling CG functions. We just need to
829         // make sure that we don't look at the alpha values of the pixels that
830         // we get from CG, and compute our own alpha value only from RGB.
831         // Note that CG requires kCGBitmapByteOrder32Little in order to do
832         // subpixel AA at all (which we need it to do in both Subpixel and
833         // Alpha+smoothing mode). But little-endian is what we want anyway, so
834         // this works out nicely.
835         let color_type = match glyph_type {
836             GlyphType::Vector => kCGImageAlphaNoneSkipFirst,
837             GlyphType::Bitmap => kCGImageAlphaPremultipliedFirst,
838         };
839 
840         CGContext::create_bitmap_context(None,
841                                          size.width as usize,
842                                          size.height as usize,
843                                          8,
844                                          size.width as usize * 4,
845                                          &CGColorSpace::create_device_rgb(),
846                                          kCGBitmapByteOrder32Little | color_type)
847     }
848 }
849 
850 #[derive(Clone, Copy, PartialEq, Debug)]
851 enum GlyphType {
852     Vector,
853     Bitmap,
854 }
855