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(SkPathFillType::kEvenOdd);
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 / std::max(path.getBounds().height(), path.getBounds().width());
124         path.transform(SkMatrix::Scale(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 }  // namespace skiagm
167