diff options
author | Dan Fox <iamdanfox@gmail.com> | 2015-03-05 17:34:18 +0000 |
---|---|---|
committer | Dan Fox <iamdanfox@gmail.com> | 2015-03-05 17:34:18 +0000 |
commit | 19686acdec7a8f6e36fe999a2896ed44718f42d6 (patch) | |
tree | adc8d1b7f13790bee5c34239cf679e5062268e1f /components/layout/display_list_builder.rs | |
parent | 3441b2c329176fbb3e63b564c6eaef40862be38a (diff) | |
parent | caf5663e0981f36a621dde1dd51be2bd0a15dd24 (diff) | |
download | servo-19686acdec7a8f6e36fe999a2896ed44718f42d6.tar.gz servo-19686acdec7a8f6e36fe999a2896ed44718f42d6.zip |
Merge in servo/master
Diffstat (limited to 'components/layout/display_list_builder.rs')
-rw-r--r-- | components/layout/display_list_builder.rs | 161 |
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, |