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 29namespace tcu 30{ 31 32namespace 33{ 34 35enum 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 43template<int Channel> 44static inline deUint8 getChannel (deUint32 color) 45{ 46 return (deUint8)((color >> (Channel*8)) & 0xff); 47} 48 49#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN) 50inline 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 55inline 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 61inline 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 74inline 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 86RGBA 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 114bool 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-1)<<NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][0]; 177 const int v = ((y-1)<<NUM_SUBPIXEL_BITS) + (int)s_offsets[sampleNdx][1]; 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 191bool 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 219bool 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 236