blob: 949dc4a0505aea9531df299f718042cac016df0f [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"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
12#include "SkRect.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000013#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000014#include "GrContext.h"
15#include "GrTexture.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +000016#include "GrTBackendEffectFactory.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +000017#include "gl/GrGLEffect.h"
bsalomon@google.comb4a55b72012-11-02 20:45:37 +000018#include "gl/GrGLEffectMatrix.h"
senorblanco@chromium.org84207c42012-08-22 20:51:19 +000019#include "effects/Gr1DKernelEffect.h"
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +000020#include "SkImageFilterUtils.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000021#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000022
23SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
24 : INHERITED(buffer) {
tomhudson@google.com75589252012-04-10 17:42:21 +000025 fRadius.fWidth = buffer.readInt();
26 fRadius.fHeight = buffer.readInt();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000027}
28
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +000029SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const SkIRect* cropRect)
30 : INHERITED(input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000031}
32
33
djsollen@google.com54924242012-03-29 15:18:04 +000034void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& 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
40static void erode(const SkPMColor* src, SkPMColor* dst,
41 int radius, int width, int height,
42 int srcStrideX, int srcStrideY,
43 int dstStrideX, int dstStrideY)
44{
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000045 radius = SkMin32(radius, width - 1);
46 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000047 for (int x = 0; x < width; ++x) {
48 const SkPMColor* lp = src;
49 const SkPMColor* up = upperSrc;
50 SkPMColor* dptr = dst;
51 for (int y = 0; y < height; ++y) {
52 int minB = 255, minG = 255, minR = 255, minA = 255;
53 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
54 int b = SkGetPackedB32(*p);
55 int g = SkGetPackedG32(*p);
56 int r = SkGetPackedR32(*p);
57 int a = SkGetPackedA32(*p);
58 if (b < minB) minB = b;
59 if (g < minG) minG = g;
60 if (r < minR) minR = r;
61 if (a < minA) minA = a;
62 }
63 *dptr = SkPackARGB32(minA, minR, minG, minB);
64 dptr += dstStrideY;
65 lp += srcStrideY;
66 up += srcStrideY;
67 }
68 if (x >= radius) src += srcStrideX;
69 if (x + radius < width - 1) upperSrc += srcStrideX;
70 dst += dstStrideX;
71 }
72}
73
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +000074static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000075{
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +000076 erode(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
77 radiusX, bounds.width(), bounds.height(),
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000078 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
79}
80
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +000081static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000082{
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +000083 erode(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
84 radiusY, bounds.height(), bounds.width(),
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000085 src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
86}
87
88static void dilate(const SkPMColor* src, SkPMColor* dst,
89 int radius, int width, int height,
90 int srcStrideX, int srcStrideY,
91 int dstStrideX, int dstStrideY)
92{
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000093 radius = SkMin32(radius, width - 1);
94 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000095 for (int x = 0; x < width; ++x) {
96 const SkPMColor* lp = src;
97 const SkPMColor* up = upperSrc;
98 SkPMColor* dptr = dst;
99 for (int y = 0; y < height; ++y) {
100 int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
101 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
102 int b = SkGetPackedB32(*p);
103 int g = SkGetPackedG32(*p);
104 int r = SkGetPackedR32(*p);
105 int a = SkGetPackedA32(*p);
106 if (b > maxB) maxB = b;
107 if (g > maxG) maxG = g;
108 if (r > maxR) maxR = r;
109 if (a > maxA) maxA = a;
110 }
111 *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
112 dptr += dstStrideY;
113 lp += srcStrideY;
114 up += srcStrideY;
115 }
116 if (x >= radius) src += srcStrideX;
117 if (x + radius < width - 1) upperSrc += srcStrideX;
118 dst += dstStrideX;
119 }
120}
121
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000122static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000123{
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000124 dilate(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
125 radiusX, bounds.width(), bounds.height(),
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000126 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
127}
128
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000129static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000130{
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000131 dilate(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
132 radiusY, bounds.height(), bounds.width(),
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000133 src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
134}
135
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000136bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
137 const SkBitmap& source, const SkMatrix& ctm,
138 SkBitmap* dst, SkIPoint* offset) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000139 SkBitmap src = source;
140 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
141 return false;
142 }
143
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000144 if (src.config() != SkBitmap::kARGB_8888_Config) {
145 return false;
146 }
147
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000148 SkIRect bounds;
149 src.getBounds(&bounds);
150 if (!this->applyCropRect(&bounds, ctm)) {
151 return false;
152 }
153
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000154 SkAutoLockPixels alp(src);
155 if (!src.getPixels()) {
156 return false;
157 }
158
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000159 dst->setConfig(src.config(), bounds.width(), bounds.height());
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000160 dst->allocPixels();
161
162 int width = radius().width();
163 int height = radius().height();
164
165 if (width < 0 || height < 0) {
166 return false;
167 }
168
169 if (width == 0 && height == 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000170 src.extractSubset(dst, bounds);
171 offset->fX += bounds.left();
172 offset->fY += bounds.top();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000173 return true;
174 }
175
176 SkBitmap temp;
177 temp.setConfig(dst->config(), dst->width(), dst->height());
178 if (!temp.allocPixels()) {
179 return false;
180 }
181
182 if (width > 0 && height > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000183 erodeX(src, &temp, width, bounds);
184 SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
185 erodeY(temp, dst, height, tmpBounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000186 } else if (width > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000187 erodeX(src, dst, width, bounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000188 } else if (height > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000189 erodeY(src, dst, height, bounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000190 }
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000191 offset->fX += bounds.left();
192 offset->fY += bounds.top();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000193 return true;
194}
195
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000196bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
197 const SkBitmap& source, const SkMatrix& ctm,
198 SkBitmap* dst, SkIPoint* offset) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000199 SkBitmap src = source;
200 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
201 return false;
202 }
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000203 if (src.config() != SkBitmap::kARGB_8888_Config) {
204 return false;
205 }
206
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000207 SkIRect bounds;
208 src.getBounds(&bounds);
209 if (!this->applyCropRect(&bounds, ctm)) {
210 return false;
211 }
212
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000213 SkAutoLockPixels alp(src);
214 if (!src.getPixels()) {
215 return false;
216 }
217
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000218 dst->setConfig(src.config(), bounds.width(), bounds.height());
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000219 dst->allocPixels();
220
221 int width = radius().width();
222 int height = radius().height();
223
224 if (width < 0 || height < 0) {
225 return false;
226 }
227
228 if (width == 0 && height == 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000229 src.extractSubset(dst, bounds);
230 offset->fX += bounds.left();
231 offset->fY += bounds.top();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000232 return true;
233 }
234
235 SkBitmap temp;
236 temp.setConfig(dst->config(), dst->width(), dst->height());
237 if (!temp.allocPixels()) {
238 return false;
239 }
240
241 if (width > 0 && height > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000242 dilateX(src, &temp, width, bounds);
243 SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
244 dilateY(temp, dst, height, tmpBounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000245 } else if (width > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000246 dilateX(src, dst, width, bounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000247 } else if (height > 0) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000248 dilateY(src, dst, height, bounds);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000249 }
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000250 offset->fX += bounds.left();
251 offset->fY += bounds.top();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000252 return true;
253}
254
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000255#if SK_SUPPORT_GPU
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000256
257///////////////////////////////////////////////////////////////////////////////
258
259class GrGLMorphologyEffect;
260
261/**
262 * Morphology effects. Depending upon the type of morphology, either the
263 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
264 * kernel is selected as the new color. The new color is modulated by the input
265 * color.
266 */
267class GrMorphologyEffect : public Gr1DKernelEffect {
268
269public:
270
271 enum MorphologyType {
272 kErode_MorphologyType,
273 kDilate_MorphologyType,
274 };
275
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000276 static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000277 AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000278 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000279 }
280
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000281 virtual ~GrMorphologyEffect();
282
283 MorphologyType type() const { return fType; }
284
285 static const char* Name() { return "Morphology"; }
286
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000287 typedef GrGLMorphologyEffect GLEffect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000288
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000289 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000290 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000291
292protected:
293
294 MorphologyType fType;
295
296private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000297 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000298
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000299 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
300
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000301 GR_DECLARE_EFFECT_TEST;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000302
303 typedef Gr1DKernelEffect INHERITED;
304};
305
306///////////////////////////////////////////////////////////////////////////////
307
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000308class GrGLMorphologyEffect : public GrGLEffect {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000309public:
bsalomon@google.comc7818882013-03-20 19:19:53 +0000310 GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrDrawEffect&);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000311
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000312 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000313 const GrDrawEffect&,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000314 EffectKey,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000315 const char* outputColor,
316 const char* inputColor,
317 const TextureSamplerArray&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000318
bsalomon@google.comc7818882013-03-20 19:19:53 +0000319 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000320
bsalomon@google.comc7818882013-03-20 19:19:53 +0000321 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000322
323private:
324 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
325
326 int fRadius;
327 GrMorphologyEffect::MorphologyType fType;
328 GrGLUniformManager::UniformHandle fImageIncrementUni;
bsalomon@google.comb4a55b72012-11-02 20:45:37 +0000329 GrGLEffectMatrix fEffectMatrix;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000330
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000331 typedef GrGLEffect INHERITED;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000332};
333
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000334GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000335 const GrDrawEffect& drawEffect)
bsalomon@google.com374e7592012-10-23 17:30:45 +0000336 : INHERITED(factory)
bsalomon@google.comc7818882013-03-20 19:19:53 +0000337 , fEffectMatrix(drawEffect.castEffect<GrMorphologyEffect>().coordsType()) {
338 const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000339 fRadius = m.radius();
340 fType = m.type();
341}
342
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000343void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000344 const GrDrawEffect&,
bsalomon@google.comb4a55b72012-11-02 20:45:37 +0000345 EffectKey key,
bsalomon@google.com47d7a882012-10-29 12:47:51 +0000346 const char* outputColor,
347 const char* inputColor,
348 const TextureSamplerArray& samplers) {
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000349 SkString coords;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000350 fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000351 fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000352 kVec2f_GrSLType, "ImageIncrement");
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000353
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000354 const char* func;
355 switch (fType) {
356 case GrMorphologyEffect::kErode_MorphologyType:
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000357 builder->fsCodeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000358 func = "min";
359 break;
360 case GrMorphologyEffect::kDilate_MorphologyType:
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000361 builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000362 func = "max";
363 break;
364 default:
365 GrCrash("Unexpected type");
366 func = ""; // suppress warning
367 break;
368 }
369 const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
370
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000371 builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords.c_str(), fRadius, imgInc);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000372 builder->fsCodeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
373 builder->fsCodeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000374 builder->fsAppendTextureLookup(samplers[0], "coord");
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000375 builder->fsCodeAppend(");\n");
376 builder->fsCodeAppendf("\t\t\tcoord += %s;\n", imgInc);
377 builder->fsCodeAppend("\t\t}\n");
378 SkString modulate;
379 GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
380 builder->fsCodeAppend(modulate.c_str());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000381}
382
bsalomon@google.comc7818882013-03-20 19:19:53 +0000383GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrDrawEffect& drawEffect,
384 const GrGLCaps&) {
385 const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
bsalomon@google.com46fba0d2012-10-25 21:42:05 +0000386 EffectKey key = static_cast<EffectKey>(m.radius());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000387 key |= (m.type() << 8);
bsalomon@google.comb4a55b72012-11-02 20:45:37 +0000388 key <<= GrGLEffectMatrix::kKeyBits;
389 EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
bsalomon@google.comc7818882013-03-20 19:19:53 +0000390 drawEffect,
391 m.coordsType(),
bsalomon@google.comb4a55b72012-11-02 20:45:37 +0000392 m.texture(0));
393 return key | matrixKey;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000394}
395
bsalomon@google.comc7818882013-03-20 19:19:53 +0000396void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman,
397 const GrDrawEffect& drawEffect) {
398 const Gr1DKernelEffect& kern = drawEffect.castEffect<Gr1DKernelEffect>();
bsalomon@google.com2d0bade2012-10-26 19:01:17 +0000399 GrTexture& texture = *kern.texture(0);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000400 // the code we generated was for a specific kernel radius
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000401 SkASSERT(kern.radius() == fRadius);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000402 float imageIncrement[2] = { 0 };
403 switch (kern.direction()) {
404 case Gr1DKernelEffect::kX_Direction:
405 imageIncrement[0] = 1.0f / texture.width();
406 break;
407 case Gr1DKernelEffect::kY_Direction:
408 imageIncrement[1] = 1.0f / texture.height();
409 break;
410 default:
411 GrCrash("Unknown filter direction.");
412 }
413 uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000414 fEffectMatrix.setData(uman, kern.getMatrix(), drawEffect, kern.texture(0));
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000415}
416
417///////////////////////////////////////////////////////////////////////////////
418
419GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
420 Direction direction,
421 int radius,
422 MorphologyType type)
423 : Gr1DKernelEffect(texture, direction, radius)
424 , fType(type) {
425}
426
427GrMorphologyEffect::~GrMorphologyEffect() {
428}
429
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000430const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const {
431 return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000432}
433
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000434bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000435 const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase);
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000436 return (this->texture(0) == s.texture(0) &&
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000437 this->radius() == s.radius() &&
438 this->direction() == s.direction() &&
439 this->type() == s.type());
440}
441
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000442void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
443 // This is valid because the color components of the result of the kernel all come
444 // exactly from existing values in the source texture.
445 this->updateConstantColorComponentsForModulation(color, validFlags);
446}
447
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000448///////////////////////////////////////////////////////////////////////////////
449
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000450GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000451
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000452GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random,
sugoi@google.come0e385c2013-03-11 18:50:03 +0000453 GrContext*,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000454 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000455 GrTexture* textures[]) {
bsalomon@google.com6f261be2012-10-24 19:07:10 +0000456 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
457 GrEffectUnitTest::kAlphaTextureIdx;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000458 Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
459 static const int kMaxRadius = 10;
460 int radius = random->nextRangeU(1, kMaxRadius);
461 MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
462 GrMorphologyEffect::kDilate_MorphologyType;
463
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000464 return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000465}
466
467namespace {
468
469void apply_morphology_pass(GrContext* context,
470 GrTexture* texture,
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000471 const SkIRect& srcRect,
472 const SkIRect& dstRect,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000473 int radius,
474 GrMorphologyEffect::MorphologyType morphType,
475 Gr1DKernelEffect::Direction direction) {
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000476 GrPaint paint;
commit-bot@chromium.org42dacab2013-07-13 17:24:24 +0000477 paint.addColorEffect(GrMorphologyEffect::Create(texture,
478 direction,
479 radius,
480 morphType))->unref();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000481 context->drawRectToRect(paint, SkRect::MakeFromIRect(dstRect), SkRect::MakeFromIRect(srcRect));
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000482}
483
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000484bool apply_morphology(const SkBitmap& input,
485 const SkIRect& rect,
486 GrMorphologyEffect::MorphologyType morphType,
487 SkISize radius,
488 SkBitmap* dst) {
489 GrTexture* srcTexture = input.getTexture();
490 SkASSERT(NULL != srcTexture);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000491 GrContext* context = srcTexture->getContext();
492 srcTexture->ref();
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000493 SkAutoTUnref<GrTexture> src(srcTexture);
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000494
495 GrContext::AutoMatrix am;
496 am.setIdentity(context);
497
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000498 GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000499 SkIntToScalar(srcTexture->height())));
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000500
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000501 SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000502 GrTextureDesc desc;
503 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000504 desc.fWidth = rect.width();
505 desc.fHeight = rect.height();
506 desc.fConfig = kSkia8888_GrPixelConfig;
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000507 SkIRect srcRect = rect;
bsalomon@google.com3cbaa2d2012-10-12 14:51:52 +0000508
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000509 if (radius.fWidth > 0) {
510 GrAutoScratchTexture ast(context, desc);
511 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000512 apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000513 morphType, Gr1DKernelEffect::kX_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000514 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
515 dstRect.width(), radius.fHeight);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000516 context->clear(&clearRect, 0x0);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000517 src.reset(ast.detach());
518 srcRect = dstRect;
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000519 }
520 if (radius.fHeight > 0) {
521 GrAutoScratchTexture ast(context, desc);
522 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000523 apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight,
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000524 morphType, Gr1DKernelEffect::kY_Direction);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000525 src.reset(ast.detach());
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000526 }
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000527 return SkImageFilterUtils::WrapTexture(src, rect.width(), rect.height(), dst);
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000528}
529
530};
531
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000532bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
533 SkBitmap* result, SkIPoint* offset) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000534 SkBitmap input;
535 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000536 return false;
537 }
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000538 SkIRect bounds;
539 src.getBounds(&bounds);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000540 if (!this->applyCropRect(&bounds, ctm)) {
541 return false;
542 }
543 int width = radius().width();
544 int height = radius().height();
545
546 if (width < 0 || height < 0) {
547 return false;
548 }
549
550 if (width == 0 && height == 0) {
551 src.extractSubset(result, bounds);
552 offset->fX += bounds.left();
553 offset->fY += bounds.top();
554 return true;
555 }
556
557 if (!apply_morphology(input, bounds, GrMorphologyEffect::kDilate_MorphologyType, radius(), result)) {
558 return false;
559 }
560 offset->fX += bounds.left();
561 offset->fY += bounds.top();
562 return true;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000563}
564
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000565bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
566 SkBitmap* result, SkIPoint* offset) {
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000567 SkBitmap input;
568 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000569 return false;
570 }
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000571 SkIRect bounds;
572 src.getBounds(&bounds);
senorblanco@chromium.org8fcad982013-09-17 13:41:43 +0000573 if (!this->applyCropRect(&bounds, ctm)) {
574 return false;
575 }
576 int width = radius().width();
577 int height = radius().height();
578
579 if (width < 0 || height < 0) {
580 return false;
581 }
582
583 if (width == 0 && height == 0) {
584 src.extractSubset(result, bounds);
585 offset->fX += bounds.left();
586 offset->fY += bounds.top();
587 return true;
588 }
589
590 if (!apply_morphology(input, bounds, GrMorphologyEffect::kErode_MorphologyType, radius(), result)) {
591 return false;
592 }
593 offset->fX += bounds.left();
594 offset->fY += bounds.top();
595 return true;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000596}
597
senorblanco@chromium.org84207c42012-08-22 20:51:19 +0000598#endif