diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 2a7bedf6bd..3c9c709401 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -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) - } } diff --git a/webrender/src/platform/macos/font.rs b/webrender/src/platform/macos/font.rs index 486c19d8bf..b2bd74ba2b 100644 --- a/webrender/src/platform/macos/font.rs +++ b/webrender/src/platform/macos/font.rs @@ -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; @@ -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, @@ -35,8 +33,6 @@ pub struct RasterizedGlyph { impl RasterizedGlyph { pub fn blank() -> RasterizedGlyph { RasterizedGlyph { - left: 0, - top: 0, width: 0, height: 0, bytes: vec![], @@ -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 { 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, + } }) } @@ -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, diff --git a/webrender/src/platform/unix/font.rs b/webrender/src/platform/unix/font.rs index 7c562267a8..2ef4514ca6 100644 --- a/webrender/src/platform/unix/font.rs +++ b/webrender/src/platform/unix/font.rs @@ -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}; @@ -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, @@ -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, @@ -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 { 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, + } }) } @@ -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 @@ -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, }); } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index ca374ffbc0..607ac6731e 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -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 { @@ -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, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 042af57067..06f061dbe1 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -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(); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 132864e966..3412040547 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -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, @@ -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, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 41870a2301..800a3681a4 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -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, BuildHasherDefault>, + pending_raster_jobs: Vec, } @@ -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, @@ -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, @@ -314,8 +316,6 @@ impl ResourceCache { } } self.texture_cache.insert(image_id, - result.left, - result.top, texture_width, texture_height, ImageFormat::RGBA8, @@ -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 { - 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 { + 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] diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 7b33981f8f..5f6f906c0b 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -348,14 +348,6 @@ impl TexturePage { } } -// TODO(gw): This is used to store data specific to glyphs. -// Perhaps find a better place to store it. -#[derive(Debug, Clone)] -pub struct TextureCacheItemUserData { - pub x0: i32, - pub y0: i32, -} - /// A binning free list. Binning is important to avoid sifting through lots of small strips when /// allocating many texture items. struct FreeRectList { @@ -440,9 +432,6 @@ pub struct TextureCacheItem { // Identifies the texture and array slice pub texture_id: TextureId, - // Custom data - unused by texture cache itself - pub user_data: TextureCacheItemUserData, - // The texture coordinates for this item pub pixel_rect: RectUv, @@ -486,7 +475,6 @@ impl FreeListItem for TextureCacheItem { impl TextureCacheItem { fn new(texture_id: TextureId, - user_x0: i32, user_y0: i32, allocated_rect: Rect, requested_rect: Rect, texture_size: &Size2D) @@ -504,10 +492,6 @@ impl TextureCacheItem { bottom_right: DevicePoint::new((requested_rect.origin.x + requested_rect.size.width) as i32, (requested_rect.origin.y + requested_rect.size.height) as i32) }, - user_data: TextureCacheItemUserData { - x0: user_x0, - y0: user_y0, - }, allocated_rect: allocated_rect, requested_rect: requested_rect, } @@ -627,10 +611,6 @@ impl TextureCache { // how the raster_jobs code works. pub fn new_item_id(&mut self) -> TextureCacheItemId { let new_item = TextureCacheItem { - user_data: TextureCacheItemUserData { - x0: 0, - y0: 0, - }, pixel_rect: RectUv { top_left: DevicePoint::zero(), top_right: DevicePoint::zero(), @@ -647,8 +627,6 @@ impl TextureCache { pub fn allocate(&mut self, image_id: TextureCacheItemId, - user_x0: i32, - user_y0: i32, requested_width: u32, requested_height: u32, format: ImageFormat, @@ -670,7 +648,6 @@ impl TextureCache { .expect("TODO: Handle running out of texture ids!"); let cache_item = TextureCacheItem::new( texture_id, - user_x0, user_y0, Rect::new(Point2D::zero(), requested_size), Rect::new(Point2D::zero(), requested_size), &requested_size); @@ -730,7 +707,6 @@ impl TextureCache { requested_size); let cache_item = TextureCacheItem::new(page.texture_id, - user_x0, user_y0, allocated_rect, requested_rect, &Size2D::new(page.texture_size, page.texture_size)); @@ -831,8 +807,6 @@ impl TextureCache { pub fn insert(&mut self, image_id: TextureCacheItemId, - x0: i32, - y0: i32, width: u32, height: u32, format: ImageFormat, @@ -840,8 +814,6 @@ impl TextureCache { insert_op: TextureInsertOp, border_type: BorderType) { let result = self.allocate(image_id, - x0, - y0, width, height, format, @@ -938,14 +910,12 @@ impl TextureCache { let horizontal_blur_image_id = self.new_item_id(); // TODO(pcwalton): Destroy these! self.allocate(unblurred_glyph_image_id, - 0, 0, glyph_size.width, glyph_size.height, ImageFormat::RGBA8, TextureCacheItemKind::Standard, BorderType::SinglePixel, TextureFilter::Linear); self.allocate(horizontal_blur_image_id, - 0, 0, width, height, ImageFormat::RGBA8, TextureCacheItemKind::Alternate, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index f75d49ee3d..20318147a1 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -2002,7 +2002,7 @@ impl FrameBuilder { fn prepare_primitives(&mut self, screen_rect: &DeviceRect, - resource_cache: &ResourceCache, + resource_cache: &mut ResourceCache, frame_id: FrameId, pipeline_auxiliary_lists: &HashMap>) { for (layer, packed_layer) in self.layer_store diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index c10ebf04f4..e425ca929c 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -58,7 +58,7 @@ pub enum ApiMsg { WebGLCommand(WebGLContextId, WebGLCommand), } -#[derive(Clone, Deserialize, Serialize)] +#[derive(Copy, Clone, Deserialize, Serialize, Debug)] pub struct GlyphDimensions { pub left: i32, pub top: i32,