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