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.rs117
1 files changed, 89 insertions, 28 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index b41c407eb58..25a292e2780 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -24,8 +24,8 @@ use util::{OpaqueNodeMethods, ToGfxColor};
use geom::approxeq::ApproxEq;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
-use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
-use gfx::display_list::{BorderRadii, BoxShadowDisplayItem, ClippingRegion};
+use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
+use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayList, DisplayItemMetadata};
use gfx::display_list::{GradientDisplayItem};
use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem};
@@ -39,7 +39,7 @@ use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::ConstellationChan;
use net::image::holder::ImageHolder;
use servo_util::cursor::Cursor;
-use servo_util::geometry::{self, Au, to_px, to_frac_px};
+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::default::Default;
@@ -196,12 +196,17 @@ pub trait FragmentDisplayListBuilding {
stacking_relative_border_box: &Rect<Au>)
-> ClippingRegion;
- /// Creates the text display item for one text fragment.
+ /// Creates the text display item for one text fragment. This can be called multiple times for
+ /// one fragment if there are text shadows.
+ ///
+ /// `shadow_blur_radius` will be `Some` if this is a shadow, even if the blur radius is zero.
fn build_display_list_for_text_fragment(&self,
display_list: &mut DisplayList,
text_fragment: &ScannedTextFragmentInfo,
text_color: RGBA,
stacking_relative_content_box: &Rect<Au>,
+ shadow_blur_radius: Option<Au>,
+ offset: &Point2D<Au>,
clip: &ClippingRegion);
/// Creates the display item for a text decoration: underline, overline, or line-through.
@@ -209,7 +214,8 @@ pub trait FragmentDisplayListBuilding {
display_list: &mut DisplayList,
color: &RGBA,
stacking_relative_box: &LogicalRect<Au>,
- clip: &ClippingRegion);
+ clip: &ClippingRegion,
+ blur_radius: Au);
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
fn build_fragment_type_specific_display_items(&mut self,
@@ -535,11 +541,10 @@ impl FragmentDisplayListBuilding for Fragment {
clip: &ClippingRegion) {
// NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
for box_shadow in style.get_effects().box_shadow.iter().rev() {
- let inflation = box_shadow.spread_radius + box_shadow.blur_radius *
- BOX_SHADOW_INFLATION_FACTOR;
- let bounds =
- absolute_bounds.translate(&Point2D(box_shadow.offset_x, box_shadow.offset_y))
- .inflate(inflation, inflation);
+ let bounds = shadow_bounds(&absolute_bounds.translate(&Point2D(box_shadow.offset_x,
+ box_shadow.offset_y)),
+ box_shadow.blur_radius,
+ box_shadow.spread_radius);
list.push(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem {
base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node,
@@ -551,7 +556,11 @@ impl FragmentDisplayListBuilding for Fragment {
offset: Point2D(box_shadow.offset_x, box_shadow.offset_y),
blur_radius: box_shadow.blur_radius,
spread_radius: box_shadow.spread_radius,
- inset: box_shadow.inset,
+ clip_mode: if box_shadow.inset {
+ BoxShadowClipMode::Inset
+ } else {
+ BoxShadowClipMode::Outset
+ },
}), level);
}
}
@@ -841,19 +850,31 @@ impl FragmentDisplayListBuilding for Fragment {
self.stacking_relative_content_box(stacking_relative_border_box);
match self.specific {
- SpecificFragmentInfo::UnscannedText(_) => {
- panic!("Shouldn't see unscanned fragments here.")
- }
- SpecificFragmentInfo::TableColumn(_) => {
- panic!("Shouldn't see table column fragments here.")
- }
SpecificFragmentInfo::ScannedText(ref text_fragment) => {
- // Create the main text display item.
+ // Create items for shadows.
+ //
+ // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
+ // to back).
let text_color = self.style().get_color().color;
+ for text_shadow in self.style.get_effects().text_shadow.0.iter().rev() {
+ let offset = &Point2D(text_shadow.offset_x, text_shadow.offset_y);
+ let color = self.style().resolve_color(text_shadow.color);
+ self.build_display_list_for_text_fragment(display_list,
+ &**text_fragment,
+ color,
+ &stacking_relative_content_box,
+ Some(text_shadow.blur_radius),
+ offset,
+ clip);
+ }
+
+ // Create the main text display item.
self.build_display_list_for_text_fragment(display_list,
&**text_fragment,
text_color,
&stacking_relative_content_box,
+ None,
+ &Point2D(Au(0), Au(0)),
clip);
if opts::get().show_debug_fragment_borders {
@@ -932,6 +953,12 @@ impl FragmentDisplayListBuilding for Fragment {
display_list.content.push_back(DisplayItem::ImageClass(canvas_display_item));
}
+ SpecificFragmentInfo::UnscannedText(_) => {
+ panic!("Shouldn't see unscanned fragments here.")
+ }
+ SpecificFragmentInfo::TableColumn(_) => {
+ panic!("Shouldn't see table column fragments here.")
+ }
}
}
@@ -984,6 +1011,8 @@ impl FragmentDisplayListBuilding for Fragment {
text_fragment: &ScannedTextFragmentInfo,
text_color: RGBA,
stacking_relative_content_box: &Rect<Au>,
+ shadow_blur_radius: Option<Au>,
+ offset: &Point2D<Au>,
clip: &ClippingRegion) {
// Determine the orientation and cursor to use.
let (orientation, cursor) = if self.style.writing_mode.is_vertical() {
@@ -1001,6 +1030,7 @@ impl FragmentDisplayListBuilding for Fragment {
// FIXME(pcwalton): Get the real container size.
let container_size = Size2D::zero();
let metrics = &text_fragment.run.font_metrics;
+ let stacking_relative_content_box = stacking_relative_content_box.translate(offset);
let baseline_origin = stacking_relative_content_box.origin +
LogicalPoint::new(self.style.writing_mode,
Au(0),
@@ -1009,7 +1039,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Create the text display item.
display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem {
- base: BaseDisplayItem::new(*stacking_relative_content_box,
+ base: BaseDisplayItem::new(stacking_relative_content_box,
DisplayItemMetadata::new(self.node, self.style(), cursor),
(*clip).clone()),
text_run: text_fragment.run.clone(),
@@ -1017,13 +1047,23 @@ impl FragmentDisplayListBuilding for Fragment {
text_color: text_color.to_gfx_color(),
orientation: orientation,
baseline_origin: baseline_origin,
+ blur_radius: shadow_blur_radius.unwrap_or(Au(0)),
}));
// Create display items for text decorations.
- let text_decorations = self.style().get_inheritedtext()._servo_text_decorations_in_effect;
+ let mut text_decorations = self.style()
+ .get_inheritedtext()
+ ._servo_text_decorations_in_effect;
+ if shadow_blur_radius.is_some() {
+ // If we're painting a shadow, paint the decorations the same color as the shadow.
+ text_decorations.underline = text_decorations.underline.map(|_| text_color);
+ text_decorations.overline = text_decorations.overline.map(|_| text_color);
+ text_decorations.line_through = text_decorations.line_through.map(|_| text_color);
+ }
+
let stacking_relative_content_box =
LogicalRect::from_physical(self.style.writing_mode,
- *stacking_relative_content_box,
+ stacking_relative_content_box,
container_size);
if let Some(ref underline_color) = text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box;
@@ -1033,7 +1073,8 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_decoration(display_list,
underline_color,
&stacking_relative_box,
- clip)
+ clip,
+ shadow_blur_radius.unwrap_or(Au(0)))
}
if let Some(ref overline_color) = text_decorations.overline {
@@ -1042,7 +1083,8 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_decoration(display_list,
overline_color,
&stacking_relative_box,
- clip)
+ clip,
+ shadow_blur_radius.unwrap_or(Au(0)))
}
if let Some(ref line_through_color) = text_decorations.line_through {
@@ -1053,7 +1095,8 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_decoration(display_list,
line_through_color,
&stacking_relative_box,
- clip)
+ clip,
+ shadow_blur_radius.unwrap_or(Au(0)))
}
}
@@ -1061,16 +1104,27 @@ impl FragmentDisplayListBuilding for Fragment {
display_list: &mut DisplayList,
color: &RGBA,
stacking_relative_box: &LogicalRect<Au>,
- clip: &ClippingRegion) {
+ clip: &ClippingRegion,
+ blur_radius: Au) {
+ // Perhaps surprisingly, text decorations are box shadows. This is because they may need
+ // to have blur in the case of `text-shadow`, and this doesn't hurt performance because box
+ // shadows are optimized into essentially solid colors if there is no need for the blur.
+ //
// FIXME(pcwalton, #2795): Get the real container size.
let container_size = Size2D::zero();
let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode,
container_size);
-
let metadata = DisplayItemMetadata::new(self.node, &*self.style, Cursor::DefaultCursor);
- display_list.content.push_back(DisplayItem::SolidColorClass(box SolidColorDisplayItem {
- base: BaseDisplayItem::new(stacking_relative_box, metadata, (*clip).clone()),
+ display_list.content.push_back(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem {
+ base: BaseDisplayItem::new(shadow_bounds(&stacking_relative_box, blur_radius, Au(0)),
+ metadata,
+ (*clip).clone()),
+ box_bounds: stacking_relative_box,
color: color.to_gfx_color(),
+ offset: ZERO_POINT,
+ blur_radius: blur_radius,
+ spread_radius: Au(0),
+ clip_mode: BoxShadowClipMode::None,
}))
}
}
@@ -1423,3 +1477,10 @@ impl StackingContextConstruction for DisplayList {
}
}
+/// Adjusts `content_rect` as necessary for the given spread, and blur so that the resulting
+/// bounding rect contains all of a shadow's ink.
+fn shadow_bounds(content_rect: &Rect<Au>, blur_radius: Au, spread_radius: Au) -> Rect<Au> {
+ let inflation = spread_radius + blur_radius * BLUR_INFLATION_FACTOR;
+ content_rect.inflate(inflation, inflation)
+}
+