GrConfigConversionEffect.cpp revision b1456d70100d309c852906fafab006fae63de53e
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        fRequiresTextureMatrix = false;
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            static_cast<const GrConfigConversionEffect&>(*stage.getEffect());
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 =
80            static_cast<const GrConfigConversionEffect&>(*s.getEffect());
81        EffectKey key = static_cast<EffectKey>(effect.swapsRedAndBlue()) |
82                        (effect.pmConversion() << 1);
83        key <<= GrGLEffectMatrix::kKeyBits;
84        EffectKey matrixKey =  GrGLEffectMatrix::GenKey(effect.getMatrix(),
85                                                        s.getCoordChangeMatrix(),
86                                                        effect.texture(0));
87        GrAssert(!(matrixKey & key));
88        return matrixKey | key;
89    }
90
91private:
92    bool                                    fSwapRedAndBlue;
93    GrConfigConversionEffect::PMConversion  fPMConversion;
94    GrGLEffectMatrix                        fEffectMatrix;
95
96    typedef GrGLEffect INHERITED;
97
98};
99
100///////////////////////////////////////////////////////////////////////////////
101
102GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
103                                                   bool swapRedAndBlue,
104                                                   PMConversion pmConversion,
105                                                   const SkMatrix& matrix)
106    : GrSingleTextureEffect(texture, matrix)
107    , fSwapRedAndBlue(swapRedAndBlue)
108    , fPMConversion(pmConversion) {
109    GrAssert(kRGBA_8888_GrPixelConfig == texture->config() ||
110             kBGRA_8888_GrPixelConfig == texture->config());
111    // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
112    GrAssert(swapRedAndBlue || kNone_PMConversion != pmConversion);
113}
114
115const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
116    return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
117}
118
119bool GrConfigConversionEffect::isEqual(const GrEffect& s) const {
120    const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
121    return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
122}
123
124///////////////////////////////////////////////////////////////////////////////
125
126GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
127
128GrEffect* GrConfigConversionEffect::TestCreate(SkRandom* random,
129                                               GrContext* context,
130                                               GrTexture* textures[]) {
131    PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
132    bool swapRB;
133    if (kNone_PMConversion == pmConv) {
134        swapRB = true;
135    } else {
136        swapRB = random->nextBool();
137    }
138    return SkNEW_ARGS(GrConfigConversionEffect, (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
139                                                 swapRB,
140                                                 pmConv,
141                                                 GrEffectUnitTest::TestMatrix(random)));
142}
143
144///////////////////////////////////////////////////////////////////////////////
145void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
146                                                              PMConversion* pmToUPMRule,
147                                                              PMConversion* upmToPMRule) {
148    *pmToUPMRule = kNone_PMConversion;
149    *upmToPMRule = kNone_PMConversion;
150    SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
151    uint32_t* srcData = data.get();
152    uint32_t* firstRead = data.get() + 256 * 256;
153    uint32_t* secondRead = data.get() + 2 * 256 * 256;
154
155    // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
156    // values in row y. We set r,g, and b to the same value since they are handled identically.
157    for (int y = 0; y < 256; ++y) {
158        for (int x = 0; x < 256; ++x) {
159            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
160            color[3] = y;
161            color[2] = GrMin(x, y);
162            color[1] = GrMin(x, y);
163            color[0] = GrMin(x, y);
164        }
165    }
166
167    GrTextureDesc desc;
168    desc.fFlags = kRenderTarget_GrTextureFlagBit |
169                  kNoStencil_GrTextureFlagBit;
170    desc.fWidth = 256;
171    desc.fHeight = 256;
172    desc.fConfig = kRGBA_8888_GrPixelConfig;
173
174    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
175    if (!readTex.get()) {
176        return;
177    }
178    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
179    if (!tempTex.get()) {
180        return;
181    }
182    desc.fFlags = kNone_GrTextureFlags;
183    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
184    if (!dataTex.get()) {
185        return;
186    }
187
188    static const PMConversion kConversionRules[][2] = {
189        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
190        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
191    };
192
193    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
194
195    bool failed = true;
196
197    for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
198        *pmToUPMRule = kConversionRules[i][0];
199        *upmToPMRule = kConversionRules[i][1];
200
201        static const GrRect kDstRect = GrRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
202        static const GrRect kSrcRect = GrRect::MakeWH(SK_Scalar1, SK_Scalar1);
203        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
204        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
205        // We then verify that two reads produced the same values.
206
207        GrPaint paint;
208        SkAutoTUnref<GrEffect> pmToUPMEffect1(SkNEW_ARGS(GrConfigConversionEffect,
209                                                        (dataTex,
210                                                         false,
211                                                         *pmToUPMRule,
212                                                         SkMatrix::I())));
213        SkAutoTUnref<GrEffect> upmToPMEffect(SkNEW_ARGS(GrConfigConversionEffect,
214                                                       (readTex,
215                                                        false,
216                                                        *upmToPMRule,
217                                                        SkMatrix::I())));
218        SkAutoTUnref<GrEffect> pmToUPMEffect2(SkNEW_ARGS(GrConfigConversionEffect,
219                                                        (tempTex,
220                                                         false,
221                                                         *pmToUPMRule,
222                                                         SkMatrix::I())));
223
224        context->setRenderTarget(readTex->asRenderTarget());
225        paint.colorStage(0)->setEffect(pmToUPMEffect1);
226        context->drawRectToRect(paint, kDstRect, kSrcRect);
227
228        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
229
230        context->setRenderTarget(tempTex->asRenderTarget());
231        paint.colorStage(0)->setEffect(upmToPMEffect);
232        context->drawRectToRect(paint, kDstRect, kSrcRect);
233        context->setRenderTarget(readTex->asRenderTarget());
234        paint.colorStage(0)->setEffect(pmToUPMEffect2);
235        context->drawRectToRect(paint, kDstRect, kSrcRect);
236
237        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
238
239        failed = false;
240        for (int y = 0; y < 256 && !failed; ++y) {
241            for (int x = 0; x <= y; ++x) {
242                if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
243                    failed = true;
244                    break;
245                }
246            }
247        }
248    }
249    if (failed) {
250        *pmToUPMRule = kNone_PMConversion;
251        *upmToPMRule = kNone_PMConversion;
252    }
253}
254
255bool GrConfigConversionEffect::InstallEffect(GrTexture* texture,
256                                             bool swapRedAndBlue,
257                                             PMConversion pmConversion,
258                                             const SkMatrix& matrix,
259                                             GrEffectStage* stage) {
260    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
261        // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
262        // then we may pollute our texture cache with redundant shaders. So in the case that no
263        // conversions were requested we instead return a GrSingleTextureEffect.
264        stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix)), matrix)->unref();
265        return true;
266    } else {
267        if (kRGBA_8888_GrPixelConfig != texture->config() &&
268            kBGRA_8888_GrPixelConfig != texture->config() &&
269            kNone_PMConversion != pmConversion) {
270            // The PM conversions assume colors are 0..255
271            return false;
272        }
273        stage->setEffect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
274                                                               swapRedAndBlue,
275                                                               pmConversion, matrix)))->unref();
276        return true;
277    }
278}
279