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