1 /* 2 * Copyright 2013 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 "SkBlurMask.h" 10 #include "SkBlurMaskFilter.h" 11 #include "SkCanvas.h" 12 #include "SkGradientShader.h" 13 #include "SkImage.h" 14 #include "SkTDArray.h" 15 #include "SkUtils.h" 16 17 #if SK_SUPPORT_GPU 18 #include "GrContext.h" 19 #include "GrContextOptions.h" 20 #include "SkGr.h" 21 #endif 22 23 /** Holds either a bitmap or image to be rendered and a rect that indicates what part of the bitmap 24 or image should be tested by the GM. The area outside of the rect is present to check 25 for bleed due to filtering/blurring. */ 26 struct TestPixels { 27 enum Type { 28 kBitmap, 29 kImage 30 }; 31 Type fType; 32 SkBitmap fBitmap; 33 sk_sp<SkImage> fImage; 34 SkIRect fRect; // The region of the bitmap/image that should be rendered. 35 }; 36 37 /** Creates a bitmap with two one-pixel rings around a checkerboard. The checkerboard is 2x2 38 logically where each check has as many pixels as is necessary to fill the interior. The rect 39 to draw is set to the checkerboard portion. */ 40 template<typename PIXEL_TYPE> 41 bool make_ringed_bitmap(TestPixels* result, int width, int height, 42 SkColorType ct, SkAlphaType at, 43 PIXEL_TYPE outerRingColor, PIXEL_TYPE innerRingColor, 44 PIXEL_TYPE checkColor1, PIXEL_TYPE checkColor2) { 45 SkASSERT(0 == width % 2 && 0 == height % 2); 46 SkASSERT(width >= 6 && height >= 6); 47 48 result->fType = TestPixels::kBitmap; 49 SkImageInfo info = SkImageInfo::Make(width, height, ct, at); 50 size_t rowBytes = SkAlign4(info.minRowBytes()); 51 result->fBitmap.allocPixels(info, rowBytes); 52 53 PIXEL_TYPE* scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, 0); 54 for (int x = 0; x < width; ++x) { 55 scanline[x] = outerRingColor; 56 } 57 scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, 1); 58 scanline[0] = outerRingColor; 59 for (int x = 1; x < width - 1; ++x) { 60 scanline[x] = innerRingColor; 61 } 62 scanline[width - 1] = outerRingColor; 63 64 for (int y = 2; y < height / 2; ++y) { 65 scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, y); 66 scanline[0] = outerRingColor; 67 scanline[1] = innerRingColor; 68 for (int x = 2; x < width / 2; ++x) { 69 scanline[x] = checkColor1; 70 } 71 for (int x = width / 2; x < width - 2; ++x) { 72 scanline[x] = checkColor2; 73 } 74 scanline[width - 2] = innerRingColor; 75 scanline[width - 1] = outerRingColor; 76 } 77 78 for (int y = height / 2; y < height - 2; ++y) { 79 scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, y); 80 scanline[0] = outerRingColor; 81 scanline[1] = innerRingColor; 82 for (int x = 2; x < width / 2; ++x) { 83 scanline[x] = checkColor2; 84 } 85 for (int x = width / 2; x < width - 2; ++x) { 86 scanline[x] = checkColor1; 87 } 88 scanline[width - 2] = innerRingColor; 89 scanline[width - 1] = outerRingColor; 90 } 91 92 scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, height - 2); 93 scanline[0] = outerRingColor; 94 for (int x = 1; x < width - 1; ++x) { 95 scanline[x] = innerRingColor; 96 } 97 scanline[width - 1] = outerRingColor; 98 99 scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, height - 1); 100 for (int x = 0; x < width; ++x) { 101 scanline[x] = outerRingColor; 102 } 103 result->fBitmap.setImmutable(); 104 result->fRect.set(2, 2, width - 2, height - 2); 105 return true; 106 } 107 108 /** Create a black and white checked bitmap with 2 1-pixel rings around the outside edge. 109 The inner ring is red and the outer ring is blue. */ 110 static bool make_ringed_color_bitmap(TestPixels* result, int width, int height) { 111 const SkPMColor kBlue = SkPreMultiplyColor(SK_ColorBLUE); 112 const SkPMColor kRed = SkPreMultiplyColor(SK_ColorRED); 113 const SkPMColor kBlack = SkPreMultiplyColor(SK_ColorBLACK); 114 const SkPMColor kWhite = SkPreMultiplyColor(SK_ColorWHITE); 115 return make_ringed_bitmap<SkPMColor>(result, width, height, kN32_SkColorType, 116 kPremul_SkAlphaType, kBlue, kRed, kBlack, kWhite); 117 } 118 119 /** Makes a alpha bitmap with 1 wide rect/ring of 0s, an inset of 1s, and the interior is a 2x2 120 checker board of 3/4 and 1/2. The inner checkers are large enough to fill the interior with 121 the 2x2 checker grid. */ 122 static bool make_ringed_alpha_bitmap(TestPixels* result, int width, int height) { 123 constexpr uint8_t kZero = 0x00; 124 constexpr uint8_t kHalf = 0x80; 125 constexpr uint8_t k3Q = 0xC0; 126 constexpr uint8_t kOne = 0xFF; 127 return make_ringed_bitmap<uint8_t>(result, width, height, kAlpha_8_SkColorType, 128 kPremul_SkAlphaType, kZero, kOne, k3Q, kHalf); 129 } 130 131 /** Helper to reuse above functions to produce images rather than bmps */ 132 static void bmp_to_image(TestPixels* result) { 133 SkASSERT(TestPixels::kBitmap == result->fType); 134 result->fImage = SkImage::MakeFromBitmap(result->fBitmap); 135 SkASSERT(result->fImage); 136 result->fType = TestPixels::kImage; 137 result->fBitmap.reset(); 138 } 139 140 /** Color image case. */ 141 bool make_ringed_color_image(TestPixels* result, int width, int height) { 142 if (make_ringed_color_bitmap(result, width, height)) { 143 bmp_to_image(result); 144 return true; 145 } 146 return false; 147 } 148 149 /** Alpha image case. */ 150 bool make_ringed_alpha_image(TestPixels* result, int width, int height) { 151 if (make_ringed_alpha_bitmap(result, width, height)) { 152 bmp_to_image(result); 153 return true; 154 } 155 return false; 156 } 157 158 static sk_sp<SkShader> make_shader() { 159 constexpr SkPoint pts[] = { {0, 0}, {20, 20} }; 160 constexpr SkColor colors[] = { SK_ColorGREEN, SK_ColorYELLOW }; 161 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode); 162 } 163 164 static sk_sp<SkShader> make_null_shader() { return nullptr; } 165 166 enum BleedTest { 167 kUseBitmap_BleedTest, 168 kUseImage_BleedTest, 169 kUseAlphaBitmap_BleedTest, 170 kUseAlphaImage_BleedTest, 171 kUseAlphaBitmapShader_BleedTest, 172 kUseAlphaImageShader_BleedTest, 173 }; 174 175 const struct { 176 const char* fName; 177 bool (*fPixelMaker)(TestPixels* result, int width, int height); 178 sk_sp<SkShader> (*fShaderMaker)(); 179 } gBleedRec[] = { 180 { "bleed", make_ringed_color_bitmap, make_null_shader }, 181 { "bleed_image", make_ringed_color_image, make_null_shader }, 182 { "bleed_alpha_bmp", make_ringed_alpha_bitmap, make_null_shader }, 183 { "bleed_alpha_image", make_ringed_alpha_image, make_null_shader }, 184 { "bleed_alpha_bmp_shader", make_ringed_alpha_bitmap, make_shader }, 185 { "bleed_alpha_image_shader", make_ringed_alpha_image, make_shader }, 186 }; 187 188 /** This GM exercises the behavior of the drawBitmapRect & drawImageRect calls. Specifically their 189 handling of : 190 - SrcRectConstraint(bleed vs.no - bleed) 191 - handling of the sub - region feature(area - of - interest) of drawBitmap* 192 - handling of 8888 vs. A8 (including presence of a shader in the A8 case). 193 In particular, we should never see the padding outside of an SkBitmap's sub - region (green for 194 8888, 1/4 for alpha). In some instances we can see the two outer rings outside of the area o 195 interest (i.e., the inner four checks) due to AA or filtering if allowed by the 196 SrcRectConstraint. 197 */ 198 class BleedGM : public skiagm::GM { 199 public: 200 BleedGM(BleedTest bt) : fBT(bt){} 201 202 protected: 203 204 SkString onShortName() override { 205 return SkString(gBleedRec[fBT].fName); 206 } 207 208 SkISize onISize() override { 209 return SkISize::Make(1200, 1080); 210 } 211 212 void drawPixels(SkCanvas* canvas, const TestPixels& pixels, const SkRect& src, 213 const SkRect& dst, const SkPaint* paint, 214 SkCanvas::SrcRectConstraint constraint) { 215 if (TestPixels::kBitmap == pixels.fType) { 216 canvas->drawBitmapRect(pixels.fBitmap, src, dst, paint, constraint); 217 } else { 218 canvas->drawImageRect(pixels.fImage.get(), src, dst, paint, constraint); 219 } 220 } 221 222 // Draw the area of interest of the small image 223 void drawCase1(SkCanvas* canvas, int transX, int transY, bool aa, 224 SkCanvas::SrcRectConstraint constraint, SkFilterQuality filter) { 225 226 SkRect src = SkRect::Make(fSmallTestPixels.fRect); 227 SkRect dst = SkRect::MakeXYWH(SkIntToScalar(transX), SkIntToScalar(transY), 228 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); 229 230 SkPaint paint; 231 paint.setFilterQuality(filter); 232 paint.setShader(fShader); 233 paint.setColor(SK_ColorBLUE); 234 paint.setAntiAlias(aa); 235 236 this->drawPixels(canvas, fSmallTestPixels, src, dst, &paint, constraint); 237 } 238 239 // Draw the area of interest of the large image 240 void drawCase2(SkCanvas* canvas, int transX, int transY, bool aa, 241 SkCanvas::SrcRectConstraint constraint, SkFilterQuality filter) { 242 SkRect src = SkRect::Make(fBigTestPixels.fRect); 243 SkRect dst = SkRect::MakeXYWH(SkIntToScalar(transX), SkIntToScalar(transY), 244 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); 245 246 SkPaint paint; 247 paint.setFilterQuality(filter); 248 paint.setShader(fShader); 249 paint.setColor(SK_ColorBLUE); 250 paint.setAntiAlias(aa); 251 252 this->drawPixels(canvas, fBigTestPixels, src, dst, &paint, constraint); 253 } 254 255 // Draw upper-left 1/4 of the area of interest of the large image 256 void drawCase3(SkCanvas* canvas, int transX, int transY, bool aa, 257 SkCanvas::SrcRectConstraint constraint, SkFilterQuality filter) { 258 SkRect src = SkRect::MakeXYWH(SkIntToScalar(fBigTestPixels.fRect.fLeft), 259 SkIntToScalar(fBigTestPixels.fRect.fTop), 260 fBigTestPixels.fRect.width()/2.f, 261 fBigTestPixels.fRect.height()/2.f); 262 SkRect dst = SkRect::MakeXYWH(SkIntToScalar(transX), SkIntToScalar(transY), 263 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); 264 265 SkPaint paint; 266 paint.setFilterQuality(filter); 267 paint.setShader(fShader); 268 paint.setColor(SK_ColorBLUE); 269 paint.setAntiAlias(aa); 270 271 this->drawPixels(canvas, fBigTestPixels, src, dst, &paint, constraint); 272 } 273 274 // Draw the area of interest of the small image with a normal blur 275 void drawCase4(SkCanvas* canvas, int transX, int transY, bool aa, 276 SkCanvas::SrcRectConstraint constraint, SkFilterQuality filter) { 277 SkRect src = SkRect::Make(fSmallTestPixels.fRect); 278 SkRect dst = SkRect::MakeXYWH(SkIntToScalar(transX), SkIntToScalar(transY), 279 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); 280 281 SkPaint paint; 282 paint.setFilterQuality(filter); 283 paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 284 SkBlurMask::ConvertRadiusToSigma(3))); 285 paint.setShader(fShader); 286 paint.setColor(SK_ColorBLUE); 287 paint.setAntiAlias(aa); 288 289 this->drawPixels(canvas, fSmallTestPixels, src, dst, &paint, constraint); 290 } 291 292 // Draw the area of interest of the small image with a outer blur 293 void drawCase5(SkCanvas* canvas, int transX, int transY, bool aa, 294 SkCanvas::SrcRectConstraint constraint, SkFilterQuality filter) { 295 SkRect src = SkRect::Make(fSmallTestPixels.fRect); 296 SkRect dst = SkRect::MakeXYWH(SkIntToScalar(transX), SkIntToScalar(transY), 297 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); 298 299 SkPaint paint; 300 paint.setFilterQuality(filter); 301 paint.setMaskFilter(SkBlurMaskFilter::Make(kOuter_SkBlurStyle, 302 SkBlurMask::ConvertRadiusToSigma(7))); 303 paint.setShader(fShader); 304 paint.setColor(SK_ColorBLUE); 305 paint.setAntiAlias(aa); 306 307 this->drawPixels(canvas, fSmallTestPixels, src, dst, &paint, constraint); 308 } 309 310 void onOnceBeforeDraw() override { 311 SkAssertResult(gBleedRec[fBT].fPixelMaker(&fSmallTestPixels, kSmallSize, kSmallSize)); 312 SkAssertResult(gBleedRec[fBT].fPixelMaker(&fBigTestPixels, 2 * kMaxTileSize, 313 2 * kMaxTileSize)); 314 } 315 316 void onDraw(SkCanvas* canvas) override { 317 fShader = gBleedRec[fBT].fShaderMaker(); 318 319 canvas->clear(SK_ColorGRAY); 320 SkTDArray<SkMatrix> matrices; 321 // Draw with identity 322 *matrices.append() = SkMatrix::I(); 323 324 // Draw with rotation and scale down in x, up in y. 325 SkMatrix m; 326 constexpr SkScalar kBottom = SkIntToScalar(kRow4Y + kBlockSize + kBlockSpacing); 327 m.setTranslate(0, kBottom); 328 m.preRotate(15.f, 0, kBottom + kBlockSpacing); 329 m.preScale(0.71f, 1.22f); 330 *matrices.append() = m; 331 332 // Align the next set with the middle of the previous in y, translated to the right in x. 333 SkPoint corners[] = {{0, 0}, { 0, kBottom }, { kWidth, kBottom }, {kWidth, 0} }; 334 matrices[matrices.count()-1].mapPoints(corners, 4); 335 SkScalar y = (corners[0].fY + corners[1].fY + corners[2].fY + corners[3].fY) / 4; 336 SkScalar x = SkTMax(SkTMax(corners[0].fX, corners[1].fX), 337 SkTMax(corners[2].fX, corners[3].fX)); 338 m.setTranslate(x, y); 339 m.preScale(0.2f, 0.2f); 340 *matrices.append() = m; 341 342 SkScalar maxX = 0; 343 for (int antiAlias = 0; antiAlias < 2; ++antiAlias) { 344 canvas->save(); 345 canvas->translate(maxX, 0); 346 for (int m = 0; m < matrices.count(); ++m) { 347 canvas->save(); 348 canvas->concat(matrices[m]); 349 bool aa = SkToBool(antiAlias); 350 351 // First draw a column with no bleeding and no filtering 352 this->drawCase1(canvas, kCol0X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); 353 this->drawCase2(canvas, kCol0X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); 354 this->drawCase3(canvas, kCol0X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); 355 this->drawCase4(canvas, kCol0X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); 356 this->drawCase5(canvas, kCol0X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); 357 358 // Then draw a column with no bleeding and low filtering 359 this->drawCase1(canvas, kCol1X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); 360 this->drawCase2(canvas, kCol1X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); 361 this->drawCase3(canvas, kCol1X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); 362 this->drawCase4(canvas, kCol1X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); 363 this->drawCase5(canvas, kCol1X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); 364 365 // Then draw a column with no bleeding and high filtering 366 this->drawCase1(canvas, kCol2X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); 367 this->drawCase2(canvas, kCol2X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); 368 this->drawCase3(canvas, kCol2X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); 369 this->drawCase4(canvas, kCol2X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); 370 this->drawCase5(canvas, kCol2X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); 371 372 // Then draw a column with bleeding and no filtering (bleed should have no effect w/out blur) 373 this->drawCase1(canvas, kCol3X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); 374 this->drawCase2(canvas, kCol3X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); 375 this->drawCase3(canvas, kCol3X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); 376 this->drawCase4(canvas, kCol3X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); 377 this->drawCase5(canvas, kCol3X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); 378 379 // Then draw a column with bleeding and low filtering 380 this->drawCase1(canvas, kCol4X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); 381 this->drawCase2(canvas, kCol4X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); 382 this->drawCase3(canvas, kCol4X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); 383 this->drawCase4(canvas, kCol4X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); 384 this->drawCase5(canvas, kCol4X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); 385 386 // Finally draw a column with bleeding and high filtering 387 this->drawCase1(canvas, kCol5X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); 388 this->drawCase2(canvas, kCol5X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); 389 this->drawCase3(canvas, kCol5X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); 390 this->drawCase4(canvas, kCol5X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); 391 this->drawCase5(canvas, kCol5X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); 392 393 SkPoint corners[] = { { 0, 0 },{ 0, kBottom },{ kWidth, kBottom },{ kWidth, 0 } }; 394 matrices[m].mapPoints(corners, 4); 395 SkScalar x = kBlockSize + SkTMax(SkTMax(corners[0].fX, corners[1].fX), 396 SkTMax(corners[2].fX, corners[3].fX)); 397 maxX = SkTMax(maxX, x); 398 canvas->restore(); 399 } 400 canvas->restore(); 401 } 402 } 403 404 #if SK_SUPPORT_GPU 405 void modifyGrContextOptions(GrContextOptions* options) override { 406 options->fMaxTileSizeOverride = kMaxTileSize; 407 } 408 #endif 409 410 private: 411 static constexpr int kBlockSize = 70; 412 static constexpr int kBlockSpacing = 12; 413 414 static constexpr int kCol0X = kBlockSpacing; 415 static constexpr int kCol1X = 2*kBlockSpacing + kBlockSize; 416 static constexpr int kCol2X = 3*kBlockSpacing + 2*kBlockSize; 417 static constexpr int kCol3X = 4*kBlockSpacing + 3*kBlockSize; 418 static constexpr int kCol4X = 5*kBlockSpacing + 4*kBlockSize; 419 static constexpr int kCol5X = 6*kBlockSpacing + 5*kBlockSize; 420 static constexpr int kWidth = 7*kBlockSpacing + 6*kBlockSize; 421 422 static constexpr int kRow0Y = kBlockSpacing; 423 static constexpr int kRow1Y = 2*kBlockSpacing + kBlockSize; 424 static constexpr int kRow2Y = 3*kBlockSpacing + 2*kBlockSize; 425 static constexpr int kRow3Y = 4*kBlockSpacing + 3*kBlockSize; 426 static constexpr int kRow4Y = 5*kBlockSpacing + 4*kBlockSize; 427 428 static constexpr int kSmallSize = 6; 429 static constexpr int kMaxTileSize = 32; 430 431 TestPixels fBigTestPixels; 432 TestPixels fSmallTestPixels; 433 434 sk_sp<SkShader> fShader; 435 436 const BleedTest fBT; 437 438 typedef GM INHERITED; 439 }; 440 441 442 DEF_GM( return new BleedGM(kUseBitmap_BleedTest); ) 443 DEF_GM( return new BleedGM(kUseImage_BleedTest); ) 444 DEF_GM( return new BleedGM(kUseAlphaBitmap_BleedTest); ) 445 DEF_GM( return new BleedGM(kUseAlphaImage_BleedTest); ) 446 DEF_GM( return new BleedGM(kUseAlphaBitmapShader_BleedTest); ) 447 DEF_GM( return new BleedGM(kUseAlphaImageShader_BleedTest); ) 448 449 /////////////////////////////////////////////////////////////////////////////////////////////////// 450 #include "SkSurface.h" 451 452 sk_sp<SkSurface> make_surface(SkCanvas* canvas, const SkImageInfo& info) { 453 auto surface = canvas->makeSurface(info); 454 if (!surface) { 455 surface = SkSurface::MakeRaster(info); 456 } 457 return surface; 458 } 459 460 // Construct an image and return the inner "src" rect. Build the image such that the interior is 461 // blue, with a margin of blue (2px) but then an outer margin of red. 462 // 463 // Show that kFast_SrcRectConstraint sees even the red margin (due to mipmapping) when the image 464 // is scaled down far enough. 465 // 466 static sk_sp<SkImage> make_image(SkCanvas* canvas, SkRect* srcR) { 467 // Intentially making the size a power of 2 to avoid the noise from how different GPUs will 468 // produce different mipmap filtering when we have an odd sized texture. 469 const int N = 10 + 2 + 8 + 2 + 10; 470 SkImageInfo info = SkImageInfo::MakeN32Premul(N, N); 471 auto surface = make_surface(canvas, info); 472 SkCanvas* c = surface->getCanvas(); 473 SkRect r = SkRect::MakeIWH(info.width(), info.height()); 474 SkPaint paint; 475 476 paint.setColor(SK_ColorRED); 477 c->drawRect(r, paint); 478 r.inset(10, 10); 479 paint.setColor(SK_ColorBLUE); 480 c->drawRect(r, paint); 481 482 *srcR = r.makeInset(2, 2); 483 return surface->makeImageSnapshot(); 484 } 485 486 DEF_SIMPLE_GM(bleed_downscale, canvas, 360, 240) { 487 SkRect src; 488 sk_sp<SkImage> img = make_image(canvas, &src); 489 SkPaint paint; 490 491 canvas->translate(10, 10); 492 493 const SkCanvas::SrcRectConstraint constraints[] = { 494 SkCanvas::kStrict_SrcRectConstraint, SkCanvas::kFast_SrcRectConstraint 495 }; 496 const SkFilterQuality qualities[] = { 497 kNone_SkFilterQuality, kLow_SkFilterQuality, kMedium_SkFilterQuality 498 }; 499 for (auto constraint : constraints) { 500 canvas->save(); 501 for (auto quality : qualities) { 502 paint.setFilterQuality(quality); 503 auto surf = make_surface(canvas, SkImageInfo::MakeN32Premul(1, 1)); 504 surf->getCanvas()->drawImageRect(img, src, SkRect::MakeWH(1, 1), &paint, constraint); 505 // now blow up the 1 pixel result 506 canvas->drawImageRect(surf->makeImageSnapshot(), SkRect::MakeWH(100, 100), nullptr); 507 canvas->translate(120, 0); 508 } 509 canvas->restore(); 510 canvas->translate(0, 120); 511 } 512 } 513 514 515