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