michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrTextureDomain.h" michael@0: #include "GrSimpleTextureEffect.h" michael@0: #include "GrTBackendEffectFactory.h" michael@0: #include "gl/GrGLEffect.h" michael@0: #include "SkFloatingPoint.h" michael@0: michael@0: michael@0: GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) michael@0: : fIndex(index) { michael@0: michael@0: static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; michael@0: if (domain.contains(kFullRect)) { michael@0: fMode = kIgnore_Mode; michael@0: } else { michael@0: fMode = mode; michael@0: } michael@0: michael@0: if (fMode != kIgnore_Mode) { michael@0: // We don't currently handle domains that are empty or don't intersect the texture. michael@0: // It is OK if the domain rect is a line or point, but it should not be inverted. We do not michael@0: // handle rects that do not intersect the [0..1]x[0..1] rect. michael@0: SkASSERT(domain.fLeft <= domain.fRight); michael@0: SkASSERT(domain.fTop <= domain.fBottom); michael@0: fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft); michael@0: fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight); michael@0: fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop); michael@0: fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom); michael@0: SkASSERT(fDomain.fLeft <= fDomain.fRight); michael@0: SkASSERT(fDomain.fTop <= fDomain.fBottom); michael@0: } michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder, michael@0: const GrTextureDomain& textureDomain, michael@0: const char* outColor, michael@0: const SkString& inCoords, michael@0: const GrGLEffect::TextureSampler sampler, michael@0: const char* inModulateColor) { michael@0: SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode); michael@0: SkDEBUGCODE(fMode = textureDomain.mode();) michael@0: michael@0: if (kIgnore_Mode == textureDomain.mode()) { michael@0: builder->fsCodeAppendf("\t%s = ", outColor); michael@0: builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, michael@0: inCoords.c_str()); michael@0: builder->fsCodeAppend(";\n"); michael@0: return; michael@0: } michael@0: michael@0: if (!fDomainUni.isValid()) { michael@0: const char* name; michael@0: SkString uniName("TexDom"); michael@0: if (textureDomain.fIndex >= 0) { michael@0: uniName.appendS32(textureDomain.fIndex); michael@0: } michael@0: fDomainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, uniName.c_str(), &name); michael@0: fDomainName = name; michael@0: } michael@0: if (kClamp_Mode == textureDomain.mode()) { michael@0: SkString clampedCoords; michael@0: clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)", michael@0: inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str()); michael@0: michael@0: builder->fsCodeAppendf("\t%s = ", outColor); michael@0: builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str()); michael@0: builder->fsCodeAppend(";\n"); michael@0: } else { michael@0: SkASSERT(GrTextureDomain::kDecal_Mode == textureDomain.mode()); michael@0: // Add a block since we're going to declare variables. michael@0: GrGLShaderBuilder::FSBlock block(builder); michael@0: michael@0: const char* domain = fDomainName.c_str(); michael@0: if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) { michael@0: // On the NexusS and GalaxyNexus, the other path (with the 'any' michael@0: // call) causes the compilation error "Calls to any function that michael@0: // may require a gradient calculation inside a conditional block michael@0: // may return undefined results". This appears to be an issue with michael@0: // the 'any' call since even the simple "result=black; if (any()) michael@0: // result=white;" code fails to compile. michael@0: builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n"); michael@0: builder->fsCodeAppend("\tvec4 inside = "); michael@0: builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str()); michael@0: builder->fsCodeAppend(";\n"); michael@0: michael@0: builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n", michael@0: inCoords.c_str(), domain, domain, domain); michael@0: builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n", michael@0: inCoords.c_str(), domain, domain, domain); michael@0: builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n"); michael@0: builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outColor); michael@0: } else { michael@0: builder->fsCodeAppend("\tbvec4 outside;\n"); michael@0: builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(), michael@0: domain); michael@0: builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(), michael@0: domain); michael@0: builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outColor); michael@0: builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str()); michael@0: builder->fsCodeAppend(";\n"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void GrTextureDomain::GLDomain::setData(const GrGLUniformManager& uman, michael@0: const GrTextureDomain& textureDomain, michael@0: GrSurfaceOrigin textureOrigin) { michael@0: SkASSERT(textureDomain.mode() == fMode); michael@0: if (kIgnore_Mode != textureDomain.mode()) { michael@0: GrGLfloat values[4] = { michael@0: SkScalarToFloat(textureDomain.domain().left()), michael@0: SkScalarToFloat(textureDomain.domain().top()), michael@0: SkScalarToFloat(textureDomain.domain().right()), michael@0: SkScalarToFloat(textureDomain.domain().bottom()) michael@0: }; michael@0: // vertical flip if necessary michael@0: if (kBottomLeft_GrSurfaceOrigin == textureOrigin) { michael@0: values[1] = 1.0f - values[1]; michael@0: values[3] = 1.0f - values[3]; michael@0: // The top and bottom were just flipped, so correct the ordering michael@0: // of elements so that values = (l, t, r, b). michael@0: SkTSwap(values[1], values[3]); michael@0: } michael@0: if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) { michael@0: uman.set4fv(fDomainUni, 1, values); michael@0: memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrGLTextureDomainEffect : public GrGLEffect { michael@0: public: michael@0: GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrDrawEffect&); michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder*, michael@0: const GrDrawEffect&, michael@0: EffectKey, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: michael@0: virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); michael@0: michael@0: private: michael@0: GrTextureDomain::GLDomain fGLDomain; michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect&) michael@0: : INHERITED(factory) { michael@0: } michael@0: michael@0: void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray& coords, michael@0: const TextureSamplerArray& samplers) { michael@0: const GrTextureDomainEffect& effect = drawEffect.castEffect(); michael@0: const GrTextureDomain& domain = effect.textureDomain(); michael@0: michael@0: SkString coords2D = builder->ensureFSCoords2D(coords, 0); michael@0: fGLDomain.sampleTexture(builder, domain, outputColor, coords2D, samplers[0], inputColor); michael@0: } michael@0: michael@0: void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, michael@0: const GrDrawEffect& drawEffect) { michael@0: const GrTextureDomainEffect& effect = drawEffect.castEffect(); michael@0: const GrTextureDomain& domain = effect.textureDomain(); michael@0: fGLDomain.setData(uman, domain, effect.texture(0)->origin()); michael@0: } michael@0: michael@0: GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: const GrTextureDomain& domain = drawEffect.castEffect().textureDomain(); michael@0: return GrTextureDomain::GLDomain::DomainKey(domain); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture, michael@0: const SkMatrix& matrix, michael@0: const SkRect& domain, michael@0: GrTextureDomain::Mode mode, michael@0: GrTextureParams::FilterMode filterMode, michael@0: GrCoordSet coordSet) { michael@0: static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; michael@0: if (GrTextureDomain::kIgnore_Mode == mode || michael@0: (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) { michael@0: return GrSimpleTextureEffect::Create(texture, matrix, filterMode); michael@0: } else { michael@0: michael@0: AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture, michael@0: matrix, michael@0: domain, michael@0: mode, michael@0: filterMode, michael@0: coordSet))); michael@0: return CreateEffectRef(effect); michael@0: michael@0: } michael@0: } michael@0: michael@0: GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture, michael@0: const SkMatrix& matrix, michael@0: const SkRect& domain, michael@0: GrTextureDomain::Mode mode, michael@0: GrTextureParams::FilterMode filterMode, michael@0: GrCoordSet coordSet) michael@0: : GrSingleTextureEffect(texture, matrix, filterMode, coordSet) michael@0: , fTextureDomain(domain, mode) { michael@0: } michael@0: michael@0: GrTextureDomainEffect::~GrTextureDomainEffect() { michael@0: michael@0: } michael@0: michael@0: const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const { michael@0: const GrTextureDomainEffect& s = CastEffect(sBase); michael@0: return this->hasSameTextureParamsMatrixAndSourceCoords(s) && michael@0: this->fTextureDomain == s.fTextureDomain; michael@0: } michael@0: michael@0: void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { michael@0: if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper michael@0: *validFlags = 0; michael@0: } else { michael@0: this->updateConstantColorComponentsForModulation(color, validFlags); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect); michael@0: michael@0: GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture* textures[]) { michael@0: int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : michael@0: GrEffectUnitTest::kAlphaTextureIdx; michael@0: SkRect domain; michael@0: domain.fLeft = random->nextUScalar1(); michael@0: domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1); michael@0: domain.fTop = random->nextUScalar1(); michael@0: domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1); michael@0: GrTextureDomain::Mode mode = michael@0: (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount); michael@0: const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random); michael@0: bool bilerp = random->nextBool(); michael@0: GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet; michael@0: return GrTextureDomainEffect::Create(textures[texIdx], michael@0: matrix, michael@0: domain, michael@0: mode, michael@0: bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode, michael@0: coords); michael@0: }