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". 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. 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. 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::GpuGM { 143 public: 144 FlippityGM() { 145 this->setBGColor(0xFFCCCCCC); 146 } 147 148 protected: 149 150 SkString onShortName() override { 151 return SkString("flippity"); 152 } 153 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 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 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 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 228 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override { 229 this->makeLabels(context); 230 231 canvas->save(); 232 233 // Top row gets TL image 234 this->drawRow(context, canvas, false, false, false); 235 236 canvas->translate(0, kCellSize); 237 238 // Bottom row gets BL image 239 this->drawRow(context, canvas, true, false, false); 240 241 canvas->translate(0, kCellSize); 242 243 // Third row gets subsets of BL images 244 this->drawRow(context, canvas, true, true, false); 245 246 canvas->translate(0, kCellSize); 247 248 // Fourth row gets scaled subsets of BL images 249 this->drawRow(context, canvas, true, true, true); 250 251 canvas->restore(); 252 253 // separator grid 254 for (int i = 0; i < 4; ++i) { 255 canvas->drawLine(0, i * kCellSize, kGMWidth, i * kCellSize, SkPaint()); 256 } 257 for (int i = 0; i < kNumMatrices; ++i) { 258 canvas->drawLine(i * kCellSize, 0, i * kCellSize, kGMHeight, SkPaint()); 259 } 260 } 261 262 private: 263 SkTArray<sk_sp<SkImage>> fLabels; 264 265 typedef GM INHERITED; 266 }; 267 268 DEF_GM(return new FlippityGM;) 269