blob: a5385ac999bfa0c4f114302f36987a3ff2abd5d9 [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"
9#include "gl/GrGLProgramStage.h"
10
11class GrGLConfigConversionEffect : public GrGLProgramStage {
12public:
13 GrGLConfigConversionEffect(const GrProgramStageFactory& factory,
14 const GrCustomStage& s) : INHERITED (factory) {
15 const GrConfigConversionEffect& stage = static_cast<const GrConfigConversionEffect&>(s);
16 fSwapRedAndBlue = stage.swapsRedAndBlue();
17 fPMConversion = stage.pmConversion();
18 }
19
20 virtual void emitVS(GrGLShaderBuilder* builder,
21 const char* vertexCoords) SK_OVERRIDE { }
22 virtual void emitFS(GrGLShaderBuilder* builder,
23 const char* outputColor,
24 const char* inputColor,
bsalomon@google.comf06df1b2012-09-06 20:22:31 +000025 const TextureSamplerArray& samplers) SK_OVERRIDE {
bsalomon@google.com868a8e72012-08-30 19:11:34 +000026 builder->fFSCode.appendf("\t\t%s = ", outputColor);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +000027 builder->appendTextureLookup(&builder->fFSCode, samplers[0]);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000028 if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
29 GrAssert(fSwapRedAndBlue);
bsalomon@google.com868a8e72012-08-30 19:11:34 +000030 builder->fFSCode.appendf("\t%s = %s.bgra;\n", outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000031 } else {
32 const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb";
33 switch (fPMConversion) {
34 case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
35 builder->fFSCode.appendf(
bsalomon@google.com868a8e72012-08-30 19:11:34 +000036 "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
37 outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000038 break;
39 case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
40 builder->fFSCode.appendf(
bsalomon@google.com868a8e72012-08-30 19:11:34 +000041 "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
42 outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000043 break;
44 case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
bsalomon@google.com868a8e72012-08-30 19:11:34 +000045 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",
46 outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000047 break;
48 case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
bsalomon@google.com868a8e72012-08-30 19:11:34 +000049 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",
50 outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000051 break;
robertphillips@google.com2af1b182012-08-28 11:23:09 +000052 default:
53 GrCrash("Unknown conversion op.");
54 break;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000055 }
56 }
bsalomon@google.com868a8e72012-08-30 19:11:34 +000057 GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000058 }
59
60 static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps&) {
61 const GrConfigConversionEffect& stage = static_cast<const GrConfigConversionEffect&>(s);
62 return static_cast<int>(stage.swapsRedAndBlue()) | (stage.pmConversion() << 1);
63 }
64
65private:
66 bool fSwapRedAndBlue;
67 GrConfigConversionEffect::PMConversion fPMConversion;
68
69 typedef GrGLProgramStage INHERITED;
70
71};
72
73///////////////////////////////////////////////////////////////////////////////
74
75GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
76 bool swapRedAndBlue,
77 PMConversion pmConversion)
78 : GrSingleTextureEffect(texture)
79 , fSwapRedAndBlue(swapRedAndBlue)
80 , fPMConversion(pmConversion) {
81 GrAssert(kRGBA_8888_GrPixelConfig == texture->config() ||
82 kBGRA_8888_GrPixelConfig == texture->config());
83 // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
84 GrAssert(swapRedAndBlue || kNone_PMConversion != pmConversion);
85}
86
87const GrProgramStageFactory& GrConfigConversionEffect::getFactory() const {
88 return GrTProgramStageFactory<GrConfigConversionEffect>::getInstance();
89}
90
91bool GrConfigConversionEffect::isEqual(const GrCustomStage& s) const {
92 const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
93 return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
94}
95
96///////////////////////////////////////////////////////////////////////////////
97
98GR_DEFINE_CUSTOM_STAGE_TEST(GrConfigConversionEffect);
99
100GrCustomStage* GrConfigConversionEffect::TestCreate(SkRandom* random,
101 GrContext* context,
102 GrTexture* textures[]) {
103 PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
104 bool swapRB;
105 if (kNone_PMConversion == pmConv) {
106 swapRB = true;
107 } else {
108 swapRB = random->nextBool();
109 }
110 return SkNEW_ARGS(GrConfigConversionEffect,
111 (textures[GrCustomStageUnitTest::kSkiaPMTextureIdx], swapRB, pmConv));
112}
113
114///////////////////////////////////////////////////////////////////////////////
115void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
116 PMConversion* pmToUPMRule,
117 PMConversion* upmToPMRule) {
118 *pmToUPMRule = kNone_PMConversion;
119 *upmToPMRule = kNone_PMConversion;
120 SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
121 uint32_t* srcData = data.get();
122 uint32_t* firstRead = data.get() + 256 * 256;
123 uint32_t* secondRead = data.get() + 2 * 256 * 256;
124
125 // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
126 // values in row y. We set r,g, and b to the same value since they are handled identically.
127 for (int y = 0; y < 256; ++y) {
128 for (int x = 0; x < 256; ++x) {
129 uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
130 color[3] = y;
131 color[2] = GrMin(x, y);
132 color[1] = GrMin(x, y);
133 color[0] = GrMin(x, y);
134 }
135 }
136
137 GrTextureDesc desc;
138 desc.fFlags = kRenderTarget_GrTextureFlagBit |
139 kNoStencil_GrTextureFlagBit;
140 desc.fWidth = 256;
141 desc.fHeight = 256;
142 desc.fConfig = kRGBA_8888_GrPixelConfig;
143
144 SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
145 if (!readTex.get()) {
146 return;
147 }
148 SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
149 if (!tempTex.get()) {
150 return;
151 }
152 desc.fFlags = kNone_GrTextureFlags;
153 SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
154 if (!dataTex.get()) {
155 return;
156 }
157
158 static const PMConversion kConversionRules[][2] = {
159 {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
160 {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
161 };
162
163 GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
164
165 bool failed = true;
166
167 for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
168 *pmToUPMRule = kConversionRules[i][0];
169 *upmToPMRule = kConversionRules[i][1];
170
171 static const GrRect kDstRect = GrRect::MakeWH(GrIntToScalar(256), GrIntToScalar(256));
172 static const GrRect kSrcRect = GrRect::MakeWH(GR_Scalar1, GR_Scalar1);
173 // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
174 // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
175 // We then verify that two reads produced the same values.
176
177 GrPaint paint;
178 paint.reset();
179
180 SkAutoTUnref<GrCustomStage> pmToUPMStage1(SkNEW_ARGS(GrConfigConversionEffect,
181 (dataTex, false, *pmToUPMRule)));
182 SkAutoTUnref<GrCustomStage> upmToPMStage(SkNEW_ARGS(GrConfigConversionEffect,
183 (readTex, false, *upmToPMRule)));
184 SkAutoTUnref<GrCustomStage> pmToUPMStage2(SkNEW_ARGS(GrConfigConversionEffect,
185 (tempTex, false, *pmToUPMRule)));
186
187 context->setRenderTarget(readTex->asRenderTarget());
188 paint.textureSampler(0)->setCustomStage(pmToUPMStage1);
189 context->drawRectToRect(paint, kDstRect, kSrcRect);
190
191 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
192
193 context->setRenderTarget(tempTex->asRenderTarget());
194 paint.textureSampler(0)->setCustomStage(upmToPMStage);
195 context->drawRectToRect(paint, kDstRect, kSrcRect);
196 context->setRenderTarget(readTex->asRenderTarget());
197 paint.textureSampler(0)->setCustomStage(pmToUPMStage2);
198 context->drawRectToRect(paint, kDstRect, kSrcRect);
199
200 readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
201
202 failed = false;
203 for (int y = 0; y < 256 && !failed; ++y) {
204 for (int x = 0; x <= y; ++x) {
205 if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
206 failed = true;
207 break;
208 }
209 }
210 }
211 }
212 if (failed) {
213 *pmToUPMRule = kNone_PMConversion;
214 *upmToPMRule = kNone_PMConversion;
215 }
216}
217
218GrCustomStage* GrConfigConversionEffect::Create(GrTexture* texture,
219 bool swapRedAndBlue,
220 PMConversion pmConversion) {
221 if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
222 // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
223 // then we may pollute our texture cache with redundant shaders. So in the case that no
224 // conversions were requested we instead return a GrSingleTextureEffect.
225 return SkNEW_ARGS(GrSingleTextureEffect, (texture));
226 } else {
227 if (kRGBA_8888_GrPixelConfig != texture->config() &&
228 kBGRA_8888_GrPixelConfig != texture->config() &&
229 kNone_PMConversion != pmConversion) {
230 // The PM conversions assume colors are 0..255
231 return NULL;
232 }
233 return SkNEW_ARGS(GrConfigConversionEffect, (texture, swapRedAndBlue, pmConversion));
234 }
235}