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 "GrDrawContext.h"
     11 #include "GrInvariantOutput.h"
     12 #include "GrSimpleTextureEffect.h"
     13 #include "SkMatrix.h"
     14 #include "glsl/GrGLSLFragmentProcessor.h"
     15 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     16 
     17 class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor {
     18 public:
     19     void emitCode(EmitArgs& args) override {
     20         const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
     21         const GrSwizzle& swizzle = cce.swizzle();
     22         GrConfigConversionEffect::PMConversion pmConversion = cce.pmConversion();
     23 
     24         // Using highp for GLES here in order to avoid some precision issues on specific GPUs.
     25         GrGLSLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
     26         SkString tmpDecl;
     27         tmpVar.appendDecl(args.fGLSLCaps, &tmpDecl);
     28 
     29         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     30 
     31         fragBuilder->codeAppendf("%s;", tmpDecl.c_str());
     32 
     33         fragBuilder->codeAppendf("%s = ", tmpVar.c_str());
     34         fragBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(),
     35                                        args.fCoords[0].getType());
     36         fragBuilder->codeAppend(";");
     37 
     38         if (GrConfigConversionEffect::kNone_PMConversion == pmConversion) {
     39             SkASSERT(GrSwizzle::RGBA() != swizzle);
     40             fragBuilder->codeAppendf("%s = %s.%s;", args.fOutputColor, tmpVar.c_str(),
     41                                      swizzle.c_str());
     42         } else {
     43             switch (pmConversion) {
     44                 case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
     45                     fragBuilder->codeAppendf(
     46                         "%s = vec4(ceil(%s.rgb * %s.a * 255.0) / 255.0, %s.a);",
     47                         tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
     48                     break;
     49                 case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
     50                     // Add a compensation(0.001) here to avoid the side effect of the floor operation.
     51                     // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
     52                     // is less than the integer value converted from  %s.r by 1 when the %s.r is
     53                     // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
     54                     fragBuilder->codeAppendf(
     55                         "%s = vec4(floor(%s.rgb * %s.a * 255.0 + 0.001) / 255.0, %s.a);",
     56                         tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
     57 
     58                     break;
     59                 case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
     60                     fragBuilder->codeAppendf(
     61                         "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
     62                         tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
     63                         tmpVar.c_str());
     64                     break;
     65                 case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
     66                     fragBuilder->codeAppendf(
     67                         "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
     68                         tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
     69                         tmpVar.c_str());
     70                     break;
     71                 default:
     72                     SkFAIL("Unknown conversion op.");
     73                     break;
     74             }
     75             fragBuilder->codeAppendf("%s = %s.%s;", args.fOutputColor, tmpVar.c_str(),
     76                                      swizzle.c_str());
     77         }
     78         SkString modulate;
     79         GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
     80         fragBuilder->codeAppend(modulate.c_str());
     81     }
     82 
     83     static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&,
     84                               GrProcessorKeyBuilder* b) {
     85         const GrConfigConversionEffect& cce = processor.cast<GrConfigConversionEffect>();
     86         uint32_t key = (cce.swizzle().asKey()) | (cce.pmConversion() << 16);
     87         b->add32(key);
     88     }
     89 
     90 private:
     91     typedef GrGLSLFragmentProcessor INHERITED;
     92 
     93 };
     94 
     95 ///////////////////////////////////////////////////////////////////////////////
     96 
     97 GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
     98                                                    const GrSwizzle& swizzle,
     99                                                    PMConversion pmConversion,
    100                                                    const SkMatrix& matrix)
    101     : INHERITED(texture, matrix)
    102     , fSwizzle(swizzle)
    103     , fPMConversion(pmConversion) {
    104     this->initClassID<GrConfigConversionEffect>();
    105     // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
    106     // conversion.
    107     SkASSERT((kRGBA_8888_GrPixelConfig == texture->config() ||
    108               kBGRA_8888_GrPixelConfig == texture->config()) ||
    109               kNone_PMConversion == pmConversion);
    110     // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
    111     SkASSERT(swizzle != GrSwizzle::RGBA() || kNone_PMConversion != pmConversion);
    112 }
    113 
    114 bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
    115     const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
    116     return other.fSwizzle == fSwizzle &&
    117            other.fPMConversion == fPMConversion;
    118 }
    119 
    120 void GrConfigConversionEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
    121     this->updateInvariantOutputForModulation(inout);
    122 }
    123 
    124 ///////////////////////////////////////////////////////////////////////////////
    125 
    126 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
    127 
    128 const GrFragmentProcessor* GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
    129     PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
    130     GrSwizzle swizzle;
    131     do {
    132         swizzle = GrSwizzle::CreateRandom(d->fRandom);
    133     } while (pmConv == kNone_PMConversion && swizzle == GrSwizzle::RGBA());
    134     return new GrConfigConversionEffect(d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx],
    135                                         swizzle, pmConv, GrTest::TestMatrix(d->fRandom));
    136 }
    137 
    138 ///////////////////////////////////////////////////////////////////////////////
    139 
    140 void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
    141                                                      GrProcessorKeyBuilder* b) const {
    142     GrGLConfigConversionEffect::GenKey(*this, caps, b);
    143 }
    144 
    145 GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
    146     return new GrGLConfigConversionEffect();
    147 }
    148 
    149 
    150 
    151 void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
    152                                                               PMConversion* pmToUPMRule,
    153                                                               PMConversion* upmToPMRule) {
    154     *pmToUPMRule = kNone_PMConversion;
    155     *upmToPMRule = kNone_PMConversion;
    156     SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
    157     uint32_t* srcData = data.get();
    158     uint32_t* firstRead = data.get() + 256 * 256;
    159     uint32_t* secondRead = data.get() + 2 * 256 * 256;
    160 
    161     // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
    162     // values in row y. We set r,g, and b to the same value since they are handled identically.
    163     for (int y = 0; y < 256; ++y) {
    164         for (int x = 0; x < 256; ++x) {
    165             uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
    166             color[3] = y;
    167             color[2] = SkTMin(x, y);
    168             color[1] = SkTMin(x, y);
    169             color[0] = SkTMin(x, y);
    170         }
    171     }
    172 
    173     GrSurfaceDesc desc;
    174     desc.fFlags = kRenderTarget_GrSurfaceFlag;
    175     desc.fWidth = 256;
    176     desc.fHeight = 256;
    177     desc.fConfig = kRGBA_8888_GrPixelConfig;
    178 
    179     SkAutoTUnref<GrTexture> readTex(context->textureProvider()->createTexture(
    180         desc, SkBudgeted::kYes, nullptr, 0));
    181     if (!readTex.get()) {
    182         return;
    183     }
    184     SkAutoTUnref<GrTexture> tempTex(context->textureProvider()->createTexture(
    185         desc, SkBudgeted::kYes, nullptr, 0));
    186     if (!tempTex.get()) {
    187         return;
    188     }
    189     desc.fFlags = kNone_GrSurfaceFlags;
    190     SkAutoTUnref<GrTexture> dataTex(context->textureProvider()->createTexture(
    191         desc, SkBudgeted::kYes, data, 0));
    192     if (!dataTex.get()) {
    193         return;
    194     }
    195 
    196     static const PMConversion kConversionRules[][2] = {
    197         {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
    198         {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
    199     };
    200 
    201     bool failed = true;
    202 
    203     for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) {
    204         *pmToUPMRule = kConversionRules[i][0];
    205         *upmToPMRule = kConversionRules[i][1];
    206 
    207         static const SkRect kDstRect = SkRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
    208         static const SkRect kSrcRect = SkRect::MakeWH(SK_Scalar1, SK_Scalar1);
    209         // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
    210         // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
    211         // We then verify that two reads produced the same values.
    212 
    213         GrPaint paint1;
    214         GrPaint paint2;
    215         GrPaint paint3;
    216         SkAutoTUnref<GrFragmentProcessor> pmToUPM1(new GrConfigConversionEffect(
    217                 dataTex, GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I()));
    218         SkAutoTUnref<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(
    219                 readTex, GrSwizzle::RGBA(), *upmToPMRule, SkMatrix::I()));
    220         SkAutoTUnref<GrFragmentProcessor> pmToUPM2(new GrConfigConversionEffect(
    221                 tempTex, GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I()));
    222 
    223         paint1.addColorFragmentProcessor(pmToUPM1);
    224         paint1.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
    225 
    226 
    227         SkAutoTUnref<GrDrawContext> readDrawContext(
    228                                     context->drawContext(readTex->asRenderTarget()));
    229         if (!readDrawContext) {
    230             failed = true;
    231             break;
    232         }
    233 
    234         readDrawContext->fillRectToRect(GrClip::WideOpen(),
    235                                         paint1,
    236                                         SkMatrix::I(),
    237                                         kDstRect,
    238                                         kSrcRect);
    239 
    240         readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
    241 
    242         paint2.addColorFragmentProcessor(upmToPM);
    243         paint2.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
    244 
    245         SkAutoTUnref<GrDrawContext> tempDrawContext(
    246                                     context->drawContext(tempTex->asRenderTarget()));
    247         if (!tempDrawContext) {
    248             failed = true;
    249             break;
    250         }
    251         tempDrawContext->fillRectToRect(GrClip::WideOpen(),
    252                                         paint2,
    253                                         SkMatrix::I(),
    254                                         kDstRect,
    255                                         kSrcRect);
    256 
    257         paint3.addColorFragmentProcessor(pmToUPM2);
    258         paint3.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
    259 
    260         readDrawContext.reset(context->drawContext(readTex->asRenderTarget()));
    261         if (!readDrawContext) {
    262             failed = true;
    263             break;
    264         }
    265 
    266         readDrawContext->fillRectToRect(GrClip::WideOpen(),
    267                                         paint3,
    268                                         SkMatrix::I(),
    269                                         kDstRect,
    270                                         kSrcRect);
    271 
    272         readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
    273 
    274         failed = false;
    275         for (int y = 0; y < 256 && !failed; ++y) {
    276             for (int x = 0; x <= y; ++x) {
    277                 if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
    278                     failed = true;
    279                     break;
    280                 }
    281             }
    282         }
    283     }
    284     if (failed) {
    285         *pmToUPMRule = kNone_PMConversion;
    286         *upmToPMRule = kNone_PMConversion;
    287     }
    288 }
    289 
    290 const GrFragmentProcessor* GrConfigConversionEffect::Create(GrTexture* texture,
    291                                                             const GrSwizzle& swizzle,
    292                                                             PMConversion pmConversion,
    293                                                             const SkMatrix& matrix) {
    294     if (swizzle == GrSwizzle::RGBA() && kNone_PMConversion == pmConversion) {
    295         // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect
    296         // then we may pollute our texture cache with redundant shaders. So in the case that no
    297         // conversions were requested we instead return a GrSimpleTextureEffect.
    298         return GrSimpleTextureEffect::Create(texture, matrix);
    299     } else {
    300         if (kRGBA_8888_GrPixelConfig != texture->config() &&
    301             kBGRA_8888_GrPixelConfig != texture->config() &&
    302             kNone_PMConversion != pmConversion) {
    303             // The PM conversions assume colors are 0..255
    304             return nullptr;
    305         }
    306         return new GrConfigConversionEffect(texture, swizzle, pmConversion, matrix);
    307     }
    308 }
    309