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/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkScalar.h"
18 #include "include/core/SkShader.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTileMode.h"
22 #include "include/core/SkTypeface.h"
23 #include "include/core/SkTypes.h"
24 #include "include/effects/SkGradientShader.h"
25 #include "include/gpu/ganesh/GrRecordingContext.h"
26 #include "include/private/gpu/ganesh/GrTypesPriv.h"
27 #include "src/core/SkCanvasPriv.h"
28 #include "src/gpu/ganesh/GrCanvas.h"
29 #include "src/gpu/ganesh/GrPaint.h"
30 #include "src/gpu/ganesh/SkGr.h"
31 #include "src/gpu/ganesh/SurfaceDrawContext.h"
32 #include "tools/ToolUtils.h"
33 #include "tools/fonts/FontToolUtils.h"
34
35 #include <utility>
36
37 static constexpr SkScalar kTileWidth = 40;
38 static constexpr SkScalar kTileHeight = 30;
39
40 static constexpr int kRowCount = 4;
41 static constexpr int kColCount = 3;
42
draw_text(SkCanvas * canvas,const char * text)43 static void draw_text(SkCanvas* canvas, const char* text) {
44 SkFont font(ToolUtils::DefaultPortableTypeface(), 12);
45 canvas->drawString(text, 0, 0, font, SkPaint());
46 }
47
draw_gradient_tiles(SkCanvas * canvas,bool alignGradients)48 static void draw_gradient_tiles(SkCanvas* canvas, bool alignGradients) {
49 // Always draw the same gradient
50 static constexpr SkPoint pts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} };
51 static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorWHITE };
52
53 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas);
54
55 auto rContext = canvas->recordingContext();
56
57 auto gradient = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
58 SkPaint paint;
59 paint.setShader(gradient);
60
61 for (int i = 0; i < kRowCount; ++i) {
62 for (int j = 0; j < kColCount; ++j) {
63 SkRect tile = SkRect::MakeWH(kTileWidth, kTileHeight);
64 if (alignGradients) {
65 tile.offset(j * kTileWidth, i * kTileHeight);
66 } else {
67 canvas->save();
68 canvas->translate(j * kTileWidth, i * kTileHeight);
69 }
70
71 unsigned aa = SkCanvas::kNone_QuadAAFlags;
72 if (i == 0) {
73 aa |= SkCanvas::kTop_QuadAAFlag;
74 }
75 if (i == kRowCount - 1) {
76 aa |= SkCanvas::kBottom_QuadAAFlag;
77 }
78 if (j == 0) {
79 aa |= SkCanvas::kLeft_QuadAAFlag;
80 }
81 if (j == kColCount - 1) {
82 aa |= SkCanvas::kRight_QuadAAFlag;
83 }
84
85 if (sdc) {
86 // Use non-public API to leverage general GrPaint capabilities
87 const SkMatrix& view = canvas->getTotalMatrix();
88 SkSurfaceProps props;
89 GrPaint grPaint;
90 SkPaintToGrPaint(rContext, sdc->colorInfo(), paint, view, props, &grPaint);
91 sdc->fillRectWithEdgeAA(nullptr, std::move(grPaint),
92 static_cast<GrQuadAAFlags>(aa), view, tile);
93 } else {
94 // Fallback to solid color on raster backend since the public API only has color
95 SkColor color = alignGradients ? SK_ColorBLUE
96 : (i * kColCount + j) % 2 == 0 ? SK_ColorBLUE
97 : SK_ColorWHITE;
98 canvas->experimental_DrawEdgeAAQuad(
99 tile, nullptr, static_cast<SkCanvas::QuadAAFlags>(aa), color,
100 SkBlendMode::kSrcOver);
101 }
102
103 if (!alignGradients) {
104 // Pop off the matrix translation when drawing unaligned
105 canvas->restore();
106 }
107 }
108 }
109 }
110
draw_color_tiles(SkCanvas * canvas,bool multicolor)111 static void draw_color_tiles(SkCanvas* canvas, bool multicolor) {
112 for (int i = 0; i < kRowCount; ++i) {
113 for (int j = 0; j < kColCount; ++j) {
114 SkRect tile = SkRect::MakeXYWH(j * kTileWidth, i * kTileHeight, kTileWidth, kTileHeight);
115
116 SkColor4f color;
117 if (multicolor) {
118 color = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f};
119 } else {
120 color = {.2f, .8f, .3f, 1.f};
121 }
122
123 unsigned aa = SkCanvas::kNone_QuadAAFlags;
124 if (i == 0) {
125 aa |= SkCanvas::kTop_QuadAAFlag;
126 }
127 if (i == kRowCount - 1) {
128 aa |= SkCanvas::kBottom_QuadAAFlag;
129 }
130 if (j == 0) {
131 aa |= SkCanvas::kLeft_QuadAAFlag;
132 }
133 if (j == kColCount - 1) {
134 aa |= SkCanvas::kRight_QuadAAFlag;
135 }
136
137 canvas->experimental_DrawEdgeAAQuad(
138 tile, nullptr, static_cast<SkCanvas::QuadAAFlags>(aa), color.toSkColor(),
139 SkBlendMode::kSrcOver);
140 }
141 }
142 }
143
draw_tile_boundaries(SkCanvas * canvas,const SkMatrix & local)144 static void draw_tile_boundaries(SkCanvas* canvas, const SkMatrix& local) {
145 // Draw grid of red lines at interior tile boundaries.
146 static constexpr SkScalar kLineOutset = 10.f;
147 SkPaint paint;
148 paint.setAntiAlias(true);
149 paint.setColor(SK_ColorRED);
150 paint.setStyle(SkPaint::kStroke_Style);
151 paint.setStrokeWidth(0.f);
152 for (int x = 1; x < kColCount; ++x) {
153 SkPoint pts[] = {{x * kTileWidth, 0}, {x * kTileWidth, kRowCount * kTileHeight}};
154 local.mapPoints(pts, 2);
155 SkVector v = pts[1] - pts[0];
156 v.setLength(v.length() + kLineOutset);
157 canvas->drawLine(pts[1] - v, pts[0] + v, paint);
158 }
159 for (int y = 1; y < kRowCount; ++y) {
160 SkPoint pts[] = {{0, y * kTileHeight}, {kTileWidth * kColCount, y * kTileHeight}};
161 local.mapPoints(pts, 2);
162 SkVector v = pts[1] - pts[0];
163 v.setLength(v.length() + kLineOutset);
164 canvas->drawLine(pts[1] - v, pts[0] + v, paint);
165 }
166 }
167
168 // Tile renderers (column variation)
169 typedef void (*TileRenderer)(SkCanvas*);
170 static TileRenderer kTileSets[] = {
__anon9a0bc0b70102() 171 [](SkCanvas* canvas) { draw_gradient_tiles(canvas, /* aligned */ false); },
__anon9a0bc0b70202() 172 [](SkCanvas* canvas) { draw_gradient_tiles(canvas, /* aligned */ true); },
__anon9a0bc0b70302() 173 [](SkCanvas* canvas) { draw_color_tiles(canvas, /* multicolor */ false); },
__anon9a0bc0b70402() 174 [](SkCanvas* canvas) { draw_color_tiles(canvas, /* multicolor */true); },
175 };
176 static const char* kTileSetNames[] = { "Local", "Aligned", "Green", "Multicolor" };
177 static_assert(std::size(kTileSets) == std::size(kTileSetNames), "Count mismatch");
178
179 namespace skiagm {
180
181 class DrawQuadSetGM : public GM {
182 private:
getName() const183 SkString getName() const override { return SkString("draw_quad_set"); }
getISize()184 SkISize getISize() override { return SkISize::Make(800, 800); }
185
onDraw(SkCanvas * canvas)186 void onDraw(SkCanvas* canvas) override {
187 SkMatrix rowMatrices[5];
188 // Identity
189 rowMatrices[0].setIdentity();
190 // Translate/scale
191 rowMatrices[1].setTranslate(5.5f, 20.25f);
192 rowMatrices[1].postScale(.9f, .7f);
193 // Rotation
194 rowMatrices[2].setRotate(20.0f);
195 rowMatrices[2].preTranslate(15.f, -20.f);
196 // Skew
197 rowMatrices[3].setSkew(.5f, .25f);
198 rowMatrices[3].preTranslate(-30.f, 0.f);
199 // Perspective
200 SkPoint src[4];
201 SkRect::MakeWH(kColCount * kTileWidth, kRowCount * kTileHeight).toQuad(src);
202 SkPoint dst[4] = {{0, 0},
203 {kColCount * kTileWidth + 10.f, 15.f},
204 {kColCount * kTileWidth - 28.f, kRowCount * kTileHeight + 40.f},
205 {25.f, kRowCount * kTileHeight - 15.f}};
206 SkAssertResult(rowMatrices[4].setPolyToPoly(src, dst, 4));
207 rowMatrices[4].preTranslate(0.f, +10.f);
208 static const char* matrixNames[] = { "Identity", "T+S", "Rotate", "Skew", "Perspective" };
209 static_assert(std::size(matrixNames) == std::size(rowMatrices), "Count mismatch");
210
211 // Print a column header
212 canvas->save();
213 canvas->translate(110.f, 20.f);
214 for (size_t j = 0; j < std::size(kTileSetNames); ++j) {
215 draw_text(canvas, kTileSetNames[j]);
216 canvas->translate(kColCount * kTileWidth + 30.f, 0.f);
217 }
218 canvas->restore();
219 canvas->translate(0.f, 40.f);
220
221 // Render all tile variations
222 for (size_t i = 0; i < std::size(rowMatrices); ++i) {
223 canvas->save();
224 canvas->translate(10.f, 0.5f * kRowCount * kTileHeight);
225 draw_text(canvas, matrixNames[i]);
226
227 canvas->translate(100.f, -0.5f * kRowCount * kTileHeight);
228 for (size_t j = 0; j < std::size(kTileSets); ++j) {
229 canvas->save();
230 draw_tile_boundaries(canvas, rowMatrices[i]);
231
232 canvas->concat(rowMatrices[i]);
233 kTileSets[j](canvas);
234 // Undo the local transformation
235 canvas->restore();
236 // And advance to the next column
237 canvas->translate(kColCount * kTileWidth + 30.f, 0.f);
238 }
239 // Reset back to the left edge
240 canvas->restore();
241 // And advance to the next row
242 canvas->translate(0.f, kRowCount * kTileHeight + 20.f);
243 }
244 }
245 };
246
247 DEF_GM(return new DrawQuadSetGM();)
248
249 } // namespace skiagm
250