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