blob: 03c50e4927d8c250c5227cecdb59a8406d1a0194 [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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000013#include "SkRect.h"
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000014#include "SkMorphology_opts.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000015#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000016#include "GrContext.h"
17#include "GrTexture.h"
joshualittb0a8a372014-09-23 09:50:21 -070018#include "GrTBackendProcessorFactory.h"
19#include "gl/GrGLProcessor.h"
joshualitt30ba4362014-08-21 20:18:45 -070020#include "gl/builders/GrGLProgramBuilder.h"
senorblanco@chromium.org84207c42012-08-22 20:51:19 +000021#include "effects/Gr1DKernelEffect.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000022#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000023
reed9fa60da2014-08-21 07:59:51 -070024#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000025SkMorphologyImageFilter::SkMorphologyImageFilter(SkReadBuffer& buffer)
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000026 : INHERITED(1, buffer) {
tomhudson@google.com75589252012-04-10 17:42:21 +000027 fRadius.fWidth = buffer.readInt();
28 fRadius.fHeight = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000029 buffer.validate((fRadius.fWidth >= 0) &&
30 (fRadius.fHeight >= 0));
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000031}
reed9fa60da2014-08-21 07:59:51 -070032#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000033
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +000034SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
35 int radiusY,
36 SkImageFilter* input,
senorblanco5e5f9482014-08-26 12:27:12 -070037 const CropRect* cropRect,
38 uint32_t uniqueID)
39 : INHERITED(1, &input, cropRect, uniqueID), fRadius(SkISize::Make(radiusX, radiusY)) {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000040}
41
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000042void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000043 this->INHERITED::flatten(buffer);
tomhudson@google.com75589252012-04-10 17:42:21 +000044 buffer.writeInt(fRadius.fWidth);
45 buffer.writeInt(fRadius.fHeight);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000046}
47
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000048enum MorphDirection {
49 kX, kY
50};
51
52template<MorphDirection direction>
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000053static void erode(const SkPMColor* src, SkPMColor* dst,
54 int radius, int width, int height,
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000055 int srcStride, int dstStride)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000056{
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000057 const int srcStrideX = direction == kX ? 1 : srcStride;
58 const int dstStrideX = direction == kX ? 1 : dstStride;
59 const int srcStrideY = direction == kX ? srcStride : 1;
60 const int dstStrideY = direction == kX ? dstStride : 1;
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000061 radius = SkMin32(radius, width - 1);
62 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000063 for (int x = 0; x < width; ++x) {
64 const SkPMColor* lp = src;
65 const SkPMColor* up = upperSrc;
66 SkPMColor* dptr = dst;
67 for (int y = 0; y < height; ++y) {
68 int minB = 255, minG = 255, minR = 255, minA = 255;
69 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
70 int b = SkGetPackedB32(*p);
71 int g = SkGetPackedG32(*p);
72 int r = SkGetPackedR32(*p);
73 int a = SkGetPackedA32(*p);
74 if (b < minB) minB = b;
75 if (g < minG) minG = g;
76 if (r < minR) minR = r;
77 if (a < minA) minA = a;
78 }
79 *dptr = SkPackARGB32(minA, minR, minG, minB);
80 dptr += dstStrideY;
81 lp += srcStrideY;
82 up += srcStrideY;
83 }
84 if (x >= radius) src += srcStrideX;
85 if (x + radius < width - 1) upperSrc += srcStrideX;
86 dst += dstStrideX;
87 }
88}
89
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000090template<MorphDirection direction>
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000091static void dilate(const SkPMColor* src, SkPMColor* dst,
92 int radius, int width, int height,
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000093 int srcStride, int dstStride)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000094{
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +000095 const int srcStrideX = direction == kX ? 1 : srcStride;
96 const int dstStrideX = direction == kX ? 1 : dstStride;
97 const int srcStrideY = direction == kX ? srcStride : 1;
98 const int dstStrideY = direction == kX ? dstStride : 1;
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000099 radius = SkMin32(radius, width - 1);
100 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000101 for (int x = 0; x < width; ++x) {
102 const SkPMColor* lp = src;
103 const SkPMColor* up = upperSrc;
104 SkPMColor* dptr = dst;
105 for (int y = 0; y < height; ++y) {
106 int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
107 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
108 int b = SkGetPackedB32(*p);
109 int g = SkGetPackedG32(*p);
110 int r = SkGetPackedR32(*p);
111 int a = SkGetPackedA32(*p);
112 if (b > maxB) maxB = b;
113 if (g > maxG) maxG = g;
114 if (r > maxR) maxR = r;
115 if (a > maxA) maxA = a;
116 }
117 *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
118 dptr += dstStrideY;
119 lp += srcStrideY;
120 up += srcStrideY;
121 }
122 if (x >= radius) src += srcStrideX;
123 if (x + radius < width - 1) upperSrc += srcStrideX;
124 dst += dstStrideX;
125 }
126}
127
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000128static void callProcX(SkMorphologyImageFilter::Proc procX, const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000129{
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000130 procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
131 radiusX, bounds.width(), bounds.height(),
132 src.rowBytesAsPixels(), dst->rowBytesAsPixels());
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000133}
134
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000135static void callProcY(SkMorphologyImageFilter::Proc procY, const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000136{
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000137 procY(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
138 radiusY, bounds.height(), bounds.width(),
139 src.rowBytesAsPixels(), dst->rowBytesAsPixels());
140}
141
142bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
143 SkMorphologyImageFilter::Proc procY,
144 Proxy* proxy,
145 const SkBitmap& source,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000146 const Context& ctx,
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000147 SkBitmap* dst,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000148 SkIPoint* offset) const {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000149 SkBitmap src = source;
150 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000151 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000152 return false;
skia.committer@gmail.com1878a442014-01-23 18:48:56 +0000153 }
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000154
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000155 if (src.colorType() != kN32_SkColorType) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000156 return false;
157 }
158
159 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000160 if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000161 return false;
162 }
163
164 SkAutoLockPixels alp(src);
165 if (!src.getPixels()) {
166 return false;
167 }
168
reed84825042014-09-02 12:50:45 -0700169 if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000170 return false;
171 }
172
173 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
174 SkIntToScalar(this->radius().height()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000175 ctx.ctm().mapVectors(&radius, 1);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000176 int width = SkScalarFloorToInt(radius.fX);
177 int height = SkScalarFloorToInt(radius.fY);
178
179 if (width < 0 || height < 0) {
180 return false;
181 }
182
183 SkIRect srcBounds = bounds;
184 srcBounds.offset(-srcOffset);
185
186 if (width == 0 && height == 0) {
187 src.extractSubset(dst, srcBounds);
188 offset->fX = bounds.left();
189 offset->fY = bounds.top();
190 return true;
191 }
192
193 SkBitmap temp;
reed84825042014-09-02 12:50:45 -0700194 if (!temp.tryAllocPixels(dst->info())) {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000195 return false;
196 }
197
198 if (width > 0 && height > 0) {
199 callProcX(procX, src, &temp, width, srcBounds);
200 SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
201 callProcY(procY, temp, dst, height, tmpBounds);
202 } else if (width > 0) {
203 callProcX(procX, src, dst, width, srcBounds);
204 } else if (height > 0) {
205 callProcY(procY, src, dst, height, srcBounds);
206 }
207 offset->fX = bounds.left();
208 offset->fY = bounds.top();
209 return true;
senorblanco@chromium.org76d4d042014-01-23 18:45:23 +0000210}
211
skia.committer@gmail.com1878a442014-01-23 18:48:56 +0000212bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000213 const SkBitmap& source, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000214 SkBitmap* dst, SkIPoint* offset) const {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000215 Proc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType);
216 if (!erodeXProc) {
217 erodeXProc = erode<kX>;
senorblanco@chromium.org7a47ad32013-10-30 21:57:04 +0000218 }
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000219 Proc erodeYProc = SkMorphologyGetPlatformProc(kErodeY_SkMorphologyProcType);
220 if (!erodeYProc) {
221 erodeYProc = erode<kY>;
senorblanco@chromium.org76d4d042014-01-23 18:45:23 +0000222 }
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000223 return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctx, dst, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000224}
225
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000226bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000227 const SkBitmap& source, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000228 SkBitmap* dst, SkIPoint* offset) const {
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000229 Proc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType);
230 if (!dilateXProc) {
231 dilateXProc = dilate<kX>;
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000232 }
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000233 Proc dilateYProc = SkMorphologyGetPlatformProc(kDilateY_SkMorphologyProcType);
234 if (!dilateYProc) {
235 dilateYProc = dilate<kY>;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000236 }
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000237 return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctx, dst, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000238}
239
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000240void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
241 if (getInput(0)) {
242 getInput(0)->computeFastBounds(src, dst);
243 } else {
244 *dst = src;
245 }
246 dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
247}
248
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000249bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
250 SkIRect* dst) const {
251 SkIRect bounds = src;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000252 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
253 SkIntToScalar(this->radius().height()));
254 ctm.mapVectors(&radius, 1);
255 bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
senorblanco1150a6d2014-08-25 12:46:58 -0700256 if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
257 return false;
258 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000259 *dst = bounds;
260 return true;
261}
262
reed9fa60da2014-08-21 07:59:51 -0700263SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
264 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
265 const int width = buffer.readInt();
266 const int height = buffer.readInt();
senorblanco5e5f9482014-08-26 12:27:12 -0700267 return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
reed9fa60da2014-08-21 07:59:51 -0700268}
269
270SkFlattenable* SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
271 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
272 const int width = buffer.readInt();
273 const int height = buffer.readInt();
senorblanco5e5f9482014-08-26 12:27:12 -0700274 return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
reed9fa60da2014-08-21 07:59:51 -0700275}
276
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000277#if SK_SUPPORT_GPU
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000278
279///////////////////////////////////////////////////////////////////////////////
280
281class GrGLMorphologyEffect;
282
283/**
284 * Morphology effects. Depending upon the type of morphology, either the
285 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
286 * kernel is selected as the new color. The new color is modulated by the input
287 * color.
288 */
289class GrMorphologyEffect : public Gr1DKernelEffect {
290
291public:
292
293 enum MorphologyType {
294 kErode_MorphologyType,
295 kDilate_MorphologyType,
296 };
297
joshualittb0a8a372014-09-23 09:50:21 -0700298 static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
299 MorphologyType type) {
bsalomon55fad7a2014-07-08 07:34:20 -0700300 return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000301 }
302
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000303 virtual ~GrMorphologyEffect();
304
305 MorphologyType type() const { return fType; }
306
307 static const char* Name() { return "Morphology"; }
308
joshualittb0a8a372014-09-23 09:50:21 -0700309 typedef GrGLMorphologyEffect GLProcessor;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000310
joshualittb0a8a372014-09-23 09:50:21 -0700311 virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000312
313protected:
314
315 MorphologyType fType;
316
317private:
bsalomon0e08fc12014-10-15 08:19:04 -0700318 virtual bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000319
egdaniel1a8ecdf2014-10-03 06:24:12 -0700320 virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
321
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000322 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
323
joshualittb0a8a372014-09-23 09:50:21 -0700324 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000325
326 typedef Gr1DKernelEffect INHERITED;
327};
328
329///////////////////////////////////////////////////////////////////////////////
330
joshualittb0a8a372014-09-23 09:50:21 -0700331class GrGLMorphologyEffect : public GrGLFragmentProcessor {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000332public:
joshualittb0a8a372014-09-23 09:50:21 -0700333 GrGLMorphologyEffect (const GrBackendProcessorFactory&, const GrProcessor&);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000334
joshualitt15988992014-10-09 15:04:05 -0700335 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700336 const GrFragmentProcessor&,
337 const GrProcessorKey&,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000338 const char* outputColor,
339 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000340 const TransformedCoordsArray&,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000341 const TextureSamplerArray&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000342
joshualittb0a8a372014-09-23 09:50:21 -0700343 static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000344
joshualittb0a8a372014-09-23 09:50:21 -0700345 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000346
347private:
348 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
349
kkinnunen7510b222014-07-30 00:04:16 -0700350 int fRadius;
351 GrMorphologyEffect::MorphologyType fType;
352 GrGLProgramDataManager::UniformHandle fImageIncrementUni;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000353
joshualittb0a8a372014-09-23 09:50:21 -0700354 typedef GrGLFragmentProcessor INHERITED;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000355};
356
joshualittb0a8a372014-09-23 09:50:21 -0700357GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendProcessorFactory& factory,
358 const GrProcessor& proc)
bsalomon@google.com77af6802013-10-02 13:04:56 +0000359 : INHERITED(factory) {
joshualittb0a8a372014-09-23 09:50:21 -0700360 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000361 fRadius = m.radius();
362 fType = m.type();
363}
364
joshualitt15988992014-10-09 15:04:05 -0700365void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder,
joshualittb0a8a372014-09-23 09:50:21 -0700366 const GrFragmentProcessor&,
367 const GrProcessorKey& key,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000368 const char* outputColor,
369 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000370 const TransformedCoordsArray& coords,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000371 const TextureSamplerArray& samplers) {
joshualitt30ba4362014-08-21 20:18:45 -0700372 fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000373 kVec2f_GrSLType, "ImageIncrement");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000374
joshualitt15988992014-10-09 15:04:05 -0700375 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700376 SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000377 const char* func;
378 switch (fType) {
379 case GrMorphologyEffect::kErode_MorphologyType:
joshualitt30ba4362014-08-21 20:18:45 -0700380 fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000381 func = "min";
382 break;
383 case GrMorphologyEffect::kDilate_MorphologyType:
joshualitt30ba4362014-08-21 20:18:45 -0700384 fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000385 func = "max";
386 break;
387 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000388 SkFAIL("Unexpected type");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000389 func = ""; // suppress warning
390 break;
391 }
392 const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
393
joshualitt30ba4362014-08-21 20:18:45 -0700394 fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
395 fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
396 fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
397 fsBuilder->appendTextureLookup(samplers[0], "coord");
398 fsBuilder->codeAppend(");\n");
399 fsBuilder->codeAppendf("\t\t\tcoord += %s;\n", imgInc);
400 fsBuilder->codeAppend("\t\t}\n");
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000401 SkString modulate;
egdaniel089f8de2014-10-09 10:34:58 -0700402 GrGLSLMulVarBy4f(&modulate, outputColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700403 fsBuilder->codeAppend(modulate.c_str());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000404}
405
joshualittb0a8a372014-09-23 09:50:21 -0700406void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
407 const GrGLCaps&, GrProcessorKeyBuilder* b) {
408 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
bsalomon63e99f72014-07-21 08:03:14 -0700409 uint32_t key = static_cast<uint32_t>(m.radius());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000410 key |= (m.type() << 8);
bsalomon63e99f72014-07-21 08:03:14 -0700411 b->add32(key);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000412}
413
kkinnunen7510b222014-07-30 00:04:16 -0700414void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700415 const GrProcessor& proc) {
416 const Gr1DKernelEffect& kern = proc.cast<Gr1DKernelEffect>();
bsalomon@google.com2d0bade2012-10-26 19:01:17 +0000417 GrTexture& texture = *kern.texture(0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000418 // the code we generated was for a specific kernel radius
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000419 SkASSERT(kern.radius() == fRadius);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000420 float imageIncrement[2] = { 0 };
421 switch (kern.direction()) {
422 case Gr1DKernelEffect::kX_Direction:
423 imageIncrement[0] = 1.0f / texture.width();
424 break;
425 case Gr1DKernelEffect::kY_Direction:
426 imageIncrement[1] = 1.0f / texture.height();
427 break;
428 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000429 SkFAIL("Unknown filter direction.");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000430 }
kkinnunen7510b222014-07-30 00:04:16 -0700431 pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000432}
433
434///////////////////////////////////////////////////////////////////////////////
435
436GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
437 Direction direction,
438 int radius,
439 MorphologyType type)
440 : Gr1DKernelEffect(texture, direction, radius)
441 , fType(type) {
442}
443
444GrMorphologyEffect::~GrMorphologyEffect() {
445}
446
joshualittb0a8a372014-09-23 09:50:21 -0700447const GrBackendFragmentProcessorFactory& GrMorphologyEffect::getFactory() const {
448 return GrTBackendFragmentProcessorFactory<GrMorphologyEffect>::getInstance();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000449}
450
bsalomon0e08fc12014-10-15 08:19:04 -0700451bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700452 const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
bsalomon420d7e92014-10-16 09:18:09 -0700453 return (this->radius() == s.radius() &&
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000454 this->direction() == s.direction() &&
455 this->type() == s.type());
456}
457
egdaniel1a8ecdf2014-10-03 06:24:12 -0700458void GrMorphologyEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000459 // This is valid because the color components of the result of the kernel all come
460 // exactly from existing values in the source texture.
egdaniel1a8ecdf2014-10-03 06:24:12 -0700461 this->updateInvariantOutputForModulation(inout);
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000462}
463
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000464///////////////////////////////////////////////////////////////////////////////
465
joshualittb0a8a372014-09-23 09:50:21 -0700466GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000467
joshualittb0a8a372014-09-23 09:50:21 -0700468GrFragmentProcessor* GrMorphologyEffect::TestCreate(SkRandom* random,
469 GrContext*,
470 const GrDrawTargetCaps&,
471 GrTexture* textures[]) {
472 int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
473 GrProcessorUnitTest::kAlphaTextureIdx;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000474 Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
475 static const int kMaxRadius = 10;
476 int radius = random->nextRangeU(1, kMaxRadius);
477 MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
478 GrMorphologyEffect::kDilate_MorphologyType;
479
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000480 return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000481}
482
483namespace {
484
485void apply_morphology_pass(GrContext* context,
486 GrTexture* texture,
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000487 const SkIRect& srcRect,
488 const SkIRect& dstRect,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000489 int radius,
490 GrMorphologyEffect::MorphologyType morphType,
491 Gr1DKernelEffect::Direction direction) {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000492 GrPaint paint;
joshualittb0a8a372014-09-23 09:50:21 -0700493 paint.addColorProcessor(GrMorphologyEffect::Create(texture,
494 direction,
495 radius,
496 morphType))->unref();
reed@google.com44699382013-10-31 17:28:30 +0000497 context->drawRectToRect(paint, SkRect::Make(dstRect), SkRect::Make(srcRect));
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000498}
499
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000500bool apply_morphology(const SkBitmap& input,
501 const SkIRect& rect,
502 GrMorphologyEffect::MorphologyType morphType,
503 SkISize radius,
504 SkBitmap* dst) {
bsalomone3059732014-10-14 11:47:22 -0700505 SkAutoTUnref<GrTexture> srcTexture(SkRef(input.getTexture()));
bsalomon49f085d2014-09-05 13:34:00 -0700506 SkASSERT(srcTexture);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000507 GrContext* context = srcTexture->getContext();
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000508
509 GrContext::AutoMatrix am;
510 am.setIdentity(context);
511
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000512 GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000513 SkIntToScalar(srcTexture->height())));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000514
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000515 SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000516 GrTextureDesc desc;
517 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000518 desc.fWidth = rect.width();
519 desc.fHeight = rect.height();
520 desc.fConfig = kSkia8888_GrPixelConfig;
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000521 SkIRect srcRect = rect;
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000522
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000523 if (radius.fWidth > 0) {
bsalomone3059732014-10-14 11:47:22 -0700524 GrTexture* texture = context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
525 if (NULL == texture) {
senorblanco673d9732014-08-15 10:48:43 -0700526 return false;
527 }
bsalomone3059732014-10-14 11:47:22 -0700528 GrContext::AutoRenderTarget art(context, texture->asRenderTarget());
529 apply_morphology_pass(context, srcTexture, srcRect, dstRect, radius.fWidth,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000530 morphType, Gr1DKernelEffect::kX_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000531 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
532 dstRect.width(), radius.fHeight);
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000533 context->clear(&clearRect, GrMorphologyEffect::kErode_MorphologyType == morphType ?
534 SK_ColorWHITE :
robertphillips@google.com56ce48a2013-10-31 21:44:25 +0000535 SK_ColorTRANSPARENT, false);
bsalomone3059732014-10-14 11:47:22 -0700536 srcTexture.reset(texture);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000537 srcRect = dstRect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000538 }
539 if (radius.fHeight > 0) {
bsalomone3059732014-10-14 11:47:22 -0700540 GrTexture* texture = context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
541 if (NULL == texture) {
senorblanco673d9732014-08-15 10:48:43 -0700542 return false;
543 }
bsalomone3059732014-10-14 11:47:22 -0700544 GrContext::AutoRenderTarget art(context, texture->asRenderTarget());
545 apply_morphology_pass(context, srcTexture, srcRect, dstRect, radius.fHeight,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000546 morphType, Gr1DKernelEffect::kY_Direction);
bsalomone3059732014-10-14 11:47:22 -0700547 srcTexture.reset(texture);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000548 }
bsalomone3059732014-10-14 11:47:22 -0700549 SkImageFilter::WrapTexture(srcTexture, rect.width(), rect.height(), dst);
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000550 return true;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000551}
552
553};
554
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000555bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
556 Proxy* proxy,
557 const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000558 const Context& ctx,
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000559 SkBitmap* result,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000560 SkIPoint* offset) const {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000561 SkBitmap input = src;
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000562 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000563 if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000564 return false;
565 }
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000566 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000567 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000568 return false;
569 }
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000570 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
571 SkIntToScalar(this->radius().height()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000572 ctx.ctm().mapVectors(&radius, 1);
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000573 int width = SkScalarFloorToInt(radius.fX);
574 int height = SkScalarFloorToInt(radius.fY);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000575
576 if (width < 0 || height < 0) {
577 return false;
578 }
579
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000580 SkIRect srcBounds = bounds;
581 srcBounds.offset(-srcOffset);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000582 if (width == 0 && height == 0) {
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000583 input.extractSubset(result, srcBounds);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000584 offset->fX = bounds.left();
585 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000586 return true;
587 }
588
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000589 GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType;
590 if (!apply_morphology(input, srcBounds, type,
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000591 SkISize::Make(width, height), result)) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000592 return false;
593 }
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000594 offset->fX = bounds.left();
595 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000596 return true;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000597}
598
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000599bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000600 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000601 return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000602}
603
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000604bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000605 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000606 return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000607}
608
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000609#endif