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