mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
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:
parent
368d6dc6bf
commit
1d845ee4f2
7 changed files with 71 additions and 61 deletions
|
@ -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) => {
|
||||||
|
|
|
@ -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: Don’t set a transform matrix for upright text, and pass a start point to
|
// Optimization: Don’t 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(¤t_transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
6
components/servo/Cargo.lock
generated
6
components/servo/Cargo.lock
generated
|
@ -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 |
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue