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