tor-browser

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

font.rs (25255B)


      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::{FontInstanceFlags, FontKey, FontRenderMode, FontVariation};
      6 use api::{ColorU, GlyphDimensions, NativeFontHandle};
      7 use crate::gamma_lut::{ColorLut, GammaLut};
      8 use crate::rasterizer::{FontInstance, FontTransform, GlyphKey};
      9 use crate::rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
     10 use crate::rasterizer::apply_multistrike_bold;
     11 use crate::types::{FastHashMap, FastHashSet};
     12 use std::borrow::Borrow;
     13 use std::collections::hash_map::Entry;
     14 use std::hash::{Hash, Hasher};
     15 use std::path::Path;
     16 use std::sync::{Arc, Mutex};
     17 use api::FontInstancePlatformOptions;
     18 use std::mem;
     19 
     20 lazy_static! {
     21    static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
     22        family_name: "Arial".to_owned(),
     23        weight: dwrote::FontWeight::Regular,
     24        stretch: dwrote::FontStretch::Normal,
     25        style: dwrote::FontStyle::Normal,
     26    };
     27 }
     28 
     29 type CachedFontKey = Arc<Path>;
     30 
     31 // A cached dwrote font file that is shared among all faces.
     32 // Each face holds a CachedFontKey to keep track of how many users of the font there are.
     33 struct CachedFont {
     34    key: CachedFontKey,
     35    file: dwrote::FontFile,
     36 }
     37 
     38 // FontFile contains a ComPtr<IDWriteFontFile>, but DWrite font files are threadsafe.
     39 unsafe impl Send for CachedFont {}
     40 
     41 impl PartialEq for CachedFont {
     42    fn eq(&self, other: &CachedFont) -> bool {
     43        self.key == other.key
     44    }
     45 }
     46 impl Eq for CachedFont {}
     47 
     48 impl Hash for CachedFont {
     49    fn hash<H: Hasher>(&self, state: &mut H) {
     50        self.key.hash(state);
     51    }
     52 }
     53 
     54 impl Borrow<Path> for CachedFont {
     55    fn borrow(&self) -> &Path {
     56        &*self.key
     57    }
     58 }
     59 
     60 lazy_static! {
     61    // This is effectively a weak map of dwrote FontFiles. CachedFonts are entered into the
     62    // cache when there are any FontFaces using them. CachedFonts are removed from the cache
     63    // when there are no more FontFaces using them at all.
     64    static ref FONT_CACHE: Mutex<FastHashSet<CachedFont>> = Mutex::new(FastHashSet::default());
     65 }
     66 
     67 struct FontFace {
     68    cached: Option<CachedFontKey>,
     69    file: dwrote::FontFile,
     70    index: u32,
     71    face: dwrote::FontFace,
     72 }
     73 
     74 pub struct FontContext {
     75    fonts: FastHashMap<FontKey, FontFace>,
     76    variations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS, Vec<FontVariation>), dwrote::FontFace>,
     77    gamma_luts: FastHashMap<(u16, u8), GammaLut>,
     78 }
     79 
     80 // DirectWrite is safe to use on multiple threads and non-shareable resources are
     81 // all hidden inside their font context.
     82 unsafe impl Send for FontContext {}
     83 
     84 fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
     85    match render_mode {
     86        FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1,
     87        FontRenderMode::Alpha |
     88        FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
     89    }
     90 }
     91 
     92 fn dwrite_measure_mode(
     93    font: &FontInstance,
     94    bitmaps: bool,
     95 ) -> dwrote::DWRITE_MEASURING_MODE {
     96    if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
     97        dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC
     98    } else {
     99      match font.render_mode {
    100          FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC,
    101          FontRenderMode::Alpha | FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
    102      }
    103    }
    104 }
    105 
    106 fn dwrite_render_mode(
    107    font_face: &dwrote::FontFace,
    108    font: &FontInstance,
    109    em_size: f32,
    110    measure_mode: dwrote::DWRITE_MEASURING_MODE,
    111    bitmaps: bool,
    112 ) -> dwrote::DWRITE_RENDERING_MODE {
    113    let dwrite_render_mode = match font.render_mode {
    114        FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
    115        FontRenderMode::Alpha | FontRenderMode::Subpixel => {
    116            if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
    117                dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
    118            } else if font.flags.contains(FontInstanceFlags::FORCE_SYMMETRIC) {
    119                dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC
    120            } else if font.flags.contains(FontInstanceFlags::NO_SYMMETRIC) {
    121                dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL
    122            } else {
    123                font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
    124            }
    125        }
    126    };
    127 
    128    if dwrite_render_mode == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
    129        // Outline mode is not supported
    130        return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
    131    }
    132 
    133    dwrite_render_mode
    134 }
    135 
    136 fn is_bitmap_font(font: &FontInstance) -> bool {
    137    // If bitmaps are requested, then treat as a bitmap font to disable transforms.
    138    // If mono AA is requested, let that take priority over using bitmaps.
    139    font.render_mode != FontRenderMode::Mono &&
    140        font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS)
    141 }
    142 
    143 impl FontContext {
    144    pub fn distribute_across_threads() -> bool {
    145        true
    146    }
    147 
    148    pub fn new() -> FontContext {
    149        FontContext {
    150            fonts: FastHashMap::default(),
    151            variations: FastHashMap::default(),
    152            gamma_luts: FastHashMap::default(),
    153        }
    154    }
    155 
    156    fn add_font_descriptor(&mut self, font_key: &FontKey, desc: &dwrote::FontDescriptor) {
    157        let system_fc = dwrote::FontCollection::get_system(false);
    158        if let Ok(font) = system_fc.font_from_descriptor(desc) {
    159            if let Some(font) = font {
    160                let face = font.create_font_face();
    161                if let Ok(mut files) = face.files() {
    162                    if let Some(file) = files.pop() {
    163                        let index = face.get_index();
    164                        self.fonts.insert(*font_key, FontFace { cached: None, file, index, face });
    165                    }
    166                }
    167            }
    168        }
    169    }
    170 
    171    pub fn add_raw_font(&mut self, font_key: &FontKey, data: Arc<Vec<u8>>, index: u32) {
    172        if self.fonts.contains_key(font_key) {
    173            return;
    174        }
    175 
    176        if let Some(file) = dwrote::FontFile::new_from_buffer(data) {
    177            if let Ok(face) = file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
    178                self.fonts.insert(*font_key, FontFace { cached: None, file, index, face });
    179                return;
    180            }
    181        }
    182        // XXX add_raw_font needs to have a way to return an error
    183        debug!("DWrite WR failed to load font from data, using Arial instead");
    184        self.add_font_descriptor(font_key, &DEFAULT_FONT_DESCRIPTOR);
    185    }
    186 
    187    pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: NativeFontHandle) {
    188        if self.fonts.contains_key(font_key) {
    189            return;
    190        }
    191 
    192        let index = font_handle.index;
    193        let mut cache = FONT_CACHE.lock().unwrap();
    194        // Check to see if the font is already in the cache. If so, reuse it.
    195        if let Some(font) = cache.get(font_handle.path.as_path()) {
    196            if let Ok(face) = font.file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
    197                self.fonts.insert(
    198                    *font_key,
    199                    FontFace { cached: Some(font.key.clone()), file: font.file.clone(), index, face },
    200                );
    201                return;
    202            }
    203        }
    204        if let Some(file) = dwrote::FontFile::new_from_path(&font_handle.path) {
    205            // The font is not in the cache yet, so try to create the font and insert it in the cache.
    206            if let Ok(face) = file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
    207                let key: CachedFontKey = font_handle.path.into();
    208                self.fonts.insert(
    209                    *font_key,
    210                    FontFace { cached: Some(key.clone()), file: file.clone(), index, face },
    211                );
    212                cache.insert(CachedFont { key, file });
    213                return;
    214            }
    215        }
    216 
    217        // XXX add_native_font needs to have a way to return an error
    218        debug!("DWrite WR failed to load font from path, using Arial instead");
    219        self.add_font_descriptor(font_key, &DEFAULT_FONT_DESCRIPTOR);
    220    }
    221 
    222    pub fn delete_font(&mut self, font_key: &FontKey) {
    223        if let Some(face) = self.fonts.remove(font_key) {
    224            self.variations.retain(|k, _| k.0 != *font_key);
    225            // Check if this was a cached font.
    226            if let Some(key) = face.cached {
    227                let mut cache = FONT_CACHE.lock().unwrap();
    228                // If there are only two references left, that means only this face and
    229                // the cache are using the font. So remove it from the cache.
    230                if Arc::strong_count(&key) == 2 {
    231                    cache.remove(&*key);
    232                }
    233            }
    234        }
    235    }
    236 
    237    pub fn delete_font_instance(&mut self, instance: &FontInstance) {
    238        // Ensure we don't keep around excessive amounts of stale variations.
    239        if !instance.variations.is_empty() {
    240            let sims = if instance.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
    241                dwrote::DWRITE_FONT_SIMULATIONS_BOLD
    242            } else {
    243                dwrote::DWRITE_FONT_SIMULATIONS_NONE
    244            };
    245            self.variations.remove(&(instance.font_key, sims, instance.variations.clone()));
    246        }
    247    }
    248 
    249    // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
    250    // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
    251    #[allow(dead_code)]
    252    fn print_glyph_data(&self, data: &[u8], width: usize, height: usize) {
    253        // Rust doesn't have step_by support on stable :(
    254        for i in 0 .. height {
    255            let current_height = i * width * 3;
    256 
    257            for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
    258                let r = pixel[0];
    259                let g = pixel[1];
    260                let b = pixel[2];
    261                debug!("({}, {}, {}) ", r, g, b,);
    262            }
    263        }
    264    }
    265 
    266    fn get_font_face(
    267        &mut self,
    268        font: &FontInstance,
    269    ) -> &dwrote::FontFace {
    270        if !font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) &&
    271           font.variations.is_empty() {
    272            return &self.fonts.get(&font.font_key).unwrap().face;
    273        }
    274        let sims = if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
    275            dwrote::DWRITE_FONT_SIMULATIONS_BOLD
    276        } else {
    277            dwrote::DWRITE_FONT_SIMULATIONS_NONE
    278        };
    279        match self.variations.entry((font.font_key, sims, font.variations.clone())) {
    280            Entry::Occupied(entry) => entry.into_mut(),
    281            Entry::Vacant(entry) => {
    282                let normal_face = self.fonts.get(&font.font_key).unwrap();
    283                if !font.variations.is_empty() {
    284                    if let Some(var_face) = normal_face.face.create_font_face_with_variations(
    285                        sims,
    286                        &font.variations.iter().map(|var| {
    287                            dwrote::DWRITE_FONT_AXIS_VALUE {
    288                                // OpenType tags are big-endian, but DWrite wants little-endian.
    289                                axisTag: var.tag.swap_bytes(),
    290                                value: var.value,
    291                            }
    292                        }).collect::<Vec<_>>(),
    293                    ) {
    294                        return entry.insert(var_face);
    295                    }
    296                }
    297                let var_face = normal_face.file
    298                    .create_face(normal_face.index, sims)
    299                    .unwrap_or_else(|_| normal_face.face.clone());
    300                entry.insert(var_face)
    301            }
    302        }
    303    }
    304 
    305    fn create_glyph_analysis(
    306        &mut self,
    307        font: &FontInstance,
    308        key: &GlyphKey,
    309        size: f32,
    310        transform: Option<dwrote::DWRITE_MATRIX>,
    311        bitmaps: bool,
    312    ) -> Result<(dwrote::GlyphRunAnalysis, dwrote::DWRITE_TEXTURE_TYPE, dwrote::RECT), dwrote::HRESULT> {
    313        let face = self.get_font_face(font);
    314        let glyph = key.index() as u16;
    315        let advance = 0.0f32;
    316        let offset = dwrote::GlyphOffset {
    317            advanceOffset: 0.0,
    318            ascenderOffset: 0.0,
    319        };
    320 
    321        let glyph_run = dwrote::DWRITE_GLYPH_RUN {
    322            fontFace: unsafe { face.as_ptr() },
    323            fontEmSize: size, // size in DIPs (1/96", same as CSS pixels)
    324            glyphCount: 1,
    325            glyphIndices: &glyph,
    326            glyphAdvances: &advance,
    327            glyphOffsets: &offset,
    328            isSideways: 0,
    329            bidiLevel: 0,
    330        };
    331 
    332        let dwrite_measure_mode = dwrite_measure_mode(font, bitmaps);
    333        let dwrite_render_mode = dwrite_render_mode(
    334            face,
    335            font,
    336            size,
    337            dwrite_measure_mode,
    338            bitmaps,
    339        );
    340 
    341        let analysis = dwrote::GlyphRunAnalysis::create(
    342            &glyph_run,
    343            1.0,
    344            transform,
    345            dwrite_render_mode,
    346            dwrite_measure_mode,
    347            0.0,
    348            0.0,
    349        )?;
    350        let texture_type = dwrite_texture_type(font.render_mode);
    351        let bounds = analysis.get_alpha_texture_bounds(texture_type)?;
    352        // If the bounds are empty, then we might not be able to render the glyph with cleartype.
    353        // Try again with aliased rendering to check if that works instead.
    354        if font.render_mode != FontRenderMode::Mono &&
    355           (bounds.left == bounds.right || bounds.top == bounds.bottom) {
    356            let analysis2 = dwrote::GlyphRunAnalysis::create(
    357                &glyph_run,
    358                1.0,
    359                transform,
    360                dwrote::DWRITE_RENDERING_MODE_ALIASED,
    361                dwrite_measure_mode,
    362                0.0,
    363                0.0,
    364            )?;
    365            let bounds2 = analysis2.get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_ALIASED_1x1)?;
    366            if bounds2.left != bounds2.right && bounds2.top != bounds2.bottom {
    367                return Ok((analysis2, dwrote::DWRITE_TEXTURE_ALIASED_1x1, bounds2));
    368            }
    369        }
    370        Ok((analysis, texture_type, bounds))
    371    }
    372 
    373    pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
    374        let face = &self.fonts.get(&font_key).unwrap().face;
    375        if let Ok(indices) = face.glyph_indices(&[ch as u32]) {
    376            return indices.first().map(|idx| *idx as u32);
    377        }
    378        None
    379    }
    380 
    381    pub fn get_glyph_dimensions(
    382        &mut self,
    383        font: &FontInstance,
    384        key: &GlyphKey,
    385    ) -> Option<GlyphDimensions> {
    386        let (size, x_scale, y_scale, bitmaps, transform) = Self::get_glyph_parameters(font, key);
    387        let (_, _, bounds) = self.create_glyph_analysis(font, key, size, transform, bitmaps).ok()?;
    388 
    389        let width = (bounds.right - bounds.left) as i32;
    390        let height = (bounds.bottom - bounds.top) as i32;
    391 
    392        // Alpha texture bounds can sometimes return an empty rect
    393        // Such as for spaces
    394        if width == 0 || height == 0 {
    395            return None;
    396        }
    397 
    398        let (strike_scale, pixel_step) = if bitmaps {
    399            (y_scale, 1.0)
    400        } else {
    401            (x_scale, y_scale / x_scale)
    402        };
    403        let extra_strikes = font.get_extra_strikes(FontInstanceFlags::MULTISTRIKE_BOLD, strike_scale);
    404        let extra_width = extra_strikes as f64 * pixel_step;
    405 
    406        let face = self.get_font_face(font);
    407        if let Ok(metrics) = face.design_glyph_metrics(&[key.index() as u16], false) {
    408            return metrics
    409                .first()
    410                .map(|metrics| {
    411                    let em_size = size / 16.;
    412                    let design_units_per_pixel = face.metrics().metrics0().designUnitsPerEm as f32 / 16. as f32;
    413                    let scaled_design_units_to_pixels = em_size / design_units_per_pixel;
    414                    let advance = metrics.advanceWidth as f32 * scaled_design_units_to_pixels;
    415 
    416                    GlyphDimensions {
    417                        left: bounds.left,
    418                        top: -bounds.top,
    419                        width: width + extra_width.ceil() as i32,
    420                        height,
    421                        advance: advance + extra_width as f32,
    422                    }
    423                });
    424        }
    425 
    426        None
    427    }
    428 
    429    // DWrite ClearType gives us values in RGB, but WR expects BGRA.
    430    fn convert_to_bgra(
    431        &self,
    432        pixels: &[u8],
    433        width: usize,
    434        height: usize,
    435        texture_type: dwrote::DWRITE_TEXTURE_TYPE,
    436        render_mode: FontRenderMode,
    437        bitmaps: bool,
    438        subpixel_bgr: bool,
    439        padding: usize,
    440    ) -> (Vec<u8>, bool) {
    441        let (buffer_width, buffer_height) = (width + padding * 2, height + padding * 2);
    442        let buffer_length = buffer_width * buffer_height * 4;
    443        let mut bgra_pixels: Vec<u8> = vec![0; buffer_length];
    444 
    445        match (texture_type, render_mode, bitmaps) {
    446            (dwrote::DWRITE_TEXTURE_ALIASED_1x1, _, _) => {
    447                assert!(width * height == pixels.len());
    448                let mut i = 0;
    449                for row in padding .. height + padding {
    450                    let row_offset = row * buffer_width;
    451                    for col in padding .. width + padding {
    452                        let offset = (row_offset + col) * 4;
    453                        let alpha = pixels[i];
    454                        i += 1;
    455                        bgra_pixels[offset + 0] = alpha;
    456                        bgra_pixels[offset + 1] = alpha;
    457                        bgra_pixels[offset + 2] = alpha;
    458                        bgra_pixels[offset + 3] = alpha;
    459                    }
    460                }
    461                (bgra_pixels, false)
    462            }
    463            (_, FontRenderMode::Subpixel, false) => {
    464                assert!(width * height * 3 == pixels.len());
    465                let mut i = 0;
    466                for row in padding .. height + padding {
    467                    let row_offset = row * buffer_width;
    468                    for col in padding .. width + padding {
    469                        let offset = (row_offset + col) * 4;
    470                        let (mut r, g, mut b) = (pixels[i + 0], pixels[i + 1], pixels[i + 2]);
    471                        if subpixel_bgr {
    472                            mem::swap(&mut r, &mut b);
    473                        }
    474                        i += 3;
    475                        bgra_pixels[offset + 0] = b;
    476                        bgra_pixels[offset + 1] = g;
    477                        bgra_pixels[offset + 2] = r;
    478                        bgra_pixels[offset + 3] = 0xff;
    479                    }
    480                }
    481                (bgra_pixels, true)
    482            }
    483            _ => {
    484                assert!(width * height * 3 == pixels.len());
    485                let mut i = 0;
    486                for row in padding .. height + padding {
    487                    let row_offset = row * buffer_width;
    488                    for col in padding .. width + padding {
    489                        let offset = (row_offset + col) * 4;
    490                        // Only take the G channel, as its closest to D2D
    491                        let alpha = pixels[i + 1] as u8;
    492                        i += 3;
    493                        bgra_pixels[offset + 0] = alpha;
    494                        bgra_pixels[offset + 1] = alpha;
    495                        bgra_pixels[offset + 2] = alpha;
    496                        bgra_pixels[offset + 3] = alpha;
    497                    }
    498                }
    499                (bgra_pixels, false)
    500            }
    501        }
    502    }
    503 
    504    pub fn prepare_font(font: &mut FontInstance) {
    505        match font.render_mode {
    506            FontRenderMode::Mono => {
    507                // In mono mode the color of the font is irrelevant.
    508                font.color = ColorU::new(255, 255, 255, 255);
    509                // Subpixel positioning is disabled in mono mode.
    510                font.disable_subpixel_position();
    511            }
    512            FontRenderMode::Alpha => {
    513                font.color = font.color.luminance_color().quantize();
    514            }
    515            FontRenderMode::Subpixel => {
    516                font.color = font.color.quantize();
    517            }
    518        }
    519    }
    520 
    521    fn get_glyph_parameters(font: &FontInstance, key: &GlyphKey)
    522                            -> (f32, f64, f64, bool, Option<dwrote::DWRITE_MATRIX>) {
    523        let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
    524        let scaled_size = font.size.to_f64_px() * y_scale;
    525        let bitmaps = is_bitmap_font(font);
    526        let (mut shape, (mut x_offset, mut y_offset)) = if bitmaps {
    527            (FontTransform::identity(), (0.0, 0.0))
    528        } else {
    529            (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
    530        };
    531        if font.flags.contains(FontInstanceFlags::FLIP_X) {
    532            shape = shape.flip_x();
    533        }
    534        if font.flags.contains(FontInstanceFlags::FLIP_Y) {
    535            shape = shape.flip_y();
    536        }
    537        if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
    538            shape = shape.swap_xy();
    539        }
    540        let (mut tx, mut ty) = (0.0, 0.0);
    541        if font.synthetic_italics.is_enabled() {
    542            let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, scaled_size);
    543            shape = shape_;
    544            tx = tx_;
    545            ty = ty_;
    546        };
    547        x_offset += tx;
    548        y_offset += ty;
    549        let transform = if !shape.is_identity() || (x_offset, y_offset) != (0.0, 0.0) {
    550            Some(dwrote::DWRITE_MATRIX {
    551                m11: shape.scale_x,
    552                m12: shape.skew_y,
    553                m21: shape.skew_x,
    554                m22: shape.scale_y,
    555                dx: x_offset as f32,
    556                dy: y_offset as f32,
    557            })
    558        } else {
    559            None
    560        };
    561        (scaled_size as f32, x_scale, y_scale, bitmaps, transform)
    562    }
    563 
    564    pub fn begin_rasterize(_font: &FontInstance) {
    565    }
    566 
    567    pub fn end_rasterize(_font: &FontInstance) {
    568    }
    569 
    570    pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
    571        let (size, x_scale, y_scale, bitmaps, transform) = Self::get_glyph_parameters(font, key);
    572        let (analysis, texture_type, bounds) = self.create_glyph_analysis(font, key, size, transform, bitmaps)
    573                                                   .or(Err(GlyphRasterError::LoadFailed))?;
    574        let mut width = (bounds.right - bounds.left) as i32;
    575        let height = (bounds.bottom - bounds.top) as i32;
    576        // Alpha texture bounds can sometimes return an empty rect
    577        // Such as for spaces
    578        if width == 0 || height == 0 {
    579            return Err(GlyphRasterError::LoadFailed);
    580        }
    581 
    582        let pixels = analysis.create_alpha_texture(texture_type, bounds).or(Err(GlyphRasterError::LoadFailed))?;
    583        let padding = if font.use_texture_padding() { 1 } else { 0 };
    584        let (mut bgra_pixels, is_subpixel) = self.convert_to_bgra(
    585            &pixels,
    586            width as usize,
    587            height as usize,
    588            texture_type,
    589            font.render_mode,
    590            bitmaps,
    591            font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR),
    592            padding as usize,
    593        );
    594 
    595        // Apply multistrike bold, if necessary, and replace the current pixels with it.
    596        let (strike_scale, pixel_step) = if bitmaps {
    597            (y_scale, 1.0)
    598        } else {
    599            (x_scale, y_scale / x_scale)
    600        };
    601        let extra_strikes = font.get_extra_strikes(FontInstanceFlags::MULTISTRIKE_BOLD, strike_scale);
    602        if extra_strikes > 0 {
    603            let (bold_pixels, bold_width) = apply_multistrike_bold(
    604                &bgra_pixels,
    605                (width + padding * 2) as usize,
    606                (height + padding * 2) as usize,
    607                is_subpixel,
    608                extra_strikes,
    609                pixel_step,
    610            );
    611            width = bold_width as i32 - padding * 2;
    612            bgra_pixels = bold_pixels;
    613        }
    614 
    615        let FontInstancePlatformOptions { gamma, contrast, cleartype_level, .. } =
    616            font.platform_options.unwrap_or_default();
    617        let gamma_lut = self.gamma_luts
    618            .entry((gamma, contrast))
    619            .or_insert_with(||
    620                GammaLut::new(
    621                    contrast as f32 / 100.0,
    622                    gamma as f32 / 100.0,
    623                    gamma as f32 / 100.0,
    624                ));
    625        if is_subpixel {
    626            gamma_lut.preblend_scaled(&mut bgra_pixels, font.color, cleartype_level);
    627        } else {
    628            gamma_lut.preblend(&mut bgra_pixels, font.color);
    629        }
    630 
    631        let format = if bitmaps {
    632            GlyphFormat::Bitmap
    633        } else if texture_type == dwrote::DWRITE_TEXTURE_ALIASED_1x1 {
    634            font.get_alpha_glyph_format()
    635        } else {
    636            font.get_glyph_format()
    637        };
    638 
    639        Ok(RasterizedGlyph {
    640            left: (bounds.left - padding) as f32,
    641            top: (-bounds.top + padding) as f32,
    642            width: width + padding * 2,
    643            height: height + padding * 2,
    644            scale: (if bitmaps { y_scale.recip() } else { 1.0 }) as f32,
    645            format,
    646            bytes: bgra_pixels,
    647            is_packed_glyph: false,
    648        })
    649    }
    650 }