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