diff options
-rw-r--r-- | gm/morphology.cpp | 98 | ||||
-rw-r--r-- | gyp/effects.gyp | 2 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/core/SkImageFilter.h | 16 | ||||
-rw-r--r-- | include/effects/SkMorphologyImageFilter.h | 65 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 37 | ||||
-rw-r--r-- | include/gpu/GrSamplerState.h | 71 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 8 | ||||
-rw-r--r-- | src/effects/SkMorphologyImageFilter.cpp | 221 | ||||
-rw-r--r-- | src/gpu/GrContext.cpp | 53 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 66 | ||||
-rw-r--r-- | src/gpu/gl/GrGLProgram.cpp | 78 | ||||
-rw-r--r-- | src/gpu/gl/GrGLProgram.h | 2 | ||||
-rw-r--r-- | src/gpu/gl/GrGpuGL.cpp | 4 | ||||
-rw-r--r-- | src/gpu/gl/GrGpuGLShaders.cpp | 31 |
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; |