Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Unified Diff: ui/gfx/path_mac_unittest.mm

Issue 1633403002: MacViews: Add native drop shadow to dialogs on OSX < 10.10. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Optimise drawRect by storing window mask in BridgedContentView. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/path_mac_unittest.mm
diff --git a/ui/gfx/path_mac_unittest.mm b/ui/gfx/path_mac_unittest.mm
new file mode 100644
index 0000000000000000000000000000000000000000..e2566ad1439d55c067c345c9e1daf247a09d27a2
--- /dev/null
+++ b/ui/gfx/path_mac_unittest.mm
@@ -0,0 +1,305 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ui/gfx/path_mac.h"
+
+#include <cmath>
+#include <vector>
+
+#import <Cocoa/Cocoa.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/path.h"
+
+namespace {
+
+// Returns the point at a distance of |radius| from the point (|centre_x|,
+// |centre_y|), and angle |degrees| from the positive horizontal axis, measured
+// anti-clockwise.
+NSPoint GetRadialPoint(const double radius,
+ const double degrees,
+ const double centre_x,
+ const double centre_y) {
+ const double radian = (degrees * SK_ScalarPI) / 180;
+ return NSMakePoint(centre_x + radius * std::cos(radian),
+ centre_y + radius * std::sin(radian));
+}
+
+// Returns the area of a circle with the given |radius|.
+double CalculateCircleArea(const double radius) {
+ return SK_ScalarPI * radius * radius;
+}
+
+// Returns the area of a simple polygon. |path| should represent a simple
+// polygon.
+double CalculatePolygonArea(NSBezierPath* const path) {
+ // If path represents a single polygon, it will have MoveTo, followed by
+ // multiple LineTo, followed By ClosePath, followed by another MoveTo
+ // NSBezierPathElement.
+ const size_t element_count = [path elementCount];
+ size_t index = 0;
+ NSPoint points[3];
+ std::vector<NSPoint> poly;
+ while (index < element_count) {
+ NSBezierPathElement element =
+ [path elementAtIndex:index++ associatedPoints:points];
+ poly.push_back(points[0]);
+ if (element == NSClosePathBezierPathElement) {
+ DCHECK_EQ(index, element_count - 1);
+ element = [path elementAtIndex:index++ associatedPoints:points];
+ DCHECK_EQ(element, NSMoveToBezierPathElement);
+ break;
+ }
+ DCHECK_EQ(element, index > 1 ? NSLineToBezierPathElement
+ : NSMoveToBezierPathElement);
+ }
+
+ // Shoelace Algorithm to find the area of a simple polygon.
+ const size_t count = poly.size();
+ DCHECK(NSEqualPoints(poly[0], poly[count - 1]));
+ double area = 0;
+ for (size_t i = 0; i < count - 1; i++)
+ area += poly[i].x * poly[i + 1].y - poly[i].y * poly[i + 1].x;
+
+ return std::fabs(area) / 2.0;
+}
+
+// Returns the area of a rounded rectangle with the given |width|, |height| and
+// |radius|.
+double CalculateRoundedRectangleArea(const double width,
+ const double height,
+ const double radius) {
+ const double inside_width = width - 2 * radius;
+ const double inside_height = height - 2 * radius;
+ return inside_width * inside_height +
+ 2 * radius * (inside_width + inside_height) +
+ CalculateCircleArea(radius);
+}
+
+// Returns the bounding box of |path| as a gfx::Rect.
+gfx::Rect GetBoundingBox(NSBezierPath* const path) {
+ const NSRect bounds = [path bounds];
+ return gfx::ToNearestRect(gfx::RectF(bounds.origin.x, bounds.origin.y,
+ bounds.size.width, bounds.size.height));
+}
+
+} // namespace
+
+namespace gfx {
+
+// Check that empty NSBezierPath is returned for empty SkRegion.
+TEST(CreateNSBezierPathFromSkRegionTest, EmptyRegion) {
+ NSBezierPath* const result = CreateNSBezierPathFromSkRegion(SkRegion());
+ EXPECT_TRUE([result isEmpty]);
+}
+
+// Check that a region containing multiple rectangles is correctly converted to
+// a NSBezierPath.
+TEST(CreateNSBezierPathFromSkRegionTest, TwoRectanglesRegion) {
+ const SkIRect rects[] = {
+ {0, 0, 50, 50}, {100, 100, 150, 150},
+ };
+ const NSPoint inside_points[] = {
+ {1, 1}, {1, 49}, {49, 49}, {49, 1}, {101, 101},
+ {101, 149}, {149, 149}, {149, 101}, {25, 25}, {125, 125}};
+ const NSPoint outside_points[] = {{-1, -1}, {-1, 51}, {51, 51}, {51, -1},
+ {99, 99}, {99, 151}, {151, 151}, {151, 99},
+ {75, 75}, {-5, -5}};
+ const size_t kNumPoints = 10;
+ const gfx::Rect expected_bounds(0, 0, 150, 150); // Bounding box of rects.
+ const size_t kSizeRectArray = 2;
+
+ SkRegion region;
+ region.setRects(rects, kSizeRectArray);
+ NSBezierPath* result = CreateNSBezierPathFromSkRegion(region);
+
+ // Check points near the boundary of the path and verify that they are
+ // reported correctly as being inside/outside the path.
+ for (size_t i = 0; i < kNumPoints; i++) {
+ EXPECT_TRUE([result containsPoint:inside_points[i]]);
+ EXPECT_FALSE([result containsPoint:outside_points[i]]);
+ }
+
+ // Verify that the bounding box of |result| is correct.
+ EXPECT_EQ(expected_bounds, GetBoundingBox(result));
+}
+
+// Check that empty NSBezierPath is returned for empty SkPath.
+TEST(CreateNSBezierPathFromSkPathTest, EmptyPath) {
+ NSBezierPath* const result = CreateNSBezierPathFromSkPath(SkPath());
+ EXPECT_TRUE([result isEmpty]);
+}
+
+// Check that the returned NSBezierPath has the correct winding rule.
+TEST(CreateNSBezierPathFromSkPathTest, FillType) {
+ SkPath path;
+ path.setFillType(SkPath::kWinding_FillType);
+ NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
+ EXPECT_EQ(NSNonZeroWindingRule, [result windingRule]);
+
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ result = CreateNSBezierPathFromSkPath(path);
+ EXPECT_EQ(NSEvenOddWindingRule, [result windingRule]);
+
+ // For inverse fill types, the returned NSBezierPath should have the default
+ // winding rule set.
+ path.setFillType(SkPath::kInverseWinding_FillType);
+ result = CreateNSBezierPathFromSkPath(path);
+ EXPECT_EQ([NSBezierPath defaultWindingRule], [result windingRule]);
+
+ path.setFillType(SkPath::kInverseEvenOdd_FillType);
+ result = CreateNSBezierPathFromSkPath(path);
+ EXPECT_EQ([NSBezierPath defaultWindingRule], [result windingRule]);
+}
+
+// Check that a path containing multiple subpaths, in this case two rectangles,
+// is correctly converted to a NSBezierPath.
+TEST(CreateNSBezierPathFromSkPathTest, TwoRectanglesPath) {
+ const SkRect rects[] = {
+ {0, 0, 50, 50}, {100, 100, 150, 150},
+ };
+ const NSPoint inside_points[] = {
+ {1, 1}, {1, 49}, {49, 49}, {49, 1}, {101, 101},
+ {101, 149}, {149, 149}, {149, 101}, {25, 25}, {125, 125}};
+ const NSPoint outside_points[] = {{-1, -1}, {-1, 51}, {51, 51}, {51, -1},
+ {99, 99}, {99, 151}, {151, 151}, {151, 99},
+ {75, 75}, {-5, -5}};
+ const size_t kNumPoints = 10;
+ const gfx::Rect expected_bounds(0, 0, 150, 150);
+
+ SkPath path;
+ path.addRect(rects[0]);
+ path.addRect(rects[1]);
+ NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
+
+ // Check points near the boundary of the path and verify that they are
+ // reported correctly as being inside/outside the path.
+ for (size_t i = 0; i < kNumPoints; i++) {
+ EXPECT_TRUE([result containsPoint:inside_points[i]]);
+ EXPECT_FALSE([result containsPoint:outside_points[i]]);
+ }
+
+ // Verify that the bounding box of |result| is correct.
+ EXPECT_EQ(expected_bounds, GetBoundingBox(result));
+}
+
+// Test that an SKPath containing a circle is converted correctly to a
+// NSBezierPath.
+TEST(CreateNSBezierPathFromSkPathTest, CirclePath) {
+ const int kRadius = 5;
+ const int kCentreX = 10;
+ const int kCentreY = 15;
+ const double kCushion = 0.1;
+ // Expected bounding box of the circle.
+ const gfx::Rect expected_bounds(kCentreX - kRadius, kCentreY - kRadius,
+ 2 * kRadius, 2 * kRadius);
+
+ SkPath path;
+ path.addCircle(SkIntToScalar(kCentreX), SkIntToScalar(kCentreY),
+ SkIntToScalar(kRadius));
+ NSBezierPath* const result = CreateNSBezierPathFromSkPath(path);
+
+ // Check points near the boundary of the circle and verify that they are
+ // reported correctly as being inside/outside the path.
+ for (size_t deg = 0; deg < 360; deg++) {
+ NSPoint inside_point =
+ GetRadialPoint(kRadius - kCushion, deg, kCentreX, kCentreY);
+ NSPoint outside_point =
+ GetRadialPoint(kRadius + kCushion, deg, kCentreX, kCentreY);
+ EXPECT_TRUE([result containsPoint:inside_point]);
+ EXPECT_FALSE([result containsPoint:outside_point]);
+ }
+
+ // Check that the returned result has the correct bounding box.
+ EXPECT_EQ(expected_bounds, GetBoundingBox(result));
+
+ // Check area of converted path is correct upto a certain tolerance value. To
+ // find the area of the NSBezierPath returned, flatten it i.e. convert it to a
+ // polygon.
+ [NSBezierPath setDefaultFlatness:0.01];
+ NSBezierPath* const polygon = [result bezierPathByFlatteningPath];
+ const double kTolerance = 0.5;
+ EXPECT_NEAR(CalculateCircleArea(kRadius), CalculatePolygonArea(polygon),
+ kTolerance);
+}
+
+// Test that an SKPath containing a rounded rectangle is converted correctly to
+// a NSBezierPath.
+TEST(CreateNSBezierPathFromSkPathTest, RoundedRectanglePath) {
+ const int kRectangleWidth = 50;
+ const int kRectangleHeight = 100;
+ const int kCornerRadius = 5;
+ const double kCushion = 0.1;
+ // Expected bounding box of the rounded rectangle.
+ const gfx::Rect expected_bounds(kRectangleWidth, kRectangleHeight);
+
+ SkRRect rrect;
+ rrect.setRectXY(SkRect::MakeWH(kRectangleWidth, kRectangleHeight),
+ kCornerRadius, kCornerRadius);
+
+ const NSPoint inside_points[] = {
+ // Bottom left corner.
+ {kCornerRadius, kCornerRadius},
+ // Bottom right corner.
+ {kRectangleWidth - kCornerRadius, kCornerRadius},
+ // Top Right corner.
+ {kRectangleWidth - kCornerRadius, kRectangleHeight - kCornerRadius},
+ // Top left corner.
+ {kCornerRadius, kRectangleHeight - kCornerRadius},
+ // Bottom middle.
+ {kRectangleWidth / 2.0, kCushion},
+ // Right middle.
+ {kRectangleWidth - kCushion, kRectangleHeight / 2.0},
+ // Top middle.
+ {kRectangleWidth / 2.0, kRectangleHeight - kCushion},
+ // Left middle.
+ {kCushion, kRectangleHeight / 2.0}};
+ const NSPoint outside_points[] = {
+ // Bottom left corner.
+ {0, 0},
+ // Bottom right corner.
+ {kRectangleWidth, 0},
+ // Top right corner.
+ {kRectangleWidth, kRectangleHeight},
+ // Top left corner.
+ {0, kRectangleHeight},
+ // Bottom middle.
+ {kRectangleWidth / 2.0, -kCushion},
+ // Right middle.
+ {kRectangleWidth + kCushion, kRectangleHeight / 2.0},
+ // Top middle.
+ {kRectangleWidth / 2.0, kRectangleHeight + kCushion},
+ // Left middle.
+ {-kCushion, kRectangleHeight / 2.0}};
+ const size_t kNumPoints = 8;
+
+ SkPath path;
+ path.addRRect(rrect);
+ NSBezierPath* const result = CreateNSBezierPathFromSkPath(path);
+
+ // Check points near the boundary of the path and verify that they are
+ // reported correctly as being inside/outside the path.
+ for (size_t i = 0; i < kNumPoints; i++) {
+ EXPECT_TRUE([result containsPoint:inside_points[i]]);
+ EXPECT_FALSE([result containsPoint:outside_points[i]]);
+ }
+
+ // Check that the returned result has the correct bounding box.
+ EXPECT_EQ(expected_bounds, GetBoundingBox(result));
+
+ // Check area of converted path is correct upto a certain tolerance value. To
+ // find the area of the NSBezierPath returned, flatten it i.e. convert it to a
+ // polygon.
+ [NSBezierPath setDefaultFlatness:0.01];
+ NSBezierPath* const polygon = [result bezierPathByFlatteningPath];
+ const double kTolerance = 0.5;
+ EXPECT_NEAR(CalculateRoundedRectangleArea(kRectangleWidth, kRectangleHeight,
+ kCornerRadius),
+ CalculatePolygonArea(polygon), kTolerance);
+}
+
+} // namespace gfx

Powered by Google App Engine
This is Rietveld 408576698