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:
Lee Salzman 2022-02-05 01:09:00 +00:00
parent 899ca3c109
commit 5136d81df6
4 changed files with 187 additions and 35 deletions

View File

@ -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,

View File

@ -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(),

View File

@ -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,

View File

@ -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;