/* 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/. */
#![allow(non_camel_case_types)]
use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode}; use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting}; use api::{FontInstanceFlags, FontTemplate, FontVariation, NativeFontHandle}; use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode}; use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32}; use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos}; use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt}; use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face}; use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph}; use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size}; use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform, FT_String, FT_ULong, FT_Vector}; use freetype::freetype::{FT_Err_Unimplemented_Feature, FT_MulFix, FT_Outline_Embolden}; use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT}; use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT}; use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING}; use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES}; use freetype::freetype::FT_FACE_FLAG_MULTIPLE_MASTERS; use freetype::succeeded; usecrate::gamma_lut::{ColorLut, GammaLut}; usecrate::rasterizer::{FontInstance, GlyphFormat, GlyphKey}; usecrate::rasterizer::{GlyphRasterError, GlyphRasterResult, RasterizedGlyph}; usecrate::types::FastHashMap; #[cfg(any(not(target_os = "android"), feature = "dynamic_freetype"))] use libc::{dlsym, RTLD_DEFAULT}; use libc::free; use std::{cmp, mem, ptr, slice}; use std::cmp::max; use std::ffi::CString; use std::sync::{Arc, Condvar, Mutex, MutexGuard};
// These constants are not present in the freetype // bindings due to bindgen not handling the way // the macros are defined. //const FT_LOAD_TARGET_NORMAL: FT_UInt = 0 << 16; const FT_LOAD_TARGET_LIGHT: FT_UInt = 1 << 16; const FT_LOAD_TARGET_MONO: FT_UInt = 2 << 16; const FT_LOAD_TARGET_LCD: FT_UInt = 3 << 16; const FT_LOAD_TARGET_LCD_V: FT_UInt = 4 << 16;
// Custom version of FT_GlyphSlot_Embolden to be less aggressive with outline // fonts than the default implementation in FreeType. #[no_mangle] pubextern"C"fn mozilla_glyphslot_embolden_less(slot: FT_GlyphSlot) { if slot.is_null() { return;
}
let slot_ = unsafe { &mut *slot }; let format = slot_.format; if format != FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE { // For non-outline glyphs, just fall back to FreeType's function. unsafe { FT_GlyphSlot_Embolden(slot) }; return;
}
let face_ = unsafe { *slot_.face };
// FT_GlyphSlot_Embolden uses a divisor of 24 here; we'll be only half as // bold. let size_ = unsafe { *face_.size }; let strength = unsafe { FT_MulFix(face_.units_per_EM as FT_Long,
size_.metrics.y_scale) / 48 }; unsafe { FT_Outline_Embolden(&mut slot_.outline, strength) };
impl Drop for CachedFont { fn drop(&mutself) { unsafe { if !self.mm_var.is_null() &&
unimplemented(FT_Done_MM_Var((*(*self.face).glyph).library, self.mm_var)) {
free(self.mm_var as _);
}
FT_Done_Face(self.face);
}
}
}
struct FontCache {
lib: FT_Library, // Maps a template to a cached font that may be used across all threads.
fonts: FastHashMap<FontTemplate, Arc<Mutex<CachedFont>>>, // The current LCD filter installed in the library.
lcd_filter: FontLCDFilter, // The number of threads currently relying on the LCD filter state.
lcd_filter_uses: usize,
}
// FreeType resources are safe to move between threads as long as they // are not concurrently accessed. In our case, everything is behind a // Mutex so it is safe to move them between threads. unsafeimpl Send for CachedFont {} unsafeimpl Send for FontCache {}
impl FontCache { fn new() -> Self { letmut lib: FT_Library = ptr::null_mut(); let result = unsafe { FT_Init_FreeType(&mut lib) }; if succeeded(result) { // Ensure the library uses the default LCD filter initially. unsafe { FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT) };
} else {
panic!("Failed to initialize FreeType - {}", result)
}
fn get_skew_bounds(bottom: i32, top: i32, skew_factor: f32, _vertical: bool) -> (f32, f32) { let skew_min = (bottom as f32 + 0.5) * skew_factor; let skew_max = (top as f32 - 0.5) * skew_factor; // Negative skew factor may switch the sense of skew_min and skew_max.
(skew_min.min(skew_max).floor(), skew_min.max(skew_max).ceil())
}
fn skew_bitmap(
bitmap: &[u8],
width: usize,
height: usize,
left: i32,
top: i32,
skew_factor: f32,
vertical: bool, // TODO: vertical skew not yet implemented!
) -> (Vec<u8>, usize, i32) { let stride = width * 4; // Calculate the skewed horizontal offsets of the bottom and top of the glyph. let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top, skew_factor, vertical); // Allocate enough extra width for the min/max skew offsets. let skew_width = width + (skew_max - skew_min) as usize; letmut skew_buffer = vec![0u8; skew_width * height * 4]; for y in0 .. height { // Calculate a skew offset at the vertical center of the current row. let offset = (top as f32 - y as f32 - 0.5) * skew_factor - skew_min; // Get a blend factor in 0..256 constant across all pixels in the row. let blend = (offset.fract() * 256.0) as u32; let src_row = y * stride; let dest_row = (y * skew_width + offset.floor() as usize) * 4; letmut prev_px = [0u32; 4]; for (src, dest) in
bitmap[src_row .. src_row + stride].chunks(4).zip(
skew_buffer[dest_row .. dest_row + stride].chunks_mut(4)
) { let px = [src[0] as u32, src[1] as u32, src[2] as u32, src[3] as u32]; // Blend current pixel with previous pixel based on blend factor. let next_px = [px[0] * blend, px[1] * blend, px[2] * blend, px[3] * blend];
dest[0] = ((((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8) as u8;
dest[1] = ((((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8) as u8;
dest[2] = ((((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8) as u8;
dest[3] = ((((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8) as u8; // Save the remainder for blending onto the next pixel.
prev_px = next_px;
} // If the skew misaligns the final pixel, write out the remainder. if blend > 0 { let dest = &mut skew_buffer[dest_row + stride .. dest_row + stride + 4];
dest[0] = ((prev_px[0] + 128) >> 8) as u8;
dest[1] = ((prev_px[1] + 128) >> 8) as u8;
dest[2] = ((prev_px[2] + 128) >> 8) as u8;
dest[3] = ((prev_px[3] + 128) >> 8) as u8;
}
}
(skew_buffer, skew_width, left + skew_min as i32)
}
pubfn add_raw_font(&mutself, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) { if !self.fonts.contains_key(font_key) { let len = bytes.len(); match FONT_CACHE.lock().unwrap().add_font(FontTemplate::Raw(bytes, index)) {
Ok(font) => self.fonts.insert(*font_key, font),
Err(result) => panic!("adding raw font failed: {} bytes, err={:?}", len, result),
};
}
}
pubfn add_native_font(&mutself, font_key: &FontKey, native_font_handle: NativeFontHandle) { if !self.fonts.contains_key(font_key) { let path = native_font_handle.path.to_string_lossy().into_owned(); match FONT_CACHE.lock().unwrap().add_font(FontTemplate::Native(native_font_handle)) {
Ok(font) => self.fonts.insert(*font_key, font),
Err(result) => panic!("adding native font failed: file={} err={:?}", path, result),
};
}
}
pubfn delete_font(&mutself, font_key: &FontKey) { iflet Some(cached) = self.fonts.remove(font_key) { // If the only references to this font are the FontCache and this FontContext, // then delete the font as there are no other existing users. if Arc::strong_count(&cached) <= 2 {
FONT_CACHE.lock().unwrap().delete_font(cached);
}
}
}
let mm_var = cached.mm_var; if !mm_var.is_null() && font.variations != cached.variations {
cached.variations.clear();
cached.variations.extend_from_slice(&font.variations);
unsafe { let num_axis = (*mm_var).num_axis; letmut coords: Vec<FT_Fixed> = Vec::with_capacity(num_axis as usize); for i in0 .. num_axis { let axis = (*mm_var).axis.offset(i as isize); letmut value = (*axis).def; for var in &font.variations { if var.tag as FT_ULong == (*axis).tag {
value = (var.value * 65536.0 + 0.5) as FT_Fixed;
value = cmp::min(value, (*axis).maximum);
value = cmp::max(value, (*axis).minimum); break;
}
}
coords.push(value);
} let res = FT_Set_Var_Design_Coordinates(face, num_axis, coords.as_mut_ptr());
debug_assert!(succeeded(res));
}
}
if font.flags.contains(FontInstanceFlags::NO_AUTOHINT) {
load_flags |= FT_LOAD_NO_AUTOHINT;
} if !font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS) {
load_flags |= FT_LOAD_NO_BITMAP;
}
let face_flags = unsafe { (*face).face_flags }; if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 { // We only set FT_LOAD_COLOR if there are bitmap strikes; // COLR (color-layer) fonts are handled internally by Gecko, and // WebRender is just asked to paint individual layers.
load_flags |= FT_LOAD_COLOR;
}
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let req_size = font.size.to_f64_px();
letmut result = if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 &&
(face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 &&
(load_flags & FT_LOAD_NO_BITMAP) == 0 { unsafe { FT_Set_Transform(face, ptr::null_mut(), ptr::null_mut()) }; Self::choose_bitmap_size(face, req_size * y_scale)
} else { letmut shape = font.transform.invert_scale(x_scale, y_scale); 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, y_scale * req_size);
shape = shape_;
tx = tx_;
ty = ty_;
}; letmut ft_shape = FT_Matrix {
xx: (shape.scale_x * 65536.0) as FT_Fixed,
xy: (shape.skew_x * -65536.0) as FT_Fixed,
yx: (shape.skew_y * -65536.0) as FT_Fixed,
yy: (shape.scale_y * 65536.0) as FT_Fixed,
}; // The delta vector for FT_Set_Transform is in units of 1/64 pixel. letmut ft_delta = FT_Vector {
x: (tx * 64.0) as FT_F26Dot6,
y: (ty * -64.0) as FT_F26Dot6,
}; unsafe {
FT_Set_Transform(face, &mut ft_shape, &mut ft_delta);
FT_Set_Char_Size(
face,
(req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6,
(req_size * y_scale * 64.0 + 0.5) as FT_F26Dot6, 0, 0,
)
}
};
if !succeeded(result) {
error!("Unable to set glyph size and transform: {}", result); //let raw_error = unsafe { FT_Error_String(result) }; //if !raw_error.is_ptr() { // error!("\tcode {:?}", CStr::from_ptr(raw_error)); //}
debug!( "\t[{}] for size {:?} and scale {:?} from font {:?}",
glyph.index(),
req_size,
(x_scale, y_scale),
font.font_key,
); return None;
}
result = unsafe { FT_Load_Glyph(face, glyph.index() as FT_UInt, load_flags as FT_Int32) }; if !succeeded(result) {
error!("Unable to load glyph: {}", result); //let raw_error = unsafe { FT_Error_String(result) }; //if !raw_error.is_ptr() { // error!("\tcode {:?}", CStr::from_ptr(raw_error)); //}
debug!( "\t[{}] with flags {:?} from font {:?}",
glyph.index(),
load_flags,
font.font_key,
); return None;
}
let slot = unsafe { (*face).glyph };
assert!(slot != ptr::null_mut());
if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
mozilla_glyphslot_embolden_less(slot);
}
let format = unsafe { (*slot).format }; match format {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => { let bitmap_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
Some((cached, slot, req_size as f32 / bitmap_size as f32))
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some((cached, slot, 1.0)),
_ => {
error!("Unsupported format");
debug!("format={:?}", format);
None
}
}
}
fn pad_bounding_box(font: &FontInstance, cbox: &mut FT_BBox) { // Apply extra pixel of padding for subpixel AA, due to the filter. if font.render_mode == FontRenderMode::Subpixel { // Using an LCD filter may add one full pixel to each side if support is built in. // As of FreeType 2.8.1, an LCD filter is always used regardless of settings // if support for the patent-encumbered LCD filter algorithms is not built in. // Thus, the only reasonable way to guess padding is to unconditonally add it if // subpixel AA is used. let lcd_extra_pixels = 1; let padding = (lcd_extra_pixels * 64) as FT_Pos; if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
cbox.yMin -= padding;
cbox.yMax += padding;
} else {
cbox.xMin -= padding;
cbox.xMax += padding;
}
}
}
// Get the bounding box for a glyph, accounting for sub-pixel positioning. fn get_bounding_box(
slot: FT_GlyphSlot,
font: &FontInstance,
glyph: &GlyphKey,
scale: f32,
) -> FT_BBox { // Get the estimated bounding box from FT (control points). letmut cbox = FT_BBox { xMin: 0, yMin: 0, xMax: 0, yMax: 0 };
// For spaces and other non-printable characters, early out. ifunsafe { (*slot).outline.n_contours } == 0 { return cbox;
}
Self::pad_bounding_box(font, &mut cbox);
// Offset the bounding box by subpixel positioning. // Convert to 26.6 fixed point format for FT. let (dx, dy) = font.get_subpx_offset(glyph); let (dx, dy) = (
(dx / scale as f64 * 64.0 + 0.5) as FT_Pos,
-(dy / scale as f64 * 64.0 + 0.5) as FT_Pos,
);
cbox.xMin += dx;
cbox.xMax += dx;
cbox.yMin += dy;
cbox.yMax += dy;
fn get_glyph_dimensions_impl(
slot: FT_GlyphSlot,
font: &FontInstance,
glyph: &GlyphKey,
scale: f32,
use_transform: bool,
) -> Option<GlyphDimensions> { let format = unsafe { (*slot).format }; let (mut left, mut top, mut width, mut height) = match format {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => { unsafe { (
(*slot).bitmap_left as i32,
(*slot).bitmap_top as i32,
(*slot).bitmap.width as i32,
(*slot).bitmap.rows as i32,
) }
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => { let cbox = Self::get_bounding_box(slot, font, glyph, scale);
(
(cbox.xMin >> 6) as i32,
(cbox.yMax >> 6) as i32,
((cbox.xMax - cbox.xMin) >> 6) as i32,
((cbox.yMax - cbox.yMin) >> 6) as i32,
)
}
_ => return None,
}; letmut advance = unsafe { (*slot).metrics.horiAdvance as f32 / 64.0 }; if use_transform { if scale != 1.0 { let x0 = left as f32 * scale; let x1 = width as f32 * scale + x0; let y1 = top as f32 * scale; let y0 = y1 - height as f32 * scale;
left = x0.round() as i32;
top = y1.round() as i32;
width = (x1.ceil() - x0.floor()) as i32;
height = (y1.ceil() - y0.floor()) as i32;
advance *= scale;
} // An outline glyph's cbox would have already been transformed inside FT_Load_Glyph, // so only handle bitmap glyphs which are not handled by FT_Load_Glyph. if format == FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP { if font.synthetic_italics.is_enabled() { let (skew_min, skew_max) = get_skew_bounds(
top - height as i32,
top,
font.synthetic_italics.to_skew(),
font.flags.contains(FontInstanceFlags::VERTICAL),
);
left += skew_min as i32;
width += (skew_max - skew_min) as i32;
} if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
mem::swap(&mut width, &mut height);
mem::swap(&mut left, &mut top);
left -= width as i32;
top += height as i32;
} if font.flags.contains(FontInstanceFlags::FLIP_X) {
left = -(left + width as i32);
} if font.flags.contains(FontInstanceFlags::FLIP_Y) {
top = -(top - height as i32);
}
}
}
Some(GlyphDimensions {
left,
top,
width,
height,
advance,
})
}
pubfn get_glyph_index(&mutself, font_key: FontKey, ch: char) -> Option<u32> { let cached = self.fonts.get(&font_key)?.lock().ok()?; let face = cached.face; unsafe { let idx = FT_Get_Char_Index(face, ch as _); if idx != 0 {
Some(idx)
} else {
None
}
}
}
fn choose_bitmap_size(face: FT_Face, requested_size: f64) -> FT_Error { letmut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size; letmut best_size = 0; let num_fixed_sizes = unsafe { (*face).num_fixed_sizes }; for i in1 .. num_fixed_sizes { // Distance is positive if strike is larger than desired size, // or negative if smaller. If previously a found smaller strike, // then prefer a larger strike. Otherwise, minimize distance. let dist = unsafe { *(*face).available_sizes.offset(i as isize) }.y_ppem as f64 / 64.0 - requested_size; if (best_dist < 0.0 && dist >= best_dist) || dist.abs() <= best_dist {
best_dist = dist;
best_size = i;
}
} unsafe { FT_Select_Size(face, best_size) }
}
pubfn prepare_font(font: &mut FontInstance) { let preblend_enabled = font.platform_options.map_or(false, |o| o.gamma >= 0 || o.enhanced_contrast > 0); match font.render_mode {
FontRenderMode::Mono => { // In mono mode the color of the font is irrelevant.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF); // Subpixel positioning is disabled in mono mode.
font.disable_subpixel_position();
}
FontRenderMode::Alpha => { if preblend_enabled {
font.color = font.color.luminance_color().quantize();
} else { // Color is unused if there is no preblend.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
}
}
FontRenderMode::Subpixel => { if preblend_enabled {
font.color = font.color.quantize();
} else { // Color is unused if there is no preblend.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
}
}
}
}
fn rasterize_glyph_outline(
slot: FT_GlyphSlot,
font: &FontInstance,
key: &GlyphKey,
scale: f32,
) -> bool { // Get the subpixel offsets in FT 26.6 format. let (dx, dy) = font.get_subpx_offset(key); let (dx, dy) = (
(dx / scale as f64 * 64.0 + 0.5) as FT_Pos,
-(dy / scale as f64 * 64.0 + 0.5) as FT_Pos,
);
// Move the outline curves to be at the origin, taking // into account the subpixel positioning. unsafe { let outline = &(*slot).outline; letmut cbox = FT_BBox { xMin: 0, yMin: 0, xMax: 0, yMax: 0 };
FT_Outline_Get_CBox(outline, &mut cbox); Self::pad_bounding_box(font, &mut cbox);
FT_Outline_Translate(
outline,
dx - ((cbox.xMin + dx) & !63),
dy - ((cbox.yMin + dy) & !63),
);
}
let render_mode = match font.render_mode {
FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
FontRenderMode::Subpixel => if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
FT_Render_Mode::FT_RENDER_MODE_LCD_V
} else {
FT_Render_Mode::FT_RENDER_MODE_LCD
},
}; let result = unsafe { FT_Render_Glyph(slot, render_mode) }; if !succeeded(result) {
error!("Unable to rasterize");
debug!( "{:?} with {:?}, {:?}",
key,
render_mode,
result
); false
} else { true
}
}
pubfn begin_rasterize(font: &FontInstance) { // The global LCD filter state is only used in subpixel rendering modes. if font.render_mode == FontRenderMode::Subpixel { letmut cache = FONT_CACHE.lock().unwrap(); let FontInstancePlatformOptions { lcd_filter, .. } = font.platform_options.unwrap_or_default(); // Check if the current LCD filter matches the requested one. if cache.lcd_filter != lcd_filter { // If the filter doesn't match, we have to wait for all other currently rasterizing threads // that may use the LCD filter state to finish before we can override it. while cache.lcd_filter_uses != 0 {
cache = LCD_FILTER_UNUSED.wait(cache).unwrap();
} // Finally set the LCD filter to the requested one now that the library is unused.
cache.lcd_filter = lcd_filter; let filter = match lcd_filter {
FontLCDFilter::None => FT_LcdFilter::FT_LCD_FILTER_NONE,
FontLCDFilter::Default => FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
FontLCDFilter::Light => FT_LcdFilter::FT_LCD_FILTER_LIGHT,
FontLCDFilter::Legacy => FT_LcdFilter::FT_LCD_FILTER_LEGACY,
}; unsafe { let result = FT_Library_SetLcdFilter(cache.lib, filter); // Setting the legacy filter may fail, so just use the default filter instead. if !succeeded(result) {
FT_Library_SetLcdFilter(cache.lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
}
}
}
cache.lcd_filter_uses += 1;
}
}
pubfn end_rasterize(font: &FontInstance) { if font.render_mode == FontRenderMode::Subpixel { letmut cache = FONT_CACHE.lock().unwrap(); // If this is the last use of the LCD filter, then signal that the LCD filter isn't used.
cache.lcd_filter_uses -= 1; if cache.lcd_filter_uses == 0 {
LCD_FILTER_UNUSED.notify_all();
}
}
}
// Get dimensions of the glyph, to see if we need to rasterize it. // Don't apply scaling to the dimensions, as the glyph cache needs to know the actual // footprint of the glyph. let dimensions = Self::get_glyph_dimensions_impl(slot, font, key, scale, false)
.ok_or(GlyphRasterError::LoadFailed)?; let GlyphDimensions { mut left, mut top, width, height, .. } = dimensions;
// For spaces and other non-printable characters, early out. if width == 0 || height == 0 { return Err(GlyphRasterError::LoadFailed);
}
let format = unsafe { (*slot).format }; match format {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => { if !Self::rasterize_glyph_outline(slot, font, key, scale) { return Err(GlyphRasterError::LoadFailed);
}
}
_ => {
error!("Unsupported format");
debug!("format={:?}", format); return Err(GlyphRasterError::LoadFailed);
}
};
debug!( "Rasterizing {:?} as {:?} with dimensions {:?}",
key,
font.render_mode,
dimensions
);
let bitmap = unsafe { &(*slot).bitmap }; let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) }; let (mut actual_width, mut actual_height) = match pixel_mode {
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
assert!(bitmap.width % 3 == 0);
((bitmap.width / 3) as usize, bitmap.rows as usize)
}
FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
assert!(bitmap.rows % 3 == 0);
(bitmap.width as usize, (bitmap.rows / 3) as usize)
}
FT_Pixel_Mode::FT_PIXEL_MODE_MONO |
FT_Pixel_Mode::FT_PIXEL_MODE_GRAY |
FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
(bitmap.width as usize, bitmap.rows as usize)
}
_ => panic!("Unsupported mode"),
};
// If we need padding, we will need to expand the buffer size. let (buffer_width, buffer_height, padding) = if font.use_texture_padding() {
(actual_width + 2, actual_height + 2, 1)
} else {
(actual_width, actual_height, 0)
};
Ok(RasterizedGlyph {
left: left as f32,
top: top as f32,
width: actual_width as i32,
height: actual_height as i32,
scale,
format: glyph_format,
bytes: final_buffer,
is_packed_glyph: false,
})
}
let gamma = gamma.min(400); let enhanced_contrast = enhanced_contrast.clamp(0, 100); let g = if gamma < 0 { 1.0 } else { gamma as f32 / 100.0 }; let c = enhanced_contrast as f32 / 100.0;
if gamma_luts.len() > 4 {
gamma_luts.clear();
} let gamma_lut = gamma_luts
.entry((gamma, enhanced_contrast))
.or_insert_with(|| GammaLut::new(c, g, g));
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.