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