/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{FontInstanceFlags, FontKey, FontRenderMode, FontVariation}; use api::{ColorU, GlyphDimensions, NativeFontHandle}; usecrate::gamma_lut::{ColorLut, GammaLut}; usecrate::rasterizer::{FontInstance, FontTransform, GlyphKey}; usecrate::rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph}; usecrate::rasterizer::apply_multistrike_bold; usecrate::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;
// 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. unsafeimpl Send for CachedFont {}
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. staticref FONT_CACHE: Mutex<FastHashSet<CachedFont>> = Mutex::new(FastHashSet::default());
}
// DirectWrite is safe to use on multiple threads and non-shareable resources are // all hidden inside their font context. unsafeimpl Send for FontContext {}
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)
}
iflet Some(file) = dwrote::FontFile::new_from_buffer(data) { iflet 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);
}
let index = font_handle.index; letmut cache = FONT_CACHE.lock().unwrap(); // Check to see if the font is already in the cache. If so, reuse it. iflet Some(font) = cache.get(font_handle.path.as_path()) { iflet 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;
}
} iflet 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. iflet 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);
}
pubfn delete_font(&mutself, font_key: &FontKey) { iflet Some(face) = self.fonts.remove(font_key) { self.variations.retain(|k, _| k.0 != *font_key); // Check if this was a cached font. iflet Some(key) = face.cached { letmut 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);
}
}
}
}
pubfn delete_font_instance(&mutself, 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 in0 .. 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 create_glyph_analysis(
&mutself,
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))
}
pubfn get_glyph_index(&mutself, font_key: FontKey, ch: char) -> Option<u32> { let face = &self.fonts.get(&font_key).unwrap().face; iflet Ok(indices) = face.glyph_indices(&[ch as u32]) { return indices.first().map(|idx| *idx as u32);
}
None
}
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); iflet Ok(metrics) = face.design_glyph_metrics(&[key.index() as u16], false) { return metrics
.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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.