mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Add background-size
This commit is contained in:
parent
649d3cb6b4
commit
b1dcd5ecb6
3 changed files with 118 additions and 38 deletions
|
@ -5,6 +5,7 @@
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::fragments::{BoxFragment, Fragment};
|
use crate::fragments::{BoxFragment, Fragment};
|
||||||
use crate::geom::physical::{Rect, Vec2};
|
use crate::geom::physical::{Rect, Vec2};
|
||||||
|
use crate::replaced::IntrinsicSizes;
|
||||||
use embedder_traits::Cursor;
|
use embedder_traits::Cursor;
|
||||||
use euclid::{Point2D, SideOffsets2D, Size2D, Vector2D};
|
use euclid::{Point2D, SideOffsets2D, Size2D, Vector2D};
|
||||||
use gfx::text::glyph::GlyphStore;
|
use gfx::text::glyph::GlyphStore;
|
||||||
|
@ -270,9 +271,17 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
key: Some(key),
|
key: Some(key),
|
||||||
}) = webrender_image
|
}) = webrender_image
|
||||||
{
|
{
|
||||||
self.build_background_raster_image(
|
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||||
builder, index, width, height, key,
|
let dppx = 1.0;
|
||||||
)
|
|
||||||
|
let intrinsic = IntrinsicSizes {
|
||||||
|
width: Some(Length::new(width as f32 / dppx)),
|
||||||
|
height: Some(Length::new(height as f32 / dppx)),
|
||||||
|
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||||
|
ratio: Some(width as f32 / height as f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.build_background_raster_image(builder, index, intrinsic, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -287,8 +296,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
builder: &mut DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
index: usize,
|
index: usize,
|
||||||
intrinsic_width: u32,
|
intrinsic: IntrinsicSizes,
|
||||||
intrinsic_height: u32,
|
|
||||||
key: wr::ImageKey,
|
key: wr::ImageKey,
|
||||||
) {
|
) {
|
||||||
// Our job here would be easier if WebRender’s `RepeatingImageDisplayItem`
|
// Our job here would be easier if WebRender’s `RepeatingImageDisplayItem`
|
||||||
|
@ -310,6 +318,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
|
|
||||||
use style::computed_values::background_clip::single_value::T as Clip;
|
use style::computed_values::background_clip::single_value::T as Clip;
|
||||||
use style::computed_values::background_origin::single_value::T as Origin;
|
use style::computed_values::background_origin::single_value::T as Origin;
|
||||||
|
use style::values::computed::background::BackgroundSize as Size;
|
||||||
|
|
||||||
fn get_cyclic<T>(values: &[T], index: usize) -> &T {
|
fn get_cyclic<T>(values: &[T], index: usize) -> &T {
|
||||||
&values[index % values.len()]
|
&values[index % values.len()]
|
||||||
|
@ -328,21 +337,86 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
Origin::BorderBox => &self.border_rect,
|
Origin::BorderBox => &self.border_rect,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
// https://drafts.csswg.org/css-backgrounds/#background-size
|
||||||
let dppx = 1.0;
|
enum ContainOrCover {
|
||||||
|
Contain,
|
||||||
|
Cover,
|
||||||
|
}
|
||||||
|
let size_contain_or_cover = |background_size| {
|
||||||
|
let mut tile_size = positioning_area.size;
|
||||||
|
if let Some(intrinsic_ratio) = intrinsic.ratio {
|
||||||
|
let positioning_ratio = positioning_area.size.width / positioning_area.size.height;
|
||||||
|
// Whether the tile width (as opposed to height)
|
||||||
|
// is scaled to that of the positioning area
|
||||||
|
let fit_width = match background_size {
|
||||||
|
ContainOrCover::Contain => positioning_ratio <= intrinsic_ratio,
|
||||||
|
ContainOrCover::Cover => positioning_ratio > intrinsic_ratio,
|
||||||
|
};
|
||||||
|
// The other dimension needs to be adjusted
|
||||||
|
if fit_width {
|
||||||
|
tile_size.height = tile_size.width / intrinsic_ratio
|
||||||
|
} else {
|
||||||
|
tile_size.width = tile_size.height * intrinsic_ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tile_size
|
||||||
|
};
|
||||||
|
let tile_size = match get_cyclic(&b.background_size.0, index) {
|
||||||
|
Size::Contain => size_contain_or_cover(ContainOrCover::Contain),
|
||||||
|
Size::Cover => size_contain_or_cover(ContainOrCover::Cover),
|
||||||
|
Size::ExplicitSize { width, height } => {
|
||||||
|
let mut width = width.non_auto().map(|lp| {
|
||||||
|
lp.0.percentage_relative_to(Length::new(positioning_area.size.width))
|
||||||
|
});
|
||||||
|
let mut height = height.non_auto().map(|lp| {
|
||||||
|
lp.0.percentage_relative_to(Length::new(positioning_area.size.height))
|
||||||
|
});
|
||||||
|
|
||||||
let intrinsic_size = units::LayoutSize::new(
|
if width.is_none() && height.is_none() {
|
||||||
intrinsic_width as f32 / dppx,
|
// Both computed values are 'auto':
|
||||||
intrinsic_height as f32 / dppx,
|
// use intrinsic sizes, treating missing width or height as 'auto'
|
||||||
);
|
width = intrinsic.width;
|
||||||
// FIXME: background-size
|
height = intrinsic.height;
|
||||||
// Size of one tile:
|
}
|
||||||
let stretch_size = intrinsic_size;
|
|
||||||
|
match (width, height) {
|
||||||
|
(Some(w), Some(h)) => units::LayoutSize::new(w.px(), h.px()),
|
||||||
|
(Some(w), None) => {
|
||||||
|
let h = if let Some(intrinsic_ratio) = intrinsic.ratio {
|
||||||
|
w / intrinsic_ratio
|
||||||
|
} else if let Some(intrinsic_height) = intrinsic.height {
|
||||||
|
intrinsic_height
|
||||||
|
} else {
|
||||||
|
// Treated as 100%
|
||||||
|
Length::new(positioning_area.size.height)
|
||||||
|
};
|
||||||
|
units::LayoutSize::new(w.px(), h.px())
|
||||||
|
},
|
||||||
|
(None, Some(h)) => {
|
||||||
|
let w = if let Some(intrinsic_ratio) = intrinsic.ratio {
|
||||||
|
h * intrinsic_ratio
|
||||||
|
} else if let Some(intrinsic_width) = intrinsic.width {
|
||||||
|
intrinsic_width
|
||||||
|
} else {
|
||||||
|
// Treated as 100%
|
||||||
|
Length::new(positioning_area.size.width)
|
||||||
|
};
|
||||||
|
units::LayoutSize::new(w.px(), h.px())
|
||||||
|
},
|
||||||
|
// Both comptued values were 'auto', and neither intrinsic size is present
|
||||||
|
(None, None) => size_contain_or_cover(ContainOrCover::Contain),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: background-repeat
|
||||||
let tile_spacing = units::LayoutSize::zero();
|
let tile_spacing = units::LayoutSize::zero();
|
||||||
let tile_stride = stretch_size + tile_spacing;
|
|
||||||
|
let tile_stride = tile_size + tile_spacing;
|
||||||
|
|
||||||
// FIXME: background-position
|
// FIXME: background-position
|
||||||
let positioned_tile_origin = positioning_area.origin;
|
let positioned_tile_origin = positioning_area.origin;
|
||||||
|
|
||||||
let offset = positioned_tile_origin - clipping_area.origin;
|
let offset = positioned_tile_origin - clipping_area.origin;
|
||||||
let first_tile_origin = positioned_tile_origin -
|
let first_tile_origin = positioned_tile_origin -
|
||||||
Vector2D::new(
|
Vector2D::new(
|
||||||
|
@ -359,7 +433,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
builder.wr.push_repeating_image(
|
builder.wr.push_repeating_image(
|
||||||
&common,
|
&common,
|
||||||
bounds,
|
bounds,
|
||||||
stretch_size,
|
tile_size,
|
||||||
tile_spacing,
|
tile_spacing,
|
||||||
image_rendering(self.fragment.style.clone_image_rendering()),
|
image_rendering(self.fragment.style.clone_image_rendering()),
|
||||||
wr::AlphaType::PremultipliedAlpha,
|
wr::AlphaType::PremultipliedAlpha,
|
||||||
|
|
|
@ -20,6 +20,8 @@ use style::Zero;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ReplacedContent {
|
pub(crate) struct ReplacedContent {
|
||||||
pub kind: ReplacedContentKind,
|
pub kind: ReplacedContentKind,
|
||||||
|
intrinsic: IntrinsicSizes,
|
||||||
|
}
|
||||||
|
|
||||||
/// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px.
|
/// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px.
|
||||||
/// The intrinsic ratio should be based on dividing those.
|
/// The intrinsic ratio should be based on dividing those.
|
||||||
|
@ -32,9 +34,11 @@ pub(crate) struct ReplacedContent {
|
||||||
///
|
///
|
||||||
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
|
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
|
||||||
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
|
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
|
||||||
intrinsic_width: Option<Length>,
|
#[derive(Debug)]
|
||||||
intrinsic_height: Option<Length>,
|
pub(crate) struct IntrinsicSizes {
|
||||||
intrinsic_ratio: Option<CSSFloat>,
|
pub width: Option<Length>,
|
||||||
|
pub height: Option<Length>,
|
||||||
|
pub ratio: Option<CSSFloat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -55,10 +59,12 @@ impl ReplacedContent {
|
||||||
let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx;
|
let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx;
|
||||||
return Some(Self {
|
return Some(Self {
|
||||||
kind: ReplacedContentKind::Image(image),
|
kind: ReplacedContentKind::Image(image),
|
||||||
intrinsic_width: Some(Length::new(width)),
|
intrinsic: IntrinsicSizes {
|
||||||
intrinsic_height: Some(Length::new(height)),
|
width: Some(Length::new(width)),
|
||||||
|
height: Some(Length::new(height)),
|
||||||
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
|
||||||
intrinsic_ratio: Some(width / height),
|
ratio: Some(width / height),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -66,8 +72,8 @@ impl ReplacedContent {
|
||||||
|
|
||||||
fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> {
|
fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> {
|
||||||
let intrinsic_size = physical::Vec2 {
|
let intrinsic_size = physical::Vec2 {
|
||||||
x: self.intrinsic_width,
|
x: self.intrinsic.width,
|
||||||
y: self.intrinsic_height,
|
y: self.intrinsic.height,
|
||||||
};
|
};
|
||||||
intrinsic_size.size_to_flow_relative(style.writing_mode)
|
intrinsic_size.size_to_flow_relative(style.writing_mode)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +82,7 @@ impl ReplacedContent {
|
||||||
&self,
|
&self,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
) -> Option<CSSFloat> {
|
) -> Option<CSSFloat> {
|
||||||
self.intrinsic_ratio.map(|width_over_height| {
|
self.intrinsic.ratio.map(|width_over_height| {
|
||||||
if style.writing_mode.is_vertical() {
|
if style.writing_mode.is_vertical() {
|
||||||
1. / width_over_height
|
1. / width_over_height
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -92,7 +92,7 @@ ${helpers.single_keyword(
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"background-size",
|
"background-size",
|
||||||
"BackgroundSize",
|
"BackgroundSize",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013 servo-2020",
|
||||||
initial_value="computed::BackgroundSize::auto()",
|
initial_value="computed::BackgroundSize::auto()",
|
||||||
initial_specified_value="specified::BackgroundSize::auto()",
|
initial_specified_value="specified::BackgroundSize::auto()",
|
||||||
spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
|
spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue