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