blob: edb0aa58df9f6dac5bc57b2634fc02b154a2ff03 [file] [log] [blame]
bsalomon@google.coma04e8e82012-08-27 12:53:13 +00001/*
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"
bsalomon@google.comd698f772012-10-25 13:22:00 +00009#include "gl/GrGLEffect.h"
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000010
bsalomon@google.com22a800a2012-10-26 19:16:46 +000011class GrGLConfigConversionEffect : public GrGLEffect {
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000012public:
bsalomon@google.com396e61f2012-10-25 19:00:29 +000013 GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +000014 const GrEffect& s) : INHERITED (factory) {
bsalomon@google.com021fc732012-10-25 12:47:42 +000015 const GrConfigConversionEffect& effect = static_cast<const GrConfigConversionEffect&>(s);
16 fSwapRedAndBlue = effect.swapsRedAndBlue();
17 fPMConversion = effect.pmConversion();
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000018 }
19
bsalomon@google.com22a800a2012-10-26 19:16:46 +000020 virtual void emitCode(GrGLShaderBuilder* builder,
21 const GrEffect&,
22 EffectKey,
23 const char* vertexCoords,
24 const char* outputColor,
25 const char* inputColor,
26 const TextureSamplerArray& samplers) SK_OVERRIDE {
bsalomon@google.com868a8e72012-08-30 19:11:34 +000027 builder->fFSCode.appendf("\t\t%s = ", outputColor);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +000028 builder->appendTextureLookup(&builder->fFSCode, samplers[0]);
bsalomon@google.com2d8edaf2012-09-07 14:47:31 +000029 builder->fFSCode.append(";\n");
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000030 if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
31 GrAssert(fSwapRedAndBlue);
bsalomon@google.com868a8e72012-08-30 19:11:34 +000032 builder->fFSCode.appendf("\t%s = %s.bgra;\n", outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000033 } else {
34 const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb";
35 switch (fPMConversion) {
36 case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
37 builder->fFSCode.appendf(
bsalomon@google.com868a8e72012-08-30 19:11:34 +000038 "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
39 outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000040 break;
41 case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
42 builder->fFSCode.appendf(
bsalomon@google.com868a8e72012-08-30 19:11:34 +000043 "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
44 outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000045 break;
46 case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
bsalomon@google.com868a8e72012-08-30 19:11:34 +000047 builder->fFSCode.appendf("\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",
48 outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000049 break;
50 case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
bsalomon@google.com868a8e72012-08-30 19:11:34 +000051 builder->fFSCode.appendf("\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",
52 outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000053 break;
robertphillips@google.com2af1b182012-08-28 11:23:09 +000054 default:
55 GrCrash("Unknown conversion op.");
56 break;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000057 }
58 }
bsalomon@google.com868a8e72012-08-30 19:11:34 +000059 GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000060 }
61
bsalomon@google.com46fba0d2012-10-25 21:42:05 +000062 static inline EffectKey GenKey(const GrEffect& s, const GrGLCaps&) {
bsalomon@google.com021fc732012-10-25 12:47:42 +000063 const GrConfigConversionEffect& effect = static_cast<const GrConfigConversionEffect&>(s);
64 return static_cast<int>(effect.swapsRedAndBlue()) | (effect.pmConversion() << 1);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000065 }
66
67private:
68 bool fSwapRedAndBlue;
69 GrConfigConversionEffect::PMConversion fPMConversion;
70
bsalomon@google.com22a800a2012-10-26 19:16:46 +000071 typedef GrGLEffect INHERITED;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000072
73};
74
75///////////////////////////////////////////////////////////////////////////////
76
77GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
78 bool swapRedAndBlue,
79 PMConversion pmConversion)
80 : GrSingleTextureEffect(texture)
81 , fSwapRedAndBlue(swapRedAndBlue)
82 , fPMConversion(pmConversion) {
83 GrAssert(kRGBA_8888_GrPixelConfig == texture->config() ||
84 kBGRA_8888_GrPixelConfig == texture->config());
85 // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
86 GrAssert(swapRedAndBlue || kNone_PMConversion != pmConversion);
87}
88
bsalomon@google.com396e61f2012-10-25 19:00:29 +000089const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
90 return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000091}
92
bsalomon@google.coma469c282012-10-24 18:28:34 +000093bool GrConfigConversionEffect::isEqual(const GrEffect& s) const {
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000094 const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
95 return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
96}
97
98///////////////////////////////////////////////////////////////////////////////
99
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000100GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000101
bsalomon@google.coma469c282012-10-24 18:28:34 +0000102GrEffect* GrConfigConversionEffect::TestCreate(SkRandom* random,
103 GrContext* context,
104 GrTexture* textures[]) {
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000105 PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
106 bool swapRB;
107 if (kNone_PMConversion == pmConv) {
108 swapRB = true;
109 } else {
110 swapRB = random->nextBool();
111 }
112 return SkNEW_ARGS(GrConfigConversionEffect,
bsalomon@google.com6f261be2012-10-24 19:07:10 +0000113 (textures[GrEffectUnitTest::kSkiaPMTextureIdx], swapRB, pmConv));
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000114}
115
116///////////////////////////////////////////////////////////////////////////////
117void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
118 PMConversion* pmToUPMRule,
119 PMConversion* upmToPMRule) {
120 *pmToUPMRule = kNone_PMConversion;
121 *upmToPMRule = kNone_PMConversion;
122 SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
123 uint32_t* srcData = data.get();
124 uint32_t* firstRead = data.get() + 256 * 256;
125 uint32_t* secondRead = data.get() + 2 * 256 * 256;
126
127 // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
128 // values in row y. We set r,g, and b to the same value since they are handled identically.
129 for (int y = 0; y < 256; ++y) {
130 for (int x = 0; x < 256; ++x) {
131 uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
132 color[3] = y;
133 color[2] = GrMin(x, y);
134 color[1] = GrMin(x, y);
135 color[0] = GrMin(x, y);
136 }
137 }
138
139 GrTextureDesc desc;
140 desc.fFlags = kRenderTarget_GrTextureFlagBit |
141 kNoStencil_GrTextureFlagBit;
142 desc.fWidth = 256;
143 desc.fHeight = 256;
144 desc.fConfig = kRGBA_8888_GrPixelConfig;
145
146 SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
147 if (!readTex.get()) {
148 return;
149 }
150 SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
151 if (!tempTex.get()) {
152 return;
153 }
154 desc.fFlags = kNone_GrTextureFlags;
155 SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
156 if (!dataTex.get()) {
157 return;
158 }
159
160 static const PMConversion kConversionRules[][2] = {
161 {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
162 {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
163 };
164
165 GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
166
167 bool failed = true;
168
169 for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
170 *pmToUPMRule = kConversionRules[i][0];
171 *upmToPMRule = kConversionRules[i][1];
172
173 static const GrRect kDstRect = GrRect::MakeWH(GrIntToScalar(256), GrIntToScalar(256));
174 static const GrRect kSrcRect = GrRect::MakeWH(GR_Scalar1, GR_Scalar1);
175 // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
176 // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
177 // We then verify that two reads produced the same values.
178
179 GrPaint paint;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000180
bsalomon@google.com021fc732012-10-25 12:47:42 +0000181 SkAutoTUnref<GrEffect> pmToUPMEffect1(SkNEW_ARGS(GrConfigConversionEffect,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000182 (dataTex, false, *pmToUPMRule)));
bsalomon@google.com021fc732012-10-25 12:47:42 +0000183 SkAutoTUnref<GrEffect> upmToPMEffect(SkNEW_ARGS(GrConfigConversionEffect,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000184 (readTex, false, *upmToPMRule)));
bsalomon@google.com021fc732012-10-25 12:47:42 +0000185 SkAutoTUnref<GrEffect> pmToUPMEffect2(SkNEW_ARGS(GrConfigConversionEffect,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000186 (tempTex, false, *pmToUPMRule)));
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000187
188 context->setRenderTarget(readTex->asRenderTarget());
bsalomon@google.com08283af2012-10-26 13:01:20 +0000189 paint.colorStage(0)->setEffect(pmToUPMEffect1);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000190 context->drawRectToRect(paint, kDstRect, kSrcRect);
191
192 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
193
194 context->setRenderTarget(tempTex->asRenderTarget());
bsalomon@google.com08283af2012-10-26 13:01:20 +0000195 paint.colorStage(0)->setEffect(upmToPMEffect);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000196 context->drawRectToRect(paint, kDstRect, kSrcRect);
197 context->setRenderTarget(readTex->asRenderTarget());
bsalomon@google.com08283af2012-10-26 13:01:20 +0000198 paint.colorStage(0)->setEffect(pmToUPMEffect2);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000199 context->drawRectToRect(paint, kDstRect, kSrcRect);
200
201 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
202
203 failed = false;
204 for (int y = 0; y < 256 && !failed; ++y) {
205 for (int x = 0; x <= y; ++x) {
206 if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
207 failed = true;
208 break;
209 }
210 }
211 }
212 }
213 if (failed) {
214 *pmToUPMRule = kNone_PMConversion;
215 *upmToPMRule = kNone_PMConversion;
216 }
217}
218
bsalomon@google.coma469c282012-10-24 18:28:34 +0000219GrEffect* GrConfigConversionEffect::Create(GrTexture* texture,
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000220 bool swapRedAndBlue,
221 PMConversion pmConversion) {
222 if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
223 // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
224 // then we may pollute our texture cache with redundant shaders. So in the case that no
225 // conversions were requested we instead return a GrSingleTextureEffect.
226 return SkNEW_ARGS(GrSingleTextureEffect, (texture));
227 } else {
228 if (kRGBA_8888_GrPixelConfig != texture->config() &&
229 kBGRA_8888_GrPixelConfig != texture->config() &&
230 kNone_PMConversion != pmConversion) {
231 // The PM conversions assume colors are 0..255
232 return NULL;
233 }
234 return SkNEW_ARGS(GrConfigConversionEffect, (texture, swapRedAndBlue, pmConversion));
235 }
236}