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