summaryrefslogtreecommitdiffstats
path: root/gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs')
-rw-r--r--gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs639
1 files changed, 639 insertions, 0 deletions
diff --git a/gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs b/gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs
new file mode 100644
index 0000000000..81232d70c4
--- /dev/null
+++ b/gfx/wr/wr_glyph_rasterizer/src/platform/windows/font.rs
@@ -0,0 +1,639 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{FontInstanceFlags, FontKey, FontRenderMode, FontVariation};
+use api::{ColorU, GlyphDimensions, NativeFontHandle};
+use dwrote;
+use crate::gamma_lut::{ColorLut, GammaLut};
+use crate::rasterizer::{FontInstance, FontTransform, GlyphKey};
+use crate::rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
+use crate::rasterizer::apply_multistrike_bold;
+use crate::types::{FastHashMap, FastHashSet};
+use std::borrow::Borrow;
+use std::collections::hash_map::Entry;
+use std::hash::{Hash, Hasher};
+use std::path::Path;
+use std::sync::{Arc, Mutex};
+use api::FontInstancePlatformOptions;
+use std::mem;
+
+lazy_static! {
+ static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
+ family_name: "Arial".to_owned(),
+ weight: dwrote::FontWeight::Regular,
+ stretch: dwrote::FontStretch::Normal,
+ style: dwrote::FontStyle::Normal,
+ };
+}
+
+type CachedFontKey = Arc<Path>;
+
+// A cached dwrote font file that is shared among all faces.
+// Each face holds a CachedFontKey to keep track of how many users of the font there are.
+struct CachedFont {
+ key: CachedFontKey,
+ file: dwrote::FontFile,
+}
+
+// FontFile contains a ComPtr<IDWriteFontFile>, but DWrite font files are threadsafe.
+unsafe impl Send for CachedFont {}
+
+impl PartialEq for CachedFont {
+ fn eq(&self, other: &CachedFont) -> bool {
+ self.key == other.key
+ }
+}
+impl Eq for CachedFont {}
+
+impl Hash for CachedFont {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.key.hash(state);
+ }
+}
+
+impl Borrow<Path> for CachedFont {
+ fn borrow(&self) -> &Path {
+ &*self.key
+ }
+}
+
+lazy_static! {
+ // This is effectively a weak map of dwrote FontFiles. CachedFonts are entered into the
+ // cache when there are any FontFaces using them. CachedFonts are removed from the cache
+ // when there are no more FontFaces using them at all.
+ static ref FONT_CACHE: Mutex<FastHashSet<CachedFont>> = Mutex::new(FastHashSet::default());
+}
+
+struct FontFace {
+ cached: Option<CachedFontKey>,
+ file: dwrote::FontFile,
+ index: u32,
+ face: dwrote::FontFace,
+}
+
+pub struct FontContext {
+ fonts: FastHashMap<FontKey, FontFace>,
+ variations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS, Vec<FontVariation>), dwrote::FontFace>,
+ gamma_luts: FastHashMap<(u16, u8), GammaLut>,
+}
+
+// DirectWrite is safe to use on multiple threads and non-shareable resources are
+// all hidden inside their font context.
+unsafe impl Send for FontContext {}
+
+fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
+ match render_mode {
+ FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1,
+ FontRenderMode::Alpha |
+ FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
+ }
+}
+
+fn dwrite_measure_mode(
+ font: &FontInstance,
+ bitmaps: bool,
+) -> dwrote::DWRITE_MEASURING_MODE {
+ if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
+ dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC
+ } else {
+ match font.render_mode {
+ FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC,
+ FontRenderMode::Alpha | FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
+ }
+ }
+}
+
+fn dwrite_render_mode(
+ font_face: &dwrote::FontFace,
+ font: &FontInstance,
+ em_size: f32,
+ measure_mode: dwrote::DWRITE_MEASURING_MODE,
+ bitmaps: bool,
+) -> dwrote::DWRITE_RENDERING_MODE {
+ let dwrite_render_mode = match font.render_mode {
+ FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
+ FontRenderMode::Alpha | FontRenderMode::Subpixel => {
+ if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
+ dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
+ } else if font.flags.contains(FontInstanceFlags::FORCE_SYMMETRIC) {
+ dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC
+ } else if font.flags.contains(FontInstanceFlags::NO_SYMMETRIC) {
+ dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL
+ } else {
+ font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
+ }
+ }
+ };
+
+ if dwrite_render_mode == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
+ // Outline mode is not supported
+ return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ }
+
+ dwrite_render_mode
+}
+
+fn is_bitmap_font(font: &FontInstance) -> bool {
+ // If bitmaps are requested, then treat as a bitmap font to disable transforms.
+ // If mono AA is requested, let that take priority over using bitmaps.
+ font.render_mode != FontRenderMode::Mono &&
+ font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS)
+}
+
+impl FontContext {
+ pub fn distribute_across_threads() -> bool {
+ true
+ }
+
+ pub fn new() -> FontContext {
+ FontContext {
+ fonts: FastHashMap::default(),
+ variations: FastHashMap::default(),
+ gamma_luts: FastHashMap::default(),
+ }
+ }
+
+ fn add_font_descriptor(&mut self, font_key: &FontKey, desc: &dwrote::FontDescriptor) {
+ let system_fc = dwrote::FontCollection::get_system(false);
+ if let Some(font) = system_fc.get_font_from_descriptor(desc) {
+ let face = font.create_font_face();
+ let file = face.get_files().pop().unwrap();
+ let index = face.get_index();
+ self.fonts.insert(*font_key, FontFace { cached: None, file, index, face });
+ }
+ }
+
+ pub fn add_raw_font(&mut self, font_key: &FontKey, data: Arc<Vec<u8>>, index: u32) {
+ if self.fonts.contains_key(font_key) {
+ return;
+ }
+
+ if let Some(file) = dwrote::FontFile::new_from_data(data) {
+ if let Ok(face) = file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
+ self.fonts.insert(*font_key, FontFace { cached: None, file, index, face });
+ return;
+ }
+ }
+ // XXX add_raw_font needs to have a way to return an error
+ debug!("DWrite WR failed to load font from data, using Arial instead");
+ self.add_font_descriptor(font_key, &DEFAULT_FONT_DESCRIPTOR);
+ }
+
+ pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: NativeFontHandle) {
+ if self.fonts.contains_key(font_key) {
+ return;
+ }
+
+ let index = font_handle.index;
+ let mut cache = FONT_CACHE.lock().unwrap();
+ // Check to see if the font is already in the cache. If so, reuse it.
+ if let Some(font) = cache.get(font_handle.path.as_path()) {
+ if let Ok(face) = font.file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
+ self.fonts.insert(
+ *font_key,
+ FontFace { cached: Some(font.key.clone()), file: font.file.clone(), index, face },
+ );
+ return;
+ }
+ }
+ if let Some(file) = dwrote::FontFile::new_from_path(&font_handle.path) {
+ // The font is not in the cache yet, so try to create the font and insert it in the cache.
+ if let Ok(face) = file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE) {
+ let key: CachedFontKey = font_handle.path.into();
+ self.fonts.insert(
+ *font_key,
+ FontFace { cached: Some(key.clone()), file: file.clone(), index, face },
+ );
+ cache.insert(CachedFont { key, file });
+ return;
+ }
+ }
+
+ // XXX add_native_font needs to have a way to return an error
+ debug!("DWrite WR failed to load font from path, using Arial instead");
+ self.add_font_descriptor(font_key, &DEFAULT_FONT_DESCRIPTOR);
+ }
+
+ pub fn delete_font(&mut self, font_key: &FontKey) {
+ if let Some(face) = self.fonts.remove(font_key) {
+ self.variations.retain(|k, _| k.0 != *font_key);
+ // Check if this was a cached font.
+ if let Some(key) = face.cached {
+ let mut cache = FONT_CACHE.lock().unwrap();
+ // If there are only two references left, that means only this face and
+ // the cache are using the font. So remove it from the cache.
+ if Arc::strong_count(&key) == 2 {
+ cache.remove(&*key);
+ }
+ }
+ }
+ }
+
+ pub fn delete_font_instance(&mut self, instance: &FontInstance) {
+ // Ensure we don't keep around excessive amounts of stale variations.
+ if !instance.variations.is_empty() {
+ let sims = if instance.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
+ dwrote::DWRITE_FONT_SIMULATIONS_BOLD
+ } else {
+ dwrote::DWRITE_FONT_SIMULATIONS_NONE
+ };
+ self.variations.remove(&(instance.font_key, sims, instance.variations.clone()));
+ }
+ }
+
+ // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
+ // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
+ #[allow(dead_code)]
+ fn print_glyph_data(&self, data: &[u8], width: usize, height: usize) {
+ // Rust doesn't have step_by support on stable :(
+ for i in 0 .. height {
+ let current_height = i * width * 3;
+
+ for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
+ let r = pixel[0];
+ let g = pixel[1];
+ let b = pixel[2];
+ debug!("({}, {}, {}) ", r, g, b,);
+ }
+ }
+ }
+
+ fn get_font_face(
+ &mut self,
+ font: &FontInstance,
+ ) -> &dwrote::FontFace {
+ if !font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) &&
+ font.variations.is_empty() {
+ return &self.fonts.get(&font.font_key).unwrap().face;
+ }
+ let sims = if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
+ dwrote::DWRITE_FONT_SIMULATIONS_BOLD
+ } else {
+ dwrote::DWRITE_FONT_SIMULATIONS_NONE
+ };
+ match self.variations.entry((font.font_key, sims, font.variations.clone())) {
+ Entry::Occupied(entry) => entry.into_mut(),
+ Entry::Vacant(entry) => {
+ let normal_face = self.fonts.get(&font.font_key).unwrap();
+ if !font.variations.is_empty() {
+ if let Some(var_face) = normal_face.face.create_font_face_with_variations(
+ sims,
+ &font.variations.iter().map(|var| {
+ dwrote::DWRITE_FONT_AXIS_VALUE {
+ // OpenType tags are big-endian, but DWrite wants little-endian.
+ axisTag: var.tag.swap_bytes(),
+ value: var.value,
+ }
+ }).collect::<Vec<_>>(),
+ ) {
+ return entry.insert(var_face);
+ }
+ }
+ let var_face = normal_face.file
+ .create_face(normal_face.index, sims)
+ .unwrap_or_else(|_| normal_face.face.clone());
+ entry.insert(var_face)
+ }
+ }
+ }
+
+ fn create_glyph_analysis(
+ &mut self,
+ font: &FontInstance,
+ key: &GlyphKey,
+ size: f32,
+ transform: Option<dwrote::DWRITE_MATRIX>,
+ bitmaps: bool,
+ ) -> Result<(dwrote::GlyphRunAnalysis, dwrote::DWRITE_TEXTURE_TYPE, dwrote::RECT), dwrote::HRESULT> {
+ let face = self.get_font_face(font);
+ let glyph = key.index() as u16;
+ let advance = 0.0f32;
+ let offset = dwrote::GlyphOffset {
+ advanceOffset: 0.0,
+ ascenderOffset: 0.0,
+ };
+
+ let glyph_run = dwrote::DWRITE_GLYPH_RUN {
+ fontFace: unsafe { face.as_ptr() },
+ fontEmSize: size, // size in DIPs (1/96", same as CSS pixels)
+ glyphCount: 1,
+ glyphIndices: &glyph,
+ glyphAdvances: &advance,
+ glyphOffsets: &offset,
+ isSideways: 0,
+ bidiLevel: 0,
+ };
+
+ let dwrite_measure_mode = dwrite_measure_mode(font, bitmaps);
+ let dwrite_render_mode = dwrite_render_mode(
+ face,
+ font,
+ size,
+ dwrite_measure_mode,
+ bitmaps,
+ );
+
+ let analysis = dwrote::GlyphRunAnalysis::create(
+ &glyph_run,
+ 1.0,
+ transform,
+ dwrite_render_mode,
+ dwrite_measure_mode,
+ 0.0,
+ 0.0,
+ )?;
+ let texture_type = dwrite_texture_type(font.render_mode);
+ let bounds = analysis.get_alpha_texture_bounds(texture_type)?;
+ // If the bounds are empty, then we might not be able to render the glyph with cleartype.
+ // Try again with aliased rendering to check if that works instead.
+ if font.render_mode != FontRenderMode::Mono &&
+ (bounds.left == bounds.right || bounds.top == bounds.bottom) {
+ let analysis2 = dwrote::GlyphRunAnalysis::create(
+ &glyph_run,
+ 1.0,
+ transform,
+ dwrote::DWRITE_RENDERING_MODE_ALIASED,
+ dwrite_measure_mode,
+ 0.0,
+ 0.0,
+ )?;
+ let bounds2 = analysis2.get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_ALIASED_1x1)?;
+ if bounds2.left != bounds2.right && bounds2.top != bounds2.bottom {
+ return Ok((analysis2, dwrote::DWRITE_TEXTURE_ALIASED_1x1, bounds2));
+ }
+ }
+ Ok((analysis, texture_type, bounds))
+ }
+
+ pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
+ let face = &self.fonts.get(&font_key).unwrap().face;
+ let indices = face.get_glyph_indices(&[ch as u32]);
+ indices.first().map(|idx| *idx as u32)
+ }
+
+ pub fn get_glyph_dimensions(
+ &mut self,
+ font: &FontInstance,
+ key: &GlyphKey,
+ ) -> Option<GlyphDimensions> {
+ let (size, x_scale, y_scale, bitmaps, transform) = Self::get_glyph_parameters(font, key);
+ let (_, _, bounds) = self.create_glyph_analysis(font, key, size, transform, bitmaps).ok()?;
+
+ let width = (bounds.right - bounds.left) as i32;
+ let height = (bounds.bottom - bounds.top) as i32;
+
+ // Alpha texture bounds can sometimes return an empty rect
+ // Such as for spaces
+ if width == 0 || height == 0 {
+ return None;
+ }
+
+ let (strike_scale, pixel_step) = if bitmaps {
+ (y_scale, 1.0)
+ } else {
+ (x_scale, y_scale / x_scale)
+ };
+ let extra_strikes = font.get_extra_strikes(FontInstanceFlags::MULTISTRIKE_BOLD, strike_scale);
+ let extra_width = extra_strikes as f64 * pixel_step;
+
+ let face = self.get_font_face(font);
+ face.get_design_glyph_metrics(&[key.index() as u16], false)
+ .first()
+ .map(|metrics| {
+ let em_size = size / 16.;
+ let design_units_per_pixel = face.metrics().metrics0().designUnitsPerEm as f32 / 16. as f32;
+ let scaled_design_units_to_pixels = em_size / design_units_per_pixel;
+ let advance = metrics.advanceWidth as f32 * scaled_design_units_to_pixels;
+
+ GlyphDimensions {
+ left: bounds.left,
+ top: -bounds.top,
+ width: width + extra_width.ceil() as i32,
+ height,
+ advance: advance + extra_width as f32,
+ }
+ })
+ }
+
+ // DWrite ClearType gives us values in RGB, but WR expects BGRA.
+ fn convert_to_bgra(
+ &self,
+ pixels: &[u8],
+ width: usize,
+ height: usize,
+ texture_type: dwrote::DWRITE_TEXTURE_TYPE,
+ render_mode: FontRenderMode,
+ bitmaps: bool,
+ subpixel_bgr: bool,
+ padding: usize,
+ ) -> (Vec<u8>, bool) {
+ let (buffer_width, buffer_height) = (width + padding * 2, height + padding * 2);
+ let buffer_length = buffer_width * buffer_height * 4;
+ let mut bgra_pixels: Vec<u8> = vec![0; buffer_length];
+
+ match (texture_type, render_mode, bitmaps) {
+ (dwrote::DWRITE_TEXTURE_ALIASED_1x1, _, _) => {
+ assert!(width * height == pixels.len());
+ let mut i = 0;
+ for row in padding .. height + padding {
+ let row_offset = row * buffer_width;
+ for col in padding .. width + padding {
+ let offset = (row_offset + col) * 4;
+ let alpha = pixels[i];
+ i += 1;
+ bgra_pixels[offset + 0] = alpha;
+ bgra_pixels[offset + 1] = alpha;
+ bgra_pixels[offset + 2] = alpha;
+ bgra_pixels[offset + 3] = alpha;
+ }
+ }
+ (bgra_pixels, false)
+ }
+ (_, FontRenderMode::Subpixel, false) => {
+ assert!(width * height * 3 == pixels.len());
+ let mut i = 0;
+ for row in padding .. height + padding {
+ let row_offset = row * buffer_width;
+ for col in padding .. width + padding {
+ let offset = (row_offset + col) * 4;
+ let (mut r, g, mut b) = (pixels[i + 0], pixels[i + 1], pixels[i + 2]);
+ if subpixel_bgr {
+ mem::swap(&mut r, &mut b);
+ }
+ i += 3;
+ bgra_pixels[offset + 0] = b;
+ bgra_pixels[offset + 1] = g;
+ bgra_pixels[offset + 2] = r;
+ bgra_pixels[offset + 3] = 0xff;
+ }
+ }
+ (bgra_pixels, true)
+ }
+ _ => {
+ assert!(width * height * 3 == pixels.len());
+ let mut i = 0;
+ for row in padding .. height + padding {
+ let row_offset = row * buffer_width;
+ for col in padding .. width + padding {
+ let offset = (row_offset + col) * 4;
+ // Only take the G channel, as its closest to D2D
+ let alpha = pixels[i + 1] as u8;
+ i += 3;
+ bgra_pixels[offset + 0] = alpha;
+ bgra_pixels[offset + 1] = alpha;
+ bgra_pixels[offset + 2] = alpha;
+ bgra_pixels[offset + 3] = alpha;
+ }
+ }
+ (bgra_pixels, false)
+ }
+ }
+ }
+
+ pub fn prepare_font(font: &mut FontInstance) {
+ match font.render_mode {
+ FontRenderMode::Mono => {
+ // In mono mode the color of the font is irrelevant.
+ font.color = ColorU::new(255, 255, 255, 255);
+ // Subpixel positioning is disabled in mono mode.
+ font.disable_subpixel_position();
+ }
+ FontRenderMode::Alpha => {
+ font.color = font.color.luminance_color().quantize();
+ }
+ FontRenderMode::Subpixel => {
+ font.color = font.color.quantize();
+ }
+ }
+ }
+
+ fn get_glyph_parameters(font: &FontInstance, key: &GlyphKey)
+ -> (f32, f64, f64, bool, Option<dwrote::DWRITE_MATRIX>) {
+ let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+ let scaled_size = font.size.to_f64_px() * y_scale;
+ let bitmaps = is_bitmap_font(font);
+ let (mut shape, (mut x_offset, mut y_offset)) = if bitmaps {
+ (FontTransform::identity(), (0.0, 0.0))
+ } else {
+ (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
+ };
+ if font.flags.contains(FontInstanceFlags::FLIP_X) {
+ shape = shape.flip_x();
+ }
+ if font.flags.contains(FontInstanceFlags::FLIP_Y) {
+ shape = shape.flip_y();
+ }
+ if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
+ shape = shape.swap_xy();
+ }
+ let (mut tx, mut ty) = (0.0, 0.0);
+ if font.synthetic_italics.is_enabled() {
+ let (shape_, (tx_, ty_)) = font.synthesize_italics(shape, scaled_size);
+ shape = shape_;
+ tx = tx_;
+ ty = ty_;
+ };
+ x_offset += tx;
+ y_offset += ty;
+ let transform = if !shape.is_identity() || (x_offset, y_offset) != (0.0, 0.0) {
+ Some(dwrote::DWRITE_MATRIX {
+ m11: shape.scale_x,
+ m12: shape.skew_y,
+ m21: shape.skew_x,
+ m22: shape.scale_y,
+ dx: x_offset as f32,
+ dy: y_offset as f32,
+ })
+ } else {
+ None
+ };
+ (scaled_size as f32, x_scale, y_scale, bitmaps, transform)
+ }
+
+ pub fn begin_rasterize(_font: &FontInstance) {
+ }
+
+ pub fn end_rasterize(_font: &FontInstance) {
+ }
+
+ pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
+ let (size, x_scale, y_scale, bitmaps, transform) = Self::get_glyph_parameters(font, key);
+ let (analysis, texture_type, bounds) = self.create_glyph_analysis(font, key, size, transform, bitmaps)
+ .or(Err(GlyphRasterError::LoadFailed))?;
+ let mut width = (bounds.right - bounds.left) as i32;
+ let height = (bounds.bottom - bounds.top) as i32;
+ // Alpha texture bounds can sometimes return an empty rect
+ // Such as for spaces
+ if width == 0 || height == 0 {
+ return Err(GlyphRasterError::LoadFailed);
+ }
+
+ let pixels = analysis.create_alpha_texture(texture_type, bounds).or(Err(GlyphRasterError::LoadFailed))?;
+ let padding = if font.use_texture_padding() { 1 } else { 0 };
+ let (mut bgra_pixels, is_subpixel) = self.convert_to_bgra(
+ &pixels,
+ width as usize,
+ height as usize,
+ texture_type,
+ font.render_mode,
+ bitmaps,
+ font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR),
+ padding as usize,
+ );
+
+ // Apply multistrike bold, if necessary, and replace the current pixels with it.
+ let (strike_scale, pixel_step) = if bitmaps {
+ (y_scale, 1.0)
+ } else {
+ (x_scale, y_scale / x_scale)
+ };
+ let extra_strikes = font.get_extra_strikes(FontInstanceFlags::MULTISTRIKE_BOLD, strike_scale);
+ if extra_strikes > 0 {
+ let (bold_pixels, bold_width) = apply_multistrike_bold(
+ &bgra_pixels,
+ (width + padding * 2) as usize,
+ (height + padding * 2) as usize,
+ is_subpixel,
+ extra_strikes,
+ pixel_step,
+ );
+ width = bold_width as i32 - padding * 2;
+ bgra_pixels = bold_pixels;
+ }
+
+ let FontInstancePlatformOptions { gamma, contrast, cleartype_level, .. } =
+ font.platform_options.unwrap_or_default();
+ let gamma_lut = self.gamma_luts
+ .entry((gamma, contrast))
+ .or_insert_with(||
+ GammaLut::new(
+ contrast as f32 / 100.0,
+ gamma as f32 / 100.0,
+ gamma as f32 / 100.0,
+ ));
+ if is_subpixel {
+ gamma_lut.preblend_scaled(&mut bgra_pixels, font.color, cleartype_level);
+ } else {
+ gamma_lut.preblend(&mut bgra_pixels, font.color);
+ }
+
+ let format = if bitmaps {
+ GlyphFormat::Bitmap
+ } else if texture_type == dwrote::DWRITE_TEXTURE_ALIASED_1x1 {
+ font.get_alpha_glyph_format()
+ } else {
+ font.get_glyph_format()
+ };
+
+ Ok(RasterizedGlyph {
+ left: (bounds.left - padding) as f32,
+ top: (-bounds.top + padding) as f32,
+ width: width + padding * 2,
+ height: height + padding * 2,
+ scale: (if bitmaps { y_scale.recip() } else { 1.0 }) as f32,
+ format,
+ bytes: bgra_pixels,
+ })
+ }
+}