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