mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
layout: Implement support for image-set()
notation (#36210)
Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
This commit is contained in:
parent
40133ce29a
commit
987716ca4b
27 changed files with 61 additions and 124 deletions
|
@ -12,11 +12,12 @@ use net_traits::image_cache::{
|
||||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use pixels::Image;
|
use pixels::Image as PixelImage;
|
||||||
use script_layout_interface::{IFrameSizes, ImageAnimationState, PendingImage, PendingImageState};
|
use script_layout_interface::{IFrameSizes, ImageAnimationState, PendingImage, PendingImageState};
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
|
use style::values::computed::image::{Gradient, Image};
|
||||||
|
|
||||||
use crate::display_list::WebRenderImageInfo;
|
use crate::display_list::WebRenderImageInfo;
|
||||||
|
|
||||||
|
@ -46,6 +47,11 @@ pub struct LayoutContext<'a> {
|
||||||
pub node_image_animation_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
|
pub node_image_animation_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ResolvedImage<'a> {
|
||||||
|
Gradient(&'a Gradient),
|
||||||
|
Image(WebRenderImageInfo),
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for LayoutContext<'_> {
|
impl Drop for LayoutContext<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !std::thread::panicking() {
|
if !std::thread::panicking() {
|
||||||
|
@ -104,7 +110,7 @@ impl LayoutContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_animated_image(&self, node: OpaqueNode, image: Arc<Image>) {
|
pub fn handle_animated_image(&self, node: OpaqueNode, image: Arc<PixelImage>) {
|
||||||
let mut store = self.node_image_animation_map.write();
|
let mut store = self.node_image_animation_map.write();
|
||||||
|
|
||||||
// 1. first check whether node previously being track for animated image.
|
// 1. first check whether node previously being track for animated image.
|
||||||
|
@ -124,7 +130,7 @@ impl LayoutContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_webrender_image_for_url(
|
fn get_webrender_image_for_url(
|
||||||
&self,
|
&self,
|
||||||
node: OpaqueNode,
|
node: OpaqueNode,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
@ -157,4 +163,35 @@ impl LayoutContext<'_> {
|
||||||
None | Some(ImageOrMetadataAvailable::MetadataAvailable(..)) => None,
|
None | Some(ImageOrMetadataAvailable::MetadataAvailable(..)) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_image<'a>(
|
||||||
|
&self,
|
||||||
|
node: Option<OpaqueNode>,
|
||||||
|
image: &'a Image,
|
||||||
|
) -> Option<ResolvedImage<'a>> {
|
||||||
|
match image {
|
||||||
|
// TODO: Add support for PaintWorklet and CrossFade rendering.
|
||||||
|
Image::None | Image::CrossFade(_) | Image::PaintWorklet(_) => None,
|
||||||
|
Image::Gradient(gradient) => Some(ResolvedImage::Gradient(gradient)),
|
||||||
|
Image::Url(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 image_url = image_url.url()?;
|
||||||
|
let webrender_info = self.get_webrender_image_for_url(
|
||||||
|
node?,
|
||||||
|
image_url.clone().into(),
|
||||||
|
UsePlaceholder::No,
|
||||||
|
)?;
|
||||||
|
Some(ResolvedImage::Image(webrender_info))
|
||||||
|
},
|
||||||
|
Image::ImageSet(image_set) => image_set
|
||||||
|
.items
|
||||||
|
.get(image_set.selected_index)
|
||||||
|
.and_then(|image| self.resolve_image(node, &image.image)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use embedder_traits::Cursor;
|
||||||
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
|
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
|
||||||
use fonts::GlyphStore;
|
use fonts::GlyphStore;
|
||||||
use gradient::WebRenderGradient;
|
use gradient::WebRenderGradient;
|
||||||
use net_traits::image_cache::UsePlaceholder;
|
|
||||||
use servo_geometry::MaxRect;
|
use servo_geometry::MaxRect;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style::color::{AbsoluteColor, ColorSpace};
|
use style::color::{AbsoluteColor, ColorSpace};
|
||||||
|
@ -22,7 +21,6 @@ use style::dom::OpaqueNode;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::properties::longhands::visibility::computed_value::T as Visibility;
|
use style::properties::longhands::visibility::computed_value::T as Visibility;
|
||||||
use style::properties::style_structs::Border;
|
use style::properties::style_structs::Border;
|
||||||
use style::values::computed::image::Image;
|
|
||||||
use style::values::computed::{
|
use style::values::computed::{
|
||||||
BorderImageSideWidth, BorderImageWidth, BorderStyle, LengthPercentage,
|
BorderImageSideWidth, BorderImageWidth, BorderStyle, LengthPercentage,
|
||||||
NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
|
NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
|
||||||
|
@ -39,7 +37,7 @@ use webrender_api::{
|
||||||
use webrender_traits::display_list::{AxesScrollSensitivity, CompositorDisplayListInfo};
|
use webrender_traits::display_list::{AxesScrollSensitivity, CompositorDisplayListInfo};
|
||||||
use wr::units::LayoutVector2D;
|
use wr::units::LayoutVector2D;
|
||||||
|
|
||||||
use crate::context::LayoutContext;
|
use crate::context::{LayoutContext, ResolvedImage};
|
||||||
use crate::display_list::conversions::ToWebRender;
|
use crate::display_list::conversions::ToWebRender;
|
||||||
use crate::display_list::stacking_context::StackingContextSection;
|
use crate::display_list::stacking_context::StackingContextSection;
|
||||||
use crate::fragment_tree::{
|
use crate::fragment_tree::{
|
||||||
|
@ -774,11 +772,12 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
) {
|
) {
|
||||||
let style = painter.style;
|
let style = painter.style;
|
||||||
let b = style.get_background();
|
let b = style.get_background();
|
||||||
|
let node = self.fragment.base.tag.map(|tag| tag.node);
|
||||||
// Reverse because the property is top layer first, we want to paint bottom layer first.
|
// Reverse because the property is top layer first, we want to paint bottom layer first.
|
||||||
for (index, image) in b.background_image.0.iter().enumerate().rev() {
|
for (index, image) in b.background_image.0.iter().enumerate().rev() {
|
||||||
match image {
|
match builder.context.resolve_image(node, image) {
|
||||||
Image::None => {},
|
None => {},
|
||||||
Image::Gradient(gradient) => {
|
Some(ResolvedImage::Gradient(gradient)) => {
|
||||||
let intrinsic = NaturalSizes::empty();
|
let intrinsic = NaturalSizes::empty();
|
||||||
let Some(layer) =
|
let Some(layer) =
|
||||||
&background::layout_layer(self, painter, builder, index, intrinsic)
|
&background::layout_layer(self, painter, builder, index, intrinsic)
|
||||||
|
@ -814,40 +813,16 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Image::Url(image_url) => {
|
Some(ResolvedImage::Image(image_info)) => {
|
||||||
// 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 node = match self.fragment.base.tag {
|
|
||||||
Some(tag) => tag.node,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let image_url = match image_url.url() {
|
|
||||||
Some(url) => url.clone(),
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let (width, height, key) = match builder.context.get_webrender_image_for_url(
|
|
||||||
node,
|
|
||||||
image_url.into(),
|
|
||||||
UsePlaceholder::No,
|
|
||||||
) {
|
|
||||||
Some(WebRenderImageInfo {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
key: Some(key),
|
|
||||||
}) => (width, height, key),
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||||
let dppx = 1.0;
|
let dppx = 1.0;
|
||||||
let intrinsic = NaturalSizes::from_width_and_height(
|
let intrinsic = NaturalSizes::from_width_and_height(
|
||||||
width as f32 / dppx,
|
image_info.width as f32 / dppx,
|
||||||
height as f32 / dppx,
|
image_info.height as f32 / dppx,
|
||||||
);
|
);
|
||||||
|
let Some(image_key) = image_info.key else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(layer) =
|
if let Some(layer) =
|
||||||
background::layout_layer(self, painter, builder, index, intrinsic)
|
background::layout_layer(self, painter, builder, index, intrinsic)
|
||||||
|
@ -860,7 +835,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
layer.tile_spacing,
|
layer.tile_spacing,
|
||||||
style.clone_image_rendering().to_webrender(),
|
style.clone_image_rendering().to_webrender(),
|
||||||
wr::AlphaType::PremultipliedAlpha,
|
wr::AlphaType::PremultipliedAlpha,
|
||||||
key,
|
image_key,
|
||||||
wr::ColorF::WHITE,
|
wr::ColorF::WHITE,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -869,18 +844,12 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
layer.bounds,
|
layer.bounds,
|
||||||
style.clone_image_rendering().to_webrender(),
|
style.clone_image_rendering().to_webrender(),
|
||||||
wr::AlphaType::PremultipliedAlpha,
|
wr::AlphaType::PremultipliedAlpha,
|
||||||
key,
|
image_key,
|
||||||
wr::ColorF::WHITE,
|
wr::ColorF::WHITE,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Image::PaintWorklet(_) => {
|
|
||||||
// TODO: Add support for PaintWorklet rendering.
|
|
||||||
},
|
|
||||||
Image::ImageSet(..) | Image::CrossFade(..) => {
|
|
||||||
// TODO: Add support for ImageSet and CrossFade rendering.
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,29 +996,13 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
let stops = Vec::new();
|
let stops = Vec::new();
|
||||||
let mut width = border_image_size.width;
|
let mut width = border_image_size.width;
|
||||||
let mut height = border_image_size.height;
|
let mut height = border_image_size.height;
|
||||||
let source = match border.border_image_source {
|
let node = self.fragment.base.tag.map(|tag| tag.node);
|
||||||
Image::Url(ref image_url) => {
|
let source = match builder
|
||||||
// FIXME: images won’t always have in intrinsic width or
|
.context
|
||||||
// height when support for SVG is added, or a WebRender
|
.resolve_image(node, &border.border_image_source)
|
||||||
// `ImageKey`, for that matter.
|
{
|
||||||
//
|
None => return false,
|
||||||
// FIXME: It feels like this should take into account the pseudo
|
Some(ResolvedImage::Image(image_info)) => {
|
||||||
// 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 {
|
let Some(key) = image_info.key else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -1058,7 +1011,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
height = image_info.height as f32;
|
height = image_info.height as f32;
|
||||||
NinePatchBorderSource::Image(key, ImageRendering::Auto)
|
NinePatchBorderSource::Image(key, ImageRendering::Auto)
|
||||||
},
|
},
|
||||||
Image::Gradient(ref gradient) => {
|
Some(ResolvedImage::Gradient(gradient)) => {
|
||||||
match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
|
match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
|
||||||
WebRenderGradient::Linear(gradient) => {
|
WebRenderGradient::Linear(gradient) => {
|
||||||
NinePatchBorderSource::Gradient(gradient)
|
NinePatchBorderSource::Gradient(gradient)
|
||||||
|
@ -1071,9 +1024,6 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Image::CrossFade(_) | Image::ImageSet(_) | Image::None | Image::PaintWorklet(_) => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = euclid::Size2D::new(width as i32, height as i32);
|
let size = euclid::Size2D::new(width as i32, height as i32);
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-calc-x-rendering-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-calc-x-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-conic-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-dpcm-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-dpi-rendering-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-dpi-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-dppx-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-empty-url-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-first-match-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-linear-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-no-res-rendering-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-no-res-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-no-url-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-radial-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-rendering-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-repeating-conic-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-repeating-linear-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-repeating-radial-gradient-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-type-first-match-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-type-rendering-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-type-rendering-3.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-type-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-type-skip-unsupported-rendering.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[image-set-unordered-res-rendering.html]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue