GrTextureDomain.cpp revision 267ce482b54f46097584e0f9350ec74aa6a2cd44
1/*
2 * Copyright 2012 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 "gl/builders/GrGLProgramBuilder.h"
9#include "GrTextureDomain.h"
10#include "GrInvariantOutput.h"
11#include "GrSimpleTextureEffect.h"
12#include "GrTBackendProcessorFactory.h"
13#include "gl/GrGLProcessor.h"
14#include "SkFloatingPoint.h"
15
16
17GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index)
18    : fIndex(index) {
19
20    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
21    if (domain.contains(kFullRect) && kClamp_Mode == mode) {
22        fMode = kIgnore_Mode;
23    } else {
24        fMode = mode;
25    }
26
27    if (fMode != kIgnore_Mode) {
28        // We don't currently handle domains that are empty or don't intersect the texture.
29        // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
30        // handle rects that do not intersect the [0..1]x[0..1] rect.
31        SkASSERT(domain.fLeft <= domain.fRight);
32        SkASSERT(domain.fTop <= domain.fBottom);
33        fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
34        fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
35        fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
36        fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
37        SkASSERT(fDomain.fLeft <= fDomain.fRight);
38        SkASSERT(fDomain.fTop <= fDomain.fBottom);
39    }
40}
41
42//////////////////////////////////////////////////////////////////////////////
43
44void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder,
45                                              const GrTextureDomain& textureDomain,
46                                              const char* outColor,
47                                              const SkString& inCoords,
48                                              const GrGLProcessor::TextureSampler sampler,
49                                              const char* inModulateColor) {
50    SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode);
51    SkDEBUGCODE(fMode = textureDomain.mode();)
52
53    GrGLProgramBuilder* program = builder->getProgramBuilder();
54
55    if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) {
56        const char* name;
57        SkString uniName("TexDom");
58        if (textureDomain.fIndex >= 0) {
59            uniName.appendS32(textureDomain.fIndex);
60        }
61        fDomainUni = program->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType,
62                uniName.c_str(), &name);
63        fDomainName = name;
64    }
65
66    switch (textureDomain.mode()) {
67        case kIgnore_Mode: {
68            builder->codeAppendf("\t%s = ", outColor);
69            builder->appendTextureLookupAndModulate(inModulateColor, sampler,
70                                                      inCoords.c_str());
71            builder->codeAppend(";\n");
72            break;
73        }
74        case kClamp_Mode: {
75            SkString clampedCoords;
76            clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)",
77                                  inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
78
79            builder->codeAppendf("\t%s = ", outColor);
80            builder->appendTextureLookupAndModulate(inModulateColor, sampler,
81                                                      clampedCoords.c_str());
82            builder->codeAppend(";\n");
83            break;
84        }
85        case kDecal_Mode: {
86            // Add a block since we're going to declare variables.
87            GrGLShaderBuilder::ShaderBlock block(builder);
88
89            const char* domain = fDomainName.c_str();
90            if (kImagination_GrGLVendor == program->ctxInfo().vendor()) {
91                // On the NexusS and GalaxyNexus, the other path (with the 'any'
92                // call) causes the compilation error "Calls to any function that
93                // may require a gradient calculation inside a conditional block
94                // may return undefined results". This appears to be an issue with
95                // the 'any' call since even the simple "result=black; if (any())
96                // result=white;" code fails to compile.
97                builder->codeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
98                builder->codeAppend("\tvec4 inside = ");
99                builder->appendTextureLookupAndModulate(inModulateColor, sampler,
100                                                          inCoords.c_str());
101                builder->codeAppend(";\n");
102                builder->codeAppendf("\tfloat x = (%s).x;\n", inCoords.c_str());
103                builder->codeAppendf("\tfloat y = (%s).y;\n", inCoords.c_str());
104
105                builder->codeAppendf("\tx = abs(2.0*(x - %s.x)/(%s.z - %s.x) - 1.0);\n",
106                                       domain, domain, domain);
107                builder->codeAppendf("\ty = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);\n",
108                                       domain, domain, domain);
109                builder->codeAppend("\tfloat blend = step(1.0, max(x, y));\n");
110                builder->codeAppendf("\t%s = mix(inside, outside, blend);\n", outColor);
111            } else {
112                builder->codeAppend("\tbvec4 outside;\n");
113                builder->codeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(),
114                                       domain);
115                builder->codeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(),
116                                       domain);
117                builder->codeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ",
118                                       outColor);
119                builder->appendTextureLookupAndModulate(inModulateColor, sampler,
120                                                          inCoords.c_str());
121                builder->codeAppend(";\n");
122            }
123            break;
124        }
125        case kRepeat_Mode: {
126            SkString clampedCoords;
127            clampedCoords.printf("\tmod(%s - %s.xy, %s.zw - %s.xy) + %s.xy",
128                                 inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(),
129                                 fDomainName.c_str(), fDomainName.c_str());
130
131            builder->codeAppendf("\t%s = ", outColor);
132            builder->appendTextureLookupAndModulate(inModulateColor, sampler,
133                                                      clampedCoords.c_str());
134            builder->codeAppend(";\n");
135            break;
136        }
137    }
138}
139
140void GrTextureDomain::GLDomain::setData(const GrGLProgramDataManager& pdman,
141                                        const GrTextureDomain& textureDomain,
142                                        GrSurfaceOrigin textureOrigin) {
143    SkASSERT(textureDomain.mode() == fMode);
144    if (kIgnore_Mode != textureDomain.mode()) {
145        GrGLfloat values[4] = {
146            SkScalarToFloat(textureDomain.domain().left()),
147            SkScalarToFloat(textureDomain.domain().top()),
148            SkScalarToFloat(textureDomain.domain().right()),
149            SkScalarToFloat(textureDomain.domain().bottom())
150        };
151        // vertical flip if necessary
152        if (kBottomLeft_GrSurfaceOrigin == textureOrigin) {
153            values[1] = 1.0f - values[1];
154            values[3] = 1.0f - values[3];
155            // The top and bottom were just flipped, so correct the ordering
156            // of elements so that values = (l, t, r, b).
157            SkTSwap(values[1], values[3]);
158        }
159        if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
160            pdman.set4fv(fDomainUni, 1, values);
161            memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
162        }
163    }
164}
165
166
167//////////////////////////////////////////////////////////////////////////////
168
169class GrGLTextureDomainEffect : public GrGLFragmentProcessor {
170public:
171    GrGLTextureDomainEffect(const GrBackendProcessorFactory&, const GrProcessor&);
172
173    virtual void emitCode(GrGLFPBuilder*,
174                          const GrFragmentProcessor&,
175                          const char* outputColor,
176                          const char* inputColor,
177                          const TransformedCoordsArray&,
178                          const TextureSamplerArray&) SK_OVERRIDE;
179
180    virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
181
182    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
183
184private:
185    GrTextureDomain::GLDomain         fGLDomain;
186    typedef GrGLFragmentProcessor INHERITED;
187};
188
189GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendProcessorFactory& factory,
190                                                 const GrProcessor&)
191    : INHERITED(factory) {
192}
193
194void GrGLTextureDomainEffect::emitCode(GrGLFPBuilder* builder,
195                                       const GrFragmentProcessor& fp,
196                                       const char* outputColor,
197                                       const char* inputColor,
198                                       const TransformedCoordsArray& coords,
199                                       const TextureSamplerArray& samplers) {
200    const GrTextureDomainEffect& textureDomainEffect = fp.cast<GrTextureDomainEffect>();
201    const GrTextureDomain& domain = textureDomainEffect.textureDomain();
202
203    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
204    SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
205    fGLDomain.sampleTexture(fsBuilder, domain, outputColor, coords2D, samplers[0], inputColor);
206}
207
208void GrGLTextureDomainEffect::setData(const GrGLProgramDataManager& pdman,
209                                      const GrProcessor& processor) {
210    const GrTextureDomainEffect& textureDomainEffect = processor.cast<GrTextureDomainEffect>();
211    const GrTextureDomain& domain = textureDomainEffect.textureDomain();
212    fGLDomain.setData(pdman, domain, processor.texture(0)->origin());
213}
214
215void GrGLTextureDomainEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
216                                     GrProcessorKeyBuilder* b) {
217    const GrTextureDomain& domain = processor.cast<GrTextureDomainEffect>().textureDomain();
218    b->add32(GrTextureDomain::GLDomain::DomainKey(domain));
219}
220
221
222///////////////////////////////////////////////////////////////////////////////
223
224GrFragmentProcessor* GrTextureDomainEffect::Create(GrTexture* texture,
225                                                   const SkMatrix& matrix,
226                                                   const SkRect& domain,
227                                                   GrTextureDomain::Mode mode,
228                                                   GrTextureParams::FilterMode filterMode,
229                                                   GrCoordSet coordSet) {
230    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
231    if (GrTextureDomain::kIgnore_Mode == mode ||
232        (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) {
233        return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
234    } else {
235
236        return SkNEW_ARGS(GrTextureDomainEffect, (texture,
237                                                  matrix,
238                                                  domain,
239                                                  mode,
240                                                  filterMode,
241                                                  coordSet));
242    }
243}
244
245GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
246                                             const SkMatrix& matrix,
247                                             const SkRect& domain,
248                                             GrTextureDomain::Mode mode,
249                                             GrTextureParams::FilterMode filterMode,
250                                             GrCoordSet coordSet)
251    : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
252    , fTextureDomain(domain, mode) {
253    SkASSERT(mode != GrTextureDomain::kRepeat_Mode ||
254            filterMode == GrTextureParams::kNone_FilterMode);
255}
256
257GrTextureDomainEffect::~GrTextureDomainEffect() {
258
259}
260
261const GrBackendFragmentProcessorFactory& GrTextureDomainEffect::getFactory() const {
262    return GrTBackendFragmentProcessorFactory<GrTextureDomainEffect>::getInstance();
263}
264
265bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
266    const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
267    return this->fTextureDomain == s.fTextureDomain;
268}
269
270void GrTextureDomainEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
271    if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper
272        if (GrPixelConfigIsAlphaOnly(this->texture(0)->config())) {
273            inout->mulByUnknownAlpha();
274        } else {
275            inout->mulByUnknownColor();
276        }
277    } else {
278        this->updateInvariantOutputForModulation(inout);
279    }
280}
281
282///////////////////////////////////////////////////////////////////////////////
283
284GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
285
286GrFragmentProcessor* GrTextureDomainEffect::TestCreate(SkRandom* random,
287                                                       GrContext*,
288                                                       const GrDrawTargetCaps&,
289                                                       GrTexture* textures[]) {
290    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
291                                      GrProcessorUnitTest::kAlphaTextureIdx;
292    SkRect domain;
293    domain.fLeft = random->nextUScalar1();
294    domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
295    domain.fTop = random->nextUScalar1();
296    domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
297    GrTextureDomain::Mode mode =
298        (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount);
299    const SkMatrix& matrix = GrProcessorUnitTest::TestMatrix(random);
300    bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? random->nextBool() : false;
301    GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
302    return GrTextureDomainEffect::Create(textures[texIdx],
303                                         matrix,
304                                         domain,
305                                         mode,
306                                         bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
307                                         coords);
308}
309