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