1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Bilinear image comparison.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuBilinearImageCompare.hpp"
25 #include "tcuTexture.hpp"
26 #include "tcuTextureUtil.hpp"
27 #include "tcuRGBA.hpp"
28
29 namespace tcu
30 {
31
32 namespace
33 {
34
35 enum
36 {
37 NUM_SUBPIXEL_BITS = 8 //!< Number of subpixel bits used when doing bilinear interpolation.
38 };
39
40 // \todo [2013-03-26 pyry] Big-endian architectures?
41
42 template<int Channel>
getChannel(deUint32 color)43 static inline deUint8 getChannel (deUint32 color)
44 {
45 return (deUint8)((color >> (Channel*8)) & 0xff);
46 }
47
readRGBA8Raw(const ConstPixelBufferAccess & src,deUint32 x,deUint32 y)48 inline deUint32 readRGBA8Raw (const ConstPixelBufferAccess& src, deUint32 x, deUint32 y)
49 {
50 return *(const deUint32*)((const deUint8*)src.getDataPtr() + y*src.getRowPitch() + x*4);
51 }
52
readRGBA8(const ConstPixelBufferAccess & src,deUint32 x,deUint32 y)53 inline RGBA readRGBA8 (const ConstPixelBufferAccess& src, deUint32 x, deUint32 y)
54 {
55 deUint32 raw = readRGBA8Raw(src, x, y);
56 deUint32 res = 0;
57
58 res |= getChannel<0>(raw) << RGBA::RED_SHIFT;
59 res |= getChannel<1>(raw) << RGBA::GREEN_SHIFT;
60 res |= getChannel<2>(raw) << RGBA::BLUE_SHIFT;
61 res |= getChannel<3>(raw) << RGBA::ALPHA_SHIFT;
62
63 return RGBA(res);
64 }
65
interpolateChannel(deUint32 fx1,deUint32 fy1,deUint8 p00,deUint8 p01,deUint8 p10,deUint8 p11)66 inline deUint8 interpolateChannel (deUint32 fx1, deUint32 fy1, deUint8 p00, deUint8 p01, deUint8 p10, deUint8 p11)
67 {
68 const deUint32 fx0 = (1u<<NUM_SUBPIXEL_BITS) - fx1;
69 const deUint32 fy0 = (1u<<NUM_SUBPIXEL_BITS) - fy1;
70 const deUint32 half = 1u<<(NUM_SUBPIXEL_BITS*2 - 1);
71 const deUint32 sum = fx0*fy0*p00 + fx1*fy0*p10 + fx0*fy1*p01 + fx1*fy1*p11;
72 const deUint32 rounded = (sum + half) >> (NUM_SUBPIXEL_BITS*2);
73
74 DE_ASSERT(de::inRange<deUint32>(rounded, 0, 0xff));
75 return (deUint8)rounded;
76 }
77
bilinearSampleRGBA8(const ConstPixelBufferAccess & access,deUint32 u,deUint32 v)78 RGBA bilinearSampleRGBA8 (const ConstPixelBufferAccess& access, deUint32 u, deUint32 v)
79 {
80 deUint32 x0 = u>>NUM_SUBPIXEL_BITS;
81 deUint32 y0 = v>>NUM_SUBPIXEL_BITS;
82 deUint32 x1 = x0+1; //de::min(x0+1, (deUint32)(access.getWidth()-1));
83 deUint32 y1 = y0+1; //de::min(y0+1, (deUint32)(access.getHeight()-1));
84
85 DE_ASSERT(x1 < (deUint32)access.getWidth());
86 DE_ASSERT(y1 < (deUint32)access.getHeight());
87
88 deUint32 fx1 = u-(x0<<NUM_SUBPIXEL_BITS);
89 deUint32 fy1 = v-(y0<<NUM_SUBPIXEL_BITS);
90
91 deUint32 p00 = readRGBA8Raw(access, x0, y0);
92 deUint32 p10 = readRGBA8Raw(access, x1, y0);
93 deUint32 p01 = readRGBA8Raw(access, x0, y1);
94 deUint32 p11 = readRGBA8Raw(access, x1, y1);
95
96 deUint32 res = 0;
97
98 res |= interpolateChannel(fx1, fy1, getChannel<0>(p00), getChannel<0>(p01), getChannel<0>(p10), getChannel<0>(p11)) << RGBA::RED_SHIFT;
99 res |= interpolateChannel(fx1, fy1, getChannel<1>(p00), getChannel<1>(p01), getChannel<1>(p10), getChannel<1>(p11)) << RGBA::GREEN_SHIFT;
100 res |= interpolateChannel(fx1, fy1, getChannel<2>(p00), getChannel<2>(p01), getChannel<2>(p10), getChannel<2>(p11)) << RGBA::BLUE_SHIFT;
101 res |= interpolateChannel(fx1, fy1, getChannel<3>(p00), getChannel<3>(p01), getChannel<3>(p10), getChannel<3>(p11)) << RGBA::ALPHA_SHIFT;
102
103 return RGBA(res);
104 }
105
comparePixelRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const RGBA threshold,int x,int y)106 bool comparePixelRGBA8 (const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const RGBA threshold, int x, int y)
107 {
108 const RGBA resPix = readRGBA8(result, (deUint32)x, (deUint32)y);
109
110 // Step 1: Compare result pixel to 3x3 neighborhood pixels in reference.
111 {
112 const deUint32 x0 = (deUint32)de::max(x-1, 0);
113 const deUint32 x1 = (deUint32)x;
114 const deUint32 x2 = (deUint32)de::min(x+1, reference.getWidth()-1);
115 const deUint32 y0 = (deUint32)de::max(y-1, 0);
116 const deUint32 y1 = (deUint32)y;
117 const deUint32 y2 = (deUint32)de::min(y+1, reference.getHeight()-1);
118
119 if (compareThreshold(resPix, readRGBA8(reference, x1, y1), threshold) ||
120 compareThreshold(resPix, readRGBA8(reference, x0, y1), threshold) ||
121 compareThreshold(resPix, readRGBA8(reference, x2, y1), threshold) ||
122 compareThreshold(resPix, readRGBA8(reference, x0, y0), threshold) ||
123 compareThreshold(resPix, readRGBA8(reference, x1, y0), threshold) ||
124 compareThreshold(resPix, readRGBA8(reference, x2, y0), threshold) ||
125 compareThreshold(resPix, readRGBA8(reference, x0, y2), threshold) ||
126 compareThreshold(resPix, readRGBA8(reference, x1, y2), threshold) ||
127 compareThreshold(resPix, readRGBA8(reference, x2, y2), threshold))
128 return true;
129 }
130
131 // Step 2: Compare using bilinear sampling.
132 {
133 // \todo [pyry] Optimize sample positions!
134 static const deUint32 s_offsets[][2] =
135 {
136 { 226, 186 },
137 { 335, 235 },
138 { 279, 334 },
139 { 178, 272 },
140 { 112, 202 },
141 { 306, 117 },
142 { 396, 299 },
143 { 206, 382 },
144 { 146, 96 },
145 { 423, 155 },
146 { 361, 412 },
147 { 84, 339 },
148 { 48, 130 },
149 { 367, 43 },
150 { 455, 367 },
151 { 105, 439 },
152 { 83, 46 },
153 { 217, 24 },
154 { 461, 71 },
155 { 450, 459 },
156 { 239, 469 },
157 { 67, 267 },
158 { 459, 255 },
159 { 13, 416 },
160 { 10, 192 },
161 { 141, 502 },
162 { 503, 304 },
163 { 380, 506 }
164 };
165
166 for (int sampleNdx = 0; sampleNdx < DE_LENGTH_OF_ARRAY(s_offsets); sampleNdx++)
167 {
168 const int u = ((x-1)<<NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][0];
169 const int v = ((y-1)<<NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][1];
170
171 if (!de::inBounds(u, 0, (reference.getWidth()-1)<<NUM_SUBPIXEL_BITS) ||
172 !de::inBounds(v, 0, (reference.getHeight()-1)<<NUM_SUBPIXEL_BITS))
173 continue;
174
175 if (compareThreshold(resPix, bilinearSampleRGBA8(reference, (deUint32)u, (deUint32)v), threshold))
176 return true;
177 }
178 }
179
180 return false;
181 }
182
bilinearCompareRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)183 bool bilinearCompareRGBA8 (const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const PixelBufferAccess& errorMask, const RGBA threshold)
184 {
185 DE_ASSERT(reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
186 result.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8));
187
188 // Clear error mask first to green (faster this way).
189 clear(errorMask, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
190
191 bool allOk = true;
192
193 for (int y = 0; y < reference.getHeight(); y++)
194 {
195 for (int x = 0; x < reference.getWidth(); x++)
196 {
197 if (!comparePixelRGBA8(reference, result, threshold, x, y) &&
198 !comparePixelRGBA8(result, reference, threshold, x, y))
199 {
200 allOk = false;
201 errorMask.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y);
202 }
203 }
204 }
205
206 return allOk;
207 }
208
209 } // anonymous
210
bilinearCompare(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)211 bool bilinearCompare (const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const PixelBufferAccess& errorMask, const RGBA threshold)
212 {
213 DE_ASSERT(reference.getWidth() == result.getWidth() &&
214 reference.getHeight() == result.getHeight() &&
215 reference.getDepth() == result.getDepth() &&
216 reference.getFormat() == result.getFormat());
217 DE_ASSERT(reference.getWidth() == errorMask.getWidth() &&
218 reference.getHeight() == errorMask.getHeight() &&
219 reference.getDepth() == errorMask.getDepth());
220
221 if (reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
222 return bilinearCompareRGBA8(reference, result, errorMask, threshold);
223 else
224 throw InternalError("Unsupported format for bilinear comparison");
225 }
226
227 } // tcu
228