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