Commit e09c592f authored by oshima@chromium.org's avatar oshima@chromium.org

Snap layers in views to physical pixel

minor changes
- print offset/mask info when printing layer hierarchy.
- copy the offset when cloning layer.

BUG=391822
TEST=Added new tests in layer_unittests and views_unittests

Review URL: https://codereview.chromium.org/375693006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283672 0039d316-1c4b-4281-b951-d872f2087c98
parent 0909a448
......@@ -67,6 +67,17 @@ void PrintLayerHierarchyImp(const Layer* layer,
*out << L'\n' << UTF8ToWide(property_indent_str);
*out << L"bounds: " << layer->bounds().x() << L',' << layer->bounds().y();
*out << L' ' << layer->bounds().width() << L'x' << layer->bounds().height();
if (!layer->subpixel_position_offset().IsZero())
*out << " " << UTF8ToWide(layer->subpixel_position_offset().ToString());
const ui::Layer* mask = const_cast<ui::Layer*>(layer)->layer_mask_layer();
if (mask) {
*out << L'\n' << UTF8ToWide(property_indent_str);
*out << L"mask layer: " << std::setprecision(2)
<< UTF8ToWide(mask->bounds().ToString())
<< UTF8ToWide(mask->subpixel_position_offset().ToString());
}
if (layer->opacity() != 1.0f) {
*out << L'\n' << UTF8ToWide(property_indent_str);
......
......@@ -5,6 +5,8 @@
#include "ui/compositor/dip_util.h"
#include "base/command_line.h"
#include "cc/base/math_util.h"
#include "cc/layers/layer.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_switches.h"
#include "ui/compositor/layer.h"
......@@ -70,4 +72,45 @@ gfx::Rect ConvertRectToPixel(const Layer* layer,
gfx::ScaleSize(rect_in_dip.size(), scale)));
}
#if !defined(NDEBUG)
namespace {
void CheckSnapped(float snapped_position) {
const float kEplison = 0.0001f;
float diff = std::abs(snapped_position - static_cast<int>(snapped_position));
DCHECK_LT(diff, kEplison);
}
} // namespace
#endif
void SnapLayerToPhysicalPixelBoundary(ui::Layer* snapped_layer,
ui::Layer* layer_to_snap) {
DCHECK_NE(snapped_layer, layer_to_snap);
DCHECK(snapped_layer);
DCHECK(snapped_layer->Contains(layer_to_snap));
gfx::Point view_offset_dips = layer_to_snap->GetTargetBounds().origin();
ui::Layer::ConvertPointToLayer(
layer_to_snap->parent(), snapped_layer, &view_offset_dips);
gfx::PointF view_offset = view_offset_dips;
float scale_factor = GetDeviceScaleFactor(layer_to_snap);
view_offset.Scale(scale_factor);
gfx::PointF view_offset_snapped(cc::MathUtil::Round(view_offset.x()),
cc::MathUtil::Round(view_offset.y()));
gfx::Vector2dF fudge = view_offset_snapped - view_offset;
fudge.Scale(1.0 / scale_factor);
layer_to_snap->SetSubpixelPositionOffset(fudge);
#if !defined(NDEBUG)
gfx::Point p;
Layer::ConvertPointToLayer(layer_to_snap->parent(), snapped_layer, &p);
cc::Layer* cc_layer = layer_to_snap->cc_layer();
gfx::PointF origin = cc_layer->position();
CheckSnapped((p.x() + origin.x()) * scale_factor);
CheckSnapped((p.y() + origin.y()) * scale_factor);
#endif
}
} // namespace ui
......@@ -44,6 +44,13 @@ COMPOSITOR_EXPORT gfx::Rect ConvertRectToPixel(
const Layer* layer,
const gfx::Rect& rect_in_dip);
// Snaps the |layer_to_snap| to the physical pixel boundary.
// |snapped_layer| is a reference layer that should also be
// snapped at the pysical pixel boundary.
COMPOSITOR_EXPORT void SnapLayerToPhysicalPixelBoundary(
ui::Layer* snapped_layer,
ui::Layer* layer_to_snap);
} // namespace ui
#endif // UI_COMPOSITOR_DIP_UTIL_H_
......@@ -143,6 +143,9 @@ class COMPOSITOR_EXPORT Layer
// The offset from our parent (stored in bounds.origin()) is an integer but we
// may need to be at a fractional pixel offset to align properly on screen.
void SetSubpixelPositionOffset(const gfx::Vector2dF offset);
const gfx::Vector2dF& subpixel_position_offset() const {
return subpixel_position_offset_;
}
// Return the target bounds if animator is running, or the current bounds
// otherwise.
......
......@@ -45,6 +45,7 @@ scoped_ptr<Layer> LayerOwner::RecreateLayer() {
new_layer->set_name(old_layer->name());
new_layer->SetFillsBoundsOpaquely(old_layer->fills_bounds_opaquely());
new_layer->SetFillsBoundsCompletely(old_layer->FillsBoundsCompletely());
new_layer->SetSubpixelPositionOffset(old_layer->subpixel_position_offset());
// Install new layer as a sibling of the old layer, stacked below it.
if (old_layer->parent()) {
......
......@@ -23,6 +23,7 @@
#include "cc/test/pixel_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/compositor_observer.h"
#include "ui/compositor/dip_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
......@@ -1554,4 +1555,44 @@ TEST_F(LayerWithDelegateTest, DestroyingLayerRemovesTheAnimatorFromCollection) {
EXPECT_FALSE(compositor()->layer_animator_collection()->HasActiveAnimators());
}
namespace {
std::string Vector2dFTo100thPercisionString(const gfx::Vector2dF& vector) {
return base::StringPrintf("%.2f %0.2f", vector.x(), vector.y());
}
} // namespace
TEST_F(LayerWithRealCompositorTest, SnapLayerToPixels) {
scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED));
scoped_ptr<Layer> c1(CreateLayer(LAYER_TEXTURED));
scoped_ptr<Layer> c11(CreateLayer(LAYER_TEXTURED));
GetCompositor()->SetScaleAndSize(1.25f, gfx::Size(100, 100));
GetCompositor()->SetRootLayer(root.get());
root->Add(c1.get());
c1->Add(c11.get());
root->SetBounds(gfx::Rect(0, 0, 100, 100));
c1->SetBounds(gfx::Rect(1, 1, 10, 10));
c11->SetBounds(gfx::Rect(1, 1, 10, 10));
SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
// 0.5 at 1.25 scale : (1 - 0.25 + 0.25) / 1.25 = 0.4
EXPECT_EQ("0.40 0.40",
Vector2dFTo100thPercisionString(c11->subpixel_position_offset()));
GetCompositor()->SetScaleAndSize(1.5f, gfx::Size(100, 100));
SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
// c11 must already be aligned at 1.5 scale.
EXPECT_EQ("0.00 0.00",
Vector2dFTo100thPercisionString(c11->subpixel_position_offset()));
c11->SetBounds(gfx::Rect(2, 2, 10, 10));
SnapLayerToPhysicalPixelBoundary(root.get(), c11.get());
// c11 is now off the pixel.
// 0.5 / 1.5 = 0.333...
EXPECT_EQ("0.33 0.33",
Vector2dFTo100thPercisionString(c11->subpixel_position_offset()));
}
} // namespace ui
......@@ -20,6 +20,7 @@
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/dip_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/events/event_target_iterator.h"
......@@ -112,6 +113,7 @@ View::View()
root_bounds_dirty_(true),
clip_insets_(0, 0, 0, 0),
needs_layout_(true),
snap_layer_to_pixel_boundary_(false),
flip_canvas_on_paint_for_rtl_ui_(false),
paint_to_layer_(false),
accelerator_focus_manager_(NULL),
......@@ -572,6 +574,19 @@ void View::SetLayoutManager(LayoutManager* layout_manager) {
layout_manager_->Installed(this);
}
void View::SnapLayerToPixelBoundary() {
if (!layer())
return;
if (snap_layer_to_pixel_boundary_ && layer()->parent() &&
layer()->GetCompositor()) {
ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer());
} else {
// Reset the offset.
layer()->SetSubpixelPositionOffset(gfx::Vector2dF());
}
}
// Attributes ------------------------------------------------------------------
const char* View::GetClassName() const {
......@@ -1518,6 +1533,9 @@ void View::OnPaintLayer(gfx::Canvas* canvas) {
}
void View::OnDeviceScaleFactorChanged(float device_scale_factor) {
snap_layer_to_pixel_boundary_ =
(device_scale_factor - std::floor(device_scale_factor)) != 0.0f;
SnapLayerToPixelBoundary();
// Repainting with new scale factor will paint the content at the right scale.
}
......@@ -2038,6 +2056,7 @@ void View::RemoveDescendantToNotify(View* view) {
void View::SetLayerBounds(const gfx::Rect& bounds) {
layer()->SetBounds(bounds);
SnapLayerToPixelBoundary();
}
void View::SetRootBoundsDirty(bool origin_changed) {
......
......@@ -380,6 +380,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
LayoutManager* GetLayoutManager() const;
void SetLayoutManager(LayoutManager* layout);
// Adjust the layer's offset so that it snaps to the physical pixel boundary.
// This has no effect if the view does not have an associated layer.
void SnapLayerToPixelBoundary();
// Attributes ----------------------------------------------------------------
// The view class name.
......@@ -1524,6 +1528,9 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Views. The default is absolute positioning according to bounds_.
scoped_ptr<LayoutManager> layout_manager_;
// Whether this View's layer should be snapped to the pixel boundary.
bool snap_layer_to_pixel_boundary_;
// Painting ------------------------------------------------------------------
// Background
......
......@@ -7,6 +7,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "grit/ui_strings.h"
#include "ui/base/accelerators/accelerator.h"
......@@ -3794,6 +3795,54 @@ TEST_F(ViewLayerTest, BoundsTreeMoveViewMovesBounds) {
EXPECT_EQ(1U, widget_view->last_cull_set_.count(v3));
}
namespace {
std::string ToString(const gfx::Vector2dF& vector) {
return base::StringPrintf("%.2f %0.2f", vector.x(), vector.y());
}
} // namespace
TEST_F(ViewLayerTest, SnapLayerToPixel) {
View* v1 = new View;
View* v11 = new View;
v1->AddChildView(v11);
widget()->SetContentsView(v1);
const gfx::Size& size = GetRootLayer()->GetCompositor()->size();
GetRootLayer()->GetCompositor()->SetScaleAndSize(1.25f, size);
v11->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
v1->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
v11->SetPaintToLayer(true);
EXPECT_EQ("0.40 0.40", ToString(v11->layer()->subpixel_position_offset()));
// Creating a layer in parent should update the child view's layer offset.
v1->SetPaintToLayer(true);
EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->subpixel_position_offset()));
EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->subpixel_position_offset()));
// DSF change should get propagated and update offsets.
GetRootLayer()->GetCompositor()->SetScaleAndSize(1.5f, size);
EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset()));
EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
// Deleting parent's layer should update the child view's layer's offset.
v1->SetPaintToLayer(false);
EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
// Setting parent view should update the child view's layer's offset.
v1->SetBoundsRect(gfx::Rect(2, 2, 10, 10));
EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
// Setting integral DSF should reset the offset.
GetRootLayer()->GetCompositor()->SetScaleAndSize(2.0f, size);
EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
}
TEST_F(ViewTest, FocusableAssertions) {
// View subclasses may change insets based on whether they are focusable,
// which effects the preferred size. To avoid preferred size changing around
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment