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