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