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