mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Add layout_2020 support for filters and mix-blend-mode
This commit is contained in:
parent
163a4fc7fc
commit
d03560b0f1
54 changed files with 236 additions and 1943 deletions
91
components/layout_2020/display_list/conversions.rs
Normal file
91
components/layout_2020/display_list/conversions.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize};
|
||||
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
||||
use style::values::computed::Filter as ComputedFilter;
|
||||
use style::values::computed::Length;
|
||||
use webrender_api as wr;
|
||||
|
||||
pub trait ToWebRender {
|
||||
type Type;
|
||||
fn to_webrender(&self) -> Self::Type;
|
||||
}
|
||||
|
||||
impl ToWebRender for ComputedFilter {
|
||||
type Type = wr::FilterOp;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
match *self {
|
||||
ComputedFilter::Blur(radius) => wr::FilterOp::Blur(radius.px()),
|
||||
ComputedFilter::Brightness(amount) => wr::FilterOp::Brightness(amount.0),
|
||||
ComputedFilter::Contrast(amount) => wr::FilterOp::Contrast(amount.0),
|
||||
ComputedFilter::Grayscale(amount) => wr::FilterOp::Grayscale(amount.0),
|
||||
ComputedFilter::HueRotate(angle) => wr::FilterOp::HueRotate(angle.radians()),
|
||||
ComputedFilter::Invert(amount) => wr::FilterOp::Invert(amount.0),
|
||||
ComputedFilter::Opacity(amount) => wr::FilterOp::Opacity(amount.0.into(), amount.0),
|
||||
ComputedFilter::Saturate(amount) => wr::FilterOp::Saturate(amount.0),
|
||||
ComputedFilter::Sepia(amount) => wr::FilterOp::Sepia(amount.0),
|
||||
// Statically check that DropShadow is impossible.
|
||||
ComputedFilter::DropShadow(ref shadow) => match *shadow {},
|
||||
// Statically check that Url is impossible.
|
||||
ComputedFilter::Url(ref url) => match *url {},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToWebRender for ComputedMixBlendMode {
|
||||
type Type = wr::MixBlendMode;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
match *self {
|
||||
ComputedMixBlendMode::Normal => wr::MixBlendMode::Normal,
|
||||
ComputedMixBlendMode::Multiply => wr::MixBlendMode::Multiply,
|
||||
ComputedMixBlendMode::Screen => wr::MixBlendMode::Screen,
|
||||
ComputedMixBlendMode::Overlay => wr::MixBlendMode::Overlay,
|
||||
ComputedMixBlendMode::Darken => wr::MixBlendMode::Darken,
|
||||
ComputedMixBlendMode::Lighten => wr::MixBlendMode::Lighten,
|
||||
ComputedMixBlendMode::ColorDodge => wr::MixBlendMode::ColorDodge,
|
||||
ComputedMixBlendMode::ColorBurn => wr::MixBlendMode::ColorBurn,
|
||||
ComputedMixBlendMode::HardLight => wr::MixBlendMode::HardLight,
|
||||
ComputedMixBlendMode::SoftLight => wr::MixBlendMode::SoftLight,
|
||||
ComputedMixBlendMode::Difference => wr::MixBlendMode::Difference,
|
||||
ComputedMixBlendMode::Exclusion => wr::MixBlendMode::Exclusion,
|
||||
ComputedMixBlendMode::Hue => wr::MixBlendMode::Hue,
|
||||
ComputedMixBlendMode::Saturation => wr::MixBlendMode::Saturation,
|
||||
ComputedMixBlendMode::Color => wr::MixBlendMode::Color,
|
||||
ComputedMixBlendMode::Luminosity => wr::MixBlendMode::Luminosity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalPoint<Length> {
|
||||
type Type = webrender_api::units::LayoutPoint;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutPoint::new(self.x.px(), self.y.px())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalSize<Length> {
|
||||
type Type = webrender_api::units::LayoutSize;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutSize::new(self.width.px(), self.height.px())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalRect<Length> {
|
||||
type Type = webrender_api::units::LayoutRect;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutRect::new(self.origin.to_webrender(), self.size.to_webrender())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalSides<Length> {
|
||||
type Type = webrender_api::units::LayoutSideOffsets;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutSideOffsets::new(
|
||||
self.top.px(),
|
||||
self.right.px(),
|
||||
self.bottom.px(),
|
||||
self.left.px(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,9 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::context::LayoutContext;
|
||||
use crate::display_list::conversions::ToWebRender;
|
||||
use crate::fragments::{BoxFragment, Fragment};
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect, ToWebRender};
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect};
|
||||
use crate::replaced::IntrinsicSizes;
|
||||
use embedder_traits::Cursor;
|
||||
use euclid::{Point2D, SideOffsets2D, Size2D};
|
||||
|
@ -20,6 +21,7 @@ use style::values::specified::ui::CursorKind;
|
|||
use webrender_api::{self as wr, units};
|
||||
|
||||
mod background;
|
||||
mod conversions;
|
||||
mod gradient;
|
||||
pub mod stacking_context;
|
||||
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::display_list::conversions::ToWebRender;
|
||||
use crate::display_list::DisplayListBuilder;
|
||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
|
||||
use crate::geom::{PhysicalRect, ToWebRender};
|
||||
use crate::geom::PhysicalRect;
|
||||
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
|
@ -15,8 +16,8 @@ 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::specified::box_::DisplayOutside;
|
||||
use webrender_api::units::LayoutVector2D;
|
||||
use webrender_api::{ExternalScrollId, ScrollSensitivity, SpaceAndClipInfo, SpatialId};
|
||||
use webrender_api as wr;
|
||||
use webrender_api::units::{LayoutPoint, LayoutVector2D};
|
||||
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub(crate) enum StackingContextSection {
|
||||
|
@ -26,7 +27,7 @@ pub(crate) enum StackingContextSection {
|
|||
}
|
||||
|
||||
pub(crate) struct StackingContextFragment<'a> {
|
||||
space_and_clip: SpaceAndClipInfo,
|
||||
space_and_clip: wr::SpaceAndClipInfo,
|
||||
section: StackingContextSection,
|
||||
containing_block: PhysicalRect<Length>,
|
||||
fragment: &'a Fragment,
|
||||
|
@ -49,12 +50,12 @@ pub(crate) enum StackingContextType {
|
|||
}
|
||||
|
||||
pub(crate) struct StackingContext<'a> {
|
||||
/// The fragment that established this stacking context.
|
||||
initializing_fragment: Option<&'a BoxFragment>,
|
||||
|
||||
/// The type of this StackingContext. Used for collecting and sorting.
|
||||
context_type: StackingContextType,
|
||||
|
||||
/// The `z-index` for this stacking context.
|
||||
pub z_index: i32,
|
||||
|
||||
/// Fragments that make up the content of this stacking context.
|
||||
fragments: Vec<StackingContextFragment<'a>>,
|
||||
|
||||
|
@ -67,22 +68,44 @@ pub(crate) struct StackingContext<'a> {
|
|||
}
|
||||
|
||||
impl<'a> StackingContext<'a> {
|
||||
pub(crate) fn new(context_type: StackingContextType, z_index: i32) -> Self {
|
||||
pub(crate) fn new(
|
||||
initializing_fragment: &'a BoxFragment,
|
||||
context_type: StackingContextType,
|
||||
) -> Self {
|
||||
Self {
|
||||
initializing_fragment: Some(initializing_fragment),
|
||||
context_type,
|
||||
z_index,
|
||||
fragments: vec![],
|
||||
stacking_contexts: vec![],
|
||||
float_stacking_contexts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_root() -> Self {
|
||||
Self {
|
||||
initializing_fragment: None,
|
||||
context_type: StackingContextType::Real,
|
||||
fragments: vec![],
|
||||
stacking_contexts: vec![],
|
||||
float_stacking_contexts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn z_index(&self) -> i32 {
|
||||
match self.initializing_fragment {
|
||||
Some(fragment) => fragment.effective_z_index(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sort(&mut self) {
|
||||
self.fragments.sort_by(|a, b| a.section.cmp(&b.section));
|
||||
|
||||
self.stacking_contexts.sort_by(|a, b| {
|
||||
if a.z_index != 0 || b.z_index != 0 {
|
||||
return a.z_index.cmp(&b.z_index);
|
||||
let a_z_index = a.z_index();
|
||||
let b_z_index = b.z_index();
|
||||
if a_z_index != 0 || b_z_index != 0 {
|
||||
return a_z_index.cmp(&b_z_index);
|
||||
}
|
||||
|
||||
match (a.context_type, b.context_type) {
|
||||
|
@ -96,7 +119,60 @@ impl<'a> StackingContext<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
fn push_webrender_stacking_context_if_necessary(
|
||||
&self,
|
||||
builder: &'a mut DisplayListBuilder,
|
||||
) -> bool {
|
||||
let fragment = match self.initializing_fragment {
|
||||
Some(fragment) => fragment,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// WebRender only uses the stacking context to apply certain effects. If we don't
|
||||
// actually need to create a stacking context, just avoid creating one.
|
||||
let effects = fragment.style.get_effects();
|
||||
if effects.filter.0.is_empty() &&
|
||||
effects.opacity == 1.0 &&
|
||||
effects.mix_blend_mode == ComputedMixBlendMode::Normal
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the filter pipeline.
|
||||
let mut filters: Vec<wr::FilterOp> = effects
|
||||
.filter
|
||||
.0
|
||||
.iter()
|
||||
.map(ToWebRender::to_webrender)
|
||||
.collect();
|
||||
if effects.opacity != 1.0 {
|
||||
filters.push(wr::FilterOp::Opacity(
|
||||
effects.opacity.into(),
|
||||
effects.opacity,
|
||||
));
|
||||
}
|
||||
|
||||
builder.wr.push_stacking_context(
|
||||
LayoutPoint::zero(), // origin
|
||||
builder.current_space_and_clip.spatial_id, // spatial_id
|
||||
wr::PrimitiveFlags::default(),
|
||||
None, // clip_id
|
||||
wr::TransformStyle::Flat,
|
||||
effects.mix_blend_mode.to_webrender(),
|
||||
&filters,
|
||||
&vec![], // filter_datas
|
||||
&vec![], // filter_primitives
|
||||
wr::RasterSpace::Screen,
|
||||
false, // cache_tiles,
|
||||
false, // false
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn build_display_list(&'a self, builder: &'a mut DisplayListBuilder) {
|
||||
let pushed_context = self.push_webrender_stacking_context_if_necessary(builder);
|
||||
|
||||
// Properly order display items that make up a stacking context. "Steps" here
|
||||
// refer to the steps in CSS 2.1 Appendix E.
|
||||
|
||||
|
@ -112,7 +188,7 @@ impl<'a> StackingContext<'a> {
|
|||
let mut child_stacking_contexts = self.stacking_contexts.iter().peekable();
|
||||
while child_stacking_contexts
|
||||
.peek()
|
||||
.map_or(false, |child| child.z_index < 0)
|
||||
.map_or(false, |child| child.z_index() < 0)
|
||||
{
|
||||
let child_context = child_stacking_contexts.next().unwrap();
|
||||
child_context.build_display_list(builder);
|
||||
|
@ -142,6 +218,10 @@ impl<'a> StackingContext<'a> {
|
|||
for child_context in child_stacking_contexts {
|
||||
child_context.build_display_list(builder);
|
||||
}
|
||||
|
||||
if pushed_context {
|
||||
builder.wr.pop_stacking_context();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,11 +290,12 @@ impl BoxFragment {
|
|||
|
||||
/// Returns true if this fragment establishes a new stacking context and false otherwise.
|
||||
fn establishes_stacking_context(&self) -> bool {
|
||||
if self.style.get_effects().opacity != 1.0 {
|
||||
let effects = self.style.get_effects();
|
||||
if effects.opacity != 1.0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.style.get_effects().mix_blend_mode != ComputedMixBlendMode::Normal {
|
||||
if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -289,8 +370,7 @@ impl BoxFragment {
|
|||
},
|
||||
};
|
||||
|
||||
let mut child_stacking_context =
|
||||
StackingContext::new(context_type, self.effective_z_index());
|
||||
let mut child_stacking_context = StackingContext::new(self, context_type);
|
||||
self.build_stacking_context_tree_for_children(
|
||||
fragment,
|
||||
builder,
|
||||
|
@ -352,7 +432,7 @@ impl BoxFragment {
|
|||
// frame that is the parent of this one once we have full support for stacking
|
||||
// contexts and transforms.
|
||||
builder.current_space_and_clip.spatial_id =
|
||||
SpatialId::root_reference_frame(builder.wr.pipeline_id);
|
||||
wr::SpatialId::root_reference_frame(builder.wr.pipeline_id);
|
||||
}
|
||||
|
||||
fn build_scroll_frame_if_necessary(
|
||||
|
@ -369,14 +449,14 @@ impl BoxFragment {
|
|||
let id =
|
||||
combine_id_with_fragment_type(self.tag.id() as usize, FragmentType::FragmentBody)
|
||||
as u64;
|
||||
let external_id = ExternalScrollId(id, builder.wr.pipeline_id);
|
||||
let external_id = wr::ExternalScrollId(id, builder.wr.pipeline_id);
|
||||
|
||||
let sensitivity = if ComputedOverflow::Hidden == overflow_x &&
|
||||
ComputedOverflow::Hidden == overflow_y
|
||||
{
|
||||
ScrollSensitivity::Script
|
||||
wr::ScrollSensitivity::Script
|
||||
} else {
|
||||
ScrollSensitivity::ScriptAndInputEvents
|
||||
wr::ScrollSensitivity::ScriptAndInputEvents
|
||||
};
|
||||
|
||||
let padding_rect = self
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::context::LayoutContext;
|
||||
use crate::display_list::stacking_context::{StackingContext, StackingContextType};
|
||||
use crate::display_list::stacking_context::StackingContext;
|
||||
use crate::dom_traversal::{Contents, NodeExt};
|
||||
use crate::flow::construct::ContainsFloats;
|
||||
use crate::flow::float::FloatBox;
|
||||
|
@ -182,7 +182,7 @@ impl BoxTreeRoot {
|
|||
|
||||
impl FragmentTreeRoot {
|
||||
pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) {
|
||||
let mut stacking_context = StackingContext::new(StackingContextType::Real, 0);
|
||||
let mut stacking_context = StackingContext::create_root();
|
||||
for fragment in &self.children {
|
||||
fragment.build_stacking_context_tree(
|
||||
builder,
|
||||
|
|
|
@ -354,41 +354,3 @@ impl<T> flow_relative::Rect<T> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToWebRender {
|
||||
type Type;
|
||||
fn to_webrender(&self) -> Self::Type;
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalPoint<Length> {
|
||||
type Type = webrender_api::units::LayoutPoint;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutPoint::new(self.x.px(), self.y.px())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalSize<Length> {
|
||||
type Type = webrender_api::units::LayoutSize;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutSize::new(self.width.px(), self.height.px())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalRect<Length> {
|
||||
type Type = webrender_api::units::LayoutRect;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutRect::new(self.origin.to_webrender(), self.size.to_webrender())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWebRender for PhysicalSides<Length> {
|
||||
type Type = webrender_api::units::LayoutSideOffsets;
|
||||
fn to_webrender(&self) -> Self::Type {
|
||||
webrender_api::units::LayoutSideOffsets::new(
|
||||
self.top.px(),
|
||||
self.right.px(),
|
||||
self.bottom.px(),
|
||||
self.left.px(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ ${helpers.predefined_type(
|
|||
"Opacity",
|
||||
"1.0",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
animation_value_type="ComputedValue",
|
||||
flags="CREATES_STACKING_CONTEXT CAN_ANIMATE_ON_COMPOSITOR",
|
||||
spec="https://drafts.csswg.org/css-color/#transparency",
|
||||
|
@ -51,7 +50,6 @@ ${helpers.predefined_type(
|
|||
"Filter",
|
||||
None,
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
vector=True,
|
||||
simple_vector_bindings=True,
|
||||
gecko_ffi_name="mFilters",
|
||||
|
@ -85,7 +83,6 @@ ${helpers.single_keyword(
|
|||
color-burn hard-light soft-light difference exclusion hue
|
||||
saturation color luminosity""",
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
gecko_constant_prefix="NS_STYLE_BLEND",
|
||||
animation_value_type="discrete",
|
||||
flags="CREATES_STACKING_CONTEXT",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue