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