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