1 /* 2 * Copyright 2018 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.h" 9 #include "sk_tool_utils.h" 10 #include "SkPaint.h" 11 #include "SkPath.h" 12 #include "SkPoint.h" 13 14 #include <array> 15 #include <vector> 16 17 namespace skiagm { 18 19 static constexpr int kPadSize = 20; 20 static constexpr int kBoxSize = 100; 21 static constexpr SkPoint kJitters[] = {{0, 0}, {.5f, .5f}, {2/3.f, 1/3.f}}; 22 23 // Tests various corners of different angles falling on the same pixel, particularly to ensure 24 // analytic AA is working properly. 25 class SharedCornersGM : public GM { 26 public: 27 SharedCornersGM() { 28 this->setBGColor(sk_tool_utils::color_to_565(0xFF1A65D7)); 29 } 30 31 protected: 32 SkString onShortName() override { 33 return SkString("sharedcorners"); 34 } 35 36 SkISize onISize() override { 37 constexpr int numRows = 3 * 2; 38 constexpr int numCols = (1 + SK_ARRAY_COUNT(kJitters)) * 2; 39 return SkISize::Make(numCols * (kBoxSize + kPadSize) + kPadSize, 40 numRows * (kBoxSize + kPadSize) + kPadSize); 41 } 42 43 void onOnceBeforeDraw() override { 44 fFillPaint.setColor(SK_ColorWHITE); 45 fFillPaint.setAntiAlias(true); 46 47 fWireFramePaint = fFillPaint; 48 fWireFramePaint.setStyle(SkPaint::kStroke_Style); 49 50 } 51 52 void onDraw(SkCanvas* canvas) override { 53 canvas->translate(kPadSize, kPadSize); 54 canvas->save(); 55 56 // Adjacent rects. 57 this->drawTriangleBoxes(canvas, 58 {{0, 0}, {40, 0}, {80, 0}, {120, 0}, 59 {0, 20}, {40, 20}, {80, 20}, {120, 20}, 60 {40, 40}, {80, 40}, 61 {40, 60}, {80, 60}}, 62 {{{0, 1, 4}}, {{1, 5, 4}}, 63 {{5, 1, 6}}, {{1, 2, 6}}, 64 {{2, 3, 6}}, {{3, 7, 6}}, 65 {{8, 5, 9}}, {{5, 6, 9}}, 66 {{10, 8, 11}}, {{8, 9, 11}}}); 67 68 // Obtuse angles. 69 this->drawTriangleBoxes(canvas, 70 {{ 0, 0}, {10, 0}, {20, 0}, 71 { 0, 2}, {20, 2}, 72 {10, 4}, 73 { 0, 6}, {20, 6}, 74 { 0, 8}, {10, 8}, {20, 8}}, 75 {{{3, 1, 4}}, {{4, 5, 3}}, {{6, 5, 7}}, {{7, 9, 6}}, 76 {{0, 1, 3}}, {{1, 2, 4}}, 77 {{3, 5, 6}}, {{5, 4, 7}}, 78 {{6, 9, 8}}, {{9, 7, 10}}}); 79 80 canvas->restore(); 81 canvas->translate((kBoxSize + kPadSize) * 4, 0); 82 83 // Right angles. 84 this->drawTriangleBoxes(canvas, 85 {{0, 0}, {-1, 0}, {0, -1}, {1, 0}, {0, 1}}, 86 {{{0, 1, 2}}, {{0, 2, 3}}, {{0, 3, 4}}, {{0, 4, 1}}}); 87 88 // Acute angles. 89 SkRandom rand; 90 std::vector<SkPoint> pts; 91 std::vector<std::array<int, 3>> indices; 92 SkScalar theta = 0; 93 pts.push_back({0, 0}); 94 while (theta < 2*SK_ScalarPI) { 95 pts.push_back({SkScalarCos(theta), SkScalarSin(theta)}); 96 if (pts.size() > 2) { 97 indices.push_back({{0, (int)pts.size() - 2, (int)pts.size() - 1}}); 98 } 99 theta += rand.nextRangeF(0, SK_ScalarPI/3); 100 } 101 indices.push_back({{0, (int)pts.size() - 1, 1}}); 102 this->drawTriangleBoxes(canvas, pts, indices); 103 } 104 105 void drawTriangleBoxes(SkCanvas* canvas, const std::vector<SkPoint>& points, 106 const std::vector<std::array<int, 3>>& triangles) { 107 SkPath path; 108 path.setFillType(SkPath::kEvenOdd_FillType); 109 path.setIsVolatile(true); 110 for (const std::array<int, 3>& triangle : triangles) { 111 path.moveTo(points[triangle[0]]); 112 path.lineTo(points[triangle[1]]); 113 path.lineTo(points[triangle[2]]); 114 path.close(); 115 } 116 SkScalar scale = kBoxSize / SkTMax(path.getBounds().height(), path.getBounds().width()); 117 path.transform(SkMatrix::MakeScale(scale, scale)); 118 119 this->drawRow(canvas, path); 120 canvas->translate(0, kBoxSize + kPadSize); 121 122 SkMatrix rot; 123 rot.setRotate(45, path.getBounds().centerX(), path.getBounds().centerY()); 124 path.transform(rot); 125 this->drawRow(canvas, path); 126 canvas->translate(0, kBoxSize + kPadSize); 127 128 rot.setRotate(-45 - 69.38111f, path.getBounds().centerX(), path.getBounds().centerY()); 129 path.transform(rot); 130 this->drawRow(canvas, path); 131 canvas->translate(0, kBoxSize + kPadSize); 132 } 133 134 void drawRow(SkCanvas* canvas, const SkPath& path) { 135 SkAutoCanvasRestore acr(canvas, true); 136 const SkRect& bounds = path.getBounds(); 137 canvas->translate((kBoxSize - bounds.width()) / 2 - bounds.left(), 138 (kBoxSize - bounds.height()) / 2 - bounds.top()); 139 140 canvas->drawPath(path, fWireFramePaint); 141 canvas->translate(kBoxSize + kPadSize, 0); 142 143 for (SkPoint jitter : kJitters) { 144 { 145 SkAutoCanvasRestore acr(canvas, true); 146 canvas->translate(jitter.x(), jitter.y()); 147 canvas->drawPath(path, fFillPaint); 148 } 149 canvas->translate(kBoxSize + kPadSize, 0); 150 } 151 } 152 153 SkPaint fWireFramePaint; 154 SkPaint fFillPaint; 155 }; 156 157 DEF_GM(return new SharedCornersGM;) 158 159 } 160