Skip to content

Commit

Permalink
Add background-size
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonSapin committed Jan 13, 2020
1 parent 649d3cb commit b1dcd5e
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 38 deletions.
106 changes: 90 additions & 16 deletions components/layout_2020/display_list.rs
Expand Up @@ -5,6 +5,7 @@
use crate::context::LayoutContext;
use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use crate::replaced::IntrinsicSizes;
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D, Vector2D};
use gfx::text::glyph::GlyphStore;
Expand Down Expand Up @@ -270,9 +271,17 @@ impl<'a> BuilderForBoxFragment<'a> {
key: Some(key),
}) = webrender_image
{
self.build_background_raster_image(
builder, index, width, height, key,
)
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
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)
}
}
},
Expand All @@ -287,8 +296,7 @@ impl<'a> BuilderForBoxFragment<'a> {
&mut self,
builder: &mut DisplayListBuilder,
index: usize,
intrinsic_width: u32,
intrinsic_height: u32,
intrinsic: IntrinsicSizes,
key: wr::ImageKey,
) {
// Our job here would be easier if WebRender’s `RepeatingImageDisplayItem`
Expand All @@ -310,6 +318,7 @@ impl<'a> BuilderForBoxFragment<'a> {

use style::computed_values::background_clip::single_value::T as Clip;
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 {
&values[index % values.len()]
Expand All @@ -328,21 +337,86 @@ impl<'a> BuilderForBoxFragment<'a> {
Origin::BorderBox => &self.border_rect,
};

// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
let dppx = 1.0;
// https://drafts.csswg.org/css-backgrounds/#background-size
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))
});

if width.is_none() && height.is_none() {
// Both computed values are 'auto':
// use intrinsic sizes, treating missing width or height as 'auto'
width = intrinsic.width;
height = intrinsic.height;
}

let intrinsic_size = units::LayoutSize::new(
intrinsic_width as f32 / dppx,
intrinsic_height as f32 / dppx,
);
// FIXME: background-size
// 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_stride = stretch_size + tile_spacing;

let tile_stride = tile_size + tile_spacing;

// FIXME: background-position
let positioned_tile_origin = positioning_area.origin;

let offset = positioned_tile_origin - clipping_area.origin;
let first_tile_origin = positioned_tile_origin -
Vector2D::new(
Expand All @@ -359,7 +433,7 @@ impl<'a> BuilderForBoxFragment<'a> {
builder.wr.push_repeating_image(
&common,
bounds,
stretch_size,
tile_size,
tile_spacing,
image_rendering(self.fragment.style.clone_image_rendering()),
wr::AlphaType::PremultipliedAlpha,
Expand Down
48 changes: 27 additions & 21 deletions components/layout_2020/replaced.rs
Expand Up @@ -20,21 +20,25 @@ use style::Zero;
#[derive(Debug)]
pub(crate) struct ReplacedContent {
pub kind: ReplacedContentKind,
intrinsic: IntrinsicSizes,
}

/// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px.
/// 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.
/// 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**.
/// See https://github.com/w3c/csswg-drafts/issues/1044 and
/// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]”
///
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
intrinsic_width: Option<Length>,
intrinsic_height: Option<Length>,
intrinsic_ratio: Option<CSSFloat>,
/// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px.
/// 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.
/// 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**.
/// See https://github.com/w3c/csswg-drafts/issues/1044 and
/// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]”
///
/// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
/// and again https://github.com/w3c/csswg-drafts/issues/4572.
#[derive(Debug)]
pub(crate) struct IntrinsicSizes {
pub width: Option<Length>,
pub height: Option<Length>,
pub ratio: Option<CSSFloat>,
}

#[derive(Debug)]
Expand All @@ -55,19 +59,21 @@ impl ReplacedContent {
let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx;
return Some(Self {
kind: ReplacedContentKind::Image(image),
intrinsic_width: Some(Length::new(width)),
intrinsic_height: Some(Length::new(height)),
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
intrinsic_ratio: Some(width / height),
intrinsic: IntrinsicSizes {
width: Some(Length::new(width)),
height: Some(Length::new(height)),
// FIXME https://github.com/w3c/csswg-drafts/issues/4572
ratio: Some(width / height),
},
});
}
None
}

fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> {
let intrinsic_size = physical::Vec2 {
x: self.intrinsic_width,
y: self.intrinsic_height,
x: self.intrinsic.width,
y: self.intrinsic.height,
};
intrinsic_size.size_to_flow_relative(style.writing_mode)
}
Expand All @@ -76,7 +82,7 @@ impl ReplacedContent {
&self,
style: &ComputedValues,
) -> Option<CSSFloat> {
self.intrinsic_ratio.map(|width_over_height| {
self.intrinsic.ratio.map(|width_over_height| {
if style.writing_mode.is_vertical() {
1. / width_over_height
} else {
Expand Down
2 changes: 1 addition & 1 deletion components/style/properties/longhands/background.mako.rs
Expand Up @@ -92,7 +92,7 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"background-size",
"BackgroundSize",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
initial_value="computed::BackgroundSize::auto()",
initial_specified_value="specified::BackgroundSize::auto()",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
Expand Down

0 comments on commit b1dcd5e

Please sign in to comment.