Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "ui/gfx/path_mac.h" | |
| 6 | |
| 7 #include <cmath> | |
| 8 #include <vector> | |
| 9 | |
| 10 #import <Cocoa/Cocoa.h> | |
| 11 | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 #include "third_party/skia/include/core/SkRegion.h" | |
| 14 #include "ui/gfx/geometry/rect.h" | |
| 15 #include "ui/gfx/geometry/rect_conversions.h" | |
| 16 #include "ui/gfx/path.h" | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 // Returns the point at a distance of |radius| from the point (|centre_x|, | |
| 21 // |centre_y|), and angle |degrees| from the positive horizontal axis, measured | |
| 22 // anti-clockwise. | |
| 23 NSPoint GetRadialPoint(const double radius, | |
|
tapted
2016/02/05 04:51:07
nit: it's unusual to put `const` on arguments of p
karandeepb
2016/02/08 07:47:09
Done.
| |
| 24 const double degrees, | |
| 25 const double centre_x, | |
| 26 const double centre_y) { | |
| 27 const double radian = (degrees * SK_ScalarPI) / 180; | |
| 28 return NSMakePoint(centre_x + radius * std::cos(radian), | |
| 29 centre_y + radius * std::sin(radian)); | |
| 30 } | |
| 31 | |
| 32 // Returns the area of a circle with the given |radius|. | |
| 33 double CalculateCircleArea(const double radius) { | |
| 34 return SK_ScalarPI * radius * radius; | |
| 35 } | |
| 36 | |
| 37 // Returns the area of a simple polygon. |path| should represent a simple | |
| 38 // polygon. | |
| 39 double CalculatePolygonArea(NSBezierPath* const path) { | |
|
karandeepb
2016/02/08 07:47:09
This function somewhat depends on the internal NSB
| |
| 40 // If path represents a single polygon, it will have MoveTo, followed by | |
| 41 // multiple LineTo, followed By ClosePath, followed by another MoveTo | |
| 42 // NSBezierPathElement. | |
| 43 const size_t element_count = [path elementCount]; | |
| 44 size_t index = 0; | |
| 45 NSPoint points[3]; | |
| 46 std::vector<NSPoint> poly; | |
| 47 while (index < element_count) { | |
| 48 NSBezierPathElement element = | |
| 49 [path elementAtIndex:index++ associatedPoints:points]; | |
|
tapted
2016/02/05 04:51:07
The `index++` is easy to lose here - can this be a
karandeepb
2016/02/08 07:47:09
Done.
| |
| 50 poly.push_back(points[0]); | |
| 51 if (element == NSClosePathBezierPathElement) { | |
| 52 DCHECK_EQ(index, element_count - 1); | |
| 53 element = [path elementAtIndex:index++ associatedPoints:points]; | |
|
tapted
2016/02/05 04:51:07
`index++` doesn't look right on this line
karandeepb
2016/02/08 07:47:09
Done.
| |
| 54 DCHECK_EQ(element, NSMoveToBezierPathElement); | |
| 55 break; | |
| 56 } | |
| 57 DCHECK_EQ(element, index > 1 ? NSLineToBezierPathElement | |
| 58 : NSMoveToBezierPathElement); | |
| 59 } | |
| 60 | |
| 61 // Shoelace Algorithm to find the area of a simple polygon. | |
| 62 const size_t count = poly.size(); | |
| 63 DCHECK(NSEqualPoints(poly[0], poly[count - 1])); | |
|
tapted
2016/02/05 04:51:07
poly.front(), poly.back()?
karandeepb
2016/02/08 07:47:09
Done.
| |
| 64 double area = 0; | |
| 65 for (size_t i = 0; i < count - 1; i++) | |
|
tapted
2016/02/05 04:51:07
then poly.size() can be moved in here
karandeepb
2016/02/08 07:47:09
Done.
| |
| 66 area += poly[i].x * poly[i + 1].y - poly[i].y * poly[i + 1].x; | |
| 67 | |
| 68 return std::fabs(area) / 2.0; | |
| 69 } | |
| 70 | |
| 71 // Returns the area of a rounded rectangle with the given |width|, |height| and | |
| 72 // |radius|. | |
| 73 double CalculateRoundedRectangleArea(const double width, | |
| 74 const double height, | |
| 75 const double radius) { | |
| 76 const double inside_width = width - 2 * radius; | |
| 77 const double inside_height = height - 2 * radius; | |
| 78 return inside_width * inside_height + | |
| 79 2 * radius * (inside_width + inside_height) + | |
| 80 CalculateCircleArea(radius); | |
| 81 } | |
| 82 | |
| 83 // Returns the bounding box of |path| as a gfx::Rect. | |
| 84 gfx::Rect GetBoundingBox(NSBezierPath* const path) { | |
| 85 const NSRect bounds = [path bounds]; | |
| 86 return gfx::ToNearestRect(gfx::RectF(bounds.origin.x, bounds.origin.y, | |
| 87 bounds.size.width, bounds.size.height)); | |
| 88 } | |
| 89 | |
| 90 } // namespace | |
| 91 | |
| 92 namespace gfx { | |
| 93 | |
| 94 // Check that empty NSBezierPath is returned for empty SkRegion. | |
| 95 TEST(CreateNSBezierPathFromSkRegionTest, EmptyRegion) { | |
| 96 NSBezierPath* const result = CreateNSBezierPathFromSkRegion(SkRegion()); | |
|
tapted
2016/02/05 04:51:07
nit: no const (const typically looks extra weird a
karandeepb
2016/02/08 07:47:09
Done.
| |
| 97 EXPECT_TRUE([result isEmpty]); | |
| 98 } | |
| 99 | |
| 100 // Check that a region containing multiple rectangles is correctly converted to | |
| 101 // a NSBezierPath. | |
| 102 TEST(CreateNSBezierPathFromSkRegionTest, TwoRectanglesRegion) { | |
| 103 const SkIRect rects[] = { | |
| 104 {0, 0, 50, 50}, {100, 100, 150, 150}, | |
| 105 }; | |
| 106 const NSPoint inside_points[] = { | |
|
tapted
2016/02/05 04:51:07
these consts look fine though, since NSPoint is a
karandeepb
2016/02/08 07:47:09
Done.
| |
| 107 {1, 1}, {1, 49}, {49, 49}, {49, 1}, {101, 101}, | |
| 108 {101, 149}, {149, 149}, {149, 101}, {25, 25}, {125, 125}}; | |
| 109 const NSPoint outside_points[] = {{-1, -1}, {-1, 51}, {51, 51}, {51, -1}, | |
| 110 {99, 99}, {99, 151}, {151, 151}, {151, 99}, | |
| 111 {75, 75}, {-5, -5}}; | |
| 112 const size_t kNumPoints = 10; | |
| 113 const gfx::Rect expected_bounds(0, 0, 150, 150); // Bounding box of rects. | |
| 114 const size_t kSizeRectArray = 2; | |
|
tapted
2016/02/05 04:51:07
= arraysize(rects)? or just use arraysize(rects) i
karandeepb
2016/02/08 07:47:09
Done.
| |
| 115 | |
| 116 SkRegion region; | |
| 117 region.setRects(rects, kSizeRectArray); | |
| 118 NSBezierPath* result = CreateNSBezierPathFromSkRegion(region); | |
| 119 | |
| 120 // Check points near the boundary of the path and verify that they are | |
| 121 // reported correctly as being inside/outside the path. | |
| 122 for (size_t i = 0; i < kNumPoints; i++) { | |
| 123 EXPECT_TRUE([result containsPoint:inside_points[i]]); | |
| 124 EXPECT_FALSE([result containsPoint:outside_points[i]]); | |
| 125 } | |
| 126 | |
| 127 // Verify that the bounding box of |result| is correct. | |
| 128 EXPECT_EQ(expected_bounds, GetBoundingBox(result)); | |
| 129 } | |
| 130 | |
| 131 // Check that empty NSBezierPath is returned for empty SkPath. | |
| 132 TEST(CreateNSBezierPathFromSkPathTest, EmptyPath) { | |
| 133 NSBezierPath* const result = CreateNSBezierPathFromSkPath(SkPath()); | |
| 134 EXPECT_TRUE([result isEmpty]); | |
| 135 } | |
| 136 | |
| 137 // Check that the returned NSBezierPath has the correct winding rule. | |
| 138 TEST(CreateNSBezierPathFromSkPathTest, FillType) { | |
| 139 SkPath path; | |
| 140 path.setFillType(SkPath::kWinding_FillType); | |
| 141 NSBezierPath* result = CreateNSBezierPathFromSkPath(path); | |
| 142 EXPECT_EQ(NSNonZeroWindingRule, [result windingRule]); | |
| 143 | |
| 144 path.setFillType(SkPath::kEvenOdd_FillType); | |
| 145 result = CreateNSBezierPathFromSkPath(path); | |
| 146 EXPECT_EQ(NSEvenOddWindingRule, [result windingRule]); | |
| 147 } | |
| 148 | |
| 149 // Check that a path containing multiple subpaths, in this case two rectangles, | |
| 150 // is correctly converted to a NSBezierPath. | |
| 151 TEST(CreateNSBezierPathFromSkPathTest, TwoRectanglesPath) { | |
| 152 const SkRect rects[] = { | |
| 153 {0, 0, 50, 50}, {100, 100, 150, 150}, | |
| 154 }; | |
| 155 const NSPoint inside_points[] = { | |
| 156 {1, 1}, {1, 49}, {49, 49}, {49, 1}, {101, 101}, | |
| 157 {101, 149}, {149, 149}, {149, 101}, {25, 25}, {125, 125}}; | |
| 158 const NSPoint outside_points[] = {{-1, -1}, {-1, 51}, {51, 51}, {51, -1}, | |
| 159 {99, 99}, {99, 151}, {151, 151}, {151, 99}, | |
| 160 {75, 75}, {-5, -5}}; | |
| 161 const size_t kNumPoints = 10; | |
| 162 const gfx::Rect expected_bounds(0, 0, 150, 150); | |
| 163 | |
| 164 SkPath path; | |
| 165 path.addRect(rects[0]); | |
| 166 path.addRect(rects[1]); | |
| 167 NSBezierPath* result = CreateNSBezierPathFromSkPath(path); | |
| 168 | |
| 169 // Check points near the boundary of the path and verify that they are | |
| 170 // reported correctly as being inside/outside the path. | |
| 171 for (size_t i = 0; i < kNumPoints; i++) { | |
| 172 EXPECT_TRUE([result containsPoint:inside_points[i]]); | |
| 173 EXPECT_FALSE([result containsPoint:outside_points[i]]); | |
| 174 } | |
| 175 | |
| 176 // Verify that the bounding box of |result| is correct. | |
| 177 EXPECT_EQ(expected_bounds, GetBoundingBox(result)); | |
| 178 } | |
| 179 | |
| 180 // Test that an SKPath containing a circle is converted correctly to a | |
| 181 // NSBezierPath. | |
| 182 TEST(CreateNSBezierPathFromSkPathTest, CirclePath) { | |
| 183 const int kRadius = 5; | |
| 184 const int kCentreX = 10; | |
| 185 const int kCentreY = 15; | |
| 186 const double kCushion = 0.1; | |
| 187 // Expected bounding box of the circle. | |
| 188 const gfx::Rect expected_bounds(kCentreX - kRadius, kCentreY - kRadius, | |
| 189 2 * kRadius, 2 * kRadius); | |
| 190 | |
| 191 SkPath path; | |
| 192 path.addCircle(SkIntToScalar(kCentreX), SkIntToScalar(kCentreY), | |
| 193 SkIntToScalar(kRadius)); | |
| 194 NSBezierPath* const result = CreateNSBezierPathFromSkPath(path); | |
| 195 | |
| 196 // Check points near the boundary of the circle and verify that they are | |
| 197 // reported correctly as being inside/outside the path. | |
| 198 for (size_t deg = 0; deg < 360; deg++) { | |
| 199 NSPoint inside_point = | |
| 200 GetRadialPoint(kRadius - kCushion, deg, kCentreX, kCentreY); | |
| 201 NSPoint outside_point = | |
| 202 GetRadialPoint(kRadius + kCushion, deg, kCentreX, kCentreY); | |
| 203 EXPECT_TRUE([result containsPoint:inside_point]); | |
| 204 EXPECT_FALSE([result containsPoint:outside_point]); | |
| 205 } | |
| 206 | |
| 207 // Check that the returned result has the correct bounding box. | |
|
tapted
2016/02/05 04:51:07
maybe mention that this is rounding to whole numbe
karandeepb
2016/02/08 07:47:08
Done.
| |
| 208 EXPECT_EQ(expected_bounds, GetBoundingBox(result)); | |
| 209 | |
| 210 // Check area of converted path is correct upto a certain tolerance value. To | |
| 211 // find the area of the NSBezierPath returned, flatten it i.e. convert it to a | |
| 212 // polygon. | |
| 213 [NSBezierPath setDefaultFlatness:0.01]; | |
| 214 NSBezierPath* const polygon = [result bezierPathByFlatteningPath]; | |
|
tapted
2016/02/05 04:51:07
nit: no const
karandeepb
2016/02/08 07:47:08
Done.
| |
| 215 const double kTolerance = 0.5; | |
|
tapted
2016/02/05 04:51:07
Can we tighten this? I guess we should go pretty c
karandeepb
2016/02/08 07:47:09
Done.
| |
| 216 EXPECT_NEAR(CalculateCircleArea(kRadius), CalculatePolygonArea(polygon), | |
| 217 kTolerance); | |
| 218 } | |
| 219 | |
| 220 // Test that an SKPath containing a rounded rectangle is converted correctly to | |
| 221 // a NSBezierPath. | |
| 222 TEST(CreateNSBezierPathFromSkPathTest, RoundedRectanglePath) { | |
| 223 const int kRectangleWidth = 50; | |
| 224 const int kRectangleHeight = 100; | |
| 225 const int kCornerRadius = 5; | |
| 226 const double kCushion = 0.1; | |
| 227 // Expected bounding box of the rounded rectangle. | |
| 228 const gfx::Rect expected_bounds(kRectangleWidth, kRectangleHeight); | |
| 229 | |
| 230 SkRRect rrect; | |
| 231 rrect.setRectXY(SkRect::MakeWH(kRectangleWidth, kRectangleHeight), | |
| 232 kCornerRadius, kCornerRadius); | |
| 233 | |
| 234 const NSPoint inside_points[] = { | |
| 235 // Bottom left corner. | |
|
tapted
2016/02/05 04:51:07
nit: remove extra space
karandeepb
2016/02/08 07:47:09
Done.
| |
| 236 {kCornerRadius, kCornerRadius}, | |
|
tapted
2016/02/05 04:51:07
maybe half the radius?
karandeepb
2016/02/08 07:47:09
Done.
| |
| 237 // Bottom right corner. | |
| 238 {kRectangleWidth - kCornerRadius, kCornerRadius}, | |
| 239 // Top Right corner. | |
| 240 {kRectangleWidth - kCornerRadius, kRectangleHeight - kCornerRadius}, | |
| 241 // Top left corner. | |
| 242 {kCornerRadius, kRectangleHeight - kCornerRadius}, | |
| 243 // Bottom middle. | |
| 244 {kRectangleWidth / 2.0, kCushion}, | |
| 245 // Right middle. | |
| 246 {kRectangleWidth - kCushion, kRectangleHeight / 2.0}, | |
| 247 // Top middle. | |
| 248 {kRectangleWidth / 2.0, kRectangleHeight - kCushion}, | |
| 249 // Left middle. | |
| 250 {kCushion, kRectangleHeight / 2.0}}; | |
|
tapted
2016/02/05 04:51:07
nit: }; on a new line? (unless clang format likes
karandeepb
2016/02/08 07:47:09
clang format causes this.
| |
| 251 const NSPoint outside_points[] = { | |
| 252 // Bottom left corner. | |
| 253 {0, 0}, | |
| 254 // Bottom right corner. | |
| 255 {kRectangleWidth, 0}, | |
| 256 // Top right corner. | |
| 257 {kRectangleWidth, kRectangleHeight}, | |
| 258 // Top left corner. | |
| 259 {0, kRectangleHeight}, | |
| 260 // Bottom middle. | |
| 261 {kRectangleWidth / 2.0, -kCushion}, | |
| 262 // Right middle. | |
| 263 {kRectangleWidth + kCushion, kRectangleHeight / 2.0}, | |
| 264 // Top middle. | |
| 265 {kRectangleWidth / 2.0, kRectangleHeight + kCushion}, | |
| 266 // Left middle. | |
| 267 {-kCushion, kRectangleHeight / 2.0}}; | |
|
tapted
2016/02/05 04:51:07
}: -> new line
karandeepb
2016/02/08 07:47:09
clang format causes this.
| |
| 268 const size_t kNumPoints = 8; | |
| 269 | |
| 270 SkPath path; | |
| 271 path.addRRect(rrect); | |
| 272 NSBezierPath* const result = CreateNSBezierPathFromSkPath(path); | |
| 273 | |
| 274 // Check points near the boundary of the path and verify that they are | |
| 275 // reported correctly as being inside/outside the path. | |
| 276 for (size_t i = 0; i < kNumPoints; i++) { | |
| 277 EXPECT_TRUE([result containsPoint:inside_points[i]]); | |
| 278 EXPECT_FALSE([result containsPoint:outside_points[i]]); | |
| 279 } | |
| 280 | |
| 281 // Check that the returned result has the correct bounding box. | |
| 282 EXPECT_EQ(expected_bounds, GetBoundingBox(result)); | |
| 283 | |
| 284 // Check area of converted path is correct upto a certain tolerance value. To | |
| 285 // find the area of the NSBezierPath returned, flatten it i.e. convert it to a | |
| 286 // polygon. | |
| 287 [NSBezierPath setDefaultFlatness:0.01]; | |
| 288 NSBezierPath* const polygon = [result bezierPathByFlatteningPath]; | |
| 289 const double kTolerance = 0.5; | |
| 290 EXPECT_NEAR(CalculateRoundedRectangleArea(kRectangleWidth, kRectangleHeight, | |
| 291 kCornerRadius), | |
| 292 CalculatePolygonArea(polygon), kTolerance); | |
| 293 } | |
| 294 | |
| 295 } // namespace gfx | |
| OLD | NEW |