This patch implements a crop rect for SkImageFilter. It has been implemented for SkColorFilterImageFilter and SkBlurImageFilter as examples.
In order to preserve the immutability of SkImageFilters, the crop rect is passed as a constructor parameter. If NULL (the default), the bounds of the input image are used, as before.
This also tightens up the boundary handling for SkImageBlurFilter on the GPU backend. Where we were previously using clamping semantics, we now respect decal semantics (so we don't oversaturate the edges). This brings the GPU and raster backends into closer alignment, but will require some new baselines for the GPU tests.
At a minimum, the following tests will need new baselines: imageblur, imagefiltersbase, imagefilterscropped, spritebitmap.
R=reed@google.com
Committed: https://code.google.com/p/skia/source/detail?r=10251
Review URL: https://codereview.chromium.org/19775006
git-svn-id: http://skia.googlecode.com/svn/trunk@10338 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index eaac5ab..4456f51 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1387,7 +1387,7 @@
SkAutoCachedTexture act(this, bitmap, NULL, &texture);
SkImageFilter* filter = paint.getImageFilter();
- SkIPoint offset = SkIPoint::Make(0, 0);
+ SkIPoint offset = SkIPoint::Make(left, top);
// This bitmap will own the filtered result as a texture.
SkBitmap filteredBitmap;
@@ -1396,6 +1396,8 @@
texture = (GrTexture*) filteredBitmap.getTexture();
w = filteredBitmap.width();
h = filteredBitmap.height();
+ } else {
+ return;
}
}
@@ -1407,12 +1409,12 @@
}
fContext->drawRectToRect(grPaint,
- SkRect::MakeXYWH(SkIntToScalar(left),
- SkIntToScalar(top),
- SkIntToScalar(w),
- SkIntToScalar(h)),
SkRect::MakeXYWH(SkIntToScalar(offset.fX),
SkIntToScalar(offset.fY),
+ SkIntToScalar(w),
+ SkIntToScalar(h)),
+ SkRect::MakeXYWH(0,
+ 0,
SK_Scalar1 * w / texture->width(),
SK_Scalar1 * h / texture->height()));
}
@@ -1481,6 +1483,8 @@
h = filteredBitmap.height();
x += offset.fX;
y += offset.fY;
+ } else {
+ return;
}
}
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 380581f..3fa1cb9 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -33,10 +33,13 @@
private:
int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
+ bool useCropRect() const { return fUseCropRect; }
int fRadius;
+ bool fUseCropRect;
UniformHandle fKernelUni;
UniformHandle fImageIncrementUni;
+ UniformHandle fCropRectUni;
GrGLEffectMatrix fEffectMatrix;
typedef GrGLEffect INHERITED;
@@ -47,9 +50,11 @@
: INHERITED(factory)
, fKernelUni(kInvalidUniformHandle)
, fImageIncrementUni(kInvalidUniformHandle)
+ , fCropRectUni(kInvalidUniformHandle)
, fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
fRadius = c.radius();
+ fUseCropRect = c.useCropRect();
}
void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
@@ -62,12 +67,16 @@
fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
kVec2f_GrSLType, "ImageIncrement");
+ if (this->useCropRect()) {
+ fCropRectUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+ kVec4f_GrSLType, "CropRect");
+ }
fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
kFloat_GrSLType, "Kernel", this->width());
builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
- int width = this ->width();
+ int width = this->width();
const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
@@ -81,9 +90,15 @@
kernel.appendArrayAccess(index.c_str(), &kernelIndex);
builder->fsCodeAppendf("\t\t%s += ", outputColor);
builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
+ if (this->useCropRect()) {
+ const char* cropRect = builder->getUniformCStr(fCropRectUni);
+ builder->fsCodeAppendf(" * float(coord.x >= %s.x && coord.x <= %s.y && coord.y >= %s.z && coord.y <= %s.w)",
+ cropRect, cropRect, cropRect, cropRect);
+ }
builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
}
+
SkString modulate;
GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
builder->fsCodeAppend(modulate.c_str());
@@ -96,17 +111,28 @@
// the code we generated was for a specific kernel radius
GrAssert(conv.radius() == fRadius);
float imageIncrement[2] = { 0 };
+ float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
switch (conv.direction()) {
case Gr1DKernelEffect::kX_Direction:
imageIncrement[0] = 1.0f / texture.width();
break;
case Gr1DKernelEffect::kY_Direction:
- imageIncrement[1] = 1.0f / texture.height();
+ imageIncrement[1] = ySign / texture.height();
break;
default:
GrCrash("Unknown filter direction.");
}
uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+ if (conv.useCropRect()) {
+ float c[4];
+ memcpy(c, conv.cropRect(), sizeof(c));
+ if (texture.origin() != kTopLeft_GrSurfaceOrigin) {
+ float tmp = 1.0f - c[2];
+ c[2] = 1.0f - c[3];
+ c[3] = tmp;
+ }
+ uman.set4fv(fCropRectUni, 0, 1, c);
+ }
uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
}
@@ -114,7 +140,8 @@
GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
const GrGLCaps&) {
const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
- EffectKey key = conv.radius();
+ EffectKey key = conv.radius() << 1;
+ key |= conv.useCropRect() ? 0x1 : 0x0;
key <<= GrGLEffectMatrix::kKeyBits;
EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
drawEffect,
@@ -128,21 +155,26 @@
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction,
int radius,
- const float* kernel)
- : Gr1DKernelEffect(texture, direction, radius) {
+ const float* kernel,
+ bool useCropRect,
+ float cropRect[4])
+ : Gr1DKernelEffect(texture, direction, radius), fUseCropRect(useCropRect) {
GrAssert(radius <= kMaxKernelRadius);
GrAssert(NULL != kernel);
int width = this->width();
for (int i = 0; i < width; i++) {
fKernel[i] = kernel[i];
}
+ memcpy(fCropRect, cropRect, sizeof(fCropRect));
}
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction,
int radius,
- float gaussianSigma)
- : Gr1DKernelEffect(texture, direction, radius) {
+ float gaussianSigma,
+ bool useCropRect,
+ float cropRect[4])
+ : Gr1DKernelEffect(texture, direction, radius), fUseCropRect(useCropRect) {
GrAssert(radius <= kMaxKernelRadius);
int width = this->width();
@@ -160,6 +192,7 @@
for (int i = 0; i < width; ++i) {
fKernel[i] *= scale;
}
+ memcpy(fCropRect, cropRect, sizeof(fCropRect));
}
GrConvolutionEffect::~GrConvolutionEffect() {
@@ -174,6 +207,7 @@
return (this->texture(0) == s.texture(0) &&
this->radius() == s.radius() &&
this->direction() == s.direction() &&
+ 0 == memcmp(fCropRect, s.fCropRect, sizeof(fCropRect)) &&
0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
}
@@ -190,9 +224,19 @@
Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
int radius = random->nextRangeU(1, kMaxKernelRadius);
float kernel[kMaxKernelRadius];
+ float cropRect[4];
for (int i = 0; i < kMaxKernelRadius; ++i) {
kernel[i] = random->nextSScalar1();
}
+ for (int i = 0; i < 4; ++i) {
+ cropRect[i] = random->nextF();
+ }
- return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel);
+ bool useCropRect = random->nextBool();
+ return GrConvolutionEffect::Create(textures[texIdx],
+ dir,
+ radius,
+ kernel,
+ useCropRect,
+ cropRect);
}
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index e4faa94..265150a 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -22,11 +22,18 @@
public:
/// Convolve with an arbitrary user-specified kernel
- static GrEffectRef* Create(GrTexture* tex, Direction dir, int halfWidth, const float* kernel) {
+ static GrEffectRef* Create(GrTexture* tex,
+ Direction dir,
+ int halfWidth,
+ const float* kernel,
+ bool useCropRect,
+ float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir,
halfWidth,
- kernel)));
+ kernel,
+ useCropRect,
+ cropRect)));
return CreateEffectRef(effect);
}
@@ -34,11 +41,15 @@
static GrEffectRef* CreateGaussian(GrTexture* tex,
Direction dir,
int halfWidth,
- float gaussianSigma) {
+ float gaussianSigma,
+ bool useCropRect,
+ float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir,
halfWidth,
- gaussianSigma)));
+ gaussianSigma,
+ useCropRect,
+ cropRect)));
return CreateEffectRef(effect);
}
@@ -46,6 +57,9 @@
const float* kernel() const { return fKernel; }
+ const float* cropRect() const { return fCropRect; }
+ bool useCropRect() const { return fUseCropRect; }
+
static const char* Name() { return "Convolution"; }
typedef GrGLConvolutionEffect GLEffect;
@@ -72,15 +86,22 @@
protected:
float fKernel[kMaxKernelWidth];
+ bool fUseCropRect;
+ float fCropRect[4];
private:
GrConvolutionEffect(GrTexture*, Direction,
- int halfWidth, const float* kernel);
+ int halfWidth,
+ const float* kernel,
+ bool useCropRect,
+ float cropRect[4]);
/// Convolve with a Gaussian kernel
GrConvolutionEffect(GrTexture*, Direction,
int halfWidth,
- float gaussianSigma);
+ float gaussianSigma,
+ bool useCropRect,
+ float cropRect[4]);
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;