aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020/display_list/mod.rs
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-07-30 08:41:23 +0200
committerGitHub <noreply@github.com>2024-07-30 06:41:23 +0000
commite23dc0bf6f5ed6dec0537a3f4f1c668937c87246 (patch)
tree2ce1daa039d3ec09a0312bfd67e55daf6e9e0c76 /components/layout_2020/display_list/mod.rs
parent29a4cca42d01ab7acf6610362cd1165d2c2e85f7 (diff)
downloadservo-e23dc0bf6f5ed6dec0537a3f4f1c668937c87246.tar.gz
servo-e23dc0bf6f5ed6dec0537a3f4f1c668937c87246.zip
layout: Port `border-image` support for legacy layout (#32874)
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Diffstat (limited to 'components/layout_2020/display_list/mod.rs')
-rw-r--r--components/layout_2020/display_list/mod.rs223
1 files changed, 215 insertions, 8 deletions
diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs
index 0049ebebf35..0898c8dc661 100644
--- a/components/layout_2020/display_list/mod.rs
+++ b/components/layout_2020/display_list/mod.rs
@@ -9,21 +9,34 @@ use app_units::Au;
use base::id::BrowsingContextId;
use base::WebRenderEpochToU16;
use embedder_traits::Cursor;
-use euclid::{Point2D, SideOffsets2D, Size2D};
+use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
use fnv::FnvHashMap;
use fonts::GlyphStore;
+use gradient::WebRenderGradient;
use net_traits::image_cache::UsePlaceholder;
use servo_geometry::MaxRect;
use style::color::{AbsoluteColor, ColorSpace};
+use style::computed_values::border_image_outset::T as BorderImageOutset;
use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle;
use style::dom::OpaqueNode;
use style::properties::longhands::visibility::computed_value::T as Visibility;
+use style::properties::style_structs::Border;
use style::properties::ComputedValues;
-use style::values::computed::{BorderStyle, Color, Length, LengthPercentage, OutlineStyle};
+use style::values::computed::image::Image;
+use style::values::computed::{
+ BorderImageSideWidth, BorderImageWidth, BorderStyle, Color, Length, LengthPercentage,
+ NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
+};
+use style::values::generics::rect::Rect;
+use style::values::generics::NonNegative;
use style::values::specified::text::TextDecorationLine;
use style::values::specified::ui::CursorKind;
-use style_traits::CSSPixel;
-use webrender_api::{self as wr, units, BoxShadowClipMode, ClipChainId};
+use style_traits::{CSSPixel, DevicePixel};
+use webrender_api::units::LayoutPixel;
+use webrender_api::{
+ self as wr, units, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties,
+ ImageRendering, NinePatchBorder, NinePatchBorderSource,
+};
use webrender_traits::display_list::{
CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId,
};
@@ -740,7 +753,6 @@ impl<'a> BuilderForBoxFragment<'a> {
builder: &mut DisplayListBuilder,
painter: &BackgroundPainter,
) {
- use style::values::computed::image::Image;
let style = painter.style;
let b = style.get_background();
// Reverse because the property is top layer first, we want to paint bottom layer first.
@@ -749,10 +761,38 @@ impl<'a> BuilderForBoxFragment<'a> {
Image::None => {},
Image::Gradient(ref gradient) => {
let intrinsic = IntrinsicSizes::empty();
- if let Some(layer) =
+ let Some(layer) =
&background::layout_layer(self, painter, builder, index, intrinsic)
- {
- gradient::build(style, gradient, layer, builder)
+ else {
+ continue;
+ };
+
+ match gradient::build(style, gradient, layer.tile_size, builder) {
+ WebRenderGradient::Linear(linear_gradient) => builder.wr().push_gradient(
+ &layer.common,
+ layer.bounds,
+ linear_gradient,
+ layer.tile_size,
+ layer.tile_spacing,
+ ),
+ WebRenderGradient::Radial(radial_gradient) => {
+ builder.wr().push_radial_gradient(
+ &layer.common,
+ layer.bounds,
+ radial_gradient,
+ layer.tile_size,
+ layer.tile_spacing,
+ )
+ },
+ WebRenderGradient::Conic(conic_gradient) => {
+ builder.wr().push_conic_gradient(
+ &layer.common,
+ layer.bounds,
+ conic_gradient,
+ layer.tile_size,
+ layer.tile_spacing,
+ )
+ },
}
},
Image::Url(ref image_url) => {
@@ -857,7 +897,12 @@ impl<'a> BuilderForBoxFragment<'a> {
return;
}
+ // `border-image` replaces an element's border entirely.
let common = builder.common_properties(self.border_rect, &self.fragment.style);
+ if self.build_border_image(builder, &common, border, border_widths) {
+ return;
+ }
+
let details = wr::BorderDetails::Normal(wr::NormalBorder {
top: self.build_border_side(border.border_top_style, border.border_top_color.clone()),
right: self
@@ -876,6 +921,100 @@ impl<'a> BuilderForBoxFragment<'a> {
.push_border(&common, self.border_rect, border_widths, details)
}
+ /// Add a display item for image borders if necessary.
+ fn build_border_image(
+ &self,
+ builder: &mut DisplayListBuilder,
+ common: &CommonItemProperties,
+ border: &Border,
+ border_widths: SideOffsets2D<f32, LayoutPixel>,
+ ) -> bool {
+ let border_style_struct = self.fragment.style.get_border();
+ let border_image_outset =
+ resolve_border_image_outset(border_style_struct.border_image_outset, border_widths);
+ let border_image_area = self.border_rect.to_rect().outer_rect(border_image_outset);
+ let border_image_size = border_image_area.size;
+ let border_image_widths = resolve_border_image_width(
+ &border_style_struct.border_image_width,
+ border_widths,
+ border_image_size,
+ );
+ let border_image_repeat = &border_style_struct.border_image_repeat;
+ let border_image_fill = border_style_struct.border_image_slice.fill;
+ let border_image_slice = &border_style_struct.border_image_slice.offsets;
+
+ let stops = Vec::new();
+ let mut width = border_image_size.width;
+ let mut height = border_image_size.height;
+ let source = match border.border_image_source {
+ Image::Url(ref image_url) => {
+ // FIXME: images won’t always have in intrinsic width or
+ // height when support for SVG is added, or a WebRender
+ // `ImageKey`, for that matter.
+ //
+ // FIXME: It feels like this should take into account the pseudo
+ // element and not just the node.
+ let Some(tag) = self.fragment.base.tag else {
+ return false;
+ };
+ let Some(image_url) = image_url.url() else {
+ return false;
+ };
+
+ let Some(image_info) = builder.context.get_webrender_image_for_url(
+ tag.node,
+ image_url.clone().into(),
+ UsePlaceholder::No,
+ ) else {
+ return false;
+ };
+
+ let Some(key) = image_info.key else {
+ return false;
+ };
+
+ width = image_info.width as f32;
+ height = image_info.height as f32;
+ NinePatchBorderSource::Image(key, ImageRendering::Auto)
+ },
+ Image::Gradient(ref gradient) => {
+ match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
+ WebRenderGradient::Linear(gradient) => {
+ NinePatchBorderSource::Gradient(gradient)
+ },
+ WebRenderGradient::Radial(gradient) => {
+ NinePatchBorderSource::RadialGradient(gradient)
+ },
+ WebRenderGradient::Conic(gradient) => {
+ NinePatchBorderSource::ConicGradient(gradient)
+ },
+ }
+ },
+ Image::CrossFade(_) | Image::ImageSet(_) | Image::None | Image::PaintWorklet(_) => {
+ return false
+ },
+ };
+
+ let size = euclid::Size2D::new(width as i32, height as i32);
+ let details = BorderDetails::NinePatch(NinePatchBorder {
+ source,
+ width: size.width,
+ height: size.height,
+ slice: resolve_border_image_slice(border_image_slice, size),
+ fill: border_image_fill,
+ repeat_horizontal: border_image_repeat.0.to_webrender(),
+ repeat_vertical: border_image_repeat.1.to_webrender(),
+ });
+ builder.wr().push_border(
+ &common,
+ border_image_area.to_box2d(),
+ border_image_widths,
+ details,
+ );
+ builder.wr().push_stops(&stops);
+ true
+ }
+
fn build_outline(&mut self, builder: &mut DisplayListBuilder) {
let outline = self.fragment.style.get_outline();
let width = outline.outline_width.to_f32_px();
@@ -1118,3 +1257,71 @@ fn create_clip_chain(
.define_clip_chain(parent_clip_chain_id, [new_clip_id]),
)
}
+
+/// Resolve the WebRender border-image outset area from the style values.
+fn resolve_border_image_outset(
+ outset: BorderImageOutset,
+ border: SideOffsets2D<f32, LayoutPixel>,
+) -> SideOffsets2D<f32, LayoutPixel> {
+ fn image_outset_for_side(outset: NonNegativeLengthOrNumber, border_width: f32) -> f32 {
+ match outset {
+ NonNegativeLengthOrNumber::Length(length) => length.px(),
+ NonNegativeLengthOrNumber::Number(factor) => border_width * factor.0,
+ }
+ }
+
+ SideOffsets2D::new(
+ image_outset_for_side(outset.0, border.top),
+ image_outset_for_side(outset.1, border.right),
+ image_outset_for_side(outset.2, border.bottom),
+ image_outset_for_side(outset.3, border.left),
+ )
+}
+
+/// Resolve the WebRender border-image width from the style values.
+fn resolve_border_image_width(
+ width: &BorderImageWidth,
+ border: SideOffsets2D<f32, LayoutPixel>,
+ border_area: Size2D<f32, LayoutPixel>,
+) -> SideOffsets2D<f32, LayoutPixel> {
+ fn image_width_for_side(
+ border_image_width: &BorderImageSideWidth,
+ border_width: f32,
+ total_length: f32,
+ ) -> f32 {
+ match border_image_width {
+ BorderImageSideWidth::LengthPercentage(v) => {
+ v.to_used_value(Au::from_f32_px(total_length)).to_f32_px()
+ },
+ BorderImageSideWidth::Number(x) => border_width * x.0,
+ BorderImageSideWidth::Auto => border_width,
+ }
+ }
+
+ SideOffsets2D::new(
+ image_width_for_side(&width.0, border.top, border_area.height),
+ image_width_for_side(&width.1, border.right, border_area.width),
+ image_width_for_side(&width.2, border.bottom, border_area.height),
+ image_width_for_side(&width.3, border.left, border_area.width),
+ )
+}
+
+/// Resolve the WebRender border-image slice from the style values.
+fn resolve_border_image_slice(
+ border_image_slice: &Rect<NonNegative<NumberOrPercentage>>,
+ size: Size2D<i32, UnknownUnit>,
+) -> SideOffsets2D<i32, DevicePixel> {
+ fn resolve_percentage(value: NonNegative<NumberOrPercentage>, length: i32) -> i32 {
+ match value.0 {
+ NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as i32,
+ NumberOrPercentage::Number(n) => n.round() as i32,
+ }
+ }
+
+ SideOffsets2D::new(
+ resolve_percentage(border_image_slice.0, size.height),
+ resolve_percentage(border_image_slice.1, size.width),
+ resolve_percentage(border_image_slice.2, size.height),
+ resolve_percentage(border_image_slice.3, size.width),
+ )
+}