diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index bc89c2b412..2a941c3a16 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -1229,11 +1229,9 @@ fn add_clip_node_to_current_chain( let conversion = if spatial_node_index == clip_spatial_node_index { Some(ClipSpaceConversion::Local) } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { - let scale_offset = ref_spatial_node - .coordinate_system_relative_scale_offset - .difference( - &clip_spatial_node.coordinate_system_relative_scale_offset - ); + let scale_offset = ref_spatial_node.coordinate_system_relative_scale_offset + .inverse() + .accumulate(&clip_spatial_node.coordinate_system_relative_scale_offset); Some(ClipSpaceConversion::ScaleOffset(scale_offset)) } else { let xf = clip_scroll_tree.get_relative_transform( diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 16acad6178..5eecd7aeee 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -131,7 +131,8 @@ impl SpaceMapper where F: fmt::Debug { } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id { CoordinateSpaceMapping::ScaleOffset( ref_spatial_node.coordinate_system_relative_scale_offset - .difference( + .inverse() + .accumulate( &target_spatial_node.coordinate_system_relative_scale_offset ) ) diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 2314184841..2313f4394f 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -83,10 +83,12 @@ impl ScaleOffset { } pub fn offset(&self, offset: Vector2D) -> Self { - ScaleOffset { - scale: self.scale, - offset: self.offset + offset, - } + self.accumulate( + &ScaleOffset { + scale: Vector2D::new(1.0, 1.0), + offset, + } + ) } // Produce a ScaleOffset that includes both self @@ -105,20 +107,6 @@ impl ScaleOffset { } } - // Find the difference between two ScaleOffset types. - pub fn difference(&self, other: &ScaleOffset) -> Self { - ScaleOffset { - scale: Vector2D::new( - other.scale.x / self.scale.x, - other.scale.y / self.scale.y, - ), - offset: Vector2D::new( - (other.offset.x - self.offset.x) / self.scale.x, - (other.offset.y - self.offset.y) / self.scale.y, - ), - } - } - pub fn map_rect(&self, rect: &TypedRect) -> TypedRect { TypedRect::new( TypedPoint2D::new( @@ -378,6 +366,7 @@ pub fn extract_inner_rect_safe( #[cfg(test)] pub mod test { + use api::{LayoutTransform, LayoutVector3D}; use super::*; use euclid::{Point2D, Angle, Transform3D}; use std::f32::consts::PI; @@ -392,6 +381,76 @@ pub mod test { // rotation by 60 degrees would imply scaling of X component by a factor of 2 assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0))); } + + fn validate_convert(xref: &LayoutTransform) { + let so = ScaleOffset::from_transform(xref).unwrap(); + let xf = so.to_transform(); + assert!(xref.approx_eq(&xf)); + } + + #[test] + fn scale_offset_convert() { + let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0); + validate_convert(&xref); + + let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0); + validate_convert(&xref); + + let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0) + .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); + validate_convert(&xref); + + let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0) + .pre_mul(&LayoutTransform::create_scale(30.0, 11.0, 1.0)); + validate_convert(&xref); + } + + fn validate_inverse(xref: &LayoutTransform) { + let s0 = ScaleOffset::from_transform(xref).unwrap(); + let s1 = s0.inverse().accumulate(&s0); + assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO && + (s1.scale.y - 1.0).abs() < NEARLY_ZERO && + s1.offset.x.abs() < NEARLY_ZERO && + s1.offset.y.abs() < NEARLY_ZERO, + "{:?}", + s1); + } + + #[test] + fn scale_offset_inverse() { + let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0); + validate_inverse(&xref); + + let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0); + validate_inverse(&xref); + + let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0) + .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); + validate_inverse(&xref); + + let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0) + .pre_mul(&LayoutTransform::create_scale(30.0, 11.0, 1.0)); + validate_inverse(&xref); + } + + fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) { + let x = x0.pre_mul(x1); + + let s0 = ScaleOffset::from_transform(x0).unwrap(); + let s1 = ScaleOffset::from_transform(x1).unwrap(); + + let s = s0.accumulate(&s1).to_transform(); + + assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s); + } + + #[test] + fn scale_offset_accumulate() { + let x0 = LayoutTransform::create_translation(130.0, 200.0, 0.0); + let x1 = LayoutTransform::create_scale(7.0, 3.0, 1.0); + + validate_accumulate(&x0, &x1); + } } pub trait MaxRect { diff --git a/wrench/reftests/scrolling/reftest.list b/wrench/reftests/scrolling/reftest.list index c3225a2c13..ed6be97763 100644 --- a/wrench/reftests/scrolling/reftest.list +++ b/wrench/reftests/scrolling/reftest.list @@ -14,3 +14,4 @@ == sticky-applied.yaml sticky-applied-ref.yaml == sticky-transformed.yaml sticky-transformed-ref.yaml == sibling-hidden-clip.yaml sibling-hidden-clip-ref.yaml +== scale-offsets.yaml scale-offsets-ref.yaml diff --git a/wrench/reftests/scrolling/scale-offsets-ref.yaml b/wrench/reftests/scrolling/scale-offsets-ref.yaml new file mode 100644 index 0000000000..9276ded8f8 --- /dev/null +++ b/wrench/reftests/scrolling/scale-offsets-ref.yaml @@ -0,0 +1,5 @@ +root: + items: + - type: rect + color: green + bounds: [50, 50, 50, 50] diff --git a/wrench/reftests/scrolling/scale-offsets.yaml b/wrench/reftests/scrolling/scale-offsets.yaml new file mode 100644 index 0000000000..5bfc2c22c9 --- /dev/null +++ b/wrench/reftests/scrolling/scale-offsets.yaml @@ -0,0 +1,16 @@ +# Test that a scroll-frame nested within a scale +# transform correctly calculates local offsets. +root: + items: + - type: stacking-context + transform: scale(0.5) + items: + - type: scroll-frame + bounds: [100, 100, 100, 100] + content-size: [260, 260] + scroll-offset: [0, 60] + items: + - type: rect + color: green + bounds: [100, 100, 260, 260] + hit-testing-tag: [0, 0]