1 /*
2 * Copyright 2017 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/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkFontTypes.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPoint.h"
19 #include "include/core/SkRect.h"
20 #include "include/core/SkRefCnt.h"
21 #include "include/core/SkSize.h"
22 #include "include/core/SkString.h"
23 #include "include/core/SkSurface.h"
24 #include "include/core/SkTypeface.h"
25 #include "include/core/SkTypes.h"
26 #include "include/gpu/GrDirectContext.h"
27 #include "include/gpu/GrRecordingContext.h"
28 #include "include/gpu/GrTypes.h"
29 #include "include/private/SkTArray.h"
30 #include "src/gpu/GrDirectContextPriv.h"
31 #include "src/image/SkImage_Base.h"
32 #include "src/image/SkImage_Gpu.h"
33 #include "tools/ToolUtils.h"
34 #include "tools/gpu/ProxyUtils.h"
35
36 #include <string.h>
37 #include <utility>
38
39 class GrRenderTargetContext;
40
41 static const int kNumMatrices = 6;
42 static const int kImageSize = 128;
43 static const int kLabelSize = 32;
44 static const int kNumLabels = 4;
45 static const int kInset = 16;
46
47 static const int kCellSize = kImageSize+2*kLabelSize;
48 static const int kGMWidth = kNumMatrices*kCellSize;
49 static const int kGMHeight = 4*kCellSize;
50
51 static const SkPoint kPoints[kNumLabels] = {
52 { 0, kImageSize }, // LL
53 { kImageSize, kImageSize }, // LR
54 { 0, 0 }, // UL
55 { kImageSize, 0 }, // UR
56 };
57
58 static const SkMatrix kUVMatrices[kNumMatrices] = {
59 SkMatrix::MakeAll( 0, -1, 1,
60 -1, 0, 1,
61 0, 0, 1),
62 SkMatrix::MakeAll( 1, 0, 0,
63 0, -1, 1,
64 0, 0, 1),
65 // flip x
66 SkMatrix::MakeAll(-1, 0, 1,
67 0, 1, 0,
68 0, 0, 1),
69 SkMatrix::MakeAll( 0, 1, 0,
70 -1, 0, 1,
71 0, 0, 1),
72 // flip both x & y == rotate 180
73 SkMatrix::MakeAll(-1, 0, 1,
74 0, -1, 1,
75 0, 0, 1),
76 // identity
77 SkMatrix::MakeAll(1, 0, 0,
78 0, 1, 0,
79 0, 0, 1)
80 };
81
82
83 // Create a fixed size text label like "LL" or "LR".
make_text_image(GrDirectContext * direct,const char * text,SkColor color)84 static sk_sp<SkImage> make_text_image(GrDirectContext* direct, const char* text, SkColor color) {
85 SkPaint paint;
86 paint.setAntiAlias(true);
87 paint.setColor(color);
88
89 SkFont font;
90 font.setEdging(SkFont::Edging::kAntiAlias);
91 font.setTypeface(ToolUtils::create_portable_typeface());
92 font.setSize(32);
93
94 SkRect bounds;
95 font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds);
96 const SkMatrix mat = SkMatrix::MakeRectToRect(bounds, SkRect::MakeWH(kLabelSize, kLabelSize),
97 SkMatrix::kFill_ScaleToFit);
98
99 const SkImageInfo ii = SkImageInfo::MakeN32Premul(kLabelSize, kLabelSize);
100 sk_sp<SkSurface> surf = SkSurface::MakeRaster(ii);
101
102 SkCanvas* canvas = surf->getCanvas();
103
104 canvas->clear(SK_ColorWHITE);
105 canvas->concat(mat);
106 canvas->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, 0, 0, font, paint);
107
108 sk_sp<SkImage> image = surf->makeImageSnapshot();
109
110 return image->makeTextureImage(direct);
111 }
112
113 // Create an image with each corner marked w/ "LL", "LR", etc., with the origin either bottom-left
114 // or top-left.
make_reference_image(GrDirectContext * context,const SkTArray<sk_sp<SkImage>> & labels,bool bottomLeftOrigin)115 static sk_sp<SkImage> make_reference_image(GrDirectContext* context,
116 const SkTArray<sk_sp<SkImage>>& labels,
117 bool bottomLeftOrigin) {
118 SkASSERT(kNumLabels == labels.count());
119
120 SkImageInfo ii =
121 SkImageInfo::Make(kImageSize, kImageSize, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
122 SkBitmap bm;
123 bm.allocPixels(ii);
124 SkCanvas canvas(bm);
125
126 canvas.clear(SK_ColorWHITE);
127 for (int i = 0; i < kNumLabels; ++i) {
128 canvas.drawImage(labels[i],
129 0.0 != kPoints[i].fX ? kPoints[i].fX-kLabelSize-kInset : kInset,
130 0.0 != kPoints[i].fY ? kPoints[i].fY-kLabelSize-kInset : kInset);
131 }
132
133 auto origin = bottomLeftOrigin ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
134
135 auto view = sk_gpu_test::MakeTextureProxyViewFromData(context, GrRenderable::kNo, origin,
136 bm.info(), bm.getPixels(), bm.rowBytes());
137 if (!view) {
138 return nullptr;
139 }
140
141 return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), kNeedNewImageUniqueID, std::move(view),
142 ii.colorType(), kOpaque_SkAlphaType, nullptr);
143 }
144
145 // Here we're converting from a matrix that is intended for UVs to a matrix that is intended
146 // for rect geometry used for a drawImage call. They are, in some sense, inverses of each
147 // other but we also need a scale to map from the [0..1] uv range to the actual size of
148 // image.
UVMatToGeomMatForImage(SkMatrix * geomMat,const SkMatrix & uvMat)149 static bool UVMatToGeomMatForImage(SkMatrix* geomMat, const SkMatrix& uvMat) {
150
151 const SkMatrix yFlip = SkMatrix::MakeAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
152
153 SkMatrix tmp = uvMat;
154 tmp.preConcat(yFlip);
155 tmp.preScale(1.0f/kImageSize, 1.0f/kImageSize);
156
157 tmp.postConcat(yFlip);
158 tmp.postScale(kImageSize, kImageSize);
159
160 return tmp.invert(geomMat);
161 }
162
163 // This GM exercises drawImage with a set of matrices that use an unusual amount of flips and
164 // rotates.
165 class FlippityGM : public skiagm::GpuGM {
166 public:
FlippityGM()167 FlippityGM() {
168 this->setBGColor(0xFFCCCCCC);
169 }
170
171 private:
onShortName()172 SkString onShortName() override {
173 return SkString("flippity");
174 }
175
onISize()176 SkISize onISize() override {
177 return SkISize::Make(kGMWidth, kGMHeight);
178 }
179
180 // Draw the reference image and the four corner labels in the matrix's coordinate space
drawImageWithMatrixAndLabels(SkCanvas * canvas,SkImage * image,int matIndex,bool drawSubset,bool drawScaled)181 void drawImageWithMatrixAndLabels(SkCanvas* canvas, SkImage* image, int matIndex,
182 bool drawSubset, bool drawScaled) {
183 static const SkRect kSubsets[kNumMatrices] = {
184 SkRect::MakeXYWH(kInset, 0, kImageSize-kInset, kImageSize),
185 SkRect::MakeXYWH(0, kInset, kImageSize, kImageSize-kInset),
186 SkRect::MakeXYWH(0, 0, kImageSize-kInset, kImageSize),
187 SkRect::MakeXYWH(0, 0, kImageSize, kImageSize-kInset),
188 SkRect::MakeXYWH(kInset/2, kInset/2, kImageSize-kInset, kImageSize-kInset),
189 SkRect::MakeXYWH(kInset, kInset, kImageSize-2*kInset, kImageSize-2*kInset),
190 };
191
192 SkMatrix imageGeomMat;
193 SkAssertResult(UVMatToGeomMatForImage(&imageGeomMat, kUVMatrices[matIndex]));
194
195 canvas->save();
196
197 // draw the reference image
198 canvas->concat(imageGeomMat);
199 if (drawSubset) {
200 canvas->drawImageRect(image, kSubsets[matIndex],
201 drawScaled ? SkRect::MakeWH(kImageSize, kImageSize)
202 : kSubsets[matIndex],
203 nullptr, SkCanvas::kFast_SrcRectConstraint);
204 } else {
205 canvas->drawImage(image, 0, 0);
206 }
207
208 // draw the labels
209 for (int i = 0; i < kNumLabels; ++i) {
210 canvas->drawImage(fLabels[i],
211 0.0f == kPoints[i].fX ? -kLabelSize : kPoints[i].fX,
212 0.0f == kPoints[i].fY ? -kLabelSize : kPoints[i].fY);
213 }
214 canvas->restore();
215 }
216
drawRow(SkCanvas * canvas,bool bottomLeftImage,bool drawSubset,bool drawScaled)217 void drawRow(SkCanvas* canvas, bool bottomLeftImage, bool drawSubset, bool drawScaled) {
218
219 canvas->save();
220 canvas->translate(kLabelSize, kLabelSize);
221
222 for (int i = 0; i < kNumMatrices; ++i) {
223 this->drawImageWithMatrixAndLabels(canvas, fReferenceImages[bottomLeftImage].get(),
224 i, drawSubset, drawScaled);
225 canvas->translate(kCellSize, 0);
226 }
227 canvas->restore();
228 }
229
makeLabels(GrDirectContext * direct)230 void makeLabels(GrDirectContext* direct) {
231 if (fLabels.count()) {
232 return;
233 }
234
235 static const char* kLabelText[kNumLabels] = { "LL", "LR", "UL", "UR" };
236
237 static const SkColor kLabelColors[kNumLabels] = {
238 SK_ColorRED,
239 SK_ColorGREEN,
240 SK_ColorBLUE,
241 SK_ColorCYAN
242 };
243
244 for (int i = 0; i < kNumLabels; ++i) {
245 fLabels.push_back(make_text_image(direct, kLabelText[i], kLabelColors[i]));
246 }
247 SkASSERT(kNumLabels == fLabels.count());
248 }
249
onGpuSetup(GrDirectContext * context,SkString * errorMsg)250 DrawResult onGpuSetup(GrDirectContext* context, SkString* errorMsg) override {
251 if (!context || context->abandoned()) {
252 return DrawResult::kSkip;
253 }
254
255 this->makeLabels(context);
256 fReferenceImages[0] = make_reference_image(context, fLabels, false);
257 fReferenceImages[1] = make_reference_image(context, fLabels, true);
258 if (!fReferenceImages[0] || !fReferenceImages[1]) {
259 *errorMsg = "Failed to create reference images.";
260 return DrawResult::kFail;
261 }
262
263 return DrawResult::kOk;
264 }
265
onGpuTeardown()266 void onGpuTeardown() override {
267 fLabels.reset();
268 fReferenceImages[0] = fReferenceImages[1] = nullptr;
269 }
270
onDraw(GrRecordingContext *,GrRenderTargetContext *,SkCanvas * canvas)271 void onDraw(GrRecordingContext*, GrRenderTargetContext*, SkCanvas* canvas) override {
272 SkASSERT(fReferenceImages[0] && fReferenceImages[1]);
273
274 canvas->save();
275
276 // Top row gets TL image
277 this->drawRow(canvas, false, false, false);
278
279 canvas->translate(0, kCellSize);
280
281 // Bottom row gets BL image
282 this->drawRow(canvas, true, false, false);
283
284 canvas->translate(0, kCellSize);
285
286 // Third row gets subsets of BL images
287 this->drawRow(canvas, true, true, false);
288
289 canvas->translate(0, kCellSize);
290
291 // Fourth row gets scaled subsets of BL images
292 this->drawRow(canvas, true, true, true);
293
294 canvas->restore();
295
296 // separator grid
297 for (int i = 0; i < 4; ++i) {
298 canvas->drawLine(0, i * kCellSize, kGMWidth, i * kCellSize, SkPaint());
299 }
300 for (int i = 0; i < kNumMatrices; ++i) {
301 canvas->drawLine(i * kCellSize, 0, i * kCellSize, kGMHeight, SkPaint());
302 }
303 }
304
305 private:
306 SkTArray<sk_sp<SkImage>> fLabels;
307 sk_sp<SkImage> fReferenceImages[2];
308
309 using INHERITED = GM;
310 };
311
312 DEF_GM(return new FlippityGM;)
313