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