Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FilterBoundsSlide.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 Google LLC
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
11#include "include/core/SkFont.h"
13#include "include/core/SkPath.h"
16#include "include/core/SkRect.h"
23#include "src/core/SkRectPriv.h"
24#include "tools/ToolUtils.h"
26#include "tools/viewer/Slide.h"
27
28static constexpr float kLineHeight = 16.f;
29static constexpr float kLineInset = 8.f;
30
31static float print_size(SkCanvas* canvas, const char* prefix,
32 std::optional<SkIRect> rect,
33 float x, float y, const SkFont& font, const SkPaint& paint) {
34 canvas->drawString(prefix, x, y, font, paint);
35 y += kLineHeight;
36 SkString sz;
37 if (rect) {
38 sz.appendf("%d x %d", rect->width(), rect->height());
39 } else {
40 sz.appendf("infinite");
41 }
42 canvas->drawString(sz, x, y, font, paint);
43 return y + kLineHeight;
44}
45
46static float print_info(SkCanvas* canvas,
47 const skif::LayerSpace<SkIRect>& layerContentBounds,
48 const skif::DeviceSpace<SkIRect>& outputBounds,
49 std::optional<skif::DeviceSpace<SkIRect>> hintedOutputBounds,
50 const skif::LayerSpace<SkIRect>& unhintedLayerBounds) {
53 text.setAntiAlias(true);
54
55 float y = kLineHeight;
56
57 text.setColor(SK_ColorRED);
58 y = print_size(canvas, "Content (in layer)", SkIRect(layerContentBounds),
59 kLineInset, y, font, text);
60 text.setColor(SK_ColorDKGRAY);
61 y = print_size(canvas, "Target (in device)", SkIRect(outputBounds),
62 kLineInset, y, font, text);
63 text.setColor(SK_ColorBLUE);
64 y = print_size(canvas, "Output (w/ hint)",
65 hintedOutputBounds ? SkIRect(*hintedOutputBounds) : std::optional<SkIRect>{},
66 kLineInset, y, font, text);
67 text.setColor(SK_ColorGREEN);
68 y = print_size(canvas, "Input (w/ no hint)", SkIRect(unhintedLayerBounds),
69 kLineInset, y, font, text);
70
71 return y;
72}
73
74static void print_label(SkCanvas* canvas, float x, float y, float value) {
77 text.setAntiAlias(true);
78
79 SkString label;
80 label.printf("%.3f", value);
81
82 canvas->drawString(label, x, y + kLineHeight / 2.f, font, text);
83}
84
85static SkPaint line_paint(SkColor color, bool dashed = false) {
87 paint.setColor(color);
88 paint.setStrokeWidth(0.f);
90 paint.setAntiAlias(true);
91 if (dashed) {
92 SkScalar dash[2] = {10.f, 10.f};
93 paint.setPathEffect(SkDashPathEffect::Make(dash, 2, 0.f));
94 }
95 return paint;
96}
97
98static SkPath create_axis_path(const SkRect& rect, float axisSpace) {
99 SkPath localSpace;
100 for (float y = rect.fTop + axisSpace; y <= rect.fBottom; y += axisSpace) {
101 localSpace.moveTo(rect.fLeft, y);
102 localSpace.lineTo(rect.fRight, y);
103 }
104 for (float x = rect.fLeft + axisSpace; x <= rect.fRight; x += axisSpace) {
105 localSpace.moveTo(x, rect.fTop);
106 localSpace.lineTo(x, rect.fBottom);
107 }
108 return localSpace;
109}
110
112 { { 0.05f, 0.0f, 6.f, 1.f }, // Severe downscaling, s < 1/8, log(s) < -3
113 { 0.6f, 0.6f, 0.8f, 0.6f }, // Okay downscaling, s < 1/2, log(s) < -1
114 { 1.f, 1.f, 1.f, 0.2f }, // No scaling, s = 1, log(s) = 0
115 { 0.95f, 0.6f, 0.5f, 0.6f }, // Okay upscaling, s > 2, log(s) > 1
116 { 0.8f, 0.1f, 0.f, 1.f } }; // Severe upscaling, s > 8, log(s) > 3
117static const SkScalar kLogScaleFactors[] = { -3.f, -1.f, 0.f, 1.f, 3.f };
118static const SkScalar kGradientStops[] = { 0.f, 0.33333f, 0.5f, 0.66667f, 1.f };
119static const int kStopCount = (int) std::size(kScaleGradientColors);
120
121static void draw_scale_key(SkCanvas* canvas, float y) {
122 SkRect key = SkRect::MakeXYWH(15.f, y + 30.f, 15.f, 100.f);
123 SkPoint pts[] = {{key.centerX(), key.fTop}, {key.centerX(), key.fBottom}};
127 SkPaint keyPaint;
128 keyPaint.setShader(gradient);
129 canvas->drawRect(key, keyPaint);
130 for (int i = 0; i < kStopCount; ++i) {
131 print_label(canvas, key.fRight + 5.f, key.fTop + kGradientStops[i] * key.height(),
133 }
134}
135
136static void draw_scale_factors(SkCanvas* canvas, const skif::Mapping& mapping, const SkRect& rect) {
137 SkPoint testPoints[5];
138 testPoints[0] = {rect.centerX(), rect.centerY()};
139 rect.toQuad(testPoints + 1);
140 for (int i = 0; i < 5; ++i) {
142 mapping.layerToDevice(),
143 SkPoint(mapping.paramToLayer(skif::ParameterSpace<SkPoint>(testPoints[i]))));
144 SkColor4f color = {0.f, 0.f, 0.f, 1.f};
145
146 if (SkIsFinite(scale)) {
147 float logScale = SkScalarLog2(scale);
148 for (int j = 0; j <= kStopCount; ++j) {
149 if (j == kStopCount) {
151 break;
152 } else if (kLogScaleFactors[j] >= logScale) {
153 if (j == 0) {
155 } else {
156 SkScalar t = (logScale - kLogScaleFactors[j - 1]) /
158
159 SkColor4f a = kScaleGradientColors[j - 1] * (1.f - t);
161 color = {a.fR + b.fR, a.fG + b.fG, a.fB + b.fB, a.fA + b.fA};
162 }
163 break;
164 }
165 }
166 }
167
168 SkPaint p;
169 p.setAntiAlias(true);
170 p.setColor4f(color, nullptr);
171 canvas->drawRect(SkRect::MakeLTRB(testPoints[i].fX - 4.f, testPoints[i].fY - 4.f,
172 testPoints[i].fX + 4.f, testPoints[i].fY + 4.f), p);
173 }
174}
175
176class FilterBoundsSample : public Slide {
177public:
178 FilterBoundsSample() { fName = "FilterBounds"; }
179
180 void load(SkScalar w, SkScalar h) override {
181 fBlur = SkImageFilters::Blur(8.f, 8.f, nullptr);
183 300, 300, SK_ColorMAGENTA, SK_ColorLTGRAY, 50);
184 }
185
186 void draw(SkCanvas* canvas) override {
187 // The local content, e.g. what would be submitted to drawRect or the bounds to saveLayer
188 const SkRect localContentRect = SkRect::MakeLTRB(100.f, 20.f, 180.f, 140.f);
189 SkMatrix ctm = canvas->getLocalToDeviceAs3x3();
190
191 // Base rendering of a filter
192 SkPaint blurPaint;
193 blurPaint.setImageFilter(fBlur);
194 canvas->saveLayer(&localContentRect, &blurPaint);
195 canvas->drawImageRect(fImage.get(), localContentRect, localContentRect,
198 canvas->restore();
199
200 // Now visualize the underlying bounds calculations used to determine the layer for the blur
201 SkIRect target = ctm.mapRect(localContentRect).roundOut();
202 if (!target.intersect(SkIRect::MakeWH(canvas->imageInfo().width(),
203 canvas->imageInfo().height()))) {
204 return;
205 }
207 skif::ParameterSpace<SkRect> contentBounds(localContentRect);
208 skif::ParameterSpace<SkPoint> contentCenter({localContentRect.centerX(),
209 localContentRect.centerY()});
210 skif::Mapping mapping;
211 SkAssertResult(mapping.decomposeCTM(ctm, fBlur.get(), contentCenter));
212
213 // Add axis lines, to show perspective distortion
214 canvas->save();
215 canvas->setMatrix(mapping.layerToDevice());
216 canvas->drawPath(create_axis_path(SkRect(mapping.paramToLayer(contentBounds)), 20.f),
218 canvas->restore();
219
220 // Visualize scale factors at the four corners and center of the local rect
221 draw_scale_factors(canvas, mapping, localContentRect);
222
223 // The device content rect, e.g. the clip bounds if 'localContentRect' were used as a clip
224 // before the draw or saveLayer, representing what the filter must cover if it affects
225 // transparent black or doesn't have a local content hint.
226 canvas->setMatrix(SkMatrix::I());
227 canvas->drawRect(ctm.mapRect(localContentRect), line_paint(SK_ColorDKGRAY));
228
229 // Layer bounds for the filter, in the layer space compatible with the filter's matrix
230 // type requirements.
231 skif::LayerSpace<SkIRect> targetOutputInLayer = mapping.deviceToLayer(targetOutput);
232 skif::LayerSpace<SkIRect> hintedLayerBounds = as_IFB(fBlur)->getInputBounds(
233 mapping, targetOutput, contentBounds);
234 skif::LayerSpace<SkIRect> unhintedLayerBounds = as_IFB(fBlur)->getInputBounds(
235 mapping, targetOutput, {});
236
237 canvas->setMatrix(mapping.layerToDevice());
238 canvas->drawRect(SkRect::Make(SkIRect(targetOutputInLayer)),
240 canvas->drawRect(SkRect::Make(SkIRect(hintedLayerBounds)), line_paint(SK_ColorRED));
241 canvas->drawRect(SkRect::Make(SkIRect(unhintedLayerBounds)), line_paint(SK_ColorGREEN));
242
243 // For visualization purposes, we want to show the layer-space output, this is what we get
244 // when contentBounds is provided as a hint in local/parameter space.
245 skif::Mapping layerOnly{mapping.layerMatrix()};
246 std::optional<skif::DeviceSpace<SkIRect>> hintedOutputBounds =
247 as_IFB(fBlur)->getOutputBounds(layerOnly, contentBounds);
248 if (hintedOutputBounds) {
249 canvas->drawRect(SkRect::Make(SkIRect(*hintedOutputBounds)), line_paint(SK_ColorBLUE));
250 }
251
252 canvas->resetMatrix();
253 float y = print_info(canvas,
254 mapping.paramToLayer(contentBounds).roundOut(),
255 targetOutput,
256 hintedOutputBounds,
257 unhintedLayerBounds);
258
259 // Draw color key for layer visualization
260 draw_scale_key(canvas, y);
261 }
262
263private:
265 sk_sp<SkImage> fImage;
266};
267
268DEF_SLIDE(return new FilterBoundsSample();)
static const int kStopCount
static float print_size(SkCanvas *canvas, const char *prefix, std::optional< SkIRect > rect, float x, float y, const SkFont &font, const SkPaint &paint)
static const SkColor4f kScaleGradientColors[]
static constexpr float kLineHeight
static void print_label(SkCanvas *canvas, float x, float y, float value)
static const SkScalar kGradientStops[]
static SkPaint line_paint(SkColor color, bool dashed=false)
static SkPath create_axis_path(const SkRect &rect, float axisSpace)
static void draw_scale_key(SkCanvas *canvas, float y)
static void draw_scale_factors(SkCanvas *canvas, const skif::Mapping &mapping, const SkRect &rect)
static float print_info(SkCanvas *canvas, const skif::LayerSpace< SkIRect > &layerContentBounds, const skif::DeviceSpace< SkIRect > &outputBounds, std::optional< skif::DeviceSpace< SkIRect > > hintedOutputBounds, const skif::LayerSpace< SkIRect > &unhintedLayerBounds)
static constexpr float kLineInset
static const SkScalar kLogScaleFactors[]
SkColor4f color
#define SkAssertResult(cond)
Definition SkAssert.h:123
constexpr SkColor SK_ColorLTGRAY
Definition SkColor.h:118
constexpr SkColor SK_ColorMAGENTA
Definition SkColor.h:147
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorGRAY
Definition SkColor.h:113
constexpr SkColor SK_ColorBLUE
Definition SkColor.h:135
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
constexpr SkColor SK_ColorDKGRAY
Definition SkColor.h:108
static bool SkIsFinite(T x, Pack... values)
static SkImageFilter_Base * as_IFB(SkImageFilter *filter)
#define SkScalarPow(b, e)
Definition SkScalar.h:43
#define SkScalarLog2(x)
Definition SkScalar.h:53
#define DEF_SLIDE(code)
Definition Slide.h:25
Type::kYUV Type::kRGBA() int(0.7 *637)
void load(SkScalar w, SkScalar h) override
void draw(SkCanvas *canvas) override
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition SkCanvas.cpp:500
void drawRect(const SkRect &rect, const SkPaint &paint)
void restore()
Definition SkCanvas.cpp:465
SkMatrix getLocalToDeviceAs3x3() const
Definition SkCanvas.h:2222
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition SkCanvas.h:1543
void resetMatrix()
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
void setMatrix(const SkM44 &matrix)
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition SkCanvas.h:1803
SkImageInfo imageInfo() const
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
std::optional< skif::DeviceSpace< SkIRect > > getOutputBounds(const skif::Mapping &mapping, const skif::ParameterSpace< SkRect > &contentBounds) const
skif::LayerSpace< SkIRect > getInputBounds(const skif::Mapping &mapping, const skif::DeviceSpace< SkIRect > &desiredOutput, std::optional< skif::ParameterSpace< SkRect > > knownContentBounds) const
static sk_sp< SkImageFilter > Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static SkScalar DifferentialAreaScale(const SkMatrix &m, const SkPoint &p)
static const SkMatrix & I()
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
void setAntiAlias(bool aa)
Definition SkPaint.h:170
void setImageFilter(sk_sp< SkImageFilter > imageFilter)
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
void setShader(sk_sp< SkShader > shader)
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
Definition Slide.h:29
SkString fName
Definition Slide.h:54
T * get() const
Definition SkRefCnt.h:303
const SkMatrix & layerToDevice() const
LayerSpace< T > paramToLayer(const ParameterSpace< T > &paramGeometry) const
const Paint & paint
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
uint8_t value
uint32_t * target
std::u16string text
double y
double x
sk_sp< SkTypeface > DefaultTypeface()
sk_sp< SkImage > create_checkerboard_image(int w, int h, SkColor c1, SkColor c2, int checkSize)
SkScalar w
SkScalar h
const Scalar scale
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
int width() const
int height() const
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
constexpr float centerX() const
Definition SkRect.h:776
constexpr float centerY() const
Definition SkRect.h:785
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646