Home | History | Annotate | Download | only in common
      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-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 
    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
    236