Add background-size

This commit is contained in:
Simon Sapin 2020-01-13 16:48:16 +01:00
parent 649d3cb6b4
commit b1dcd5ecb6
3 changed files with 118 additions and 38 deletions

View file

@ -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 WebRenders `RepeatingImageDisplayItem` // Our job here would be easier if WebRenders `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,

View file

@ -20,21 +20,25 @@ 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.
/// See https://github.com/w3c/csswg-drafts/issues/4572 for the case where either is zero. /// See https://github.com/w3c/csswg-drafts/issues/4572 for the case where either is zero.
/// PNG specifically disallows this but I (SimonSapin) am not sure about other formats. /// PNG specifically disallows this but I (SimonSapin) am not sure about other formats.
/// ///
/// * Form controls have both intrinsic width and height **but no intrinsic ratio**. /// * Form controls have both intrinsic width and height **but no intrinsic ratio**.
/// See https://github.com/w3c/csswg-drafts/issues/1044 and /// See https://github.com/w3c/csswg-drafts/issues/1044 and
/// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]” /// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]”
/// ///
/// * 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)),
// FIXME https://github.com/w3c/csswg-drafts/issues/4572 height: Some(Length::new(height)),
intrinsic_ratio: Some(width / height), // FIXME https://github.com/w3c/csswg-drafts/issues/4572
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 {

View file

@ -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",