blob: 5b9141fd2af01670c4ca214e535f71fafb084ff0 [file] [log] [blame]
senorblanco@chromium.org05054f12012-03-02 21:05:45 +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 "SkMorphologyImageFilter.h"
djsollen@google.com64a0ec32012-06-12 15:17:27 +00009#include "SkBitmap.h"
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000010#include "SkColorPriv.h"
senorblanco1d3ff432015-10-20 10:17:34 -070011#include "SkDevice.h"
mtkleind029ded2015-08-04 14:09:09 -070012#include "SkOpts.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000013#include "SkReadBuffer.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000014#include "SkRect.h"
mtkleind029ded2015-08-04 14:09:09 -070015#include "SkWriteBuffer.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000016#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000017#include "GrContext.h"
robertphillipsea461502015-05-26 11:38:03 -070018#include "GrDrawContext.h"
egdaniel605dd0f2014-11-12 08:35:25 -080019#include "GrInvariantOutput.h"
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000020#include "GrTexture.h"
joshualitteb2a6762014-12-04 11:35:33 -080021#include "effects/Gr1DKernelEffect.h"
wangyix6af0c932015-07-22 10:21:17 -070022#include "gl/GrGLFragmentProcessor.h"
joshualitt30ba4362014-08-21 20:18:45 -070023#include "gl/builders/GrGLProgramBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070024#include "glsl/GrGLSLProgramDataManager.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000025#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000026
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +000027SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
28 int radiusY,
29 SkImageFilter* input,
senorblanco24e06d52015-03-18 12:11:33 -070030 const CropRect* cropRect)
31 : INHERITED(1, &input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000032}
33
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000034void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000035 this->INHERITED::flatten(buffer);
tomhudson@google.com75589252012-04-10 17:42:21 +000036 buffer.writeInt(fRadius.fWidth);
37 buffer.writeInt(fRadius.fHeight);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000038}
39
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000040static void callProcX(SkMorphologyImageFilter::Proc procX, const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000041{
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000042 procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
43 radiusX, bounds.width(), bounds.height(),
44 src.rowBytesAsPixels(), dst->rowBytesAsPixels());
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000045}
46
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000047static void callProcY(SkMorphologyImageFilter::Proc procY, const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000048{
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000049 procY(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
50 radiusY, bounds.height(), bounds.width(),
51 src.rowBytesAsPixels(), dst->rowBytesAsPixels());
52}
53
54bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
55 SkMorphologyImageFilter::Proc procY,
56 Proxy* proxy,
57 const SkBitmap& source,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000058 const Context& ctx,
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000059 SkBitmap* dst,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +000060 SkIPoint* offset) const {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000061 SkBitmap src = source;
62 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblancob9519f82015-10-15 12:15:13 -070063 if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000064 return false;
skia.committer@gmail.com1878a442014-01-23 18:48:56 +000065 }
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000066
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +000067 if (src.colorType() != kN32_SkColorType) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000068 return false;
69 }
70
71 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +000072 if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000073 return false;
74 }
75
76 SkAutoLockPixels alp(src);
77 if (!src.getPixels()) {
78 return false;
79 }
80
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000081 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
82 SkIntToScalar(this->radius().height()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +000083 ctx.ctm().mapVectors(&radius, 1);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +000084 int width = SkScalarFloorToInt(radius.fX);
85 int height = SkScalarFloorToInt(radius.fY);
86
87 if (width < 0 || height < 0) {
88 return false;
89 }
90
91 SkIRect srcBounds = bounds;
92 srcBounds.offset(-srcOffset);
93
94 if (width == 0 && height == 0) {
95 src.extractSubset(dst, srcBounds);
96 offset->fX = bounds.left();
97 offset->fY = bounds.top();
98 return true;
99 }
100
senorblanco1d3ff432015-10-20 10:17:34 -0700101 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
102 if (!device) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000103 return false;
104 }
senorblanco1d3ff432015-10-20 10:17:34 -0700105 *dst = device->accessBitmap(false);
106 SkAutoLockPixels alp_dst(*dst);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000107
108 if (width > 0 && height > 0) {
senorblanco1d3ff432015-10-20 10:17:34 -0700109 SkAutoTUnref<SkBaseDevice> tempDevice(proxy->createDevice(dst->width(), dst->height()));
110 if (!tempDevice) {
111 return false;
112 }
113 SkBitmap temp = tempDevice->accessBitmap(false);
114 SkAutoLockPixels alp_temp(temp);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000115 callProcX(procX, src, &temp, width, srcBounds);
116 SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
117 callProcY(procY, temp, dst, height, tmpBounds);
118 } else if (width > 0) {
119 callProcX(procX, src, dst, width, srcBounds);
120 } else if (height > 0) {
121 callProcY(procY, src, dst, height, srcBounds);
122 }
123 offset->fX = bounds.left();
124 offset->fY = bounds.top();
125 return true;
senorblanco@chromium.org76d4d042014-01-23 18:45:23 +0000126}
127
skia.committer@gmail.com1878a442014-01-23 18:48:56 +0000128bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000129 const SkBitmap& source, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000130 SkBitmap* dst, SkIPoint* offset) const {
mtkleind029ded2015-08-04 14:09:09 -0700131 return this->filterImageGeneric(SkOpts::erode_x, SkOpts::erode_y,
132 proxy, source, ctx, dst, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000133}
134
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000135bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000136 const SkBitmap& source, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000137 SkBitmap* dst, SkIPoint* offset) const {
mtkleind029ded2015-08-04 14:09:09 -0700138 return this->filterImageGeneric(SkOpts::dilate_x, SkOpts::dilate_y,
139 proxy, source, ctx, dst, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000140}
141
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000142void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
robertphillipsff0ca5e2015-07-22 11:54:44 -0700143 if (this->getInput(0)) {
144 this->getInput(0)->computeFastBounds(src, dst);
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000145 } else {
146 *dst = src;
147 }
148 dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
149}
150
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000151bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
152 SkIRect* dst) const {
153 SkIRect bounds = src;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000154 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
155 SkIntToScalar(this->radius().height()));
156 ctm.mapVectors(&radius, 1);
157 bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
robertphillipsff0ca5e2015-07-22 11:54:44 -0700158 if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) {
senorblanco1150a6d2014-08-25 12:46:58 -0700159 return false;
160 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000161 *dst = bounds;
162 return true;
163}
164
reed9fa60da2014-08-21 07:59:51 -0700165SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
166 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
167 const int width = buffer.readInt();
168 const int height = buffer.readInt();
senorblanco24e06d52015-03-18 12:11:33 -0700169 return Create(width, height, common.getInput(0), &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -0700170}
171
172SkFlattenable* SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
173 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
174 const int width = buffer.readInt();
175 const int height = buffer.readInt();
senorblanco24e06d52015-03-18 12:11:33 -0700176 return Create(width, height, common.getInput(0), &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -0700177}
178
robertphillipsf3f5bad2014-12-19 13:49:15 -0800179#ifndef SK_IGNORE_TO_STRING
180void SkErodeImageFilter::toString(SkString* str) const {
181 str->appendf("SkErodeImageFilter: (");
182 str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
183 str->append(")");
184}
185#endif
186
187#ifndef SK_IGNORE_TO_STRING
188void SkDilateImageFilter::toString(SkString* str) const {
189 str->appendf("SkDilateImageFilter: (");
190 str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
191 str->append(")");
192}
193#endif
194
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000195#if SK_SUPPORT_GPU
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000196
197///////////////////////////////////////////////////////////////////////////////
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000198/**
199 * Morphology effects. Depending upon the type of morphology, either the
200 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
201 * kernel is selected as the new color. The new color is modulated by the input
202 * color.
203 */
204class GrMorphologyEffect : public Gr1DKernelEffect {
205
206public:
207
208 enum MorphologyType {
209 kErode_MorphologyType,
210 kDilate_MorphologyType,
211 };
212
bsalomon4a339522015-10-06 08:40:50 -0700213 static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
214 MorphologyType type) {
215 return new GrMorphologyEffect(tex, dir, radius, type);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000216 }
217
bsalomon4a339522015-10-06 08:40:50 -0700218 static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
219 MorphologyType type, float bounds[2]) {
220 return new GrMorphologyEffect(tex, dir, radius, type, bounds);
cwallez80a61df2015-01-26 12:20:14 -0800221 }
222
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000223 virtual ~GrMorphologyEffect();
224
225 MorphologyType type() const { return fType; }
cwallez80a61df2015-01-26 12:20:14 -0800226 bool useRange() const { return fUseRange; }
227 const float* range() const { return fRange; }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000228
mtklein36352bf2015-03-25 18:17:31 -0700229 const char* name() const override { return "Morphology"; }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000230
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000231protected:
232
233 MorphologyType fType;
cwallez80a61df2015-01-26 12:20:14 -0800234 bool fUseRange;
235 float fRange[2];
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000236
237private:
wangyixb1daa862015-08-18 11:29:31 -0700238 GrGLFragmentProcessor* onCreateGLInstance() const override;
239
wangyix4b3050b2015-08-04 07:59:37 -0700240 void onGetGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
241
mtklein36352bf2015-03-25 18:17:31 -0700242 bool onIsEqual(const GrFragmentProcessor&) const override;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000243
mtklein36352bf2015-03-25 18:17:31 -0700244 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
egdaniel1a8ecdf2014-10-03 06:24:12 -0700245
bsalomon4a339522015-10-06 08:40:50 -0700246 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
247 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType,
joshualitt5f10b5c2015-07-09 10:24:35 -0700248 float bounds[2]);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000249
joshualittb0a8a372014-09-23 09:50:21 -0700250 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000251
252 typedef Gr1DKernelEffect INHERITED;
253};
254
255///////////////////////////////////////////////////////////////////////////////
256
joshualittb0a8a372014-09-23 09:50:21 -0700257class GrGLMorphologyEffect : public GrGLFragmentProcessor {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000258public:
joshualitteb2a6762014-12-04 11:35:33 -0800259 GrGLMorphologyEffect(const GrProcessor&);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000260
wangyix7c157a92015-07-22 15:08:53 -0700261 virtual void emitCode(EmitArgs&) override;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000262
jvanverthcfc18862015-04-28 08:48:20 -0700263 static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000264
wangyixb1daa862015-08-18 11:29:31 -0700265protected:
egdaniel018fb622015-10-28 07:26:40 -0700266 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000267
268private:
269 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
270
kkinnunen7510b222014-07-30 00:04:16 -0700271 int fRadius;
cwallez80a61df2015-01-26 12:20:14 -0800272 Gr1DKernelEffect::Direction fDirection;
273 bool fUseRange;
kkinnunen7510b222014-07-30 00:04:16 -0700274 GrMorphologyEffect::MorphologyType fType;
egdaniel018fb622015-10-28 07:26:40 -0700275 GrGLSLProgramDataManager::UniformHandle fPixelSizeUni;
276 GrGLSLProgramDataManager::UniformHandle fRangeUni;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000277
joshualittb0a8a372014-09-23 09:50:21 -0700278 typedef GrGLFragmentProcessor INHERITED;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000279};
280
joshualitteb2a6762014-12-04 11:35:33 -0800281GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) {
joshualittb0a8a372014-09-23 09:50:21 -0700282 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000283 fRadius = m.radius();
cwallez80a61df2015-01-26 12:20:14 -0800284 fDirection = m.direction();
285 fUseRange = m.useRange();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000286 fType = m.type();
287}
288
wangyix7c157a92015-07-22 15:08:53 -0700289void GrGLMorphologyEffect::emitCode(EmitArgs& args) {
290 fPixelSizeUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
cwallez80a61df2015-01-26 12:20:14 -0800291 kFloat_GrSLType, kDefault_GrSLPrecision,
292 "PixelSize");
wangyix7c157a92015-07-22 15:08:53 -0700293 const char* pixelSizeInc = args.fBuilder->getUniformCStr(fPixelSizeUni);
294 fRangeUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
cwallez80a61df2015-01-26 12:20:14 -0800295 kVec2f_GrSLType, kDefault_GrSLPrecision,
296 "Range");
wangyix7c157a92015-07-22 15:08:53 -0700297 const char* range = args.fBuilder->getUniformCStr(fRangeUni);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000298
wangyix7c157a92015-07-22 15:08:53 -0700299 GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
300 SkString coords2D = fsBuilder->ensureFSCoords2D(args.fCoords, 0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000301 const char* func;
302 switch (fType) {
303 case GrMorphologyEffect::kErode_MorphologyType:
wangyix7c157a92015-07-22 15:08:53 -0700304 fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", args.fOutputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000305 func = "min";
306 break;
307 case GrMorphologyEffect::kDilate_MorphologyType:
wangyix7c157a92015-07-22 15:08:53 -0700308 fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", args.fOutputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000309 func = "max";
310 break;
311 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000312 SkFAIL("Unexpected type");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000313 func = ""; // suppress warning
314 break;
315 }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000316
cwallez80a61df2015-01-26 12:20:14 -0800317 const char* dir;
318 switch (fDirection) {
319 case Gr1DKernelEffect::kX_Direction:
320 dir = "x";
321 break;
322 case Gr1DKernelEffect::kY_Direction:
323 dir = "y";
324 break;
325 default:
326 SkFAIL("Unknown filter direction.");
327 dir = ""; // suppress warning
328 }
329
330 // vec2 coord = coord2D;
331 fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
332 // coord.x -= radius * pixelSize;
333 fsBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, fRadius, pixelSizeInc);
334 if (fUseRange) {
335 // highBound = min(highBound, coord.x + (width-1) * pixelSize);
336 fsBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);",
337 range, dir, float(width() - 1), pixelSizeInc);
338 // coord.x = max(lowBound, coord.x);
339 fsBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir);
340 }
341 fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width());
wangyix7c157a92015-07-22 15:08:53 -0700342 fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", args.fOutputColor, func, args.fOutputColor);
343 fsBuilder->appendTextureLookup(args.fSamplers[0], "coord");
joshualitt30ba4362014-08-21 20:18:45 -0700344 fsBuilder->codeAppend(");\n");
cwallez80a61df2015-01-26 12:20:14 -0800345 // coord.x += pixelSize;
346 fsBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc);
347 if (fUseRange) {
348 // coord.x = min(highBound, coord.x);
349 fsBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir);
350 }
joshualitt30ba4362014-08-21 20:18:45 -0700351 fsBuilder->codeAppend("\t\t}\n");
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000352 SkString modulate;
wangyix7c157a92015-07-22 15:08:53 -0700353 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700354 fsBuilder->codeAppend(modulate.c_str());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000355}
356
joshualittb0a8a372014-09-23 09:50:21 -0700357void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
jvanverthcfc18862015-04-28 08:48:20 -0700358 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700359 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
bsalomon63e99f72014-07-21 08:03:14 -0700360 uint32_t key = static_cast<uint32_t>(m.radius());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000361 key |= (m.type() << 8);
cwallez80a61df2015-01-26 12:20:14 -0800362 key |= (m.direction() << 9);
363 if (m.useRange()) key |= 1 << 10;
bsalomon63e99f72014-07-21 08:03:14 -0700364 b->add32(key);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000365}
366
egdaniel018fb622015-10-28 07:26:40 -0700367void GrGLMorphologyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700368 const GrProcessor& proc) {
cwallez80a61df2015-01-26 12:20:14 -0800369 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
370 GrTexture& texture = *m.texture(0);
371 // the code we generated was for a specific kernel radius, direction and bound usage
372 SkASSERT(m.radius() == fRadius);
373 SkASSERT(m.direction() == fDirection);
374 SkASSERT(m.useRange() == fUseRange);
375
376 float pixelSize = 0.0f;
377 switch (fDirection) {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000378 case Gr1DKernelEffect::kX_Direction:
cwallez80a61df2015-01-26 12:20:14 -0800379 pixelSize = 1.0f / texture.width();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000380 break;
381 case Gr1DKernelEffect::kY_Direction:
cwallez80a61df2015-01-26 12:20:14 -0800382 pixelSize = 1.0f / texture.height();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000383 break;
384 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000385 SkFAIL("Unknown filter direction.");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000386 }
cwallez80a61df2015-01-26 12:20:14 -0800387 pdman.set1f(fPixelSizeUni, pixelSize);
388
389 if (fUseRange) {
390 const float* range = m.range();
391 if (fDirection && texture.origin() == kBottomLeft_GrSurfaceOrigin) {
392 pdman.set2f(fRangeUni, 1.0f - range[1], 1.0f - range[0]);
393 } else {
394 pdman.set2f(fRangeUni, range[0], range[1]);
395 }
396 }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000397}
398
399///////////////////////////////////////////////////////////////////////////////
400
bsalomon4a339522015-10-06 08:40:50 -0700401GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000402 Direction direction,
403 int radius,
404 MorphologyType type)
bsalomon4a339522015-10-06 08:40:50 -0700405 : INHERITED(texture, direction, radius)
cwallez80a61df2015-01-26 12:20:14 -0800406 , fType(type), fUseRange(false) {
joshualitteb2a6762014-12-04 11:35:33 -0800407 this->initClassID<GrMorphologyEffect>();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000408}
409
bsalomon4a339522015-10-06 08:40:50 -0700410GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
cwallez80a61df2015-01-26 12:20:14 -0800411 Direction direction,
412 int radius,
413 MorphologyType type,
414 float range[2])
bsalomon4a339522015-10-06 08:40:50 -0700415 : INHERITED(texture, direction, radius)
cwallez80a61df2015-01-26 12:20:14 -0800416 , fType(type), fUseRange(true) {
417 this->initClassID<GrMorphologyEffect>();
418 fRange[0] = range[0];
419 fRange[1] = range[1];
420}
421
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000422GrMorphologyEffect::~GrMorphologyEffect() {
423}
424
wangyix4b3050b2015-08-04 07:59:37 -0700425void GrMorphologyEffect::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800426 GrGLMorphologyEffect::GenKey(*this, caps, b);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000427}
428
wangyixb1daa862015-08-18 11:29:31 -0700429GrGLFragmentProcessor* GrMorphologyEffect::onCreateGLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700430 return new GrGLMorphologyEffect(*this);
joshualitteb2a6762014-12-04 11:35:33 -0800431}
bsalomon0e08fc12014-10-15 08:19:04 -0700432bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700433 const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
bsalomon420d7e92014-10-16 09:18:09 -0700434 return (this->radius() == s.radius() &&
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000435 this->direction() == s.direction() &&
cwallez80a61df2015-01-26 12:20:14 -0800436 this->useRange() == s.useRange() &&
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000437 this->type() == s.type());
438}
439
egdaniel605dd0f2014-11-12 08:35:25 -0800440void GrMorphologyEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000441 // This is valid because the color components of the result of the kernel all come
442 // exactly from existing values in the source texture.
egdaniel1a8ecdf2014-10-03 06:24:12 -0700443 this->updateInvariantOutputForModulation(inout);
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000444}
445
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000446///////////////////////////////////////////////////////////////////////////////
447
joshualittb0a8a372014-09-23 09:50:21 -0700448GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000449
bsalomonc21b09e2015-08-28 18:46:56 -0700450const GrFragmentProcessor* GrMorphologyEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700451 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
joshualittb0a8a372014-09-23 09:50:21 -0700452 GrProcessorUnitTest::kAlphaTextureIdx;
joshualitt0067ff52015-07-08 14:26:19 -0700453 Direction dir = d->fRandom->nextBool() ? kX_Direction : kY_Direction;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000454 static const int kMaxRadius = 10;
joshualitt0067ff52015-07-08 14:26:19 -0700455 int radius = d->fRandom->nextRangeU(1, kMaxRadius);
456 MorphologyType type = d->fRandom->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000457 GrMorphologyEffect::kDilate_MorphologyType;
458
bsalomon4a339522015-10-06 08:40:50 -0700459 return GrMorphologyEffect::Create(d->fTextures[texIdx], dir, radius, type);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000460}
461
462namespace {
463
cwallez80a61df2015-01-26 12:20:14 -0800464
robertphillipsea461502015-05-26 11:38:03 -0700465void apply_morphology_rect(GrDrawContext* drawContext,
joshualitt570d2f82015-02-25 13:19:48 -0800466 const GrClip& clip,
cwallez80a61df2015-01-26 12:20:14 -0800467 GrTexture* texture,
468 const SkIRect& srcRect,
469 const SkIRect& dstRect,
470 int radius,
471 GrMorphologyEffect::MorphologyType morphType,
472 float bounds[2],
473 Gr1DKernelEffect::Direction direction) {
474 GrPaint paint;
bsalomon4a339522015-10-06 08:40:50 -0700475 paint.addColorFragmentProcessor(GrMorphologyEffect::Create(texture,
bsalomonac856c92015-08-27 06:30:17 -0700476 direction,
477 radius,
478 morphType,
479 bounds))->unref();
robertphillips2e1e51f2015-10-15 08:01:48 -0700480 drawContext->drawNonAARectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
robertphillipsea461502015-05-26 11:38:03 -0700481 SkRect::Make(srcRect));
cwallez80a61df2015-01-26 12:20:14 -0800482}
483
robertphillipsea461502015-05-26 11:38:03 -0700484void apply_morphology_rect_no_bounds(GrDrawContext* drawContext,
joshualitt570d2f82015-02-25 13:19:48 -0800485 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800486 GrTexture* texture,
487 const SkIRect& srcRect,
488 const SkIRect& dstRect,
489 int radius,
490 GrMorphologyEffect::MorphologyType morphType,
491 Gr1DKernelEffect::Direction direction) {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000492 GrPaint paint;
bsalomon4a339522015-10-06 08:40:50 -0700493 paint.addColorFragmentProcessor(GrMorphologyEffect::Create(texture,
bsalomonac856c92015-08-27 06:30:17 -0700494 direction,
495 radius,
496 morphType))->unref();
robertphillips2e1e51f2015-10-15 08:01:48 -0700497 drawContext->drawNonAARectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
robertphillipsea461502015-05-26 11:38:03 -0700498 SkRect::Make(srcRect));
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000499}
500
robertphillipsea461502015-05-26 11:38:03 -0700501void apply_morphology_pass(GrDrawContext* drawContext,
joshualitt570d2f82015-02-25 13:19:48 -0800502 const GrClip& clip,
cwallez80a61df2015-01-26 12:20:14 -0800503 GrTexture* texture,
504 const SkIRect& srcRect,
505 const SkIRect& dstRect,
506 int radius,
507 GrMorphologyEffect::MorphologyType morphType,
508 Gr1DKernelEffect::Direction direction) {
509 float bounds[2] = { 0.0f, 1.0f };
510 SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect;
511 SkIRect middleSrcRect = srcRect, middleDstRect = dstRect;
512 SkIRect upperSrcRect = srcRect, upperDstRect = dstRect;
513 if (direction == Gr1DKernelEffect::kX_Direction) {
514 bounds[0] = (SkIntToScalar(srcRect.left()) + 0.5f) / texture->width();
515 bounds[1] = (SkIntToScalar(srcRect.right()) - 0.5f) / texture->width();
516 lowerSrcRect.fRight = srcRect.left() + radius;
517 lowerDstRect.fRight = dstRect.left() + radius;
518 upperSrcRect.fLeft = srcRect.right() - radius;
519 upperDstRect.fLeft = dstRect.right() - radius;
520 middleSrcRect.inset(radius, 0);
521 middleDstRect.inset(radius, 0);
522 } else {
523 bounds[0] = (SkIntToScalar(srcRect.top()) + 0.5f) / texture->height();
524 bounds[1] = (SkIntToScalar(srcRect.bottom()) - 0.5f) / texture->height();
525 lowerSrcRect.fBottom = srcRect.top() + radius;
526 lowerDstRect.fBottom = dstRect.top() + radius;
527 upperSrcRect.fTop = srcRect.bottom() - radius;
528 upperDstRect.fTop = dstRect.bottom() - radius;
529 middleSrcRect.inset(0, radius);
530 middleDstRect.inset(0, radius);
531 }
532 if (middleSrcRect.fLeft - middleSrcRect.fRight >= 0) {
533 // radius covers srcRect; use bounds over entire draw
robertphillips2e1e51f2015-10-15 08:01:48 -0700534 apply_morphology_rect(drawContext, clip, texture, srcRect, dstRect, radius,
cwallez80a61df2015-01-26 12:20:14 -0800535 morphType, bounds, direction);
536 } else {
537 // Draw upper and lower margins with bounds; middle without.
robertphillips2e1e51f2015-10-15 08:01:48 -0700538 apply_morphology_rect(drawContext, clip, texture, lowerSrcRect, lowerDstRect, radius,
cwallez80a61df2015-01-26 12:20:14 -0800539 morphType, bounds, direction);
robertphillips2e1e51f2015-10-15 08:01:48 -0700540 apply_morphology_rect(drawContext, clip, texture, upperSrcRect, upperDstRect, radius,
cwallez80a61df2015-01-26 12:20:14 -0800541 morphType, bounds, direction);
robertphillips2e1e51f2015-10-15 08:01:48 -0700542 apply_morphology_rect_no_bounds(drawContext, clip, texture, middleSrcRect, middleDstRect,
joshualitt570d2f82015-02-25 13:19:48 -0800543 radius, morphType, direction);
cwallez80a61df2015-01-26 12:20:14 -0800544 }
545}
546
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000547bool apply_morphology(const SkBitmap& input,
548 const SkIRect& rect,
549 GrMorphologyEffect::MorphologyType morphType,
550 SkISize radius,
reedc9b5f8b2015-10-22 13:20:20 -0700551 SkBitmap* dst,
552 GrTextureProvider::SizeConstraint constraint) {
bsalomone3059732014-10-14 11:47:22 -0700553 SkAutoTUnref<GrTexture> srcTexture(SkRef(input.getTexture()));
bsalomon49f085d2014-09-05 13:34:00 -0700554 SkASSERT(srcTexture);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000555 GrContext* context = srcTexture->getContext();
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000556
joshualitt570d2f82015-02-25 13:19:48 -0800557 // setup new clip
558 GrClip clip(SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
559 SkIntToScalar(srcTexture->height())));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000560
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000561 SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
bsalomonf2703d82014-10-28 14:33:06 -0700562 GrSurfaceDesc desc;
bsalomon6bc1b5f2015-02-23 09:06:38 -0800563 desc.fFlags = kRenderTarget_GrSurfaceFlag;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000564 desc.fWidth = rect.width();
565 desc.fHeight = rect.height();
566 desc.fConfig = kSkia8888_GrPixelConfig;
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000567 SkIRect srcRect = rect;
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000568
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000569 if (radius.fWidth > 0) {
reedc9b5f8b2015-10-22 13:20:20 -0700570 GrTextureProvider::SizeConstraint horiConstraint = constraint;
571 if (radius.fHeight > 0) {
572 // Optimization: we will fall through and allocate the "real" texture after this one
573 // so ours can be approximate (likely faster to allocate)
574 horiConstraint = GrTextureProvider::kApprox_SizeConstraint;
575 }
576
577 GrTexture* scratch = context->textureProvider()->createTexture(desc, horiConstraint);
halcanary96fcdcc2015-08-27 07:41:13 -0700578 if (nullptr == scratch) {
senorblanco673d9732014-08-15 10:48:43 -0700579 return false;
580 }
robertphillips77a2e522015-10-17 07:43:27 -0700581 SkAutoTUnref<GrDrawContext> dstDrawContext(
582 context->drawContext(scratch->asRenderTarget()));
robertphillipsff0ca5e2015-07-22 11:54:44 -0700583 if (!dstDrawContext) {
584 return false;
585 }
586
robertphillips2e1e51f2015-10-15 08:01:48 -0700587 apply_morphology_pass(dstDrawContext, clip, srcTexture,
joshualitt570d2f82015-02-25 13:19:48 -0800588 srcRect, dstRect, radius.fWidth, morphType,
589 Gr1DKernelEffect::kX_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000590 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
591 dstRect.width(), radius.fHeight);
bsalomon89c62982014-11-03 12:08:42 -0800592 GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType ?
593 SK_ColorWHITE :
594 SK_ColorTRANSPARENT;
robertphillips2e1e51f2015-10-15 08:01:48 -0700595 dstDrawContext->clear(&clearRect, clearColor, false);
robertphillipsff0ca5e2015-07-22 11:54:44 -0700596
robertphillipseb86b552015-07-23 11:35:08 -0700597 srcTexture.reset(scratch);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000598 srcRect = dstRect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000599 }
600 if (radius.fHeight > 0) {
reedc9b5f8b2015-10-22 13:20:20 -0700601 GrTexture* scratch = context->textureProvider()->createTexture(desc, constraint);
halcanary96fcdcc2015-08-27 07:41:13 -0700602 if (nullptr == scratch) {
senorblanco673d9732014-08-15 10:48:43 -0700603 return false;
604 }
robertphillips77a2e522015-10-17 07:43:27 -0700605 SkAutoTUnref<GrDrawContext> dstDrawContext(
606 context->drawContext(scratch->asRenderTarget()));
robertphillipsff0ca5e2015-07-22 11:54:44 -0700607 if (!dstDrawContext) {
608 return false;
609 }
610
robertphillips2e1e51f2015-10-15 08:01:48 -0700611 apply_morphology_pass(dstDrawContext, clip, srcTexture,
joshualitt570d2f82015-02-25 13:19:48 -0800612 srcRect, dstRect, radius.fHeight, morphType,
613 Gr1DKernelEffect::kY_Direction);
robertphillipsff0ca5e2015-07-22 11:54:44 -0700614
robertphillipseb86b552015-07-23 11:35:08 -0700615 srcTexture.reset(scratch);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000616 }
bsalomone3059732014-10-14 11:47:22 -0700617 SkImageFilter::WrapTexture(srcTexture, rect.width(), rect.height(), dst);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000618 return true;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000619}
620
621};
622
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000623bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
624 Proxy* proxy,
625 const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000626 const Context& ctx,
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000627 SkBitmap* result,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000628 SkIPoint* offset) const {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000629 SkBitmap input = src;
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000630 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco9a70b6e2015-10-16 11:35:14 -0700631 if (!this->filterInputGPU(0, proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000632 return false;
633 }
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000634 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000635 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000636 return false;
637 }
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000638 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
639 SkIntToScalar(this->radius().height()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000640 ctx.ctm().mapVectors(&radius, 1);
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000641 int width = SkScalarFloorToInt(radius.fX);
642 int height = SkScalarFloorToInt(radius.fY);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000643
644 if (width < 0 || height < 0) {
645 return false;
646 }
647
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000648 SkIRect srcBounds = bounds;
649 srcBounds.offset(-srcOffset);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000650 if (width == 0 && height == 0) {
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000651 input.extractSubset(result, srcBounds);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000652 offset->fX = bounds.left();
653 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000654 return true;
655 }
656
robertphillipsff0ca5e2015-07-22 11:54:44 -0700657 GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType
658 : GrMorphologyEffect::kErode_MorphologyType;
reedc9b5f8b2015-10-22 13:20:20 -0700659 if (!apply_morphology(input, srcBounds, type, SkISize::Make(width, height), result,
660 GrTextureProvider::FromImageFilter(ctx.sizeConstraint()))) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000661 return false;
662 }
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000663 offset->fX = bounds.left();
664 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000665 return true;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000666}
667
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000668bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000669 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000670 return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000671}
672
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000673bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000674 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000675 return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000676}
677
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000678#endif