Flutter Engine
The Flutter Engine
bleed.cpp
Go to the documentation of this file.
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"
19#include "include/core/SkRect.h"
23#include "include/core/SkSize.h"
31#include "src/core/SkBlurMask.h"
32#include "tools/ToolUtils.h"
33
34#if defined(SK_GRAPHITE)
37#endif
38
39/** Creates an image with two one-pixel wide borders around a checkerboard. The checkerboard is 2x2
40 checks where each check has as many pixels as is necessary to fill the interior. It returns
41 the image and a src rect that bounds the checkerboard portion. */
42std::tuple<sk_sp<SkImage>, SkRect> make_ringed_image(SkCanvas* canvas, int width, int height) {
43
44 // These are kRGBA_8888_SkColorType values.
45 static constexpr uint32_t kOuterRingColor = 0xFFFF0000,
46 kInnerRingColor = 0xFF0000FF,
47 kCheckColor1 = 0xFF000000,
48 kCheckColor2 = 0xFFFFFFFF;
49
50 SkASSERT(0 == width % 2 && 0 == height % 2);
51 SkASSERT(width >= 6 && height >= 6);
52
55 size_t rowBytes = SkAlign4(info.minRowBytes());
57 bitmap.allocPixels(info, rowBytes);
58
59 uint32_t* scanline = bitmap.getAddr32(0, 0);
60 for (int x = 0; x < width; ++x) {
61 scanline[x] = kOuterRingColor;
62 }
63 scanline = bitmap.getAddr32(0, 1);
64 scanline[0] = kOuterRingColor;
65 for (int x = 1; x < width - 1; ++x) {
66 scanline[x] = kInnerRingColor;
67 }
68 scanline[width - 1] = kOuterRingColor;
69
70 for (int y = 2; y < height / 2; ++y) {
71 scanline = bitmap.getAddr32(0, y);
72 scanline[0] = kOuterRingColor;
73 scanline[1] = kInnerRingColor;
74 for (int x = 2; x < width / 2; ++x) {
75 scanline[x] = kCheckColor1;
76 }
77 for (int x = width / 2; x < width - 2; ++x) {
78 scanline[x] = kCheckColor2;
79 }
80 scanline[width - 2] = kInnerRingColor;
81 scanline[width - 1] = kOuterRingColor;
82 }
83
84 for (int y = height / 2; y < height - 2; ++y) {
85 scanline = bitmap.getAddr32(0, y);
86 scanline[0] = kOuterRingColor;
87 scanline[1] = kInnerRingColor;
88 for (int x = 2; x < width / 2; ++x) {
89 scanline[x] = kCheckColor2;
90 }
91 for (int x = width / 2; x < width - 2; ++x) {
92 scanline[x] = kCheckColor1;
93 }
94 scanline[width - 2] = kInnerRingColor;
95 scanline[width - 1] = kOuterRingColor;
96 }
97
98 scanline = bitmap.getAddr32(0, height - 2);
99 scanline[0] = kOuterRingColor;
100 for (int x = 1; x < width - 1; ++x) {
101 scanline[x] = kInnerRingColor;
102 }
103 scanline[width - 1] = kOuterRingColor;
104
105 scanline = bitmap.getAddr32(0, height - 1);
106 for (int x = 0; x < width; ++x) {
107 scanline[x] = kOuterRingColor;
108 }
109 bitmap.setImmutable();
110 return { bitmap.asImage(), SkRect::Make({2, 2, width - 2, height - 2})};
111}
112
113/**
114 * These GMs exercise the behavior of the drawImageRect and its SrcRectConstraint parameter. They
115 * tests various matrices, filter qualities, and interaction with mask filters. They also exercise
116 * the tiling image draws of SkGpuDevice by overriding the maximum texture size of the GrContext.
117 */
119public:
120 SrcRectConstraintGM(const char* shortName, SkCanvas::SrcRectConstraint constraint, bool manual)
121 : fShortName(shortName)
122 , fConstraint(constraint)
123 , fManual(manual) {
124 // Make sure GPU SkSurfaces can be created for this GM.
125 SkASSERT(this->getISize().width() <= kMaxTextureSize &&
126 this->getISize().height() <= kMaxTextureSize);
127 }
128
129protected:
130 SkString getName() const override { return fShortName; }
131 SkISize getISize() override { return SkISize::Make(800, 1000); }
132
133 void drawImage(SkCanvas* canvas, sk_sp<SkImage> image, SkRect srcRect, SkRect dstRect,
135 if (fManual) {
136 SkTiledImageUtils::DrawImageRect(canvas, image.get(), srcRect, dstRect,
137 sampling, paint, fConstraint);
138 } else {
139 canvas->drawImageRect(image.get(), srcRect, dstRect, sampling, paint, fConstraint);
140 }
141 }
142
143 // Draw the area of interest of the small image
144 void drawCase1(SkCanvas* canvas, int transX, int transY, bool aa,
147 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
148
150 paint.setColor(SK_ColorBLUE);
151 paint.setAntiAlias(aa);
152
153 this->drawImage(canvas, fSmallImage, fSmallSrcRect, dst, sampling, &paint);
154 }
155
156 // Draw the area of interest of the large image
157 void drawCase2(SkCanvas* canvas, int transX, int transY, bool aa,
160 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
161
163 paint.setColor(SK_ColorBLUE);
164 paint.setAntiAlias(aa);
165
166 this->drawImage(canvas, fBigImage, fBigSrcRect, dst, sampling, &paint);
167 }
168
169 // Draw upper-left 1/4 of the area of interest of the large image
170 void drawCase3(SkCanvas* canvas, int transX, int transY, bool aa,
172 SkRect src = SkRect::MakeXYWH(fBigSrcRect.fLeft,
173 fBigSrcRect.fTop,
174 fBigSrcRect.width()/2,
175 fBigSrcRect.height()/2);
177 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
178
180 paint.setColor(SK_ColorBLUE);
181 paint.setAntiAlias(aa);
182
183 this->drawImage(canvas, fBigImage, src, dst, sampling, &paint);
184 }
185
186 // Draw the area of interest of the small image with a normal blur
187 void drawCase4(SkCanvas* canvas, int transX, int transY, bool aa,
190 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
191
195 paint.setColor(SK_ColorBLUE);
196 paint.setAntiAlias(aa);
197
198 this->drawImage(canvas, fSmallImage, fSmallSrcRect, dst, sampling, &paint);
199 }
200
201 // Draw the area of interest of the small image with a outer blur
202 void drawCase5(SkCanvas* canvas, int transX, int transY, bool aa,
205 SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
206
210 paint.setColor(SK_ColorBLUE);
211 paint.setAntiAlias(aa);
212
213 this->drawImage(canvas, fSmallImage, fSmallSrcRect, dst, sampling, &paint);
214 }
215
216 void onDraw(SkCanvas* canvas) override {
217 if (!fSmallImage) {
218 std::tie(fBigImage, fBigSrcRect) = make_ringed_image(canvas,
219 2*kMaxTextureSize,
220 2*kMaxTextureSize);
221 std::tie(fSmallImage, fSmallSrcRect) = make_ringed_image(canvas,
222 kSmallSize, kSmallSize);
223 }
224
225 canvas->clear(SK_ColorGRAY);
226 std::vector<SkMatrix> matrices;
227 // Draw with identity
228 matrices.push_back(SkMatrix::I());
229
230 // Draw with rotation and scale down in x, up in y.
231 SkMatrix m;
232 constexpr SkScalar kBottom = SkIntToScalar(kRow4Y + kBlockSize + kBlockSpacing);
233 m.setTranslate(0, kBottom);
234 m.preRotate(15.f, 0, kBottom + kBlockSpacing);
235 m.preScale(0.71f, 1.22f);
236 matrices.push_back(m);
237
238 // Align the next set with the middle of the previous in y, translated to the right in x.
239 SkPoint corners[] = {{0, 0}, {0, kBottom}, {kWidth, kBottom}, {kWidth, 0}};
240 matrices.back().mapPoints(corners, 4);
241 m.setTranslate(std::max({corners[0].fX, corners[1].fX, corners[2].fX, corners[3].fX}),
242 (corners[0].fY + corners[1].fY + corners[2].fY + corners[3].fY) / 4);
243 m.preScale(0.2f, 0.2f);
244 matrices.push_back(m);
245
249
250 SkScalar maxX = 0;
251 for (bool antiAlias : {false, true}) {
252 canvas->save();
253 canvas->translate(maxX, 0);
254 for (const SkMatrix& matrix : matrices) {
255 canvas->save();
256 canvas->concat(matrix);
257
258 // First draw a column with no filtering
259 this->drawCase1(canvas, kCol0X, kRow0Y, antiAlias, none);
260 this->drawCase2(canvas, kCol0X, kRow1Y, antiAlias, none);
261 this->drawCase3(canvas, kCol0X, kRow2Y, antiAlias, none);
262 this->drawCase4(canvas, kCol0X, kRow3Y, antiAlias, none);
263 this->drawCase5(canvas, kCol0X, kRow4Y, antiAlias, none);
264
265 // Then draw a column with low filtering
266 this->drawCase1(canvas, kCol1X, kRow0Y, antiAlias, low);
267 this->drawCase2(canvas, kCol1X, kRow1Y, antiAlias, low);
268 this->drawCase3(canvas, kCol1X, kRow2Y, antiAlias, low);
269 this->drawCase4(canvas, kCol1X, kRow3Y, antiAlias, low);
270 this->drawCase5(canvas, kCol1X, kRow4Y, antiAlias, low);
271
272 // Then draw a column with high filtering. Skip it if in kStrict mode and MIP
273 // mapping will be used. On GPU we allow bleeding at non-base levels because
274 // building a new MIP chain for the subset is expensive.
275 SkScalar scales[2];
276 SkAssertResult(matrix.getMinMaxScales(scales));
277 if (fConstraint != SkCanvas::kStrict_SrcRectConstraint || scales[0] >= 1.f) {
278 this->drawCase1(canvas, kCol2X, kRow0Y, antiAlias, high);
279 this->drawCase2(canvas, kCol2X, kRow1Y, antiAlias, high);
280 this->drawCase3(canvas, kCol2X, kRow2Y, antiAlias, high);
281 this->drawCase4(canvas, kCol2X, kRow3Y, antiAlias, high);
282 this->drawCase5(canvas, kCol2X, kRow4Y, antiAlias, high);
283 }
284
285 SkPoint innerCorners[] = {{0, 0}, {0, kBottom}, {kWidth, kBottom}, {kWidth, 0}};
286 matrix.mapPoints(innerCorners, 4);
287 SkScalar x = kBlockSize + std::max({innerCorners[0].fX, innerCorners[1].fX,
288 innerCorners[2].fX, innerCorners[3].fX});
289 maxX = std::max(maxX, x);
290 canvas->restore();
291 }
292 canvas->restore();
293 }
294 }
295
297 options->fMaxTextureSizeOverride = kMaxTextureSize;
298 }
299
300#if defined(SK_GRAPHITE)
302 SkASSERT(options->fOptionsPriv);
303 options->fOptionsPriv->fMaxTextureSizeOverride = kMaxTextureSize;
304 }
305#endif
306
307private:
308 inline static constexpr int kBlockSize = 70;
309 inline static constexpr int kBlockSpacing = 12;
310
311 inline static constexpr int kCol0X = kBlockSpacing;
312 inline static constexpr int kCol1X = 2*kBlockSpacing + kBlockSize;
313 inline static constexpr int kCol2X = 3*kBlockSpacing + 2*kBlockSize;
314 inline static constexpr int kWidth = 4*kBlockSpacing + 3*kBlockSize;
315
316 inline static constexpr int kRow0Y = kBlockSpacing;
317 inline static constexpr int kRow1Y = 2*kBlockSpacing + kBlockSize;
318 inline static constexpr int kRow2Y = 3*kBlockSpacing + 2*kBlockSize;
319 inline static constexpr int kRow3Y = 4*kBlockSpacing + 3*kBlockSize;
320 inline static constexpr int kRow4Y = 5*kBlockSpacing + 4*kBlockSize;
321
322 inline static constexpr int kSmallSize = 6;
323 // This must be at least as large as the GM width and height so that a surface can be made, and
324 // a power-of-2 to account for any approx-fitting that the backend may perform.
325 inline static constexpr int kMaxTextureSize = 1024;
326
327 SkString fShortName;
328 sk_sp<SkImage> fBigImage;
329 sk_sp<SkImage> fSmallImage;
330 SkRect fBigSrcRect;
331 SkRect fSmallSrcRect;
332 SkCanvas::SrcRectConstraint fConstraint;
333 bool fManual;
334 using INHERITED = GM;
335};
336
337DEF_GM(return new SrcRectConstraintGM("strict_constraint_no_red_allowed",
339 /* manual= */ false););
340DEF_GM(return new SrcRectConstraintGM("strict_constraint_no_red_allowed_manual",
342 /* manual= */ true););
343
344DEF_GM(return new SrcRectConstraintGM("strict_constraint_batch_no_red_allowed",
346 /* manual= */ false););
347DEF_GM(return new SrcRectConstraintGM("strict_constraint_batch_no_red_allowed_manual",
349 /* manual= */ true););
350
351DEF_GM(return new SrcRectConstraintGM("fast_constraint_red_is_allowed",
353 /* manual= */ false););
354DEF_GM(return new SrcRectConstraintGM("fast_constraint_red_is_allowed_manual",
356 /* manual= */ true););
357
358///////////////////////////////////////////////////////////////////////////////////////////////////
359
360// Construct an image and return the inner "src" rect. Build the image such that the interior is
361// blue, with a margin of blue (2px) but then an outer margin of red.
362//
363// Show that kFast_SrcRectConstraint sees even the red margin (due to mipmapping) when the image
364// is scaled down far enough.
365//
366static sk_sp<SkImage> make_image(SkCanvas* canvas, SkRect* srcR) {
367 // Intentially making the size a power of 2 to avoid the noise from how different GPUs will
368 // produce different mipmap filtering when we have an odd sized texture.
369 const int N = 10 + 2 + 8 + 2 + 10;
371 auto surface = ToolUtils::makeSurface(canvas, info);
372 SkCanvas* c = surface->getCanvas();
373 SkRect r = SkRect::MakeIWH(info.width(), info.height());
375
376 paint.setColor(SK_ColorRED);
377 c->drawRect(r, paint);
378 r.inset(10, 10);
379 paint.setColor(SK_ColorBLUE);
380 c->drawRect(r, paint);
381
382 *srcR = r.makeInset(2, 2);
383 return surface->makeImageSnapshot();
384}
385
386DEF_SIMPLE_GM(bleed_downscale, canvas, 360, 240) {
387 SkRect src;
388 sk_sp<SkImage> img = make_image(canvas, &src);
390
391 canvas->translate(10, 10);
392
393 const SkCanvas::SrcRectConstraint constraints[] = {
395 };
396 const SkSamplingOptions samplings[] = {
400 };
401 for (auto constraint : constraints) {
402 canvas->save();
403 for (auto sampling : samplings) {
404 auto surf = ToolUtils::makeSurface(canvas, SkImageInfo::MakeN32Premul(1, 1));
405 surf->getCanvas()->drawImageRect(img, src, SkRect::MakeWH(1, 1), sampling,
406 nullptr, constraint);
407 // now blow up the 1 pixel result
408 canvas->drawImageRect(surf->makeImageSnapshot(), SkRect::MakeWH(100, 100),
410 canvas->translate(120, 0);
411 }
412 canvas->restore();
413 canvas->translate(0, 120);
414 }
415}
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static constexpr T SkAlign4(T x)
Definition: SkAlign.h:16
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kOuter_SkBlurStyle
nothing inside, fuzzy outside
Definition: SkBlurTypes.h:14
@ kNormal_SkBlurStyle
fuzzy inside and outside
Definition: SkBlurTypes.h:12
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
constexpr SkColor SK_ColorGRAY
Definition: SkColor.h:113
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define N
Definition: beziers.cpp:19
DEF_SIMPLE_GM(bleed_downscale, canvas, 360, 240)
Definition: bleed.cpp:386
std::tuple< sk_sp< SkImage >, SkRect > make_ringed_image(SkCanvas *canvas, int width, int height)
Definition: bleed.cpp:42
DEF_GM(return new SrcRectConstraintGM("strict_constraint_no_red_allowed", SkCanvas::kStrict_SrcRectConstraint, false);)
static sk_sp< SkImage > make_image(SkCanvas *canvas, SkRect *srcR)
Definition: bleed.cpp:366
static SkScalar SK_SPI ConvertRadiusToSigma(SkScalar radius)
Definition: SkBlurMask.cpp:39
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
SrcRectConstraint
Definition: SkCanvas.h:1541
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition: SkCanvas.h:1542
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition: SkCanvas.h:1543
void clear(SkColor color)
Definition: SkCanvas.h:1199
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
Definition: SkCanvas.cpp:2333
int save()
Definition: SkCanvas.cpp:447
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
static sk_sp< SkMaskFilter > MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM=true)
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
void drawImage(SkCanvas *canvas, sk_sp< SkImage > image, SkRect srcRect, SkRect dstRect, const SkSamplingOptions &sampling, SkPaint *paint)
Definition: bleed.cpp:133
SrcRectConstraintGM(const char *shortName, SkCanvas::SrcRectConstraint constraint, bool manual)
Definition: bleed.cpp:120
void drawCase4(SkCanvas *canvas, int transX, int transY, bool aa, const SkSamplingOptions &sampling)
Definition: bleed.cpp:187
void drawCase5(SkCanvas *canvas, int transX, int transY, bool aa, const SkSamplingOptions &sampling)
Definition: bleed.cpp:202
void drawCase1(SkCanvas *canvas, int transX, int transY, bool aa, const SkSamplingOptions &sampling)
Definition: bleed.cpp:144
void onDraw(SkCanvas *canvas) override
Definition: bleed.cpp:216
void drawCase3(SkCanvas *canvas, int transX, int transY, bool aa, const SkSamplingOptions &sampling)
Definition: bleed.cpp:170
SkString getName() const override
Definition: bleed.cpp:130
void modifyGrContextOptions(GrContextOptions *options) override
Definition: bleed.cpp:296
void drawCase2(SkCanvas *canvas, int transX, int transY, bool aa, const SkSamplingOptions &sampling)
Definition: bleed.cpp:157
SkISize getISize() override
Definition: bleed.cpp:131
T * get() const
Definition: SkRefCnt.h:303
Definition: gm.h:110
GM(SkColor backgroundColor=SK_ColorWHITE)
Definition: gm.cpp:81
virtual void modifyGraphiteContextOptions(skgpu::graphite::ContextOptions *) const
Definition: gm.h:179
SkScalar width()
Definition: gm.h:159
SkScalar height()
Definition: gm.h:162
const Paint & paint
Definition: color_source.cc:38
VkSurfaceKHR surface
Definition: main.cc:49
float SkScalar
Definition: extension.cpp:12
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SkSamplingOptions sampling
Definition: SkRecords.h:337
SK_API void DrawImageRect(SkCanvas *canvas, const SkImage *image, const SkRect &src, const SkRect &dst, const SkSamplingOptions &sampling={}, const SkPaint *paint=nullptr, SkCanvas::SrcRectConstraint constraint=SkCanvas::kFast_SrcRectConstraint)
sk_sp< SkSurface > makeSurface(SkCanvas *canvas, const SkImageInfo &info, const SkSurfaceProps *props)
Definition: ToolUtils.cpp:512
Definition: bitmap.py:1
dst
Definition: cp.py:12
SkSamplingOptions(SkFilterMode::kLinear))
int32_t height
int32_t width
static constexpr SkCubicResampler Mitchell()
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
float fX
x-axis value
Definition: SkPoint_impl.h:164
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
void inset(float dx, float dy)
Definition: SkRect.h:1060
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
SkRect makeInset(float dx, float dy) const
Definition: SkRect.h:987
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15