Flutter Engine
The Flutter Engine
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),
60 text.setColor(SK_ColorDKGRAY);
61 y = print_size(canvas, "Target (in device)", SkIRect(outputBounds),
63 text.setColor(SK_ColorBLUE);
64 y = print_size(canvas, "Output (w/ hint)",
65 hintedOutputBounds ? SkIRect(*hintedOutputBounds) : std::optional<SkIRect>{},
67 text.setColor(SK_ColorGREEN);
68 y = print_size(canvas, "Input (w/ no hint)", SkIRect(unhintedLayerBounds),
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 };
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[]
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
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
void load(SkScalar w, SkScalar h) override
void draw(SkCanvas *canvas) override
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
SkMatrix getLocalToDeviceAs3x3() const
Definition: SkCanvas.h:2222
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition: SkCanvas.h:1543
void resetMatrix()
Definition: SkCanvas.cpp:1355
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 drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void setMatrix(const SkM44 &matrix)
Definition: SkCanvas.cpp:1349
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.h:1803
SkImageInfo imageInfo() const
Definition: SkCanvas.cpp:1206
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
Definition: SkFont.h:35
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)
Definition: SkMatrix.cpp:1786
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
void setImageFilter(sk_sp< SkImageFilter > imageFilter)
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
void setShader(sk_sp< SkShader > shader)
Definition: SkPath.h:59
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
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
Definition: color_source.cc:38
DlColor color
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< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
sk_sp< SkTypeface > DefaultTypeface()
sk_sp< SkImage > create_checkerboard_image(int w, int h, SkColor c1, SkColor c2, int checkSize)
Definition: ToolUtils.cpp:168
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
SkScalar w
SkScalar h
const Scalar scale
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
int width() const
Definition: SkImageInfo.h:365
int height() const
Definition: SkImageInfo.h:371
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