blob: 178d892b610235be8583b28e8648e6c2f27fd39c [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"
senorblanco55b6d8b2014-07-30 11:26:46 -070014#include "SkLazyPtr.h"
senorblanco8c874ee2015-03-20 06:38:17 -070015#include "SkMatrixImageFilter.h"
mtklein1b249332015-07-07 12:21:21 -070016#include "SkMutex.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
stephana195f62d2015-04-09 07:57:54 -070030#ifdef SK_BUILD_FOR_IOS
31 enum { kDefaultCacheSize = 2 * 1024 * 1024 };
mtklein1b249332015-07-07 12:21:21 -070032#else
stephana195f62d2015-04-09 07:57:54 -070033 enum { kDefaultCacheSize = 128 * 1024 * 1024 };
mtklein1b249332015-07-07 12:21:21 -070034#endif
senorblanco55b6d8b2014-07-30 11:26:46 -070035
36static int32_t next_image_filter_unique_id() {
37 static int32_t gImageFilterUniqueID;
38
39 // Never return 0.
40 int32_t id;
41 do {
42 id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
43 } while (0 == id);
44 return id;
45}
46
senorblancobe129b22014-08-08 07:14:35 -070047struct SkImageFilter::Cache::Key {
senorblanco55b6d8b2014-07-30 11:26:46 -070048 Key(const uint32_t uniqueID, const SkMatrix& matrix, const SkIRect& clipBounds, uint32_t srcGenID)
49 : fUniqueID(uniqueID), fMatrix(matrix), fClipBounds(clipBounds), fSrcGenID(srcGenID) {
50 // Assert that Key is tightly-packed, since it is hashed.
51 SK_COMPILE_ASSERT(sizeof(Key) == sizeof(uint32_t) + sizeof(SkMatrix) + sizeof(SkIRect) +
52 sizeof(uint32_t), image_filter_key_tight_packing);
53 fMatrix.getType(); // force initialization of type, so hashes match
54 }
55 uint32_t fUniqueID;
56 SkMatrix fMatrix;
57 SkIRect fClipBounds;
58 uint32_t fSrcGenID;
59 bool operator==(const Key& other) const {
60 return fUniqueID == other.fUniqueID
61 && fMatrix == other.fMatrix
62 && fClipBounds == other.fClipBounds
63 && fSrcGenID == other.fSrcGenID;
64 }
65};
66
reedb959ec72014-07-17 07:03:09 -070067SkImageFilter::Common::~Common() {
68 for (int i = 0; i < fInputs.count(); ++i) {
69 SkSafeUnref(fInputs[i]);
70 }
71}
72
73void SkImageFilter::Common::allocInputs(int count) {
74 const size_t size = count * sizeof(SkImageFilter*);
75 fInputs.reset(count);
76 sk_bzero(fInputs.get(), size);
77}
78
79void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) {
80 const size_t size = fInputs.count() * sizeof(SkImageFilter*);
81 memcpy(inputs, fInputs.get(), size);
82 sk_bzero(fInputs.get(), size);
83}
84
85bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
reed9fa60da2014-08-21 07:59:51 -070086 const int count = buffer.readInt();
87 if (!buffer.validate(count >= 0)) {
88 return false;
reedb959ec72014-07-17 07:03:09 -070089 }
reed9fa60da2014-08-21 07:59:51 -070090 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
reedb959ec72014-07-17 07:03:09 -070091 return false;
92 }
93
94 this->allocInputs(count);
95 for (int i = 0; i < count; i++) {
96 if (buffer.readBool()) {
97 fInputs[i] = buffer.readImageFilter();
98 }
99 if (!buffer.isValid()) {
100 return false;
101 }
102 }
103 SkRect rect;
104 buffer.readRect(&rect);
105 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
106 return false;
107 }
mtklein148ec592014-10-13 13:17:56 -0700108
reedb959ec72014-07-17 07:03:09 -0700109 uint32_t flags = buffer.readUInt();
110 fCropRect = CropRect(rect, flags);
senorblanco4a22a432015-03-18 13:14:54 -0700111 if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
112
113 (void) buffer.readUInt();
114 }
reedb959ec72014-07-17 07:03:09 -0700115 return buffer.isValid();
116}
117
118///////////////////////////////////////////////////////////////////////////////////////////////////
119
senorblanco24e06d52015-03-18 12:11:33 -0700120SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000121 : fInputCount(inputCount),
122 fInputs(new SkImageFilter*[inputCount]),
senorblanco55b6d8b2014-07-30 11:26:46 -0700123 fUsesSrcInput(false),
124 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)),
senorblanco24e06d52015-03-18 12:11:33 -0700125 fUniqueID(next_image_filter_unique_id()) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000126 for (int i = 0; i < inputCount; ++i) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700127 if (NULL == inputs[i] || inputs[i]->usesSrcInput()) {
128 fUsesSrcInput = true;
129 }
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000130 fInputs[i] = inputs[i];
131 SkSafeRef(fInputs[i]);
132 }
133}
134
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000135SkImageFilter::~SkImageFilter() {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000136 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000137 SkSafeUnref(fInputs[i]);
138 }
139 delete[] fInputs;
140}
141
senorblanco55b6d8b2014-07-30 11:26:46 -0700142SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
senorblanco24e06d52015-03-18 12:11:33 -0700143 : fUsesSrcInput(false)
144 , fUniqueID(next_image_filter_unique_id()) {
reedb959ec72014-07-17 07:03:09 -0700145 Common common;
146 if (common.unflatten(buffer, inputCount)) {
147 fCropRect = common.cropRect();
148 fInputCount = common.inputCount();
149 fInputs = SkNEW_ARRAY(SkImageFilter*, fInputCount);
150 common.detachInputs(fInputs);
senorblanco55b6d8b2014-07-30 11:26:46 -0700151 for (int i = 0; i < fInputCount; ++i) {
152 if (NULL == fInputs[i] || fInputs[i]->usesSrcInput()) {
153 fUsesSrcInput = true;
154 }
155 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +0000156 } else {
157 fInputCount = 0;
158 fInputs = NULL;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000159 }
160}
161
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000162void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000163 buffer.writeInt(fInputCount);
164 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000165 SkImageFilter* input = getInput(i);
166 buffer.writeBool(input != NULL);
167 if (input != NULL) {
168 buffer.writeFlattenable(input);
169 }
170 }
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000171 buffer.writeRect(fCropRect.rect());
172 buffer.writeUInt(fCropRect.flags());
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +0000173}
174
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000175bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000176 const Context& context,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000177 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000178 SkASSERT(result);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000179 SkASSERT(offset);
senorblanco55b6d8b2014-07-30 11:26:46 -0700180 uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0;
senorblancobe129b22014-08-08 07:14:35 -0700181 Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID);
182 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700183 if (context.cache()->get(key, result, offset)) {
184 return true;
185 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000186 }
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000187 /*
188 * Give the proxy first shot at the filter. If it returns false, ask
189 * the filter to do it.
190 */
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000191 if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
192 this->onFilterImage(proxy, src, context, result, offset)) {
senorblancobe129b22014-08-08 07:14:35 -0700193 if (context.cache()) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700194 context.cache()->set(key, *result, *offset);
195 }
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000196 return true;
197 }
198 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000199}
200
201bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000202 SkIRect* dst) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000203 SkASSERT(dst);
204 return this->onFilterBounds(src, ctm, dst);
205}
206
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000207void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
208 if (0 == fInputCount) {
209 *dst = src;
210 return;
211 }
212 if (this->getInput(0)) {
213 this->getInput(0)->computeFastBounds(src, dst);
214 } else {
215 *dst = src;
216 }
217 for (int i = 1; i < fInputCount; i++) {
218 SkImageFilter* input = this->getInput(i);
219 if (input) {
220 SkRect bounds;
221 input->computeFastBounds(src, &bounds);
222 dst->join(bounds);
223 } else {
224 dst->join(src);
225 }
226 }
227}
228
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000229bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000230 SkBitmap*, SkIPoint*) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000231 return false;
232}
233
234bool SkImageFilter::canFilterImageGPU() const {
joshualitt85ff25e2015-07-08 09:10:03 -0700235 return this->asFragmentProcessor(NULL, NULL, NULL, SkMatrix::I(), SkIRect());
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000236}
237
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000238bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000239 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000240#if SK_SUPPORT_GPU
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000241 SkBitmap input = src;
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000242 SkASSERT(fInputCount == 1);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000243 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000244 if (this->getInput(0) &&
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000245 !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000246 return false;
247 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000248 GrTexture* srcTexture = input.getTexture();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000249 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000250 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000251 return false;
252 }
253 SkRect srcRect = SkRect::Make(bounds);
254 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000255 GrContext* context = srcTexture->getContext();
256
bsalomonf2703d82014-10-28 14:33:06 -0700257 GrSurfaceDesc desc;
258 desc.fFlags = kRenderTarget_GrSurfaceFlag,
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000259 desc.fWidth = bounds.width();
260 desc.fHeight = bounds.height();
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000261 desc.fConfig = kRGBA_8888_GrPixelConfig;
262
bsalomond309e7a2015-04-30 14:18:54 -0700263 SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
264 desc, GrTextureProvider::kApprox_ScratchTexMatch));
bsalomone3059732014-10-14 11:47:22 -0700265 if (!dst) {
senorblanco673d9732014-08-15 10:48:43 -0700266 return false;
267 }
joshualitt25d9c152015-02-18 12:29:52 -0800268
joshualitt570d2f82015-02-25 13:19:48 -0800269 // setup new clip
270 GrClip clip(dstRect);
271
joshualittb0a8a372014-09-23 09:50:21 -0700272 GrFragmentProcessor* fp;
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000273 offset->fX = bounds.left();
274 offset->fY = bounds.top();
275 bounds.offset(-srcOffset);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000276 SkMatrix matrix(ctx.ctm());
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000277 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
joshualitt85ff25e2015-07-08 09:10:03 -0700278 GrPaint paint;
joshualitt9cc17752015-07-09 06:28:14 -0700279 if (this->asFragmentProcessor(&fp, paint.getProcessorDataManager(), srcTexture, matrix, bounds)) {
bsalomonb501ecd2014-10-14 08:40:57 -0700280 SkASSERT(fp);
bsalomonb501ecd2014-10-14 08:40:57 -0700281 paint.addColorProcessor(fp)->unref();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000282
robertphillipsea461502015-05-26 11:38:03 -0700283 GrDrawContext* drawContext = context->drawContext();
284 if (drawContext) {
285 drawContext->drawNonAARectToRect(dst->asRenderTarget(), clip, paint, SkMatrix::I(),
286 dstRect, srcRect);
287
288 WrapTexture(dst, bounds.width(), bounds.height(), result);
289 return true;
290 }
bsalomonb501ecd2014-10-14 08:40:57 -0700291 }
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000292#endif
bsalomonb501ecd2014-10-14 08:40:57 -0700293 return false;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000294}
295
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000296bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src,
297 const SkIPoint& srcOffset, SkIRect* bounds) const {
298 SkIRect srcBounds;
299 src.getBounds(&srcBounds);
300 srcBounds.offset(srcOffset);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000301 SkRect cropRect;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000302 ctx.ctm().mapRect(&cropRect, fCropRect.rect());
reedb07a94f2014-11-19 05:03:18 -0800303 const SkIRect cropRectI = cropRect.roundOut();
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000304 uint32_t flags = fCropRect.flags();
senorblanco24d2a7b2015-07-13 10:27:05 -0700305 if (flags & CropRect::kHasLeft_CropEdge) {
306 srcBounds.fLeft = cropRectI.fLeft;
307 }
308 if (flags & CropRect::kHasTop_CropEdge) {
309 srcBounds.fTop = cropRectI.fTop;
310 }
311 if (flags & CropRect::kHasRight_CropEdge) {
312 srcBounds.fRight = srcBounds.fLeft + cropRectI.width();
313 }
314 if (flags & CropRect::kHasBottom_CropEdge) {
315 srcBounds.fBottom = srcBounds.fTop + cropRectI.height();
316 }
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000317 if (!srcBounds.intersect(ctx.clipBounds())) {
318 return false;
319 }
320 *bounds = srcBounds;
321 return true;
322}
323
324bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
325 SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const {
326 SkIRect srcBounds;
327 src.getBounds(&srcBounds);
328 srcBounds.offset(*srcOffset);
329 SkRect cropRect;
330 ctx.ctm().mapRect(&cropRect, fCropRect.rect());
reedb07a94f2014-11-19 05:03:18 -0800331 const SkIRect cropRectI = cropRect.roundOut();
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000332 uint32_t flags = fCropRect.flags();
333 *bounds = srcBounds;
senorblanco24d2a7b2015-07-13 10:27:05 -0700334 if (flags & CropRect::kHasLeft_CropEdge) {
335 bounds->fLeft = cropRectI.fLeft;
336 }
337 if (flags & CropRect::kHasTop_CropEdge) {
338 bounds->fTop = cropRectI.fTop;
339 }
340 if (flags & CropRect::kHasRight_CropEdge) {
341 bounds->fRight = bounds->fLeft + cropRectI.width();
342 }
343 if (flags & CropRect::kHasBottom_CropEdge) {
344 bounds->fBottom = bounds->fTop + cropRectI.height();
345 }
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000346 if (!bounds->intersect(ctx.clipBounds())) {
347 return false;
348 }
349 if (srcBounds.contains(*bounds)) {
350 *dst = src;
351 return true;
352 } else {
353 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
354 if (!device) {
355 return false;
356 }
357 SkCanvas canvas(device);
358 canvas.clear(0x00000000);
359 canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
360 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
361 *dst = device->accessBitmap(false);
362 return true;
363 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000364}
365
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000366bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000367 SkIRect* dst) const {
368 if (fInputCount < 1) {
senorblanco8f3937d2014-10-29 12:36:32 -0700369 *dst = src;
370 return true;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000371 }
372
373 SkIRect bounds;
374 for (int i = 0; i < fInputCount; ++i) {
375 SkImageFilter* filter = this->getInput(i);
376 SkIRect rect = src;
377 if (filter && !filter->filterBounds(src, ctm, &rect)) {
378 return false;
379 }
380 if (0 == i) {
381 bounds = rect;
382 } else {
383 bounds.join(rect);
384 }
385 }
386
387 // don't modify dst until now, so we don't accidentally change it in the
388 // loop, but then return false on the next filter.
389 *dst = bounds;
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000390 return true;
391}
392
joshualitt9cc17752015-07-09 06:28:14 -0700393bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrProcessorDataManager*, GrTexture*,
joshualitt85ff25e2015-07-08 09:10:03 -0700394 const SkMatrix&, const SkIRect&) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000395 return false;
396}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000397
senorblanco8c874ee2015-03-20 06:38:17 -0700398SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
399 SkFilterQuality filterQuality,
400 SkImageFilter* input) {
401 return SkMatrixImageFilter::Create(matrix, filterQuality, input);
402}
403
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000404#if SK_SUPPORT_GPU
405
406void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
407 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000408 result->setInfo(info);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000409 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
410}
411
412bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000413 const SkBitmap& src, const Context& ctx,
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000414 SkBitmap* result, SkIPoint* offset) const {
415 // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
416 // matrix with no clip and that the matrix, clip, and render target set before this function was
417 // called are restored before we return to the caller.
418 GrContext* context = src.getTexture()->getContext();
joshualitt570d2f82015-02-25 13:19:48 -0800419
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000420 if (this->canFilterImageGPU()) {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000421 return this->filterImageGPU(proxy, src, ctx, result, offset);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000422 } else {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000423 if (this->filterImage(proxy, src, ctx, result, offset)) {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000424 if (!result->getTexture()) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000425 const SkImageInfo info = result->info();
426 if (kUnknown_SkColorType == info.colorType()) {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000427 return false;
428 }
bsalomonbcf0a522014-10-08 08:40:09 -0700429 SkAutoTUnref<GrTexture> resultTex(GrRefCachedBitmapTexture(context, *result, NULL));
430 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, resultTex)))->unref();
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000431 }
432 return true;
433 } else {
434 return false;
435 }
436 }
437}
438#endif
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +0000439
senorblanco55b6d8b2014-07-30 11:26:46 -0700440namespace {
441
senorblancobe129b22014-08-08 07:14:35 -0700442class CacheImpl : public SkImageFilter::Cache {
senorblanco55b6d8b2014-07-30 11:26:46 -0700443public:
senorblancobe129b22014-08-08 07:14:35 -0700444 CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) {
senorblanco55b6d8b2014-07-30 11:26:46 -0700445 }
senorblancobe129b22014-08-08 07:14:35 -0700446 virtual ~CacheImpl() {
senorblanco55b6d8b2014-07-30 11:26:46 -0700447 SkTDynamicHash<Value, Key>::Iter iter(&fLookup);
448
449 while (!iter.done()) {
450 Value* v = &*iter;
451 ++iter;
452 delete v;
453 }
454 }
455 struct Value {
456 Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset)
457 : fKey(key), fBitmap(bitmap), fOffset(offset) {}
458 Key fKey;
459 SkBitmap fBitmap;
460 SkIPoint fOffset;
461 static const Key& GetKey(const Value& v) {
462 return v.fKey;
463 }
464 static uint32_t Hash(const Key& key) {
465 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
466 }
467 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
468 };
mtklein36352bf2015-03-25 18:17:31 -0700469 bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override {
senorblanco55b6d8b2014-07-30 11:26:46 -0700470 SkAutoMutexAcquire mutex(fMutex);
471 if (Value* v = fLookup.find(key)) {
472 *result = v->fBitmap;
473 *offset = v->fOffset;
474 if (v != fLRU.head()) {
475 fLRU.remove(v);
476 fLRU.addToHead(v);
477 }
478 return true;
479 }
480 return false;
481 }
mtklein36352bf2015-03-25 18:17:31 -0700482 void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override {
senorblanco55b6d8b2014-07-30 11:26:46 -0700483 SkAutoMutexAcquire mutex(fMutex);
484 if (Value* v = fLookup.find(key)) {
485 removeInternal(v);
486 }
487 Value* v = new Value(key, result, offset);
488 fLookup.add(v);
489 fLRU.addToHead(v);
490 fCurrentBytes += result.getSize();
491 while (fCurrentBytes > fMaxBytes) {
492 Value* tail = fLRU.tail();
493 SkASSERT(tail);
494 if (tail == v) {
495 break;
496 }
497 removeInternal(tail);
498 }
499 }
reed67ca2a92015-05-20 13:22:58 -0700500
501 void purge() override {
502 SkAutoMutexAcquire mutex(fMutex);
503 while (fCurrentBytes > 0) {
504 Value* tail = fLRU.tail();
505 SkASSERT(tail);
506 this->removeInternal(tail);
507 }
508 }
509
senorblanco55b6d8b2014-07-30 11:26:46 -0700510private:
511 void removeInternal(Value* v) {
512 fCurrentBytes -= v->fBitmap.getSize();
513 fLRU.remove(v);
514 fLookup.remove(v->fKey);
515 delete v;
516 }
517private:
518 SkTDynamicHash<Value, Key> fLookup;
519 mutable SkTInternalLList<Value> fLRU;
520 size_t fMaxBytes;
521 size_t fCurrentBytes;
522 mutable SkMutex fMutex;
523};
524
senorblancobe129b22014-08-08 07:14:35 -0700525SkImageFilter::Cache* CreateCache() {
526 return SkImageFilter::Cache::Create(kDefaultCacheSize);
senorblanco55b6d8b2014-07-30 11:26:46 -0700527}
528
529} // namespace
530
senorblancobe129b22014-08-08 07:14:35 -0700531SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) {
532 return SkNEW_ARGS(CacheImpl, (maxBytes));
senorblanco55b6d8b2014-07-30 11:26:46 -0700533}
534
mtklein148ec592014-10-13 13:17:56 -0700535SK_DECLARE_STATIC_LAZY_PTR(SkImageFilter::Cache, cache, CreateCache);
536
senorblancobe129b22014-08-08 07:14:35 -0700537SkImageFilter::Cache* SkImageFilter::Cache::Get() {
senorblanco55b6d8b2014-07-30 11:26:46 -0700538 return cache.get();
539}
reed67ca2a92015-05-20 13:22:58 -0700540
541void SkImageFilter::PurgeCache() {
542 cache.get()->purge();
543}
reed2c55d7b2015-06-09 08:18:39 -0700544
545///////////////////////////////////////////////////////////////////////////////////////////////////
546
reed2c55d7b2015-06-09 08:18:39 -0700547SkBaseDevice* SkImageFilter::Proxy::createDevice(int w, int h) {
548 SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
549 SkBaseDevice::kNever_TileUsage,
550 kUnknown_SkPixelGeometry,
551 true /*forImageFilter*/);
552 SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, NULL);
553 if (NULL == dev) {
robertphillips9a53fd72015-06-22 09:46:59 -0700554 const SkSurfaceProps surfaceProps(fDevice->fSurfaceProps.flags(),
555 kUnknown_SkPixelGeometry);
556 dev = SkBitmapDevice::Create(cinfo.fInfo, surfaceProps);
reed2c55d7b2015-06-09 08:18:39 -0700557 }
558 return dev;
559}
560
561bool SkImageFilter::Proxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
562 const SkImageFilter::Context& ctx,
563 SkBitmap* result, SkIPoint* offset) {
564 return fDevice->filterImage(filter, src, ctx, result, offset);
565}
mtklein1b249332015-07-07 12:21:21 -0700566