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/SkCanvas.h"
11#include "include/core/SkRect.h"
12#include "include/effects/SkComposeImageFilter.h"
13#include "include/private/SkSafe32.h"
14#include "src/core/SkFuzzLogging.h"
15#include "src/core/SkImageFilterCache.h"
16#include "src/core/SkImageFilter_Base.h"
17#include "src/core/SkLocalMatrixImageFilter.h"
18#include "src/core/SkMatrixImageFilter.h"
19#include "src/core/SkReadBuffer.h"
20#include "src/core/SkSpecialImage.h"
21#include "src/core/SkSpecialSurface.h"
22#include "src/core/SkValidationUtils.h"
23#include "src/core/SkWriteBuffer.h"
24#if SK_SUPPORT_GPU
25#include "include/gpu/GrContext.h"
26#include "include/private/GrRecordingContext.h"
27#include "src/gpu/GrColorSpaceXform.h"
28#include "src/gpu/GrContextPriv.h"
29#include "src/gpu/GrFixedClip.h"
30#include "src/gpu/GrRecordingContextPriv.h"
31#include "src/gpu/GrRenderTargetContext.h"
32#include "src/gpu/GrTextureProxy.h"
33#include "src/gpu/SkGr.h"
34#endif
35#include <atomic>
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 */
52const SkImageFilter* SkImageFilter::getInput(int i) const {
53 SkASSERT(i < this->countInputs());
54 return as_IFB(this)->fInputs[i].get();
55}
56
57bool SkImageFilter::isColorFilterNode(SkColorFilter** filterPtr) const {
58 return as_IFB(this)->onIsColorFilterNode(filterPtr);
59}
60
61SkIRect 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(SkMatrix::I(), ctm);
67 if (kReverse_MapDirection == direction) {
68 skif::LayerSpace<SkIRect> targetOutput(src);
69 skif::LayerSpace<SkIRect> content(inputRect ? *inputRect : src);
70 return SkIRect(as_IFB(this)->onGetInputLayerBounds(mapping, targetOutput, content));
71 } else {
72 SkASSERT(!inputRect);
73 skif::LayerSpace<SkIRect> content(src);
74 skif::LayerSpace<SkIRect> output = as_IFB(this)->onGetOutputLayerBounds(mapping, content);
75 // Manually apply the crop rect for now, until cropping is performed by a dedicated SkIF.
76 SkIRect dst;
77 as_IFB(this)->getCropRect().applyTo(
78 SkIRect(output), ctm, as_IFB(this)->affectsTransparentBlack(), &dst);
79 return dst;
80 }
81}
82
83SkRect SkImageFilter::computeFastBounds(const SkRect& src) const {
84 if (0 == this->countInputs()) {
85 return src;
86 }
87 SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
88 for (int i = 1; i < this->countInputs(); i++) {
89 const SkImageFilter* input = this->getInput(i);
90 if (input) {
91 combinedBounds.join(input->computeFastBounds(src));
92 } else {
93 combinedBounds.join(src);
94 }
95 }
96 return combinedBounds;
97}
98
99bool SkImageFilter::canComputeFastBounds() const {
100 if (as_IFB(this)->affectsTransparentBlack()) {
101 return false;
102 }
103 for (int i = 0; i < this->countInputs(); i++) {
104 const SkImageFilter* input = this->getInput(i);
105 if (input && !input->canComputeFastBounds()) {
106 return false;
107 }
108 }
109 return true;
110}
111
112bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
113 SkASSERT(nullptr != filterPtr);
114 if (!this->isColorFilterNode(filterPtr)) {
115 return false;
116 }
117 if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) {
118 (*filterPtr)->unref();
119 return false;
120 }
121 return true;
122}
123
124sk_sp<SkImageFilter> SkImageFilter::MakeMatrixFilter(const SkMatrix& matrix,
125 SkFilterQuality filterQuality,
126 sk_sp<SkImageFilter> input) {
127 return SkMatrixImageFilter::Make(matrix, filterQuality, std::move(input));
128}
129
130sk_sp<SkImageFilter> SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const {
131 return SkLocalMatrixImageFilter::Make(matrix, this->refMe());
132}
133
134///////////////////////////////////////////////////////////////////////////////////////////////////
135// SkImageFilter_Base
136///////////////////////////////////////////////////////////////////////////////////////////////////
137
138SK_USE_FLUENT_IMAGE_FILTER_TYPES
139
140static int32_t next_image_filter_unique_id() {
141 static std::atomic<int32_t> nextID{1};
142
143 int32_t id;
144 do {
145 id = nextID++;
146 } while (id == 0);
147 return id;
148}
149
150SkImageFilter_Base::SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs,
151 int inputCount, const CropRect* cropRect)
152 : fUsesSrcInput(false)
153 , fUniqueID(next_image_filter_unique_id()) {
154 fCropRect = cropRect ? *cropRect : CropRect(SkRect(), 0x0);
155
156 fInputs.reset(inputCount);
157
158 for (int i = 0; i < inputCount; ++i) {
159 if (!inputs[i] || as_IFB(inputs[i])->fUsesSrcInput) {
160 fUsesSrcInput = true;
161 }
162 fInputs[i] = inputs[i];
163 }
164}
165
166SkImageFilter_Base::~SkImageFilter_Base() {
167 SkImageFilterCache::Get()->purgeByImageFilter(this);
168}
169
170bool SkImageFilter_Base::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
171 const int count = buffer.readInt();
172 if (!buffer.validate(count >= 0)) {
173 return false;
174 }
175 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
176 return false;
177 }
178
179 SkASSERT(fInputs.empty());
180 for (int i = 0; i < count; i++) {
181 fInputs.push_back(buffer.readBool() ? buffer.readImageFilter() : nullptr);
182 if (!buffer.isValid()) {
183 return false;
184 }
185 }
186 SkRect rect;
187 buffer.readRect(&rect);
188 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
189 return false;
190 }
191
192 uint32_t flags = buffer.readUInt();
193 fCropRect = CropRect(rect, flags);
194 return buffer.isValid();
195}
196
197void SkImageFilter_Base::flatten(SkWriteBuffer& buffer) const {
198 buffer.writeInt(fInputs.count());
199 for (int i = 0; i < fInputs.count(); i++) {
200 const SkImageFilter* input = this->getInput(i);
201 buffer.writeBool(input != nullptr);
202 if (input != nullptr) {
203 buffer.writeFlattenable(input);
204 }
205 }
206 buffer.writeRect(fCropRect.rect());
207 buffer.writeUInt(fCropRect.flags());
208}
209
210skif::FilterResult<For::kOutput> SkImageFilter_Base::filterImage(const skif::Context& context) const {
211 // TODO (michaelludwig) - Old filters have an implicit assumption that the source image
212 // (originally passed separately) has an origin of (0, 0). SkComposeImageFilter makes an effort
213 // to ensure that remains the case. Once everyone uses the new type systems for bounds, non
214 // (0, 0) source origins will be easy to support.
215 SkASSERT(context.source().layerOrigin().x() == 0 && context.source().layerOrigin().y() == 0);
216
217 skif::FilterResult<For::kOutput> result;
218 if (!context.isValid()) {
219 return result;
220 }
221
222 uint32_t srcGenID = fUsesSrcInput ? context.sourceImage()->uniqueID() : 0;
223 const SkIRect srcSubset = fUsesSrcInput ? context.sourceImage()->subset()
224 : SkIRect::MakeWH(0, 0);
225
226 SkImageFilterCacheKey key(fUniqueID, context.mapping().layerMatrix(), context.clipBounds(),
227 srcGenID, srcSubset);
228 if (context.cache() && context.cache()->get(key, &result)) {
229 return result;
230 }
231
232 result = this->onFilterImage(context);
233
234#if SK_SUPPORT_GPU
235 if (context.gpuBacked() && result.image() && !result.image()->isTextureBacked()) {
236 // Keep the result on the GPU - this is still required for some
237 // image filters that don't support GPU in all cases
238 auto asTexture = result.image()->makeTextureImage(context.getContext());
239 result = skif::FilterResult<For::kOutput>(std::move(asTexture), result.layerOrigin());
240 }
241#endif
242
243 if (context.cache()) {
244 context.cache()->set(key, this, result);
245 }
246
247 return result;
248}
249
250skif::LayerSpace<SkIRect> SkImageFilter_Base::getInputBounds(
251 const skif::Mapping& mapping, const skif::DeviceSpace<SkRect>& desiredOutput,
252 const skif::ParameterSpace<SkRect>* knownContentBounds) const {
253 // Map both the device-space desired coverage area and the known content bounds to layer space
254 skif::LayerSpace<SkIRect> desiredBounds = mapping.deviceToLayer(desiredOutput).roundOut();
255 // If we have no known content bounds use the desired coverage area, because that is the most
256 // conservative possibility.
257 skif::LayerSpace<SkIRect> contentBounds =
258 knownContentBounds ? mapping.paramToLayer(*knownContentBounds).roundOut()
259 : desiredBounds;
260
261 // Process the layer-space desired output with the filter DAG to determine required input
262 skif::LayerSpace<SkIRect> requiredInput = this->onGetInputLayerBounds(
263 mapping, desiredBounds, contentBounds);
264 // If we know what's actually going to be drawn into the layer, and we don't change transparent
265 // black, then we can further restrict the layer to what the known content is
266 if (knownContentBounds && !this->affectsTransparentBlack()) {
267 if (!requiredInput.intersect(contentBounds)) {
268 // Nothing would be output by the filter, so return empty rect
269 return skif::LayerSpace<SkIRect>(SkIRect::MakeEmpty());
270 }
271 }
272 return requiredInput;
273}
274
275skif::DeviceSpace<SkIRect> SkImageFilter_Base::getOutputBounds(
276 const skif::Mapping& mapping, const skif::ParameterSpace<SkRect>& contentBounds) const {
277 // Map the input content into the layer space where filtering will occur
278 skif::LayerSpace<SkRect> layerContent = mapping.paramToLayer(contentBounds);
279 // Determine the filter DAGs output bounds in layer space
280 skif::LayerSpace<SkIRect> filterOutput = this->onGetOutputLayerBounds(
281 mapping, layerContent.roundOut());
282 // FIXME (michaelludwig) - To be removed once cropping is isolated, but remain consistent with
283 // old filterBounds(kForward) behavior.
284 SkIRect dst;
285 as_IFB(this)->getCropRect().applyTo(
286 SkIRect(filterOutput), mapping.layerMatrix(),
287 as_IFB(this)->affectsTransparentBlack(), &dst);
288
289 // Map all the way to device space
290 return mapping.layerToDevice(skif::LayerSpace<SkIRect>(dst));
291}
292
293// TODO (michaelludwig) - Default to using the old onFilterImage, as filters are updated one by one.
294// Once the old function is gone, this onFilterImage() will be made a pure virtual.
295skif::FilterResult<For::kOutput> SkImageFilter_Base::onFilterImage(const skif::Context& context) const {
296 SkIPoint origin;
297 auto image = this->onFilterImage(context, &origin);
298 return skif::FilterResult<For::kOutput>(std::move(image), skif::LayerSpace<SkIPoint>(origin));
299}
300
301bool SkImageFilter_Base::canHandleComplexCTM() const {
302 // CropRects need to apply in the source coordinate system, but are not aware of complex CTMs
303 // when performing clipping. For a simple fix, any filter with a crop rect set cannot support
304 // complex CTMs until that's updated.
305 if (this->cropRectIsSet() || !this->onCanHandleComplexCTM()) {
306 return false;
307 }
308 const int count = this->countInputs();
309 for (int i = 0; i < count; ++i) {
310 const SkImageFilter_Base* input = as_IFB(this->getInput(i));
311 if (input && !input->canHandleComplexCTM()) {
312 return false;
313 }
314 }
315 return true;
316}
317
318void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds, const SkMatrix& ctm,
319 bool embiggen, SkIRect* cropped) const {
320 *cropped = imageBounds;
321 if (fFlags) {
322 SkRect devCropR;
323 ctm.mapRect(&devCropR, fRect);
324 SkIRect devICropR = devCropR.roundOut();
325
326 // Compute the left/top first, in case we need to modify the right/bottom for a missing edge
327 if (fFlags & kHasLeft_CropEdge) {
328 if (embiggen || devICropR.fLeft > cropped->fLeft) {
329 cropped->fLeft = devICropR.fLeft;
330 }
331 } else {
332 devICropR.fRight = Sk32_sat_add(cropped->fLeft, devICropR.width());
333 }
334 if (fFlags & kHasTop_CropEdge) {
335 if (embiggen || devICropR.fTop > cropped->fTop) {
336 cropped->fTop = devICropR.fTop;
337 }
338 } else {
339 devICropR.fBottom = Sk32_sat_add(cropped->fTop, devICropR.height());
340 }
341 if (fFlags & kHasWidth_CropEdge) {
342 if (embiggen || devICropR.fRight < cropped->fRight) {
343 cropped->fRight = devICropR.fRight;
344 }
345 }
346 if (fFlags & kHasHeight_CropEdge) {
347 if (embiggen || devICropR.fBottom < cropped->fBottom) {
348 cropped->fBottom = devICropR.fBottom;
349 }
350 }
351 }
352}
353
354bool SkImageFilter_Base::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
355 SkIRect* dstBounds) const {
356 SkIRect tmpDst = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection, nullptr);
357 fCropRect.applyTo(tmpDst, ctx.ctm(), this->affectsTransparentBlack(), dstBounds);
358 // Intersect against the clip bounds, in case the crop rect has
359 // grown the bounds beyond the original clip. This can happen for
360 // example in tiling, where the clip is much smaller than the filtered
361 // primitive. If we didn't do this, we would be processing the filter
362 // at the full crop rect size in every tile.
363 return dstBounds->intersect(ctx.clipBounds());
364}
365
366// Return a larger (newWidth x newHeight) copy of 'src' with black padding
367// around it.
368static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src, const SkImageFilter_Base::Context& ctx,
369 int newWidth, int newHeight, int offX, int offY) {
370 // We would like to operate in the source's color space (so that we return an "identical"
371 // image, other than the padding. To achieve that, we'd create a new context using
372 // src->getColorSpace() to replace ctx.colorSpace().
373
374 // That fails in at least two ways. For formats that are texturable but not renderable (like
375 // F16 on some ES implementations), we can't create a surface to do the work. For sRGB, images
376 // may be tagged with an sRGB color space (which leads to an sRGB config in makeSurface). But
377 // the actual config of that sRGB image on a device with no sRGB support is non-sRGB.
378 //
379 // Rather than try to special case these situations, we execute the image padding in the
380 // destination color space. This should not affect the output of the DAG in (almost) any case,
381 // because the result of this call is going to be used as an input, where it would have been
382 // switched to the destination space anyway. The one exception would be a filter that expected
383 // to consume unclamped F16 data, but the padded version of the image is pre-clamped to 8888.
384 // We can revisit this logic if that ever becomes an actual problem.
385 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(SkISize::Make(newWidth, newHeight)));
386 if (!surf) {
387 return nullptr;
388 }
389
390 SkCanvas* canvas = surf->getCanvas();
391 SkASSERT(canvas);
392
393 canvas->clear(0x0);
394
395 src->draw(canvas, offX, offY, nullptr);
396
397 return surf->makeImageSnapshot();
398}
399
400sk_sp<SkSpecialImage> SkImageFilter_Base::applyCropRectAndPad(const Context& ctx,
401 SkSpecialImage* src,
402 SkIPoint* srcOffset,
403 SkIRect* bounds) const {
404 const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset->x(), srcOffset->y(),
405 src->width(), src->height());
406
407 if (!this->applyCropRect(ctx, srcBounds, bounds)) {
408 return nullptr;
409 }
410
411 if (srcBounds.contains(*bounds)) {
412 return sk_sp<SkSpecialImage>(SkRef(src));
413 } else {
414 sk_sp<SkSpecialImage> img(pad_image(src, ctx, bounds->width(), bounds->height(),
415 Sk32_sat_sub(srcOffset->x(), bounds->x()),
416 Sk32_sat_sub(srcOffset->y(), bounds->y())));
417 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
418 return img;
419 }
420}
421
422// NOTE: The new onGetOutputLayerBounds() and onGetInputLayerBounds() default to calling into the
423// deprecated onFilterBounds and onFilterNodeBounds. While these functions are not tagged, they do
424// match the documented default behavior for the new bounds functions.
425SkIRect SkImageFilter_Base::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
426 MapDirection dir, const SkIRect* inputRect) const {
427 if (this->countInputs() < 1) {
428 return src;
429 }
430
431 SkIRect totalBounds;
432 for (int i = 0; i < this->countInputs(); ++i) {
433 const SkImageFilter* filter = this->getInput(i);
434 SkIRect rect = filter ? filter->filterBounds(src, ctm, dir, inputRect) : src;
435 if (0 == i) {
436 totalBounds = rect;
437 } else {
438 totalBounds.join(rect);
439 }
440 }
441
442 return totalBounds;
443}
444
445SkIRect SkImageFilter_Base::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
446 MapDirection, const SkIRect*) const {
447 return src;
448}
449
450skif::LayerSpace<SkIRect> SkImageFilter_Base::visitInputLayerBounds(
451 const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput,
452 const skif::LayerSpace<SkIRect>& contentBounds) const {
453 if (this->countInputs() < 1) {
454 // TODO (michaelludwig) - if a filter doesn't have any inputs, it doesn't need any
455 // implicit source image, so arguably we could return an empty rect here. 'desiredOutput' is
456 // consistent with original behavior, so empty bounds may have unintended side effects
457 // but should be explored later.
458 return desiredOutput;
459 }
460
461 skif::LayerSpace<SkIRect> netInput;
462 for (int i = 0; i < this->countInputs(); ++i) {
463 const SkImageFilter* filter = this->getInput(i);
464 // The required input for this input filter, or 'targetOutput' if the filter is null and
465 // the source image is used (so must be sized to cover 'targetOutput').
466 skif::LayerSpace<SkIRect> requiredInput =
467 filter ? as_IFB(filter)->onGetInputLayerBounds(mapping, desiredOutput,
468 contentBounds)
469 : desiredOutput;
470 // Accumulate with all other filters
471 if (i == 0) {
472 netInput = requiredInput;
473 } else {
474 netInput.join(requiredInput);
475 }
476 }
477 return netInput;
478}
479
480skif::LayerSpace<SkIRect> SkImageFilter_Base::visitOutputLayerBounds(
481 const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const {
482 if (this->countInputs() < 1) {
483 // TODO (michaelludwig) - if a filter doesn't have any inputs, it presumably is determining
484 // its output size from something other than the implicit source contentBounds, in which
485 // case it shouldn't be calling this helper function, so explore adding an unreachable test
486 return contentBounds;
487 }
488
489 skif::LayerSpace<SkIRect> netOutput;
490 for (int i = 0; i < this->countInputs(); ++i) {
491 const SkImageFilter* filter = this->getInput(i);
492 // The output for just this input filter, or 'contentBounds' if the filter is null and
493 // the source image is used (i.e. the identity filter applied to the source).
494 skif::LayerSpace<SkIRect> output =
495 filter ? as_IFB(filter)->onGetOutputLayerBounds(mapping, contentBounds)
496 : contentBounds;
497 // Accumulate with all other filters
498 if (i == 0) {
499 netOutput = output;
500 } else {
501 netOutput.join(output);
502 }
503 }
504 return netOutput;
505}
506
507skif::LayerSpace<SkIRect> SkImageFilter_Base::onGetInputLayerBounds(
508 const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput,
509 const skif::LayerSpace<SkIRect>& contentBounds, VisitChildren recurse) const {
510 // Call old functions for now since they may have been overridden by a subclass that's not been
511 // updated yet; normally this would just default to visitInputLayerBounds()
512 SkIRect content = SkIRect(contentBounds);
513 SkIRect input = this->onFilterNodeBounds(SkIRect(desiredOutput), mapping.layerMatrix(),
514 kReverse_MapDirection, &content);
515 if (recurse == VisitChildren::kYes) {
516 SkIRect aggregate = this->onFilterBounds(input, mapping.layerMatrix(),
517 kReverse_MapDirection, &input);
518 return skif::LayerSpace<SkIRect>(aggregate);
519 } else {
520 return skif::LayerSpace<SkIRect>(input);
521 }
522}
523
524skif::LayerSpace<SkIRect> SkImageFilter_Base::onGetOutputLayerBounds(
525 const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const {
526 // Call old functions for now; normally this would default to visitOutputLayerBounds()
527 SkIRect aggregate = this->onFilterBounds(SkIRect(contentBounds), mapping.layerMatrix(),
528 kForward_MapDirection, nullptr);
529 SkIRect output = this->onFilterNodeBounds(aggregate, mapping.layerMatrix(),
530 kForward_MapDirection, nullptr);
531 return skif::LayerSpace<SkIRect>(output);
532}
533
534template<skif::Usage kU>
535skif::FilterResult<kU> SkImageFilter_Base::filterInput(int index, const skif::Context& ctx) const {
536 // Sanity checks for the index-specific input usages
537 SkASSERT(kU != skif::Usage::kInput0 || index == 0);
538 SkASSERT(kU != skif::Usage::kInput1 || index == 1);
539
540 const SkImageFilter* input = this->getInput(index);
541 if (!input) {
542 // Convert from the generic kInput of the source image to kU
543 return static_cast<skif::FilterResult<kU>>(ctx.source());
544 }
545
546 skif::FilterResult<For::kOutput> result = as_IFB(input)->filterImage(this->mapContext(ctx));
547 SkASSERT(!result.image() || ctx.gpuBacked() == result.image()->isTextureBacked());
548
549 // Map the output result of the input image filter to the input usage requested for this filter
550 return static_cast<skif::FilterResult<kU>>(std::move(result));
551}
552// Instantiate filterInput() for kInput, kInput0, and kInput1. This does not provide a definition
553// for kOutput, which should never be used anyways, and this way the linker will fail for us then.
554template skif::FilterResult<For::kInput> SkImageFilter_Base::filterInput(int, const skif::Context&) const;
555template skif::FilterResult<For::kInput0> SkImageFilter_Base::filterInput(int, const skif::Context&) const;
556template skif::FilterResult<For::kInput1> SkImageFilter_Base::filterInput(int, const skif::Context&) const;
557
558SkImageFilter_Base::Context SkImageFilter_Base::mapContext(const Context& ctx) const {
559 // We don't recurse through the child input filters because that happens automatically
560 // as part of the filterImage() evaluation. In this case, we want the bounds for the
561 // edge from this node to its children, without the effects of the child filters.
562 skif::LayerSpace<SkIRect> childOutput = this->onGetInputLayerBounds(
563 ctx.mapping(), ctx.desiredOutput(), ctx.desiredOutput(), VisitChildren::kNo);
564 return ctx.withNewDesiredOutput(childOutput);
565}
566
567#if SK_SUPPORT_GPU
568sk_sp<SkSpecialImage> SkImageFilter_Base::DrawWithFP(GrRecordingContext* context,
569 std::unique_ptr<GrFragmentProcessor> fp,
570 const SkIRect& bounds,
571 SkColorType colorType,
572 const SkColorSpace* colorSpace,
573 GrProtected isProtected) {
574 GrPaint paint;
575 paint.addColorFragmentProcessor(std::move(fp));
576 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
577
578 auto renderTargetContext = GrRenderTargetContext::Make(
579 context, SkColorTypeToGrColorType(colorType), sk_ref_sp(colorSpace),
580 SkBackingFit::kApprox, bounds.size(), 1, GrMipMapped::kNo, isProtected,
581 kBottomLeft_GrSurfaceOrigin);
582 if (!renderTargetContext) {
583 return nullptr;
584 }
585
586 SkIRect dstIRect = SkIRect::MakeWH(bounds.width(), bounds.height());
587 SkRect srcRect = SkRect::Make(bounds);
588 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
589 GrFixedClip clip(dstIRect);
590 renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
591 srcRect);
592
593 return SkSpecialImage::MakeDeferredFromGpu(
594 context, dstIRect, kNeedNewImageUniqueID_SpecialImage,
595 renderTargetContext->readSurfaceView(), renderTargetContext->colorInfo().colorType(),
596 renderTargetContext->colorInfo().refColorSpace());
597}
598
599sk_sp<SkSpecialImage> SkImageFilter_Base::ImageToColorSpace(SkSpecialImage* src,
600 SkColorType colorType,
601 SkColorSpace* colorSpace) {
602 // There are several conditions that determine if we actually need to convert the source to the
603 // destination's color space. Rather than duplicate that logic here, just try to make an xform
604 // object. If that produces something, then both are tagged, and the source is in a different
605 // gamut than the dest. There is some overhead to making the xform, but those are cached, and
606 // if we get one back, that means we're about to use it during the conversion anyway.
607 auto colorSpaceXform = GrColorSpaceXform::Make(src->getColorSpace(), src->alphaType(),
608 colorSpace, kPremul_SkAlphaType);
609
610 if (!colorSpaceXform) {
611 // No xform needed, just return the original image
612 return sk_ref_sp(src);
613 }
614
615 sk_sp<SkSpecialSurface> surf(src->makeSurface(colorType, colorSpace,
616 SkISize::Make(src->width(), src->height())));
617 if (!surf) {
618 return sk_ref_sp(src);
619 }
620
621 SkCanvas* canvas = surf->getCanvas();
622 SkASSERT(canvas);
623 SkPaint p;
624 p.setBlendMode(SkBlendMode::kSrc);
625 src->draw(canvas, 0, 0, &p);
626 return surf->makeImageSnapshot();
627}
628#endif
629
630// In repeat mode, when we are going to sample off one edge of the srcBounds we require the
631// opposite side be preserved.
632SkIRect SkImageFilter_Base::DetermineRepeatedSrcBound(const SkIRect& srcBounds,
633 const SkIVector& filterOffset,
634 const SkISize& filterSize,
635 const SkIRect& originalSrcBounds) {
636 SkIRect tmp = srcBounds;
637 tmp.adjust(-filterOffset.fX, -filterOffset.fY,
638 filterSize.fWidth - filterOffset.fX, filterSize.fHeight - filterOffset.fY);
639
640 if (tmp.fLeft < originalSrcBounds.fLeft || tmp.fRight > originalSrcBounds.fRight) {
641 tmp.fLeft = originalSrcBounds.fLeft;
642 tmp.fRight = originalSrcBounds.fRight;
643 }
644 if (tmp.fTop < originalSrcBounds.fTop || tmp.fBottom > originalSrcBounds.fBottom) {
645 tmp.fTop = originalSrcBounds.fTop;
646 tmp.fBottom = originalSrcBounds.fBottom;
647 }
648
649 return tmp;
650}
651
652void SkImageFilter_Base::PurgeCache() {
653 SkImageFilterCache::Get()->purge();
654}
655
656static sk_sp<SkImageFilter> apply_ctm_to_filter(sk_sp<SkImageFilter> input, const SkMatrix& ctm,
657 SkMatrix* remainder) {
658 if (ctm.isScaleTranslate() || as_IFB(input)->canHandleComplexCTM()) {
659 // The filter supports the CTM, so leave it as-is and 'remainder' stores the whole CTM
660 *remainder = ctm;
661 return input;
662 }
663
664 // We have a complex CTM and a filter that can't support them, so it needs to use the matrix
665 // transform filter that resamples the image contents. Decompose the simple portion of the ctm
666 // into 'remainder'
667 SkMatrix ctmToEmbed;
668 SkSize scale;
669 if (ctm.decomposeScale(&scale, &ctmToEmbed)) {
670 // decomposeScale splits ctm into scale * ctmToEmbed, so bake ctmToEmbed into DAG
671 // with a matrix filter and return scale as the remaining matrix for the real CTM.
672 remainder->setScale(scale.fWidth, scale.fHeight);
673
674 // ctmToEmbed is passed to SkMatrixImageFilter, which performs its transforms as if it were
675 // a pre-transformation before applying the image-filter context's CTM. In this case, we
676 // need ctmToEmbed to be a post-transformation (i.e. after the scale matrix since
677 // decomposeScale produces ctm = ctmToEmbed * scale). Giving scale^-1 * ctmToEmbed * scale
678 // to the matrix filter achieves this effect.
679 // TODO (michaelludwig) - When the original root node of a filter can be drawn directly to a
680 // device using ctmToEmbed, this abuse of SkMatrixImageFilter can go away.
681 ctmToEmbed.preScale(scale.fWidth, scale.fHeight);
682 ctmToEmbed.postScale(1.f / scale.fWidth, 1.f / scale.fHeight);
683 } else {
684 // Unable to decompose
685 // FIXME Ideally we'd embed the entire CTM as part of the matrix image filter, but
686 // the device <-> src bounds calculations for filters are very brittle under perspective,
687 // and can easily run into precision issues (wrong bounds that clip), or performance issues
688 // (producing large source-space images where 80% of the image is compressed into a few
689 // device pixels). A longer term solution for perspective-space image filtering is needed
690 // see skbug.com/9074
691 if (ctm.hasPerspective()) {
692 *remainder = ctm;
693 return input;
694 }
695
696 ctmToEmbed = ctm;
697 remainder->setIdentity();
698 }
699
700 return SkMatrixImageFilter::Make(ctmToEmbed, kLow_SkFilterQuality, input);
701}
702
703sk_sp<SkImageFilter> SkImageFilter_Base::applyCTM(const SkMatrix& ctm, SkMatrix* remainder) const {
704 return apply_ctm_to_filter(this->refMe(), ctm, remainder);
705}
706