2 * Copyright 2012 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkImageFilter.h"
11 #include "SkFuzzLogging.h"
12 #include "SkImageFilterCache.h"
13 #include "SkLocalMatrixImageFilter.h"
14 #include "SkMatrixImageFilter.h"
15 #include "SkReadBuffer.h"
17 #include "SkSpecialImage.h"
18 #include "SkSpecialSurface.h"
19 #include "SkValidationUtils.h"
20 #include "SkWriteBuffer.h"
22 #include "GrContext.h"
23 #include "GrDrawContext.h"
24 #include "GrFixedClip.h"
28 #ifndef SK_IGNORE_TO_STRING
29 void SkImageFilter::CropRect::toString(SkString* str) const {
34 str->appendf("cropRect (");
35 if (fFlags & CropRect::kHasLeft_CropEdge) {
36 str->appendf("%.2f, ", fRect.fLeft);
40 if (fFlags & CropRect::kHasTop_CropEdge) {
41 str->appendf("%.2f, ", fRect.fTop);
45 if (fFlags & CropRect::kHasWidth_CropEdge) {
46 str->appendf("%.2f, ", fRect.width());
50 if (fFlags & CropRect::kHasHeight_CropEdge) {
51 str->appendf("%.2f", fRect.height());
59 void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds,
62 SkIRect* cropped) const {
63 *cropped = imageBounds;
66 ctm.mapRect(&devCropR, fRect);
67 SkIRect devICropR = devCropR.roundOut();
69 // Compute the left/top first, in case we need to modify the right/bottom for a missing edge
70 if (fFlags & kHasLeft_CropEdge) {
71 if (embiggen || devICropR.fLeft > cropped->fLeft) {
72 cropped->fLeft = devICropR.fLeft;
75 devICropR.fRight = cropped->fLeft + devICropR.width();
77 if (fFlags & kHasTop_CropEdge) {
78 if (embiggen || devICropR.fTop > cropped->fTop) {
79 cropped->fTop = devICropR.fTop;
82 devICropR.fBottom = cropped->fTop + devICropR.height();
84 if (fFlags & kHasWidth_CropEdge) {
85 if (embiggen || devICropR.fRight < cropped->fRight) {
86 cropped->fRight = devICropR.fRight;
89 if (fFlags & kHasHeight_CropEdge) {
90 if (embiggen || devICropR.fBottom < cropped->fBottom) {
91 cropped->fBottom = devICropR.fBottom;
97 ///////////////////////////////////////////////////////////////////////////////////////////////////
99 static int32_t next_image_filter_unique_id() {
100 static int32_t gImageFilterUniqueID;
105 id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
110 void SkImageFilter::Common::allocInputs(int count) {
111 fInputs.reset(count);
114 bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
115 const int count = buffer.readInt();
116 if (!buffer.validate(count >= 0)) {
119 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
123 SkFUZZF(("allocInputs: %d\n", count));
124 this->allocInputs(count);
125 for (int i = 0; i < count; i++) {
126 if (buffer.readBool()) {
127 fInputs[i] = sk_sp<SkImageFilter>(buffer.readImageFilter());
129 if (!buffer.isValid()) {
134 buffer.readRect(&rect);
135 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
139 uint32_t flags = buffer.readUInt();
140 fCropRect = CropRect(rect, flags);
141 if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
143 (void) buffer.readUInt();
145 return buffer.isValid();
148 ///////////////////////////////////////////////////////////////////////////////////////////////////
150 void SkImageFilter::init(sk_sp<SkImageFilter>* inputs,
152 const CropRect* cropRect) {
153 fCropRect = cropRect ? *cropRect : CropRect(SkRect(), 0x0);
155 fInputs.reset(inputCount);
157 for (int i = 0; i < inputCount; ++i) {
158 if (!inputs[i] || inputs[i]->usesSrcInput()) {
159 fUsesSrcInput = true;
161 fInputs[i] = inputs[i];
165 SkImageFilter::SkImageFilter(sk_sp<SkImageFilter>* inputs,
167 const CropRect* cropRect)
168 : fUsesSrcInput(false)
169 , fUniqueID(next_image_filter_unique_id()) {
170 this->init(inputs, inputCount, cropRect);
173 SkImageFilter::~SkImageFilter() {
174 SkImageFilterCache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count());
177 SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
178 : fUsesSrcInput(false)
179 , fCropRect(SkRect(), 0x0)
180 , fUniqueID(next_image_filter_unique_id()) {
182 if (common.unflatten(buffer, inputCount)) {
183 this->init(common.inputs(), common.inputCount(), &common.cropRect());
187 void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
188 buffer.writeInt(fInputs.count());
189 for (int i = 0; i < fInputs.count(); i++) {
190 SkImageFilter* input = this->getInput(i);
191 buffer.writeBool(input != nullptr);
192 if (input != nullptr) {
193 buffer.writeFlattenable(input);
196 buffer.writeRect(fCropRect.rect());
197 buffer.writeUInt(fCropRect.flags());
200 sk_sp<SkSpecialImage> SkImageFilter::filterImage(SkSpecialImage* src, const Context& context,
201 SkIPoint* offset) const {
202 SkASSERT(src && offset);
204 uint32_t srcGenID = fUsesSrcInput ? src->uniqueID() : 0;
205 const SkIRect srcSubset = fUsesSrcInput ? src->subset() : SkIRect::MakeWH(0, 0);
206 SkImageFilterCacheKey key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID, srcSubset);
207 if (context.cache()) {
208 SkSpecialImage* result = context.cache()->get(key, offset);
210 return sk_sp<SkSpecialImage>(SkRef(result));
214 sk_sp<SkSpecialImage> result(this->onFilterImage(src, context, offset));
217 if (src->isTextureBacked() && result && !result->isTextureBacked()) {
218 // Keep the result on the GPU - this is still required for some
219 // image filters that don't support GPU in all cases
220 GrContext* context = src->getContext();
221 result = result->makeTextureImage(context);
225 if (result && context.cache()) {
226 context.cache()->set(key, result.get(), *offset);
227 SkAutoMutexAcquire mutex(fMutex);
228 fCacheKeys.push_back(key);
234 SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
235 MapDirection direction) const {
236 if (kReverse_MapDirection == direction) {
237 SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction);
238 return this->onFilterBounds(bounds, ctm, direction);
240 SkIRect bounds = this->onFilterBounds(src, ctm, direction);
241 bounds = this->onFilterNodeBounds(bounds, ctm, direction);
243 this->getCropRect().applyTo(bounds, ctm, this->affectsTransparentBlack(), &dst);
248 SkRect SkImageFilter::computeFastBounds(const SkRect& src) const {
249 if (0 == this->countInputs()) {
252 SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
253 for (int i = 1; i < this->countInputs(); i++) {
254 SkImageFilter* input = this->getInput(i);
256 combinedBounds.join(input->computeFastBounds(src));
258 combinedBounds.join(src);
261 return combinedBounds;
264 bool SkImageFilter::canComputeFastBounds() const {
265 if (this->affectsTransparentBlack()) {
268 for (int i = 0; i < this->countInputs(); i++) {
269 SkImageFilter* input = this->getInput(i);
270 if (input && !input->canComputeFastBounds()) {
278 sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context,
279 sk_sp<GrFragmentProcessor> fp,
280 const SkIRect& bounds,
281 const OutputProperties& outputProperties) {
283 paint.addColorFragmentProcessor(std::move(fp));
284 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
286 sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace());
287 GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get());
288 sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox,
289 bounds.width(), bounds.height(),
291 std::move(colorSpace)));
295 paint.setGammaCorrect(drawContext->isGammaCorrect());
297 SkIRect dstIRect = SkIRect::MakeWH(bounds.width(), bounds.height());
298 SkRect srcRect = SkRect::Make(bounds);
299 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
300 GrFixedClip clip(dstIRect);
301 drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);
303 return SkSpecialImage::MakeFromGpu(dstIRect, kNeedNewImageUniqueID_SpecialImage,
304 drawContext->asTexture(),
305 sk_ref_sp(drawContext->getColorSpace()));
309 bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
310 SkASSERT(nullptr != filterPtr);
311 if (!this->isColorFilterNode(filterPtr)) {
314 if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) {
315 (*filterPtr)->unref();
321 bool SkImageFilter::canHandleComplexCTM() const {
322 if (!this->onCanHandleComplexCTM()) {
325 const int count = this->countInputs();
326 for (int i = 0; i < count; ++i) {
327 SkImageFilter* input = this->getInput(i);
328 if (input && !input->canHandleComplexCTM()) {
335 bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
336 SkIRect* dstBounds) const {
337 SkIRect temp = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
338 fCropRect.applyTo(temp, ctx.ctm(), this->affectsTransparentBlack(), dstBounds);
339 // Intersect against the clip bounds, in case the crop rect has
340 // grown the bounds beyond the original clip. This can happen for
341 // example in tiling, where the clip is much smaller than the filtered
342 // primitive. If we didn't do this, we would be processing the filter
343 // at the full crop rect size in every tile.
344 return dstBounds->intersect(ctx.clipBounds());
347 // Return a larger (newWidth x newHeight) copy of 'src' with black padding
349 static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src,
350 const SkImageFilter::OutputProperties& outProps,
351 int newWidth, int newHeight, int offX, int offY) {
352 // We would like to operate in the source's color space (so that we return an "identical"
353 // image, other than the padding. To achieve that, we'd create new output properties:
355 // SkImageFilter::OutputProperties outProps(src->getColorSpace());
357 // That fails in at least two ways. For formats that are texturable but not renderable (like
358 // F16 on some ES implementations), we can't create a surface to do the work. For sRGB, images
359 // may be tagged with an sRGB color space (which leads to an sRGB config in makeSurface). But
360 // the actual config of that sRGB image on a device with no sRGB support is non-sRGB.
362 // Rather than try to special case these situations, we execute the image padding in the
363 // destination color space. This should not affect the output of the DAG in (almost) any case,
364 // because the result of this call is going to be used as an input, where it would have been
365 // switched to the destination space anyway. The one exception would be a filter that expected
366 // to consume unclamped F16 data, but the padded version of the image is pre-clamped to 8888.
367 // We can revisit this logic if that ever becomes an actual problem.
368 sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps, SkISize::Make(newWidth, newHeight)));
373 SkCanvas* canvas = surf->getCanvas();
378 src->draw(canvas, offX, offY, nullptr);
380 return surf->makeImageSnapshot();
383 sk_sp<SkSpecialImage> SkImageFilter::applyCropRect(const Context& ctx,
386 SkIRect* bounds) const {
387 const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset->x(), srcOffset->y(),
388 src->width(), src->height());
390 SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
391 fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds);
392 if (!bounds->intersect(ctx.clipBounds())) {
396 if (srcBounds.contains(*bounds)) {
397 return sk_sp<SkSpecialImage>(SkRef(src));
399 sk_sp<SkSpecialImage> img(pad_image(src, ctx.outputProperties(),
400 bounds->width(), bounds->height(),
401 srcOffset->x() - bounds->x(),
402 srcOffset->y() - bounds->y()));
403 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
408 SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
409 MapDirection direction) const {
410 if (this->countInputs() < 1) {
415 for (int i = 0; i < this->countInputs(); ++i) {
416 SkImageFilter* filter = this->getInput(i);
417 SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src;
421 totalBounds.join(rect);
428 SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const {
433 SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
434 SkIRect clipBounds = this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(),
435 MapDirection::kReverse_MapDirection);
436 return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.outputProperties());
439 sk_sp<SkImageFilter> SkImageFilter::MakeMatrixFilter(const SkMatrix& matrix,
440 SkFilterQuality filterQuality,
441 sk_sp<SkImageFilter> input) {
442 return SkMatrixImageFilter::Make(matrix, filterQuality, std::move(input));
445 sk_sp<SkImageFilter> SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const {
446 // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter
447 // is *always* treated as a const ptr. Hence the const-cast here.
449 SkImageFilter* nonConstThis = const_cast<SkImageFilter*>(this);
450 return SkLocalMatrixImageFilter::Make(matrix, sk_ref_sp<SkImageFilter>(nonConstThis));
453 sk_sp<SkSpecialImage> SkImageFilter::filterInput(int index,
456 SkIPoint* offset) const {
457 SkImageFilter* input = this->getInput(index);
459 return sk_sp<SkSpecialImage>(SkRef(src));
462 sk_sp<SkSpecialImage> result(input->filterImage(src, this->mapContext(ctx), offset));
464 SkASSERT(!result || src->isTextureBacked() == result->isTextureBacked());
469 void SkImageFilter::PurgeCache() {
470 SkImageFilterCache::Get()->purge();