Home | History | Annotate | Download | only in gm
      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