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 // \note Algorithm assumes that colors are packed to 32-bit values as dictated by
41 // tcu::RGBA::*_SHIFT values.
42
43 template <int Channel>
getChannel(uint32_t color)44 static inline uint8_t getChannel(uint32_t color)
45 {
46 return (uint8_t)((color >> (Channel * 8)) & 0xff);
47 }
48
49 #if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
readRGBA8Raw(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)50 inline uint32_t readRGBA8Raw(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
51 {
52 return *(const uint32_t *)((const uint8_t *)src.getDataPtr() + y * src.getRowPitch() + x * 4);
53 }
54 #else
readRGBA8Raw(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)55 inline uint32_t readRGBA8Raw(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
56 {
57 return deReverseBytes32(*(const uint32_t *)((const uint8_t *)src.getDataPtr() + y * src.getRowPitch() + x * 4));
58 }
59 #endif
60
readRGBA8(const ConstPixelBufferAccess & src,uint32_t x,uint32_t y)61 inline RGBA readRGBA8(const ConstPixelBufferAccess &src, uint32_t x, uint32_t y)
62 {
63 uint32_t raw = readRGBA8Raw(src, x, y);
64 uint32_t res = 0;
65
66 res |= getChannel<0>(raw) << RGBA::RED_SHIFT;
67 res |= getChannel<1>(raw) << RGBA::GREEN_SHIFT;
68 res |= getChannel<2>(raw) << RGBA::BLUE_SHIFT;
69 res |= getChannel<3>(raw) << RGBA::ALPHA_SHIFT;
70
71 return RGBA(res);
72 }
73
interpolateChannel(uint32_t fx1,uint32_t fy1,uint8_t p00,uint8_t p01,uint8_t p10,uint8_t p11)74 inline uint8_t interpolateChannel(uint32_t fx1, uint32_t fy1, uint8_t p00, uint8_t p01, uint8_t p10, uint8_t p11)
75 {
76 const uint32_t fx0 = (1u << NUM_SUBPIXEL_BITS) - fx1;
77 const uint32_t fy0 = (1u << NUM_SUBPIXEL_BITS) - fy1;
78 const uint32_t half = 1u << (NUM_SUBPIXEL_BITS * 2 - 1);
79 const uint32_t sum = fx0 * fy0 * p00 + fx1 * fy0 * p10 + fx0 * fy1 * p01 + fx1 * fy1 * p11;
80 const uint32_t rounded = (sum + half) >> (NUM_SUBPIXEL_BITS * 2);
81
82 DE_ASSERT(de::inRange<uint32_t>(rounded, 0, 0xff));
83 return (uint8_t)rounded;
84 }
85
bilinearSampleRGBA8(const ConstPixelBufferAccess & access,uint32_t u,uint32_t v)86 RGBA bilinearSampleRGBA8(const ConstPixelBufferAccess &access, uint32_t u, uint32_t v)
87 {
88 uint32_t x0 = u >> NUM_SUBPIXEL_BITS;
89 uint32_t y0 = v >> NUM_SUBPIXEL_BITS;
90 uint32_t x1 = x0 + 1; //de::min(x0+1, (uint32_t)(access.getWidth()-1));
91 uint32_t y1 = y0 + 1; //de::min(y0+1, (uint32_t)(access.getHeight()-1));
92
93 DE_ASSERT(x1 < (uint32_t)access.getWidth());
94 DE_ASSERT(y1 < (uint32_t)access.getHeight());
95
96 uint32_t fx1 = u - (x0 << NUM_SUBPIXEL_BITS);
97 uint32_t fy1 = v - (y0 << NUM_SUBPIXEL_BITS);
98
99 uint32_t p00 = readRGBA8Raw(access, x0, y0);
100 uint32_t p10 = readRGBA8Raw(access, x1, y0);
101 uint32_t p01 = readRGBA8Raw(access, x0, y1);
102 uint32_t p11 = readRGBA8Raw(access, x1, y1);
103
104 uint32_t res = 0;
105
106 res |= interpolateChannel(fx1, fy1, getChannel<0>(p00), getChannel<0>(p01), getChannel<0>(p10), getChannel<0>(p11))
107 << RGBA::RED_SHIFT;
108 res |= interpolateChannel(fx1, fy1, getChannel<1>(p00), getChannel<1>(p01), getChannel<1>(p10), getChannel<1>(p11))
109 << RGBA::GREEN_SHIFT;
110 res |= interpolateChannel(fx1, fy1, getChannel<2>(p00), getChannel<2>(p01), getChannel<2>(p10), getChannel<2>(p11))
111 << RGBA::BLUE_SHIFT;
112 res |= interpolateChannel(fx1, fy1, getChannel<3>(p00), getChannel<3>(p01), getChannel<3>(p10), getChannel<3>(p11))
113 << RGBA::ALPHA_SHIFT;
114
115 return RGBA(res);
116 }
117
comparePixelRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const RGBA threshold,int x,int y)118 bool comparePixelRGBA8(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
119 const RGBA threshold, int x, int y)
120 {
121 const RGBA resPix = readRGBA8(result, (uint32_t)x, (uint32_t)y);
122
123 // Step 1: Compare result pixel to 3x3 neighborhood pixels in reference.
124 {
125 const uint32_t x0 = (uint32_t)de::max(x - 1, 0);
126 const uint32_t x1 = (uint32_t)x;
127 const uint32_t x2 = (uint32_t)de::min(x + 1, reference.getWidth() - 1);
128 const uint32_t y0 = (uint32_t)de::max(y - 1, 0);
129 const uint32_t y1 = (uint32_t)y;
130 const uint32_t y2 = (uint32_t)de::min(y + 1, reference.getHeight() - 1);
131
132 if (compareThreshold(resPix, readRGBA8(reference, x1, y1), threshold) ||
133 compareThreshold(resPix, readRGBA8(reference, x0, y1), threshold) ||
134 compareThreshold(resPix, readRGBA8(reference, x2, y1), threshold) ||
135 compareThreshold(resPix, readRGBA8(reference, x0, y0), threshold) ||
136 compareThreshold(resPix, readRGBA8(reference, x1, y0), threshold) ||
137 compareThreshold(resPix, readRGBA8(reference, x2, y0), threshold) ||
138 compareThreshold(resPix, readRGBA8(reference, x0, y2), threshold) ||
139 compareThreshold(resPix, readRGBA8(reference, x1, y2), threshold) ||
140 compareThreshold(resPix, readRGBA8(reference, x2, y2), threshold))
141 return true;
142 }
143
144 // Step 2: Compare using bilinear sampling.
145 {
146 // \todo [pyry] Optimize sample positions!
147 static const uint32_t s_offsets[][2] = {{226, 186}, {335, 235}, {279, 334}, {178, 272}, {112, 202}, {306, 117},
148 {396, 299}, {206, 382}, {146, 96}, {423, 155}, {361, 412}, {84, 339},
149 {48, 130}, {367, 43}, {455, 367}, {105, 439}, {83, 46}, {217, 24},
150 {461, 71}, {450, 459}, {239, 469}, {67, 267}, {459, 255}, {13, 416},
151 {10, 192}, {141, 502}, {503, 304}, {380, 506}};
152
153 for (int sampleNdx = 0; sampleNdx < DE_LENGTH_OF_ARRAY(s_offsets); sampleNdx++)
154 {
155 const int u = (x << NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][0] - (1 << NUM_SUBPIXEL_BITS);
156 const int v = (y << NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][1] - (1 << NUM_SUBPIXEL_BITS);
157
158 if (!de::inBounds(u, 0, (reference.getWidth() - 1) << NUM_SUBPIXEL_BITS) ||
159 !de::inBounds(v, 0, (reference.getHeight() - 1) << NUM_SUBPIXEL_BITS))
160 continue;
161
162 if (compareThreshold(resPix, bilinearSampleRGBA8(reference, (uint32_t)u, (uint32_t)v), threshold))
163 return true;
164 }
165 }
166
167 return false;
168 }
169
bilinearCompareRGBA8(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)170 bool bilinearCompareRGBA8(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
171 const PixelBufferAccess &errorMask, const RGBA threshold)
172 {
173 DE_ASSERT(reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
174 result.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8));
175
176 // Clear error mask first to green (faster this way).
177 clear(errorMask, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
178
179 bool allOk = true;
180
181 for (int y = 0; y < reference.getHeight(); y++)
182 {
183 for (int x = 0; x < reference.getWidth(); x++)
184 {
185 if (!comparePixelRGBA8(reference, result, threshold, x, y) &&
186 !comparePixelRGBA8(result, reference, threshold, x, y))
187 {
188 allOk = false;
189 errorMask.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y);
190 }
191 }
192 }
193
194 return allOk;
195 }
196
197 } // namespace
198
bilinearCompare(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const PixelBufferAccess & errorMask,const RGBA threshold)199 bool bilinearCompare(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
200 const PixelBufferAccess &errorMask, const RGBA threshold)
201 {
202 DE_ASSERT(reference.getWidth() == result.getWidth() && reference.getHeight() == result.getHeight() &&
203 reference.getDepth() == result.getDepth() && reference.getFormat() == result.getFormat());
204 DE_ASSERT(reference.getWidth() == errorMask.getWidth() && reference.getHeight() == errorMask.getHeight() &&
205 reference.getDepth() == errorMask.getDepth());
206
207 if (reference.getFormat() == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
208 return bilinearCompareRGBA8(reference, result, errorMask, threshold);
209 else
210 throw InternalError("Unsupported format for bilinear comparison");
211 }
212
213 } // namespace tcu
214