GrConfigConversionEffect.cpp revision dbe49f735484f8862e378b63d0a074a301093dd0
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
127GrEffect* 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    return SkNEW_ARGS(GrConfigConversionEffect, (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
138                                                 swapRB,
139                                                 pmConv,
140                                                 GrEffectUnitTest::TestMatrix(random)));
141}
142
143///////////////////////////////////////////////////////////////////////////////
144void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
145                                                              PMConversion* pmToUPMRule,
146                                                              PMConversion* upmToPMRule) {
147    *pmToUPMRule = kNone_PMConversion;
148    *upmToPMRule = kNone_PMConversion;
149    SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
150    uint32_t* srcData = data.get();
151    uint32_t* firstRead = data.get() + 256 * 256;
152    uint32_t* secondRead = data.get() + 2 * 256 * 256;
153
154    // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
155    // values in row y. We set r,g, and b to the same value since they are handled identically.
156    for (int y = 0; y < 256; ++y) {
157        for (int x = 0; x < 256; ++x) {
158            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
159            color[3] = y;
160            color[2] = GrMin(x, y);
161            color[1] = GrMin(x, y);
162            color[0] = GrMin(x, y);
163        }
164    }
165
166    GrTextureDesc desc;
167    desc.fFlags = kRenderTarget_GrTextureFlagBit |
168                  kNoStencil_GrTextureFlagBit;
169    desc.fWidth = 256;
170    desc.fHeight = 256;
171    desc.fConfig = kRGBA_8888_GrPixelConfig;
172
173    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
174    if (!readTex.get()) {
175        return;
176    }
177    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
178    if (!tempTex.get()) {
179        return;
180    }
181    desc.fFlags = kNone_GrTextureFlags;
182    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
183    if (!dataTex.get()) {
184        return;
185    }
186
187    static const PMConversion kConversionRules[][2] = {
188        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
189        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
190    };
191
192    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
193
194    bool failed = true;
195
196    for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
197        *pmToUPMRule = kConversionRules[i][0];
198        *upmToPMRule = kConversionRules[i][1];
199
200        static const GrRect kDstRect = GrRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
201        static const GrRect kSrcRect = GrRect::MakeWH(SK_Scalar1, SK_Scalar1);
202        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
203        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
204        // We then verify that two reads produced the same values.
205
206        GrPaint paint;
207        SkAutoTUnref<GrEffect> pmToUPMEffect1(SkNEW_ARGS(GrConfigConversionEffect,
208                                                        (dataTex,
209                                                         false,
210                                                         *pmToUPMRule,
211                                                         SkMatrix::I())));
212        SkAutoTUnref<GrEffect> upmToPMEffect(SkNEW_ARGS(GrConfigConversionEffect,
213                                                       (readTex,
214                                                        false,
215                                                        *upmToPMRule,
216                                                        SkMatrix::I())));
217        SkAutoTUnref<GrEffect> pmToUPMEffect2(SkNEW_ARGS(GrConfigConversionEffect,
218                                                        (tempTex,
219                                                         false,
220                                                         *pmToUPMRule,
221                                                         SkMatrix::I())));
222
223        context->setRenderTarget(readTex->asRenderTarget());
224        paint.colorStage(0)->setEffect(pmToUPMEffect1);
225        context->drawRectToRect(paint, kDstRect, kSrcRect);
226
227        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
228
229        context->setRenderTarget(tempTex->asRenderTarget());
230        paint.colorStage(0)->setEffect(upmToPMEffect);
231        context->drawRectToRect(paint, kDstRect, kSrcRect);
232        context->setRenderTarget(readTex->asRenderTarget());
233        paint.colorStage(0)->setEffect(pmToUPMEffect2);
234        context->drawRectToRect(paint, kDstRect, kSrcRect);
235
236        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
237
238        failed = false;
239        for (int y = 0; y < 256 && !failed; ++y) {
240            for (int x = 0; x <= y; ++x) {
241                if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
242                    failed = true;
243                    break;
244                }
245            }
246        }
247    }
248    if (failed) {
249        *pmToUPMRule = kNone_PMConversion;
250        *upmToPMRule = kNone_PMConversion;
251    }
252}
253
254bool GrConfigConversionEffect::InstallEffect(GrTexture* texture,
255                                             bool swapRedAndBlue,
256                                             PMConversion pmConversion,
257                                             const SkMatrix& matrix,
258                                             GrEffectStage* stage) {
259    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
260        // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
261        // then we may pollute our texture cache with redundant shaders. So in the case that no
262        // conversions were requested we instead return a GrSingleTextureEffect.
263        stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix)))->unref();
264        return true;
265    } else {
266        if (kRGBA_8888_GrPixelConfig != texture->config() &&
267            kBGRA_8888_GrPixelConfig != texture->config() &&
268            kNone_PMConversion != pmConversion) {
269            // The PM conversions assume colors are 0..255
270            return false;
271        }
272        stage->setEffect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
273                                                               swapRedAndBlue,
274                                                               pmConversion, matrix)))->unref();
275        return true;
276    }
277}
278