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