Flutter Engine
The Flutter Engine
SkImageFilter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2012 The Android Open Source Project
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
9
15#include "include/core/SkRect.h"
25#include "src/core/SkRectPriv.h"
30
31#include <algorithm>
32#include <atomic>
33#include <cstdint>
34#include <optional>
35#include <utility>
36
37///////////////////////////////////////////////////////////////////////////////////////////////////
38// SkImageFilter - A number of the public APIs on SkImageFilter downcast to SkImageFilter_Base
39// in order to perform their actual work.
40///////////////////////////////////////////////////////////////////////////////////////////////////
41
42/**
43 * Returns the number of inputs this filter will accept (some inputs can
44 * be NULL).
45 */
46int SkImageFilter::countInputs() const { return as_IFB(this)->fInputs.count(); }
47
48/**
49 * Returns the input filter at a given index, or NULL if no input is
50 * connected. The indices used are filter-specific.
51 */
53 SkASSERT(i < this->countInputs());
54 return as_IFB(this)->fInputs[i].get();
55}
56
58 return as_IFB(this)->onIsColorFilterNode(filterPtr);
59}
60
62 MapDirection direction, const SkIRect* inputRect) const {
63 // The old filterBounds() function uses SkIRects that are defined in layer space so, while
64 // we still are supporting it, bypass SkIF_B's new public filter bounds functions and go right
65 // to the internal layer-space calculations.
66 skif::Mapping mapping{ctm};
67 if (kReverse_MapDirection == direction) {
68 skif::LayerSpace<SkIRect> targetOutput(src);
69 std::optional<skif::LayerSpace<SkIRect>> content;
70 if (inputRect) {
72 }
73 return SkIRect(as_IFB(this)->onGetInputLayerBounds(mapping, targetOutput, content));
74 } else {
75 SkASSERT(!inputRect);
77 auto output = as_IFB(this)->onGetOutputLayerBounds(mapping, content);
79 }
80}
81
83 if (0 == this->countInputs()) {
84 return src;
85 }
86 SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
87 for (int i = 1; i < this->countInputs(); i++) {
88 const SkImageFilter* input = this->getInput(i);
89 if (input) {
90 combinedBounds.join(input->computeFastBounds(src));
91 } else {
92 combinedBounds.join(src);
93 }
94 }
95 return combinedBounds;
96}
97
99 return !as_IFB(this)->affectsTransparentBlack();
100}
101
103 if (this->onAffectsTransparentBlack()) {
104 return true;
105 } else if (this->ignoreInputsAffectsTransparentBlack()) {
106 // TODO(skbug.com/14611): Automatically infer this from output bounds being finite
107 return false;
108 }
109 for (int i = 0; i < this->countInputs(); i++) {
110 const SkImageFilter* input = this->getInput(i);
111 if (input && as_IFB(input)->affectsTransparentBlack()) {
112 return true;
113 }
114 }
115 return false;
116}
117
119 SkASSERT(nullptr != filterPtr);
120 if (!this->isColorFilterNode(filterPtr)) {
121 return false;
122 }
123 if (nullptr != this->getInput(0) || as_CFB(*filterPtr)->affectsTransparentBlack()) {
124 (*filterPtr)->unref();
125 return false;
126 }
127 return true;
128}
129
132}
133
134///////////////////////////////////////////////////////////////////////////////////////////////////
135// SkImageFilter_Base
136///////////////////////////////////////////////////////////////////////////////////////////////////
137
139 static std::atomic<int32_t> nextID{1};
140
141 int32_t id;
142 do {
143 id = nextID.fetch_add(1, std::memory_order_relaxed);
144 } while (id == 0);
145 return id;
146}
147
149 int inputCount,
150 std::optional<bool> usesSrc)
151 : fUsesSrcInput(usesSrc.has_value() ? *usesSrc : false)
152 , fUniqueID(next_image_filter_unique_id()) {
153 fInputs.reset(inputCount);
154
155 for (int i = 0; i < inputCount; ++i) {
156 if (!usesSrc.has_value() && (!inputs[i] || as_IFB(inputs[i])->usesSource())) {
157 fUsesSrcInput = true;
158 }
159 fInputs[i] = inputs[i];
160 }
161}
162
165}
166
167std::pair<sk_sp<SkImageFilter>, std::optional<SkRect>>
170 if (!common.unflatten(buffer, 1)) {
171 return {nullptr, std::nullopt};
172 } else {
173 return {common.getInput(0), common.cropRect()};
174 }
175}
176
178 const int count = buffer.readInt();
179 if (!buffer.validate(count >= 0)) {
180 return false;
181 }
182 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
183 return false;
184 }
185
186#if defined(SK_BUILD_FOR_FUZZER)
187 if (count > 4) {
188 return false;
189 }
190#endif
191
192 SkASSERT(fInputs.empty());
193 for (int i = 0; i < count; i++) {
194 fInputs.push_back(buffer.readBool() ? buffer.readImageFilter() : nullptr);
195 if (!buffer.isValid()) {
196 return false;
197 }
198 }
199
201 static constexpr uint32_t kHasAll_CropEdge = 0x0F;
202 SkRect rect;
203 buffer.readRect(&rect);
204 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
205 return false;
206 }
207
208 uint32_t flags = buffer.readUInt();
209 if (!buffer.isValid() ||
210 !buffer.validate(flags == 0x0 || flags == kHasAll_CropEdge)) {
211 return false;
212 }
213 if (flags == kHasAll_CropEdge) {
214 fCropRect = rect;
215 }
216 }
217 return buffer.isValid();
218}
219
221 buffer.writeInt(fInputs.count());
222 for (int i = 0; i < fInputs.count(); i++) {
223 const SkImageFilter* input = this->getInput(i);
224 buffer.writeBool(input != nullptr);
225 if (input != nullptr) {
226 buffer.writeFlattenable(input);
227 }
228 }
229}
230
232 context.markVisitedImageFilter();
233
235 if (context.desiredOutput().isEmpty() || !context.mapping().layerMatrix().isFinite()) {
236 return result;
237 }
238
239 // Some image filters that operate on the source image still affect transparent black, so if
240 // there is clipping, we may have optimized away the source image as an empty input, but still
241 // need to run the filter on it. This means `fUsesSrcInput` is not equivalent to the source
242 // being non-null.
243 const bool srcInKey = fUsesSrcInput && context.source();
244 uint32_t srcGenID = srcInKey ? context.source().image()->uniqueID() : SK_InvalidUniqueID;
245 const SkIRect srcSubset = srcInKey ? context.source().image()->subset() : SkIRect::MakeWH(0, 0);
246
247 SkImageFilterCacheKey key(fUniqueID,
248 context.mapping().layerMatrix(),
249 SkIRect(context.desiredOutput()),
250 srcGenID, srcSubset);
251 if (context.backend()->cache() && context.backend()->cache()->get(key, &result)) {
252 context.markCacheHit();
253 return result;
254 }
255
256 result = this->onFilterImage(context);
257
258 if (context.backend()->cache()) {
259 context.backend()->cache()->set(key, this, result);
260 }
261
262 return result;
263}
264
267 const SkIRect& subset,
268 const SkIRect& clipBounds,
269 SkIRect* outSubset,
270 SkIPoint* offset) const {
271 if (!outSubset || !offset || !src->bounds().contains(subset)) {
272 return nullptr;
273 }
274
275 auto srcSpecialImage = backend->makeImage(subset, src);
276 if (!srcSpecialImage) {
277 return nullptr;
278 }
279
281 const skif::Context context{std::move(backend),
283 skif::LayerSpace<SkIRect>(clipBounds),
284 skif::FilterResult(std::move(srcSpecialImage),
286 src->imageInfo().colorSpace(),
287 &stats};
288
290 stats.reportStats();
291
292 if (!result) {
293 return nullptr;
294 }
295
296 SkASSERT(clipBounds.contains(SkIRect::MakeXYWH(offset->fX, offset->fY,
297 result->width(), result->height())));
298 *outSubset = result->subset();
299 return result->asImage();
300}
301
303 const skif::Mapping& mapping,
304 const skif::DeviceSpace<SkIRect>& desiredOutput,
305 std::optional<skif::ParameterSpace<SkRect>> knownContentBounds) const {
306 // Map both the device-space desired coverage area and the known content bounds to layer space
307 skif::LayerSpace<SkIRect> desiredBounds = mapping.deviceToLayer(desiredOutput);
308
309 // If we have no known content bounds, leave 'contentBounds' uninstantiated to represent
310 // infinite possible content.
311 std::optional<skif::LayerSpace<SkIRect>> contentBounds;
312 if (knownContentBounds) {
313 contentBounds = mapping.paramToLayer(*knownContentBounds).roundOut();
314 }
315
316 // Process the layer-space desired output with the filter DAG to determine required input
317 return this->onGetInputLayerBounds(mapping, desiredBounds, contentBounds);
318}
319
320std::optional<skif::DeviceSpace<SkIRect>> SkImageFilter_Base::getOutputBounds(
321 const skif::Mapping& mapping,
322 const skif::ParameterSpace<SkRect>& contentBounds) const {
323 // Map the input content into the layer space where filtering will occur
324 skif::LayerSpace<SkRect> layerContent = mapping.paramToLayer(contentBounds);
325 // Determine the filter DAGs output bounds in layer space
326 std::optional<skif::LayerSpace<SkIRect>> filterOutput =
327 this->onGetOutputLayerBounds(mapping, layerContent.roundOut());
328 if (filterOutput) {
329 // Map all the way to device space
330 return mapping.layerToDevice(*filterOutput);
331 } else {
332 // Infinite layer output is infinite device-space output too
333 return {};
334 }
335}
336
339 const int count = this->countInputs();
340 for (int i = 0; i < count; ++i) {
341 if (const SkImageFilter_Base* input = as_IFB(this->getInput(i))) {
342 result = std::min(result, input->getCTMCapability());
343 }
344 }
345 return result;
346}
347
349 int index,
350 const skif::Mapping& mapping,
351 const skif::LayerSpace<SkIRect>& desiredOutput,
352 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
353 // The required input for childFilter filter, or 'contentBounds' intersected with
354 // 'desiredOutput' if the filter is null and the source image is used (i.e. the identity filter)
355 const SkImageFilter* childFilter = this->getInput(index);
356 if (childFilter) {
357 return as_IFB(childFilter)->onGetInputLayerBounds(mapping, desiredOutput, contentBounds);
358 } else {
359 // NOTE: We don't calculate the intersection between content and root desired output because
360 // the desired output can expand or contract as it propagates through the filter graph to
361 // the leaves that would actually sample from the source content.
362 skif::LayerSpace<SkIRect> visibleContent = desiredOutput;
363 if (contentBounds && !visibleContent.intersect(*contentBounds)) {
365 } else {
366 // This will be equal to 'desiredOutput' if the contentBounds are unknown.
367 return visibleContent;
368 }
369 }
370}
371
372std::optional<skif::LayerSpace<SkIRect>> SkImageFilter_Base::getChildOutputLayerBounds(
373 int index,
374 const skif::Mapping& mapping,
375 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
376 // The output for just childFilter filter, or 'contentBounds' if the filter is null and
377 // the source image is used (i.e. the identity filter applied to the source).
378 const SkImageFilter* childFilter = this->getInput(index);
379 return childFilter ? as_IFB(childFilter)->onGetOutputLayerBounds(mapping, contentBounds)
380 : contentBounds;
381}
382
384 const SkImageFilter* input = this->getInput(index);
385 return input ? as_IFB(input)->filterImage(ctx) : ctx.source();
386}
387
388void SkImageFilter_Base::PurgeCache() {
390 if (cache) {
391 cache->purge();
392 }
393}
const char * backend
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkColorFilterBase * as_CFB(SkColorFilter *filter)
static int32_t next_image_filter_unique_id()
static SkImageFilter_Base * as_IFB(SkImageFilter *filter)
static constexpr uint32_t SK_InvalidUniqueID
Definition: SkTypes.h:196
static bool SkIsValidRect(const SkRect &rect)
bool affectsTransparentBlack() const
virtual bool get(const SkImageFilterCacheKey &key, skif::FilterResult *result) const =0
virtual void set(const SkImageFilterCacheKey &key, const SkImageFilter *filter, const skif::FilterResult &result)=0
static sk_sp< SkImageFilterCache > Get(CreateIfNecessary=CreateIfNecessary::kYes)
virtual void purgeByImageFilter(const SkImageFilter *)=0
bool unflatten(SkReadBuffer &, int expectedInputs)
virtual bool onIsColorFilterNode(SkColorFilter **) const
~SkImageFilter_Base() override
virtual skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
std::optional< skif::DeviceSpace< SkIRect > > getOutputBounds(const skif::Mapping &mapping, const skif::ParameterSpace< SkRect > &contentBounds) const
virtual bool onAffectsTransparentBlack() const
skif::LayerSpace< SkIRect > getChildInputLayerBounds(int index, const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const
void flatten(SkWriteBuffer &) const override
virtual bool ignoreInputsAffectsTransparentBlack() const
virtual std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual skif::FilterResult onFilterImage(const skif::Context &context) const =0
skif::LayerSpace< SkIRect > getInputBounds(const skif::Mapping &mapping, const skif::DeviceSpace< SkIRect > &desiredOutput, std::optional< skif::ParameterSpace< SkRect > > knownContentBounds) const
skif::FilterResult getChildOutput(int index, const skif::Context &ctx) const
sk_sp< SkImage > makeImageWithFilter(sk_sp< skif::Backend > backend, sk_sp< SkImage > src, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset) const
skif::FilterResult filterImage(const skif::Context &context) const
bool affectsTransparentBlack() const
std::optional< skif::LayerSpace< SkIRect > > getChildOutputLayerBounds(int index, const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const
MatrixCapability getCTMCapability() const
static std::pair< sk_sp< SkImageFilter >, std::optional< SkRect > > Unflatten(SkReadBuffer &buffer)
virtual MatrixCapability onGetCTMCapability() const
friend class SkImageFilter_Base
sk_sp< SkImageFilter > refMe() const
SkIRect filterBounds(const SkIRect &src, const SkMatrix &ctm, MapDirection, const SkIRect *inputRect=nullptr) const
bool asAColorFilter(SkColorFilter **filterPtr) const
bool isColorFilterNode(SkColorFilter **filterPtr) const
virtual SkRect computeFastBounds(const SkRect &bounds) const
const SkImageFilter * getInput(int i) const
int countInputs() const
bool canComputeFastBounds() const
sk_sp< SkImageFilter > makeWithLocalMatrix(const SkMatrix &matrix) const
static sk_sp< SkImageFilter > Make(const SkMatrix &localMatrix, sk_sp< SkImageFilter > input)
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
bool isFinite() const
Definition: SkMatrix.h:1834
static SkIRect MakeILarge()
Definition: SkRectPriv.h:22
uint32_t uniqueID() const
const SkIRect & subset() const
virtual void writeBool(bool value)=0
void reset(int count)
Definition: SkTemplates.h:195
bool empty() const
Definition: SkTArray.h:199
SkImageFilterCache * cache() const
void markCacheHit() const
const Backend * backend() const
const LayerSpace< SkIRect > & desiredOutput() const
const FilterResult & source() const
const Mapping & mapping() const
void markVisitedImageFilter() const
sk_sp< SkSpecialImage > imageAndOffset(const Context &ctx, SkIPoint *offset) const
const SkSpecialImage * image() const
bool intersect(const LayerSpace< SkIRect > &r)
LayerSpace< SkIRect > roundOut() const
const SkMatrix & deviceToLayer() const
const SkMatrix & layerToDevice() const
const SkMatrix & layerMatrix() const
LayerSpace< T > paramToLayer(const ParameterSpace< T > &paramGeometry) const
FlutterSemanticsFlag flags
GAsyncResult * result
static float min(float r, float g, float b)
Definition: hsl.cpp:48
union flutter::testing::@2836::KeyboardChange::@76 content
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkShader > Empty()
Definition: common.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
dictionary stats
Definition: malisc.py:20
SeparatedVector2 offset
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
constexpr SkIPoint topLeft() const
Definition: SkRect.h:151
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
void join(const SkRect &r)
Definition: SkRect.cpp:126
const uintptr_t id