mirror of
https://github.com/servo/servo.git
synced 2025-08-10 16:05:43 +01:00
Add layout_2020 support for transformations
This commit is contained in:
parent
9a760cfc02
commit
80b29380f1
238 changed files with 250 additions and 6319 deletions
|
@ -61,7 +61,7 @@ use style::selector_parser::RestyleDamage;
|
|||
use style::servo::restyle_damage::ServoRestyleDamage;
|
||||
use style::str::char_is_whitespace;
|
||||
use style::values::computed::counters::ContentItem;
|
||||
use style::values::computed::{Size, VerticalAlign};
|
||||
use style::values::computed::{Length, Size, VerticalAlign};
|
||||
use style::values::generics::box_::{Perspective, VerticalAlignKeyword};
|
||||
use style::values::generics::transform;
|
||||
use webrender_api;
|
||||
|
@ -3169,9 +3169,19 @@ impl Fragment {
|
|||
stacking_relative_border_box: &Rect<Au>,
|
||||
) -> Option<LayoutTransform> {
|
||||
let list = &self.style.get_box().transform;
|
||||
let border_box_as_length = Rect::new(
|
||||
Point2D::new(
|
||||
Length::new(stacking_relative_border_box.origin.x.to_f32_px()),
|
||||
Length::new(stacking_relative_border_box.origin.y.to_f32_px()),
|
||||
),
|
||||
Size2D::new(
|
||||
Length::new(stacking_relative_border_box.size.width.to_f32_px()),
|
||||
Length::new(stacking_relative_border_box.size.height.to_f32_px()),
|
||||
),
|
||||
);
|
||||
let transform = LayoutTransform::from_untyped(
|
||||
&list
|
||||
.to_transform_3d_matrix(Some(stacking_relative_border_box))
|
||||
.to_transform_3d_matrix(Some(&border_box_as_length))
|
||||
.ok()?
|
||||
.0,
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize};
|
||||
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
||||
use style::computed_values::transform_style::T as ComputedTransformStyle;
|
||||
use style::values::computed::Filter as ComputedFilter;
|
||||
use style::values::computed::Length;
|
||||
use webrender_api as wr;
|
||||
|
@ -57,6 +58,16 @@ impl ToWebRender for ComputedMixBlendMode {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for ComputedTransformStyle {
|
||||
type Type = wr::TransformStyle;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
match *self {
|
||||
ComputedTransformStyle::Flat => wr::TransformStyle::Flat,
|
||||
ComputedTransformStyle::Preserve3d => wr::TransformStyle::Preserve3D,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalPoint<Length> {
|
||||
type Type = webrender_api::units::LayoutPoint;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::display_list::conversions::ToWebRender;
|
|||
use crate::display_list::DisplayListBuilder;
|
||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
|
||||
use crate::geom::PhysicalRect;
|
||||
use euclid::default::Rect;
|
||||
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
|
@ -15,9 +16,11 @@ use style::computed_values::overflow_x::T as ComputedOverflow;
|
|||
use style::computed_values::position::T as ComputedPosition;
|
||||
use style::computed_values::transform_style::T as ComputedTransformStyle;
|
||||
use style::values::computed::Length;
|
||||
use style::values::generics::box_::Perspective;
|
||||
use style::values::generics::transform;
|
||||
use style::values::specified::box_::DisplayOutside;
|
||||
use webrender_api as wr;
|
||||
use webrender_api::units::{LayoutPoint, LayoutVector2D};
|
||||
use webrender_api::units::{LayoutPoint, LayoutTransform, LayoutVector2D};
|
||||
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub(crate) enum StackingContextSection {
|
||||
|
@ -299,7 +302,11 @@ impl BoxFragment {
|
|||
return true;
|
||||
}
|
||||
|
||||
if self.has_filter_transform_or_perspective() {
|
||||
if self.has_transform_or_perspective() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if !self.style.get_effects().filter.0.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -340,11 +347,10 @@ impl BoxFragment {
|
|||
0
|
||||
}
|
||||
|
||||
/// Returns true if this fragment has a filter, transform, or perspective property set.
|
||||
fn has_filter_transform_or_perspective(&self) -> bool {
|
||||
// TODO(mrobinson): We need to handle perspective here.
|
||||
/// Returns true if this fragment has a transform, or perspective property set.
|
||||
fn has_transform_or_perspective(&self) -> bool {
|
||||
!self.style.get_box().transform.0.is_empty() ||
|
||||
!self.style.get_effects().filter.0.is_empty()
|
||||
self.style.get_box().perspective != Perspective::None
|
||||
}
|
||||
|
||||
fn build_stacking_context_tree<'a>(
|
||||
|
@ -363,7 +369,7 @@ impl BoxFragment {
|
|||
self.build_stacking_context_tree_for_children(
|
||||
fragment,
|
||||
builder,
|
||||
containing_block,
|
||||
*containing_block,
|
||||
stacking_context,
|
||||
);
|
||||
return;
|
||||
|
@ -374,7 +380,7 @@ impl BoxFragment {
|
|||
self.build_stacking_context_tree_for_children(
|
||||
fragment,
|
||||
builder,
|
||||
containing_block,
|
||||
*containing_block,
|
||||
&mut child_stacking_context,
|
||||
);
|
||||
|
||||
|
@ -400,23 +406,38 @@ impl BoxFragment {
|
|||
&'a self,
|
||||
fragment: &'a Fragment,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
mut containing_block: PhysicalRect<Length>,
|
||||
stacking_context: &mut StackingContext<'a>,
|
||||
) {
|
||||
let relative_border_rect = self
|
||||
.border_rect()
|
||||
.to_physical(self.style.writing_mode, &containing_block);
|
||||
let border_rect = relative_border_rect.translate(containing_block.origin.to_vector());
|
||||
let established_reference_frame =
|
||||
self.build_reference_frame_if_necessary(builder, &border_rect);
|
||||
|
||||
// WebRender reference frames establish a new coordinate system at their origin
|
||||
// (the border box of the fragment). We need to ensure that any coordinates we
|
||||
// give to WebRender in this reference frame are relative to the fragment border
|
||||
// box. We do this by adjusting the containing block origin.
|
||||
if established_reference_frame {
|
||||
containing_block.origin = (-relative_border_rect.origin.to_vector()).to_point();
|
||||
}
|
||||
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
space_and_clip: builder.current_space_and_clip,
|
||||
section: self.get_stacking_context_section(),
|
||||
containing_block: *containing_block,
|
||||
containing_block: containing_block,
|
||||
fragment,
|
||||
});
|
||||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
// they shouldn't scroll with the rest of the box content.
|
||||
self.build_scroll_frame_if_necessary(builder, containing_block);
|
||||
self.build_scroll_frame_if_necessary(builder, &containing_block);
|
||||
|
||||
let new_containing_block = self
|
||||
.content_rect
|
||||
.to_physical(self.style.writing_mode, containing_block)
|
||||
.to_physical(self.style.writing_mode, &containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
for child in &self.children {
|
||||
child.build_stacking_context_tree(builder, &new_containing_block, stacking_context);
|
||||
|
@ -476,6 +497,128 @@ impl BoxFragment {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a reference frame for this fragment if it is necessary. Returns `true` if
|
||||
/// a reference was built and `false` otherwise.
|
||||
fn build_reference_frame_if_necessary(
|
||||
&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
border_rect: &PhysicalRect<Length>,
|
||||
) -> bool {
|
||||
if !self.has_transform_or_perspective() {
|
||||
return false;
|
||||
}
|
||||
let untyped_border_rect = border_rect.to_untyped();
|
||||
let transform = self.calculate_transform_matrix(&untyped_border_rect);
|
||||
let perspective = self.calculate_perspective_matrix(&untyped_border_rect);
|
||||
let (reference_frame_transform, reference_frame_kind) = match (transform, perspective) {
|
||||
(None, Some(perspective)) => (
|
||||
perspective,
|
||||
wr::ReferenceFrameKind::Perspective {
|
||||
scrolling_relative_to: None,
|
||||
},
|
||||
),
|
||||
(Some(transform), None) => (transform, wr::ReferenceFrameKind::Transform),
|
||||
(Some(transform), Some(perspective)) => (
|
||||
transform.pre_transform(&perspective),
|
||||
wr::ReferenceFrameKind::Perspective {
|
||||
scrolling_relative_to: None,
|
||||
},
|
||||
),
|
||||
(None, None) => unreachable!(),
|
||||
};
|
||||
|
||||
builder.current_space_and_clip.spatial_id = builder.wr.push_reference_frame(
|
||||
border_rect.origin.to_webrender(),
|
||||
builder.current_space_and_clip.spatial_id,
|
||||
self.style.get_box().transform_style.to_webrender(),
|
||||
wr::PropertyBinding::Value(reference_frame_transform),
|
||||
reference_frame_kind,
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the 4D matrix representing this fragment's transform.
|
||||
pub fn calculate_transform_matrix(
|
||||
&self,
|
||||
border_rect: &Rect<Length>,
|
||||
) -> Option<LayoutTransform> {
|
||||
let list = &self.style.get_box().transform;
|
||||
let transform =
|
||||
LayoutTransform::from_untyped(&list.to_transform_3d_matrix(Some(&border_rect)).ok()?.0);
|
||||
|
||||
let transform_origin = &self.style.get_box().transform_origin;
|
||||
let transform_origin_x = transform_origin
|
||||
.horizontal
|
||||
.percentage_relative_to(border_rect.size.width)
|
||||
.px();
|
||||
let transform_origin_y = transform_origin
|
||||
.vertical
|
||||
.percentage_relative_to(border_rect.size.height)
|
||||
.px();
|
||||
let transform_origin_z = transform_origin.depth.px();
|
||||
|
||||
let pre_transform = LayoutTransform::create_translation(
|
||||
transform_origin_x,
|
||||
transform_origin_y,
|
||||
transform_origin_z,
|
||||
);
|
||||
let post_transform = LayoutTransform::create_translation(
|
||||
-transform_origin_x,
|
||||
-transform_origin_y,
|
||||
-transform_origin_z,
|
||||
);
|
||||
|
||||
Some(
|
||||
pre_transform
|
||||
.pre_transform(&transform)
|
||||
.pre_transform(&post_transform),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the 4D matrix representing this fragment's perspective.
|
||||
pub fn calculate_perspective_matrix(
|
||||
&self,
|
||||
border_rect: &Rect<Length>,
|
||||
) -> Option<LayoutTransform> {
|
||||
match self.style.get_box().perspective {
|
||||
Perspective::Length(length) => {
|
||||
let perspective_origin = &self.style.get_box().perspective_origin;
|
||||
let perspective_origin = LayoutPoint::new(
|
||||
perspective_origin
|
||||
.horizontal
|
||||
.percentage_relative_to(border_rect.size.width)
|
||||
.px(),
|
||||
perspective_origin
|
||||
.vertical
|
||||
.percentage_relative_to(border_rect.size.height)
|
||||
.px(),
|
||||
);
|
||||
|
||||
let pre_transform = LayoutTransform::create_translation(
|
||||
perspective_origin.x,
|
||||
perspective_origin.y,
|
||||
0.0,
|
||||
);
|
||||
let post_transform = LayoutTransform::create_translation(
|
||||
-perspective_origin.x,
|
||||
-perspective_origin.y,
|
||||
0.0,
|
||||
);
|
||||
|
||||
let perspective_matrix = LayoutTransform::from_untyped(
|
||||
&transform::create_perspective_matrix(length.px()),
|
||||
);
|
||||
|
||||
Some(
|
||||
pre_transform
|
||||
.pre_transform(&perspective_matrix)
|
||||
.pre_transform(&post_transform),
|
||||
)
|
||||
},
|
||||
Perspective::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnonymousFragment {
|
||||
|
|
|
@ -345,7 +345,6 @@ ${helpers.predefined_type(
|
|||
"Transform",
|
||||
"generics::transform::Transform::none()",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
animation_value_type="ComputedValue",
|
||||
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
|
||||
|
@ -549,7 +548,7 @@ ${helpers.predefined_type(
|
|||
"perspective",
|
||||
"Perspective",
|
||||
"computed::Perspective::none()",
|
||||
engines="gecko servo-2013",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
gecko_ffi_name="mChildPerspective",
|
||||
spec="https://drafts.csswg.org/css-transforms/#perspective",
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
|
@ -562,7 +561,7 @@ ${helpers.predefined_type(
|
|||
"perspective-origin",
|
||||
"Position",
|
||||
"computed::position::Position::center()",
|
||||
engines="gecko servo-2013",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
boxed=True,
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
|
||||
|
@ -573,7 +572,7 @@ ${helpers.predefined_type(
|
|||
${helpers.single_keyword(
|
||||
"backface-visibility",
|
||||
"visible hidden",
|
||||
engines="gecko servo-2013",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
animation_value_type="discrete",
|
||||
|
@ -595,7 +594,6 @@ ${helpers.predefined_type(
|
|||
"TransformStyle",
|
||||
"computed::TransformStyle::Flat",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
|
||||
needs_context=False,
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
|
@ -608,7 +606,7 @@ ${helpers.predefined_type(
|
|||
"transform-origin",
|
||||
"TransformOrigin",
|
||||
"computed::TransformOrigin::initial_value()",
|
||||
engines="gecko servo-2013",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
animation_value_type="ComputedValue",
|
||||
extra_prefixes=transform_extra_prefixes,
|
||||
gecko_ffi_name="mTransformOrigin",
|
||||
|
|
|
@ -11,7 +11,6 @@ use crate::values::specified::length::Length as SpecifiedLength;
|
|||
use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
|
||||
use crate::values::{computed, CSSFloat};
|
||||
use crate::Zero;
|
||||
use app_units::Au;
|
||||
use euclid;
|
||||
use euclid::default::{Rect, Transform3D};
|
||||
use std::fmt::{self, Write};
|
||||
|
@ -329,7 +328,7 @@ where
|
|||
/// Convert a length type into the absolute lengths.
|
||||
pub trait ToAbsoluteLength {
|
||||
/// Returns the absolute length as pixel value.
|
||||
fn to_pixel_length(&self, containing_len: Option<Au>) -> Result<CSSFloat, ()>;
|
||||
fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
|
||||
}
|
||||
|
||||
impl ToAbsoluteLength for SpecifiedLength {
|
||||
|
@ -337,7 +336,7 @@ impl ToAbsoluteLength for SpecifiedLength {
|
|||
// parsing a transform list of DOMMatrix because we want to return a DOM Exception
|
||||
// if there is relative length.
|
||||
#[inline]
|
||||
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||
fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
|
||||
match *self {
|
||||
SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
|
||||
SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
|
||||
|
@ -350,7 +349,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage {
|
|||
// parsing a transform list of DOMMatrix because we want to return a DOM Exception
|
||||
// if there is relative length.
|
||||
#[inline]
|
||||
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||
fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
|
||||
use self::SpecifiedLengthPercentage::*;
|
||||
match *self {
|
||||
Length(len) => len.to_computed_pixel_length_without_context(),
|
||||
|
@ -362,16 +361,16 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage {
|
|||
|
||||
impl ToAbsoluteLength for ComputedLength {
|
||||
#[inline]
|
||||
fn to_pixel_length(&self, _containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||
fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
|
||||
Ok(self.px())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAbsoluteLength for ComputedLengthPercentage {
|
||||
#[inline]
|
||||
fn to_pixel_length(&self, containing_len: Option<Au>) -> Result<CSSFloat, ()> {
|
||||
fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
|
||||
match containing_len {
|
||||
Some(relative_len) => Ok(self.to_pixel_length(relative_len).px()),
|
||||
Some(relative_len) => Ok(self.resolve(relative_len).px()),
|
||||
// If we don't have reference box, we cannot resolve the used value,
|
||||
// so only retrieve the length part. This will be used for computing
|
||||
// distance without any layout info.
|
||||
|
@ -388,7 +387,10 @@ pub trait ToMatrix {
|
|||
fn is_3d(&self) -> bool;
|
||||
|
||||
/// Return the equivalent 3d matrix.
|
||||
fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()>;
|
||||
fn to_3d_matrix(
|
||||
&self,
|
||||
reference_box: Option<&Rect<ComputedLength>>,
|
||||
) -> Result<Transform3D<f64>, ()>;
|
||||
}
|
||||
|
||||
/// A little helper to deal with both specified and computed angles.
|
||||
|
@ -434,7 +436,10 @@ where
|
|||
/// However, for specified TransformOperation, we will return Err(()) if there is any relative
|
||||
/// lengths because the only caller, DOMMatrix, doesn't accept relative lengths.
|
||||
#[inline]
|
||||
fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()> {
|
||||
fn to_3d_matrix(
|
||||
&self,
|
||||
reference_box: Option<&Rect<ComputedLength>>,
|
||||
) -> Result<Transform3D<f64>, ()> {
|
||||
use self::TransformOperation::*;
|
||||
use std::f64;
|
||||
|
||||
|
@ -537,7 +542,7 @@ impl<T: ToMatrix> Transform<T> {
|
|||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
pub fn to_transform_3d_matrix(
|
||||
&self,
|
||||
reference_box: Option<&Rect<Au>>
|
||||
reference_box: Option<&Rect<ComputedLength>>
|
||||
) -> Result<(Transform3D<CSSFloat>, bool), ()> {
|
||||
let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
|
||||
use std::{f32, f64};
|
||||
|
@ -557,7 +562,7 @@ impl<T: ToMatrix> Transform<T> {
|
|||
/// Same as Transform::to_transform_3d_matrix but a f64 version.
|
||||
pub fn to_transform_3d_matrix_f64(
|
||||
&self,
|
||||
reference_box: Option<&Rect<Au>>,
|
||||
reference_box: Option<&Rect<ComputedLength>>,
|
||||
) -> Result<(Transform3D<f64>, bool), ()> {
|
||||
// We intentionally use Transform3D<f64> during computation to avoid error propagation
|
||||
// because using f32 to compute triangle functions (e.g. in create_rotation()) is not
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue