Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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);
78 return output ? SkIRect(*output) : SkRectPriv::MakeILarge();
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
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
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
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
266 sk_sp<SkImage> src,
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
280 skif::Stats stats;
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
#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()
bool isFinite() const
Definition SkMatrix.h:1834
static SkIRect MakeILarge()
Definition SkRectPriv.h:22
uint32_t uniqueID() const
const SkIRect & subset() const
bool empty() const
Definition SkTArray.h:194
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
const SkMatrix & deviceToLayer() const
const SkMatrix & layerToDevice() const
const SkMatrix & layerMatrix() const
LayerSpace< T > paramToLayer(const ParameterSpace< T > &paramGeometry) const
FlutterSemanticsFlag flags
static const uint8_t buffer[]
GAsyncResult * result
union flutter::testing::@2838::KeyboardChange::@76 content
Point offset
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