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