diff --git a/src/batch_builder.rs b/src/batch_builder.rs index e8b9d3468b..7dadc7d021 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -14,6 +14,7 @@ use resource_cache::ResourceCache; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_state::DefaultState; +use tessellator::{self, BorderCornerTessellation}; use texture_cache::{TextureCacheItem}; use webrender_traits::{ColorF, ImageFormat, BorderStyle, BoxShadowClipMode}; use webrender_traits::{BorderRadius, BorderSide, FontKey, GlyphInstance, ImageKey}; @@ -874,6 +875,7 @@ impl<'a> BatchBuilder<'a> { BorderRadiusRasterOp::create(&Size2D::new(mask_radius, mask_radius), &Size2D::new(0.0, 0.0), false, + 0, ImageFormat::RGBA8).expect( "Didn't find border radius mask for dashed border!"); let raster_item = RasterItem::BorderRadius(raster_op); @@ -985,101 +987,129 @@ impl<'a> BatchBuilder<'a> { // TODO: Check for zero width/height borders! let white_image = resource_cache.get_dummy_color_image(); - let mask_image = match BorderRadiusRasterOp::create(outer_radius, - inner_radius, - false, - ImageFormat::A8) { - Some(raster_item) => { - let raster_item = RasterItem::BorderRadius(raster_item); - resource_cache.get_raster(&raster_item) - } - None => { - resource_cache.get_dummy_mask_image() - } - }; - // FIXME(pcwalton): Either use RGBA8 textures instead of alpha masks here, or implement - // a mask combiner. - let mask_uv = RectUv::from_image_and_rotation_angle(mask_image, rotation_angle); - clipper::clip_rect_to_combined_region( - RectPosUv { - pos: *vertices_rect, - uv: mask_uv, - }, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.rect_pos_uv, - clip); - for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { - let v0; - let v1; - let muv0; - let muv1; - match rotation_angle { - BasicRotationAngle::Upright => { - v0 = clip_region.rect_result.pos.origin; - muv0 = clip_region.rect_result.uv.top_left; - v1 = clip_region.rect_result.pos.bottom_right(); - muv1 = clip_region.rect_result.uv.bottom_right; - } - BasicRotationAngle::Clockwise90 => { - v0 = clip_region.rect_result.pos.top_right(); - muv0 = clip_region.rect_result.uv.top_right; - v1 = clip_region.rect_result.pos.bottom_left(); - muv1 = clip_region.rect_result.uv.bottom_left; + for rect_index in 0..tessellator::quad_count_for_border_corner(outer_radius) { + let tessellated_rect = vertices_rect.tessellate_border_corner(outer_radius, + inner_radius, + rotation_angle, + rect_index); + let mask_image = match BorderRadiusRasterOp::create(outer_radius, + inner_radius, + false, + rect_index, + ImageFormat::A8) { + Some(raster_item) => { + let raster_item = RasterItem::BorderRadius(raster_item); + resource_cache.get_raster(&raster_item) } - BasicRotationAngle::Clockwise180 => { - v0 = clip_region.rect_result.pos.bottom_right(); - muv0 = clip_region.rect_result.uv.bottom_right; - v1 = clip_region.rect_result.pos.origin; - muv1 = clip_region.rect_result.uv.top_left; + None => { + resource_cache.get_dummy_mask_image() } - BasicRotationAngle::Clockwise270 => { - v0 = clip_region.rect_result.pos.bottom_left(); - muv0 = clip_region.rect_result.uv.bottom_left; - v1 = clip_region.rect_result.pos.top_right(); - muv1 = clip_region.rect_result.uv.top_right; - } - } + }; - let mut vertices = [ - PackedVertex::from_components(v0.x, v0.y, - color0, - 0.0, 0.0, - muv0.x, muv0.y, - white_image.texture_index, - mask_image.texture_index), - PackedVertex::from_components(v1.x, v1.y, - color0, - 0.0, 0.0, - muv1.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v0.x, v1.y, - color0, - 0.0, 0.0, - muv0.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v0.x, v0.y, - color1, - 0.0, 0.0, - muv0.x, muv0.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v1.x, v0.y, - color1, - 0.0, 0.0, - muv1.x, muv0.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v1.x, v1.y, - color1, - 0.0, 0.0, - muv1.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - ]; + // FIXME(pcwalton): Either use RGBA8 textures instead of alpha masks here, or implement + // a mask combiner. + let mask_uv = RectUv::from_image_and_rotation_angle(mask_image, rotation_angle); + let tessellated_rect = RectPosUv { + pos: tessellated_rect, + uv: mask_uv, + }; - self.add_draw_item(matrix_index, - white_image.texture_id, - mask_image.texture_id, - Primitive::Triangles, - &mut vertices); + clipper::clip_rect_to_combined_region(tessellated_rect, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + + for clip_region in clip_buffers.rect_pos_uv + .clip_rect_to_region_result_output + .drain(..) { + let rect_pos_uv = &clip_region.rect_result; + let v0; + let v1; + let muv0; + let muv1; + let muv2; + let muv3; + match rotation_angle { + BasicRotationAngle::Upright => { + v0 = rect_pos_uv.pos.origin; + v1 = rect_pos_uv.pos.bottom_right(); + muv0 = rect_pos_uv.uv.top_left; + muv1 = rect_pos_uv.uv.top_right; + muv2 = rect_pos_uv.uv.bottom_right; + muv3 = rect_pos_uv.uv.bottom_left; + } + BasicRotationAngle::Clockwise90 => { + v0 = rect_pos_uv.pos.top_right(); + v1 = rect_pos_uv.pos.bottom_left(); + muv0 = rect_pos_uv.uv.top_right; + muv1 = rect_pos_uv.uv.top_left; + muv2 = rect_pos_uv.uv.bottom_left; + muv3 = rect_pos_uv.uv.bottom_right; + } + BasicRotationAngle::Clockwise180 => { + v0 = rect_pos_uv.pos.bottom_right(); + v1 = rect_pos_uv.pos.origin; + muv0 = rect_pos_uv.uv.bottom_right; + muv1 = rect_pos_uv.uv.bottom_left; + muv2 = rect_pos_uv.uv.top_left; + muv3 = rect_pos_uv.uv.top_right; + } + BasicRotationAngle::Clockwise270 => { + v0 = rect_pos_uv.pos.bottom_left(); + v1 = rect_pos_uv.pos.top_right(); + muv0 = rect_pos_uv.uv.bottom_left; + muv1 = rect_pos_uv.uv.bottom_right; + muv2 = rect_pos_uv.uv.top_right; + muv3 = rect_pos_uv.uv.top_left; + } + } + + let mut vertices = [ + PackedVertex::from_components(v0.x, v0.y, + color0, + 0.0, 0.0, + muv0.x, muv0.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v1.x, v1.y, + color0, + 0.0, 0.0, + muv2.x, muv2.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v0.x, v1.y, + color0, + 0.0, 0.0, + muv3.x, muv3.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v0.x, v0.y, + color1, + 0.0, 0.0, + muv0.x, muv0.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v1.x, v0.y, + color1, + 0.0, 0.0, + muv1.x, muv1.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v1.x, v1.y, + color1, + 0.0, 0.0, + muv2.x, muv2.y, + white_image.texture_index, + mask_image.texture_index), + ]; + + self.add_draw_item(matrix_index, + white_image.texture_id, + mask_image.texture_id, + Primitive::Triangles, + &mut vertices); + } } } @@ -1373,6 +1403,7 @@ impl BorderSideHelpers for BorderSide { } } +/// NB: Only returns non-tessellated border radius images! fn mask_for_border_radius<'a>(resource_cache: &'a ResourceCache, border_radius: f32, inverted: bool) @@ -1388,6 +1419,7 @@ fn mask_for_border_radius<'a>(resource_cache: &'a ResourceCache, inner_radius_x: Au(0), inner_radius_y: Au(0), inverted: inverted, + index: 0, image_format: ImageFormat::A8, })) } diff --git a/src/clipper.rs b/src/clipper.rs index 7392a08906..ac457b714f 100644 --- a/src/clipper.rs +++ b/src/clipper.rs @@ -1,6 +1,6 @@ use euclid::{Point2D, Rect, Size2D}; use internal_types::{ClipRectToRegionMaskResult, ClipRectToRegionResult}; -use internal_types::{CombinedClipRegion, PolygonPosColorUv, RectPosUv, RectUv, WorkVertex}; +use internal_types::{CombinedClipRegion, PolygonPosColorUv, RectPosUv, WorkVertex}; use simd::f32x4; use std::fmt::Debug; use std::mem; @@ -397,19 +397,9 @@ impl RectPosUv { return } - let uv_tl = util::bilerp(&clipped_rect.origin, &self.pos, &self.uv); - let uv_tr = util::bilerp(&clipped_rect.top_right(), &self.pos, &self.uv); - let uv_br = util::bilerp(&clipped_rect.bottom_right(), &self.pos, &self.uv); - let uv_bl = util::bilerp(&clipped_rect.bottom_left(), &self.pos, &self.uv); - output.push(RectPosUv { pos: *clipped_rect, - uv: RectUv { - top_left: uv_tl, - top_right: uv_tr, - bottom_left: uv_bl, - bottom_right: uv_br, - } + uv: util::bilerp_rect(clipped_rect, &self.pos, &self.uv), }); } } diff --git a/src/internal_types.rs b/src/internal_types.rs index 588d49e8e6..d22cd9ea3b 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -193,8 +193,8 @@ pub enum RenderTargetMode { pub enum TextureUpdateDetails { Blit(Vec), Blur(Vec, Size2D, Au, TextureImage, TextureImage), - /// All four corners and whether inverted, respectively. - BorderRadius(Au, Au, Au, Au, bool), + /// All four corners, the tessellation index, and whether inverted, respectively. + BorderRadius(Au, Au, Au, Au, u32, bool), /// Blur radius, box shadow part, and whether inverted, respectively. BoxShadow(Au, BoxShadowPart, bool), /// Bytes, stretch size, and scratch texture image, respectively. @@ -582,9 +582,9 @@ impl RectUv { BasicRotationAngle::Clockwise90 => { RectUv { top_right: Point2D::new(image.u0, image.v0), - bottom_right: Point2D::new(image.u1, image.v0), + top_left: Point2D::new(image.u1, image.v0), bottom_left: Point2D::new(image.u1, image.v1), - top_left: Point2D::new(image.u0, image.v1), + bottom_right: Point2D::new(image.u0, image.v1), } } BasicRotationAngle::Clockwise180 => { @@ -598,9 +598,9 @@ impl RectUv { BasicRotationAngle::Clockwise270 => { RectUv { bottom_left: Point2D::new(image.u0, image.v0), - top_left: Point2D::new(image.u1, image.v0), + bottom_right: Point2D::new(image.u1, image.v0), top_right: Point2D::new(image.u1, image.v1), - bottom_right: Point2D::new(image.u0, image.v1), + top_left: Point2D::new(image.u0, image.v1), } } } @@ -718,14 +718,16 @@ pub struct BorderRadiusRasterOp { pub outer_radius_y: Au, pub inner_radius_x: Au, pub inner_radius_y: Au, - pub inverted: bool, + pub index: u32, pub image_format: ImageFormat, + pub inverted: bool, } impl BorderRadiusRasterOp { pub fn create(outer_radius: &Size2D, inner_radius: &Size2D, inverted: bool, + index: u32, image_format: ImageFormat) -> Option { if outer_radius.width > 0.0 || outer_radius.height > 0.0 { @@ -734,6 +736,7 @@ impl BorderRadiusRasterOp { outer_radius_y: Au::from_f32_px(outer_radius.height), inner_radius_x: Au::from_f32_px(inner_radius.width), inner_radius_y: Au::from_f32_px(inner_radius.height), + index: index, inverted: inverted, image_format: image_format, }) diff --git a/src/lib.rs b/src/lib.rs index 5acc6fcbc6..9ea2b988f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ mod render_backend; mod resource_cache; mod resource_list; mod scene; +mod tessellator; mod texture_cache; mod util; diff --git a/src/render_backend.rs b/src/render_backend.rs index c68a5a0b31..ad704bd331 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -225,3 +225,4 @@ impl RenderBackend { notifier.new_frame_ready(); } } + diff --git a/src/renderer.rs b/src/renderer.rs index ad16c20e3c..594537cc79 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -6,9 +6,9 @@ use fnv::FnvHasher; use gleam::gl; use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp, BatchUpdateOp, BatchUpdateList}; use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode}; -use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, BoxShadowPart}; -use internal_types::{PackedVertexForTextureCacheUpdate, TextureTarget}; -use internal_types::{CompositionOp, LowLevelFilterOp, BlurDirection, DrawCommand}; +use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, BoxShadowPart, BasicRotationAngle}; +use internal_types::{PackedVertexForTextureCacheUpdate, TextureTarget, CompositionOp}; +use internal_types::{BlurDirection, LowLevelFilterOp, DrawCommand}; use render_backend::RenderBackend; use std::collections::HashMap; use std::collections::hash_state::DefaultState; @@ -17,6 +17,7 @@ use std::mem; use std::path::PathBuf; use std::sync::mpsc::{channel, Receiver}; use std::thread; +use tessellator::BorderCornerTessellation; use texture_cache::{TextureCache, TextureInsertOp}; use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier}; use webrender_traits::{ImageFormat, MixBlendMode, RenderApi}; @@ -451,7 +452,11 @@ impl Renderer { outer_ry, inner_rx, inner_ry, + index, inverted) => { + println!("outer_rx={:?} outer_ry={:?} inner_rx={:?} inner_ry={:?}", + outer_rx, outer_ry, + inner_rx, inner_ry); let x = x as f32; let y = y as f32; let inner_rx = inner_rx.to_f32_px(); @@ -468,9 +473,25 @@ impl Renderer { let border_radii_outer = Point2D::new(outer_rx, outer_ry); let border_radii_inner = Point2D::new(inner_rx, inner_ry); - let border_position = Point2D::new(x + outer_rx, y + outer_ry); + + let tessellated_rect = + Rect::new(Point2D::new(0.0, 0.0), + Size2D::new(outer_rx, outer_ry)); + let tessellated_rect = + tessellated_rect.tessellate_border_corner( + &Size2D::new(outer_rx, outer_ry), + &Size2D::new(inner_rx, inner_ry), + BasicRotationAngle::Upright, + index); + let border_position = + Point2D::new(x - tessellated_rect.origin.x + outer_rx, + y - tessellated_rect.origin.y + outer_ry); let zero_point = Point2D::new(0.0, 0.0); let zero_size = Size2D::new(0.0, 0.0); + + let (x, y) = (x as f32, y as f32); + let (width, height) = (width as f32, height as f32); + let vertices: [PackedVertexForTextureCacheUpdate; 4] = [ PackedVertexForTextureCacheUpdate::new( &Point2D::new(x, y), @@ -485,7 +506,7 @@ impl Renderer { &zero_size, 0.0), PackedVertexForTextureCacheUpdate::new( - &Point2D::new(x + outer_rx, y), + &Point2D::new(x + width, y), &color, &zero_point, TextureIndex(0), @@ -497,7 +518,7 @@ impl Renderer { &zero_size, 0.0), PackedVertexForTextureCacheUpdate::new( - &Point2D::new(x, y + outer_ry), + &Point2D::new(x, y + height), &color, &zero_point, TextureIndex(0), @@ -509,7 +530,7 @@ impl Renderer { &zero_size, 0.0), PackedVertexForTextureCacheUpdate::new( - &Point2D::new(x + outer_rx, y + outer_ry), + &Point2D::new(x + width, y + height), &color, &zero_point, TextureIndex(0), diff --git a/src/resource_list.rs b/src/resource_list.rs index eabb6256c7..72bc148758 100644 --- a/src/resource_list.rs +++ b/src/resource_list.rs @@ -8,6 +8,7 @@ use resource_cache::ResourceCache; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_state::DefaultState; +use tessellator; use webrender_traits::{BorderRadius, BorderStyle, BoxShadowClipMode}; use webrender_traits::{FontKey, ImageFormat, ImageKey, SpecificDisplayItem}; @@ -69,10 +70,12 @@ impl ResourceList { outer_radius: &Size2D, inner_radius: &Size2D, inverted: bool, + index: u32, image_format: ImageFormat) { if let Some(raster_item) = BorderRadiusRasterOp::create(outer_radius, inner_radius, inverted, + index, image_format) { self.required_rasters.insert(RasterItem::BorderRadius(raster_item)); } @@ -80,10 +83,10 @@ impl ResourceList { pub fn add_radius_raster_for_border_radii(&mut self, radii: &BorderRadius) { let zero_size = Size2D::new(0.0, 0.0); - self.add_radius_raster(&radii.top_left, &zero_size, false, ImageFormat::A8); - self.add_radius_raster(&radii.top_right, &zero_size, false, ImageFormat::A8); - self.add_radius_raster(&radii.bottom_left, &zero_size, false, ImageFormat::A8); - self.add_radius_raster(&radii.bottom_right, &zero_size, false, ImageFormat::A8); + self.add_radius_raster(&radii.top_left, &zero_size, false, 0, ImageFormat::A8); + self.add_radius_raster(&radii.top_right, &zero_size, false, 0, ImageFormat::A8); + self.add_radius_raster(&radii.bottom_left, &zero_size, false, 0, ImageFormat::A8); + self.add_radius_raster(&radii.bottom_right, &zero_size, false, 0, ImageFormat::A8); } pub fn add_box_shadow_corner(&mut self, blur_radius: f32, border_radius: f32, inverted: bool) { @@ -223,28 +226,45 @@ impl BuildRequiredResources for AABBTreeNode { } } SpecificDisplayItem::Border(ref info) => { - resource_list.add_radius_raster(&info.radius.top_left, - &info.top_left_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.top_right, - &info.top_right_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.bottom_left, - &info.bottom_left_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.bottom_right, - &info.bottom_right_inner_radius(), - false, - ImageFormat::A8); + for rect_index in 0..tessellator::quad_count_for_border_corner( + &info.radius.top_left) { + resource_list.add_radius_raster(&info.radius.top_left, + &info.top_left_inner_radius(), + false, + rect_index, + ImageFormat::A8); + } + for rect_index in 0..tessellator::quad_count_for_border_corner( + &info.radius.top_right) { + resource_list.add_radius_raster(&info.radius.top_right, + &info.top_right_inner_radius(), + false, + rect_index, + ImageFormat::A8); + } + for rect_index in 0..tessellator::quad_count_for_border_corner( + &info.radius.bottom_left) { + resource_list.add_radius_raster(&info.radius.bottom_left, + &info.bottom_left_inner_radius(), + false, + rect_index, + ImageFormat::A8); + } + for rect_index in 0..tessellator::quad_count_for_border_corner( + &info.radius.bottom_right) { + resource_list.add_radius_raster(&info.radius.bottom_right, + &info.bottom_right_inner_radius(), + false, + rect_index, + ImageFormat::A8); + } if info.top.style == BorderStyle::Dotted { resource_list.add_radius_raster(&Size2D::new(info.top.width / 2.0, info.top.width / 2.0), &Size2D::new(0.0, 0.0), false, + 0, ImageFormat::RGBA8); } if info.right.style == BorderStyle::Dotted { @@ -252,6 +272,7 @@ impl BuildRequiredResources for AABBTreeNode { info.right.width / 2.0), &Size2D::new(0.0, 0.0), false, + 0, ImageFormat::RGBA8); } if info.bottom.style == BorderStyle::Dotted { @@ -259,6 +280,7 @@ impl BuildRequiredResources for AABBTreeNode { info.bottom.width / 2.0), &Size2D::new(0.0, 0.0), false, + 0, ImageFormat::RGBA8); } if info.left.style == BorderStyle::Dotted { @@ -266,6 +288,7 @@ impl BuildRequiredResources for AABBTreeNode { info.left.width / 2.0), &Size2D::new(0.0, 0.0), false, + 0, ImageFormat::RGBA8); } } diff --git a/src/tessellator.rs b/src/tessellator.rs new file mode 100644 index 0000000000..6825d5022f --- /dev/null +++ b/src/tessellator.rs @@ -0,0 +1,103 @@ +use euclid::{Point2D, Rect, Size2D}; +use internal_types::BasicRotationAngle; + +const EPSILON: f32 = 0.01; + +pub fn quad_count_for_border_corner(outer_radius: &Size2D) -> u32 { + if outer_radius.width < 32.0 && outer_radius.height < 32.0 { + 1 + } else { + 4 + } +} + +pub trait BorderCornerTessellation { + fn tessellate_border_corner(&self, + outer_radius: &Size2D, + inner_radius: &Size2D, + rotation_angle: BasicRotationAngle, + index: u32) + -> Rect; +} + +impl BorderCornerTessellation for Rect { + fn tessellate_border_corner(&self, + outer_radius: &Size2D, + inner_radius: &Size2D, + rotation_angle: BasicRotationAngle, + index: u32) + -> Rect { + let quad_count = quad_count_for_border_corner(outer_radius); + if quad_count == 1 { + return *self + } + + // FIXME(pcwalton): This is basically a hack to keep Acid2 working. We don't currently + // render border corners properly when the corner size is greater than zero but less than + // the radius, and we'll have to modify this when we do. + if self.size.width - outer_radius.width > EPSILON || + self.size.height - outer_radius.height > EPSILON { + println!("self.size.width={:?} outer_radius.width={:?} self.size.height={:?} \ + outer_radius.height={:?}", + self.size.width, + outer_radius.width, + self.size.height, + outer_radius.height); + return Rect::new(Point2D::new(self.origin.x + self.size.width / (quad_count as f32) * + (index as f32), + self.origin.y), + Size2D::new(self.size.width / (quad_count as f32), + self.size.height)) + } + + let delta = outer_radius.width / (quad_count as f32); + let prev_x = delta * (index as f32); + let prev_outer_y = ellipse_y_coordinate(prev_x, outer_radius); + + let next_x = prev_x + delta; + let next_inner_y = ellipse_y_coordinate(next_x, inner_radius); + + let top_left = Point2D::new(prev_x, prev_outer_y); + let bottom_right = Point2D::new(next_x, next_inner_y); + + let subrect = Rect::new(Point2D::new(top_left.x, bottom_right.y), + Size2D::new(bottom_right.x - top_left.x, + top_left.y - bottom_right.y)); + + let subrect = match rotation_angle { + BasicRotationAngle::Upright => { + Rect::new(Point2D::new(outer_radius.width - subrect.max_x(), + outer_radius.height - subrect.max_y()), + subrect.size) + } + BasicRotationAngle::Clockwise90 => { + Rect::new(Point2D::new(subrect.origin.x, + outer_radius.height - subrect.max_y()), + subrect.size) + } + BasicRotationAngle::Clockwise180 => { + subrect + } + BasicRotationAngle::Clockwise270 => { + Rect::new(Point2D::new(outer_radius.width - subrect.max_x(), + subrect.origin.y), + subrect.size) + } + }; + + subrect.translate(&self.origin) + } +} + +fn ellipse_y_coordinate(x: f32, radius: &Size2D) -> f32 { + if radius.width == 0.0 { + return x + } + let radicand = 1.0 - (x / radius.width) * (x / radius.width); + if radicand < 0.0 { + 0.0 + } else { + radius.height * radicand.sqrt() + } +} + diff --git a/src/texture_cache.rs b/src/texture_cache.rs index 156ea52308..cbe8a4602f 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -5,12 +5,14 @@ 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 std::collections::HashMap; use std::collections::hash_map::Entry; use std::collections::hash_state::DefaultState; use std::mem; -use webrender_traits::ImageFormat; +use tessellator::BorderCornerTessellation; use util; +use webrender_traits::ImageFormat; /// The number of bytes we're allowed to use for a texture. const MAX_BYTES_PER_TEXTURE: u32 = 64 * 1024 * 1024; @@ -506,8 +508,16 @@ impl TextureCache { item: &RasterItem) { let update_op = match item { &RasterItem::BorderRadius(ref op) => { - let width = op.outer_radius_x.to_nearest_px() as u32; - let height = op.outer_radius_y.to_nearest_px() as u32; + let rect = Rect::new(Point2D::new(0.0, 0.0), + Size2D::new(op.outer_radius_x.to_f32_px(), + op.outer_radius_y.to_f32_px())); + let tessellated_rect = rect.tessellate_border_corner( + &Size2D::new(op.outer_radius_x.to_f32_px(), op.outer_radius_y.to_f32_px()), + &Size2D::new(op.inner_radius_x.to_f32_px(), op.inner_radius_y.to_f32_px()), + BasicRotationAngle::Upright, + op.index); + let width = tessellated_rect.size.width.round() as u32; + let height = tessellated_rect.size.height.round() as u32; let allocation = self.allocate(image_id, 0, @@ -531,6 +541,7 @@ impl TextureCache { op.outer_radius_y, op.inner_radius_x, op.inner_radius_y, + op.index, op.inverted)), } } diff --git a/src/util.rs b/src/util.rs index 64ecada796..a822602e0b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -74,6 +74,19 @@ pub fn bilerp(point: &Point2D, quad: &Rect, uv: &RectUv) -> Point2D, quad: &Rect, uv: &RectUv) -> RectUv { + let uv_tl = bilerp(&clipped_rect.origin, quad, uv); + let uv_tr = bilerp(&clipped_rect.top_right(), quad, uv); + let uv_br = bilerp(&clipped_rect.bottom_right(), quad, uv); + let uv_bl = bilerp(&clipped_rect.bottom_left(), quad, uv); + RectUv { + top_left: uv_tl, + top_right: uv_tr, + bottom_left: uv_bl, + bottom_right: uv_br, + } +} + // Don't use `euclid`'s `is_empty` because that has effectively has an "and" in the conditional // below instead of an "or". pub fn rect_is_empty(rect: &Rect) -> bool {