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