mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1587094 - Support MULTISTRIKE_BOLD font instance flag for WebRender. r=jfkthame
Differential Revision: https://phabricator.services.mozilla.com/D137725
This commit is contained in:
parent
899ca3c109
commit
5136d81df6
@ -604,8 +604,8 @@ impl FontInstance {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_extra_strikes(&self, x_scale: f64) -> usize {
|
||||
if self.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
|
||||
pub fn get_extra_strikes(&self, flags: FontInstanceFlags, x_scale: f64) -> usize {
|
||||
if self.flags.intersects(flags) {
|
||||
let mut bold_offset = self.size.to_f64_px() / 48.0;
|
||||
if bold_offset < 1.0 {
|
||||
bold_offset = 0.25 + 0.75 * bold_offset;
|
||||
@ -772,6 +772,119 @@ impl GlyphFormat {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
fn blend_strike_pixel(dest: u8, src: u32, src_alpha: u32) -> u8 {
|
||||
// Assume premultiplied alpha such that src and dest are already multiplied
|
||||
// by their respective alpha values and in range 0..=255. The rounded over
|
||||
// blend is then (src * 255 + dest * (255 - src_alpha) + 128) / 255.
|
||||
// We approximate (x + 128) / 255 as (x + 128 + ((x + 128) >> 8)) >> 8.
|
||||
let x = src * 255 + dest as u32 * (255 - src_alpha) + 128;
|
||||
((x + (x >> 8)) >> 8) as u8
|
||||
}
|
||||
|
||||
// Blends a single strike at a given offset into a destination buffer, assuming
|
||||
// the destination has been allocated with enough extra space to accommodate the
|
||||
// offset.
|
||||
#[allow(dead_code)]
|
||||
fn blend_strike(
|
||||
dest_bitmap: &mut [u8],
|
||||
src_bitmap: &[u8],
|
||||
width: usize,
|
||||
height: usize,
|
||||
subpixel_mask: bool,
|
||||
offset: f64,
|
||||
) {
|
||||
let dest_stride = dest_bitmap.len() / height;
|
||||
let src_stride = width * 4;
|
||||
let offset_integer = offset.floor() as usize * 4;
|
||||
let offset_fract = (offset.fract() * 256.0) as u32;
|
||||
for (src_row, dest_row) in src_bitmap.chunks(src_stride).zip(dest_bitmap.chunks_mut(dest_stride)) {
|
||||
let mut prev_px = [0u32; 4];
|
||||
let dest_row_offset = &mut dest_row[offset_integer .. offset_integer + src_stride];
|
||||
for (src, dest) in src_row.chunks(4).zip(dest_row_offset.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 fractional offset.
|
||||
let next_px = [px[0] * offset_fract,
|
||||
px[1] * offset_fract,
|
||||
px[2] * offset_fract,
|
||||
px[3] * offset_fract];
|
||||
let offset_px = [(((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8,
|
||||
(((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8,
|
||||
(((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8,
|
||||
(((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8];
|
||||
if subpixel_mask {
|
||||
// Subpixel masks assume each component is an independent weight.
|
||||
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[0]);
|
||||
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[1]);
|
||||
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[2]);
|
||||
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
|
||||
} else {
|
||||
// Otherwise assume we have a premultiplied alpha BGRA value.
|
||||
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[3]);
|
||||
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[3]);
|
||||
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[3]);
|
||||
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
|
||||
}
|
||||
// Save the remainder for blending onto the next pixel.
|
||||
prev_px = next_px;
|
||||
}
|
||||
if offset_fract > 0 {
|
||||
// When there is fractional offset, there will be a remaining value
|
||||
// from the previous pixel but no next pixel, so just use that.
|
||||
let dest = &mut dest_row[offset_integer + src_stride .. ];
|
||||
let offset_px = [(prev_px[0] + 128) >> 8,
|
||||
(prev_px[1] + 128) >> 8,
|
||||
(prev_px[2] + 128) >> 8,
|
||||
(prev_px[3] + 128) >> 8];
|
||||
if subpixel_mask {
|
||||
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[0]);
|
||||
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[1]);
|
||||
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[2]);
|
||||
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
|
||||
} else {
|
||||
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[3]);
|
||||
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[3]);
|
||||
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[3]);
|
||||
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies multistrike bold to a source bitmap. This assumes the source bitmap
|
||||
// is a tighly packed slice of BGRA pixel values of exactly the specified width
|
||||
// and height. The specified extra strikes and pixel step control where to put
|
||||
// each strike. The pixel step is allowed to have a fractional offset and does
|
||||
// not strictly need to be integer.
|
||||
#[allow(dead_code)]
|
||||
pub fn apply_multistrike_bold(
|
||||
src_bitmap: &[u8],
|
||||
width: usize,
|
||||
height: usize,
|
||||
subpixel_mask: bool,
|
||||
extra_strikes: usize,
|
||||
pixel_step: f64,
|
||||
) -> (Vec<u8>, usize) {
|
||||
let src_stride = width * 4;
|
||||
// The amount of extra width added to the bitmap from the extra strikes.
|
||||
let extra_width = (extra_strikes as f64 * pixel_step).ceil() as usize;
|
||||
let dest_width = width + extra_width;
|
||||
let dest_stride = dest_width * 4;
|
||||
// Zero out the initial bitmap so any extra width is cleared.
|
||||
let mut dest_bitmap = vec![0u8; dest_stride * height];
|
||||
for (src_row, dest_row) in src_bitmap.chunks(src_stride).zip(dest_bitmap.chunks_mut(dest_stride)) {
|
||||
// Copy the initial bitmap strike rows directly from the source.
|
||||
dest_row[0 .. src_stride].copy_from_slice(src_row);
|
||||
}
|
||||
// Finally blend each extra strike in turn.
|
||||
for i in 1 ..= extra_strikes {
|
||||
let offset = i as f64 * pixel_step;
|
||||
blend_strike(&mut dest_bitmap, src_bitmap, width, height, subpixel_mask, offset);
|
||||
}
|
||||
(dest_bitmap, dest_width)
|
||||
}
|
||||
|
||||
pub struct RasterizedGlyph {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
|
@ -507,7 +507,10 @@ impl FontContext {
|
||||
} else {
|
||||
(x_scale, y_scale / x_scale)
|
||||
};
|
||||
let extra_strikes = font.get_extra_strikes(strike_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(),
|
||||
@ -673,8 +676,10 @@ impl FontContext {
|
||||
} else {
|
||||
(x_scale, y_scale / x_scale)
|
||||
};
|
||||
|
||||
let extra_strikes = font.get_extra_strikes(strike_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(),
|
||||
|
@ -5,11 +5,11 @@
|
||||
use api::{FontInstanceFlags, FontKey, FontRenderMode, FontVariation};
|
||||
use api::{ColorU, GlyphDimensions, NativeFontHandle};
|
||||
use dwrote;
|
||||
use crate::gamma_lut::ColorLut;
|
||||
use crate::gamma_lut::{ColorLut, GammaLut};
|
||||
use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, ResourceCacheError};
|
||||
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
|
||||
use crate::gamma_lut::GammaLut;
|
||||
use crate::glyph_rasterizer::apply_multistrike_bold;
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, ResourceCacheError};
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::{Hash, Hasher};
|
||||
@ -377,7 +377,7 @@ impl FontContext {
|
||||
font: &FontInstance,
|
||||
key: &GlyphKey,
|
||||
) -> Option<GlyphDimensions> {
|
||||
let (size, _, bitmaps, transform) = Self::get_glyph_parameters(font, key);
|
||||
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;
|
||||
@ -389,6 +389,14 @@ impl FontContext {
|
||||
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()
|
||||
@ -401,9 +409,9 @@ impl FontContext {
|
||||
GlyphDimensions {
|
||||
left: bounds.left,
|
||||
top: -bounds.top,
|
||||
width,
|
||||
width: width + extra_width.ceil() as i32,
|
||||
height,
|
||||
advance,
|
||||
advance: advance + extra_width as f32,
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -418,14 +426,9 @@ impl FontContext {
|
||||
render_mode: FontRenderMode,
|
||||
bitmaps: bool,
|
||||
subpixel_bgr: bool,
|
||||
texture_padding: bool,
|
||||
) -> Vec<u8> {
|
||||
let (buffer_width, buffer_height, padding) = if texture_padding {
|
||||
(width + 2, height + 2, 1)
|
||||
} else {
|
||||
(width, height, 0)
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
@ -445,6 +448,7 @@ impl FontContext {
|
||||
bgra_pixels[offset + 3] = alpha;
|
||||
}
|
||||
}
|
||||
(bgra_pixels, false)
|
||||
}
|
||||
(_, FontRenderMode::Subpixel, false) => {
|
||||
assert!(width * height * 3 == pixels.len());
|
||||
@ -464,6 +468,7 @@ impl FontContext {
|
||||
bgra_pixels[offset + 3] = 0xff;
|
||||
}
|
||||
}
|
||||
(bgra_pixels, true)
|
||||
}
|
||||
_ => {
|
||||
assert!(width * height * 3 == pixels.len());
|
||||
@ -481,9 +486,9 @@ impl FontContext {
|
||||
bgra_pixels[offset + 3] = alpha;
|
||||
}
|
||||
}
|
||||
(bgra_pixels, false)
|
||||
}
|
||||
};
|
||||
bgra_pixels
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_font(font: &mut FontInstance) {
|
||||
@ -503,8 +508,9 @@ impl FontContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_glyph_parameters(font: &FontInstance, key: &GlyphKey) -> (f32, f64, bool, Option<dwrote::DWRITE_MATRIX>) {
|
||||
let (_, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
|
||||
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 {
|
||||
@ -542,14 +548,14 @@ impl FontContext {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(scaled_size as f32, y_scale, bitmaps, transform)
|
||||
(scaled_size as f32, x_scale, y_scale, bitmaps, transform)
|
||||
}
|
||||
|
||||
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
|
||||
let (size, y_scale, bitmaps, transform) = Self::get_glyph_parameters(font, key);
|
||||
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 width = (bounds.right - bounds.left) as i32;
|
||||
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
|
||||
@ -558,10 +564,37 @@ impl FontContext {
|
||||
}
|
||||
|
||||
let pixels = analysis.create_alpha_texture(texture_type, bounds).or(Err(GlyphRasterError::LoadFailed))?;
|
||||
let mut bgra_pixels = self.convert_to_bgra(&pixels, width as usize, height as usize,
|
||||
texture_type, font.render_mode, bitmaps,
|
||||
font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR),
|
||||
font.use_texture_padding());
|
||||
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 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();
|
||||
@ -573,11 +606,10 @@ impl FontContext {
|
||||
gamma as f32 / 100.0,
|
||||
gamma as f32 / 100.0,
|
||||
));
|
||||
if bitmaps || texture_type == dwrote::DWRITE_TEXTURE_ALIASED_1x1 ||
|
||||
font.render_mode != FontRenderMode::Subpixel {
|
||||
gamma_lut.preblend(&mut bgra_pixels, font.color);
|
||||
} else {
|
||||
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 {
|
||||
@ -588,7 +620,6 @@ impl FontContext {
|
||||
font.get_glyph_format()
|
||||
};
|
||||
|
||||
let padding = if font.use_texture_padding() { 1 } else { 0 };
|
||||
Ok(RasterizedGlyph {
|
||||
left: (bounds.left - padding) as f32,
|
||||
top: (-bounds.top + padding) as f32,
|
||||
|
@ -359,6 +359,7 @@ bitflags! {
|
||||
#[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
|
||||
pub struct FontInstanceFlags: u32 {
|
||||
// Common flags
|
||||
// Use native synthetic bold, if supported.
|
||||
const SYNTHETIC_BOLD = 1 << 1;
|
||||
const EMBEDDED_BITMAPS = 1 << 2;
|
||||
const SUBPIXEL_BGR = 1 << 3;
|
||||
@ -367,6 +368,8 @@ bitflags! {
|
||||
const FLIP_Y = 1 << 6;
|
||||
const SUBPIXEL_POSITION = 1 << 7;
|
||||
const VERTICAL = 1 << 8;
|
||||
// Explicitly use multi-strike bold emulation.
|
||||
const MULTISTRIKE_BOLD = 1 << 9;
|
||||
|
||||
// Internal flags
|
||||
const TRANSFORM_GLYPHS = 1 << 12;
|
||||
|
Loading…
Reference in New Issue
Block a user