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
This commit is contained in:
parent
c8ccfb0fba
commit
05054f1a78
98
gm/morphology.cpp
Normal file
98
gm/morphology.cpp
Normal file
@ -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);
|
||||
|
||||
}
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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) {}
|
||||
|
65
include/effects/SkMorphologyImageFilter.h
Normal file
65
include/effects/SkMorphologyImageFilter.h
Normal file
@ -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
|
||||
|
@ -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
|
||||
* @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 convolveInY(GrTexture* texture,
|
||||
const SkRect& rect,
|
||||
const float* kernel,
|
||||
int kernelWidth);
|
||||
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
|
||||
*/
|
||||
|
@ -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
|
||||
};
|
||||
@ -86,6 +94,17 @@ public:
|
||||
kDefault_WrapMode = kClamp_WrapMode
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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) {
|
||||
|
221
src/effects/SkMorphologyImageFilter.cpp
Normal file
221
src/effects/SkMorphologyImageFilter.cpp
Normal file
@ -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)
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -110,6 +110,8 @@ public:
|
||||
kSingle_FetchMode,
|
||||
k2x2_FetchMode,
|
||||
kConvolution_FetchMode,
|
||||
kErode_FetchMode,
|
||||
kDilate_FetchMode,
|
||||
|
||||
kFetchModeCnt,
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user