diff options
-rw-r--r-- | components/gfx/display_list/mod.rs | 32 | ||||
-rw-r--r-- | components/gfx/lib.rs | 2 | ||||
-rw-r--r-- | components/gfx/paint_context.rs | 313 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 526 | ||||
-rw-r--r-- | components/layout/flow.rs | 5 | ||||
-rw-r--r-- | components/layout/fragment.rs | 2 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 3 | ||||
-rw-r--r-- | components/util/geometry.rs | 2 |
8 files changed, 516 insertions, 369 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 62c19a03c6a..ea09b645ff0 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -6,8 +6,8 @@ //! perform. Using a list instead of painting elements in immediate mode allows transforms, hit //! testing, and invalidation to be performed using the same primitives as painting. It also allows //! Servo to aggressively cull invisible and out-of-bounds painting elements, to reduce overdraw. -//! Finally, display lists allow tiles to be farmed out onto multiple CPUs and painted in -//! parallel (although this benefit does not apply to GPU-based painting). +//! Finally, display lists allow tiles to be farmed out onto multiple CPUs and painted in parallel +//! (although this benefit does not apply to GPU-based painting). //! //! Display items describe relatively high-level drawing operations (for example, entire borders //! and shadows instead of lines and blur operations), to reduce the amount of allocation required. @@ -610,12 +610,12 @@ pub struct BorderDisplayItem { /// Information about the border radii. /// /// TODO(pcwalton): Elliptical radii. -#[deriving(Clone, Default, Show)] +#[deriving(Clone, Default, PartialEq, Show)] pub struct BorderRadii<T> { - pub top_left: T, - pub top_right: T, + pub top_left: T, + pub top_right: T, pub bottom_right: T, - pub bottom_left: T, + pub bottom_left: T, } /// Paints a line segment. @@ -693,6 +693,8 @@ impl DisplayItem { } DisplayItem::ImageClass(ref image_item) => { + // FIXME(pcwalton): This is a really inefficient way to draw a tiled image; use a + // brush instead. debug!("Drawing image at {}.", image_item.base.bounds); let mut y_offset = Au(0); @@ -715,23 +717,21 @@ impl DisplayItem { DisplayItem::BorderClass(ref border) => { paint_context.draw_border(&border.base.bounds, - border.border_widths, - &border.radius, - border.color, - border.style) + &border.border_widths, + &border.radius, + &border.color, + &border.style) } DisplayItem::GradientClass(ref gradient) => { paint_context.draw_linear_gradient(&gradient.base.bounds, - &gradient.start_point, - &gradient.end_point, - gradient.stops.as_slice()); + &gradient.start_point, + &gradient.end_point, + gradient.stops.as_slice()); } DisplayItem::LineClass(ref line) => { - paint_context.draw_line(&line.base.bounds, - line.color, - line.style) + paint_context.draw_line(&line.base.bounds, line.color, line.style) } DisplayItem::BoxShadowClass(ref box_shadow) => { diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index c680233f7ac..4d54758288d 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#![feature(globs, macro_rules, phase, unsafe_destructor, default_type_params)] +#![feature(globs, macro_rules, phase, unsafe_destructor, default_type_params, if_let)] #![deny(unused_imports)] #![deny(unused_variables)] diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs index 7dea4896d6b..835870136cb 100644 --- a/components/gfx/paint_context.rs +++ b/components/gfx/paint_context.rs @@ -77,25 +77,20 @@ impl<'a> PaintContext<'a> { pub fn draw_border(&self, bounds: &Rect<Au>, - border: SideOffsets2D<Au>, + border: &SideOffsets2D<Au>, radius: &BorderRadii<Au>, - color: SideOffsets2D<Color>, - style: SideOffsets2D<border_style::T>) { + color: &SideOffsets2D<Color>, + style: &SideOffsets2D<border_style::T>) { let border = border.to_float_px(); let radius = radius.to_radii_px(); - self.draw_target.make_current(); - - self.draw_border_segment(Direction::Top, bounds, border, &radius, color, style); - self.draw_border_segment(Direction::Right, bounds, border, &radius, color, style); - self.draw_border_segment(Direction::Bottom, bounds, border, &radius, color, style); - self.draw_border_segment(Direction::Left, bounds, border, &radius, color, style); + self.draw_border_segment(Direction::Top, bounds, &border, &radius, color, style); + self.draw_border_segment(Direction::Right, bounds, &border, &radius, color, style); + self.draw_border_segment(Direction::Bottom, bounds, &border, &radius, color, style); + self.draw_border_segment(Direction::Left, bounds, &border, &radius, color, style); } - pub fn draw_line(&self, - bounds: &Rect<Au>, - color: Color, - style: border_style::T) { + pub fn draw_line(&self, bounds: &Rect<Au>, color: Color, style: border_style::T) { self.draw_target.make_current(); self.draw_line_segment(bounds, &Default::default(), color, style); @@ -108,7 +103,8 @@ impl<'a> PaintContext<'a> { let left_top = Point2D(rect.origin.x, rect.origin.y); let right_top = Point2D(rect.origin.x + rect.size.width, rect.origin.y); let left_bottom = Point2D(rect.origin.x, rect.origin.y + rect.size.height); - let right_bottom = Point2D(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + let right_bottom = Point2D(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height); path_builder.move_to(left_top); path_builder.line_to(right_top); @@ -166,10 +162,10 @@ impl<'a> PaintContext<'a> { fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, - border: SideOffsets2D<f32>, + border: &SideOffsets2D<f32>, radius: &BorderRadii<AzFloat>, - color: SideOffsets2D<Color>, - style: SideOffsets2D<border_style::T>) { + color: &SideOffsets2D<Color>, + style: &SideOffsets2D<border_style::T>) { let (style_select, color_select) = match direction { Direction::Top => (style.top, color.top), Direction::Left => (style.left, color.left), @@ -177,59 +173,111 @@ impl<'a> PaintContext<'a> { Direction::Bottom => (style.bottom, color.bottom) }; - match style_select{ - border_style::none => { - } - border_style::hidden => { - } - //FIXME(sammykim): This doesn't work with dash_pattern and cap_style well. I referred firefox code. - border_style::dotted => { - self.draw_dashed_border_segment(direction, bounds, border, color_select, DashSize::DottedBorder); + match style_select { + border_style::none | border_style::hidden => {} + border_style::dotted => { + // FIXME(sammykim): This doesn't work well with dash_pattern and cap_style. + self.draw_dashed_border_segment(direction, + bounds, + border, + color_select, + DashSize::DottedBorder); } - border_style::dashed => { - self.draw_dashed_border_segment(direction, bounds, border, color_select, DashSize::DashedBorder); + border_style::dashed => { + self.draw_dashed_border_segment(direction, + bounds, + border, + color_select, + DashSize::DashedBorder); } - border_style::solid => { - self.draw_solid_border_segment(direction,bounds,border,radius,color_select); + border_style::solid => { + self.draw_solid_border_segment(direction, bounds, border, radius, color_select); } - border_style::double => { + border_style::double => { self.draw_double_border_segment(direction, bounds, border, radius, color_select); } border_style::groove | border_style::ridge => { - self.draw_groove_ridge_border_segment(direction, bounds, border, radius, color_select, style_select); + self.draw_groove_ridge_border_segment(direction, + bounds, + border, + radius, + color_select, + style_select); } border_style::inset | border_style::outset => { - self.draw_inset_outset_border_segment(direction, bounds, border, radius, color_select, style_select); + self.draw_inset_outset_border_segment(direction, + bounds, + border, + radius, + color_select, + style_select); } } } - fn draw_line_segment(&self, bounds: &Rect<Au>, radius: &BorderRadii<AzFloat>, color: Color, style: border_style::T) { + fn draw_line_segment(&self, + bounds: &Rect<Au>, + radius: &BorderRadii<AzFloat>, + color: Color, + style: border_style::T) { let border = SideOffsets2D::new_all_same(bounds.size.width).to_float_px(); - match style { - border_style::none | border_style::hidden => {} - border_style::dotted => { - self.draw_dashed_border_segment(Direction::Right, bounds, border, color, DashSize::DottedBorder); + border_style::none | border_style::hidden => {} + border_style::dotted => { + self.draw_dashed_border_segment(Direction::Right, + bounds, + &border, + color, + DashSize::DottedBorder); } - border_style::dashed => { - self.draw_dashed_border_segment(Direction::Right, bounds, border, color, DashSize::DashedBorder); + border_style::dashed => { + self.draw_dashed_border_segment(Direction::Right, + bounds, + &border, + color, + DashSize::DashedBorder); } - border_style::solid => { - self.draw_solid_border_segment(Direction::Right, bounds, border, radius, color); + border_style::solid => { + self.draw_solid_border_segment(Direction::Right, bounds, &border, radius, color) } - border_style::double => { - self.draw_double_border_segment(Direction::Right, bounds, border, radius, color); + border_style::double => { + self.draw_double_border_segment(Direction::Right, bounds, &border, radius, color) } border_style::groove | border_style::ridge => { - self.draw_groove_ridge_border_segment(Direction::Right, bounds, border, radius, color, style); + self.draw_groove_ridge_border_segment(Direction::Right, + bounds, + &border, + radius, + color, + style); } border_style::inset | border_style::outset => { - self.draw_inset_outset_border_segment(Direction::Right, bounds, border, radius, color, style); + self.draw_inset_outset_border_segment(Direction::Right, + bounds, + &border, + radius, + color, + style); } } } + fn draw_border_path(&self, + bounds: &Rect<f32>, + direction: Direction, + border: &SideOffsets2D<f32>, + radii: &BorderRadii<AzFloat>, + color: Color) { + let mut path_builder = self.draw_target.create_path_builder(); + self.create_border_path_segment(&mut path_builder, + bounds, + direction, + border, + radii); + let draw_options = DrawOptions::new(1.0, 0); + self.draw_target.fill(&path_builder.finish(), &ColorPattern::new(color), &draw_options); + } + // The following comment is wonderful, and stolen from // gecko:gfx/thebes/gfxContext.cpp:RoundedRectangle for reference. // @@ -310,14 +358,13 @@ impl<'a> PaintContext<'a> { // For the various corners and for each axis, the sign of this // constant changes, or it might be 0 -- it's multiplied by the // appropriate multiplier from the list before using. - #[allow(non_snake_case)] - fn draw_border_path(&self, - bounds: &Rect<f32>, - direction: Direction, - border: SideOffsets2D<f32>, - radius: &BorderRadii<AzFloat>, - color: Color) { + fn create_border_path_segment(&self, + path_builder: &mut PathBuilder, + bounds: &Rect<f32>, + direction: Direction, + border: &SideOffsets2D<f32>, + radius: &BorderRadii<AzFloat>) { // T = top, B = bottom, L = left, R = right let box_TL = bounds.origin; @@ -325,9 +372,6 @@ impl<'a> PaintContext<'a> { let box_BL = box_TL + Point2D(0.0, bounds.size.height); let box_BR = box_TL + Point2D(bounds.size.width, bounds.size.height); - let draw_opts = DrawOptions::new(1.0, 0); - let path_builder = self.draw_target.create_path_builder(); - let rad_R: AzFloat = 0.; let rad_BR = rad_R + Float::frac_pi_4(); let rad_B = rad_BR + Float::frac_pi_4(); @@ -354,9 +398,9 @@ impl<'a> PaintContext<'a> { } match direction { - Direction::Top => { - let edge_TL = box_TL + dx(radius.top_left.max(border.left)); - let edge_TR = box_TR + dx(-radius.top_right.max(border.right)); + Direction::Top => { + let edge_TL = box_TL + dx(radius.top_left.max(border.left)); + let edge_TR = box_TR + dx(-radius.top_right.max(border.right)); let edge_BR = edge_TR + dy(border.top); let edge_BL = edge_TL + dy(border.top); @@ -368,7 +412,8 @@ impl<'a> PaintContext<'a> { if radius.top_right != 0. { // the origin is the center of the arcs we're about to draw. - let origin = edge_TR + Point2D((border.right - radius.top_right).max(0.), radius.top_right); + let origin = edge_TR + Point2D((border.right - radius.top_right).max(0.), + radius.top_right); // the elbow is the inside of the border's curve. let distance_to_elbow = (radius.top_right - border.top).max(0.); @@ -380,16 +425,17 @@ impl<'a> PaintContext<'a> { path_builder.line_to(edge_BL); if radius.top_left != 0. { - let origin = edge_TL + Point2D(-(border.left - radius.top_left).max(0.), radius.top_left); + let origin = edge_TL + Point2D(-(border.left - radius.top_left).max(0.), + radius.top_left); let distance_to_elbow = (radius.top_left - border.top).max(0.); path_builder.arc(origin, distance_to_elbow, rad_T, rad_TL, true); path_builder.arc(origin, radius.top_left, rad_TL, rad_T, false); } } - Direction::Left => { - let edge_TL = box_TL + dy(radius.top_left.max(border.top)); - let edge_BL = box_BL + dy(-radius.bottom_left.max(border.bottom)); + Direction::Left => { + let edge_TL = box_TL + dy(radius.top_left.max(border.top)); + let edge_BL = box_BL + dy(-radius.bottom_left.max(border.bottom)); let edge_TR = edge_TL + dx(border.left); let edge_BR = edge_BL + dx(border.left); @@ -400,7 +446,8 @@ impl<'a> PaintContext<'a> { path_builder.line_to(corner_TL); if radius.top_left != 0. { - let origin = edge_TL + Point2D(radius.top_left, -(border.top - radius.top_left).max(0.)); + let origin = edge_TL + Point2D(radius.top_left, + -(border.top - radius.top_left).max(0.)); let distance_to_elbow = (radius.top_left - border.left).max(0.); path_builder.arc(origin, radius.top_left, rad_L, rad_TL, false); @@ -411,16 +458,18 @@ impl<'a> PaintContext<'a> { path_builder.line_to(edge_BR); if radius.bottom_left != 0. { - let origin = edge_BL + Point2D(radius.bottom_left, (border.bottom - radius.bottom_left).max(0.)); + let origin = edge_BL + + Point2D(radius.bottom_left, + (border.bottom - radius.bottom_left).max(0.)); let distance_to_elbow = (radius.bottom_left - border.left).max(0.); path_builder.arc(origin, distance_to_elbow, rad_L, rad_BL, true); path_builder.arc(origin, radius.bottom_left, rad_BL, rad_L, false); } } - Direction::Right => { - let edge_TR = box_TR + dy(radius.top_right.max(border.top)); - let edge_BR = box_BR + dy(-radius.bottom_right.max(border.bottom)); + Direction::Right => { + let edge_TR = box_TR + dy(radius.top_right.max(border.top)); + let edge_BR = box_BR + dy(-radius.bottom_right.max(border.bottom)); let edge_TL = edge_TR + dx(-border.right); let edge_BL = edge_BR + dx(-border.right); @@ -431,7 +480,8 @@ impl<'a> PaintContext<'a> { path_builder.line_to(edge_TL); if radius.top_right != 0. { - let origin = edge_TR + Point2D(-radius.top_right, -(border.top - radius.top_right).max(0.)); + let origin = edge_TR + Point2D(-radius.top_right, + -(border.top - radius.top_right).max(0.)); let distance_to_elbow = (radius.top_right - border.right).max(0.); path_builder.arc(origin, distance_to_elbow, rad_R, rad_TR, true); @@ -442,7 +492,9 @@ impl<'a> PaintContext<'a> { path_builder.line_to(corner_BR); if radius.bottom_right != 0. { - let origin = edge_BR + Point2D(-radius.bottom_right, (border.bottom - radius.bottom_right).max(0.)); + let origin = edge_BR + + Point2D(-radius.bottom_right, + (border.bottom - radius.bottom_right).max(0.)); let distance_to_elbow = (radius.bottom_right - border.right).max(0.); path_builder.arc(origin, radius.bottom_right, rad_R, rad_BR, false); @@ -450,8 +502,8 @@ impl<'a> PaintContext<'a> { } } Direction::Bottom => { - let edge_BL = box_BL + dx(radius.bottom_left.max(border.left)); - let edge_BR = box_BR + dx(-radius.bottom_right.max(border.right)); + let edge_BL = box_BL + dx(radius.bottom_left.max(border.left)); + let edge_BR = box_BR + dx(-radius.bottom_right.max(border.right)); let edge_TL = edge_BL + dy(-border.bottom); let edge_TR = edge_BR + dy(-border.bottom); @@ -462,7 +514,8 @@ impl<'a> PaintContext<'a> { path_builder.line_to(edge_TR); if radius.bottom_right != 0. { - let origin = edge_BR + Point2D((border.right - radius.bottom_right).max(0.), -radius.bottom_right); + let origin = edge_BR + Point2D((border.right - radius.bottom_right).max(0.), + -radius.bottom_right); let distance_to_elbow = (radius.bottom_right - border.bottom).max(0.); path_builder.arc(origin, distance_to_elbow, rad_B, rad_BR, true); @@ -473,7 +526,8 @@ impl<'a> PaintContext<'a> { path_builder.line_to(corner_BL); if radius.bottom_left != 0. { - let origin = edge_BL - Point2D((border.left - radius.bottom_left).max(0.), radius.bottom_left); + let origin = edge_BL - Point2D((border.left - radius.bottom_left).max(0.), + radius.bottom_left); let distance_to_elbow = (radius.bottom_left - border.bottom).max(0.); path_builder.arc(origin, radius.bottom_left, rad_B, rad_BL, false); @@ -481,6 +535,7 @@ impl<'a> PaintContext<'a> { } } } + } let path = path_builder.finish(); self.draw_target.fill(&path, &ColorPattern::new(color), &draw_opts); @@ -488,9 +543,9 @@ impl<'a> PaintContext<'a> { fn draw_dashed_border_segment(&self, direction: Direction, - bounds: &Rect<Au>, - border: SideOffsets2D<f32>, - color: Color, + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, + color: Color, dash_size: DashSize) { let rect = bounds.to_azure_rect(); let draw_opts = DrawOptions::new(1u as AzFloat, 0 as uint16_t); @@ -546,14 +601,19 @@ impl<'a> PaintContext<'a> { &draw_opts); } - fn draw_solid_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, radius: &BorderRadii<AzFloat>, color: Color) { + fn draw_solid_border_segment(&self, + direction: Direction, + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, + radius: &BorderRadii<AzFloat>, + color: Color) { let rect = bounds.to_azure_rect(); self.draw_border_path(&rect, direction, border, radius, color); } fn get_scaled_bounds(&self, - bounds: &Rect<Au>, - border: SideOffsets2D<f32>, + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, shrink_factor: f32) -> Rect<f32> { let rect = bounds.to_azure_rect(); let scaled_border = SideOffsets2D::new(shrink_factor * border.top, @@ -571,25 +631,30 @@ impl<'a> PaintContext<'a> { return Color::new(color.r * scale_factor, color.g * scale_factor, color.b * scale_factor, color.a); } - fn draw_double_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, radius: &BorderRadii<AzFloat>, color: Color) { - let scaled_border = SideOffsets2D::new((1.0/3.0) * border.top, - (1.0/3.0) * border.right, - (1.0/3.0) * border.bottom, - (1.0/3.0) * border.left); + fn draw_double_border_segment(&self, + direction: Direction, + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, + radius: &BorderRadii<AzFloat>, + color: Color) { + let scaled_border = SideOffsets2D::new((1.0/3.0) * border.top, + (1.0/3.0) * border.right, + (1.0/3.0) * border.bottom, + (1.0/3.0) * border.left); let inner_scaled_bounds = self.get_scaled_bounds(bounds, border, 2.0/3.0); // draw the outer portion of the double border. - self.draw_solid_border_segment(direction, bounds, scaled_border, radius, color); + self.draw_solid_border_segment(direction, bounds, &scaled_border, radius, color); // draw the inner portion of the double border. - self.draw_border_path(&inner_scaled_bounds, direction, scaled_border, radius, color); + self.draw_border_path(&inner_scaled_bounds, direction, &scaled_border, radius, color); } fn draw_groove_ridge_border_segment(&self, direction: Direction, - bounds: &Rect<Au>, - border: SideOffsets2D<f32>, - radius: &BorderRadii<AzFloat>, - color: Color, - style: border_style::T) { + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, + radius: &BorderRadii<AzFloat>, + color: Color, + style: border_style::T) { // original bounds as a Rect<f32>, with no scaling. let original_bounds = self.get_scaled_bounds(bounds, border, 0.0); // shrink the bounds by 1/2 of the border, leaving the innermost 1/2 of the border @@ -617,44 +682,65 @@ impl<'a> PaintContext<'a> { } let (outer_color, inner_color) = match (direction, is_groove) { - (Direction::Top, true) | (Direction::Left, true) | - (Direction::Right, false) | (Direction::Bottom, false) => (darker_color, lighter_color), + (Direction::Top, true) | (Direction::Left, true) | + (Direction::Right, false) | (Direction::Bottom, false) => { + (darker_color, lighter_color) + } (Direction::Top, false) | (Direction::Left, false) | - (Direction::Right, true) | (Direction::Bottom, true) => (lighter_color, darker_color) + (Direction::Right, true) | (Direction::Bottom, true) => (lighter_color, darker_color), }; // outer portion of the border - self.draw_border_path(&original_bounds, direction, scaled_border, radius, outer_color); + self.draw_border_path(&original_bounds, direction, &scaled_border, radius, outer_color); // inner portion of the border - self.draw_border_path(&inner_scaled_bounds, direction, scaled_border, radius, inner_color); + self.draw_border_path(&inner_scaled_bounds, + direction, + &scaled_border, + radius, + inner_color); } fn draw_inset_outset_border_segment(&self, direction: Direction, - bounds: &Rect<Au>, - border: SideOffsets2D<f32>, - radius: &BorderRadii<AzFloat>, - color: Color, - style: border_style::T) { + bounds: &Rect<Au>, + border: &SideOffsets2D<f32>, + radius: &BorderRadii<AzFloat>, + color: Color, + style: border_style::T) { let is_inset = match style { - border_style::inset => true, - border_style::outset => false, - _ => panic!("invalid border style") + border_style::inset => true, + border_style::outset => false, + _ => panic!("invalid border style") }; // original bounds as a Rect<f32> let original_bounds = self.get_scaled_bounds(bounds, border, 0.0); - let mut scaled_color; - // You can't scale black color (i.e. 'scaled = 0 * scale', equals black). + let mut scaled_color; if color.r != 0.0 || color.g != 0.0 || color.b != 0.0 { scaled_color = match direction { - Direction::Top | Direction::Left => self.scale_color(color, if is_inset { 2.0/3.0 } else { 1.0 }), - Direction::Right | Direction::Bottom => self.scale_color(color, if is_inset { 1.0 } else { 2.0/3.0 }) + Direction::Top | Direction::Left => { + self.scale_color(color, if is_inset { 2.0/3.0 } else { 1.0 }) + } + Direction::Right | Direction::Bottom => { + self.scale_color(color, if is_inset { 1.0 } else { 2.0/3.0 }) + } }; } else { scaled_color = match direction { - Direction::Top | Direction::Left => if is_inset { Color::new(0.3, 0.3, 0.3, color.a) } else { Color::new(0.7, 0.7, 0.7, color.a) }, - Direction::Right | Direction::Bottom => if is_inset { Color::new(0.7, 0.7, 0.7, color.a) } else { Color::new(0.3, 0.3, 0.3, color.a) } + Direction::Top | Direction::Left => { + if is_inset { + Color::new(0.3, 0.3, 0.3, color.a) + } else { + Color::new(0.7, 0.7, 0.7, color.a) + } + } + Direction::Right | Direction::Bottom => { + if is_inset { + Color::new(0.7, 0.7, 0.7, color.a) + } else { + Color::new(0.3, 0.3, 0.3, color.a) + } + } }; } @@ -852,9 +938,8 @@ impl<'a> PaintContext<'a> { } pub fn push_clip_if_applicable(&self) { - match self.clip_rect { - None => {} - Some(ref clip_rect) => self.draw_push_clip(clip_rect), + if let Some(ref clip_rect) = self.clip_rect { + self.draw_push_clip(clip_rect) } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 25e567d4e3a..760c441f010 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -167,25 +167,53 @@ pub trait FragmentDisplayListBuilding { offset: Point2D<Au>, layout_context: &LayoutContext); - fn clip_rect_for_children(&self, current_clip_rect: &Rect<Au>, flow_origin: &Point2D<Au>) - -> Rect<Au>; + fn clipping_region_for_children(&self, current_clip: ClippingRegion, flow_origin: Point2D<Au>) + -> ClippingRegion; /// Calculates the clipping rectangle for a fragment, taking the `clip` property into account /// per CSS 2.1 § 11.1.2. fn calculate_style_specified_clip(&self, parent_clip_rect: &Rect<Au>, origin: &Point2D<Au>) -> Rect<Au>; + + /// Creates the text display item for one text fragment. + fn build_display_list_for_text_fragment(&self, + display_list: &mut DisplayList, + text_fragment: &ScannedTextFragmentInfo, + text_color: RGBA, + offset: &Point2D<Au>, + flow_origin: &Point2D<Au>, + clip: &ClippingRegion); + + /// Creates the display item for a text decoration: underline, overline, or line-through. + fn build_display_list_for_text_decoration(&self, + display_list: &mut DisplayList, + color: RGBA, + flow_origin: &Point2D<Au>, + clip: &ClippingRegion, + logical_bounds: &LogicalRect<Au>, + offset: &Point2D<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, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + clip: &ClippingRegion); } fn build_border_radius(abs_bounds: &Rect<Au>, border_style: &Border) -> BorderRadii<Au> { // TODO(cgaebel): Support border radii even in the case of multiple border widths. - // This is an extennsion of supporting elliptical radii. For now, all percentage + // This is an extension of supporting elliptical radii. For now, all percentage // radii will be relative to the width. BorderRadii { - top_left: model::specified(border_style.border_top_left_radius.radius, abs_bounds.size.width), - top_right: model::specified(border_style.border_top_right_radius.radius, abs_bounds.size.width), - bottom_right: model::specified(border_style.border_bottom_right_radius.radius, abs_bounds.size.width), - bottom_left: model::specified(border_style.border_bottom_left_radius.radius, abs_bounds.size.width), + top_left: model::specified(border_style.border_top_left_radius.radius, + abs_bounds.size.width), + top_right: model::specified(border_style.border_top_right_radius.radius, + abs_bounds.size.width), + bottom_right: model::specified(border_style.border_bottom_right_radius.radius, + abs_bounds.size.width), + bottom_left: model::specified(border_style.border_bottom_left_radius.radius, + abs_bounds.size.width), } } @@ -645,13 +673,6 @@ impl FragmentDisplayListBuilding for Fragment { let absolute_fragment_bounds = self.stacking_relative_bounds(&stacking_relative_flow_origin); - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| { - let physical_rect = logical_rect.to_physical(writing_mode, container_size); - Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) - }; - debug!("Fragment::build_display_list at rel={}, abs={}: {}", self.border_box, absolute_fragment_bounds, @@ -684,250 +705,179 @@ impl FragmentDisplayListBuilding for Fragment { StackingLevel::from_background_and_border_level(background_and_border_level); // Add a shadow to the list, if applicable. - match self.inline_context { - Some(ref inline_context) => { - for style in inline_context.styles.iter().rev() { - self.build_display_list_for_box_shadow_if_applicable( - &**style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip_rect); - } + if let Some(ref inline_context) = self.inline_context { + for style in inline_context.styles.iter().rev() { + self.build_display_list_for_box_shadow_if_applicable(&**style, + display_list, + layout_context, + level, + &absolute_fragment_bounds, + clip); } - None => {} } - match self.specific { - SpecificFragmentInfo::ScannedText(_) => {}, - _ => { - self.build_display_list_for_box_shadow_if_applicable( - &*self.style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip_rect); - } + if !self.is_scanned_text_fragment() { + self.build_display_list_for_box_shadow_if_applicable(&*self.style, + display_list, + layout_context, + level, + &absolute_fragment_bounds, + clip); } // Add the background to the list, if applicable. - match self.inline_context { - Some(ref inline_context) => { - for style in inline_context.styles.iter().rev() { - self.build_display_list_for_background_if_applicable( - &**style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip_rect); - } + if let Some(ref inline_context) = self.inline_context { + for style in inline_context.styles.iter().rev() { + self.build_display_list_for_background_if_applicable(&**style, + display_list, + layout_context, + level, + &absolute_fragment_bounds, + clip); } - None => {} } - match self.specific { - SpecificFragmentInfo::ScannedText(_) => {}, - _ => { - self.build_display_list_for_background_if_applicable( - &*self.style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip_rect); - } + if !self.is_scanned_text_fragment() { + self.build_display_list_for_background_if_applicable(&*self.style, + display_list, + layout_context, + level, + &absolute_fragment_bounds, + clip); } // Add a border and outlines, if applicable. - match self.inline_context { - Some(ref inline_context) => { - for style in inline_context.styles.iter().rev() { - self.build_display_list_for_borders_if_applicable( - &**style, - display_list, - &absolute_fragment_bounds, - level, - &clip_rect); - self.build_display_list_for_outline_if_applicable( - &**style, - display_list, - &absolute_fragment_bounds, - &clip_rect); - } + if let Some(ref inline_context) = self.inline_context { + for style in inline_context.styles.iter().rev() { + self.build_display_list_for_borders_if_applicable(&**style, + display_list, + &absolute_fragment_bounds, + level, + clip); + self.build_display_list_for_outline_if_applicable(&**style, + display_list, + &absolute_fragment_bounds, + clip); } - None => {} } - match self.specific { - SpecificFragmentInfo::ScannedText(_) => {}, - _ => { - self.build_display_list_for_borders_if_applicable( - &*self.style, - display_list, - &absolute_fragment_bounds, - level, - &clip_rect); - self.build_display_list_for_outline_if_applicable( - &*self.style, - display_list, - &absolute_fragment_bounds, - &clip_rect); - } + if !self.is_scanned_text_fragment() { + self.build_display_list_for_borders_if_applicable(&*self.style, + display_list, + &absolute_fragment_bounds, + level, + clip); + self.build_display_list_for_outline_if_applicable(&*self.style, + display_list, + &absolute_fragment_bounds, + clip); } } + // Create special per-fragment-type display items. + self.build_fragment_type_specific_display_items(display_list, flow_origin, clip); + + if opts::get().show_debug_fragment_borders { + self.build_debug_borders_around_fragment(display_list, flow_origin, clip) + } + + // If this is an iframe, then send its position and size up to the constellation. + // + // FIXME(pcwalton): Doing this during display list construction seems potentially + // problematic if iframes are outside the area we're computing the display list for, since + // they won't be able to reflow at all until the user scrolls to them. Perhaps we should + // separate this into two parts: first we should send the size only to the constellation + // once that's computed during assign-block-sizes, and second we should should send the + // origin to the constellation here during display list construction. This should work + // because layout for the iframe only needs to know size, and origin is only relevant if + // the iframe is actually going to be displayed. + if let SpecificFragmentInfo::Iframe(ref iframe_fragment) = self.specific { + self.finalize_position_and_size_of_iframe(&**iframe_fragment, + absolute_fragment_bounds.origin, + layout_context) + } + } + + fn build_fragment_type_specific_display_items(&mut self, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + clip: &ClippingRegion) { + // Compute the fragment position relative to the parent stacking context. If the fragment + // itself establishes a stacking context, then the origin of its position will be (0, 0) + // for the purposes of this computation. + let stacking_relative_flow_origin = if self.establishes_stacking_context() { + ZERO_POINT + } else { + flow_origin + }; + + // FIXME(#2795): Get the real container size. let content_box = self.content_box(); - let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box); + let container_size = Size2D::zero(); + let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| { + let physical_rect = logical_rect.to_physical(writing_mode, container_size); + Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) + }; - // Create special per-fragment-type display items. match self.specific { - SpecificFragmentInfo::UnscannedText(_) => panic!("Shouldn't see unscanned fragments here."), - SpecificFragmentInfo::TableColumn(_) => panic!("Shouldn't see table column fragments here."), + 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 text display item. - let (orientation, cursor) = if self.style.writing_mode.is_vertical() { - if self.style.writing_mode.is_sideways_left() { - (SidewaysLeft, VerticalTextCursor) - } else { - (SidewaysRight, VerticalTextCursor) - } - } else { - (Upright, TextCursor) - }; - - let metrics = &text_fragment.run.font_metrics; - let baseline_origin = { - let mut content_box_start = content_box.start; - content_box_start.b = content_box_start.b + metrics.ascent; - content_box_start.to_physical(self.style.writing_mode, container_size) - + flow_origin - }; - - display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, - DisplayItemMetadata::new(self.node, - self.style(), - cursor), - clip_rect), - text_run: text_fragment.run.clone(), - range: text_fragment.range, - text_color: self.style().get_color().color.to_gfx_color(), - orientation: orientation, - baseline_origin: baseline_origin, - })); - - // Create display items for text decoration - { - let line = |maybe_color: Option<RGBA>, - style: &ComputedValues, - rect: || -> LogicalRect<Au>| { - match maybe_color { - None => {} - Some(color) => { - let bounds = rect_to_absolute(self.style.writing_mode, rect()); - display_list.content.push_back(DisplayItem::SolidColorClass( - box SolidColorDisplayItem { - base: BaseDisplayItem::new( - bounds, - DisplayItemMetadata::new(self.node, - style, - DefaultCursor), - clip_rect), - color: color.to_gfx_color(), - })) - } - } - }; - - let text_decorations = - self.style().get_inheritedtext()._servo_text_decorations_in_effect; - line(text_decorations.underline, self.style(), || { - let mut rect = content_box.clone(); - rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset; - rect.size.block = metrics.underline_size; - rect - }); - - line(text_decorations.overline, self.style(), || { - let mut rect = content_box.clone(); - rect.size.block = metrics.underline_size; - rect - }); - - line(text_decorations.line_through, self.style(), || { - let mut rect = content_box.clone(); - rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset; - rect.size.block = metrics.strikeout_size; - rect - }); - } + // Create the main text display item. + let text_color = self.style().get_color().color; + self.build_display_list_for_text_fragment(display_list, + &**text_fragment, + text_color, + &flow_origin, + &Point2D(Au(0), Au(0)), + clip); if opts::get().show_debug_fragment_borders { self.build_debug_borders_around_text_fragments(self.style(), display_list, flow_origin, &**text_fragment, - &clip_rect); + clip); } } - SpecificFragmentInfo::Generic | SpecificFragmentInfo::Iframe(..) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableRow | SpecificFragmentInfo::TableWrapper | SpecificFragmentInfo::InlineBlock(_) | + SpecificFragmentInfo::Generic | + SpecificFragmentInfo::Iframe(..) | + SpecificFragmentInfo::Table | + SpecificFragmentInfo::TableCell | + SpecificFragmentInfo::TableRow | + SpecificFragmentInfo::TableWrapper | + SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => { if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, - flow_origin, - &clip_rect); + self.build_debug_borders_around_fragment(display_list, flow_origin, clip); } } SpecificFragmentInfo::Image(ref mut image_fragment) => { let image_ref = &mut image_fragment.image; - match image_ref.get_image(self.node.to_untrusted_node_address()) { - Some(image) => { - debug!("(building display list) building image fragment"); - - // Place the image into the display list. - display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, - DisplayItemMetadata::new(self.node, - &*self.style, - DefaultCursor), - clip_rect), - image: image.clone(), - stretch_size: absolute_content_box.size, - })); - } - None => { - // No image data at all? Do nothing. - // - // TODO: Add some kind of placeholder image. - debug!("(building display list) no image :("); - } + if let Some(image) = image_ref.get_image(self.node.to_untrusted_node_address()) { + debug!("(building display list) building image fragment"); + let absolute_content_box = rect_to_absolute(self.style.writing_mode, + content_box); + + // Place the image into the display list. + display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem { + base: BaseDisplayItem::new(absolute_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + DefaultCursor), + (*clip).clone()), + image: image.clone(), + stretch_size: absolute_content_box.size, + })); + } else { + // No image data at all? Do nothing. + // + // TODO: Add some kind of placeholder image. + debug!("(building display list) no image :("); } } } - - if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, flow_origin, &clip_rect) - } - - // If this is an iframe, then send its position and size up to the constellation. - // - // FIXME(pcwalton): Doing this during display list construction seems potentially - // problematic if iframes are outside the area we're computing the display list for, since - // they won't be able to reflow at all until the user scrolls to them. Perhaps we should - // separate this into two parts: first we should send the size only to the constellation - // once that's computed during assign-block-sizes, and second we should should send the - // origin to the constellation here during display list construction. This should work - // because layout for the iframe only needs to know size, and origin is only relevant if - // the iframe is actually going to be displayed. - match self.specific { - SpecificFragmentInfo::Iframe(ref iframe_fragment) => { - self.finalize_position_and_size_of_iframe(&**iframe_fragment, - absolute_fragment_bounds.origin, - layout_context) - } - _ => {} - } } #[inline(never)] @@ -951,12 +901,11 @@ impl FragmentDisplayListBuilding for Fragment { iframe_rect)); } - fn clip_rect_for_children(&self, current_clip_rect: &Rect<Au>, origin: &Point2D<Au>) - -> Rect<Au> { + fn clipping_region_for_children(&self, current_clip: ClippingRegion, flow_origin: Point2D<Au>) + -> ClippingRegion { // Don't clip if we're text. - match self.specific { - SpecificFragmentInfo::ScannedText(_) => return *current_clip_rect, - _ => {} + if self.is_scanned_text_fragment() { + return current_clip } // Account for style-specified `clip`. @@ -965,16 +914,131 @@ impl FragmentDisplayListBuilding for Fragment { // Only clip if `overflow` tells us to. match self.style.get_box().overflow { overflow::hidden | overflow::auto | overflow::scroll => {} - _ => return current_clip_rect, + _ => return current_clip, } // Create a new clip rect. // // FIXME(#2795): Get the real container size. let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); - current_clip_rect.intersection(&Rect(Point2D(physical_rect.origin.x + origin.x, - physical_rect.origin.y + origin.y), - physical_rect.size)).unwrap_or(ZERO_RECT) + current_clip.intersect_rect(&Rect(physical_rect.origin + flow_origin, physical_rect.size)) + } + + fn build_display_list_for_text_fragment(&self, + display_list: &mut DisplayList, + text_fragment: &ScannedTextFragmentInfo, + text_color: RGBA, + flow_origin: &Point2D<Au>, + offset: &Point2D<Au>, + clip: &ClippingRegion) { + // Determine the orientation and cursor to use. + let (orientation, cursor) = if self.style.writing_mode.is_vertical() { + if self.style.writing_mode.is_sideways_left() { + (SidewaysLeft, VerticalTextCursor) + } else { + (SidewaysRight, VerticalTextCursor) + } + } else { + (Upright, TextCursor) + }; + + // Compute location of the baseline. + // + // FIXME(pcwalton): Get the real container size. + let container_size = Size2D::zero(); + let content_box = self.content_box(); + let metrics = &text_fragment.run.font_metrics; + let baseline_origin = { + let mut content_box_start = content_box.start; + content_box_start.b = content_box_start.b + metrics.ascent; + content_box_start.to_physical(self.style.writing_mode, container_size) + *flow_origin + + *offset + }; + let stacking_relative_flow_origin = if self.establishes_stacking_context() { + ZERO_POINT + } else { + *flow_origin + }; + let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| { + let physical_rect = logical_rect.to_physical(writing_mode, container_size); + Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) + }; + let content_rect = rect_to_absolute(self.style.writing_mode, + content_box).translate(offset); + + // Create the text display item. + display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem { + base: BaseDisplayItem::new(content_rect, + DisplayItemMetadata::new(self.node, self.style(), cursor), + (*clip).clone()), + text_run: text_fragment.run.clone(), + range: text_fragment.range, + text_color: text_color.to_gfx_color(), + orientation: orientation, + baseline_origin: baseline_origin, + })); + + // Create display items for text decorations. + let text_decorations = self.style().get_inheritedtext()._servo_text_decorations_in_effect; + if let Some(underline_color) = text_decorations.underline { + let mut rect = content_box.clone(); + rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset; + rect.size.block = metrics.underline_size; + self.build_display_list_for_text_decoration(display_list, + underline_color, + flow_origin, + clip, + &rect, + offset) + } + + if let Some(overline_color) = text_decorations.overline { + let mut rect = content_box.clone(); + rect.size.block = metrics.underline_size; + self.build_display_list_for_text_decoration(display_list, + overline_color, + flow_origin, + clip, + &rect, + offset) + } + + if let Some(line_through_color) = text_decorations.line_through { + let mut rect = content_box.clone(); + rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset; + rect.size.block = metrics.strikeout_size; + self.build_display_list_for_text_decoration(display_list, + line_through_color, + flow_origin, + clip, + &rect, + offset) + } + } + + fn build_display_list_for_text_decoration(&self, + display_list: &mut DisplayList, + color: RGBA, + flow_origin: &Point2D<Au>, + clip: &ClippingRegion, + logical_bounds: &LogicalRect<Au>, + offset: &Point2D<Au>) { + // FIXME(pcwalton): Get the real container size. + let container_size = Size2D::zero(); + let stacking_relative_flow_origin = if self.establishes_stacking_context() { + ZERO_POINT + } else { + *flow_origin + }; + let physical_rect = logical_bounds.to_physical(self.style.writing_mode, container_size); + + let bounds = Rect(physical_rect.origin + stacking_relative_flow_origin, + physical_rect.size).translate(offset); + let metadata = DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor); + display_list.content.push_back(DisplayItem::SolidColorClass(box SolidColorDisplayItem { + base: BaseDisplayItem::new(bounds, metadata, (*clip).clone()), + color: color.to_gfx_color(), + })) } } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 873f6268fd7..7574e2ec470 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -50,8 +50,7 @@ use geom::{Point2D, Rect, Size2D}; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; -use servo_util::logical_geometry::WritingMode; -use servo_util::logical_geometry::{LogicalRect, LogicalSize}; +use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use std::mem; use std::fmt; use std::iter::Zip; @@ -290,7 +289,7 @@ pub trait Flow: fmt::Show + ToString + Sync { /// NB: Do not change this `&self` to `&mut self` under any circumstances! It has security /// implications because this can be called on parents concurrently from descendants! fn generated_containing_block_rect(&self) -> LogicalRect<Au> { - panic!("generated_containing_block_position not yet implemented for this flow") + panic!("generated_containing_block_rect not yet implemented for this flow") } /// Returns a layer ID for the given fragment. diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index bfc4286ae0e..b50f484f8d9 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -954,7 +954,7 @@ impl Fragment { } /// Returns true if and only if this is a scanned text fragment. - fn is_scanned_text_fragment(&self) -> bool { + pub fn is_scanned_text_fragment(&self) -> bool { match self.specific { SpecificFragmentInfo::ScannedText(..) => true, _ => false, diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 94073cd4e09..0da87f249d8 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -28,8 +28,7 @@ use gfx::color; use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, StackingContext}; use gfx::font_cache_task::FontCacheTask; use gfx::paint_task::{mod, PaintInitMsg, PaintChan, PaintLayer}; -use layout_traits; -use layout_traits::{LayoutControlMsg, LayoutTaskFactory}; +use layout_traits::{mod, LayoutControlMsg, LayoutTaskFactory}; use log; use script::dom::bindings::js::JS; use script::dom::node::{LayoutDataRef, Node, NodeTypeId}; diff --git a/components/util/geometry.rs b/components/util/geometry.rs index 1e28a3a56f4..1191edba02a 100644 --- a/components/util/geometry.rs +++ b/components/util/geometry.rs @@ -65,7 +65,7 @@ pub enum PagePx {} // See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info. // // FIXME: Implement Au using Length and ScaleFactor instead of a custom type. -#[deriving(Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +#[deriving(Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Zero)] pub struct Au(pub i32); impl Default for Au { |