Skip to content

Use glyph metrics to calculate text run bounds and offsets. #514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions webrender/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,9 +879,4 @@ impl Frame {
}
layers_bouncing_back
}

/// Returns the id which the next Frame would possess
pub fn next_frame_id(&self) -> FrameId {
FrameId(self.id.0 + 1)
}
}
18 changes: 8 additions & 10 deletions webrender/src/platform/macos/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use core_text;
use internal_types::FontRenderMode;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use webrender_traits::FontKey;
use webrender_traits::{FontKey, GlyphDimensions};

pub type NativeFontHandle = CGFont;

Expand All @@ -25,8 +25,6 @@ pub struct FontContext {
}

pub struct RasterizedGlyph {
pub left: i32,
pub top: i32,
pub width: u32,
pub height: u32,
pub bytes: Vec<u8>,
Expand All @@ -35,8 +33,6 @@ pub struct RasterizedGlyph {
impl RasterizedGlyph {
pub fn blank() -> RasterizedGlyph {
RasterizedGlyph {
left: 0,
top: 0,
width: 0,
height: 0,
bytes: vec![],
Expand Down Expand Up @@ -120,16 +116,20 @@ impl FontContext {
}
}

#[allow(dead_code)] // TODO(gw): Expose this to the public glyph dimensions API.
pub fn get_glyph_dimensions(&mut self,
font_key: FontKey,
size: Au,
character: u32,
device_pixel_ratio: f32) -> Option<(Au, Au)> {
device_pixel_ratio: f32) -> Option<GlyphDimensions> {
self.get_ct_font(font_key, size, device_pixel_ratio).map(|ref ct_font| {
let glyph = character as CGGlyph;
let metrics = get_glyph_metrics(ct_font, glyph);
(Au::from_px(metrics.rasterized_width as i32), Au::from_px(metrics.rasterized_height as i32))
GlyphDimensions {
left: metrics.rasterized_left,
top: metrics.rasterized_ascent,
width: metrics.rasterized_width as u32,
height: metrics.rasterized_height as u32,
}
})
}

Expand Down Expand Up @@ -178,8 +178,6 @@ impl FontContext {
}

Some(RasterizedGlyph {
left: metrics.rasterized_left,
top: metrics.rasterized_ascent,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
bytes: rasterized_pixels,
Expand Down
72 changes: 46 additions & 26 deletions webrender/src/platform/unix/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use app_units::Au;
use internal_types::FontRenderMode;
use webrender_traits::{FontKey, NativeFontHandle};
use webrender_traits::{FontKey, GlyphDimensions, NativeFontHandle};

use freetype::freetype::{FTErrorMethods, FT_PIXEL_MODE_GRAY, FT_PIXEL_MODE_MONO, FT_PIXEL_MODE_LCD};
use freetype::freetype::{FT_Done_FreeType, FT_RENDER_MODE_LCD, FT_Library_SetLcdFilter};
Expand All @@ -16,7 +16,6 @@ use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};

use std::{mem, ptr, slice};
use std::collections::HashMap;
//use util;

struct Face {
face: FT_Face,
Expand All @@ -28,8 +27,6 @@ pub struct FontContext {
}

pub struct RasterizedGlyph {
pub left: i32,
pub top: i32,
pub width: u32,
pub height: u32,
pub bytes: Vec<u8>,
Expand Down Expand Up @@ -113,15 +110,19 @@ impl FontContext {
None
}

#[allow(dead_code)] // TODO(gw): Expose this to the public glyph dimensions API.
pub fn get_glyph_dimensions(&self,
font_key: FontKey,
size: Au,
character: u32,
device_pixel_ratio: f32) -> Option<(Au, Au)> {
device_pixel_ratio: f32) -> Option<GlyphDimensions> {
self.load_glyph(font_key, size, character, device_pixel_ratio).map(|slot| {
let metrics = unsafe { &(*slot).metrics };
(Au::from_px((metrics.width >> 6) as i32), Au::from_px((metrics.height >> 6) as i32))
GlyphDimensions {
left: (metrics.horiBearingX >> 6) as i32,
top: (metrics.horiBearingY >> 6) as i32,
width: (metrics.width >> 6) as u32,
height: (metrics.height >> 6) as u32,
}
})
}

Expand Down Expand Up @@ -150,34 +151,55 @@ impl FontContext {
let bitmap = &(*slot).bitmap;
let bitmap_mode = bitmap.pixel_mode as u32;

let width = match bitmap_mode {
FT_PIXEL_MODE_MONO | FT_PIXEL_MODE_GRAY => bitmap.width,
FT_PIXEL_MODE_LCD => bitmap.width / 3,
_ => panic!("Unexpected render mode!"),
} as u32;

let mut final_buffer = Vec::with_capacity(width as usize * bitmap.rows as usize * 4);
let metrics = &(*slot).metrics;
let glyph_width = (metrics.width >> 6) as i32;
let glyph_height = (metrics.height >> 6) as i32;
let mut final_buffer = Vec::with_capacity(glyph_width as usize *
glyph_height as usize *
4);

match bitmap_mode {
FT_PIXEL_MODE_MONO => {
// This is not exactly efficient... but it's only used by the
// reftest pass when we have AA disabled on glyphs.
for y in 0..bitmap.rows {
for x in 0..bitmap.width {
let byte_index = (y * bitmap.pitch) + (x >> 3);
let bit_index = x & 7;
let byte_ptr = bitmap.buffer.offset(byte_index as isize);
let bit = (*byte_ptr & (0x80 >> bit_index)) != 0;
let byte_value = if bit {
0xff
let offset_x = (metrics.horiBearingX >> 6) as i32 - (*slot).bitmap_left;
let offset_y = (metrics.horiBearingY >> 6) as i32 - (*slot).bitmap_top;

// Due to AA being disabled, the bitmap produced for mono
// glyphs is often smaller than the reported glyph dimensions.
// To account for this, place the rendered glyph within the
// box of the glyph dimensions, filling in invalid pixels with
// zero alpha.
for iy in 0..glyph_height {
let y = iy - offset_y;
for ix in 0..glyph_width {
let x = ix + offset_x;
let valid_byte = x >= 0 &&
y >= 0 &&
x < bitmap.width &&
y < bitmap.rows;
let byte_value = if valid_byte {
let byte_index = (y * bitmap.pitch) + (x >> 3);
let bit_index = x & 7;
let byte_ptr = bitmap.buffer.offset(byte_index as isize);
let bit = (*byte_ptr & (0x80 >> bit_index)) != 0;
if bit {
0xff
} else {
0
}
} else {
0
};

final_buffer.extend_from_slice(&[ 0xff, 0xff, 0xff, byte_value ]);
}
}
}
FT_PIXEL_MODE_GRAY => {
// We can assume that the reported glyph dimensions exactly
// match the rasterized bitmap for normal alpha coverage glyphs.

let buffer = slice::from_raw_parts(
bitmap.buffer,
(bitmap.width * bitmap.rows) as usize
Expand All @@ -204,10 +226,8 @@ impl FontContext {
}

glyph = Some(RasterizedGlyph {
left: (*slot).bitmap_left as i32,
top: (*slot).bitmap_top as i32,
width: width,
height: bitmap.rows as u32,
width: glyph_width as u32,
height: glyph_height as u32,
bytes: final_buffer,
});
}
Expand Down
29 changes: 15 additions & 14 deletions webrender/src/prim_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ impl PrimitiveStore {

pub fn prepare_prim_for_render(&mut self,
prim_index: PrimitiveIndex,
resource_cache: &ResourceCache,
resource_cache: &mut ResourceCache,
frame_id: FrameId,
device_pixel_ratio: f32,
auxiliary_lists: &AuxiliaryLists) -> bool {
Expand Down Expand Up @@ -718,29 +718,30 @@ impl PrimitiveStore {
for src in src_glyphs {
glyph_key.index = src.index;

let image_info = match resource_cache.get_glyph(&glyph_key, frame_id) {
let dimensions = match resource_cache.get_glyph_dimensions(&glyph_key) {
None => continue,
Some(image_info) => image_info,
Some(dimensions) => dimensions,
};

debug_assert!(metadata.color_texture_id == TextureId::invalid() ||
metadata.color_texture_id == image_info.texture_id);
metadata.color_texture_id = image_info.texture_id;
let x = src.x + dimensions.left as f32 / device_pixel_ratio - blur_offset;
let y = src.y - dimensions.top as f32 / device_pixel_ratio - blur_offset;

let x = src.x + image_info.user_data.x0 as f32 / device_pixel_ratio -
blur_offset;
let y = src.y - image_info.user_data.y0 as f32 / device_pixel_ratio -
blur_offset;
let width = dimensions.width as f32 / device_pixel_ratio;
let height = dimensions.height as f32 / device_pixel_ratio;

let width = image_info.requested_rect.size.width as f32 /
device_pixel_ratio;
let height = image_info.requested_rect.size.height as f32 /
device_pixel_ratio;
let image_info = match resource_cache.get_glyph(&glyph_key, frame_id) {
None => continue,
Some(image_info) => image_info,
};

let local_glyph_rect = Rect::new(Point2D::new(x, y),
Size2D::new(width, height));
local_rect = local_rect.union(&local_glyph_rect);

debug_assert!(metadata.color_texture_id == TextureId::invalid() ||
metadata.color_texture_id == image_info.texture_id);
metadata.color_texture_id = image_info.texture_id;

dest_glyphs[actual_glyph_count] = GpuBlock32::from(GlyphPrimitive {
offset: local_glyph_rect.origin,
uv0: Point2D::new(image_info.pixel_rect.top_left.x as f32,
Expand Down
6 changes: 1 addition & 5 deletions webrender/src/render_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,7 @@ impl RenderBackend {
ApiMsg::GetGlyphDimensions(glyph_keys, tx) => {
let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
for glyph_key in &glyph_keys {
// Get the next frame id, so it'll remain in the texture cache.
// This assumes that the glyph will be used in the next frame.
let frame_id = self.frame.next_frame_id();
let glyph_dim = self.resource_cache
.get_glyph_dimensions(&glyph_key, frame_id);
let glyph_dim = self.resource_cache.get_glyph_dimensions(glyph_key);
glyph_dimensions.push(glyph_dim);
};
tx.send(glyph_dimensions).unwrap();
Expand Down
4 changes: 0 additions & 4 deletions webrender/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,6 @@ impl Renderer {
// TODO: Ensure that the white texture can never get evicted when the cache supports LRU eviction!
let white_image_id = texture_cache.new_item_id();
texture_cache.insert(white_image_id,
0,
0,
2,
2,
ImageFormat::RGBA8,
Expand All @@ -540,8 +538,6 @@ impl Renderer {

let dummy_mask_image_id = texture_cache.new_item_id();
texture_cache.insert(dummy_mask_image_id,
0,
0,
2,
2,
ImageFormat::A8,
Expand Down
49 changes: 32 additions & 17 deletions webrender/src/resource_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ pub struct ResourceCache {

texture_cache: TextureCache,

// TODO(gw): We should expire (parts of) this cache semi-regularly!
cached_glyph_dimensions: HashMap<GlyphKey, Option<GlyphDimensions>, BuildHasherDefault<FnvHasher>>,

pending_raster_jobs: Vec<GlyphRasterJob>,
}

Expand All @@ -146,6 +149,7 @@ impl ResourceCache {
draw_lists: FreeList::new(),
font_templates: HashMap::with_hasher(Default::default()),
image_templates: HashMap::with_hasher(Default::default()),
cached_glyph_dimensions: HashMap::with_hasher(Default::default()),
texture_cache: texture_cache,
pending_raster_jobs: Vec::new(),
device_pixel_ratio: device_pixel_ratio,
Expand Down Expand Up @@ -247,8 +251,6 @@ impl ResourceCache {

// TODO: Can we avoid the clone of the bytes here?
self.texture_cache.insert(image_id,
0,
0,
image_template.width,
image_template.height,
image_template.format,
Expand Down Expand Up @@ -314,8 +316,6 @@ impl ResourceCache {
}
}
self.texture_cache.insert(image_id,
result.left,
result.top,
texture_width,
texture_height,
ImageFormat::RGBA8,
Expand Down Expand Up @@ -353,20 +353,35 @@ impl ResourceCache {
image_id.map(|image_id| self.texture_cache.get(image_id))
}

pub fn get_glyph_dimensions(&mut self,
glyph_key: &GlyphKey,
frame_id: FrameId)
-> Option<GlyphDimensions> {
self.create_raster_job(&glyph_key, frame_id);
self.raster_pending_glyphs(frame_id);
self.get_glyph(&glyph_key, frame_id).map(|cached_glyph| {
GlyphDimensions {
left: cached_glyph.user_data.x0,
top: cached_glyph.user_data.y0,
width: cached_glyph.requested_rect.size.width,
height: cached_glyph.requested_rect.size.height,
pub fn get_glyph_dimensions(&mut self, glyph_key: &GlyphKey) -> Option<GlyphDimensions> {
match self.cached_glyph_dimensions.entry(glyph_key.clone()) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let mut dimensions = None;
let device_pixel_ratio = self.device_pixel_ratio;
let font_template = &self.font_templates[&glyph_key.font_key];

FONT_CONTEXT.with(|font_context| {
let mut font_context = font_context.borrow_mut();
match *font_template {
FontTemplate::Raw(ref bytes) => {
font_context.add_raw_font(&glyph_key.font_key, &**bytes);
}
FontTemplate::Native(ref native_font_handle) => {
font_context.add_native_font(&glyph_key.font_key,
(*native_font_handle).clone());
}
}

dimensions = font_context.get_glyph_dimensions(glyph_key.font_key,
glyph_key.size,
glyph_key.index,
device_pixel_ratio);
});

*entry.insert(dimensions)
}
})
}
}

#[inline]
Expand Down
Loading