aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/display_list_builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/display_list_builder.rs')
-rw-r--r--components/layout/display_list_builder.rs161
1 files changed, 126 insertions, 35 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 10585964543..b4fb0001a80 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -19,7 +19,7 @@ use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo
use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
use inline::InlineFlow;
use list_item::ListItemFlow;
-use model;
+use model::{self, MaybeAuto};
use opaque_node::OpaqueNodeMethods;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
@@ -32,8 +32,7 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem};
use gfx::display_list::{OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation};
use gfx::paint_task::{PaintLayer, THREAD_TINT_COLORS};
-use png;
-use png::PixelsByColorType;
+use png::{self, PixelsByColorType};
use msg::compositor_msg::ScrollPolicy;
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::ConstellationChan;
@@ -42,15 +41,16 @@ use servo_util::cursor::Cursor;
use servo_util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use servo_util::opts;
+use std::cmp;
use std::default::Default;
use std::iter::repeat;
use std::num::Float;
use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
-use style::values::computed::{Image, LinearGradient, LengthOrPercentage};
+use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::RGBA;
use style::computed_values::filter::Filter;
-use style::computed_values::{background_attachment, background_repeat, border_style, overflow};
-use style::computed_values::{position, visibility};
+use style::computed_values::{background_attachment, background_repeat, background_size};
+use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility};
use style::properties::style_structs::Border;
use style::properties::ComputedValues;
use std::num::ToPrimitive;
@@ -93,6 +93,14 @@ pub trait FragmentDisplayListBuilding {
absolute_bounds: &Rect<Au>,
clip: &ClippingRegion);
+ /// Computes the background size for an image with the given background area according to the
+ /// rules in CSS-BACKGROUNDS § 3.9.
+ fn compute_background_image_size(&self,
+ style: &ComputedValues,
+ bounds: &Rect<Au>,
+ image: &png::Image)
+ -> Size2D<Au>;
+
/// Adds the display items necessary to paint the background image of this fragment to the
/// display list at the appropriate stacking level.
fn build_display_list_for_background_image(&self,
@@ -326,6 +334,59 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
+ fn compute_background_image_size(&self,
+ style: &ComputedValues,
+ bounds: &Rect<Au>,
+ image: &png::Image)
+ -> Size2D<Au> {
+ // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
+ // wide.
+ let image_aspect_ratio = (image.width as f64) / (image.height as f64);
+ let bounds_aspect_ratio = bounds.size.width.to_subpx() / bounds.size.height.to_subpx();
+ let intrinsic_size = Size2D(Au::from_px(image.width as int),
+ Au::from_px(image.height as int));
+ match (style.get_background().background_size.clone(),
+ image_aspect_ratio < bounds_aspect_ratio) {
+ (background_size::T::Contain, false) | (background_size::T::Cover, true) => {
+ Size2D(bounds.size.width,
+ Au::from_frac_px(bounds.size.width.to_subpx() / image_aspect_ratio))
+ }
+
+ (background_size::T::Contain, true) | (background_size::T::Cover, false) => {
+ Size2D(Au::from_frac_px(bounds.size.height.to_subpx() * image_aspect_ratio),
+ bounds.size.height)
+ }
+
+ (background_size::T::Explicit(background_size::ExplicitSize {
+ width,
+ height: LengthOrPercentageOrAuto::Auto,
+ }), _) => {
+ let width = MaybeAuto::from_style(width, bounds.size.width)
+ .specified_or_default(intrinsic_size.width);
+ Size2D(width, Au::from_frac_px(width.to_subpx() / image_aspect_ratio))
+ }
+
+ (background_size::T::Explicit(background_size::ExplicitSize {
+ width: LengthOrPercentageOrAuto::Auto,
+ height
+ }), _) => {
+ let height = MaybeAuto::from_style(height, bounds.size.height)
+ .specified_or_default(intrinsic_size.height);
+ Size2D(Au::from_frac_px(height.to_subpx() * image_aspect_ratio), height)
+ }
+
+ (background_size::T::Explicit(background_size::ExplicitSize {
+ width,
+ height
+ }), _) => {
+ Size2D(MaybeAuto::from_style(width, bounds.size.width)
+ .specified_or_default(intrinsic_size.width),
+ MaybeAuto::from_style(height, bounds.size.height)
+ .specified_or_default(intrinsic_size.height))
+ }
+ }
+ }
+
fn build_display_list_for_background_image(&self,
style: &ComputedValues,
display_list: &mut DisplayList,
@@ -349,16 +410,16 @@ impl FragmentDisplayListBuilding for Fragment {
};
debug!("(building display list) building background image");
- let image_width = Au::from_px(image.width as int);
- let image_height = Au::from_px(image.height as int);
+ // Use `background-size` to get the size.
let mut bounds = *absolute_bounds;
+ let image_size = self.compute_background_image_size(style, &bounds, &**image);
// Clip.
//
// TODO: Check the bounds to see if a clip item is actually required.
let clip = clip.clone().intersect_rect(&bounds);
- // Use background-attachment to get the initial virtual origin
+ // Use `background-attachment` to get the initial virtual origin
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
background_attachment::T::scroll => {
(absolute_bounds.origin.x, absolute_bounds.origin.y)
@@ -368,11 +429,11 @@ impl FragmentDisplayListBuilding for Fragment {
}
};
- // Use background-position to get the offset
+ // Use `background-position` to get the offset.
let horizontal_position = model::specified(background.background_position.horizontal,
- bounds.size.width - image_width);
+ bounds.size.width - image_size.width);
let vertical_position = model::specified(background.background_position.vertical,
- bounds.size.height - image_height);
+ bounds.size.height - image_size.height);
let abs_x = virtual_origin_x + horizontal_position;
let abs_y = virtual_origin_y + vertical_position;
@@ -382,26 +443,34 @@ impl FragmentDisplayListBuilding for Fragment {
background_repeat::T::no_repeat => {
bounds.origin.x = abs_x;
bounds.origin.y = abs_y;
- bounds.size.width = image_width;
- bounds.size.height = image_height;
+ bounds.size.width = image_size.width;
+ bounds.size.height = image_size.height;
}
background_repeat::T::repeat_x => {
bounds.origin.y = abs_y;
- bounds.size.height = image_height;
- ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
- abs_x, image.width);
+ bounds.size.height = image_size.height;
+ ImageFragmentInfo::tile_image(&mut bounds.origin.x,
+ &mut bounds.size.width,
+ abs_x,
+ image_size.width.to_nearest_px() as u32);
}
background_repeat::T::repeat_y => {
bounds.origin.x = abs_x;
- bounds.size.width = image_width;
- ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
- abs_y, image.height);
+ bounds.size.width = image_size.width;
+ ImageFragmentInfo::tile_image(&mut bounds.origin.y,
+ &mut bounds.size.height,
+ abs_y,
+ image_size.height.to_nearest_px() as u32);
}
background_repeat::T::repeat => {
- ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
- abs_x, image.width);
- ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
- abs_y, image.height);
+ ImageFragmentInfo::tile_image(&mut bounds.origin.x,
+ &mut bounds.size.width,
+ abs_x,
+ image_size.width.to_nearest_px() as u32);
+ ImageFragmentInfo::tile_image(&mut bounds.origin.y,
+ &mut bounds.size.height,
+ abs_y,
+ image_size.height.to_nearest_px() as u32);
}
};
@@ -413,8 +482,8 @@ impl FragmentDisplayListBuilding for Fragment {
Cursor::DefaultCursor),
clip),
image: image.clone(),
- stretch_size: Size2D(Au::from_px(image.width as int),
- Au::from_px(image.height as int)),
+ stretch_size: Size2D(image_size.width, image_size.height),
+ image_rendering: style.get_effects().image_rendering.clone(),
}), level);
}
@@ -912,6 +981,7 @@ impl FragmentDisplayListBuilding for Fragment {
(*clip).clone()),
image: image.clone(),
stretch_size: stacking_relative_content_box.size,
+ image_rendering: self.style.get_effects().image_rendering.clone(),
}));
} else {
// No image data at all? Do nothing.
@@ -947,6 +1017,7 @@ impl FragmentDisplayListBuilding for Fragment {
pixels: PixelsByColorType::RGBA8(canvas_data),
}),
stretch_size: stacking_relative_content_box.size,
+ image_rendering: image_rendering::T::Auto,
};
display_list.content.push_back(DisplayItem::ImageClass(canvas_display_item));
@@ -991,17 +1062,37 @@ impl FragmentDisplayListBuilding for Fragment {
}
// Account for style-specified `clip`.
- let current_clip = self.calculate_style_specified_clip(current_clip,
- stacking_relative_border_box);
-
- // Only clip if `overflow` tells us to.
- match self.style.get_box().overflow {
- overflow::T::hidden | overflow::T::auto | overflow::T::scroll => {
- // Create a new clip rect.
- current_clip.intersect_rect(stacking_relative_border_box)
+ let mut current_clip = self.calculate_style_specified_clip(current_clip,
+ stacking_relative_border_box);
+
+ // Clip according to the values of `overflow-x` and `overflow-y`.
+ //
+ // TODO(pcwalton): Support scrolling.
+ // FIXME(pcwalton): This may be more complex than it needs to be, since it seems to be
+ // impossible with the computed value rules as they are to have `overflow-x: visible` with
+ // `overflow-y: <scrolling>` or vice versa!
+ match self.style.get_box().overflow_x {
+ overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => {
+ let mut bounds = current_clip.bounding_rect();
+ let max_x = cmp::min(bounds.max_x(), stacking_relative_border_box.max_x());
+ bounds.origin.x = cmp::max(bounds.origin.x, stacking_relative_border_box.origin.x);
+ bounds.size.width = max_x - bounds.origin.x;
+ current_clip = current_clip.intersect_rect(&bounds)
+ }
+ _ => {}
+ }
+ match self.style.get_box().overflow_y.0 {
+ overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => {
+ let mut bounds = current_clip.bounding_rect();
+ let max_y = cmp::min(bounds.max_y(), stacking_relative_border_box.max_y());
+ bounds.origin.y = cmp::max(bounds.origin.y, stacking_relative_border_box.origin.y);
+ bounds.size.height = max_y - bounds.origin.y;
+ current_clip = current_clip.intersect_rect(&bounds)
}
- _ => current_clip,
+ _ => {}
}
+
+ current_clip
}
fn build_display_list_for_text_fragment(&self,