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