/* 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::{ColorF, ColorU, FontKey, FontRenderMode, FontSize, GlyphDimensions}; use api::{FontInstanceFlags, FontVariation, NativeFontHandle}; use core_foundation::data::CFData; use core_foundation::base::TCFType; use core_foundation::dictionary::CFDictionary; use core_foundation::number::{CFNumber}; use core_foundation::string::CFString; use core_foundation::url::{CFURL, kCFURLPOSIXPathStyle}; use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst}; use core_graphics::base::{kCGBitmapByteOrder32Little}; use core_graphics::color_space::CGColorSpace; use core_graphics::context::CGContext; use core_graphics::context::{CGBlendMode, CGTextDrawingMode}; use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize}; use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect}; use core_text::font::CTFont; use core_text::font_descriptor::{CTFontDescriptor, kCTFontDefaultOrientation}; use core_text::font_descriptor::{kCTFontURLAttribute, kCTFontVariationAttribute}; use core_text::font_manager; use euclid::default::Size2D; usecrate::gamma_lut::{ColorLut, GammaLut}; usecrate::rasterizer::{FontInstance, FontTransform, GlyphKey}; usecrate::rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph}; usecrate::types::FastHashMap; use std::collections::hash_map::Entry; use std::sync::Arc;
const INITIAL_CG_CONTEXT_SIDE_LENGTH: u32 = 32;
pubstruct FontContext {
ct_font_descs: FastHashMap<FontKey, CTFontDescriptor>, // Table mapping a sized font key with variations to its instantiated CoreText font.
ct_fonts: FastHashMap<(FontKey, FontSize, Vec<FontVariation>), CTFont>, #[allow(dead_code)]
graphics_context: GraphicsContext, #[allow(dead_code)]
gamma_lut: GammaLut,
}
// core text is safe to use on multiple threads and non-shareable resources are // all hidden inside their font context. unsafeimpl Send for FontContext {}
// There are a number of different OS prefs that control whether or not // requesting font smoothing actually results in subpixel AA. This gets even // murkier in newer macOS versions that deprecate subpixel AA, with the prefs // potentially interacting and overriding each other. In an attempt to future- // proof things against any new prefs or interpretation of those prefs in // future macOS versions, we do a check here to request font smoothing and see // what result it actually gives us much like Skia does. We need to check for // each of three potential results and process them in the font backend in // distinct ways: // 1) subpixel AA (differing RGB channels) with dilation // 2) grayscale AA (matching RGB channels) with dilation, a compatibility mode // 3) grayscale AA without dilation as if font smoothing was not requested // We can discern between case 1 and the rest by checking if the subpixels differ. // We can discern between cases 2 and 3 by rendering with and without smoothing // and comparing the two to determine if there was some dilation. // This returns the actual FontRenderMode needed to support each case, if any. fn determine_font_smoothing_mode() -> Option<FontRenderMode> { letmut smooth_context = CGContext::create_bitmap_context(
None, 12, 12, 8, 12 * 4,
&CGColorSpace::create_device_rgb(),
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
);
smooth_context.set_should_smooth_fonts(true);
smooth_context.set_should_antialias(true);
smooth_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); letmut gray_context = CGContext::create_bitmap_context(
None, 12, 12, 8, 12 * 4,
&CGColorSpace::create_device_rgb(),
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
);
gray_context.set_should_smooth_fonts(false);
gray_context.set_should_antialias(true);
gray_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
// Autorelease pool for CTFont
objc::rc::autoreleasepool(|| { // Lucida Grande 12 is the default fallback font in Firefox let ct_font = core_text::font::new_from_name("Lucida Grande", 12.).unwrap(); let point = CGPoint { x: 0., y: 0. }; let glyph = 'X'as CGGlyph;
ct_font.draw_glyphs(&[glyph], &[point], smooth_context.clone());
ct_font.draw_glyphs(&[glyph], &[point], gray_context.clone());
});
// We cache the font smoothing mode globally, rather than storing it in each FontContext, // to avoid having to determine this redundantly in each context and to avoid needing to // lock them to access this setting in prepare_font.
lazy_static! { staticref FONT_SMOOTHING_MODE: Option<FontRenderMode> = determine_font_smoothing_mode();
}
if bounds.origin.x.is_nan() || bounds.origin.y.is_nan() || bounds.size.width.is_nan() ||
bounds.size.height.is_nan()
{ // If an unexpected glyph index is requested, core text will return NaN values // which causes us to do bad thing as the value is cast into an integer and // overflow when expanding the bounds a few lines below. // Instead we are better off returning zero-sized metrics because this special // case is handled by the callers of this method. return GlyphMetrics {
rasterized_left: 0,
rasterized_width: 0,
rasterized_height: 0,
rasterized_ascent: 0,
rasterized_descent: 0,
advance: 0.0,
};
}
// First round out to pixel boundaries // CG Origin is bottom left letmut left = bounds.origin.x.floor() as i32; letmut bottom = bounds.origin.y.floor() as i32; letmut right = (bounds.origin.x + bounds.size.width + x_offset).ceil() as i32; letmut top = (bounds.origin.y + bounds.size.height + y_offset).ceil() as i32;
// Expand the bounds by 1 pixel, to give CG room for anti-aliasing. // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset // is not currently known, as CG dilates the outlines by some percentage. // This is taken from Skia.
left -= 1;
bottom -= 1;
right += 1;
top += 1;
let width = right - left; let height = top - bottom;
fn new_ct_font_with_variations(ct_font_desc: &CTFontDescriptor, size: f64, variations: &[FontVariation]) -> CTFont { let ct_font = core_text::font::new_from_descriptor(ct_font_desc, size); if variations.is_empty() { return ct_font;
} letmut vals: Vec<(CFNumber, CFNumber)> = Vec::with_capacity(variations.len() as usize); for variation in variations {
vals.push((CFNumber::from(variation.tag as i64), CFNumber::from(variation.value as f64)));
} if vals.is_empty() { return ct_font;
} let vals_dict = CFDictionary::from_CFType_pairs(&vals); let variation_attribute = unsafe { CFString::wrap_under_get_rule(kCTFontVariationAttribute) }; let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute, vals_dict)]); let ct_var_font_desc = ct_font.copy_descriptor().create_copy_with_attributes(attrs_dict.to_untyped()).unwrap();
core_text::font::new_from_descriptor(&ct_var_font_desc, size)
}
// We rely on Gecko to determine whether the font may have color glyphs to avoid // needing to load the font ahead of time to query its symbolic traits. fn is_bitmap_font(font: &FontInstance) -> bool {
font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS)
}
// There's no way great way to go from a CGFont to a CTFontDescriptor // We could use the postscript name but that doesn't work for the // system UI fonts on newer macOS versions. Instead we create a CTFont // and use the descriptor for that. Normally we'd try to avoid new_from_CGFont // because that adds the CGFont to the descriptor cache which can keep the CGFont // around for a long time, but that should be ok for non-web (native) fonts. let cf_name = CFString::new(&native_font_handle.name);
// For "hidden" system fonts, whose names start with a period, // we can't instantiate CTFonts via a descriptor. We're really // supposed to use CTFontCreateUIFontForLanguage, but for now // we just use the CGFont. letmut desc = if native_font_handle.name.starts_with('.') { let cg_font = match CGFont::from_name(&cf_name) {
Ok(cg_font) => cg_font,
Err(_) => { // If for some reason we failed to load a font descriptor, then our // only options are to either abort or substitute a fallback font. // It is preferable to use a fallback font instead so that rendering // can at least still proceed in some fashion without erroring. // Lucida Grande is the fallback font in Gecko, so use that here.
CGFont::from_name(&CFString::from_static_string("Lucida Grande"))
.expect("couldn't find font with postscript name and couldn't load fallback font")
}
};
core_text::font::new_from_CGFont(&cg_font, 0.).copy_descriptor()
} else {
core_text::font_descriptor::new_from_postscript_name(&cf_name)
};
// If the NativeFontHandle includes a file path, add this to the descriptor // to disambiguate cases where multiple installed fonts have the same psname. if native_font_handle.path.len() > 0 { let cf_path = CFString::new(&native_font_handle.path); let url_attribute = unsafe { CFString::wrap_under_get_rule(kCTFontURLAttribute) }; let attrs = CFDictionary::from_CFType_pairs(&[
(url_attribute, CFURL::from_file_system_path(cf_path, kCFURLPOSIXPathStyle, false)),
]); iflet Ok(desc_with_path) = desc.create_copy_with_attributes(attrs.to_untyped()) {
desc = desc_with_path;
}
}
pubfn delete_font_instance(&mutself, instance: &FontInstance) { // Remove the CoreText font corresponding to this instance. let size = FontSize::from_f64_px(instance.get_transformed_size()); self.ct_fonts.remove(&(instance.font_key, size, instance.variations.clone()));
}
pubfn get_glyph_index(&mutself, font_key: FontKey, ch: char) -> Option<u32> { let character = ch as u16; letmut glyph = 0;
self.get_ct_font(font_key, 16.0, &[])
.and_then(|ct_font| { unsafe { let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
if result {
Some(glyph as u32)
} else {
None
}
}
})
}
pubfn get_glyph_dimensions(
&mutself,
font: &FontInstance,
key: &GlyphKey,
) -> Option<GlyphDimensions> { let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let size = font.size.to_f64_px() * y_scale; self.get_ct_font(font.font_key, size, &font.variations)
.and_then(|ct_font| { let glyph = key.index() as CGGlyph; let bitmap = is_bitmap_font(font); let (mut shape, (x_offset, y_offset)) = if bitmap {
(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, size);
shape = shape_;
tx = tx_;
ty = ty_;
} let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) {
Some(CGAffineTransform {
a: shape.scale_x as f64,
b: -shape.skew_y as f64,
c: -shape.skew_x as f64,
d: shape.scale_y as f64,
tx: tx,
ty: -ty,
})
} else {
None
}; let (strike_scale, pixel_step) = if bitmap {
(y_scale, 1.0)
} else {
(x_scale, y_scale / x_scale)
}; let extra_strikes = font.get_extra_strikes(
FontInstanceFlags::SYNTHETIC_BOLD | FontInstanceFlags::MULTISTRIKE_BOLD,
strike_scale,
); let metrics = get_glyph_metrics(
&ct_font,
transform.as_ref(),
glyph,
x_offset,
y_offset,
extra_strikes as f64 * pixel_step,
); if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
None
} else {
Some(GlyphDimensions {
left: metrics.rasterized_left,
top: metrics.rasterized_ascent,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
advance: metrics.advance,
})
}
})
}
// Assumes the pixels here are linear values from CG fn gamma_correct_pixels(
&self,
pixels: &mut Vec<u8>,
render_mode: FontRenderMode,
color: ColorU,
) { let ColorU {r, g, b, a} = color; let smooth_color = match *FONT_SMOOTHING_MODE { // Use Skia's gamma approximation for subpixel smoothing of 3/4.
Some(FontRenderMode::Subpixel) => ColorU::new(r - r / 4, g - g / 4, b - b / 4, a), // Use Skia's gamma approximation for grayscale smoothing of 1/2.
Some(FontRenderMode::Alpha) => ColorU::new(r / 2, g / 2, b / 2, a),
_ => color,
};
// Then convert back to gamma corrected values. match render_mode {
FontRenderMode::Alpha => { self.gamma_lut.preblend_grayscale(pixels, smooth_color);
}
FontRenderMode::Subpixel => { self.gamma_lut.preblend(pixels, smooth_color);
}
_ => {} // Again, give mono untouched since only the alpha matters.
}
}
#[allow(dead_code)] fn print_glyph_data(&mutself, data: &[u8], width: usize, height: usize) { // Rust doesn't have step_by support on stable :(
debug!("Width is: {:?} height: {:?}", width, height); for i in0 .. height { let current_height = i * width * 4;
for pixel in data[current_height .. current_height + (width * 4)].chunks(4) { let b = pixel[0]; let g = pixel[1]; let r = pixel[2]; let a = pixel[3];
debug!("({}, {}, {}, {}) ", r, g, b, a);
}
}
}
pubfn prepare_font(font: &mut FontInstance) { if is_bitmap_font(font) { // Render mode is ignored for bitmap fonts. Also, avoid normalizing the color // in case CoreText needs the current color for rendering glyph color layers.
font.render_mode = FontRenderMode::Mono;
font.disable_subpixel_position(); return;
} // Sanitize the render mode for font smoothing. If font smoothing is supported, // then we just need to ensure the render mode is limited to what is supported. // If font smoothing is actually disabled, then we need to fall back to grayscale. if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) ||
font.render_mode == FontRenderMode::Subpixel { match *FONT_SMOOTHING_MODE {
Some(mode) => {
font.render_mode = font.render_mode.limit_by(mode);
font.flags.insert(FontInstanceFlags::FONT_SMOOTHING);
}
None => {
font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
font.flags.remove(FontInstanceFlags::FONT_SMOOTHING);
}
}
} 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 = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) {
font.color.luminance_color().quantize()
} else {
ColorU::new(255, 255, 255, 255)
};
}
FontRenderMode::Subpixel => {
font.color = font.color.quantize();
}
}
}
pubfn begin_rasterize(_font: &FontInstance) {
}
pubfn end_rasterize(_font: &FontInstance) {
}
pubfn rasterize_glyph(&mutself, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
objc::rc::autoreleasepool(|| { let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let size = font.size.to_f64_px() * y_scale; let ct_font = self.get_ct_font(font.font_key, size, &font.variations).ok_or(GlyphRasterError::LoadFailed)?; let glyph_type = if is_bitmap_font(font) {
GlyphType::Bitmap
} else {
GlyphType::Vector
};
let (mut shape, (x_offset, y_offset)) = match glyph_type {
GlyphType::Bitmap => (FontTransform::identity(), (0.0, 0.0)),
GlyphType::Vector => {
(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, size);
shape = shape_;
tx = tx_;
ty = ty_;
} let transform = if !shape.is_identity() || (tx, ty) != (0.0, 0.0) {
Some(CGAffineTransform {
a: shape.scale_x as f64,
b: -shape.skew_y as f64,
c: -shape.skew_x as f64,
d: shape.scale_y as f64,
tx: tx,
ty: -ty,
})
} else {
None
};
let glyph = key.index() as CGGlyph; let (strike_scale, pixel_step) = if glyph_type == GlyphType::Bitmap {
(y_scale, 1.0)
} else {
(x_scale, y_scale / x_scale)
}; let extra_strikes = font.get_extra_strikes(
FontInstanceFlags::SYNTHETIC_BOLD | FontInstanceFlags::MULTISTRIKE_BOLD,
strike_scale,
); let metrics = get_glyph_metrics(
&ct_font,
transform.as_ref(),
glyph,
x_offset,
y_offset,
extra_strikes as f64 * pixel_step,
); if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 { return Err(GlyphRasterError::LoadFailed);
}
let raster_size = Size2D::new(
metrics.rasterized_width as u32,
metrics.rasterized_height as u32
);
// If the font render mode is Alpha, we support two different ways to // compute the grayscale mask, depending on the value of the platform // options' font_smoothing flag: // - Alpha + smoothing: // We will recover a grayscale mask from a subpixel rasterization, in // such a way that the result looks as close to subpixel text // blending as we can make it. This involves gamma correction, // luminance computations and preblending based on the text color, // just like with the Subpixel render mode. // - Alpha without smoothing: // We will ask CoreGraphics to rasterize the text with font_smoothing // off. This will cause it to use grayscale anti-aliasing with // comparatively thin text. This method of text rendering is not // gamma-aware. // // For subpixel rasterization, starting with macOS 10.11, CoreGraphics // uses different glyph dilation based on the text color. Bright text // uses less font dilation (looks thinner) than dark text. // As a consequence, when we ask CG to rasterize with subpixel AA, we // will render white-on-black text as opposed to black-on-white text if // the text color brightness exceeds a certain threshold. This applies // to both the Subpixel and the "Alpha + smoothing" modes, but not to // the "Alpha without smoothing" and Mono modes. // // Fonts with color glyphs may, depending on the state within per-glyph // table data, require the current font color to determine the output // color. For such fonts we must thus supply the current font color just // in case it is necessary. let use_font_smoothing = font.flags.contains(FontInstanceFlags::FONT_SMOOTHING); let (antialias, smooth, text_color, bg_color) = match glyph_type {
GlyphType::Bitmap => (true, false, ColorF::from(font.color), ColorF::TRANSPARENT),
GlyphType::Vector => { match (font.render_mode, use_font_smoothing) {
(FontRenderMode::Subpixel, _) |
(FontRenderMode::Alpha, true) => (true, true, ColorF::BLACK, ColorF::WHITE),
(FontRenderMode::Alpha, false) => (true, false, ColorF::BLACK, ColorF::WHITE),
(FontRenderMode::Mono, _) => (false, false, ColorF::BLACK, ColorF::WHITE),
}
}
};
{ let cg_context = self.graphics_context.get_context(&raster_size, glyph_type);
// These are always true in Gecko, even for non-AA fonts
cg_context.set_allows_font_subpixel_positioning(true);
cg_context.set_should_subpixel_position_fonts(true);
// Don't quantize because we're doing it already.
cg_context.set_allows_font_subpixel_quantization(false);
cg_context.set_should_subpixel_quantize_fonts(false);
// Fill the background. This could be opaque white, opaque black, or // transparency.
cg_context.set_rgb_fill_color(
bg_color.r.into(),
bg_color.g.into(),
bg_color.b.into(),
bg_color.a.into(),
); let rect = CGRect {
origin: CGPoint { x: 0.0, y: 0.0 },
size: CGSize {
width: metrics.rasterized_width as f64,
height: metrics.rasterized_height as f64,
},
};
// Make sure we use the Copy blend mode, or else we'll get the Porter-Duff OVER // operator, which can't clear to the transparent color!
cg_context.set_blend_mode(CGBlendMode::Copy);
cg_context.fill_rect(rect);
cg_context.set_blend_mode(CGBlendMode::Normal);
// Set the text color and draw the glyphs.
cg_context.set_rgb_fill_color(
text_color.r.into(),
text_color.g.into(),
text_color.b.into(), 1.0,
);
cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
// CG Origin is bottom left, WR is top left. Need -y offset letmut draw_origin = CGPoint {
x: -metrics.rasterized_left as f64 + x_offset + tx,
y: metrics.rasterized_descent as f64 - y_offset - ty,
};
draw_origin = draw_origin.apply_transform(&transform.invert());
} else { // Make sure to reset this because some previous glyph rasterization might have // changed it.
cg_context.set_text_matrix(&CG_AFFINE_TRANSFORM_IDENTITY);
}
// We'd like to render all the strikes in a single ct_font.draw_glyphs call, // passing an array of glyph IDs and an array of origins, but unfortunately // with some fonts, Core Text may inappropriately pixel-snap the rasterization, // such that the strikes overprint instead of being offset. Rendering the // strikes with individual draw_glyphs calls avoids this. // (See https://bugzilla.mozilla.org/show_bug.cgi?id=1633397 for details.) for i in1 ..= extra_strikes { let origin = CGPoint {
x: draw_origin.x + i as f64 * pixel_step,
y: draw_origin.y,
};
ct_font.draw_glyphs(&[glyph], &[origin], cg_context.clone());
}
}
if glyph_type == GlyphType::Vector { // We rendered text into an opaque surface. The code below needs to // ignore the current value of each pixel's alpha channel. But it's // allowed to write to the alpha channel, because we're done calling // CG functions now.
if smooth { // Convert to linear space for subpixel AA. // We explicitly do not do this for grayscale AA ("Alpha without // smoothing" or Mono) because those rendering modes are not // gamma-aware in CoreGraphics. self.gamma_lut.coregraphics_convert_to_linear(
&mut rasterized_pixels,
);
}
for pixel in rasterized_pixels.chunks_mut(4) {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
// Set alpha to the value of the green channel. For grayscale // text, all three channels have the same value anyway. // For subpixel text, the mask's alpha only makes a difference // when computing the destination alpha on destination pixels // that are not completely opaque. Picking an alpha value // that's somehow based on the mask at least ensures that text // blending doesn't modify the destination alpha on pixels where // the mask is entirely zero.
pixel[3] = pixel[1];
}
if smooth { // Convert back from linear space into device space, and perform // some "preblending" based on the text color. // In Alpha + smoothing mode, this will also convert subpixel AA // into grayscale AA. self.gamma_correct_pixels(
&mut rasterized_pixels,
font.render_mode,
font.color,
);
}
}
Ok(RasterizedGlyph {
left: metrics.rasterized_left as f32,
top: metrics.rasterized_ascent as f32,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
scale: match glyph_type {
GlyphType::Bitmap => y_scale.recip() as f32,
GlyphType::Vector => 1.0,
},
format: match glyph_type {
GlyphType::Bitmap => GlyphFormat::ColorBitmap,
GlyphType::Vector => font.get_glyph_format(),
},
bytes: rasterized_pixels,
is_packed_glyph: false,
})})
}
}
#[allow(dead_code)] fn get_rasterized_pixels(&mutself, size: &Size2D<u32>, glyph_type: GlyphType)
-> Vec<u8> { let (cached_context, cached_size) = match glyph_type {
GlyphType::Vector => (&mutself.vector_context, &self.vector_context_size),
GlyphType::Bitmap => (&mutself.bitmap_context, &self.bitmap_context_size),
}; let cached_data = cached_context.data(); let cached_stride = cached_size.width as usize * 4;
let result_len = size.width as usize * size.height as usize * 4; letmut result = Vec::with_capacity(result_len); for y in (cached_size.height - size.height)..cached_size.height { let cached_start = y as usize * cached_stride; let cached_end = cached_start + size.width as usize * 4;
result.extend_from_slice(&cached_data[cached_start..cached_end]);
}
debug_assert_eq!(result.len(), result_len);
result
}
fn create_cg_context(size: &Size2D<u32>, glyph_type: GlyphType) -> CGContext { // The result of rasterization, in all render modes, is going to be a // BGRA surface with white text on transparency using premultiplied // alpha. For subpixel text, the RGB values will be the mask value for // the individual components. For bitmap glyphs, the RGB values will be // the (premultiplied) color of the pixel. For Alpha and Mono, each // pixel will have R==G==B==A at the end of this function. // We access the color channels in little-endian order. // The CGContext will create and own our pixel buffer. // In the non-Bitmap cases, we will ask CoreGraphics to draw text onto // an opaque background. In order to hit the most efficient path in CG // for this, we will tell CG that the CGContext is opaque, by passing // an "[...]AlphaNone[...]" context flag. This creates a slight // contradiction to the way we use the buffer after CG is done with it, // because we will convert it into text-on-transparency. But that's ok; // we still get four bytes per pixel and CG won't mess with the alpha // channel after we've stopped calling CG functions. We just need to // make sure that we don't look at the alpha values of the pixels that // we get from CG, and compute our own alpha value only from RGB. // Note that CG requires kCGBitmapByteOrder32Little in order to do // subpixel AA at all (which we need it to do in both Subpixel and // Alpha+smoothing mode). But little-endian is what we want anyway, so // this works out nicely. let color_type = match glyph_type {
GlyphType::Vector => kCGImageAlphaNoneSkipFirst,
GlyphType::Bitmap => kCGImageAlphaPremultipliedFirst,
};
CGContext::create_bitmap_context(None,
size.width as usize,
size.height as usize, 8,
size.width as usize * 4,
&CGColorSpace::create_device_rgb(),
kCGBitmapByteOrder32Little | color_type)
}
}
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.