| 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..d1d4a99234e38574b21376612bb89fb843b8a890
|
| --- /dev/null
|
| +++ b/ui/gfx/path_mac_unittest.mm
|
| @@ -0,0 +1,258 @@
|
| +// 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 gfx {
|
| +
|
| +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(double radius,
|
| + double degrees,
|
| + double centre_x,
|
| + 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(double radius) {
|
| + return SK_ScalarPI * radius * radius;
|
| +}
|
| +
|
| +// Returns the area of a simple polygon. |path| should represent a simple
|
| +// polygon.
|
| +double CalculatePolygonArea(NSBezierPath* 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];
|
| + NSPoint points[3];
|
| + std::vector<NSPoint> poly;
|
| +
|
| + for (size_t i = 0; i < element_count - 1; i++) {
|
| + NSBezierPathElement element =
|
| + [path elementAtIndex:i associatedPoints:points];
|
| + poly.push_back(points[0]);
|
| + DCHECK_EQ(element,
|
| + i ? (i == element_count - 2 ? NSClosePathBezierPathElement
|
| + : NSLineToBezierPathElement)
|
| + : NSMoveToBezierPathElement);
|
| + }
|
| + DCHECK_EQ([path elementAtIndex:element_count - 1], NSMoveToBezierPathElement);
|
| +
|
| + // Shoelace Algorithm to find the area of a simple polygon.
|
| + DCHECK(NSEqualPoints(poly.front(), poly.back()));
|
| + double area = 0;
|
| + for (size_t i = 0; i < poly.size() - 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(double width,
|
| + double height,
|
| + 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 Rect.
|
| +Rect GetBoundingBox(NSBezierPath* path) {
|
| + const NSRect bounds = [path bounds];
|
| + return ToNearestRect(RectF(bounds.origin.x, bounds.origin.y,
|
| + bounds.size.width, bounds.size.height));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Check that empty NSBezierPath is returned for empty SkPath.
|
| +TEST(CreateNSBezierPathFromSkPathTest, EmptyPath) {
|
| + NSBezierPath* 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]);
|
| +}
|
| +
|
| +// 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}, {25, 25},
|
| + {101, 101}, {101, 149}, {149, 149}, {149, 101}, {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}};
|
| + ASSERT_EQ(arraysize(inside_points), arraysize(outside_points));
|
| + const 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 < arraysize(inside_points); 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. GetBoundingBox
|
| + // rounds the coordinates to nearest integer values.
|
| + 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 Rect expected_bounds(kCentreX - kRadius, kCentreY - kRadius,
|
| + 2 * kRadius, 2 * kRadius);
|
| +
|
| + SkPath path;
|
| + path.addCircle(SkIntToScalar(kCentreX), SkIntToScalar(kCentreY),
|
| + SkIntToScalar(kRadius));
|
| + NSBezierPath* 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. GetBoundingBox
|
| + // rounds the coordinates to nearest integer values.
|
| + EXPECT_EQ(expected_bounds, GetBoundingBox(result));
|
| +
|
| + // Check area of converted path is correct up to 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* polygon = [result bezierPathByFlatteningPath];
|
| + const double kTolerance = 0.14;
|
| + 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 Rect expected_bounds(kRectangleWidth, kRectangleHeight);
|
| +
|
| + SkRRect rrect;
|
| + rrect.setRectXY(SkRect::MakeWH(kRectangleWidth, kRectangleHeight),
|
| + kCornerRadius, kCornerRadius);
|
| +
|
| + const NSPoint inside_points[] = {
|
| + // Bottom left corner.
|
| + {kCornerRadius / 2.0, kCornerRadius / 2.0},
|
| + // Bottom right corner.
|
| + {kRectangleWidth - kCornerRadius / 2.0, kCornerRadius / 2.0},
|
| + // Top Right corner.
|
| + {kRectangleWidth - kCornerRadius / 2.0,
|
| + kRectangleHeight - kCornerRadius / 2.0},
|
| + // Top left corner.
|
| + {kCornerRadius / 2.0, kRectangleHeight - kCornerRadius / 2.0},
|
| + // 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}};
|
| + ASSERT_EQ(arraysize(inside_points), arraysize(outside_points));
|
| +
|
| + SkPath path;
|
| + path.addRRect(rrect);
|
| + 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 < arraysize(inside_points); 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. GetBoundingBox
|
| + // rounds the coordinates to nearest integer values.
|
| + EXPECT_EQ(expected_bounds, GetBoundingBox(result));
|
| +
|
| + // Check area of converted path is correct up to 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* polygon = [result bezierPathByFlatteningPath];
|
| + const double kTolerance = 0.14;
|
| + EXPECT_NEAR(CalculateRoundedRectangleArea(kRectangleWidth, kRectangleHeight,
|
| + kCornerRadius),
|
| + CalculatePolygonArea(polygon), kTolerance);
|
| +}
|
| +
|
| +} // namespace gfx
|
|
|