blob: a38124b0e62b4db1894883e7654630cff0de9a1d [file] [log] [blame]
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +00001/*
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 "SkImageFilter.h"
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +00009
10#include "SkBitmap.h"
robertphillips9a53fd72015-06-22 09:46:59 -070011#include "SkBitmapDevice.h"
mtklein19fcc742014-07-11 08:42:17 -070012#include "SkChecksum.h"
senorblanco@chromium.org11825292014-03-14 17:44:41 +000013#include "SkDevice.h"
senorblanco20311d42015-10-14 04:53:31 -070014#include "SkLocalMatrixImageFilter.h"
senorblanco8c874ee2015-03-20 06:38:17 -070015#include "SkMatrixImageFilter.h"
mtklein6c59d802015-09-09 09:09:53 -070016#include "SkOncePtr.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000017#include "SkReadBuffer.h"
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000018#include "SkRect.h"
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000019#include "SkTDynamicHash.h"
senorblanco55b6d8b2014-07-30 11:26:46 -070020#include "SkTInternalLList.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000021#include "SkValidationUtils.h"
mtklein1b249332015-07-07 12:21:21 -070022#include "SkWriteBuffer.h"
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +000023#if SK_SUPPORT_GPU
24#include "GrContext.h"
robertphillipsea461502015-05-26 11:38:03 -070025#include "GrDrawContext.h"
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +000026#include "SkGrPixelRef.h"
27#include "SkGr.h"
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +000028#endif
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000029
mtklein242397a2015-09-29 12:17:08 -070030#ifdef SK_BUILD_FOR_IOS
31 enum { kDefaultCacheSize = 2 * 1024 * 1024 };
32#else
33 enum { kDefaultCacheSize = 128 * 1024 * 1024 };
34#endif
35
reed18918632015-07-16 13:17:13 -070036#ifndef SK_IGNORE_TO_STRING
37void SkImageFilter::CropRect::toString(SkString* str) const {
38 if (!fFlags) {
39 return;
40 }
41
42 str->appendf("cropRect (");
43 if (fFlags & CropRect::kHasLeft_CropEdge) {
44 str->appendf("%.2f, ", fRect.fLeft);
45 } else {
46 str->appendf("X, ");
47 }
48 if (fFlags & CropRect::kHasTop_CropEdge) {
49 str->appendf("%.2f, ", fRect.fTop);
50 } else {
51 str->appendf("X, ");
52 }
senorblancoed7cf272015-07-16 15:19:11 -070053 if (fFlags & CropRect::kHasWidth_CropEdge) {
54 str->appendf("%.2f, ", fRect.width());
reed18918632015-07-16 13:17:13 -070055 } else {
56 str->appendf("X, ");
57 }
senorblancoed7cf272015-07-16 15:19:11 -070058 if (fFlags & CropRect::kHasHeight_CropEdge) {
59 str->appendf("%.2f", fRect.height());
reed18918632015-07-16 13:17:13 -070060 } else {
61 str->appendf("X");
62 }
63 str->appendf(") ");
64}
65#endif
66
senorblancod8ff5b32016-01-28 08:23:02 -080067void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds,
68 const SkMatrix& ctm,
reed18918632015-07-16 13:17:13 -070069 SkIRect* cropped) const {
70 *cropped = imageBounds;
71 if (fFlags) {
72 SkRect devCropR;
senorblancod8ff5b32016-01-28 08:23:02 -080073 ctm.mapRect(&devCropR, fRect);
reed18918632015-07-16 13:17:13 -070074 const SkIRect devICropR = devCropR.roundOut();
75
76 // Compute the left/top first, in case we have to read them to compute right/bottom
77 if (fFlags & kHasLeft_CropEdge) {
78 cropped->fLeft = devICropR.fLeft;
79 }
80 if (fFlags & kHasTop_CropEdge) {
81 cropped->fTop = devICropR.fTop;
82 }
senorblancoed7cf272015-07-16 15:19:11 -070083 if (fFlags & kHasWidth_CropEdge) {
reed18918632015-07-16 13:17:13 -070084 cropped->fRight = cropped->fLeft + devICropR.width();
85 }
senorblancoed7cf272015-07-16 15:19:11 -070086 if (fFlags & kHasHeight_CropEdge) {
reed18918632015-07-16 13:17:13 -070087 cropped->fBottom = cropped->fTop + devICropR.height();
88 }
89 }
reed18918632015-07-16 13:17:13 -070090}
91
92///////////////////////////////////////////////////////////////////////////////////////////////////
93
senorblanco55b6d8b2014-07-30 11:26:46 -070094static int32_t next_image_filter_unique_id() {
95 static int32_t gImageFilterUniqueID;
96
97 // Never return 0.
98 int32_t id;
99 do {
100 id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
101 } while (0 == id);
102 return id;
103}
104
senorblancobe129b22014-08-08 07:14:35 -0700105struct SkImageFilter::Cache::Key {
senorblanco55b6d8b2014-07-30 11:26:46 -0700106 Key(const uint32_t uniqueID, const SkMatrix& matrix, const SkIRect& clipBounds, uint32_t srcGenID)
107 : fUniqueID(uniqueID), fMatrix(matrix), fClipBounds(clipBounds), fSrcGenID(srcGenID) {
108 // Assert that Key is tightly-packed, since it is hashed.
bungeman99fe8222015-08-20 07:57:51 -0700109 static_assert(sizeof(Key) == sizeof(uint32_t) + sizeof(SkMatrix) + sizeof(SkIRect) +
110 sizeof(uint32_t), "image_filter_key_tight_packing");
senorblanco55b6d8b2014-07-30 11:26:46 -0700111 fMatrix.getType(); // force initialization of type, so hashes match
112 }
113 uint32_t fUniqueID;
114 SkMatrix fMatrix;
115 SkIRect fClipBounds;
116 uint32_t fSrcGenID;
117 bool operator==(const Key& other) const {
118 return fUniqueID == other.fUniqueID
119 && fMatrix == other.fMatrix
120 && fClipBounds == other.fClipBounds
121 && fSrcGenID == other.fSrcGenID;
122 }
123};
124
reedb959ec72014-07-17 07:03:09 -0700125SkImageFilter::Common::~Common() {
126 for (int i = 0; i < fInputs.count(); ++i) {
127 SkSafeUnref(fInputs[i]);
128 }
129}
130
131void SkImageFilter::Common::allocInputs(int count) {
132 const size_t size = count * sizeof(SkImageFilter*);
133 fInputs.reset(count);
134 sk_bzero(fInputs.get(), size);
135}
136
137void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) {
138 const size_t size = fInputs.count() * sizeof(SkImageFilter*);
139 memcpy(inputs, fInputs.get(), size);
140 sk_bzero(fInputs.get(), size);
141}
142
143bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
reed9fa60da2014-08-21 07:59:51 -0700144 const int count = buffer.readInt();
145 if (!buffer.validate(count >= 0)) {
146 return false;
reedb959ec72014-07-17 07:03:09 -0700147 }
reed9fa60da2014-08-21 07:59:51 -0700148 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
reedb959ec72014-07-17 07:03:09 -0700149 return false;
150 }
151
152 this->allocInputs(count);
153 for (int i = 0; i < count; i++) {
154 if (buffer.readBool()) {
155 fInputs[i] = buffer.readImageFilter();
156 }
157 if (!buffer.isValid()) {
158 return false;
159 }
160 }
161 SkRect rect;
162 buffer.readRect(&rect);
163 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
164 return false;
165 }
mtklein148ec592014-10-13 13:17:56 -0700166
reedb959ec72014-07-17 07:03:09 -0700167 uint32_t flags = buffer.readUInt();
168 fCropRect = CropRect(rect, flags);
senorblanco4a22a432015-03-18 13:14:54 -0700169 if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
170
171 (void) buffer.readUInt();
172 }
reedb959ec72014-07-17 07:03:09 -0700173 return buffer.isValid();
174}
175
176///////////////////////////////////////////////////////////////////////////////////////////////////
177
senorblanco24e06d52015-03-18 12:11:33 -0700178SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000179 : fInputCount(inputCount),
180 fInputs(new SkImageFilter*[inputCount]),
senorblanco55b6d8b2014-07-30 11:26:46 -0700181 fUsesSrcInput(false),
182 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)),
senorblanco24e06d52015-03-18 12:11:33 -0700183 fUniqueID(next_image_filter_unique_id()) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000184 for (int i = 0; i < inputCount; ++i) {
halcanary96fcdcc2015-08-27 07:41:13 -0700185 if (nullptr == inputs[i] || inputs[i]->usesSrcInput()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700186 fUsesSrcInput = true;
187 }
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000188 fInputs[i] = inputs[i];
189 SkSafeRef(fInputs[i]);
190 }
191}
192
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000193SkImageFilter::~SkImageFilter() {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000194 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000195 SkSafeUnref(fInputs[i]);
196 }
197 delete[] fInputs;
xidachen23526962016-02-01 05:27:16 -0800198 Cache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count());
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000199}
200
senorblanco55b6d8b2014-07-30 11:26:46 -0700201SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
senorblanco24e06d52015-03-18 12:11:33 -0700202 : fUsesSrcInput(false)
203 , fUniqueID(next_image_filter_unique_id()) {
reedb959ec72014-07-17 07:03:09 -0700204 Common common;
205 if (common.unflatten(buffer, inputCount)) {
206 fCropRect = common.cropRect();
207 fInputCount = common.inputCount();
halcanary385fe4d2015-08-26 13:07:48 -0700208 fInputs = new SkImageFilter* [fInputCount];
reedb959ec72014-07-17 07:03:09 -0700209 common.detachInputs(fInputs);
senorblanco55b6d8b2014-07-30 11:26:46 -0700210 for (int i = 0; i < fInputCount; ++i) {
halcanary96fcdcc2015-08-27 07:41:13 -0700211 if (nullptr == fInputs[i] || fInputs[i]->usesSrcInput()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700212 fUsesSrcInput = true;
213 }
214 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +0000215 } else {
216 fInputCount = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700217 fInputs = nullptr;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000218 }
219}
220
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000221void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000222 buffer.writeInt(fInputCount);
223 for (int i = 0; i < fInputCount; i++) {
senorblancob9519f82015-10-15 12:15:13 -0700224 SkImageFilter* input = this->getInput(i);
halcanary96fcdcc2015-08-27 07:41:13 -0700225 buffer.writeBool(input != nullptr);
226 if (input != nullptr) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000227 buffer.writeFlattenable(input);
228 }
229 }
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000230 buffer.writeRect(fCropRect.rect());
231 buffer.writeUInt(fCropRect.flags());
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000232}
233
robertphillips48e78462016-02-17 13:57:16 -0800234bool SkImageFilter::filterImageDeprecated(Proxy* proxy, const SkBitmap& src,
235 const Context& context,
236 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000237 SkASSERT(result);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000238 SkASSERT(offset);
senorblanco55b6d8b2014-07-30 11:26:46 -0700239 uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0;
senorblancobe129b22014-08-08 07:14:35 -0700240 Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID);
241 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700242 if (context.cache()->get(key, result, offset)) {
243 return true;
244 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000245 }
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000246 /*
247 * Give the proxy first shot at the filter. If it returns false, ask
248 * the filter to do it.
249 */
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000250 if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
robertphillips48e78462016-02-17 13:57:16 -0800251 this->onFilterImageDeprecated(proxy, src, context, result, offset)) {
senorblancobe129b22014-08-08 07:14:35 -0700252 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700253 context.cache()->set(key, *result, *offset);
xidachen23526962016-02-01 05:27:16 -0800254 SkAutoMutexAcquire mutex(fMutex);
255 fCacheKeys.push_back(key);
senorblanco55b6d8b2014-07-30 11:26:46 -0700256 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000257 return true;
258 }
259 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000260}
261
robertphillips48e78462016-02-17 13:57:16 -0800262bool SkImageFilter::filterInputDeprecated(int index, Proxy* proxy, const SkBitmap& src,
263 const Context& ctx,
264 SkBitmap* result, SkIPoint* offset) const {
senorblancob9519f82015-10-15 12:15:13 -0700265 SkImageFilter* input = this->getInput(index);
reedc9b5f8b2015-10-22 13:20:20 -0700266 if (!input) {
267 return true;
268 }
robertphillips48e78462016-02-17 13:57:16 -0800269 return input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset);
senorblancob9519f82015-10-15 12:15:13 -0700270}
271
senorblancod8ff5b32016-01-28 08:23:02 -0800272bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst,
273 MapDirection direction) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000274 SkASSERT(dst);
senorblancod8ff5b32016-01-28 08:23:02 -0800275 SkIRect bounds;
276 if (kReverse_MapDirection == direction) {
277 this->onFilterNodeBounds(src, ctm, &bounds, direction);
278 return this->onFilterBounds(bounds, ctm, dst, direction);
279 } else {
280 SkIRect temp;
281 if (!this->onFilterBounds(src, ctm, &bounds, direction)) {
282 return false;
283 }
284 this->onFilterNodeBounds(bounds, ctm, &temp, direction);
285 this->getCropRect().applyTo(temp, ctm, dst);
286 return true;
287 }
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000288}
289
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000290void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
291 if (0 == fInputCount) {
292 *dst = src;
293 return;
294 }
295 if (this->getInput(0)) {
296 this->getInput(0)->computeFastBounds(src, dst);
297 } else {
298 *dst = src;
299 }
300 for (int i = 1; i < fInputCount; i++) {
301 SkImageFilter* input = this->getInput(i);
302 if (input) {
303 SkRect bounds;
304 input->computeFastBounds(src, &bounds);
305 dst->join(bounds);
306 } else {
307 dst->join(src);
308 }
309 }
310}
311
senorblanco0abdf762015-08-20 11:10:41 -0700312bool SkImageFilter::canComputeFastBounds() const {
senorblanco0abdf762015-08-20 11:10:41 -0700313 for (int i = 0; i < fInputCount; i++) {
314 SkImageFilter* input = this->getInput(i);
315 if (input && !input->canComputeFastBounds()) {
316 return false;
317 }
318 }
319 return true;
320}
321
robertphillips48e78462016-02-17 13:57:16 -0800322bool SkImageFilter::onFilterImageDeprecated(Proxy*, const SkBitmap&, const Context&,
323 SkBitmap*, SkIPoint*) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000324 return false;
325}
326
327bool SkImageFilter::canFilterImageGPU() const {
bsalomon4a339522015-10-06 08:40:50 -0700328 return this->asFragmentProcessor(nullptr, nullptr, SkMatrix::I(), SkIRect());
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000329}
330
robertphillips48e78462016-02-17 13:57:16 -0800331bool SkImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx,
332 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000333#if SK_SUPPORT_GPU
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000334 SkBitmap input = src;
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000335 SkASSERT(fInputCount == 1);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000336 SkIPoint srcOffset = SkIPoint::Make(0, 0);
robertphillips48e78462016-02-17 13:57:16 -0800337 if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000338 return false;
339 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000340 GrTexture* srcTexture = input.getTexture();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000341 SkIRect bounds;
robertphillips48e78462016-02-17 13:57:16 -0800342 if (!this->applyCropRectDeprecated(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000343 return false;
344 }
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000345 GrContext* context = srcTexture->getContext();
346
bsalomonf2703d82014-10-28 14:33:06 -0700347 GrSurfaceDesc desc;
348 desc.fFlags = kRenderTarget_GrSurfaceFlag,
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000349 desc.fWidth = bounds.width();
350 desc.fHeight = bounds.height();
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000351 desc.fConfig = kRGBA_8888_GrPixelConfig;
352
reed4e23cda2016-01-11 10:56:59 -0800353 SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));
bsalomone3059732014-10-14 11:47:22 -0700354 if (!dst) {
senorblanco673d9732014-08-15 10:48:43 -0700355 return false;
356 }
joshualitt25d9c152015-02-18 12:29:52 -0800357
joshualittb0a8a372014-09-23 09:50:21 -0700358 GrFragmentProcessor* fp;
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000359 offset->fX = bounds.left();
360 offset->fY = bounds.top();
361 bounds.offset(-srcOffset);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000362 SkMatrix matrix(ctx.ctm());
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000363 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
joshualitt85ff25e2015-07-08 09:10:03 -0700364 GrPaint paint;
bsalomon4a339522015-10-06 08:40:50 -0700365 if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) {
bsalomonb501ecd2014-10-14 08:40:57 -0700366 SkASSERT(fp);
bsalomonac856c92015-08-27 06:30:17 -0700367 paint.addColorFragmentProcessor(fp)->unref();
egdanielc4b72722015-11-23 13:20:41 -0800368 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000369
robertphillips2e1e51f2015-10-15 08:01:48 -0700370 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));
robertphillipsea461502015-05-26 11:38:03 -0700371 if (drawContext) {
senorblancoeae84c22016-01-26 08:41:02 -0800372 SkRect srcRect = SkRect::Make(bounds);
373 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
374 GrClip clip(dstRect);
bsalomona2e69fc2015-11-05 10:41:43 -0800375 drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);
robertphillipsea461502015-05-26 11:38:03 -0700376
robertphillips1de87df2016-01-14 06:03:29 -0800377 GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result);
robertphillipsea461502015-05-26 11:38:03 -0700378 return true;
379 }
bsalomonb501ecd2014-10-14 08:40:57 -0700380 }
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000381#endif
bsalomonb501ecd2014-10-14 08:40:57 -0700382 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000383}
384
senorblancoa544eda2015-12-07 07:48:34 -0800385bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
386 SkASSERT(nullptr != filterPtr);
387 if (!this->isColorFilterNode(filterPtr)) {
388 return false;
389 }
390 if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) {
391 (*filterPtr)->unref();
392 return false;
393 }
394 return true;
395}
396
senorblancoafec27f2016-02-16 09:11:18 -0800397bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
398 SkIRect* dstBounds) const {
399 this->onFilterNodeBounds(srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection);
senorblancod8ff5b32016-01-28 08:23:02 -0800400 fCropRect.applyTo(*dstBounds, ctx.ctm(), dstBounds);
401 // Intersect against the clip bounds, in case the crop rect has
402 // grown the bounds beyond the original clip. This can happen for
403 // example in tiling, where the clip is much smaller than the filtered
404 // primitive. If we didn't do this, we would be processing the filter
405 // at the full crop rect size in every tile.
406 return dstBounds->intersect(ctx.clipBounds());
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000407}
408
robertphillips48e78462016-02-17 13:57:16 -0800409bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, const SkBitmap& src,
410 SkIPoint* srcOffset, SkIRect* bounds,
411 SkBitmap* dst) const {
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000412 SkIRect srcBounds;
413 src.getBounds(&srcBounds);
414 srcBounds.offset(*srcOffset);
senorblancodb64af32015-12-09 10:11:43 -0800415 SkIRect dstBounds;
416 this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
senorblancod8ff5b32016-01-28 08:23:02 -0800417 fCropRect.applyTo(dstBounds, ctx.ctm(), bounds);
418 if (!bounds->intersect(ctx.clipBounds())) {
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000419 return false;
420 }
reed18918632015-07-16 13:17:13 -0700421
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000422 if (srcBounds.contains(*bounds)) {
423 *dst = src;
424 return true;
425 } else {
426 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
427 if (!device) {
428 return false;
429 }
430 SkCanvas canvas(device);
431 canvas.clear(0x00000000);
432 canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
433 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
434 *dst = device->accessBitmap(false);
435 return true;
436 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000437}
438
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000439bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
senorblancod8ff5b32016-01-28 08:23:02 -0800440 SkIRect* dst, MapDirection direction) const {
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000441 if (fInputCount < 1) {
senorblanco8f3937d2014-10-29 12:36:32 -0700442 *dst = src;
443 return true;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000444 }
445
senorblancod8ff5b32016-01-28 08:23:02 -0800446 SkIRect totalBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000447 for (int i = 0; i < fInputCount; ++i) {
448 SkImageFilter* filter = this->getInput(i);
senorblancod8ff5b32016-01-28 08:23:02 -0800449 SkIRect rect = src;
450 if (filter && !filter->filterBounds(src, ctm, &rect, direction)) {
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000451 return false;
452 }
453 if (0 == i) {
senorblancodb64af32015-12-09 10:11:43 -0800454 totalBounds = rect;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000455 } else {
senorblancodb64af32015-12-09 10:11:43 -0800456 totalBounds.join(rect);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000457 }
458 }
459
460 // don't modify dst until now, so we don't accidentally change it in the
461 // loop, but then return false on the next filter.
senorblancodb64af32015-12-09 10:11:43 -0800462 *dst = totalBounds;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000463 return true;
464}
465
senorblancodb64af32015-12-09 10:11:43 -0800466void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
467 SkIRect* dst, MapDirection) const {
468 *dst = src;
469}
470
471
472SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
senorblancodb64af32015-12-09 10:11:43 -0800473 SkIRect clipBounds;
474 this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds,
475 MapDirection::kReverse_MapDirection);
reed4e23cda2016-01-11 10:56:59 -0800476 return Context(ctx.ctm(), clipBounds, ctx.cache());
senorblancodb64af32015-12-09 10:11:43 -0800477}
478
bsalomon4a339522015-10-06 08:40:50 -0700479bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*,
joshualitt85ff25e2015-07-08 09:10:03 -0700480 const SkMatrix&, const SkIRect&) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000481 return false;
482}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000483
senorblanco8c874ee2015-03-20 06:38:17 -0700484SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
485 SkFilterQuality filterQuality,
486 SkImageFilter* input) {
487 return SkMatrixImageFilter::Create(matrix, filterQuality, input);
488}
489
reed94dd7a52015-10-14 07:49:35 -0700490SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const {
491 // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter
492 // is *always* treated as a const ptr. Hence the const-cast here.
493 //
494 return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this));
senorblanco20311d42015-10-14 04:53:31 -0700495}
496
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000497#if SK_SUPPORT_GPU
498
robertphillips48e78462016-02-17 13:57:16 -0800499bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy,
500 const SkBitmap& src, const Context& ctx,
501 SkBitmap* result, SkIPoint* offset) const {
senorblanco9a70b6e2015-10-16 11:35:14 -0700502 SkImageFilter* input = this->getInput(index);
503 if (!input) {
504 return true;
505 }
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000506 // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
507 // matrix with no clip and that the matrix, clip, and render target set before this function was
508 // called are restored before we return to the caller.
509 GrContext* context = src.getTexture()->getContext();
robertphillips48e78462016-02-17 13:57:16 -0800510 if (input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset)) {
senorblanco7b87ee72015-10-26 06:55:47 -0700511 if (!result->getTexture()) {
512 const SkImageInfo info = result->info();
513 if (kUnknown_SkColorType == info.colorType()) {
514 return false;
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000515 }
senorblanco7b87ee72015-10-26 06:55:47 -0700516 SkAutoTUnref<GrTexture> resultTex(
bsalomon45eefcf2016-01-05 08:39:28 -0800517 GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter()));
518 if (!resultTex) {
519 return false;
520 }
senorblanco7b87ee72015-10-26 06:55:47 -0700521 result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000522 }
senorblanco7b87ee72015-10-26 06:55:47 -0700523 return true;
524 } else {
525 return false;
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000526 }
527}
528#endif
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000529
senorblanco55b6d8b2014-07-30 11:26:46 -0700530namespace {
531
mtklein242397a2015-09-29 12:17:08 -0700532class CacheImpl : public SkImageFilter::Cache {
senorblanco55b6d8b2014-07-30 11:26:46 -0700533public:
mtklein242397a2015-09-29 12:17:08 -0700534 CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) {
535 }
536 virtual ~CacheImpl() {
537 SkTDynamicHash<Value, Key>::Iter iter(&fLookup);
538
539 while (!iter.done()) {
540 Value* v = &*iter;
541 ++iter;
542 delete v;
senorblanco55b6d8b2014-07-30 11:26:46 -0700543 }
mtklein242397a2015-09-29 12:17:08 -0700544 }
545 struct Value {
546 Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset)
547 : fKey(key), fBitmap(bitmap), fOffset(offset) {}
548 Key fKey;
549 SkBitmap fBitmap;
550 SkIPoint fOffset;
551 static const Key& GetKey(const Value& v) {
552 return v.fKey;
553 }
554 static uint32_t Hash(const Key& key) {
555 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
556 }
557 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
senorblanco55b6d8b2014-07-30 11:26:46 -0700558 };
mtklein242397a2015-09-29 12:17:08 -0700559 bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override {
560 SkAutoMutexAcquire mutex(fMutex);
561 if (Value* v = fLookup.find(key)) {
562 *result = v->fBitmap;
563 *offset = v->fOffset;
564 if (v != fLRU.head()) {
565 fLRU.remove(v);
566 fLRU.addToHead(v);
567 }
568 return true;
senorblanco55b6d8b2014-07-30 11:26:46 -0700569 }
mtklein242397a2015-09-29 12:17:08 -0700570 return false;
571 }
572 void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override {
573 SkAutoMutexAcquire mutex(fMutex);
574 if (Value* v = fLookup.find(key)) {
575 removeInternal(v);
576 }
577 Value* v = new Value(key, result, offset);
578 fLookup.add(v);
579 fLRU.addToHead(v);
580 fCurrentBytes += result.getSize();
581 while (fCurrentBytes > fMaxBytes) {
582 Value* tail = fLRU.tail();
583 SkASSERT(tail);
584 if (tail == v) {
585 break;
586 }
587 removeInternal(tail);
588 }
senorblanco55b6d8b2014-07-30 11:26:46 -0700589 }
mtklein75135d82015-09-29 12:15:52 -0700590
mtklein242397a2015-09-29 12:17:08 -0700591 void purge() override {
592 SkAutoMutexAcquire mutex(fMutex);
593 while (fCurrentBytes > 0) {
594 Value* tail = fLRU.tail();
595 SkASSERT(tail);
596 this->removeInternal(tail);
597 }
598 }
mtklein75135d82015-09-29 12:15:52 -0700599
xidachen23526962016-02-01 05:27:16 -0800600 void purgeByKeys(const Key keys[], int count) override {
xidachen62776912015-12-21 07:29:03 -0800601 SkAutoMutexAcquire mutex(fMutex);
xidachen23526962016-02-01 05:27:16 -0800602 for (int i = 0; i < count; i++) {
603 if (Value* v = fLookup.find(keys[i])) {
604 this->removeInternal(v);
xidachen62776912015-12-21 07:29:03 -0800605 }
xidachen62776912015-12-21 07:29:03 -0800606 }
607 }
608
mtklein75135d82015-09-29 12:15:52 -0700609private:
mtklein242397a2015-09-29 12:17:08 -0700610 void removeInternal(Value* v) {
611 fCurrentBytes -= v->fBitmap.getSize();
612 fLRU.remove(v);
613 fLookup.remove(v->fKey);
614 delete v;
senorblanco55b6d8b2014-07-30 11:26:46 -0700615 }
616private:
xidachen62776912015-12-21 07:29:03 -0800617 SkTDynamicHash<Value, Key> fLookup;
xidachen62776912015-12-21 07:29:03 -0800618 mutable SkTInternalLList<Value> fLRU;
619 size_t fMaxBytes;
620 size_t fCurrentBytes;
621 mutable SkMutex fMutex;
senorblanco55b6d8b2014-07-30 11:26:46 -0700622};
623
senorblanco55b6d8b2014-07-30 11:26:46 -0700624} // namespace
625
senorblancobe129b22014-08-08 07:14:35 -0700626SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) {
mtklein242397a2015-09-29 12:17:08 -0700627 return new CacheImpl(maxBytes);
senorblanco55b6d8b2014-07-30 11:26:46 -0700628}
629
mtklein6c59d802015-09-09 09:09:53 -0700630SK_DECLARE_STATIC_ONCE_PTR(SkImageFilter::Cache, cache);
senorblancobe129b22014-08-08 07:14:35 -0700631SkImageFilter::Cache* SkImageFilter::Cache::Get() {
mtklein242397a2015-09-29 12:17:08 -0700632 return cache.get([]{ return SkImageFilter::Cache::Create(kDefaultCacheSize); });
633}
634
635void SkImageFilter::PurgeCache() {
636 Cache::Get()->purge();
reed67ca2a92015-05-20 13:22:58 -0700637}
reed2c55d7b2015-06-09 08:18:39 -0700638
639///////////////////////////////////////////////////////////////////////////////////////////////////
640
senorblancoa9fbd162016-01-11 14:09:09 -0800641SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h, TileUsage usage) {
reed2c55d7b2015-06-09 08:18:39 -0700642 SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
senorblancoa9fbd162016-01-11 14:09:09 -0800643 kPossible_TileUsage == usage ? SkBaseDevice::kPossible_TileUsage
644 : SkBaseDevice::kNever_TileUsage,
reed2c55d7b2015-06-09 08:18:39 -0700645 kUnknown_SkPixelGeometry,
reed70ee31b2015-12-10 13:44:45 -0800646 false, /* preserveLCDText */
reed2c55d7b2015-06-09 08:18:39 -0700647 true /*forImageFilter*/);
halcanary96fcdcc2015-08-27 07:41:13 -0700648 SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, nullptr);
649 if (nullptr == dev) {
robertphillips9a53fd72015-06-22 09:46:59 -0700650 const SkSurfaceProps surfaceProps(fDevice->fSurfaceProps.flags(),
651 kUnknown_SkPixelGeometry);
652 dev = SkBitmapDevice::Create(cinfo.fInfo, surfaceProps);
reed2c55d7b2015-06-09 08:18:39 -0700653 }
654 return dev;
655}
656
reed88d064d2015-10-12 11:30:02 -0700657bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
reed2c55d7b2015-06-09 08:18:39 -0700658 const SkImageFilter::Context& ctx,
659 SkBitmap* result, SkIPoint* offset) {
660 return fDevice->filterImage(filter, src, ctx, result, offset);
661}
mtklein1b249332015-07-07 12:21:21 -0700662