aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-03-02 21:05:45 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-03-02 21:05:45 +0000
commit05054f1a78a697b507580d0025db6c90423e033f (patch)
treefa00f0862980cdc712f21d2d792d59fe070ebcaa
parentc8ccfb0fbadfdcadcc860bc648c5ac42aa9277b1 (diff)
Erode and dilate image filter effects, CPU and GPU implementations.
Review URL: http://codereview.appspot.com/5656067/ git-svn-id: http://skia.googlecode.com/svn/trunk@3310 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/morphology.cpp98
-rw-r--r--gyp/effects.gyp2
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkImageFilter.h16
-rw-r--r--include/effects/SkMorphologyImageFilter.h65
-rw-r--r--include/gpu/GrContext.h37
-rw-r--r--include/gpu/GrSamplerState.h71
-rw-r--r--src/core/SkPaint.cpp8
-rw-r--r--src/effects/SkMorphologyImageFilter.cpp221
-rw-r--r--src/gpu/GrContext.cpp53
-rw-r--r--src/gpu/SkGpuDevice.cpp66
-rw-r--r--src/gpu/gl/GrGLProgram.cpp78
-rw-r--r--src/gpu/gl/GrGLProgram.h2
-rw-r--r--src/gpu/gl/GrGpuGL.cpp4
-rw-r--r--src/gpu/gl/GrGpuGLShaders.cpp31
15 files changed, 678 insertions, 75 deletions
diff --git a/gm/morphology.cpp b/gm/morphology.cpp
new file mode 100644
index 0000000000..bfaa406336
--- /dev/null
+++ b/gm/morphology.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkMorphologyImageFilter.h"
+
+#define WIDTH 640
+#define HEIGHT 480
+
+namespace skiagm {
+
+class MorphologyGM : public GM {
+public:
+ MorphologyGM() {
+ this->setBGColor(0xFF000000);
+ fOnce = false;
+ }
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("morphology");
+ }
+
+ void make_bitmap() {
+ fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 135, 135);
+ fBitmap.allocPixels();
+ SkDevice device(fBitmap);
+ SkCanvas canvas(&device);
+ canvas.clear(0x0);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ const char* str1 = "ABC";
+ const char* str2 = "XYZ";
+ paint.setColor(0xFFFFFFFF);
+ paint.setTextSize(64);
+ canvas.drawText(str1, strlen(str1), 10, 55, paint);
+ canvas.drawText(str2, strlen(str2), 10, 110, paint);
+ }
+
+ virtual SkISize onISize() {
+ return make_isize(WIDTH, HEIGHT);
+ }
+ virtual void onDraw(SkCanvas* canvas) {
+ if (!fOnce) {
+ make_bitmap();
+ fOnce = true;
+ }
+ struct {
+ int fRadiusX, fRadiusY;
+ bool erode;
+ SkScalar fX, fY;
+ } samples[] = {
+ { 0, 0, false, 0, 0 },
+ { 0, 2, false, 140, 0 },
+ { 2, 0, false, 280, 0 },
+ { 2, 2, false, 420, 0 },
+ { 0, 0, true, 0, 140 },
+ { 0, 2, true, 140, 140 },
+ { 2, 0, true, 280, 140 },
+ { 2, 2, true, 420, 140 },
+ };
+ const char* str = "The quick brown fox jumped over the lazy dog.";
+ SkPaint paint;
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(samples); ++i) {
+ if (samples[i].erode) {
+ paint.setImageFilter(new SkErodeImageFilter(
+ samples[i].fRadiusX,
+ samples[i].fRadiusY))->unref();
+ } else {
+ paint.setImageFilter(new SkDilateImageFilter(
+ samples[i].fRadiusX,
+ samples[i].fRadiusY))->unref();
+ }
+ SkRect bounds = SkRect::MakeXYWH(samples[i].fX,
+ samples[i].fY,
+ 140, 140);
+ canvas->saveLayer(&bounds, &paint);
+ canvas->drawBitmap(fBitmap, samples[i].fX, samples[i].fY);
+ canvas->restore();
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+ SkBitmap fBitmap;
+ bool fOnce;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new MorphologyGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gyp/effects.gyp b/gyp/effects.gyp
index 28f0017a2a..ac56510a11 100644
--- a/gyp/effects.gyp
+++ b/gyp/effects.gyp
@@ -32,6 +32,7 @@
'../include/effects/SkKernel33MaskFilter.h',
'../include/effects/SkLayerDrawLooper.h',
'../include/effects/SkLayerRasterizer.h',
+ '../include/effects/SkMorphologyImageFilter.h',
'../include/effects/SkPaintFlagsDrawFilter.h',
'../include/effects/SkPixelXorXfermode.h',
'../include/effects/SkPorterDuff.h',
@@ -66,6 +67,7 @@
'../src/effects/SkKernel33MaskFilter.cpp',
'../src/effects/SkLayerDrawLooper.cpp',
'../src/effects/SkLayerRasterizer.cpp',
+ '../src/effects/SkMorphologyImageFilter.cpp',
'../src/effects/SkPaintFlagsDrawFilter.cpp',
'../src/effects/SkPixelXorXfermode.cpp',
'../src/effects/SkPorterDuff.cpp',
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 43bf87a2ca..2c96e45ebc 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -26,6 +26,7 @@
'../gm/imageblur.cpp',
'../gm/lcdtext.cpp',
'../gm/linepaths.cpp',
+ '../gm/morphology.cpp',
'../gm/ninepatchstretch.cpp',
'../gm/nocolorbleed.cpp',
'../gm/patheffects.cpp',
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 22b9569156..7d7c1404f1 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -80,6 +80,22 @@ public:
*/
virtual bool asABlur(SkSize* sigma) const;
+ /**
+ * Experimental.
+ *
+ * If the filter can be expressed as an erode, return true and
+ * set the radius in X and Y.
+ */
+ virtual bool asAnErode(SkISize* radius) const;
+
+ /**
+ * Experimental.
+ *
+ * If the filter can be expressed as a dilation, return true and
+ * set the radius in X and Y.
+ */
+ virtual bool asADilate(SkISize* radius) const;
+
protected:
SkImageFilter() {}
explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
new file mode 100644
index 0000000000..2297938cef
--- /dev/null
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkMorphologyImageFilter_DEFINED
+#define SkMorphologyImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SK_API SkMorphologyImageFilter : public SkImageFilter {
+public:
+ explicit SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer);
+ SkMorphologyImageFilter(int radiusX, int radiusY);
+
+protected:
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
+ SkISize radius() const { return fRadius; }
+
+private:
+ SkISize fRadius;
+ typedef SkImageFilter INHERITED;
+};
+
+class SK_API SkDilateImageFilter : public SkMorphologyImageFilter {
+public:
+ SkDilateImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
+ explicit SkDilateImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+ virtual bool asADilate(SkISize* radius) const SK_OVERRIDE;
+ virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkDilateImageFilter, (buffer));
+ }
+ virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+ SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+ typedef SkMorphologyImageFilter INHERITED;
+};
+
+class SK_API SkErodeImageFilter : public SkMorphologyImageFilter {
+public:
+ SkErodeImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
+ explicit SkErodeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+ virtual bool asAnErode(SkISize* radius) const SK_OVERRIDE;
+ virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkErodeImageFilter, (buffer));
+ }
+ virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+ SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+private:
+ typedef SkMorphologyImageFilter INHERITED;
+};
+
+#endif
+
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index b939c9e37a..ffb5065925 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -577,30 +577,33 @@ public:
void resolveRenderTarget(GrRenderTarget* target);
/**
- * Applies a 1D convolution kernel in the X direction to a rectangle of
+ * Applies a 1D convolution kernel in the given direction to a rectangle of
* pixels from a given texture.
* @param texture the texture to read from
* @param rect the destination rectangle
* @param kernel the convolution kernel (kernelWidth elements)
* @param kernelWidth the width of the convolution kernel
+ * @param direction the direction in which to apply the kernel
*/
- void convolveInX(GrTexture* texture,
- const SkRect& rect,
- const float* kernel,
- int kernelWidth);
+ void convolve(GrTexture* texture,
+ const SkRect& rect,
+ const float* kernel,
+ int kernelWidth,
+ GrSamplerState::FilterDirection direction);
/**
- * Applies a 1D convolution kernel in the Y direction to a rectangle of
+ * Applies a 1D morphology in the given direction to a rectangle of
* pixels from a given texture.
- * direction.
* @param texture the texture to read from
* @param rect the destination rectangle
- * @param kernel the convolution kernel (kernelWidth elements)
- * @param kernelWidth the width of the convolution kernel
- */
- void convolveInY(GrTexture* texture,
- const SkRect& rect,
- const float* kernel,
- int kernelWidth);
+ * @param radius the radius of the morphological operator
+ * @param filter the filter kernel (must be kDilate or kErode)
+ * @param direction the direction in which to apply the morphology
+ */
+ void applyMorphology(GrTexture* texture,
+ const SkRect& rect,
+ int radius,
+ GrSamplerState::Filter filter,
+ GrSamplerState::FilterDirection direction);
///////////////////////////////////////////////////////////////////////////
// Helpers
@@ -699,12 +702,6 @@ private:
GrPathFill fill,
bool antiAlias);
- void convolve(GrTexture* texture,
- const SkRect& rect,
- float imageIncrement[2],
- const float* kernel,
- int kernelWidth);
-
/**
* Flags to the internal read/write pixels funcs
*/
diff --git a/include/gpu/GrSamplerState.h b/include/gpu/GrSamplerState.h
index 81dfdb3969..50a6cc9ec7 100644
--- a/include/gpu/GrSamplerState.h
+++ b/include/gpu/GrSamplerState.h
@@ -39,6 +39,14 @@ public:
* Apply a separable convolution kernel.
*/
kConvolution_Filter,
+ /**
+ * Apply a dilate filter (max over a 1D radius).
+ */
+ kDilate_Filter,
+ /**
+ * Apply an erode filter (min over a 1D radius).
+ */
+ kErode_Filter,
kDefault_Filter = kNearest_Filter
};
@@ -87,6 +95,17 @@ public:
};
/**
+ * For the filters which perform more than one texture sample (convolution,
+ * erode, dilate), this determines the direction in which the texture
+ * coordinates will be incremented.
+ */
+ enum FilterDirection {
+ kX_FilterDirection,
+ kY_FilterDirection,
+
+ kDefault_FilterDirection = kX_FilterDirection,
+ };
+ /**
* Default sampler state is set to clamp, use normal sampling mode, be
* unfiltered, and use identity matrix.
*/
@@ -99,6 +118,7 @@ public:
WrapMode getWrapX() const { return fWrapX; }
WrapMode getWrapY() const { return fWrapY; }
+ FilterDirection getFilterDirection() const { return fFilterDirection; }
SampleMode getSampleMode() const { return fSampleMode; }
const GrMatrix& getMatrix() const { return fMatrix; }
const GrRect& getTextureDomain() const { return fTextureDomain; }
@@ -106,7 +126,6 @@ public:
Filter getFilter() const { return fFilter; }
int getKernelWidth() const { return fKernelWidth; }
const float* getKernel() const { return fKernel; }
- const float* getImageIncrement() const { return fImageIncrement; }
bool swapsRAndB() const { return fSwapRAndB; }
bool isGradient() const {
@@ -118,6 +137,7 @@ public:
void setWrapX(WrapMode mode) { fWrapX = mode; }
void setWrapY(WrapMode mode) { fWrapY = mode; }
void setSampleMode(SampleMode mode) { fSampleMode = mode; }
+ void setFilterDirection(FilterDirection mode) { fFilterDirection = mode; }
/**
* Access the sampler's matrix. See SampleMode for explanation of
@@ -158,24 +178,29 @@ public:
void reset(WrapMode wrapXAndY,
Filter filter,
+ FilterDirection direction,
const GrMatrix& matrix) {
fWrapX = wrapXAndY;
fWrapY = wrapXAndY;
fSampleMode = kDefault_SampleMode;
fFilter = filter;
+ fFilterDirection = direction;
fMatrix = matrix;
fTextureDomain.setEmpty();
fSwapRAndB = false;
}
+ void reset(WrapMode wrapXAndY, Filter filter, const GrMatrix& matrix) {
+ this->reset(wrapXAndY, filter, kDefault_FilterDirection, matrix);
+ }
void reset(WrapMode wrapXAndY,
Filter filter) {
- this->reset(wrapXAndY, filter, GrMatrix::I());
+ this->reset(wrapXAndY, filter, kDefault_FilterDirection, GrMatrix::I());
}
void reset(const GrMatrix& matrix) {
- this->reset(kDefault_WrapMode, kDefault_Filter, matrix);
+ this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, matrix);
}
void reset() {
- this->reset(kDefault_WrapMode, kDefault_Filter, GrMatrix::I());
+ this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, GrMatrix::I());
}
GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
@@ -198,37 +223,37 @@ public:
fRadial2PosRoot = posRoot;
}
- void setConvolutionParams(int kernelWidth, const float* kernel, float imageIncrement[2]) {
+ void setConvolutionParams(int kernelWidth, const float* kernel) {
GrAssert(kernelWidth >= 0 && kernelWidth <= MAX_KERNEL_WIDTH);
fKernelWidth = kernelWidth;
if (NULL != kernel) {
memcpy(fKernel, kernel, kernelWidth * sizeof(float));
}
- if (NULL != imageIncrement) {
- memcpy(fImageIncrement, imageIncrement, sizeof(fImageIncrement));
- } else {
- memset(fImageIncrement, 0, sizeof(fImageIncrement));
- }
+ }
+
+ void setMorphologyRadius(int radius) {
+ GrAssert(radius >= 0 && radius <= MAX_KERNEL_WIDTH);
+ fKernelWidth = radius;
}
private:
- WrapMode fWrapX : 8;
- WrapMode fWrapY : 8;
- SampleMode fSampleMode : 8;
- Filter fFilter : 8;
- GrMatrix fMatrix;
- bool fSwapRAndB;
- GrRect fTextureDomain;
+ WrapMode fWrapX : 8;
+ WrapMode fWrapY : 8;
+ FilterDirection fFilterDirection : 8;
+ SampleMode fSampleMode : 8;
+ Filter fFilter : 8;
+ GrMatrix fMatrix;
+ bool fSwapRAndB;
+ GrRect fTextureDomain;
// these are undefined unless fSampleMode == kRadial2_SampleMode
- GrScalar fRadial2CenterX1;
- GrScalar fRadial2Radius0;
- SkBool8 fRadial2PosRoot;
+ GrScalar fRadial2CenterX1;
+ GrScalar fRadial2Radius0;
+ SkBool8 fRadial2PosRoot;
// These are undefined unless fFilter == kConvolution_Filter
- uint8_t fKernelWidth;
- float fImageIncrement[2];
- float fKernel[MAX_KERNEL_WIDTH];
+ uint8_t fKernelWidth;
+ float fKernel[MAX_KERNEL_WIDTH];
};
#endif
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 47743bd239..4f5128f7a2 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -2140,6 +2140,14 @@ bool SkImageFilter::asABlur(SkSize* sigma) const {
return false;
}
+bool SkImageFilter::asAnErode(SkISize* radius) const {
+ return false;
+}
+
+bool SkImageFilter::asADilate(SkISize* radius) const {
+ return false;
+}
+
//////
bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
new file mode 100644
index 0000000000..78fabc5616
--- /dev/null
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMorphologyImageFilter.h"
+#include "SkColorPriv.h"
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {
+ fRadius.fWidth = buffer.readScalar();
+ fRadius.fHeight = buffer.readScalar();
+}
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY)
+ : fRadius(SkISize::Make(radiusX, radiusY)) {
+}
+
+
+void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fRadius.fWidth);
+ buffer.writeScalar(fRadius.fHeight);
+}
+
+static void erode(const SkPMColor* src, SkPMColor* dst,
+ int radius, int width, int height,
+ int srcStrideX, int srcStrideY,
+ int dstStrideX, int dstStrideY)
+{
+ const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+ for (int x = 0; x < width; ++x) {
+ const SkPMColor* lp = src;
+ const SkPMColor* up = upperSrc;
+ SkPMColor* dptr = dst;
+ for (int y = 0; y < height; ++y) {
+ int minB = 255, minG = 255, minR = 255, minA = 255;
+ for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
+ int b = SkGetPackedB32(*p);
+ int g = SkGetPackedG32(*p);
+ int r = SkGetPackedR32(*p);
+ int a = SkGetPackedA32(*p);
+ if (b < minB) minB = b;
+ if (g < minG) minG = g;
+ if (r < minR) minR = r;
+ if (a < minA) minA = a;
+ }
+ *dptr = SkPackARGB32(minA, minR, minG, minB);
+ dptr += dstStrideY;
+ lp += srcStrideY;
+ up += srcStrideY;
+ }
+ if (x >= radius) src += srcStrideX;
+ if (x + radius < width - 1) upperSrc += srcStrideX;
+ dst += dstStrideX;
+ }
+}
+
+static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX)
+{
+ erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+ radiusX, src.width(), src.height(),
+ 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
+}
+
+static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY)
+{
+ erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+ radiusY, src.height(), src.width(),
+ src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
+}
+
+static void dilate(const SkPMColor* src, SkPMColor* dst,
+ int radius, int width, int height,
+ int srcStrideX, int srcStrideY,
+ int dstStrideX, int dstStrideY)
+{
+ const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+ for (int x = 0; x < width; ++x) {
+ const SkPMColor* lp = src;
+ const SkPMColor* up = upperSrc;
+ SkPMColor* dptr = dst;
+ for (int y = 0; y < height; ++y) {
+ int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
+ for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
+ int b = SkGetPackedB32(*p);
+ int g = SkGetPackedG32(*p);
+ int r = SkGetPackedR32(*p);
+ int a = SkGetPackedA32(*p);
+ if (b > maxB) maxB = b;
+ if (g > maxG) maxG = g;
+ if (r > maxR) maxR = r;
+ if (a > maxA) maxA = a;
+ }
+ *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
+ dptr += dstStrideY;
+ lp += srcStrideY;
+ up += srcStrideY;
+ }
+ if (x >= radius) src += srcStrideX;
+ if (x + radius < width - 1) upperSrc += srcStrideX;
+ dst += dstStrideX;
+ }
+}
+
+static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX)
+{
+ dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+ radiusX, src.width(), src.height(),
+ 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
+}
+
+static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY)
+{
+ dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+ radiusY, src.height(), src.width(),
+ src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
+}
+
+bool SkErodeImageFilter::onFilterImage(Proxy*,
+ const SkBitmap& src, const SkMatrix&,
+ SkBitmap* dst, SkIPoint*) {
+ if (src.config() != SkBitmap::kARGB_8888_Config) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(src);
+ if (!src.getPixels()) {
+ return false;
+ }
+
+ dst->setConfig(src.config(), src.width(), src.height());
+ dst->allocPixels();
+
+ int width = radius().width();
+ int height = radius().height();
+
+ if (width < 0 || height < 0) {
+ return false;
+ }
+
+ if (width == 0 && height == 0) {
+ src.copyTo(dst, dst->config());
+ return true;
+ }
+
+ SkBitmap temp;
+ temp.setConfig(dst->config(), dst->width(), dst->height());
+ if (!temp.allocPixels()) {
+ return false;
+ }
+
+ if (width > 0 && height > 0) {
+ erodeX(src, &temp, width);
+ erodeY(temp, dst, height);
+ } else if (width > 0) {
+ erodeX(src, dst, width);
+ } else if (height > 0) {
+ erodeY(src, dst, height);
+ }
+ return true;
+}
+
+bool SkDilateImageFilter::onFilterImage(Proxy*,
+ const SkBitmap& src, const SkMatrix&,
+ SkBitmap* dst, SkIPoint*) {
+ if (src.config() != SkBitmap::kARGB_8888_Config) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(src);
+ if (!src.getPixels()) {
+ return false;
+ }
+
+ dst->setConfig(src.config(), src.width(), src.height());
+ dst->allocPixels();
+
+ int width = radius().width();
+ int height = radius().height();
+
+ if (width < 0 || height < 0) {
+ return false;
+ }
+
+ if (width == 0 && height == 0) {
+ src.copyTo(dst, dst->config());
+ return true;
+ }
+
+ SkBitmap temp;
+ temp.setConfig(dst->config(), dst->width(), dst->height());
+ if (!temp.allocPixels()) {
+ return false;
+ }
+
+ if (width > 0 && height > 0) {
+ dilateX(src, &temp, width);
+ dilateY(temp, dst, height);
+ } else if (width > 0) {
+ dilateX(src, dst, width);
+ } else if (height > 0) {
+ dilateY(src, dst, height);
+ }
+ return true;
+}
+
+bool SkDilateImageFilter::asADilate(SkISize* radius) const {
+ *radius = this->radius();
+ return true;
+}
+
+bool SkErodeImageFilter::asAnErode(SkISize* radius) const {
+ *radius = this->radius();
+ return true;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkDilateImageFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkErodeImageFilter)
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 2de978f757..d6ebada82a 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -2007,31 +2007,11 @@ const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
return fGpu->getQuadIndexBuffer();
}
-void GrContext::convolveInX(GrTexture* texture,
- const SkRect& rect,
- const float* kernel,
- int kernelWidth) {
- ASSERT_OWNED_RESOURCE(texture);
-
- float imageIncrement[2] = {1.0f / texture->width(), 0.0f};
- convolve(texture, rect, imageIncrement, kernel, kernelWidth);
-}
-
-void GrContext::convolveInY(GrTexture* texture,
- const SkRect& rect,
- const float* kernel,
- int kernelWidth) {
- ASSERT_OWNED_RESOURCE(texture);
-
- float imageIncrement[2] = {0.0f, 1.0f / texture->height()};
- convolve(texture, rect, imageIncrement, kernel, kernelWidth);
-}
-
void GrContext::convolve(GrTexture* texture,
const SkRect& rect,
- float imageIncrement[2],
const float* kernel,
- int kernelWidth) {
+ int kernelWidth,
+ GrSamplerState::FilterDirection direction) {
ASSERT_OWNED_RESOURCE(texture);
GrDrawTarget::AutoStateRestore asr(fGpu);
@@ -2044,10 +2024,33 @@ void GrContext::convolve(GrTexture* texture,
drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
GrSamplerState::kConvolution_Filter,
sampleM);
- drawState->sampler(0)->setConvolutionParams(kernelWidth,
- kernel,
- imageIncrement);
+ drawState->sampler(0)->setConvolutionParams(kernelWidth, kernel);
+ drawState->sampler(0)->setFilterDirection(direction);
+ drawState->setTexture(0, texture);
+ fGpu->drawSimpleRect(rect, NULL, 1 << 0);
+}
+void GrContext::applyMorphology(GrTexture* texture,
+ const SkRect& rect,
+ int radius,
+ GrSamplerState::Filter filter,
+ GrSamplerState::FilterDirection direction) {
+ ASSERT_OWNED_RESOURCE(texture);
+ GrAssert(filter == GrSamplerState::kErode_Filter ||
+ filter == GrSamplerState::kDilate_Filter);
+
+ GrDrawTarget::AutoStateRestore asr(fGpu);
+ GrDrawState* drawState = fGpu->drawState();
+ GrRenderTarget* target = drawState->getRenderTarget();
+ drawState->reset();
+ drawState->setRenderTarget(target);
+ GrMatrix sampleM;
+ sampleM.setIDiv(texture->width(), texture->height());
+ drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
+ filter,
+ sampleM);
+ drawState->sampler(0)->setMorphologyRadius(radius);
+ drawState->sampler(0)->setFilterDirection(direction);
drawState->setTexture(0, texture);
fGpu->drawSimpleRect(rect, NULL, 1 << 0);
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 94ac5d2cba..af59699654 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -698,6 +698,36 @@ static GrPathFill skToGrFillType(SkPath::FillType fillType) {
}
}
+static GrTexture* applyMorphology(GrContext* context, GrTexture* texture,
+ const GrRect& srcRect,
+ GrTexture* temp1, GrTexture* temp2,
+ GrSamplerState::Filter filter,
+ SkISize radius) {
+ GrRenderTarget* oldRenderTarget = context->getRenderTarget();
+ GrAutoMatrix avm(context, GrMatrix::I());
+ GrClip oldClip = context->getClip();
+ context->setClip(GrRect::MakeWH(texture->width(), texture->height()));
+ if (radius.fWidth > 0) {
+ context->setRenderTarget(temp1->asRenderTarget());
+ context->applyMorphology(texture, srcRect, radius.fWidth, filter,
+ GrSamplerState::kX_FilterDirection);
+ SkIRect clearRect = SkIRect::MakeXYWH(
+ srcRect.fLeft, srcRect.fBottom,
+ srcRect.width(), radius.fHeight);
+ context->clear(&clearRect, 0x0);
+ texture = temp1;
+ }
+ if (radius.fHeight > 0) {
+ context->setRenderTarget(temp2->asRenderTarget());
+ context->applyMorphology(texture, srcRect, radius.fHeight, filter,
+ GrSamplerState::kY_FilterDirection);
+ texture = temp2;
+ }
+ context->setRenderTarget(oldRenderTarget);
+ context->setClip(oldClip);
+ return texture;
+}
+
static void buildKernel(float sigma, float* kernel, int kernelWidth) {
int halfWidth = (kernelWidth - 1) / 2;
float sum = 0.0f;
@@ -808,7 +838,8 @@ static GrTexture* gaussianBlur(GrContext* context, GrTexture* srcTexture,
}
context->setRenderTarget(dstTexture->asRenderTarget());
- context->convolveInX(srcTexture, srcRect, kernelX, kernelWidthX);
+ context->convolve(srcTexture, srcRect, kernelX, kernelWidthX,
+ GrSamplerState::kX_FilterDirection);
SkTSwap(srcTexture, dstTexture);
if (temp2 && dstTexture == origTexture) dstTexture = temp2->texture();
}
@@ -827,7 +858,8 @@ static GrTexture* gaussianBlur(GrContext* context, GrTexture* srcTexture,
}
context->setRenderTarget(dstTexture->asRenderTarget());
- context->convolveInY(srcTexture, srcRect, kernelY, kernelWidthY);
+ context->convolve(srcTexture, srcRect, kernelY, kernelWidthY,
+ GrSamplerState::kY_FilterDirection);
SkTSwap(srcTexture, dstTexture);
if (temp2 && dstTexture == origTexture) dstTexture = temp2->texture();
}
@@ -1481,6 +1513,7 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
SkImageFilter* imageFilter = paint.getImageFilter();
SkSize blurSize;
+ SkISize radius;
if (NULL != imageFilter && imageFilter->asABlur(&blurSize)) {
GrAutoScratchTexture temp1, temp2;
GrTexture* blurTexture = gaussianBlur(fContext,
@@ -1490,6 +1523,32 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
blurSize.height());
texture = blurTexture;
grPaint.setTexture(kBitmapTextureIdx, texture);
+ } else if (NULL != imageFilter && imageFilter->asADilate(&radius)) {
+ const GrTextureDesc desc = {
+ kRenderTarget_GrTextureFlagBit,
+ w,
+ h,
+ kRGBA_8888_PM_GrPixelConfig,
+ {0} // samples
+ };
+ GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
+ texture = applyMorphology(fContext, texture, GrRect::MakeWH(w, h),
+ temp1.texture(), temp2.texture(),
+ GrSamplerState::kDilate_Filter, radius);
+ grPaint.setTexture(kBitmapTextureIdx, texture);
+ } else if (NULL != imageFilter && imageFilter->asAnErode(&radius)) {
+ const GrTextureDesc desc = {
+ kRenderTarget_GrTextureFlagBit,
+ w,
+ h,
+ kRGBA_8888_PM_GrPixelConfig,
+ {0} // samples
+ };
+ GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
+ texture = applyMorphology(fContext, texture, GrRect::MakeWH(w, h),
+ temp1.texture(), temp2.texture(),
+ GrSamplerState::kErode_Filter, radius);
+ grPaint.setTexture(kBitmapTextureIdx, texture);
} else {
grPaint.setTexture(kBitmapTextureIdx, texture);
}
@@ -1541,7 +1600,8 @@ bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
const SkMatrix& ctm,
SkBitmap* result, SkIPoint* offset) {
SkSize size;
- if (!filter->asABlur(&size)) {
+ SkISize radius;
+ if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) {
return false;
}
SkDevice* dev = this->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 7eecf914c0..29252131a5 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -166,6 +166,11 @@ inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder*
i->appendS32(stage);
}
+inline void image_increment_param_name(int stage, GrStringBuilder* i) {
+ *i = "uImageIncrement";
+ i->appendS32(stage);
+}
+
inline void tex_domain_name(int stage, GrStringBuilder* s) {
*s = "uTexDom";
s->appendS32(stage);
@@ -1655,6 +1660,68 @@ void genConvolutionFS(int stageNum,
segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
sumVar.c_str(), modulate.c_str());
}
+
+void genMorphologyVS(int stageNum,
+ const StageDesc& desc,
+ ShaderCodeSegments* segments,
+ GrGLProgram::StageUniLocations* locations,
+ const char** imageIncrementName,
+ const char* varyingVSName) {
+ GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
+ imgInc->setType(GrGLShaderVar::kVec2f_Type);
+ imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+
+ image_increment_param_name(stageNum, imgInc->accessName());
+ *imageIncrementName = imgInc->getName().c_str();
+
+ // need image increment in both VS and FS
+ segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
+
+ locations->fImageIncrementUni = kUseUniform;
+ segments->fVSCode.appendf("\t%s -= vec2(%d, %d) * %s;\n",
+ varyingVSName, desc.fKernelWidth,
+ desc.fKernelWidth, *imageIncrementName);
+}
+
+void genMorphologyFS(int stageNum,
+ const StageDesc& desc,
+ ShaderCodeSegments* segments,
+ const char* samplerName,
+ const char* swizzle,
+ const char* imageIncrementName,
+ const char* fsOutColor,
+ GrStringBuilder& sampleCoords,
+ GrStringBuilder& texFunc,
+ GrStringBuilder& modulate) {
+ GrStringBuilder valueVar("value");
+ valueVar.appendS32(stageNum);
+ GrStringBuilder coordVar("coord");
+ coordVar.appendS32(stageNum);
+ bool isDilate = StageDesc::kDilate_FetchMode == desc.fFetchMode;
+
+ if (isDilate) {
+ segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
+ valueVar.c_str());
+ } else {
+ segments->fFSCode.appendf("\tvec4 %s = vec4(1, 1, 1, 1);\n",
+ valueVar.c_str());
+ }
+ segments->fFSCode.appendf("\tvec2 %s = %s;\n",
+ coordVar.c_str(),
+ sampleCoords.c_str());
+ segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
+ desc.fKernelWidth * 2 + 1);
+ segments->fFSCode.appendf("\t\t%s = %s(%s, %s(%s, %s)%s);\n",
+ valueVar.c_str(), isDilate ? "max" : "min",
+ valueVar.c_str(), texFunc.c_str(),
+ samplerName, coordVar.c_str(), swizzle);
+ segments->fFSCode.appendf("\t\t%s += %s;\n",
+ coordVar.c_str(),
+ imageIncrementName);
+ segments->fFSCode.appendf("\t}\n");
+ segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
+ valueVar.c_str(), modulate.c_str());
+}
}
@@ -1755,6 +1822,10 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
if (StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
genConvolutionVS(stageNum, desc, segments, locations,
&kernel, &imageIncrementName, varyingVSName);
+ } else if (StageDesc::kDilate_FetchMode == desc.fFetchMode ||
+ StageDesc::kErode_FetchMode == desc.fFetchMode) {
+ genMorphologyVS(stageNum, desc, segments, locations,
+ &imageIncrementName, varyingVSName);
}
/// Fragment Shader Stuff
@@ -1866,6 +1937,13 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
samplerName, kernel, swizzle, imageIncrementName, fsOutColor,
sampleCoords, texFunc, modulate);
break;
+ case StageDesc::kDilate_FetchMode:
+ case StageDesc::kErode_FetchMode:
+ GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
+ genMorphologyFS(stageNum, desc, segments,
+ samplerName, swizzle, imageIncrementName, fsOutColor,
+ sampleCoords, texFunc, modulate);
+ break;
default:
if (desc.fInConfigFlags & kMulByAlphaMask) {
// only one of the mul by alpha flags should be set
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index e9030bc793..76f9c9006f 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -110,6 +110,8 @@ public:
kSingle_FetchMode,
k2x2_FetchMode,
kConvolution_FetchMode,
+ kErode_FetchMode,
+ kDilate_FetchMode,
kFetchModeCnt,
};
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index bee2017272..69880e5555 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -445,7 +445,7 @@ void GrGpuGL::onResetContext() {
-GR_ScalarMax,
true);
*fHWDrawState.sampler(s)->matrix() = GrMatrix::InvalidMatrix();
- fHWDrawState.sampler(s)->setConvolutionParams(0, NULL, NULL);
+ fHWDrawState.sampler(s)->setConvolutionParams(0, NULL);
}
fHWBounds.fScissorRect.invalidate();
@@ -1935,6 +1935,8 @@ unsigned gr_to_gl_filter(GrSamplerState::Filter filter) {
return GR_GL_LINEAR;
case GrSamplerState::kNearest_Filter:
case GrSamplerState::kConvolution_Filter:
+ case GrSamplerState::kErode_Filter:
+ case GrSamplerState::kDilate_Filter:
return GR_GL_NEAREST;
default:
GrAssert(!"Unknown filter type");
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
index a0a2df5802..db7e3a7f76 100644
--- a/src/gpu/gl/GrGpuGLShaders.cpp
+++ b/src/gpu/gl/GrGpuGLShaders.cpp
@@ -261,7 +261,9 @@ bool GrGpuGLShaders::programUnitTest() {
stage.fCoordMapping = random_int(&random, StageDesc::kCoordMappingCnt);
stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
// convolution shaders don't work with persp tex matrix
- if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
+ if (stage.fFetchMode == StageDesc::kConvolution_FetchMode ||
+ stage.fFetchMode == StageDesc::kDilate_FetchMode ||
+ stage.fFetchMode == StageDesc::kErode_FetchMode) {
stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
}
stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
@@ -273,6 +275,8 @@ bool GrGpuGLShaders::programUnitTest() {
stage.fKernelWidth = 0;
break;
case StageDesc::kConvolution_FetchMode:
+ case StageDesc::kDilate_FetchMode:
+ case StageDesc::kErode_FetchMode:
stage.fKernelWidth = random_int(&random, 2, 8);
stage.fInConfigFlags &= ~kMulByAlphaMask;
break;
@@ -560,7 +564,20 @@ void GrGpuGLShaders::flushConvolution(int s) {
}
int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
- GL_CALL(Uniform2fv(imageIncrementUni, 1, sampler.getImageIncrement()));
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
+ float imageIncrement[2] = { 0 };
+ switch (sampler.getFilterDirection()) {
+ case GrSamplerState::kX_FilterDirection:
+ imageIncrement[0] = 1.0f / texture->width();
+ break;
+ case GrSamplerState::kY_FilterDirection:
+ imageIncrement[1] = 1.0f / texture->height();
+ break;
+ default:
+ GrCrash("Unknown filter direction.");
+ }
+ GL_CALL(Uniform2fv(imageIncrementUni, 1, imageIncrement));
}
}
@@ -1081,6 +1098,12 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
case GrSamplerState::kConvolution_Filter:
stage.fFetchMode = StageDesc::kConvolution_FetchMode;
break;
+ case GrSamplerState::kDilate_Filter:
+ stage.fFetchMode = StageDesc::kDilate_FetchMode;
+ break;
+ case GrSamplerState::kErode_Filter:
+ stage.fFetchMode = StageDesc::kErode_FetchMode;
+ break;
default:
GrCrash("Unexpected filter!");
break;
@@ -1119,7 +1142,9 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
}
}
- if (sampler.getFilter() == GrSamplerState::kConvolution_Filter) {
+ if (sampler.getFilter() == GrSamplerState::kConvolution_Filter ||
+ sampler.getFilter() == GrSamplerState::kDilate_Filter ||
+ sampler.getFilter() == GrSamplerState::kErode_Filter) {
stage.fKernelWidth = sampler.getKernelWidth();
} else {
stage.fKernelWidth = 0;