GrConfigConversionEffect.cpp revision 8a252f79629b189a03de22cd8ff0312c5bccedd1
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 "GrConfigConversionEffect.h"
9#include "GrContext.h"
10#include "GrTBackendEffectFactory.h"
11#include "GrSimpleTextureEffect.h"
12#include "gl/GrGLEffect.h"
13#include "gl/GrGLEffectMatrix.h"
14#include "SkMatrix.h"
15
16class GrGLConfigConversionEffect : public GrGLEffect {
17public:
18    GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
19                               const GrEffectRef& s) : INHERITED (factory) {
20        const GrConfigConversionEffect& effect = CastEffect<GrConfigConversionEffect>(s);
21        fSwapRedAndBlue = effect.swapsRedAndBlue();
22        fPMConversion = effect.pmConversion();
23    }
24
25    virtual void emitCode(GrGLShaderBuilder* builder,
26                          const GrEffectStage&,
27                          EffectKey key,
28                          const char* vertexCoords,
29                          const char* outputColor,
30                          const char* inputColor,
31                          const TextureSamplerArray& samplers) SK_OVERRIDE {
32        const char* coords;
33        GrSLType coordsType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coords);
34        builder->fFSCode.appendf("\t\t%s = ", outputColor);
35        builder->appendTextureLookup(&builder->fFSCode, samplers[0], coords, coordsType);
36        builder->fFSCode.append(";\n");
37        if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
38            GrAssert(fSwapRedAndBlue);
39            builder->fFSCode.appendf("\t%s = %s.bgra;\n", outputColor, outputColor);
40        } else {
41            const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb";
42            switch (fPMConversion) {
43                case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
44                    builder->fFSCode.appendf(
45                        "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
46                        outputColor, outputColor, swiz, outputColor, outputColor);
47                    break;
48                case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
49                    builder->fFSCode.appendf(
50                        "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
51                        outputColor, outputColor, swiz, outputColor, outputColor);
52                    break;
53                case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
54                    builder->fFSCode.appendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
55                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
56                    break;
57                case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
58                    builder->fFSCode.appendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
59                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
60                    break;
61                default:
62                    GrCrash("Unknown conversion op.");
63                    break;
64            }
65        }
66        GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
67    }
68
69    void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
70        const GrConfigConversionEffect& effect =
71            GetEffectFromStage<GrConfigConversionEffect>(stage);
72        fEffectMatrix.setData(uman,
73                              effect.getMatrix(),
74                              stage.getCoordChangeMatrix(),
75                              effect.texture(0));
76    }
77
78    static inline EffectKey GenKey(const GrEffectStage& s, const GrGLCaps&) {
79        const GrConfigConversionEffect& effect = GetEffectFromStage<GrConfigConversionEffect>(s);
80        EffectKey key = static_cast<EffectKey>(effect.swapsRedAndBlue()) |
81                        (effect.pmConversion() << 1);
82        key <<= GrGLEffectMatrix::kKeyBits;
83        EffectKey matrixKey =  GrGLEffectMatrix::GenKey(effect.getMatrix(),
84                                                        s.getCoordChangeMatrix(),
85                                                        effect.texture(0));
86        GrAssert(!(matrixKey & key));
87        return matrixKey | key;
88    }
89
90private:
91    bool                                    fSwapRedAndBlue;
92    GrConfigConversionEffect::PMConversion  fPMConversion;
93    GrGLEffectMatrix                        fEffectMatrix;
94
95    typedef GrGLEffect INHERITED;
96
97};
98
99///////////////////////////////////////////////////////////////////////////////
100
101GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
102                                                   bool swapRedAndBlue,
103                                                   PMConversion pmConversion,
104                                                   const SkMatrix& matrix)
105    : GrSingleTextureEffect(texture, matrix)
106    , fSwapRedAndBlue(swapRedAndBlue)
107    , fPMConversion(pmConversion) {
108    GrAssert(kRGBA_8888_GrPixelConfig == texture->config() ||
109             kBGRA_8888_GrPixelConfig == texture->config());
110    // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
111    GrAssert(swapRedAndBlue || kNone_PMConversion != pmConversion);
112}
113
114const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
115    return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
116}
117
118bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const {
119    const GrConfigConversionEffect& other = CastEffect<GrConfigConversionEffect>(s);
120    return this->texture(0) == s.texture(0) &&
121           other.fSwapRedAndBlue == fSwapRedAndBlue &&
122           other.fPMConversion == fPMConversion;
123}
124
125void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
126                                                          uint32_t* validFlags) const {
127    this->updateConstantColorComponentsForModulation(color, validFlags);
128}
129
130///////////////////////////////////////////////////////////////////////////////
131
132GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
133
134GrEffectRef* GrConfigConversionEffect::TestCreate(SkRandom* random,
135                                                  GrContext* context,
136                                                  GrTexture* textures[]) {
137    PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
138    bool swapRB;
139    if (kNone_PMConversion == pmConv) {
140        swapRB = true;
141    } else {
142        swapRB = random->nextBool();
143    }
144    AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect,
145                                      (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
146                                       swapRB,
147                                       pmConv,
148                                       GrEffectUnitTest::TestMatrix(random))));
149    return CreateEffectRef(effect);
150}
151
152///////////////////////////////////////////////////////////////////////////////
153void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
154                                                              PMConversion* pmToUPMRule,
155                                                              PMConversion* upmToPMRule) {
156    *pmToUPMRule = kNone_PMConversion;
157    *upmToPMRule = kNone_PMConversion;
158    SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
159    uint32_t* srcData = data.get();
160    uint32_t* firstRead = data.get() + 256 * 256;
161    uint32_t* secondRead = data.get() + 2 * 256 * 256;
162
163    // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
164    // values in row y. We set r,g, and b to the same value since they are handled identically.
165    for (int y = 0; y < 256; ++y) {
166        for (int x = 0; x < 256; ++x) {
167            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
168            color[3] = y;
169            color[2] = GrMin(x, y);
170            color[1] = GrMin(x, y);
171            color[0] = GrMin(x, y);
172        }
173    }
174
175    GrTextureDesc desc;
176    desc.fFlags = kRenderTarget_GrTextureFlagBit |
177                  kNoStencil_GrTextureFlagBit;
178    desc.fWidth = 256;
179    desc.fHeight = 256;
180    desc.fConfig = kRGBA_8888_GrPixelConfig;
181
182    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
183    if (!readTex.get()) {
184        return;
185    }
186    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
187    if (!tempTex.get()) {
188        return;
189    }
190    desc.fFlags = kNone_GrTextureFlags;
191    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
192    if (!dataTex.get()) {
193        return;
194    }
195
196    static const PMConversion kConversionRules[][2] = {
197        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
198        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
199    };
200
201    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
202
203    bool failed = true;
204
205    for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
206        *pmToUPMRule = kConversionRules[i][0];
207        *upmToPMRule = kConversionRules[i][1];
208
209        static const GrRect kDstRect = GrRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
210        static const GrRect kSrcRect = GrRect::MakeWH(SK_Scalar1, SK_Scalar1);
211        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
212        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
213        // We then verify that two reads produced the same values.
214
215        GrPaint paint;
216        AutoEffectUnref pmToUPM1(SkNEW_ARGS(GrConfigConversionEffect, (dataTex,
217                                                                       false,
218                                                                       *pmToUPMRule,
219                                                                       SkMatrix::I())));
220        AutoEffectUnref upmToPM(SkNEW_ARGS(GrConfigConversionEffect, (readTex,
221                                                                      false,
222                                                                      *upmToPMRule,
223                                                                      SkMatrix::I())));
224        AutoEffectUnref pmToUPM2(SkNEW_ARGS(GrConfigConversionEffect, (tempTex,
225                                                                       false,
226                                                                       *pmToUPMRule,
227                                                                       SkMatrix::I())));
228
229        SkAutoTUnref<GrEffectRef> pmToUPMEffect1(CreateEffectRef(pmToUPM1));
230        SkAutoTUnref<GrEffectRef> upmToPMEffect(CreateEffectRef(upmToPM));
231        SkAutoTUnref<GrEffectRef> pmToUPMEffect2(CreateEffectRef(pmToUPM2));
232
233        context->setRenderTarget(readTex->asRenderTarget());
234        paint.colorStage(0)->setEffect(pmToUPMEffect1);
235        context->drawRectToRect(paint, kDstRect, kSrcRect);
236
237        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
238
239        context->setRenderTarget(tempTex->asRenderTarget());
240        paint.colorStage(0)->setEffect(upmToPMEffect);
241        context->drawRectToRect(paint, kDstRect, kSrcRect);
242        context->setRenderTarget(readTex->asRenderTarget());
243        paint.colorStage(0)->setEffect(pmToUPMEffect2);
244        context->drawRectToRect(paint, kDstRect, kSrcRect);
245
246        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
247
248        failed = false;
249        for (int y = 0; y < 256 && !failed; ++y) {
250            for (int x = 0; x <= y; ++x) {
251                if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
252                    failed = true;
253                    break;
254                }
255            }
256        }
257    }
258    if (failed) {
259        *pmToUPMRule = kNone_PMConversion;
260        *upmToPMRule = kNone_PMConversion;
261    }
262}
263
264bool GrConfigConversionEffect::InstallEffect(GrTexture* texture,
265                                             bool swapRedAndBlue,
266                                             PMConversion pmConversion,
267                                             const SkMatrix& matrix,
268                                             GrEffectStage* stage) {
269    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
270        // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
271        // then we may pollute our texture cache with redundant shaders. So in the case that no
272        // conversions were requested we instead return a GrSingleTextureEffect.
273        stage->setEffect(GrSimpleTextureEffect::Create(texture, matrix))->unref();
274        return true;
275    } else {
276        if (kRGBA_8888_GrPixelConfig != texture->config() &&
277            kBGRA_8888_GrPixelConfig != texture->config() &&
278            kNone_PMConversion != pmConversion) {
279            // The PM conversions assume colors are 0..255
280            return false;
281        }
282        AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
283                                                                     swapRedAndBlue,
284                                                                     pmConversion,
285                                                                     matrix)));
286        stage->setEffect(CreateEffectRef(effect))->unref();
287        return true;
288    }
289}
290