blob: 91b3bc3d4afad1efc7da7a6e95ded8cf3752a82b [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"
mtklein19fcc742014-07-11 08:42:17 -070011#include "SkChecksum.h"
senorblanco@chromium.org11825292014-03-14 17:44:41 +000012#include "SkDevice.h"
senorblanco55b6d8b2014-07-30 11:26:46 -070013#include "SkLazyPtr.h"
senorblanco8c874ee2015-03-20 06:38:17 -070014#include "SkMatrixImageFilter.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000015#include "SkReadBuffer.h"
16#include "SkWriteBuffer.h"
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000017#include "SkRect.h"
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +000018#include "SkTDynamicHash.h"
senorblanco55b6d8b2014-07-30 11:26:46 -070019#include "SkTInternalLList.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000020#include "SkValidationUtils.h"
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +000021#if SK_SUPPORT_GPU
22#include "GrContext.h"
robertphillipsea461502015-05-26 11:38:03 -070023#include "GrDrawContext.h"
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +000024#include "SkGrPixelRef.h"
25#include "SkGr.h"
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +000026#endif
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000027
stephana195f62d2015-04-09 07:57:54 -070028#ifdef SK_BUILD_FOR_IOS
29 enum { kDefaultCacheSize = 2 * 1024 * 1024 };
30#else
31 enum { kDefaultCacheSize = 128 * 1024 * 1024 };
32#endif
senorblanco55b6d8b2014-07-30 11:26:46 -070033
34static int32_t next_image_filter_unique_id() {
35 static int32_t gImageFilterUniqueID;
36
37 // Never return 0.
38 int32_t id;
39 do {
40 id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
41 } while (0 == id);
42 return id;
43}
44
senorblancobe129b22014-08-08 07:14:35 -070045struct SkImageFilter::Cache::Key {
senorblanco55b6d8b2014-07-30 11:26:46 -070046 Key(const uint32_t uniqueID, const SkMatrix& matrix, const SkIRect& clipBounds, uint32_t srcGenID)
47 : fUniqueID(uniqueID), fMatrix(matrix), fClipBounds(clipBounds), fSrcGenID(srcGenID) {
48 // Assert that Key is tightly-packed, since it is hashed.
49 SK_COMPILE_ASSERT(sizeof(Key) == sizeof(uint32_t) + sizeof(SkMatrix) + sizeof(SkIRect) +
50 sizeof(uint32_t), image_filter_key_tight_packing);
51 fMatrix.getType(); // force initialization of type, so hashes match
52 }
53 uint32_t fUniqueID;
54 SkMatrix fMatrix;
55 SkIRect fClipBounds;
56 uint32_t fSrcGenID;
57 bool operator==(const Key& other) const {
58 return fUniqueID == other.fUniqueID
59 && fMatrix == other.fMatrix
60 && fClipBounds == other.fClipBounds
61 && fSrcGenID == other.fSrcGenID;
62 }
63};
64
reedb959ec72014-07-17 07:03:09 -070065SkImageFilter::Common::~Common() {
66 for (int i = 0; i < fInputs.count(); ++i) {
67 SkSafeUnref(fInputs[i]);
68 }
69}
70
71void SkImageFilter::Common::allocInputs(int count) {
72 const size_t size = count * sizeof(SkImageFilter*);
73 fInputs.reset(count);
74 sk_bzero(fInputs.get(), size);
75}
76
77void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) {
78 const size_t size = fInputs.count() * sizeof(SkImageFilter*);
79 memcpy(inputs, fInputs.get(), size);
80 sk_bzero(fInputs.get(), size);
81}
82
83bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
reed9fa60da2014-08-21 07:59:51 -070084 const int count = buffer.readInt();
85 if (!buffer.validate(count >= 0)) {
86 return false;
reedb959ec72014-07-17 07:03:09 -070087 }
reed9fa60da2014-08-21 07:59:51 -070088 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
reedb959ec72014-07-17 07:03:09 -070089 return false;
90 }
91
92 this->allocInputs(count);
93 for (int i = 0; i < count; i++) {
94 if (buffer.readBool()) {
95 fInputs[i] = buffer.readImageFilter();
96 }
97 if (!buffer.isValid()) {
98 return false;
99 }
100 }
101 SkRect rect;
102 buffer.readRect(&rect);
103 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
104 return false;
105 }
mtklein148ec592014-10-13 13:17:56 -0700106
reedb959ec72014-07-17 07:03:09 -0700107 uint32_t flags = buffer.readUInt();
108 fCropRect = CropRect(rect, flags);
senorblanco4a22a432015-03-18 13:14:54 -0700109 if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
110
111 (void) buffer.readUInt();
112 }
reedb959ec72014-07-17 07:03:09 -0700113 return buffer.isValid();
114}
115
116///////////////////////////////////////////////////////////////////////////////////////////////////
117
senorblanco24e06d52015-03-18 12:11:33 -0700118SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000119 : fInputCount(inputCount),
120 fInputs(new SkImageFilter*[inputCount]),
senorblanco55b6d8b2014-07-30 11:26:46 -0700121 fUsesSrcInput(false),
122 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)),
senorblanco24e06d52015-03-18 12:11:33 -0700123 fUniqueID(next_image_filter_unique_id()) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000124 for (int i = 0; i < inputCount; ++i) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700125 if (NULL == inputs[i] || inputs[i]->usesSrcInput()) {
126 fUsesSrcInput = true;
127 }
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000128 fInputs[i] = inputs[i];
129 SkSafeRef(fInputs[i]);
130 }
131}
132
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000133SkImageFilter::~SkImageFilter() {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000134 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000135 SkSafeUnref(fInputs[i]);
136 }
137 delete[] fInputs;
138}
139
senorblanco55b6d8b2014-07-30 11:26:46 -0700140SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
senorblanco24e06d52015-03-18 12:11:33 -0700141 : fUsesSrcInput(false)
142 , fUniqueID(next_image_filter_unique_id()) {
reedb959ec72014-07-17 07:03:09 -0700143 Common common;
144 if (common.unflatten(buffer, inputCount)) {
145 fCropRect = common.cropRect();
146 fInputCount = common.inputCount();
147 fInputs = SkNEW_ARRAY(SkImageFilter*, fInputCount);
148 common.detachInputs(fInputs);
senorblanco55b6d8b2014-07-30 11:26:46 -0700149 for (int i = 0; i < fInputCount; ++i) {
150 if (NULL == fInputs[i] || fInputs[i]->usesSrcInput()) {
151 fUsesSrcInput = true;
152 }
153 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +0000154 } else {
155 fInputCount = 0;
156 fInputs = NULL;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000157 }
158}
159
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000160void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000161 buffer.writeInt(fInputCount);
162 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000163 SkImageFilter* input = getInput(i);
164 buffer.writeBool(input != NULL);
165 if (input != NULL) {
166 buffer.writeFlattenable(input);
167 }
168 }
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000169 buffer.writeRect(fCropRect.rect());
170 buffer.writeUInt(fCropRect.flags());
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000171}
172
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000173bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000174 const Context& context,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000175 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000176 SkASSERT(result);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000177 SkASSERT(offset);
senorblanco55b6d8b2014-07-30 11:26:46 -0700178 uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0;
senorblancobe129b22014-08-08 07:14:35 -0700179 Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID);
180 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700181 if (context.cache()->get(key, result, offset)) {
182 return true;
183 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000184 }
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000185 /*
186 * Give the proxy first shot at the filter. If it returns false, ask
187 * the filter to do it.
188 */
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000189 if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
190 this->onFilterImage(proxy, src, context, result, offset)) {
senorblancobe129b22014-08-08 07:14:35 -0700191 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700192 context.cache()->set(key, *result, *offset);
193 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000194 return true;
195 }
196 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000197}
198
199bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000200 SkIRect* dst) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000201 SkASSERT(dst);
202 return this->onFilterBounds(src, ctm, dst);
203}
204
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000205void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
206 if (0 == fInputCount) {
207 *dst = src;
208 return;
209 }
210 if (this->getInput(0)) {
211 this->getInput(0)->computeFastBounds(src, dst);
212 } else {
213 *dst = src;
214 }
215 for (int i = 1; i < fInputCount; i++) {
216 SkImageFilter* input = this->getInput(i);
217 if (input) {
218 SkRect bounds;
219 input->computeFastBounds(src, &bounds);
220 dst->join(bounds);
221 } else {
222 dst->join(src);
223 }
224 }
225}
226
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000227bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000228 SkBitmap*, SkIPoint*) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000229 return false;
230}
231
232bool SkImageFilter::canFilterImageGPU() const {
joshualittb0a8a372014-09-23 09:50:21 -0700233 return this->asFragmentProcessor(NULL, NULL, SkMatrix::I(), SkIRect());
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000234}
235
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000236bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000237 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000238#if SK_SUPPORT_GPU
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000239 SkBitmap input = src;
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000240 SkASSERT(fInputCount == 1);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000241 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000242 if (this->getInput(0) &&
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000243 !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000244 return false;
245 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000246 GrTexture* srcTexture = input.getTexture();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000247 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000248 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000249 return false;
250 }
251 SkRect srcRect = SkRect::Make(bounds);
252 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000253 GrContext* context = srcTexture->getContext();
254
bsalomonf2703d82014-10-28 14:33:06 -0700255 GrSurfaceDesc desc;
256 desc.fFlags = kRenderTarget_GrSurfaceFlag,
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000257 desc.fWidth = bounds.width();
258 desc.fHeight = bounds.height();
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000259 desc.fConfig = kRGBA_8888_GrPixelConfig;
260
bsalomond309e7a2015-04-30 14:18:54 -0700261 SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
262 desc, GrTextureProvider::kApprox_ScratchTexMatch));
bsalomone3059732014-10-14 11:47:22 -0700263 if (!dst) {
senorblanco673d9732014-08-15 10:48:43 -0700264 return false;
265 }
joshualitt25d9c152015-02-18 12:29:52 -0800266
joshualitt570d2f82015-02-25 13:19:48 -0800267 // setup new clip
268 GrClip clip(dstRect);
269
joshualittb0a8a372014-09-23 09:50:21 -0700270 GrFragmentProcessor* fp;
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000271 offset->fX = bounds.left();
272 offset->fY = bounds.top();
273 bounds.offset(-srcOffset);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000274 SkMatrix matrix(ctx.ctm());
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000275 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
bsalomonb501ecd2014-10-14 08:40:57 -0700276 if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) {
277 SkASSERT(fp);
278 GrPaint paint;
279 paint.addColorProcessor(fp)->unref();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000280
robertphillipsea461502015-05-26 11:38:03 -0700281 GrDrawContext* drawContext = context->drawContext();
282 if (drawContext) {
283 drawContext->drawNonAARectToRect(dst->asRenderTarget(), clip, paint, SkMatrix::I(),
284 dstRect, srcRect);
285
286 WrapTexture(dst, bounds.width(), bounds.height(), result);
287 return true;
288 }
bsalomonb501ecd2014-10-14 08:40:57 -0700289 }
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000290#endif
bsalomonb501ecd2014-10-14 08:40:57 -0700291 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000292}
293
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000294bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src,
295 const SkIPoint& srcOffset, SkIRect* bounds) const {
296 SkIRect srcBounds;
297 src.getBounds(&srcBounds);
298 srcBounds.offset(srcOffset);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000299 SkRect cropRect;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000300 ctx.ctm().mapRect(&cropRect, fCropRect.rect());
reedb07a94f2014-11-19 05:03:18 -0800301 const SkIRect cropRectI = cropRect.roundOut();
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000302 uint32_t flags = fCropRect.flags();
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000303 if (flags & CropRect::kHasLeft_CropEdge) srcBounds.fLeft = cropRectI.fLeft;
304 if (flags & CropRect::kHasTop_CropEdge) srcBounds.fTop = cropRectI.fTop;
305 if (flags & CropRect::kHasRight_CropEdge) srcBounds.fRight = cropRectI.fRight;
306 if (flags & CropRect::kHasBottom_CropEdge) srcBounds.fBottom = cropRectI.fBottom;
307 if (!srcBounds.intersect(ctx.clipBounds())) {
308 return false;
309 }
310 *bounds = srcBounds;
311 return true;
312}
313
314bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
315 SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const {
316 SkIRect srcBounds;
317 src.getBounds(&srcBounds);
318 srcBounds.offset(*srcOffset);
319 SkRect cropRect;
320 ctx.ctm().mapRect(&cropRect, fCropRect.rect());
reedb07a94f2014-11-19 05:03:18 -0800321 const SkIRect cropRectI = cropRect.roundOut();
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000322 uint32_t flags = fCropRect.flags();
323 *bounds = srcBounds;
324 if (flags & CropRect::kHasLeft_CropEdge) bounds->fLeft = cropRectI.fLeft;
325 if (flags & CropRect::kHasTop_CropEdge) bounds->fTop = cropRectI.fTop;
326 if (flags & CropRect::kHasRight_CropEdge) bounds->fRight = cropRectI.fRight;
327 if (flags & CropRect::kHasBottom_CropEdge) bounds->fBottom = cropRectI.fBottom;
328 if (!bounds->intersect(ctx.clipBounds())) {
329 return false;
330 }
331 if (srcBounds.contains(*bounds)) {
332 *dst = src;
333 return true;
334 } else {
335 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
336 if (!device) {
337 return false;
338 }
339 SkCanvas canvas(device);
340 canvas.clear(0x00000000);
341 canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
342 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
343 *dst = device->accessBitmap(false);
344 return true;
345 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000346}
347
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000348bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000349 SkIRect* dst) const {
350 if (fInputCount < 1) {
senorblanco8f3937d2014-10-29 12:36:32 -0700351 *dst = src;
352 return true;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000353 }
354
355 SkIRect bounds;
356 for (int i = 0; i < fInputCount; ++i) {
357 SkImageFilter* filter = this->getInput(i);
358 SkIRect rect = src;
359 if (filter && !filter->filterBounds(src, ctm, &rect)) {
360 return false;
361 }
362 if (0 == i) {
363 bounds = rect;
364 } else {
365 bounds.join(rect);
366 }
367 }
368
369 // don't modify dst until now, so we don't accidentally change it in the
370 // loop, but then return false on the next filter.
371 *dst = bounds;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000372 return true;
373}
374
joshualittb0a8a372014-09-23 09:50:21 -0700375bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
376 const SkIRect&) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000377 return false;
378}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000379
senorblanco8c874ee2015-03-20 06:38:17 -0700380SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
381 SkFilterQuality filterQuality,
382 SkImageFilter* input) {
383 return SkMatrixImageFilter::Create(matrix, filterQuality, input);
384}
385
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000386#if SK_SUPPORT_GPU
387
388void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
389 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000390 result->setInfo(info);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000391 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
392}
393
394bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000395 const SkBitmap& src, const Context& ctx,
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000396 SkBitmap* result, SkIPoint* offset) const {
397 // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
398 // matrix with no clip and that the matrix, clip, and render target set before this function was
399 // called are restored before we return to the caller.
400 GrContext* context = src.getTexture()->getContext();
joshualitt570d2f82015-02-25 13:19:48 -0800401
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000402 if (this->canFilterImageGPU()) {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000403 return this->filterImageGPU(proxy, src, ctx, result, offset);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000404 } else {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000405 if (this->filterImage(proxy, src, ctx, result, offset)) {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000406 if (!result->getTexture()) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000407 const SkImageInfo info = result->info();
408 if (kUnknown_SkColorType == info.colorType()) {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000409 return false;
410 }
bsalomonbcf0a522014-10-08 08:40:09 -0700411 SkAutoTUnref<GrTexture> resultTex(GrRefCachedBitmapTexture(context, *result, NULL));
412 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, resultTex)))->unref();
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000413 }
414 return true;
415 } else {
416 return false;
417 }
418 }
419}
420#endif
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000421
senorblanco55b6d8b2014-07-30 11:26:46 -0700422namespace {
423
senorblancobe129b22014-08-08 07:14:35 -0700424class CacheImpl : public SkImageFilter::Cache {
senorblanco55b6d8b2014-07-30 11:26:46 -0700425public:
senorblancobe129b22014-08-08 07:14:35 -0700426 CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700427 }
senorblancobe129b22014-08-08 07:14:35 -0700428 virtual ~CacheImpl() {
senorblanco55b6d8b2014-07-30 11:26:46 -0700429 SkTDynamicHash<Value, Key>::Iter iter(&fLookup);
430
431 while (!iter.done()) {
432 Value* v = &*iter;
433 ++iter;
434 delete v;
435 }
436 }
437 struct Value {
438 Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset)
439 : fKey(key), fBitmap(bitmap), fOffset(offset) {}
440 Key fKey;
441 SkBitmap fBitmap;
442 SkIPoint fOffset;
443 static const Key& GetKey(const Value& v) {
444 return v.fKey;
445 }
446 static uint32_t Hash(const Key& key) {
447 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
448 }
449 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
450 };
mtklein36352bf2015-03-25 18:17:31 -0700451 bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override {
senorblanco55b6d8b2014-07-30 11:26:46 -0700452 SkAutoMutexAcquire mutex(fMutex);
453 if (Value* v = fLookup.find(key)) {
454 *result = v->fBitmap;
455 *offset = v->fOffset;
456 if (v != fLRU.head()) {
457 fLRU.remove(v);
458 fLRU.addToHead(v);
459 }
460 return true;
461 }
462 return false;
463 }
mtklein36352bf2015-03-25 18:17:31 -0700464 void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override {
senorblanco55b6d8b2014-07-30 11:26:46 -0700465 SkAutoMutexAcquire mutex(fMutex);
466 if (Value* v = fLookup.find(key)) {
467 removeInternal(v);
468 }
469 Value* v = new Value(key, result, offset);
470 fLookup.add(v);
471 fLRU.addToHead(v);
472 fCurrentBytes += result.getSize();
473 while (fCurrentBytes > fMaxBytes) {
474 Value* tail = fLRU.tail();
475 SkASSERT(tail);
476 if (tail == v) {
477 break;
478 }
479 removeInternal(tail);
480 }
481 }
reed67ca2a92015-05-20 13:22:58 -0700482
483 void purge() override {
484 SkAutoMutexAcquire mutex(fMutex);
485 while (fCurrentBytes > 0) {
486 Value* tail = fLRU.tail();
487 SkASSERT(tail);
488 this->removeInternal(tail);
489 }
490 }
491
senorblanco55b6d8b2014-07-30 11:26:46 -0700492private:
493 void removeInternal(Value* v) {
494 fCurrentBytes -= v->fBitmap.getSize();
495 fLRU.remove(v);
496 fLookup.remove(v->fKey);
497 delete v;
498 }
499private:
500 SkTDynamicHash<Value, Key> fLookup;
501 mutable SkTInternalLList<Value> fLRU;
502 size_t fMaxBytes;
503 size_t fCurrentBytes;
504 mutable SkMutex fMutex;
505};
506
senorblancobe129b22014-08-08 07:14:35 -0700507SkImageFilter::Cache* CreateCache() {
508 return SkImageFilter::Cache::Create(kDefaultCacheSize);
senorblanco55b6d8b2014-07-30 11:26:46 -0700509}
510
511} // namespace
512
senorblancobe129b22014-08-08 07:14:35 -0700513SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) {
514 return SkNEW_ARGS(CacheImpl, (maxBytes));
senorblanco55b6d8b2014-07-30 11:26:46 -0700515}
516
mtklein148ec592014-10-13 13:17:56 -0700517SK_DECLARE_STATIC_LAZY_PTR(SkImageFilter::Cache, cache, CreateCache);
518
senorblancobe129b22014-08-08 07:14:35 -0700519SkImageFilter::Cache* SkImageFilter::Cache::Get() {
senorblanco55b6d8b2014-07-30 11:26:46 -0700520 return cache.get();
521}
reed67ca2a92015-05-20 13:22:58 -0700522
523void SkImageFilter::PurgeCache() {
524 cache.get()->purge();
525}