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 #if SK_SUPPORT_GPU
14 
15 #include "GrContextPriv.h"
16 #include "GrProxyProvider.h"
17 #include "SkImage_Gpu.h"
18 
19 static const int kNumMatrices = 6;
20 static const int kImageSize = 128;
21 static const int kLabelSize = 32;
22 static const int kNumLabels = 4;
23 static const int kInset = 16;
24 
25 static const int kCellSize = kImageSize+2*kLabelSize;
26 static const int kGMWidth  = kNumMatrices*kCellSize;
27 static const int kGMHeight = 4*kCellSize;
28 
29 static const SkPoint kPoints[kNumLabels] = {
30     {          0, kImageSize },     // LL
31     { kImageSize, kImageSize },     // LR
32     {          0,          0 },     // UL
33     { kImageSize,          0 },     // UR
34 };
35 
36 static const SkMatrix kUVMatrices[kNumMatrices] = {
37     SkMatrix::MakeAll( 0, -1, 1,
38                       -1,  0, 1,
39                        0,  0, 1),
40     SkMatrix::MakeAll( 1,  0, 0,
41                        0, -1, 1,
42                        0,  0, 1),
43     // flip x
44     SkMatrix::MakeAll(-1,  0, 1,
45                        0,  1, 0,
46                        0,  0, 1),
47     SkMatrix::MakeAll( 0,  1, 0,
48                       -1,  0, 1,
49                        0,  0, 1),
50     // flip both x & y == rotate 180
51     SkMatrix::MakeAll(-1,  0, 1,
52                        0, -1, 1,
53                        0,  0, 1),
54     // identity
55     SkMatrix::MakeAll(1,  0, 0,
56                       0,  1, 0,
57                       0,  0, 1)
58 };
59 
60 
61 // Create a fixed size text label like "LL" or "LR".
make_text_image(GrContext * context,const char * text,SkColor color)62 static sk_sp<SkImage> make_text_image(GrContext* context, const char* text, SkColor color) {
63     SkPaint paint;
64     sk_tool_utils::set_portable_typeface(&paint);
65     paint.setAntiAlias(true);
66     paint.setTextSize(32);
67     paint.setColor(color);
68 
69     SkRect bounds;
70     paint.measureText(text, strlen(text), &bounds);
71     const SkMatrix mat = SkMatrix::MakeRectToRect(bounds, SkRect::MakeWH(kLabelSize, kLabelSize),
72                                                   SkMatrix::kFill_ScaleToFit);
73 
74     const SkImageInfo ii = SkImageInfo::MakeN32Premul(kLabelSize, kLabelSize);
75     sk_sp<SkSurface> surf = SkSurface::MakeRaster(ii);
76 
77     SkCanvas* canvas = surf->getCanvas();
78 
79     canvas->clear(SK_ColorWHITE);
80     canvas->concat(mat);
81     canvas->drawText(text, strlen(text), 0, 0, paint);
82 
83     sk_sp<SkImage> image = surf->makeImageSnapshot();
84 
85     return image->makeTextureImage(context, nullptr);
86 }
87 
swap_red_and_blue(SkColor c)88 static SkColor swap_red_and_blue(SkColor c) {
89     return SkColorSetRGB(SkColorGetB(c), SkColorGetG(c), SkColorGetR(c));
90 }
91 
92 // Create an image with each corner marked w/ "LL", "LR", etc., with the origin either bottom-left
93 // or top-left.
make_reference_image(GrContext * context,const SkTArray<sk_sp<SkImage>> & labels,bool bottomLeftOrigin)94 static sk_sp<SkImage> make_reference_image(GrContext* context,
95                                            const SkTArray<sk_sp<SkImage>>& labels,
96                                            bool bottomLeftOrigin) {
97     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
98     SkASSERT(kNumLabels == labels.count());
99 
100     SkImageInfo ii = SkImageInfo::Make(kImageSize, kImageSize,
101                                        kN32_SkColorType, kOpaque_SkAlphaType);
102     SkBitmap bm;
103     bm.allocPixels(ii);
104     SkCanvas canvas(bm);
105 
106     canvas.clear(SK_ColorWHITE);
107     for (int i = 0; i < kNumLabels; ++i) {
108         canvas.drawImage(labels[i],
109                          0.0 != kPoints[i].fX ? kPoints[i].fX-kLabelSize-kInset : kInset,
110                          0.0 != kPoints[i].fY ? kPoints[i].fY-kLabelSize-kInset : kInset);
111     }
112 
113     GrSurfaceDesc desc;
114     desc.fOrigin = kTopLeft_GrSurfaceOrigin;
115     desc.fWidth = kImageSize;
116     desc.fHeight = kImageSize;
117     desc.fConfig = kRGBA_8888_GrPixelConfig;
118 
119     if (bottomLeftOrigin) {
120         // Note that Ganesh will flip the data when it is uploaded
121         desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
122     }
123 
124     if (kN32_SkColorType == kBGRA_8888_SkColorType) {
125         // We're playing a game here and uploading N32 data into an RGB dest. We might have
126         // to swap red & blue to compensate.
127         for (int y = 0; y < bm.height(); ++y) {
128             uint32_t *sl = bm.getAddr32(0, y);
129             for (int x = 0; x < bm.width(); ++x) {
130                 sl[x] = swap_red_and_blue(sl[x]);
131             }
132         }
133     }
134 
135     sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(desc, SkBudgeted::kYes,
136                                                                     bm.getPixels(), bm.rowBytes());
137     if (!proxy) {
138         return nullptr;
139     }
140 
141     return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, kOpaque_SkAlphaType,
142                                    std::move(proxy), nullptr, SkBudgeted::kYes);
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::GM {
166 public:
FlippityGM()167     FlippityGM() {
168         this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
169     }
170 
171 protected:
172 
onShortName()173     SkString onShortName() override {
174         return SkString("flippity");
175     }
176 
onISize()177     SkISize onISize() override {
178         return SkISize::Make(kGMWidth, kGMHeight);
179     }
180 
181     // 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)182     void drawImageWithMatrixAndLabels(SkCanvas* canvas, SkImage* image, int matIndex,
183                                       bool drawSubset, bool drawScaled) {
184         static const SkRect kSubsets[kNumMatrices] = {
185             SkRect::MakeXYWH(kInset, 0, kImageSize-kInset, kImageSize),
186             SkRect::MakeXYWH(0, kInset, kImageSize, kImageSize-kInset),
187             SkRect::MakeXYWH(0, 0, kImageSize-kInset, kImageSize),
188             SkRect::MakeXYWH(0, 0, kImageSize, kImageSize-kInset),
189             SkRect::MakeXYWH(kInset/2, kInset/2, kImageSize-kInset, kImageSize-kInset),
190             SkRect::MakeXYWH(kInset, kInset, kImageSize-2*kInset, kImageSize-2*kInset),
191         };
192 
193         SkMatrix imageGeomMat;
194         SkAssertResult(UVMatToGeomMatForImage(&imageGeomMat, kUVMatrices[matIndex]));
195 
196         canvas->save();
197 
198             // draw the reference image
199             canvas->concat(imageGeomMat);
200             if (drawSubset) {
201                 canvas->drawImageRect(image, kSubsets[matIndex],
202                                       drawScaled ? SkRect::MakeWH(kImageSize, kImageSize)
203                                                  : kSubsets[matIndex],
204                                       nullptr, SkCanvas::kFast_SrcRectConstraint);
205             } else {
206                 canvas->drawImage(image, 0, 0);
207             }
208 
209             // draw the labels
210             for (int i = 0; i < kNumLabels; ++i) {
211                 canvas->drawImage(fLabels[i],
212                                     0.0f == kPoints[i].fX ? -kLabelSize : kPoints[i].fX,
213                                     0.0f == kPoints[i].fY ? -kLabelSize : kPoints[i].fY);
214             }
215         canvas->restore();
216     }
217 
drawRow(GrContext * context,SkCanvas * canvas,bool bottomLeftImage,bool drawSubset,bool drawScaled)218     void drawRow(GrContext* context, SkCanvas* canvas,
219                  bool bottomLeftImage, bool drawSubset, bool drawScaled) {
220 
221         sk_sp<SkImage> referenceImage = make_reference_image(context, fLabels, bottomLeftImage);
222 
223         canvas->save();
224             canvas->translate(kLabelSize, kLabelSize);
225 
226             for (int i = 0; i < kNumMatrices; ++i) {
227                 this->drawImageWithMatrixAndLabels(canvas, referenceImage.get(), i,
228                                                    drawSubset, drawScaled);
229                 canvas->translate(kCellSize, 0);
230             }
231         canvas->restore();
232     }
233 
makeLabels(GrContext * context)234     void makeLabels(GrContext* context) {
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         SkASSERT(!fLabels.count());
245         for (int i = 0; i < kNumLabels; ++i) {
246             fLabels.push_back(make_text_image(context, kLabelText[i], kLabelColors[i]));
247         }
248         SkASSERT(kNumLabels == fLabels.count());
249     }
250 
onDraw(SkCanvas * canvas)251     void onDraw(SkCanvas* canvas) override {
252         GrContext* context = canvas->getGrContext();
253         if (!context) {
254             skiagm::GM::DrawGpuOnlyMessage(canvas);
255             return;
256         }
257 
258         this->makeLabels(context);
259 
260         canvas->save();
261 
262         // Top row gets TL image
263         this->drawRow(context, canvas, false, false, false);
264 
265         canvas->translate(0, kCellSize);
266 
267         // Bottom row gets BL image
268         this->drawRow(context, canvas, true, false, false);
269 
270         canvas->translate(0, kCellSize);
271 
272         // Third row gets subsets of BL images
273         this->drawRow(context, canvas, true, true, false);
274 
275         canvas->translate(0, kCellSize);
276 
277         // Fourth row gets scaled subsets of BL images
278         this->drawRow(context, canvas, true, true, true);
279 
280         canvas->restore();
281 
282         // separator grid
283         for (int i = 0; i < 4; ++i) {
284             canvas->drawLine(0, i * kCellSize, kGMWidth, i * kCellSize, SkPaint());
285         }
286         for (int i = 0; i < kNumMatrices; ++i) {
287             canvas->drawLine(i * kCellSize, 0, i * kCellSize, kGMHeight, SkPaint());
288         }
289     }
290 
291 private:
292     SkTArray<sk_sp<SkImage>> fLabels;
293 
294     typedef GM INHERITED;
295 };
296 
297 DEF_GM(return new FlippityGM;)
298 
299 #endif
300