tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

font.rs (38587B)


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