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