Support mirror-repeat in GrTextureDomain
Bug: skia:9570
Change-Id: Iccfbb36b130ec2827164efda33470e2fa75ad833
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/258638
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index 6245ecf..0c8c34e 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -324,9 +324,10 @@
GrSamplerState::Filter::kBilerp);
if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
// GrDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
- GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
- ? GrTextureDomain::kDecal_Mode
- : mode;
+ GrTextureDomain::Mode modeForScaling = (GrTextureDomain::kRepeat_Mode == mode ||
+ GrTextureDomain::kMirrorRepeat_Mode == mode)
+ ? GrTextureDomain::kDecal_Mode
+ : mode;
SkRect domain = SkRect::Make(*contentRect);
domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
diff --git a/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp b/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp
index 611f027..296eaa4 100644
--- a/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp
@@ -389,7 +389,7 @@
case SkTileMode::kClamp:
return GrTextureDomain::kClamp_Mode;
case SkTileMode::kMirror:
- // TODO (michaelludwig) - Implement mirror tiling, treat as repeat for now.
+ return GrTextureDomain::kMirrorRepeat_Mode;
case SkTileMode::kRepeat:
return GrTextureDomain::kRepeat_Mode;
case SkTileMode::kDecal:
diff --git a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
index 231c1b6..cc6b8d2 100644
--- a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
+++ b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
@@ -89,6 +89,9 @@
component, component, bounds, bounds);
break;
}
+ // Deferring implementing kMirrorRepeat until we use DomainEffects as
+ // child processors. Fallback to Repeat.
+ case GrTextureDomain::kMirrorRepeat_Mode:
case GrTextureDomain::kRepeat_Mode: {
fragBuilder->codeAppendf("coordSampled.%s = "
"mod(coord.%s - %s.x, %s.y - %s.x) + %s.x;\n",
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index 8a0014a..f09857a 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -55,31 +55,33 @@
//////////////////////////////////////////////////////////////////////////////
-static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
- const char* coordSwizzle, const char* domain,
- const char* minSwizzle, const char* maxSwizzle) {
- SkString clampedExpr;
+static void append_wrap(GrGLSLShaderBuilder* builder, GrTextureDomain::Mode mode,
+ const char* inCoord, const char* domainStart, const char* domainEnd,
+ const char* out) {
switch(mode) {
case GrTextureDomain::kIgnore_Mode:
- clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle);
+ builder->codeAppendf("%s = %s;\n", out, inCoord);
break;
case GrTextureDomain::kDecal_Mode:
// The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
// it's just that the post-processing will be different, so fall through
case GrTextureDomain::kClamp_Mode:
- clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)",
- inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle);
+ builder->codeAppendf("%s = clamp(%s, %s, %s);", out, inCoord, domainStart, domainEnd);
break;
case GrTextureDomain::kRepeat_Mode:
- clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s",
- inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle,
- domain, minSwizzle, domain, minSwizzle);
+ builder->codeAppendf("%s = mod(%s - %s, %s - %s) + %s;", out, inCoord, domainStart,
+ domainEnd, domainStart, domainStart);
break;
- default:
- SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode);
+ case GrTextureDomain::kMirrorRepeat_Mode: {
+ builder->codeAppend("{");
+ builder->codeAppendf("float w = %s - %s;", domainEnd, domainStart);
+ builder->codeAppendf("float w2 = 2 * w;");
+ builder->codeAppendf("float m = mod(%s - %s, w2);", inCoord, domainStart);
+ builder->codeAppendf("%s = mix(m, w2 - m, step(w, m)) + %s;", out, domainStart);
+ builder->codeAppend("}");
break;
+ }
}
- return clampedExpr;
}
void GrTextureDomain::GLDomain::sampleProcessor(const GrTextureDomain& textureDomain,
@@ -158,23 +160,20 @@
// Always use a local variable for the input coordinates; often callers pass in an expression
// and we want to cache it across all of its references in the code below
builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
- builder->codeAppend("float2 clampedCoord = ");
- if (textureDomain.modeX() != textureDomain.modeY()) {
- // The wrap modes differ on the two axes, so build up a coordinate that respects each axis'
- // domain rule independently before sampling the texture.
- SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x",
- fDomainName.c_str(), "x", "z");
- SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y",
- fDomainName.c_str(), "y", "w");
- builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str());
- } else {
- // Since the x and y axis wrap modes are the same, they can be calculated together using
- // more efficient vector operations
- SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy",
- fDomainName.c_str(), "xy", "zw");
- builder->codeAppend(tc.c_str());
- }
- builder->codeAppend(";");
+ builder->codeAppend("float2 clampedCoord;");
+ SkString start;
+ SkString end;
+ // Apply x mode to the x coordinate using the left and right edges of the domain rect
+ // (stored as the x and z components of the domain uniform).
+ start.printf("%s.x", fDomainName.c_str());
+ end.printf("%s.z", fDomainName.c_str());
+ append_wrap(builder, textureDomain.modeX(), "origCoord.x", start.c_str(), end.c_str(),
+ "clampedCoord.x");
+ // Repeat the same logic for y.
+ start.printf("%s.y", fDomainName.c_str());
+ end.printf("%s.w", fDomainName.c_str());
+ append_wrap(builder, textureDomain.modeY(), "origCoord.y", start.c_str(), end.c_str(),
+ "clampedCoord.y");
// Sample 'appendSample' at the clamped coordinate location.
SkString color = appendSample("clampedCoord");
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 073a15b..53f3fcb 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -38,8 +38,10 @@
// read texels outside of the domain. We could perform additional texture reads and filter
// in the shader, but are not currently doing this for performance reasons
kRepeat_Mode,
+ // Mirror wrap texture coordinates. NOTE: suffers the same filtering limitation as kRepeat.
+ kMirrorRepeat_Mode,
- kLastMode = kRepeat_Mode
+ kLastMode = kMirrorRepeat_Mode
};
static const int kModeCount = kLastMode + 1;
@@ -183,7 +185,7 @@
bool filterIfDecal);
enum {
- kModeBits = 2, // See DomainKey().
+ kModeBits = 3, // See DomainKey().
kDomainKeyBits = 4
};