blob: 02c0be1674a7724ae5140e6f22d0bf39fde122cd [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"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +000018#include "GrTBackendEffectFactory.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +000019#include "gl/GrGLEffect.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
bsalomon83d081a2014-07-08 09:56:10 -0700298 static GrEffect* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
bsalomon55fad7a2014-07-08 07:34:20 -0700299 return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000300 }
301
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000302 virtual ~GrMorphologyEffect();
303
304 MorphologyType type() const { return fType; }
305
306 static const char* Name() { return "Morphology"; }
307
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000308 typedef GrGLMorphologyEffect GLEffect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000309
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000310 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000311 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000312
313protected:
314
315 MorphologyType fType;
316
317private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000318 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000319
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000320 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
321
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000322 GR_DECLARE_EFFECT_TEST;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000323
324 typedef Gr1DKernelEffect INHERITED;
325};
326
327///////////////////////////////////////////////////////////////////////////////
328
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000329class GrGLMorphologyEffect : public GrGLEffect {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000330public:
joshualitt49586be2014-09-16 08:21:41 -0700331 GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrEffect&);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000332
joshualitt30ba4362014-08-21 20:18:45 -0700333 virtual void emitCode(GrGLProgramBuilder*,
joshualitt49586be2014-09-16 08:21:41 -0700334 const GrEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700335 const GrEffectKey&,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000336 const char* outputColor,
337 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000338 const TransformedCoordsArray&,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000339 const TextureSamplerArray&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000340
joshualitt49586be2014-09-16 08:21:41 -0700341 static inline void GenKey(const GrEffect&, const GrGLCaps&, GrEffectKeyBuilder* b);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000342
joshualitt49586be2014-09-16 08:21:41 -0700343 virtual void setData(const GrGLProgramDataManager&, const GrEffect&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000344
345private:
346 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
347
kkinnunen7510b222014-07-30 00:04:16 -0700348 int fRadius;
349 GrMorphologyEffect::MorphologyType fType;
350 GrGLProgramDataManager::UniformHandle fImageIncrementUni;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000351
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000352 typedef GrGLEffect INHERITED;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000353};
354
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000355GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
joshualitt49586be2014-09-16 08:21:41 -0700356 const GrEffect& effect)
bsalomon@google.com77af6802013-10-02 13:04:56 +0000357 : INHERITED(factory) {
joshualitt49586be2014-09-16 08:21:41 -0700358 const GrMorphologyEffect& m = effect.cast<GrMorphologyEffect>();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000359 fRadius = m.radius();
360 fType = m.type();
361}
362
joshualitt30ba4362014-08-21 20:18:45 -0700363void GrGLMorphologyEffect::emitCode(GrGLProgramBuilder* builder,
joshualitt49586be2014-09-16 08:21:41 -0700364 const GrEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700365 const GrEffectKey& key,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000366 const char* outputColor,
367 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000368 const TransformedCoordsArray& coords,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000369 const TextureSamplerArray& samplers) {
joshualitt30ba4362014-08-21 20:18:45 -0700370 fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000371 kVec2f_GrSLType, "ImageIncrement");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000372
joshualitt30ba4362014-08-21 20:18:45 -0700373 GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
374 SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000375 const char* func;
376 switch (fType) {
377 case GrMorphologyEffect::kErode_MorphologyType:
joshualitt30ba4362014-08-21 20:18:45 -0700378 fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000379 func = "min";
380 break;
381 case GrMorphologyEffect::kDilate_MorphologyType:
joshualitt30ba4362014-08-21 20:18:45 -0700382 fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000383 func = "max";
384 break;
385 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000386 SkFAIL("Unexpected type");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000387 func = ""; // suppress warning
388 break;
389 }
390 const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
391
joshualitt30ba4362014-08-21 20:18:45 -0700392 fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
393 fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
394 fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
395 fsBuilder->appendTextureLookup(samplers[0], "coord");
396 fsBuilder->codeAppend(");\n");
397 fsBuilder->codeAppendf("\t\t\tcoord += %s;\n", imgInc);
398 fsBuilder->codeAppend("\t\t}\n");
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000399 SkString modulate;
400 GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
joshualitt30ba4362014-08-21 20:18:45 -0700401 fsBuilder->codeAppend(modulate.c_str());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000402}
403
joshualitt49586be2014-09-16 08:21:41 -0700404void GrGLMorphologyEffect::GenKey(const GrEffect& effect,
bsalomon63e99f72014-07-21 08:03:14 -0700405 const GrGLCaps&, GrEffectKeyBuilder* b) {
joshualitt49586be2014-09-16 08:21:41 -0700406 const GrMorphologyEffect& m = effect.cast<GrMorphologyEffect>();
bsalomon63e99f72014-07-21 08:03:14 -0700407 uint32_t key = static_cast<uint32_t>(m.radius());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000408 key |= (m.type() << 8);
bsalomon63e99f72014-07-21 08:03:14 -0700409 b->add32(key);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000410}
411
kkinnunen7510b222014-07-30 00:04:16 -0700412void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman,
joshualitt49586be2014-09-16 08:21:41 -0700413 const GrEffect& effect) {
414 const Gr1DKernelEffect& kern = effect.cast<Gr1DKernelEffect>();
bsalomon@google.com2d0bade2012-10-26 19:01:17 +0000415 GrTexture& texture = *kern.texture(0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000416 // the code we generated was for a specific kernel radius
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000417 SkASSERT(kern.radius() == fRadius);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000418 float imageIncrement[2] = { 0 };
419 switch (kern.direction()) {
420 case Gr1DKernelEffect::kX_Direction:
421 imageIncrement[0] = 1.0f / texture.width();
422 break;
423 case Gr1DKernelEffect::kY_Direction:
424 imageIncrement[1] = 1.0f / texture.height();
425 break;
426 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000427 SkFAIL("Unknown filter direction.");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000428 }
kkinnunen7510b222014-07-30 00:04:16 -0700429 pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000430}
431
432///////////////////////////////////////////////////////////////////////////////
433
434GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
435 Direction direction,
436 int radius,
437 MorphologyType type)
438 : Gr1DKernelEffect(texture, direction, radius)
439 , fType(type) {
440}
441
442GrMorphologyEffect::~GrMorphologyEffect() {
443}
444
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000445const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const {
446 return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000447}
448
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000449bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700450 const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000451 return (this->texture(0) == s.texture(0) &&
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000452 this->radius() == s.radius() &&
453 this->direction() == s.direction() &&
454 this->type() == s.type());
455}
456
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000457void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
458 // This is valid because the color components of the result of the kernel all come
459 // exactly from existing values in the source texture.
460 this->updateConstantColorComponentsForModulation(color, validFlags);
461}
462
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000463///////////////////////////////////////////////////////////////////////////////
464
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000465GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000466
bsalomon83d081a2014-07-08 09:56:10 -0700467GrEffect* GrMorphologyEffect::TestCreate(SkRandom* random,
468 GrContext*,
469 const GrDrawTargetCaps&,
470 GrTexture* textures[]) {
bsalomon@google.com6f261be2012-10-24 19:07:10 +0000471 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
472 GrEffectUnitTest::kAlphaTextureIdx;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000473 Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
474 static const int kMaxRadius = 10;
475 int radius = random->nextRangeU(1, kMaxRadius);
476 MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
477 GrMorphologyEffect::kDilate_MorphologyType;
478
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000479 return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000480}
481
482namespace {
483
484void apply_morphology_pass(GrContext* context,
485 GrTexture* texture,
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000486 const SkIRect& srcRect,
487 const SkIRect& dstRect,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000488 int radius,
489 GrMorphologyEffect::MorphologyType morphType,
490 Gr1DKernelEffect::Direction direction) {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000491 GrPaint paint;
commit-bot@chromium.org42dacab2013-07-13 17:24:24 +0000492 paint.addColorEffect(GrMorphologyEffect::Create(texture,
493 direction,
494 radius,
495 morphType))->unref();
reed@google.com44699382013-10-31 17:28:30 +0000496 context->drawRectToRect(paint, SkRect::Make(dstRect), SkRect::Make(srcRect));
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000497}
498
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000499bool apply_morphology(const SkBitmap& input,
500 const SkIRect& rect,
501 GrMorphologyEffect::MorphologyType morphType,
502 SkISize radius,
503 SkBitmap* dst) {
504 GrTexture* srcTexture = input.getTexture();
bsalomon49f085d2014-09-05 13:34:00 -0700505 SkASSERT(srcTexture);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000506 GrContext* context = srcTexture->getContext();
507 srcTexture->ref();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000508 SkAutoTUnref<GrTexture> src(srcTexture);
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000509
510 GrContext::AutoMatrix am;
511 am.setIdentity(context);
512
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000513 GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000514 SkIntToScalar(srcTexture->height())));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000515
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000516 SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000517 GrTextureDesc desc;
518 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000519 desc.fWidth = rect.width();
520 desc.fHeight = rect.height();
521 desc.fConfig = kSkia8888_GrPixelConfig;
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000522 SkIRect srcRect = rect;
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000523
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000524 if (radius.fWidth > 0) {
525 GrAutoScratchTexture ast(context, desc);
senorblanco673d9732014-08-15 10:48:43 -0700526 if (NULL == ast.texture()) {
527 return false;
528 }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000529 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000530 apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000531 morphType, Gr1DKernelEffect::kX_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000532 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
533 dstRect.width(), radius.fHeight);
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000534 context->clear(&clearRect, GrMorphologyEffect::kErode_MorphologyType == morphType ?
535 SK_ColorWHITE :
robertphillips@google.com56ce48a2013-10-31 21:44:25 +0000536 SK_ColorTRANSPARENT, false);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000537 src.reset(ast.detach());
538 srcRect = dstRect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000539 }
540 if (radius.fHeight > 0) {
541 GrAutoScratchTexture ast(context, desc);
senorblanco673d9732014-08-15 10:48:43 -0700542 if (NULL == ast.texture()) {
543 return false;
544 }
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000545 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000546 apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000547 morphType, Gr1DKernelEffect::kY_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000548 src.reset(ast.detach());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000549 }
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000550 SkImageFilter::WrapTexture(src, rect.width(), rect.height(), dst);
551 return true;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000552}
553
554};
555
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000556bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
557 Proxy* proxy,
558 const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000559 const Context& ctx,
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000560 SkBitmap* result,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000561 SkIPoint* offset) const {
senorblanco@chromium.org6aa6fec2014-03-03 22:13:56 +0000562 SkBitmap input = src;
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000563 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000564 if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000565 return false;
566 }
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000567 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000568 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000569 return false;
570 }
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000571 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
572 SkIntToScalar(this->radius().height()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000573 ctx.ctm().mapVectors(&radius, 1);
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000574 int width = SkScalarFloorToInt(radius.fX);
575 int height = SkScalarFloorToInt(radius.fY);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000576
577 if (width < 0 || height < 0) {
578 return false;
579 }
580
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000581 SkIRect srcBounds = bounds;
582 srcBounds.offset(-srcOffset);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000583 if (width == 0 && height == 0) {
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000584 input.extractSubset(result, srcBounds);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000585 offset->fX = bounds.left();
586 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000587 return true;
588 }
589
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000590 GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType;
591 if (!apply_morphology(input, srcBounds, type,
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000592 SkISize::Make(width, height), result)) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000593 return false;
594 }
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000595 offset->fX = bounds.left();
596 offset->fY = bounds.top();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000597 return true;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000598}
599
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000600bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000601 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000602 return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
senorblanco@chromium.org0ded88d2014-01-24 15:43:50 +0000603}
604
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000605bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000606 SkBitmap* result, SkIPoint* offset) const {
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000607 return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000608}
609
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000610#endif