1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/effects/GrConfigConversionEffect.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,277 @@ 1.4 +/* 1.5 + * Copyright 2012 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "GrConfigConversionEffect.h" 1.12 +#include "GrContext.h" 1.13 +#include "GrTBackendEffectFactory.h" 1.14 +#include "GrSimpleTextureEffect.h" 1.15 +#include "gl/GrGLEffect.h" 1.16 +#include "SkMatrix.h" 1.17 + 1.18 +class GrGLConfigConversionEffect : public GrGLEffect { 1.19 +public: 1.20 + GrGLConfigConversionEffect(const GrBackendEffectFactory& factory, 1.21 + const GrDrawEffect& drawEffect) 1.22 + : INHERITED (factory) { 1.23 + const GrConfigConversionEffect& effect = drawEffect.castEffect<GrConfigConversionEffect>(); 1.24 + fSwapRedAndBlue = effect.swapsRedAndBlue(); 1.25 + fPMConversion = effect.pmConversion(); 1.26 + } 1.27 + 1.28 + virtual void emitCode(GrGLShaderBuilder* builder, 1.29 + const GrDrawEffect&, 1.30 + EffectKey key, 1.31 + const char* outputColor, 1.32 + const char* inputColor, 1.33 + const TransformedCoordsArray& coords, 1.34 + const TextureSamplerArray& samplers) SK_OVERRIDE { 1.35 + builder->fsCodeAppendf("\t\t%s = ", outputColor); 1.36 + builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type()); 1.37 + builder->fsCodeAppend(";\n"); 1.38 + if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) { 1.39 + SkASSERT(fSwapRedAndBlue); 1.40 + builder->fsCodeAppendf("\t%s = %s.bgra;\n", outputColor, outputColor); 1.41 + } else { 1.42 + const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb"; 1.43 + switch (fPMConversion) { 1.44 + case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion: 1.45 + builder->fsCodeAppendf( 1.46 + "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n", 1.47 + outputColor, outputColor, swiz, outputColor, outputColor); 1.48 + break; 1.49 + case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion: 1.50 + // Add a compensation(0.001) here to avoid the side effect of the floor operation. 1.51 + // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0 1.52 + // is less than the integer value converted from %s.r by 1 when the %s.r is 1.53 + // converted from the integer value 2^n, such as 1, 2, 4, 8, etc. 1.54 + builder->fsCodeAppendf( 1.55 + "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0 + 0.001) / 255.0, %s.a);\n", 1.56 + outputColor, outputColor, swiz, outputColor, outputColor); 1.57 + break; 1.58 + case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion: 1.59 + 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", 1.60 + outputColor, outputColor, outputColor, swiz, outputColor, outputColor); 1.61 + break; 1.62 + case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion: 1.63 + 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", 1.64 + outputColor, outputColor, outputColor, swiz, outputColor, outputColor); 1.65 + break; 1.66 + default: 1.67 + GrCrash("Unknown conversion op."); 1.68 + break; 1.69 + } 1.70 + } 1.71 + SkString modulate; 1.72 + GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor); 1.73 + builder->fsCodeAppend(modulate.c_str()); 1.74 + } 1.75 + 1.76 + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 1.77 + const GrConfigConversionEffect& conv = drawEffect.castEffect<GrConfigConversionEffect>(); 1.78 + return static_cast<EffectKey>(conv.swapsRedAndBlue()) | (conv.pmConversion() << 1); 1.79 + } 1.80 + 1.81 +private: 1.82 + bool fSwapRedAndBlue; 1.83 + GrConfigConversionEffect::PMConversion fPMConversion; 1.84 + 1.85 + typedef GrGLEffect INHERITED; 1.86 + 1.87 +}; 1.88 + 1.89 +/////////////////////////////////////////////////////////////////////////////// 1.90 + 1.91 +GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture, 1.92 + bool swapRedAndBlue, 1.93 + PMConversion pmConversion, 1.94 + const SkMatrix& matrix) 1.95 + : GrSingleTextureEffect(texture, matrix) 1.96 + , fSwapRedAndBlue(swapRedAndBlue) 1.97 + , fPMConversion(pmConversion) { 1.98 + SkASSERT(kRGBA_8888_GrPixelConfig == texture->config() || 1.99 + kBGRA_8888_GrPixelConfig == texture->config()); 1.100 + // Why did we pollute our texture cache instead of using a GrSingleTextureEffect? 1.101 + SkASSERT(swapRedAndBlue || kNone_PMConversion != pmConversion); 1.102 +} 1.103 + 1.104 +const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const { 1.105 + return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance(); 1.106 +} 1.107 + 1.108 +bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const { 1.109 + const GrConfigConversionEffect& other = CastEffect<GrConfigConversionEffect>(s); 1.110 + return this->texture(0) == s.texture(0) && 1.111 + other.fSwapRedAndBlue == fSwapRedAndBlue && 1.112 + other.fPMConversion == fPMConversion; 1.113 +} 1.114 + 1.115 +void GrConfigConversionEffect::getConstantColorComponents(GrColor* color, 1.116 + uint32_t* validFlags) const { 1.117 + this->updateConstantColorComponentsForModulation(color, validFlags); 1.118 +} 1.119 + 1.120 +/////////////////////////////////////////////////////////////////////////////// 1.121 + 1.122 +GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect); 1.123 + 1.124 +GrEffectRef* GrConfigConversionEffect::TestCreate(SkRandom* random, 1.125 + GrContext*, 1.126 + const GrDrawTargetCaps&, 1.127 + GrTexture* textures[]) { 1.128 + PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt)); 1.129 + bool swapRB; 1.130 + if (kNone_PMConversion == pmConv) { 1.131 + swapRB = true; 1.132 + } else { 1.133 + swapRB = random->nextBool(); 1.134 + } 1.135 + AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, 1.136 + (textures[GrEffectUnitTest::kSkiaPMTextureIdx], 1.137 + swapRB, 1.138 + pmConv, 1.139 + GrEffectUnitTest::TestMatrix(random)))); 1.140 + return CreateEffectRef(effect); 1.141 +} 1.142 + 1.143 +/////////////////////////////////////////////////////////////////////////////// 1.144 +void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context, 1.145 + PMConversion* pmToUPMRule, 1.146 + PMConversion* upmToPMRule) { 1.147 + *pmToUPMRule = kNone_PMConversion; 1.148 + *upmToPMRule = kNone_PMConversion; 1.149 + SkAutoTMalloc<uint32_t> data(256 * 256 * 3); 1.150 + uint32_t* srcData = data.get(); 1.151 + uint32_t* firstRead = data.get() + 256 * 256; 1.152 + uint32_t* secondRead = data.get() + 2 * 256 * 256; 1.153 + 1.154 + // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate 1.155 + // values in row y. We set r,g, and b to the same value since they are handled identically. 1.156 + for (int y = 0; y < 256; ++y) { 1.157 + for (int x = 0; x < 256; ++x) { 1.158 + uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]); 1.159 + color[3] = y; 1.160 + color[2] = GrMin(x, y); 1.161 + color[1] = GrMin(x, y); 1.162 + color[0] = GrMin(x, y); 1.163 + } 1.164 + } 1.165 + 1.166 + GrTextureDesc desc; 1.167 + desc.fFlags = kRenderTarget_GrTextureFlagBit | 1.168 + kNoStencil_GrTextureFlagBit; 1.169 + desc.fWidth = 256; 1.170 + desc.fHeight = 256; 1.171 + desc.fConfig = kRGBA_8888_GrPixelConfig; 1.172 + 1.173 + SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0)); 1.174 + if (!readTex.get()) { 1.175 + return; 1.176 + } 1.177 + SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0)); 1.178 + if (!tempTex.get()) { 1.179 + return; 1.180 + } 1.181 + desc.fFlags = kNone_GrTextureFlags; 1.182 + SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0)); 1.183 + if (!dataTex.get()) { 1.184 + return; 1.185 + } 1.186 + 1.187 + static const PMConversion kConversionRules[][2] = { 1.188 + {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion}, 1.189 + {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion}, 1.190 + }; 1.191 + 1.192 + GrContext::AutoWideOpenIdentityDraw awoid(context, NULL); 1.193 + 1.194 + bool failed = true; 1.195 + 1.196 + for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) { 1.197 + *pmToUPMRule = kConversionRules[i][0]; 1.198 + *upmToPMRule = kConversionRules[i][1]; 1.199 + 1.200 + static const SkRect kDstRect = SkRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256)); 1.201 + static const SkRect kSrcRect = SkRect::MakeWH(SK_Scalar1, SK_Scalar1); 1.202 + // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw 1.203 + // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. 1.204 + // We then verify that two reads produced the same values. 1.205 + 1.206 + AutoEffectUnref pmToUPM1(SkNEW_ARGS(GrConfigConversionEffect, (dataTex, 1.207 + false, 1.208 + *pmToUPMRule, 1.209 + SkMatrix::I()))); 1.210 + AutoEffectUnref upmToPM(SkNEW_ARGS(GrConfigConversionEffect, (readTex, 1.211 + false, 1.212 + *upmToPMRule, 1.213 + SkMatrix::I()))); 1.214 + AutoEffectUnref pmToUPM2(SkNEW_ARGS(GrConfigConversionEffect, (tempTex, 1.215 + false, 1.216 + *pmToUPMRule, 1.217 + SkMatrix::I()))); 1.218 + 1.219 + SkAutoTUnref<GrEffectRef> pmToUPMEffect1(CreateEffectRef(pmToUPM1)); 1.220 + SkAutoTUnref<GrEffectRef> upmToPMEffect(CreateEffectRef(upmToPM)); 1.221 + SkAutoTUnref<GrEffectRef> pmToUPMEffect2(CreateEffectRef(pmToUPM2)); 1.222 + 1.223 + context->setRenderTarget(readTex->asRenderTarget()); 1.224 + GrPaint paint1; 1.225 + paint1.addColorEffect(pmToUPMEffect1); 1.226 + context->drawRectToRect(paint1, kDstRect, kSrcRect); 1.227 + 1.228 + readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead); 1.229 + 1.230 + context->setRenderTarget(tempTex->asRenderTarget()); 1.231 + GrPaint paint2; 1.232 + paint2.addColorEffect(upmToPMEffect); 1.233 + context->drawRectToRect(paint2, kDstRect, kSrcRect); 1.234 + context->setRenderTarget(readTex->asRenderTarget()); 1.235 + 1.236 + GrPaint paint3; 1.237 + paint3.addColorEffect(pmToUPMEffect2); 1.238 + context->drawRectToRect(paint3, kDstRect, kSrcRect); 1.239 + 1.240 + readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead); 1.241 + 1.242 + failed = false; 1.243 + for (int y = 0; y < 256 && !failed; ++y) { 1.244 + for (int x = 0; x <= y; ++x) { 1.245 + if (firstRead[256 * y + x] != secondRead[256 * y + x]) { 1.246 + failed = true; 1.247 + break; 1.248 + } 1.249 + } 1.250 + } 1.251 + } 1.252 + if (failed) { 1.253 + *pmToUPMRule = kNone_PMConversion; 1.254 + *upmToPMRule = kNone_PMConversion; 1.255 + } 1.256 +} 1.257 + 1.258 +const GrEffectRef* GrConfigConversionEffect::Create(GrTexture* texture, 1.259 + bool swapRedAndBlue, 1.260 + PMConversion pmConversion, 1.261 + const SkMatrix& matrix) { 1.262 + if (!swapRedAndBlue && kNone_PMConversion == pmConversion) { 1.263 + // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect 1.264 + // then we may pollute our texture cache with redundant shaders. So in the case that no 1.265 + // conversions were requested we instead return a GrSimpleTextureEffect. 1.266 + return GrSimpleTextureEffect::Create(texture, matrix); 1.267 + } else { 1.268 + if (kRGBA_8888_GrPixelConfig != texture->config() && 1.269 + kBGRA_8888_GrPixelConfig != texture->config() && 1.270 + kNone_PMConversion != pmConversion) { 1.271 + // The PM conversions assume colors are 0..255 1.272 + return NULL; 1.273 + } 1.274 + AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, (texture, 1.275 + swapRedAndBlue, 1.276 + pmConversion, 1.277 + matrix))); 1.278 + return CreateEffectRef(effect); 1.279 + } 1.280 +}