servo: Merge #12543 - Accumulate subpixels through stacking contexts (from mrobinson:off-by-one-ng); r=pcwalton

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Instead of simply rounding layer origins and discarding subpixel
offsets, accumulate them by transforming them into the space of the
next child stacking context. This is an attempt to eliminate subpixel
differences that are caused by different stacking context boundaries in
reference tests.

Currently these accumulated subpixels are only used for text
positioning, but the plan is that they can be used for all drawing in
the future.

Source-Repo: https://github.com/servo/servo
Source-Revision: 4077ae7d04e64d66e2dfd1577dc4d337c45fecd4
This commit is contained in:
Martin Robinson 2016-07-27 05:17:22 -05:00
Родитель 325364ad0a
Коммит 849f2102d3
3 изменённых файлов: 56 добавлений и 22 удалений

Просмотреть файл

@ -382,7 +382,11 @@ impl DisplayList {
current_item_index: start, current_item_index: start,
last_item_index: end, last_item_index: end,
}; };
self.draw_stacking_context(stacking_context, &mut traversal, paint_context, transform); self.draw_stacking_context(stacking_context,
&mut traversal,
paint_context,
transform,
&Point2D::zero());
} }
fn draw_stacking_context_contents<'a>(&'a self, fn draw_stacking_context_contents<'a>(&'a self,
@ -390,6 +394,7 @@ impl DisplayList {
traversal: &mut DisplayListTraversal<'a>, traversal: &mut DisplayListTraversal<'a>,
paint_context: &mut PaintContext, paint_context: &mut PaintContext,
transform: &Matrix4D<f32>, transform: &Matrix4D<f32>,
subpixel_offset: &Point2D<Au>,
tile_rect: Option<Rect<Au>>) { tile_rect: Option<Rect<Au>>) {
for child in stacking_context.children.iter() { for child in stacking_context.children.iter() {
while let Some(item) = traversal.advance(stacking_context) { while let Some(item) = traversal.advance(stacking_context) {
@ -399,7 +404,11 @@ impl DisplayList {
} }
if child.intersects_rect_in_parent_context(tile_rect) { if child.intersects_rect_in_parent_context(tile_rect) {
self.draw_stacking_context(child, traversal, paint_context, &transform); self.draw_stacking_context(child,
traversal,
paint_context,
&transform,
subpixel_offset);
} else { } else {
traversal.skip_past_stacking_context(child); traversal.skip_past_stacking_context(child);
} }
@ -417,12 +426,14 @@ impl DisplayList {
stacking_context: &StackingContext, stacking_context: &StackingContext,
traversal: &mut DisplayListTraversal<'a>, traversal: &mut DisplayListTraversal<'a>,
paint_context: &mut PaintContext, paint_context: &mut PaintContext,
transform: &Matrix4D<f32>) { transform: &Matrix4D<f32>,
subpixel_offset: &Point2D<Au>) {
if stacking_context.context_type != StackingContextType::Real { if stacking_context.context_type != StackingContextType::Real {
self.draw_stacking_context_contents(stacking_context, self.draw_stacking_context_contents(stacking_context,
traversal, traversal,
paint_context, paint_context,
transform, transform,
subpixel_offset,
None); None);
return; return;
} }
@ -431,18 +442,35 @@ impl DisplayList {
&stacking_context.filters, &stacking_context.filters,
stacking_context.blend_mode); stacking_context.blend_mode);
// If a layer is being used, the transform for this layer
// will be handled by the compositor.
let old_transform = paint_context.draw_target.get_transform(); let old_transform = paint_context.draw_target.get_transform();
let transform = match stacking_context.layer_info { let pixels_per_px = paint_context.screen_pixels_per_px();
Some(..) => *transform, let (transform, subpixel_offset) = match stacking_context.layer_info {
// If this stacking context starts a layer, the offset and transformation are handled
// by layer position within the compositor.
Some(..) => (*transform, *subpixel_offset),
None => { None => {
let pixels_per_px = paint_context.screen_pixels_per_px(); let origin = stacking_context.bounds.origin + *subpixel_offset;
let origin = &stacking_context.bounds.origin; let pixel_snapped_origin =
transform.translate( Point2D::new(origin.x.to_nearest_pixel(pixels_per_px.get()),
origin.x.to_nearest_pixel(pixels_per_px.get()) as AzFloat, origin.y.to_nearest_pixel(pixels_per_px.get()));
origin.y.to_nearest_pixel(pixels_per_px.get()) as AzFloat,
0.0).mul(&stacking_context.transform) let transform = transform.translate(pixel_snapped_origin.x as AzFloat,
pixel_snapped_origin.y as AzFloat,
0.0).mul(&stacking_context.transform);
let inverse_transform = transform.invert();
// Here we are trying to accumulate any subpixel distances across transformed
// stacking contexts. This allows us transform stacking context with a
// pixel-snapped transform, but continue to propagate any subpixels from stacking
// context origins to children.
let subpixel_offset = Point2D::new(origin.x.to_f32_px() - pixel_snapped_origin.x,
origin.y.to_f32_px() - pixel_snapped_origin.y);
let subpixel_offset = inverse_transform.transform_point(&subpixel_offset) -
inverse_transform.transform_point(&Point2D::zero());;
let subpixel_offset = Point2D::new(Au::from_f32_px(subpixel_offset.x),
Au::from_f32_px(subpixel_offset.y));
(transform, subpixel_offset)
} }
}; };
@ -455,6 +483,7 @@ impl DisplayList {
clip_rect: Some(stacking_context.overflow), clip_rect: Some(stacking_context.overflow),
transient_clip: None, transient_clip: None,
layer_kind: paint_context.layer_kind, layer_kind: paint_context.layer_kind,
subpixel_offset: subpixel_offset,
}; };
// Set up our clip rect and transform. // Set up our clip rect and transform.
@ -469,6 +498,7 @@ impl DisplayList {
traversal, traversal,
&mut paint_subcontext, &mut paint_subcontext,
&transform, &transform,
&subpixel_offset,
Some(transformed_tile_rect(paint_context.screen_rect, &transform))); Some(transformed_tile_rect(paint_context.screen_rect, &transform)));
paint_subcontext.remove_transient_clip_if_applicable(); paint_subcontext.remove_transient_clip_if_applicable();

Просмотреть файл

@ -53,6 +53,10 @@ pub struct PaintContext<'a> {
pub transient_clip: Option<ClippingRegion>, pub transient_clip: Option<ClippingRegion>,
/// A temporary hack to disable clipping optimizations on 3d layers. /// A temporary hack to disable clipping optimizations on 3d layers.
pub layer_kind: LayerKind, pub layer_kind: LayerKind,
/// The current subpixel offset, used to make pixel snapping aware of accumulated subpixels
/// from the StackingContext.
/// TODO: Eventually this should be added to all points handled by the PaintContext.
pub subpixel_offset: Point2D<Au>,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -1338,24 +1342,26 @@ impl<'a> PaintContext<'a> {
pub fn draw_text(&mut self, text: &TextDisplayItem) { pub fn draw_text(&mut self, text: &TextDisplayItem) {
let draw_target_transform = self.draw_target.get_transform(); let draw_target_transform = self.draw_target.get_transform();
let origin = text.baseline_origin + self.subpixel_offset;
// 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`.
// //
// For sideways text, its easier to do the rotation such that its center (the baselines // For sideways text, its easier to do the rotation such that its center (the baselines
// start point) is at (0, 0) coordinates. // start point) is at (0, 0) coordinates.
let baseline_origin = match text.orientation { let baseline_origin = match text.orientation {
Upright => text.baseline_origin, Upright => origin,
SidewaysLeft => { SidewaysLeft => {
let x = text.baseline_origin.x.to_f32_px(); let x = origin.x.to_f32_px();
let y = text.baseline_origin.y.to_f32_px(); let y = origin.y.to_f32_px();
self.draw_target.set_transform(&draw_target_transform.mul(&Matrix2D::new(0., -1., self.draw_target.set_transform(&draw_target_transform.mul(&Matrix2D::new(0., -1.,
1., 0., 1., 0.,
x, y))); x, y)));
Point2D::zero() Point2D::zero()
} }
SidewaysRight => { SidewaysRight => {
let x = text.baseline_origin.x.to_f32_px(); let x = origin.x.to_f32_px();
let y = text.baseline_origin.y.to_f32_px(); let y = origin.y.to_f32_px();
self.draw_target.set_transform(&draw_target_transform.mul(&Matrix2D::new(0., 1., self.draw_target.set_transform(&draw_target_transform.mul(&Matrix2D::new(0., 1.,
-1., 0., -1., 0.,
x, y))); x, y)));
@ -1382,10 +1388,7 @@ impl<'a> PaintContext<'a> {
// Blur, if necessary. // Blur, if necessary.
self.blur_if_necessary(temporary_draw_target, text.blur_radius); self.blur_if_necessary(temporary_draw_target, text.blur_radius);
// Undo the transform, only when we did one. self.draw_target.set_transform(&draw_target_transform)
if text.orientation != Upright {
self.draw_target.set_transform(&draw_target_transform)
}
} }
/// Draws a linear gradient in the given boundaries from the given start point to the given end /// Draws a linear gradient in the given boundaries from the given start point to the given end

Просмотреть файл

@ -690,6 +690,7 @@ impl WorkerThread {
clip_rect: None, clip_rect: None,
transient_clip: None, transient_clip: None,
layer_kind: layer_kind, layer_kind: layer_kind,
subpixel_offset: Point2D::zero(),
}; };
// Apply the translation to paint the tile we want. // Apply the translation to paint the tile we want.