Home | History | Annotate | Download | only in effects
      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 
     16 class GrGLConfigConversionEffect : public GrGLEffect {
     17 public:
     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 
     96 private:
     97     bool                                    fSwapRedAndBlue;
     98     GrConfigConversionEffect::PMConversion  fPMConversion;
     99     GrGLEffectMatrix                        fEffectMatrix;
    100 
    101     typedef GrGLEffect INHERITED;
    102 
    103 };
    104 
    105 ///////////////////////////////////////////////////////////////////////////////
    106 
    107 GrConfigConversionEffect::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 
    120 const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
    121     return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
    122 }
    123 
    124 bool 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 
    131 void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
    132                                                           uint32_t* validFlags) const {
    133     this->updateConstantColorComponentsForModulation(color, validFlags);
    134 }
    135 
    136 ///////////////////////////////////////////////////////////////////////////////
    137 
    138 GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
    139 
    140 GrEffectRef* 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 ///////////////////////////////////////////////////////////////////////////////
    160 void 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 
    274 const 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