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