diff --git a/src/batch_builder.rs b/src/batch_builder.rs index d9421a4e27..9858671641 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -80,11 +80,13 @@ impl<'a> BatchBuilder<'a> { bottom_right: Point2D::new(u1, v1), }; + let uv_size = image_info.uv_rect.bottom_right - image_info.uv_rect.top_left; + let tile_params = TileParams { - u0: image_info.u0, - v0: image_info.v0, - u_size: image_info.u1 - image_info.u0, - v_size: image_info.v1 - image_info.v0, + u0: image_info.uv_rect.top_left.x, + v0: image_info.uv_rect.top_left.y, + u_size: uv_size.x, + v_size: uv_size.y, }; clipper::clip_rect_to_combined_region(RectPosUv { @@ -139,24 +141,16 @@ impl<'a> BatchBuilder<'a> { for glyph in glyphs { glyph_key.index = glyph.index; let image_info = resource_cache.get_glyph(&glyph_key); - - if image_info.width > 0 && image_info.height > 0 { - let x = glyph.x + image_info.user_x0 as f32 / device_pixel_ratio - blur_offset; - let y = glyph.y - image_info.user_y0 as f32 / device_pixel_ratio - blur_offset; - let width = image_info.width as f32 / device_pixel_ratio; - let height = image_info.height as f32 / device_pixel_ratio; - - let uv = RectUv { - top_left: Point2D::new(image_info.u0, image_info.v0), - top_right: Point2D::new(image_info.u1, image_info.v0), - bottom_left: Point2D::new(image_info.u0, image_info.v1), - bottom_right: Point2D::new(image_info.u1, image_info.v1), - }; + if let Some(image_info) = image_info { + let x = glyph.x + image_info.user_data.x0 as f32 / device_pixel_ratio - blur_offset; + let y = glyph.y - image_info.user_data.y0 as f32 / device_pixel_ratio - blur_offset; + let width = image_info.rect.size.width as f32 / device_pixel_ratio; + let height = image_info.rect.size.height as f32 / device_pixel_ratio; let rect = RectPosUv { pos: Rect::new(Point2D::new(x, y), Size2D::new(width, height)), - uv: uv, + uv: image_info.uv_rect, }; let rect_buffer = match text_batches.entry((image_info.texture_id, @@ -199,28 +193,28 @@ impl<'a> BatchBuilder<'a> { x0, y0, color, rect.uv.top_left.x, rect.uv.top_left.y, - dummy_mask_image.u0, dummy_mask_image.v0, + dummy_mask_image.uv_rect.top_left.x, dummy_mask_image.uv_rect.top_left.y, texture_index, dummy_mask_image.texture_index)); vertex_buffer.push(PackedVertex::from_components( x1, y0, color, rect.uv.top_right.x, rect.uv.top_right.y, - dummy_mask_image.u1, dummy_mask_image.v0, + dummy_mask_image.uv_rect.top_right.x, dummy_mask_image.uv_rect.top_right.y, texture_index, dummy_mask_image.texture_index)); vertex_buffer.push(PackedVertex::from_components( x0, y1, color, rect.uv.bottom_left.x, rect.uv.bottom_left.y, - dummy_mask_image.u0, dummy_mask_image.v1, + dummy_mask_image.uv_rect.bottom_left.x, dummy_mask_image.uv_rect.bottom_left.y, texture_index, dummy_mask_image.texture_index)); vertex_buffer.push(PackedVertex::from_components( x1, y1, color, rect.uv.bottom_right.x, rect.uv.bottom_right.y, - dummy_mask_image.v0, dummy_mask_image.v1, + dummy_mask_image.uv_rect.bottom_right.x, dummy_mask_image.uv_rect.bottom_right.y, texture_index, dummy_mask_image.texture_index)); } @@ -266,17 +260,10 @@ impl<'a> BatchBuilder<'a> { return } - let uv = RectUv { - top_left: Point2D::new(image_info.u0, image_info.v0), - top_right: Point2D::new(image_info.u1, image_info.v0), - bottom_left: Point2D::new(image_info.u0, image_info.v1), - bottom_right: Point2D::new(image_info.u1, image_info.v1), - }; - clipper::clip_rect_to_combined_region( RectPosUv { pos: *rect, - uv: uv, + uv: image_info.uv_rect, }, &mut clip_buffers.sh_clip_buffers, &mut clip_buffers.rect_pos_uv, diff --git a/src/internal_types.rs b/src/internal_types.rs index ffe1554969..e285c9b5ec 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -405,15 +405,16 @@ impl

ClipRectToRegionResult

{ pub fn muv_for_position(&self, position: &Point2D, mask: &TextureCacheItem) -> Point2D { - let mask_uv_size = Size2D::new(mask.u1 - mask.u0, mask.v1 - mask.v0); + let mask_uv_size = Size2D::new(mask.uv_rect.bottom_right.x - mask.uv_rect.top_left.x, + mask.uv_rect.bottom_right.y - mask.uv_rect.top_left.y); let mask_result = match self.mask_result { None => return Point2D::new(0.0, 0.0), Some(ref mask_result) => mask_result, }; let muv_rect = - Rect::new(Point2D::new(mask.u0 + mask_result.muv_rect.origin.x * mask_uv_size.width, - mask.v0 + mask_result.muv_rect.origin.y * mask_uv_size.height), + Rect::new(Point2D::new(mask.uv_rect.top_left.x + mask_result.muv_rect.origin.x * mask_uv_size.width, + mask.uv_rect.top_left.y + mask_result.muv_rect.origin.y * mask_uv_size.height), Size2D::new(mask_result.muv_rect.size.width * mask_uv_size.width, mask_result.muv_rect.size.height * mask_uv_size.height)); let position_rect = &mask_result.position_rect; @@ -572,34 +573,34 @@ impl RectUv { match rotation_angle { BasicRotationAngle::Upright => { RectUv { - top_left: Point2D::new(image.u0, image.v0), - top_right: Point2D::new(image.u1, image.v0), - bottom_right: Point2D::new(image.u1, image.v1), - bottom_left: Point2D::new(image.u0, image.v1), + top_left: image.uv_rect.top_left, + top_right: image.uv_rect.top_right, + bottom_right: image.uv_rect.bottom_right, + bottom_left: image.uv_rect.bottom_left, } } BasicRotationAngle::Clockwise90 => { RectUv { - top_right: Point2D::new(image.u0, image.v0), - top_left: Point2D::new(image.u1, image.v0), - bottom_left: Point2D::new(image.u1, image.v1), - bottom_right: Point2D::new(image.u0, image.v1), + top_right: image.uv_rect.top_left, + top_left: image.uv_rect.top_right, + bottom_left: image.uv_rect.bottom_right, + bottom_right: image.uv_rect.bottom_left, } } BasicRotationAngle::Clockwise180 => { RectUv { - bottom_right: Point2D::new(image.u0, image.v0), - bottom_left: Point2D::new(image.u1, image.v0), - top_left: Point2D::new(image.u1, image.v1), - top_right: Point2D::new(image.u0, image.v1), + bottom_right: image.uv_rect.top_left, + bottom_left: image.uv_rect.top_right, + top_left: image.uv_rect.bottom_right, + top_right: image.uv_rect.bottom_left, } } BasicRotationAngle::Clockwise270 => { RectUv { - bottom_left: Point2D::new(image.u0, image.v0), - bottom_right: Point2D::new(image.u1, image.v0), - top_right: Point2D::new(image.u1, image.v1), - top_left: Point2D::new(image.u0, image.v1), + bottom_left: image.uv_rect.top_left, + bottom_right: image.uv_rect.top_right, + top_right: image.uv_rect.bottom_right, + top_left: image.uv_rect.bottom_left, } } } diff --git a/src/resource_cache.rs b/src/resource_cache.rs index dc4a1bf6c4..2562f81dd5 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -32,7 +32,6 @@ struct ImageResource { } struct GlyphRasterJob { - image_id: TextureCacheItemId, glyph_key: GlyphKey, result: Option, } @@ -43,7 +42,7 @@ struct CachedImageInfo { } pub struct ResourceCache { - cached_glyphs: HashMap>, + cached_glyphs: HashMap, DefaultState>, cached_rasters: HashMap>, cached_images: HashMap>, @@ -201,13 +200,10 @@ impl ResourceCache { // Update texture cache with any newly rasterized glyphs. resource_list.for_each_glyph(|glyph_key| { if !self.cached_glyphs.contains_key(glyph_key) { - let image_id = self.texture_cache.new_item_id(); self.pending_raster_jobs.push(GlyphRasterJob { - image_id: image_id, glyph_key: glyph_key.clone(), result: None, }); - self.cached_glyphs.insert(glyph_key.clone(), image_id); } }); } @@ -223,32 +219,39 @@ impl ResourceCache { // Add completed raster jobs to the texture cache for job in self.pending_raster_jobs.drain(..) { let result = job.result.expect("Failed to rasterize the glyph?"); - let texture_width; - let texture_height; - let insert_op; - match job.glyph_key.blur_radius { - Au(0) => { - texture_width = result.width; - texture_height = result.height; - insert_op = TextureInsertOp::Blit(result.bytes); - } - blur_radius => { - let blur_radius_px = f32::ceil(blur_radius.to_f32_px() * self.device_pixel_ratio) - as u32; - texture_width = result.width + blur_radius_px * BLUR_INFLATION_FACTOR; - texture_height = result.height + blur_radius_px * BLUR_INFLATION_FACTOR; - insert_op = TextureInsertOp::Blur(result.bytes, - Size2D::new(result.width, result.height), - blur_radius); + let image_id = if result.width > 0 && result.height > 0 { + let image_id = self.texture_cache.new_item_id(); + let texture_width; + let texture_height; + let insert_op; + match job.glyph_key.blur_radius { + Au(0) => { + texture_width = result.width; + texture_height = result.height; + insert_op = TextureInsertOp::Blit(result.bytes); + } + blur_radius => { + let blur_radius_px = f32::ceil(blur_radius.to_f32_px() * self.device_pixel_ratio) + as u32; + texture_width = result.width + blur_radius_px * BLUR_INFLATION_FACTOR; + texture_height = result.height + blur_radius_px * BLUR_INFLATION_FACTOR; + insert_op = TextureInsertOp::Blur(result.bytes, + Size2D::new(result.width, result.height), + blur_radius); + } } - } - self.texture_cache.insert(job.image_id, - result.left, - result.top, - texture_width, - texture_height, - ImageFormat::RGBA8, - insert_op); + self.texture_cache.insert(image_id, + result.left, + result.top, + texture_width, + texture_height, + ImageFormat::RGBA8, + insert_op); + Some(image_id) + } else { + None + }; + self.cached_glyphs.insert(job.glyph_key, image_id); } } @@ -301,9 +304,9 @@ impl ResourceCache { } #[inline] - pub fn get_glyph(&self, glyph_key: &GlyphKey) -> &TextureCacheItem { + pub fn get_glyph(&self, glyph_key: &GlyphKey) -> Option<&TextureCacheItem> { let image_id = self.cached_glyphs[glyph_key]; - self.texture_cache.get(image_id) + image_id.map(|image_id| self.texture_cache.get(image_id)) } #[inline] diff --git a/src/texture_cache.rs b/src/texture_cache.rs index faeee236cd..d6c102b561 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -5,7 +5,7 @@ use fnv::FnvHasher; use freelist::{FreeList, FreeListItem, FreeListItemId}; use internal_types::{TextureTarget, TextureUpdate, TextureUpdateOp, TextureUpdateDetails}; use internal_types::{RasterItem, RenderTargetMode, TextureImage, TextureUpdateList}; -use internal_types::{BasicRotationAngle}; +use internal_types::{BasicRotationAngle, RectUv}; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::collections::hash_state::DefaultState; @@ -188,44 +188,44 @@ 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, +} + #[derive(Debug, Clone)] pub struct TextureCacheItem { - pub u0: f32, // todo(gw): don't precalc these? - pub v0: f32, - pub u1: f32, - pub v1: f32, - pub user_x0: i32, - pub user_y0: i32, - pub page_x0: u32, - pub page_y0: u32, - pub width: u32, - pub height: u32, - pub texture_id: TextureId, // todo(gw): can this ever get invalidated? (page defragmentation?) - pub format: ImageFormat, + pub user_data: TextureCacheItemUserData, + pub rect: Rect, + pub uv_rect: RectUv, + pub texture_id: TextureId, pub texture_index: TextureIndex, } // Structure squat the width/height fields to maintain the free list information :) impl FreeListItem for TextureCacheItem { fn next_free_id(&self) -> Option { - if self.width == 0 { - debug_assert!(self.height == 0); + if self.rect.size.width == 0 { + debug_assert!(self.rect.size.height == 0); None } else { - debug_assert!(self.width == 1); - Some(FreeListItemId::new(self.height)) + debug_assert!(self.rect.size.width == 1); + Some(FreeListItemId::new(self.rect.size.height)) } } fn set_next_free_id(&mut self, id: Option) { match id { Some(id) => { - self.width = 1; - self.height = id.value(); + self.rect.size.width = 1; + self.rect.size.height = id.value(); } None => { - self.width = 0; - self.height = 0; + self.rect.size.width = 0; + self.rect.size.height = 0; } } } @@ -234,27 +234,19 @@ impl FreeListItem for TextureCacheItem { impl TextureCacheItem { fn new(texture_id: TextureId, texture_index: TextureIndex, - format: ImageFormat, user_x0: i32, user_y0: i32, - page_x0: u32, page_y0: u32, - width: u32, height: u32, - u0: f32, v0: f32, - u1: f32, v1: f32) + rect: Rect, + uv_rect: RectUv) -> TextureCacheItem { TextureCacheItem { texture_id: texture_id, texture_index: texture_index, - u0: u0, - v0: v0, - u1: u1, - v1: v1, - user_x0: user_x0, - user_y0: user_y0, - page_x0: page_x0, - page_y0: page_y0, - width: width, - height: height, - format: format, + uv_rect: uv_rect, + user_data: TextureCacheItemUserData { + x0: user_x0, + y0: user_y0, + }, + rect: rect, } } @@ -263,10 +255,12 @@ impl TextureCacheItem { TextureImage { texture_id: self.texture_id, texture_index: self.texture_index, - texel_uv: Rect::new(Point2D::new(self.u0, self.v0), Size2D::new(self.u1 - self.u0, - self.v1 - self.v0)), - pixel_uv: Point2D::new((self.u0 * texture_size) as u32, - (self.v0 * texture_size) as u32), + texel_uv: Rect::new(Point2D::new(self.uv_rect.top_left.x, + self.uv_rect.top_left.y), + Size2D::new(self.uv_rect.bottom_right.x - self.uv_rect.top_left.x, + self.uv_rect.bottom_right.y - self.uv_rect.top_left.y)), + pixel_uv: Point2D::new((self.uv_rect.top_left.x * texture_size) as u32, + (self.uv_rect.top_left.y * texture_size) as u32), } } } @@ -336,18 +330,18 @@ impl TextureCache { // how the raster_jobs code works. pub fn new_item_id(&mut self) -> TextureCacheItemId { let new_item = TextureCacheItem { - u0: 0.0, - v0: 0.0, - u1: 0.0, - v1: 0.0, - user_x0: 0, - user_y0: 0, - page_x0: 0, - page_y0: 0, - width: 0, - height: 0, + uv_rect: RectUv { + top_left: Point2D::zero(), + top_right: Point2D::zero(), + bottom_left: Point2D::zero(), + bottom_right: Point2D::zero(), + }, + user_data: TextureCacheItemUserData { + x0: 0, + y0: 0, + }, + rect: Rect::zero(), texture_id: TextureId::invalid(), - format: ImageFormat::Invalid, texture_index: TextureIndex(0), }; self.items.insert(new_item) @@ -453,12 +447,15 @@ impl TextureCache { .expect("TODO: Handle running out of texture ids!"); let cache_item = TextureCacheItem::new(texture_id, TextureIndex(0), - format, x0, y0, - 0, 0, - width, height, - 0.0, 0.0, - 1.0, 1.0); + Rect::new(Point2D::zero(), + Size2D::new(width, height)), + RectUv { + top_left: Point2D::new(0.0, 0.0), + top_right: Point2D::new(1.0, 0.0), + bottom_left: Point2D::new(0.0, 1.0), + bottom_right: Point2D::new(1.0, 1.0), + }); *self.items.get_mut(image_id) = cache_item; return AllocationResult { @@ -486,12 +483,15 @@ impl TextureCache { let v1 = v0 + height as f32 / page.texture_size as f32; let cache_item = TextureCacheItem::new(page.texture_id, page.texture_index, - format, x0, y0, - tx0, ty0, - width, height, - u0, v0, - u1, v1); + Rect::new(Point2D::new(tx0, ty0), + Size2D::new(width, height)), + RectUv { + top_left: Point2D::new(u0, v0), + top_right: Point2D::new(u1, v0), + bottom_left: Point2D::new(u0, v1), + bottom_right: Point2D::new(u1, v1) + }); *self.items.get_mut(image_id) = cache_item; // TODO(pcwalton): Select a texture index if we're using texture arrays. @@ -577,17 +577,16 @@ impl TextureCache { image_id: TextureCacheItemId, width: u32, height: u32, - format: ImageFormat, + _format: ImageFormat, bytes: Vec) { let existing_item = self.items.get(image_id); // TODO(gw): Handle updates to size/format! - debug_assert!(existing_item.width == width); - debug_assert!(existing_item.height == height); - debug_assert!(existing_item.format == format); + debug_assert!(existing_item.rect.size.width == width); + debug_assert!(existing_item.rect.size.height == height); - let op = TextureUpdateOp::Update(existing_item.page_x0, - existing_item.page_y0, + let op = TextureUpdateOp::Update(existing_item.rect.origin.x, + existing_item.rect.origin.y, width, height, TextureUpdateDetails::Blit(bytes));