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