gfx: Update Azure and Skia, and rewrite broken clipping logic.

This exposed some problems in our clipping logic, which was never
properly rewritten for the stacking context reform. The clipping code
worked in terms of a stack of clips, but the new stacking context code
has no concept of a stack of clip regions. Fixing that in turn exposed
some flaky/incorrect tests:

* `borders` had an incorrect reference image, as far as I can tell.

* `negative_margins` had some stray pixels, fixed by changing the text.
This commit is contained in:
Patrick Walton 2014-12-05 03:55:39 -08:00
parent 368d6dc6bf
commit 1d845ee4f2
7 changed files with 71 additions and 61 deletions

View file

@ -176,8 +176,8 @@ impl StackingContext {
pub fn optimize_and_draw_into_context(&self, pub fn optimize_and_draw_into_context(&self,
paint_context: &mut PaintContext, paint_context: &mut PaintContext,
tile_bounds: &Rect<AzFloat>, tile_bounds: &Rect<AzFloat>,
current_transform: &Matrix2D<AzFloat>, transform: &Matrix2D<AzFloat>,
current_clip_stack: &mut Vec<Rect<Au>>) { clip_rect: Option<&Rect<Au>>) {
let temporary_draw_target = let temporary_draw_target =
paint_context.get_or_create_temporary_draw_target(self.opacity); paint_context.get_or_create_temporary_draw_target(self.opacity);
{ {
@ -186,7 +186,7 @@ impl StackingContext {
font_ctx: &mut *paint_context.font_ctx, font_ctx: &mut *paint_context.font_ctx,
page_rect: paint_context.page_rect, page_rect: paint_context.page_rect,
screen_rect: paint_context.screen_rect, screen_rect: paint_context.screen_rect,
..*paint_context transient_clip_rect: None,
}; };
// Optimize the display list to throw out out-of-bounds display items and so forth. // Optimize the display list to throw out out-of-bounds display items and so forth.
@ -201,11 +201,17 @@ impl StackingContext {
positioned_children.as_slice_mut() positioned_children.as_slice_mut()
.sort_by(|this, other| this.z_index.cmp(&other.z_index)); .sort_by(|this, other| this.z_index.cmp(&other.z_index));
// Set up our clip rect and transform.
match clip_rect {
None => {}
Some(clip_rect) => paint_subcontext.draw_push_clip(clip_rect),
}
let old_transform = paint_subcontext.draw_target.get_transform();
paint_subcontext.draw_target.set_transform(transform);
// Steps 1 and 2: Borders and background for the root. // Steps 1 and 2: Borders and background for the root.
for display_item in display_list.background_and_borders.iter() { for display_item in display_list.background_and_borders.iter() {
display_item.draw_into_context(&mut paint_subcontext, display_item.draw_into_context(&mut paint_subcontext)
current_transform,
current_clip_stack)
} }
// Step 3: Positioned descendants with negative z-indices. // Step 3: Positioned descendants with negative z-indices.
@ -215,41 +221,39 @@ impl StackingContext {
} }
if positioned_kid.layer.is_none() { if positioned_kid.layer.is_none() {
let new_transform = let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px() transform.translate(positioned_kid.bounds
as AzFloat, .origin
positioned_kid.bounds.origin.y.to_nearest_px() .x
as AzFloat); .to_nearest_px() as AzFloat,
positioned_kid.bounds
.origin
.y
.to_nearest_px() as AzFloat);
let new_tile_rect = let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds, self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid); &**positioned_kid);
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext, positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect, &new_tile_rect,
&new_transform, &new_transform,
current_clip_stack); Some(&positioned_kid.clip_rect))
} }
} }
// Step 4: Block backgrounds and borders. // Step 4: Block backgrounds and borders.
for display_item in display_list.block_backgrounds_and_borders.iter() { for display_item in display_list.block_backgrounds_and_borders.iter() {
display_item.draw_into_context(&mut paint_subcontext, display_item.draw_into_context(&mut paint_subcontext)
current_transform,
current_clip_stack)
} }
// Step 5: Floats. // Step 5: Floats.
for display_item in display_list.floats.iter() { for display_item in display_list.floats.iter() {
display_item.draw_into_context(&mut paint_subcontext, display_item.draw_into_context(&mut paint_subcontext)
current_transform,
current_clip_stack)
} }
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts. // TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
// Step 7: Content. // Step 7: Content.
for display_item in display_list.content.iter() { for display_item in display_list.content.iter() {
display_item.draw_into_context(&mut paint_subcontext, display_item.draw_into_context(&mut paint_subcontext)
current_transform,
current_clip_stack)
} }
// Steps 8 and 9: Positioned descendants with nonnegative z-indices. // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
@ -260,25 +264,38 @@ impl StackingContext {
if positioned_kid.layer.is_none() { if positioned_kid.layer.is_none() {
let new_transform = let new_transform =
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px() transform.translate(positioned_kid.bounds
as AzFloat, .origin
positioned_kid.bounds.origin.y.to_nearest_px() .x
as AzFloat); .to_nearest_px() as AzFloat,
positioned_kid.bounds
.origin
.y
.to_nearest_px() as AzFloat);
let new_tile_rect = let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds, self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid); &**positioned_kid);
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext, positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
&new_tile_rect, &new_tile_rect,
&new_transform, &new_transform,
current_clip_stack); Some(&positioned_kid.clip_rect))
} }
} }
// TODO(pcwalton): Step 10: Outlines. // TODO(pcwalton): Step 10: Outlines.
// Undo our clipping and transform.
if paint_subcontext.transient_clip_rect.is_some() {
paint_subcontext.draw_pop_clip();
paint_subcontext.transient_clip_rect = None
}
paint_subcontext.draw_target.set_transform(&old_transform);
if clip_rect.is_some() {
paint_subcontext.draw_pop_clip()
}
} }
paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target, paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target, self.opacity)
self.opacity)
} }
/// Translate the given tile rect into the coordinate system of a child stacking context. /// Translate the given tile rect into the coordinate system of a child stacking context.
@ -560,24 +577,17 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
} }
impl DisplayItem { impl DisplayItem {
/// Paints this display item into the given paint context. /// Paints this display item into the given painting context.
fn draw_into_context(&self, fn draw_into_context(&self, paint_context: &mut PaintContext) {
paint_context: &mut PaintContext, let this_clip_rect = self.base().clip_rect;
current_transform: &Matrix2D<AzFloat>, if paint_context.transient_clip_rect != Some(this_clip_rect) {
current_clip_stack: &mut Vec<Rect<Au>>) { if paint_context.transient_clip_rect.is_some() {
// TODO(pcwalton): This will need some tweaking to deal with more complex clipping regions.
let clip_rect = &self.base().clip_rect;
if current_clip_stack.len() == 0 || current_clip_stack.last().unwrap() != clip_rect {
while current_clip_stack.len() != 0 {
paint_context.draw_pop_clip(); paint_context.draw_pop_clip();
drop(current_clip_stack.pop());
} }
paint_context.draw_push_clip(clip_rect); paint_context.draw_push_clip(&this_clip_rect);
current_clip_stack.push(*clip_rect); paint_context.transient_clip_rect = Some(this_clip_rect)
} }
paint_context.draw_target.set_transform(current_transform);
match *self { match *self {
SolidColorDisplayItemClass(ref solid_color) => { SolidColorDisplayItemClass(ref solid_color) => {
paint_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) paint_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
@ -585,7 +595,7 @@ impl DisplayItem {
TextDisplayItemClass(ref text) => { TextDisplayItemClass(ref text) => {
debug!("Drawing text at {}.", text.base.bounds); debug!("Drawing text at {}.", text.base.bounds);
paint_context.draw_text(&**text, current_transform); paint_context.draw_text(&**text);
} }
ImageDisplayItemClass(ref image_item) => { ImageDisplayItemClass(ref image_item) => {

View file

@ -40,6 +40,10 @@ pub struct PaintContext<'a> {
pub page_rect: Rect<f32>, pub page_rect: Rect<f32>,
/// The rectangle that this context encompasses in screen coordinates (pixels). /// The rectangle that this context encompasses in screen coordinates (pixels).
pub screen_rect: Rect<uint>, pub screen_rect: Rect<uint>,
/// The current transient clipping rect, if any. A "transient clipping rect" is the clipping
/// rect used by the last display item. We cache the last value so that we avoid pushing and
/// popping clip rects unnecessarily.
pub transient_clip_rect: Option<Rect<Au>>,
} }
enum Direction { enum Direction {
@ -130,11 +134,11 @@ impl<'a> PaintContext<'a> {
size, size,
stride as i32, stride as i32,
source_format); source_format);
let source_rect = Rect(Point2D(0u as AzFloat, 0u as AzFloat), let source_rect = Rect(Point2D(0.0, 0.0),
Size2D(image.width as AzFloat, image.height as AzFloat)); Size2D(image.width as AzFloat, image.height as AzFloat));
let dest_rect = bounds.to_azure_rect(); let dest_rect = bounds.to_azure_rect();
let draw_surface_options = DrawSurfaceOptions::new(Linear, true); let draw_surface_options = DrawSurfaceOptions::new(Linear, true);
let draw_options = DrawOptions::new(1.0f64 as AzFloat, 0); let draw_options = DrawOptions::new(1.0, 0);
draw_target_ref.draw_surface(azure_surface, draw_target_ref.draw_surface(azure_surface,
dest_rect, dest_rect,
source_rect, source_rect,
@ -628,9 +632,9 @@ impl<'a> PaintContext<'a> {
self.draw_border_path(&original_bounds, direction, border, radius, scaled_color); self.draw_border_path(&original_bounds, direction, border, radius, scaled_color);
} }
pub fn draw_text(&mut self, pub fn draw_text(&mut self, text: &TextDisplayItem) {
text: &TextDisplayItem, let current_transform = self.draw_target.get_transform();
current_transform: &Matrix2D<AzFloat>) {
// Optimization: Dont set a transform matrix for upright text, and pass a start point to // Optimization: Dont set a transform matrix for upright text, and pass a start point to
// `draw_text_into_context`. // `draw_text_into_context`.
// //
@ -669,7 +673,7 @@ impl<'a> PaintContext<'a> {
// Undo the transform, only when we did one. // Undo the transform, only when we did one.
if text.orientation != Upright { if text.orientation != Upright {
self.draw_target.set_transform(current_transform) self.draw_target.set_transform(&current_transform)
} }
} }

View file

@ -509,6 +509,7 @@ impl WorkerThread {
font_ctx: &mut self.font_context, font_ctx: &mut self.font_context,
page_rect: tile.page_rect, page_rect: tile.page_rect,
screen_rect: tile.screen_rect, screen_rect: tile.screen_rect,
transient_clip_rect: None,
}; };
// Apply the translation to paint the tile we want. // Apply the translation to paint the tile we want.
@ -518,18 +519,15 @@ impl WorkerThread {
let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat,
-tile_bounds.origin.y as AzFloat); -tile_bounds.origin.y as AzFloat);
paint_context.draw_target.set_transform(&matrix);
// Clear the buffer. // Clear the buffer.
paint_context.clear(); paint_context.clear();
// Draw the display list. // Draw the display list.
profile(time::PaintingPerTileCategory, None, self.time_profiler_sender.clone(), || { profile(time::PaintingPerTileCategory, None, self.time_profiler_sender.clone(), || {
let mut clip_stack = Vec::new();
stacking_context.optimize_and_draw_into_context(&mut paint_context, stacking_context.optimize_and_draw_into_context(&mut paint_context,
&tile.page_rect, &tile.page_rect,
&matrix, &matrix,
&mut clip_stack); None);
paint_context.draw_target.flush(); paint_context.draw_target.flush();
}); });
} }

View file

@ -25,7 +25,7 @@ dependencies = [
[[package]] [[package]]
name = "azure" name = "azure"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#d323c3c7c248d3d5a2d46a6a5ee61c6e92aec0b0" source = "git+https://github.com/servo/rust-azure#452939480f5ef7640714f16c6a9e5d02e0e2d147"
dependencies = [ dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
@ -34,7 +34,7 @@ dependencies = [
"freetype 0.1.0 (git+https://github.com/servo/rust-freetype)", "freetype 0.1.0 (git+https://github.com/servo/rust-freetype)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"skia-sys 0.0.20130412 (git+https://github.com/servo/skia)", "skia-sys 0.0.20130412 (git+https://github.com/servo/skia?ref=upstream-2014-06-16)",
"xlib 0.1.0 (git+https://github.com/servo/rust-xlib)", "xlib 0.1.0 (git+https://github.com/servo/rust-xlib)",
] ]
@ -600,7 +600,7 @@ source = "git+https://github.com/rust-lang/semver#7dca047a9cd40e929a4545b37a1917
[[package]] [[package]]
name = "skia-sys" name = "skia-sys"
version = "0.0.20130412" version = "0.0.20130412"
source = "git+https://github.com/servo/skia#79aa9354837bc195b83fa041b9632ea628e6f7d0" source = "git+https://github.com/servo/skia?ref=upstream-2014-06-16#c3dd8cacbddbfc20b0dae9b456ac1545b0402cff"
dependencies = [ dependencies = [
"expat-sys 2.1.0 (git+https://github.com/servo/libexpat)", "expat-sys 2.1.0 (git+https://github.com/servo/libexpat)",
"freetype-sys 2.4.11 (git+https://github.com/servo/libfreetype2)", "freetype-sys 2.4.11 (git+https://github.com/servo/libfreetype2)",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Before After
Before After

View file

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>You see here a scroll labeled JUYED AWK YACC.</title>
<style> <style>
* { * {
line-height: 14px; line-height: 14px;
@ -12,8 +11,8 @@
</style> </style>
</head> </head>
<body> <body>
<div id=a>Here lies the body of Jonathan Blake.</div> <div id=a>X</div>
<div id=b>Stepped on the gas instead of the brake.</div> <div id=b>X</div>
</body> </body>
</html> </html>

View file

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>You see here a scroll labeled JUYED AWK YACC.</title>
<style> <style>
* { * {
line-height: 14px; line-height: 14px;
@ -18,8 +17,8 @@
</style> </style>
</head> </head>
<body> <body>
<div id=a>Here lies the body of Jonathan Blake. <div id=a>X
<div id=b>Stepped on the gas instead of the brake.</div></div> <div id=b>X</div></div>
</body> </body>
</html> </html>