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