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>
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)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. */
make_ringed_color_bitmap(TestPixels * result,int width,int height)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. */
make_ringed_alpha_bitmap(TestPixels * result,int width,int height)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 */
bmp_to_image(TestPixels * result)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. */
make_ringed_color_image(TestPixels * result,int width,int height)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. */
make_ringed_alpha_image(TestPixels * result,int width,int height)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
make_shader()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
make_null_shader()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:
BleedGM(BleedTest bt)201 BleedGM(BleedTest bt) : fBT(bt){}
202
203 protected:
204
onShortName()205 SkString onShortName() override {
206 return SkString(gBleedRec[fBT].fName);
207 }
208
onISize()209 SkISize onISize() override {
210 return SkISize::Make(1200, 1080);
211 }
212
drawPixels(SkCanvas * canvas,const TestPixels & pixels,const SkRect & src,const SkRect & dst,const SkPaint * paint,SkCanvas::SrcRectConstraint constraint)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
drawCase1(SkCanvas * canvas,int transX,int transY,bool aa,SkCanvas::SrcRectConstraint constraint,SkFilterQuality filter)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
drawCase2(SkCanvas * canvas,int transX,int transY,bool aa,SkCanvas::SrcRectConstraint constraint,SkFilterQuality filter)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
drawCase3(SkCanvas * canvas,int transX,int transY,bool aa,SkCanvas::SrcRectConstraint constraint,SkFilterQuality filter)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
drawCase4(SkCanvas * canvas,int transX,int transY,bool aa,SkCanvas::SrcRectConstraint constraint,SkFilterQuality filter)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
drawCase5(SkCanvas * canvas,int transX,int transY,bool aa,SkCanvas::SrcRectConstraint constraint,SkFilterQuality filter)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
onOnceBeforeDraw()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
onDraw(SkCanvas * canvas)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
modifyGrContextOptions(GrContextOptions * options)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); )
DEF_GM(return new BleedGM (kUseImage_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