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". 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 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. 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. 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: 167 FlippityGM() { 168 this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); 169 } 170 171 protected: 172 173 SkString onShortName() override { 174 return SkString("flippity"); 175 } 176 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 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 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 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 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