xref: /aosp_15_r20/external/skia/src/core/SkImageFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
8 #include "include/core/SkImageFilter.h"
9 
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPoint.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/base/SkTArray.h"
18 #include "include/private/base/SkTemplates.h"
19 #include "src/core/SkImageFilterCache.h"
20 #include "src/core/SkImageFilterTypes.h"
21 #include "src/core/SkImageFilter_Base.h"
22 #include "src/core/SkLocalMatrixImageFilter.h"
23 #include "src/core/SkPicturePriv.h"
24 #include "src/core/SkReadBuffer.h"
25 #include "src/core/SkRectPriv.h"
26 #include "src/core/SkSpecialImage.h"
27 #include "src/core/SkValidationUtils.h"
28 #include "src/core/SkWriteBuffer.h"
29 #include "src/effects/colorfilters/SkColorFilterBase.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  */
countInputs() const46 int 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  */
getInput(int i) const52 const SkImageFilter* SkImageFilter::getInput(int i) const {
53     SkASSERT(i < this->countInputs());
54     return as_IFB(this)->fInputs[i].get();
55 }
56 
isColorFilterNode(SkColorFilter ** filterPtr) const57 bool SkImageFilter::isColorFilterNode(SkColorFilter** filterPtr) const {
58     return as_IFB(this)->onIsColorFilterNode(filterPtr);
59 }
60 
filterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection direction,const SkIRect * inputRect) const61 SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
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) {
71             content = skif::LayerSpace<SkIRect>(*inputRect);
72         }
73         return SkIRect(as_IFB(this)->onGetInputLayerBounds(mapping, targetOutput, content));
74     } else {
75         SkASSERT(!inputRect);
76         skif::LayerSpace<SkIRect> content(src);
77         auto output = as_IFB(this)->onGetOutputLayerBounds(mapping, content);
78         return output ? SkIRect(*output) : SkRectPriv::MakeILarge();
79     }
80 }
81 
computeFastBounds(const SkRect & src) const82 SkRect SkImageFilter::computeFastBounds(const SkRect& src) const {
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 
canComputeFastBounds() const98 bool SkImageFilter::canComputeFastBounds() const {
99     return !as_IFB(this)->affectsTransparentBlack();
100 }
101 
affectsTransparentBlack() const102 bool SkImageFilter_Base::affectsTransparentBlack() const {
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 
asAColorFilter(SkColorFilter ** filterPtr) const118 bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
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 
makeWithLocalMatrix(const SkMatrix & matrix) const130 sk_sp<SkImageFilter> SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const {
131     return SkLocalMatrixImageFilter::Make(matrix, this->refMe());
132 }
133 
134 ///////////////////////////////////////////////////////////////////////////////////////////////////
135 // SkImageFilter_Base
136 ///////////////////////////////////////////////////////////////////////////////////////////////////
137 
next_image_filter_unique_id()138 static int32_t next_image_filter_unique_id() {
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 
SkImageFilter_Base(sk_sp<SkImageFilter> const * inputs,int inputCount,std::optional<bool> usesSrc)148 SkImageFilter_Base::SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs,
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 
~SkImageFilter_Base()163 SkImageFilter_Base::~SkImageFilter_Base() {
164     SkImageFilterCache::Get()->purgeByImageFilter(this);
165 }
166 
167 std::pair<sk_sp<SkImageFilter>, std::optional<SkRect>>
Unflatten(SkReadBuffer & buffer)168 SkImageFilter_Base::Unflatten(SkReadBuffer& buffer) {
169     Common common;
170     if (!common.unflatten(buffer, 1)) {
171         return {nullptr, std::nullopt};
172     } else {
173         return {common.getInput(0), common.cropRect()};
174     }
175 }
176 
unflatten(SkReadBuffer & buffer,int expectedCount)177 bool SkImageFilter_Base::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
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 
200     if (buffer.isVersionLT(SkPicturePriv::kRemoveDeprecatedCropRect)) {
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 
flatten(SkWriteBuffer & buffer) const220 void SkImageFilter_Base::flatten(SkWriteBuffer& buffer) const {
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 
filterImage(const skif::Context & context) const231 skif::FilterResult SkImageFilter_Base::filterImage(const skif::Context& context) const {
232     context.markVisitedImageFilter();
233 
234     skif::FilterResult result;
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 
makeImageWithFilter(sk_sp<skif::Backend> backend,sk_sp<SkImage> src,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const265 sk_sp<SkImage> SkImageFilter_Base::makeImageWithFilter(sk_sp<skif::Backend> backend,
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),
282                                 skif::Mapping(SkMatrix::I()),
283                                 skif::LayerSpace<SkIRect>(clipBounds),
284                                 skif::FilterResult(std::move(srcSpecialImage),
285                                                    skif::LayerSpace<SkIPoint>(subset.topLeft())),
286                                 src->imageInfo().colorSpace(),
287                                 &stats};
288 
289     sk_sp<SkSpecialImage> result = this->filterImage(context).imageAndOffset(context, offset);
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 
getInputBounds(const skif::Mapping & mapping,const skif::DeviceSpace<SkIRect> & desiredOutput,std::optional<skif::ParameterSpace<SkRect>> knownContentBounds) const302 skif::LayerSpace<SkIRect> SkImageFilter_Base::getInputBounds(
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 
getOutputBounds(const skif::Mapping & mapping,const skif::ParameterSpace<SkRect> & contentBounds) const320 std::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 
getCTMCapability() const337 SkImageFilter_Base::MatrixCapability SkImageFilter_Base::getCTMCapability() const {
338     MatrixCapability result = this->onGetCTMCapability();
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 
getChildInputLayerBounds(int index,const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const348 skif::LayerSpace<SkIRect> SkImageFilter_Base::getChildInputLayerBounds(
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)) {
364             return skif::LayerSpace<SkIRect>::Empty();
365         } else {
366             // This will be equal to 'desiredOutput' if the contentBounds are unknown.
367             return visibleContent;
368         }
369     }
370 }
371 
getChildOutputLayerBounds(int index,const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const372 std::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 
getChildOutput(int index,const skif::Context & ctx) const383 skif::FilterResult SkImageFilter_Base::getChildOutput(int index, const skif::Context& ctx) const {
384     const SkImageFilter* input = this->getInput(index);
385     return input ? as_IFB(input)->filterImage(ctx) : ctx.source();
386 }
387 
PurgeCache()388 void SkImageFilter_Base::PurgeCache() {
389     auto cache = SkImageFilterCache::Get(SkImageFilterCache::CreateIfNecessary::kNo);
390     if (cache) {
391         cache->purge();
392     }
393 }
394