blob: 43e96bbb5ff48362335ab1bb77b3b95dee4f6017 [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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000013#include "SkRect.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000014#include "SkValidationUtils.h"
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +000015#if SK_SUPPORT_GPU
16#include "GrContext.h"
17#include "GrTexture.h"
18#include "SkImageFilterUtils.h"
19#endif
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000020
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000021SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000022 : fInputCount(inputCount),
23 fInputs(new SkImageFilter*[inputCount]),
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000024 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000025 for (int i = 0; i < inputCount; ++i) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000026 fInputs[i] = inputs[i];
27 SkSafeRef(fInputs[i]);
28 }
29}
30
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000031SkImageFilter::SkImageFilter(SkImageFilter* input, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000032 : fInputCount(1),
33 fInputs(new SkImageFilter*[1]),
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000034 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)) {
senorblanco@chromium.orgc2e8cef2012-10-22 15:07:14 +000035 fInputs[0] = input;
36 SkSafeRef(fInputs[0]);
37}
38
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000039SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000040 : fInputCount(2), fInputs(new SkImageFilter*[2]),
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000041 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)) {
senorblanco@chromium.orgc2e8cef2012-10-22 15:07:14 +000042 fInputs[0] = input1;
43 fInputs[1] = input2;
44 SkSafeRef(fInputs[0]);
45 SkSafeRef(fInputs[1]);
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000046}
47
48SkImageFilter::~SkImageFilter() {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000049 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000050 SkSafeUnref(fInputs[i]);
51 }
52 delete[] fInputs;
53}
54
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000055SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) {
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000056 fInputCount = buffer.readInt();
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +000057 if (buffer.validate((fInputCount >= 0) && ((inputCount < 0) || (fInputCount == inputCount)))) {
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000058 fInputs = new SkImageFilter*[fInputCount];
59 for (int i = 0; i < fInputCount; i++) {
60 if (buffer.readBool()) {
61 fInputs[i] = buffer.readImageFilter();
62 } else {
63 fInputs[i] = NULL;
64 }
commit-bot@chromium.orgc2e9db32013-12-06 20:14:46 +000065 if (!buffer.isValid()) {
66 fInputCount = i; // Do not use fInputs past that point in the destructor
67 break;
68 }
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000069 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000070 SkRect rect;
71 buffer.readRect(&rect);
commit-bot@chromium.orgc2e9db32013-12-06 20:14:46 +000072 if (buffer.isValid() && buffer.validate(SkIsValidRect(rect))) {
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000073 uint32_t flags = buffer.readUInt();
74 fCropRect = CropRect(rect, flags);
75 }
76 } else {
77 fInputCount = 0;
78 fInputs = NULL;
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000079 }
80}
81
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000082void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000083 buffer.writeInt(fInputCount);
84 for (int i = 0; i < fInputCount; i++) {
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000085 SkImageFilter* input = getInput(i);
86 buffer.writeBool(input != NULL);
87 if (input != NULL) {
88 buffer.writeFlattenable(input);
89 }
90 }
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +000091 buffer.writeRect(fCropRect.rect());
92 buffer.writeUInt(fCropRect.flags());
senorblanco@chromium.org9f25de72012-10-10 20:36:13 +000093}
94
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000095bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
96 const SkMatrix& ctm,
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000097 SkBitmap* result, SkIPoint* offset) {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +000098 SkASSERT(result);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000099 SkASSERT(offset);
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000100 /*
101 * Give the proxy first shot at the filter. If it returns false, ask
102 * the filter to do it.
103 */
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000104 return (proxy && proxy->filterImage(this, src, ctm, result, offset)) ||
105 this->onFilterImage(proxy, src, ctm, result, offset);
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000106}
107
108bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
109 SkIRect* dst) {
110 SkASSERT(&src);
111 SkASSERT(dst);
112 return this->onFilterBounds(src, ctm, dst);
113}
114
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000115void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
116 if (0 == fInputCount) {
117 *dst = src;
118 return;
119 }
120 if (this->getInput(0)) {
121 this->getInput(0)->computeFastBounds(src, dst);
122 } else {
123 *dst = src;
124 }
125 for (int i = 1; i < fInputCount; i++) {
126 SkImageFilter* input = this->getInput(i);
127 if (input) {
128 SkRect bounds;
129 input->computeFastBounds(src, &bounds);
130 dst->join(bounds);
131 } else {
132 dst->join(src);
133 }
134 }
135}
136
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000137bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
138 SkBitmap*, SkIPoint*) {
139 return false;
140}
141
142bool SkImageFilter::canFilterImageGPU() const {
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000143 return this->asNewEffect(NULL, NULL, SkMatrix::I(), SkIRect());
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000144}
145
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000146bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
147 SkBitmap* result, SkIPoint* offset) {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000148#if SK_SUPPORT_GPU
149 SkBitmap input;
150 SkASSERT(fInputCount == 1);
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000151 if (!SkImageFilterUtils::GetInputResultGPU(this->getInput(0), proxy, src, ctm, &input, offset)) {
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000152 return false;
153 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000154 GrTexture* srcTexture = input.getTexture();
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000155 SkIRect bounds;
156 src.getBounds(&bounds);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000157 if (!this->applyCropRect(&bounds, ctm)) {
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000158 return false;
159 }
160 SkRect srcRect = SkRect::Make(bounds);
161 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000162 GrContext* context = srcTexture->getContext();
163
164 GrTextureDesc desc;
165 desc.fFlags = kRenderTarget_GrTextureFlagBit,
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000166 desc.fWidth = bounds.width();
167 desc.fHeight = bounds.height();
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000168 desc.fConfig = kRGBA_8888_GrPixelConfig;
169
170 GrAutoScratchTexture dst(context, desc);
171 GrContext::AutoMatrix am;
172 am.setIdentity(context);
173 GrContext::AutoRenderTarget art(context, dst.texture()->asRenderTarget());
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000174 GrContext::AutoClip acs(context, dstRect);
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000175 GrEffectRef* effect;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000176 SkMatrix matrix(ctm);
177 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000178 this->asNewEffect(&effect, srcTexture, matrix, bounds);
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000179 SkASSERT(effect);
180 SkAutoUnref effectRef(effect);
181 GrPaint paint;
commit-bot@chromium.org42dacab2013-07-13 17:24:24 +0000182 paint.addColorEffect(effect);
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000183 context->drawRectToRect(paint, dstRect, srcRect);
184
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000185 SkAutoTUnref<GrTexture> resultTex(dst.detach());
senorblanco@chromium.org4e16bb22013-07-26 00:10:07 +0000186 SkImageFilterUtils::WrapTexture(resultTex, bounds.width(), bounds.height(), result);
187 offset->fX += bounds.left();
188 offset->fY += bounds.top();
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000189 return true;
190#else
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000191 return false;
senorblanco@chromium.orgd043cce2013-04-08 19:43:22 +0000192#endif
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000193}
194
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000195bool SkImageFilter::applyCropRect(SkIRect* rect, const SkMatrix& matrix) const {
196 SkRect cropRect;
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000197 matrix.mapRect(&cropRect, fCropRect.rect());
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000198 SkIRect cropRectI;
199 cropRect.roundOut(&cropRectI);
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000200 uint32_t flags = fCropRect.flags();
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000201 // If the original crop rect edges were unset, max out the new crop edges
senorblanco@chromium.org3f1f2a32013-10-16 18:07:48 +0000202 if (!(flags & CropRect::kHasLeft_CropEdge)) cropRectI.fLeft = SK_MinS32;
203 if (!(flags & CropRect::kHasTop_CropEdge)) cropRectI.fTop = SK_MinS32;
204 if (!(flags & CropRect::kHasRight_CropEdge)) cropRectI.fRight = SK_MaxS32;
205 if (!(flags & CropRect::kHasBottom_CropEdge)) cropRectI.fBottom = SK_MaxS32;
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000206 return rect->intersect(cropRectI);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000207}
208
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000209bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
210 SkIRect* dst) {
211 *dst = src;
212 return true;
213}
214
senorblanco@chromium.org7938bae2013-10-18 20:08:14 +0000215bool SkImageFilter::asNewEffect(GrEffectRef**, GrTexture*, const SkMatrix&, const SkIRect&) const {
senorblanco@chromium.orgbf2768b2012-08-20 15:43:14 +0000216 return false;
217}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000218
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000219bool SkImageFilter::asColorFilter(SkColorFilter**) const {
220 return false;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000221}