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 }