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