GrBicubicEffect.cpp revision 5af9ea399d5e0344cc4b7da4e97b5dc5b3c74f64
1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "GrBicubicEffect.h" 9 10#include "GrTexture.h" 11#include "glsl/GrGLSLColorSpaceXformHelper.h" 12#include "glsl/GrGLSLFragmentShaderBuilder.h" 13#include "glsl/GrGLSLProgramDataManager.h" 14#include "glsl/GrGLSLUniformHandler.h" 15#include "../private/GrGLSL.h" 16 17class GrGLBicubicEffect : public GrGLSLFragmentProcessor { 18public: 19 void emitCode(EmitArgs&) override; 20 21 static inline void GenKey(const GrProcessor& effect, const GrShaderCaps&, 22 GrProcessorKeyBuilder* b) { 23 const GrBicubicEffect& bicubicEffect = effect.cast<GrBicubicEffect>(); 24 b->add32(GrTextureDomain::GLDomain::DomainKey(bicubicEffect.domain())); 25 b->add32(GrColorSpaceXform::XformKey(bicubicEffect.colorSpaceXform())); 26 } 27 28protected: 29 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; 30 31private: 32 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; 33 34 UniformHandle fImageIncrementUni; 35 GrGLSLColorSpaceXformHelper fColorSpaceHelper; 36 GrTextureDomain::GLDomain fDomain; 37 38 typedef GrGLSLFragmentProcessor INHERITED; 39}; 40 41void GrGLBicubicEffect::emitCode(EmitArgs& args) { 42 const GrBicubicEffect& bicubicEffect = args.fFp.cast<GrBicubicEffect>(); 43 44 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 45 fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 46 kVec2f_GrSLType, kDefault_GrSLPrecision, 47 "ImageIncrement"); 48 49 const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); 50 51 fColorSpaceHelper.emitCode(uniformHandler, bicubicEffect.colorSpaceXform()); 52 53 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 54 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); 55 56 /* 57 * Filter weights come from Don Mitchell & Arun Netravali's 'Reconstruction Filters in Computer 58 * Graphics', ACM SIGGRAPH Computer Graphics 22, 4 (Aug. 1988). 59 * ACM DL: http://dl.acm.org/citation.cfm?id=378514 60 * Free : http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf 61 * 62 * The authors define a family of cubic filters with two free parameters (B and C): 63 * 64 * { (12 - 9B - 6C)|x|^3 + (-18 + 12B + 6C)|x|^2 + (6 - 2B) if |x| < 1 65 * k(x) = 1/6 { (-B - 6C)|x|^3 + (6B + 30C)|x|^2 + (-12B - 48C)|x| + (8B + 24C) if 1 <= |x| < 2 66 * { 0 otherwise 67 * 68 * Various well-known cubic splines can be generated, and the authors select (1/3, 1/3) as their 69 * favorite overall spline - this is now commonly known as the Mitchell filter, and is the 70 * source of the specific weights below. 71 * 72 * This is GLSL, so the matrix is column-major (transposed from standard matrix notation). 73 */ 74 fragBuilder->codeAppend("float4x4 kMitchellCoefficients = float4x4(" 75 " 1.0 / 18.0, 16.0 / 18.0, 1.0 / 18.0, 0.0 / 18.0," 76 "-9.0 / 18.0, 0.0 / 18.0, 9.0 / 18.0, 0.0 / 18.0," 77 "15.0 / 18.0, -36.0 / 18.0, 27.0 / 18.0, -6.0 / 18.0," 78 "-7.0 / 18.0, 21.0 / 18.0, -21.0 / 18.0, 7.0 / 18.0);"); 79 fragBuilder->codeAppendf("float2 coord = %s - %s * float2(0.5);", coords2D.c_str(), imgInc); 80 // We unnormalize the coord in order to determine our fractional offset (f) within the texel 81 // We then snap coord to a texel center and renormalize. The snap prevents cases where the 82 // starting coords are near a texel boundary and accumulations of imgInc would cause us to skip/ 83 // double hit a texel. 84 fragBuilder->codeAppendf("coord /= %s;", imgInc); 85 fragBuilder->codeAppend("float2 f = fract(coord);"); 86 fragBuilder->codeAppendf("coord = (coord - f + float2(0.5)) * %s;", imgInc); 87 fragBuilder->codeAppend("float4 wx = kMitchellCoefficients * float4(1.0, f.x, f.x * f.x, f.x * f.x * f.x);"); 88 fragBuilder->codeAppend("float4 wy = kMitchellCoefficients * float4(1.0, f.y, f.y * f.y, f.y * f.y * f.y);"); 89 fragBuilder->codeAppend("float4 rowColors[4];"); 90 for (int y = 0; y < 4; ++y) { 91 for (int x = 0; x < 4; ++x) { 92 SkString coord; 93 coord.printf("coord + %s * float2(%d, %d)", imgInc, x - 1, y - 1); 94 SkString sampleVar; 95 sampleVar.printf("rowColors[%d]", x); 96 fDomain.sampleTexture(fragBuilder, 97 args.fUniformHandler, 98 args.fShaderCaps, 99 bicubicEffect.domain(), 100 sampleVar.c_str(), 101 coord, 102 args.fTexSamplers[0]); 103 } 104 fragBuilder->codeAppendf( 105 "float4 s%d = wx.x * rowColors[0] + wx.y * rowColors[1] + wx.z * rowColors[2] + wx.w * rowColors[3];", 106 y); 107 } 108 SkString bicubicColor("(wy.x * s0 + wy.y * s1 + wy.z * s2 + wy.w * s3)"); 109 if (fColorSpaceHelper.isValid()) { 110 SkString xformedColor; 111 fragBuilder->appendColorGamutXform(&xformedColor, bicubicColor.c_str(), &fColorSpaceHelper); 112 bicubicColor.swap(xformedColor); 113 } 114 fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, bicubicColor.c_str(), 115 args.fInputColor); 116} 117 118void GrGLBicubicEffect::onSetData(const GrGLSLProgramDataManager& pdman, 119 const GrFragmentProcessor& processor) { 120 const GrBicubicEffect& bicubicEffect = processor.cast<GrBicubicEffect>(); 121 GrSurfaceProxy* proxy = processor.textureSampler(0).proxy(); 122 GrTexture* texture = proxy->priv().peekTexture(); 123 124 float imageIncrement[2]; 125 imageIncrement[0] = 1.0f / texture->width(); 126 imageIncrement[1] = 1.0f / texture->height(); 127 pdman.set2fv(fImageIncrementUni, 1, imageIncrement); 128 fDomain.setData(pdman, bicubicEffect.domain(), proxy); 129 if (SkToBool(bicubicEffect.colorSpaceXform())) { 130 fColorSpaceHelper.setData(pdman, bicubicEffect.colorSpaceXform()); 131 } 132} 133 134GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy, 135 sk_sp<GrColorSpaceXform> colorSpaceXform, 136 const SkMatrix& matrix, 137 const SkShader::TileMode tileModes[2]) 138 : INHERITED{ModulateByConfigOptimizationFlags(proxy->config())} 139 , fCoordTransform(matrix, proxy.get()) 140 , fDomain(GrTextureDomain::IgnoredDomain()) 141 , fTextureSampler(std::move(proxy), 142 GrSamplerParams(tileModes, GrSamplerParams::kNone_FilterMode)) 143 , fColorSpaceXform(std::move(colorSpaceXform)) { 144 this->initClassID<GrBicubicEffect>(); 145 this->addCoordTransform(&fCoordTransform); 146 this->addTextureSampler(&fTextureSampler); 147} 148 149GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy, 150 sk_sp<GrColorSpaceXform> colorSpaceXform, 151 const SkMatrix& matrix, 152 const SkRect& domain) 153 : INHERITED(ModulateByConfigOptimizationFlags(proxy->config())) 154 , fCoordTransform(matrix, proxy.get()) 155 , fDomain(proxy.get(), domain, GrTextureDomain::kClamp_Mode) 156 , fTextureSampler(std::move(proxy)) 157 , fColorSpaceXform(std::move(colorSpaceXform)) { 158 this->initClassID<GrBicubicEffect>(); 159 this->addCoordTransform(&fCoordTransform); 160 this->addTextureSampler(&fTextureSampler); 161} 162 163GrBicubicEffect::GrBicubicEffect(const GrBicubicEffect& that) 164 : INHERITED(that.optimizationFlags()) 165 , fCoordTransform(that.fCoordTransform) 166 , fDomain(that.fDomain) 167 , fTextureSampler(that.fTextureSampler) 168 , fColorSpaceXform(that.fColorSpaceXform) { 169 this->initClassID<GrBicubicEffect>(); 170 this->addCoordTransform(&fCoordTransform); 171 this->addTextureSampler(&fTextureSampler); 172} 173 174void GrBicubicEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 175 GrProcessorKeyBuilder* b) const { 176 GrGLBicubicEffect::GenKey(*this, caps, b); 177} 178 179GrGLSLFragmentProcessor* GrBicubicEffect::onCreateGLSLInstance() const { 180 return new GrGLBicubicEffect; 181} 182 183bool GrBicubicEffect::onIsEqual(const GrFragmentProcessor& sBase) const { 184 const GrBicubicEffect& s = sBase.cast<GrBicubicEffect>(); 185 return fDomain == s.fDomain; 186} 187 188GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrBicubicEffect); 189 190#if GR_TEST_UTILS 191sk_sp<GrFragmentProcessor> GrBicubicEffect::TestCreate(GrProcessorTestData* d) { 192 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 193 : GrProcessorUnitTest::kAlphaTextureIdx; 194 sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom); 195 static const SkShader::TileMode kClampClamp[] = 196 { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; 197 return GrBicubicEffect::Make(d->textureProxy(texIdx), std::move(colorSpaceXform), 198 SkMatrix::I(), kClampClamp); 199} 200#endif 201 202////////////////////////////////////////////////////////////////////////////// 203 204bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix, 205 GrSamplerParams::FilterMode* filterMode) { 206 if (matrix.isIdentity()) { 207 *filterMode = GrSamplerParams::kNone_FilterMode; 208 return false; 209 } 210 211 SkScalar scales[2]; 212 if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) { 213 // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped 214 // entirely, 215 *filterMode = GrSamplerParams::kMipMap_FilterMode; 216 return false; 217 } 218 // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling. 219 if (scales[1] == SK_Scalar1) { 220 if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) && 221 SkScalarIsInt(matrix.getTranslateY())) { 222 *filterMode = GrSamplerParams::kNone_FilterMode; 223 } else { 224 // Use bilerp to handle rotation or fractional translation. 225 *filterMode = GrSamplerParams::kBilerp_FilterMode; 226 } 227 return false; 228 } 229 // When we use the bicubic filtering effect each sample is read from the texture using 230 // nearest neighbor sampling. 231 *filterMode = GrSamplerParams::kNone_FilterMode; 232 return true; 233} 234