зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1522787 - downscale bitmap glyphs to avoid filtering artifacts r=gw
Differential Revision: https://phabricator.services.mozilla.com/D22704
This commit is contained in:
Родитель
278fe39848
Коммит
18932c1c7f
|
@ -485,6 +485,89 @@ pub struct RasterizedGlyph {
|
||||||
pub bytes: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RasterizedGlyph {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn downscale_bitmap_if_required(&mut self, font: &FontInstance) {
|
||||||
|
// Check if the glyph is going to be downscaled in the shader. If the scaling is
|
||||||
|
// less than 0.5, that means bilinear filtering can't effectively filter the glyph
|
||||||
|
// without aliasing artifacts.
|
||||||
|
//
|
||||||
|
// Instead of fixing this by mipmapping the glyph cache texture, rather manually
|
||||||
|
// produce the appropriate mip level for individual glyphs where bilinear filtering
|
||||||
|
// will still produce acceptable results.
|
||||||
|
match self.format {
|
||||||
|
GlyphFormat::Bitmap | GlyphFormat::ColorBitmap => {},
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
|
||||||
|
let upscaled = x_scale.max(y_scale) as f32;
|
||||||
|
let mut new_scale = self.scale;
|
||||||
|
if new_scale * upscaled <= 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut steps = 0;
|
||||||
|
while new_scale * upscaled <= 0.5 {
|
||||||
|
new_scale *= 2.0;
|
||||||
|
steps += 1;
|
||||||
|
}
|
||||||
|
// If no mipping is necessary, just bail.
|
||||||
|
if steps == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the actual size of the mip level.
|
||||||
|
let new_width = (self.width as usize + (1 << steps) - 1) >> steps;
|
||||||
|
let new_height = (self.height as usize + (1 << steps) - 1) >> steps;
|
||||||
|
let mut new_bytes: Vec<u8> = Vec::with_capacity(new_width * new_height * 4);
|
||||||
|
|
||||||
|
// Produce destination pixels by applying a box filter to the source pixels.
|
||||||
|
// The box filter corresponds to how graphics drivers may generate mipmaps.
|
||||||
|
for y in 0 .. new_height {
|
||||||
|
for x in 0 .. new_width {
|
||||||
|
// Calculate the number of source samples that contribute to the destination pixel.
|
||||||
|
let src_y = y << steps;
|
||||||
|
let src_x = x << steps;
|
||||||
|
let y_samples = (1 << steps).min(self.height as usize - src_y);
|
||||||
|
let x_samples = (1 << steps).min(self.width as usize - src_x);
|
||||||
|
let num_samples = (x_samples * y_samples) as u32;
|
||||||
|
|
||||||
|
let mut src_idx = (src_y * self.width as usize + src_x) * 4;
|
||||||
|
// Initialize the accumulator with half an increment so that when later divided
|
||||||
|
// by the sample count, it will effectively round the accumulator to the nearest
|
||||||
|
// increment.
|
||||||
|
let mut accum = [num_samples / 2; 4];
|
||||||
|
// Accumulate all the contributing source sampless.
|
||||||
|
for _ in 0 .. y_samples {
|
||||||
|
for _ in 0 .. x_samples {
|
||||||
|
accum[0] += self.bytes[src_idx + 0] as u32;
|
||||||
|
accum[1] += self.bytes[src_idx + 1] as u32;
|
||||||
|
accum[2] += self.bytes[src_idx + 2] as u32;
|
||||||
|
accum[3] += self.bytes[src_idx + 3] as u32;
|
||||||
|
src_idx += 4;
|
||||||
|
}
|
||||||
|
src_idx += (self.width as usize - x_samples) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, divide by the sample count to get the mean value for the new pixel.
|
||||||
|
new_bytes.extend_from_slice(&[
|
||||||
|
(accum[0] / num_samples) as u8,
|
||||||
|
(accum[1] / num_samples) as u8,
|
||||||
|
(accum[2] / num_samples) as u8,
|
||||||
|
(accum[3] / num_samples) as u8,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix the bounds for the new glyph data.
|
||||||
|
self.top /= (1 << steps) as f32;
|
||||||
|
self.left /= (1 << steps) as f32;
|
||||||
|
self.width = new_width as i32;
|
||||||
|
self.height = new_height as i32;
|
||||||
|
self.scale = new_scale;
|
||||||
|
self.bytes = new_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FontContexts {
|
pub struct FontContexts {
|
||||||
// These worker are mostly accessed from their corresponding worker threads.
|
// These worker are mostly accessed from their corresponding worker threads.
|
||||||
// The goal is that there should be no noticeable contention on the mutexes.
|
// The goal is that there should be no noticeable contention on the mutexes.
|
||||||
|
|
|
@ -107,18 +107,22 @@ impl GlyphRasterizer {
|
||||||
.map(|key: &GlyphKey| {
|
.map(|key: &GlyphKey| {
|
||||||
profile_scope!("glyph-raster");
|
profile_scope!("glyph-raster");
|
||||||
let mut context = font_contexts.lock_current_context();
|
let mut context = font_contexts.lock_current_context();
|
||||||
let job = GlyphRasterJob {
|
let mut job = GlyphRasterJob {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
result: context.rasterize_glyph(&font, key),
|
result: context.rasterize_glyph(&font, key),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sanity check.
|
if let Ok(ref mut glyph) = job.result {
|
||||||
if let Ok(ref glyph) = job.result {
|
// Sanity check.
|
||||||
let bpp = 4; // We always render glyphs in 32 bits RGBA format.
|
let bpp = 4; // We always render glyphs in 32 bits RGBA format.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
glyph.bytes.len(),
|
glyph.bytes.len(),
|
||||||
bpp * (glyph.width * glyph.height) as usize
|
bpp * (glyph.width * glyph.height) as usize
|
||||||
);
|
);
|
||||||
|
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
||||||
|
|
||||||
|
// Check if the glyph has a bitmap that needs to be downscaled.
|
||||||
|
glyph.downscale_bitmap_if_required(&font);
|
||||||
}
|
}
|
||||||
|
|
||||||
job
|
job
|
||||||
|
@ -166,7 +170,6 @@ impl GlyphRasterizer {
|
||||||
GlyphCacheEntry::Blank
|
GlyphCacheEntry::Blank
|
||||||
}
|
}
|
||||||
Ok(glyph) => {
|
Ok(glyph) => {
|
||||||
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
|
||||||
let mut texture_cache_handle = TextureCacheHandle::invalid();
|
let mut texture_cache_handle = TextureCacheHandle::invalid();
|
||||||
texture_cache.request(&texture_cache_handle, gpu_cache);
|
texture_cache.request(&texture_cache_handle, gpu_cache);
|
||||||
texture_cache.update(
|
texture_cache.update(
|
||||||
|
|
Загрузка…
Ссылка в новой задаче