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