1/* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "gm/gm.h" 9#include "include/core/SkCanvas.h" 10#include "include/core/SkPaint.h" 11#include "include/core/SkPathBuilder.h" 12#include "include/core/SkRect.h" 13#include "include/core/SkScalar.h" 14#include "include/core/SkSize.h" 15#include "include/core/SkString.h" 16#include "include/core/SkTypes.h" 17#include "include/pathops/SkPathOps.h" 18#include <tuple> 19 20namespace { 21struct PathDY { 22 SkPath path; 23 SkScalar dy; 24}; 25} 26 27typedef PathDY (*MakePathProc)(); 28 29static PathDY make_triangle() { 30 constexpr int gCoord[] = { 31 10, 20, 15, 5, 30, 30 32 }; 33 return { 34 SkPathBuilder().moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1])) 35 .lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3])) 36 .lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5])) 37 .close() 38 .offset(10, 0) 39 .detach(), 40 30 41 }; 42} 43 44static PathDY make_rect() { 45 SkRect r = { SkIntToScalar(10), SkIntToScalar(10), 46 SkIntToScalar(30), SkIntToScalar(30) }; 47 return { 48 SkPath::Rect(r.makeOffset(10, 0)), 49 30 50 }; 51} 52 53static PathDY make_oval() { 54 SkRect r = { SkIntToScalar(10), SkIntToScalar(10), 55 SkIntToScalar(30), SkIntToScalar(30) }; 56 return { 57 SkPath::Oval(r.makeOffset(10, 0)), 58 30 59 }; 60} 61 62static PathDY make_star(int n) { 63 const SkScalar c = SkIntToScalar(45); 64 const SkScalar r = SkIntToScalar(20); 65 66 SkScalar rad = -SK_ScalarPI / 2; 67 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 68 69 SkPathBuilder b; 70 b.moveTo(c, c - r); 71 for (int i = 1; i < n; i++) { 72 rad += drad; 73 b.lineTo(c + SkScalarCos(rad) * r, c + SkScalarSin(rad) * r); 74 } 75 b.close(); 76 return { b.detach(), r * 2 * 6 / 5 }; 77} 78 79static PathDY make_star_5() { return make_star(5); } 80static PathDY make_star_13() { return make_star(13); } 81 82static PathDY make_three_line() { 83 static SkScalar xOffset = 34.f; 84 static SkScalar yOffset = 50.f; 85 SkPathBuilder b; 86 b.moveTo(-32.5f + xOffset, 0.0f + yOffset); 87 b.lineTo(32.5f + xOffset, 0.0f + yOffset); 88 89 b.moveTo(-32.5f + xOffset, 19 + yOffset); 90 b.lineTo(32.5f + xOffset, 19 + yOffset); 91 92 b.moveTo(-32.5f + xOffset, -19 + yOffset); 93 b.lineTo(32.5f + xOffset, -19 + yOffset); 94 b.lineTo(-32.5f + xOffset, -19 + yOffset); 95 96 b.close(); 97 98 return { b.detach(), 70 }; 99} 100 101static PathDY make_arrow() { 102 static SkScalar xOffset = 34.f; 103 static SkScalar yOffset = 40.f; 104 SkPathBuilder b; 105 b.moveTo(-26.f + xOffset, 0.0f + yOffset); 106 b.lineTo(26.f + xOffset, 0.0f + yOffset); 107 108 b.moveTo(-28.f + xOffset, -2.4748745f + yOffset); 109 b.lineTo(0 + xOffset, 25.525126f + yOffset); 110 111 b.moveTo(-28.f + xOffset, 2.4748745f + yOffset); 112 b.lineTo(0 + xOffset, -25.525126f + yOffset); 113 b.lineTo(-28.f + xOffset, 2.4748745f + yOffset); 114 115 b.close(); 116 117 return { b.detach(), 70 }; 118} 119 120static PathDY make_curve() { 121 static SkScalar xOffset = -382.f; 122 static SkScalar yOffset = -50.f; 123 SkPathBuilder b; 124 b.moveTo(491 + xOffset, 56 + yOffset); 125 b.conicTo(435.93292f + xOffset, 56.000031f + yOffset, 126 382.61078f + xOffset, 69.752716f + yOffset, 127 0.9920463f); 128 129 return { b.detach(), 40 }; 130} 131 132static PathDY make_battery() { 133 static SkScalar xOffset = 5.0f; 134 135 SkPathBuilder b; 136 b.moveTo(24.67f + xOffset, 0.33000004f); 137 b.lineTo(8.3299999f + xOffset, 0.33000004f); 138 b.lineTo(8.3299999f + xOffset, 5.3299999f); 139 b.lineTo(0.33000004f + xOffset, 5.3299999f); 140 b.lineTo(0.33000004f + xOffset, 50.669998f); 141 b.lineTo(32.669998f + xOffset, 50.669998f); 142 b.lineTo(32.669998f + xOffset, 5.3299999f); 143 b.lineTo(24.67f + xOffset, 5.3299999f); 144 b.lineTo(24.67f + xOffset, 0.33000004f); 145 b.close(); 146 147 b.moveTo(25.727224f + xOffset, 12.886665f); 148 b.lineTo(10.907918f + xOffset, 12.886665f); 149 b.lineTo(7.5166659f + xOffset, 28.683645f); 150 b.lineTo(14.810181f + xOffset, 28.683645f); 151 b.lineTo(7.7024879f + xOffset, 46.135998f); 152 b.lineTo(28.049999f + xOffset, 25.136419f); 153 b.lineTo(16.854223f + xOffset, 25.136419f); 154 b.lineTo(25.727224f + xOffset, 12.886665f); 155 b.close(); 156 return { b.detach(), 50 }; 157} 158 159static PathDY make_battery2() { 160 static SkScalar xOffset = 225.625f; 161 162 SkPathBuilder b; 163 b.moveTo(32.669998f + xOffset, 9.8640003f); 164 b.lineTo(0.33000004f + xOffset, 9.8640003f); 165 b.lineTo(0.33000004f + xOffset, 50.669998f); 166 b.lineTo(32.669998f + xOffset, 50.669998f); 167 b.lineTo(32.669998f + xOffset, 9.8640003f); 168 b.close(); 169 170 b.moveTo(10.907918f + xOffset, 12.886665f); 171 b.lineTo(25.727224f + xOffset, 12.886665f); 172 b.lineTo(16.854223f + xOffset, 25.136419f); 173 b.lineTo(28.049999f + xOffset, 25.136419f); 174 b.lineTo(7.7024879f + xOffset, 46.135998f); 175 b.lineTo(14.810181f + xOffset, 28.683645f); 176 b.lineTo(7.5166659f + xOffset, 28.683645f); 177 b.lineTo(10.907918f + xOffset, 12.886665f); 178 b.close(); 179 180 return { b.detach(), 60 }; 181} 182 183static PathDY make_ring() { 184 static SkScalar xOffset = 120; 185 static SkScalar yOffset = -270.f; 186 187 SkPathBuilder b; 188 b.setFillType(SkPathFillType::kWinding); 189 b.moveTo(xOffset + 144.859f, yOffset + 285.172f); 190 b.lineTo(xOffset + 144.859f, yOffset + 285.172f); 191 b.lineTo(xOffset + 144.859f, yOffset + 285.172f); 192 b.lineTo(xOffset + 143.132f, yOffset + 284.617f); 193 b.lineTo(xOffset + 144.859f, yOffset + 285.172f); 194 b.close(); 195 b.moveTo(xOffset + 135.922f, yOffset + 286.844f); 196 b.lineTo(xOffset + 135.922f, yOffset + 286.844f); 197 b.lineTo(xOffset + 135.922f, yOffset + 286.844f); 198 b.lineTo(xOffset + 135.367f, yOffset + 288.571f); 199 b.lineTo(xOffset + 135.922f, yOffset + 286.844f); 200 b.close(); 201 b.moveTo(xOffset + 135.922f, yOffset + 286.844f); 202 b.cubicTo(xOffset + 137.07f, yOffset + 287.219f, xOffset + 138.242f, yOffset + 287.086f, 203 xOffset + 139.242f, yOffset + 286.578f); 204 b.cubicTo(xOffset + 140.234f, yOffset + 286.078f, xOffset + 141.031f, yOffset + 285.203f, 205 xOffset + 141.406f, yOffset + 284.055f); 206 b.lineTo(xOffset + 144.859f, yOffset + 285.172f); 207 b.cubicTo(xOffset + 143.492f, yOffset + 289.375f, xOffset + 138.992f, yOffset + 291.656f, 208 xOffset + 134.797f, yOffset + 290.297f); 209 b.lineTo(xOffset + 135.922f, yOffset + 286.844f); 210 b.close(); 211 b.moveTo(xOffset + 129.68f, yOffset + 280.242f); 212 b.lineTo(xOffset + 129.68f, yOffset + 280.242f); 213 b.lineTo(xOffset + 129.68f, yOffset + 280.242f); 214 b.lineTo(xOffset + 131.407f, yOffset + 280.804f); 215 b.lineTo(xOffset + 129.68f, yOffset + 280.242f); 216 b.close(); 217 b.moveTo(xOffset + 133.133f, yOffset + 281.367f); 218 b.cubicTo(xOffset + 132.758f, yOffset + 282.508f, xOffset + 132.883f, yOffset + 283.687f, 219 xOffset + 133.391f, yOffset + 284.679f); 220 b.cubicTo(xOffset + 133.907f, yOffset + 285.679f, xOffset + 134.774f, yOffset + 286.468f, 221 xOffset + 135.922f, yOffset + 286.843f); 222 b.lineTo(xOffset + 134.797f, yOffset + 290.296f); 223 b.cubicTo(xOffset + 130.602f, yOffset + 288.929f, xOffset + 128.313f, yOffset + 284.437f, 224 xOffset + 129.68f, yOffset + 280.241f); 225 b.lineTo(xOffset + 133.133f, yOffset + 281.367f); 226 b.close(); 227 b.moveTo(xOffset + 139.742f, yOffset + 275.117f); 228 b.lineTo(xOffset + 139.742f, yOffset + 275.117f); 229 b.lineTo(xOffset + 139.18f, yOffset + 276.844f); 230 b.lineTo(xOffset + 139.742f, yOffset + 275.117f); 231 b.close(); 232 b.moveTo(xOffset + 138.609f, yOffset + 278.57f); 233 b.cubicTo(xOffset + 137.461f, yOffset + 278.203f, xOffset + 136.297f, yOffset + 278.328f, 234 xOffset + 135.297f, yOffset + 278.836f); 235 b.cubicTo(xOffset + 134.297f, yOffset + 279.344f, xOffset + 133.508f, yOffset + 280.219f, 236 xOffset + 133.133f, yOffset + 281.367f); 237 b.lineTo(xOffset + 129.68f, yOffset + 280.242f); 238 b.cubicTo(xOffset + 131.047f, yOffset + 276.039f, xOffset + 135.539f, yOffset + 273.758f, 239 xOffset + 139.742f, yOffset + 275.117f); 240 b.lineTo(xOffset + 138.609f, yOffset + 278.57f); 241 b.close(); 242 b.moveTo(xOffset + 141.406f, yOffset + 284.055f); 243 b.cubicTo(xOffset + 141.773f, yOffset + 282.907f, xOffset + 141.648f, yOffset + 281.735f, 244 xOffset + 141.148f, yOffset + 280.735f); 245 b.cubicTo(xOffset + 140.625f, yOffset + 279.735f, xOffset + 139.757f, yOffset + 278.946f, 246 xOffset + 138.609f, yOffset + 278.571f); 247 b.lineTo(xOffset + 139.742f, yOffset + 275.118f); 248 b.cubicTo(xOffset + 143.937f, yOffset + 276.493f, xOffset + 146.219f, yOffset + 280.977f, 249 xOffset + 144.859f, yOffset + 285.173f); 250 b.lineTo(xOffset + 141.406f, yOffset + 284.055f); 251 b.close(); 252 253 // uncomment to reveal PathOps bug, see https://bugs.chromium.org/p/skia/issues/detail?id=9732 254 // (void) Simplify(*path, path); 255 256 return { b.detach(), 15 }; 257} 258 259constexpr MakePathProc gProcs[] = { 260 make_triangle, 261 make_rect, 262 make_oval, 263 make_star_5, 264 make_star_13, 265 make_three_line, 266 make_arrow, 267 make_curve, 268 make_battery, 269 make_battery2, 270 make_ring 271}; 272 273constexpr SkScalar gWidths[] = { 274 2.0f, 275 3.0f, 276 4.0f, 277 5.0f, 278 6.0f, 279 7.0f, 280 7.0f, 281 14.0f, 282 0.0f, 283 0.0f, 284 0.0f 285}; 286static_assert(SK_ARRAY_COUNT(gWidths) == SK_ARRAY_COUNT(gProcs)); 287 288constexpr SkScalar gMiters[] = { 289 2.0f, 290 3.0f, 291 3.0f, 292 3.0f, 293 4.0f, 294 4.0f, 295 4.0f, 296 4.0f, 297 4.0f, 298 4.0f, 299 4.0f, 300}; 301static_assert(SK_ARRAY_COUNT(gMiters) == SK_ARRAY_COUNT(gProcs)); 302 303constexpr SkScalar gXTranslate[] = { 304 0.0f, 305 0.0f, 306 0.0f, 307 0.0f, 308 0.0f, 309 0.0f, 310 0.0f, 311 0.0f, 312 -220.625f, 313 0.0f, 314 0.0f, 315}; 316static_assert(SK_ARRAY_COUNT(gXTranslate) == SK_ARRAY_COUNT(gProcs)); 317 318#define N SK_ARRAY_COUNT(gProcs) 319 320// This GM tests out drawing small paths (i.e., for Ganesh, using the Distance 321// Field path renderer) which are filled, stroked and filledAndStroked. In 322// particular this ensures that any cache keys in use include the stroking 323// parameters. 324class SmallPathsGM : public skiagm::GM { 325 SkPath fPath[N]; 326 SkScalar fDY[N]; 327protected: 328 void onOnceBeforeDraw() override { 329 for (size_t i = 0; i < N; i++) { 330 auto [path, dy] = gProcs[i](); 331 fPath[i] = path; 332 fDY[i] = dy; 333 } 334 } 335 336 SkString onShortName() override { 337 return SkString("smallpaths"); 338 } 339 340 SkISize onISize() override { 341 return SkISize::Make(640, 512); 342 } 343 344 void onDraw(SkCanvas* canvas) override { 345 SkPaint paint; 346 paint.setAntiAlias(true); 347 348 // first column: filled paths 349 canvas->save(); 350 for (size_t i = 0; i < N; i++) { 351 canvas->drawPath(fPath[i], paint); 352 canvas->translate(gXTranslate[i], fDY[i]); 353 } 354 canvas->restore(); 355 canvas->translate(SkIntToScalar(120), SkIntToScalar(0)); 356 357 // second column: stroked paths 358 canvas->save(); 359 paint.setStyle(SkPaint::kStroke_Style); 360 paint.setStrokeCap(SkPaint::kButt_Cap); 361 for (size_t i = 0; i < N; i++) { 362 paint.setStrokeWidth(gWidths[i]); 363 paint.setStrokeMiter(gMiters[i]); 364 canvas->drawPath(fPath[i], paint); 365 canvas->translate(gXTranslate[i], fDY[i]); 366 } 367 canvas->restore(); 368 canvas->translate(SkIntToScalar(120), SkIntToScalar(0)); 369 370 // third column: stroked paths with different widths 371 canvas->save(); 372 paint.setStyle(SkPaint::kStroke_Style); 373 paint.setStrokeCap(SkPaint::kButt_Cap); 374 for (size_t i = 0; i < N; i++) { 375 paint.setStrokeWidth(gWidths[i] + 2.0f); 376 paint.setStrokeMiter(gMiters[i]); 377 canvas->drawPath(fPath[i], paint); 378 canvas->translate(gXTranslate[i], fDY[i]); 379 } 380 canvas->restore(); 381 canvas->translate(SkIntToScalar(120), SkIntToScalar(0)); 382 383 // fourth column: stroked and filled paths 384 paint.setStyle(SkPaint::kStrokeAndFill_Style); 385 paint.setStrokeCap(SkPaint::kButt_Cap); 386 for (size_t i = 0; i < N; i++) { 387 paint.setStrokeWidth(gWidths[i]); 388 paint.setStrokeMiter(gMiters[i]); 389 canvas->drawPath(fPath[i], paint); 390 canvas->translate(gXTranslate[i], fDY[i]); 391 } 392 393 } 394 395private: 396 using INHERITED = skiagm::GM; 397}; 398 399DEF_GM(return new SmallPathsGM;) 400