13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry/*-------------------------------------------------------------------------
23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * drawElements Quality Program Tester Core
33c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * ----------------------------------------
43c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Copyright 2014 The Android Open Source Project
63c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Licensed under the Apache License, Version 2.0 (the "License");
83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * you may not use this file except in compliance with the License.
93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * You may obtain a copy of the License at
103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *      http://www.apache.org/licenses/LICENSE-2.0
123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Unless required by applicable law or agreed to in writing, software
143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * distributed under the License is distributed on an "AS IS" BASIS,
153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * See the License for the specific language governing permissions and
173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * limitations under the License.
183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *
193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *//*!
203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * \file
213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * \brief Fuzzy image comparison.
223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry *//*--------------------------------------------------------------------*/
233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "tcuFuzzyImageCompare.hpp"
253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "tcuTexture.hpp"
263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "tcuTextureUtil.hpp"
273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "deMath.h"
283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "deRandom.hpp"
293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include <vector>
313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
323c827367444ee418f129b2c238299f49d3264554Jarkko Poyrynamespace tcu
333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
35ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulosenum
36ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos{
37ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	MIN_ERR_THRESHOLD	= 4 // Magic to make small differences go away
38ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos};
39ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos
403c827367444ee418f129b2c238299f49d3264554Jarkko Poyryusing std::vector;
413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
423c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int Channel>
433c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint8 getChannel (deUint32 color)
443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return (deUint8)((color >> (Channel*8)) & 0xff);
463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
483c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint8 getChannel (deUint32 color, int channel)
493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return (deUint8)((color >> (channel*8)) & 0xff);
513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
533c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint32 setChannel (deUint32 color, int channel, deUint8 val)
543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return (color & ~(0xffu << (8*channel))) | (val << (8*channel));
563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
583c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline Vec4 toFloatVec (deUint32 color)
593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return Vec4((float)getChannel<0>(color), (float)getChannel<1>(color), (float)getChannel<2>(color), (float)getChannel<3>(color));
613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
633c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint8 roundToUint8Sat (float v)
643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return (deUint8)de::clamp((int)(v + 0.5f), 0, 255);
663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
683c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint32 toColor (Vec4 v)
693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return roundToUint8Sat(v[0]) | (roundToUint8Sat(v[1]) << 8) | (roundToUint8Sat(v[2]) << 16) | (roundToUint8Sat(v[3]) << 24);
713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
733c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int NumChannels>
743c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline deUint32 readUnorm8 (const tcu::ConstPixelBufferAccess& src, int x, int y)
753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	const deUint8*	ptr	= (const deUint8*)src.getDataPtr() + src.getRowPitch()*y + x*NumChannels;
773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32		v	= 0;
783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int c = 0; c < NumChannels; c++)
803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		v |= ptr[c] << (c*8);
813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (NumChannels < 4)
833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		v |= 0xffu << 24;
843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return v;
863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
8874731a6adf5816339b00b854a513b1b950ad4357Pyry Haulos#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
893c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<>
903c827367444ee418f129b2c238299f49d3264554Jarkko Poyryinline deUint32 readUnorm8<4> (const tcu::ConstPixelBufferAccess& src, int x, int y)
913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return *(const deUint32*)((const deUint8*)src.getDataPtr() + src.getRowPitch()*y + x*4);
933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
9474731a6adf5816339b00b854a513b1b950ad4357Pyry Haulos#endif
953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
963c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int NumChannels>
973c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline void writeUnorm8 (const tcu::PixelBufferAccess& dst, int x, int y, deUint32 val)
983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint8* ptr = (deUint8*)dst.getDataPtr() + dst.getRowPitch()*y + x*NumChannels;
1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int c = 0; c < NumChannels; c++)
1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		ptr[c] = getChannel(val, c);
1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
10574731a6adf5816339b00b854a513b1b950ad4357Pyry Haulos#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<>
1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyryinline void writeUnorm8<4> (const tcu::PixelBufferAccess& dst, int x, int y, deUint32 val)
1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	*(deUint32*)((deUint8*)dst.getDataPtr() + dst.getRowPitch()*y + x*4) = val;
1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
11174731a6adf5816339b00b854a513b1b950ad4357Pyry Haulos#endif
1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
113ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulosstatic inline deUint32 colorDistSquared (deUint32 pa, deUint32 pb)
1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
115ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	const int	r	= de::max<int>(de::abs((int)getChannel<0>(pa) - (int)getChannel<0>(pb)) - MIN_ERR_THRESHOLD, 0);
116ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	const int	g	= de::max<int>(de::abs((int)getChannel<1>(pa) - (int)getChannel<1>(pb)) - MIN_ERR_THRESHOLD, 0);
117ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	const int	b	= de::max<int>(de::abs((int)getChannel<2>(pa) - (int)getChannel<2>(pb)) - MIN_ERR_THRESHOLD, 0);
118ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	const int	a	= de::max<int>(de::abs((int)getChannel<3>(pa) - (int)getChannel<3>(pb)) - MIN_ERR_THRESHOLD, 0);
1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
120ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	return deUint32(r*r + g*g + b*b + a*a);
1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int NumChannels>
1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyryinline deUint32 bilinearSample (const ConstPixelBufferAccess& src, float u, float v)
1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int w = src.getWidth();
1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int h = src.getHeight();
1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int x0 = deFloorFloatToInt32(u-0.5f);
1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int x1 = x0+1;
1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int y0 = deFloorFloatToInt32(v-0.5f);
1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int y1 = y0+1;
1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int i0 = de::clamp(x0, 0, w-1);
1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int i1 = de::clamp(x1, 0, w-1);
1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int j0 = de::clamp(y0, 0, h-1);
1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int j1 = de::clamp(y1, 0, h-1);
1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	float a = deFloatFrac(u-0.5f);
1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	float b = deFloatFrac(v-0.5f);
1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32 p00	= readUnorm8<NumChannels>(src, i0, j0);
1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32 p10	= readUnorm8<NumChannels>(src, i1, j0);
1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32 p01	= readUnorm8<NumChannels>(src, i0, j1);
1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32 p11	= readUnorm8<NumChannels>(src, i1, j1);
1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	deUint32 dst	= 0;
1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Interpolate.
1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int c = 0; c < NumChannels; c++)
1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		float f = (getChannel(p00, c)*(1.0f-a)*(1.0f-b)) +
1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				  (getChannel(p10, c)*(     a)*(1.0f-b)) +
1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				  (getChannel(p01, c)*(1.0f-a)*(     b)) +
1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				  (getChannel(p11, c)*(     a)*(     b));
1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		dst = setChannel(dst, c, roundToUint8Sat(f));
1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return dst;
1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int DstChannels, int SrcChannels>
1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic void separableConvolve (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, int shiftX, int shiftY, const std::vector<float>& kernelX, const std::vector<float>& kernelY)
1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	DE_ASSERT(dst.getWidth() == src.getWidth() && dst.getHeight() == src.getHeight());
1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	TextureLevel		tmp			(dst.getFormat(), dst.getHeight(), dst.getWidth());
1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	PixelBufferAccess	tmpAccess	= tmp.getAccess();
1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int kw = (int)kernelX.size();
1703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int kh = (int)kernelY.size();
1713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Horizontal pass
1733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// \note Temporary surface is written in column-wise order
1743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int j = 0; j < src.getHeight(); j++)
1753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		for (int i = 0; i < src.getWidth(); i++)
1773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{
1783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			Vec4 sum(0);
1793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			for (int kx = 0; kx < kw; kx++)
1813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			{
1823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				float		f = kernelX[kw-kx-1];
1833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				deUint32	p = readUnorm8<SrcChannels>(src, de::clamp(i+kx-shiftX, 0, src.getWidth()-1), j);
1843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				sum += toFloatVec(p)*f;
1863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			}
1873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			writeUnorm8<DstChannels>(tmpAccess, j, i, toColor(sum));
1893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		}
1903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
1913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Vertical pass
1933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int j = 0; j < src.getHeight(); j++)
1943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
1953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		for (int i = 0; i < src.getWidth(); i++)
1963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{
1973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			Vec4 sum(0.0f);
1983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			for (int ky = 0; ky < kh; ky++)
2003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			{
2013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				float		f = kernelY[kh-ky-1];
2023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				deUint32	p = readUnorm8<DstChannels>(tmpAccess, de::clamp(j+ky-shiftY, 0, tmp.getWidth()-1), i);
2033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry				sum += toFloatVec(p)*f;
2053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			}
2063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			writeUnorm8<DstChannels>(dst, i, j, toColor(sum));
2083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		}
2093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
2103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2123c827367444ee418f129b2c238299f49d3264554Jarkko Poyrytemplate<int NumChannels>
213ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulosstatic deUint32 distSquaredToNeighbor (de::Random& rnd, deUint32 pixel, const ConstPixelBufferAccess& surface, int x, int y)
2143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// (x, y) + (0, 0)
216ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	deUint32	minDist		= colorDistSquared(pixel, readUnorm8<NumChannels>(surface, x, y));
217ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos
218ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	if (minDist == 0)
219ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		return minDist;
2203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Area around (x, y)
2223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	static const int s_coords[][2] =
2233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
2243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{-1, -1},
2253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{ 0, -1},
2263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{+1, -1},
2273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{-1,  0},
2283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{+1,  0},
2293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{-1, +1},
2303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{ 0, +1},
2313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{+1, +1}
2323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	};
2333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int d = 0; d < (int)DE_LENGTH_OF_ARRAY(s_coords); d++)
2353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
2363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		int dx = x + s_coords[d][0];
2373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		int dy = y + s_coords[d][1];
2383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if (!deInBounds32(dx, 0, surface.getWidth()) || !deInBounds32(dy, 0, surface.getHeight()))
2403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			continue;
2413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
242ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		minDist = de::min(minDist, colorDistSquared(pixel, readUnorm8<NumChannels>(surface, dx, dy)));
243ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		if (minDist == 0)
244ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos			return minDist;
2453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
2463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Random bilinear-interpolated samples around (x, y)
2483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int s = 0; s < 32; s++)
2493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
2503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		float dx = (float)x + rnd.getFloat()*2.0f - 0.5f;
2513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		float dy = (float)y + rnd.getFloat()*2.0f - 0.5f;
2523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		deUint32 sample = bilinearSample<NumChannels>(surface, dx, dy);
2543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
255ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		minDist = de::min(minDist, colorDistSquared(pixel, sample));
256ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		if (minDist == 0)
257ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos			return minDist;
2583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
2593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
260ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	return minDist;
2613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2633c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic inline float toGrayscale (const Vec4& c)
2643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return 0.2126f*c[0] + 0.7152f*c[1] + 0.0722f*c[2];
2663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2683c827367444ee418f129b2c238299f49d3264554Jarkko Poyrystatic bool isFormatSupported (const TextureFormat& format)
2693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return format.type == TextureFormat::UNORM_INT8 && (format.order == TextureFormat::RGB || format.order == TextureFormat::RGBA);
2713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
2723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2733c827367444ee418f129b2c238299f49d3264554Jarkko Poyryfloat fuzzyCompare (const FuzzyCompareParams& params, const ConstPixelBufferAccess& ref, const ConstPixelBufferAccess& cmp, const PixelBufferAccess& errorMask)
2743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry{
2753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	DE_ASSERT(ref.getWidth() == cmp.getWidth() && ref.getHeight() == cmp.getHeight());
2763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	DE_ASSERT(errorMask.getWidth() == ref.getWidth() && errorMask.getHeight() == ref.getHeight());
2773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if (!isFormatSupported(ref.getFormat()) || !isFormatSupported(cmp.getFormat()))
2793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		throw InternalError("Unsupported format in fuzzy comparison", DE_NULL, __FILE__, __LINE__);
2803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			width	= ref.getWidth();
2823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int			height	= ref.getHeight();
2833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	de::Random	rnd		(667);
2843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Filtered
2863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	TextureLevel refFiltered(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), width, height);
2873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	TextureLevel cmpFiltered(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), width, height);
2883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
289ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	// Kernel = {0.1, 0.8, 0.1}
2903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	vector<float> kernel(3);
2913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	kernel[0] = kernel[2] = 0.1f; kernel[1]= 0.8f;
2923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	int shift = (int)(kernel.size() - 1) / 2;
2933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
2943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	switch (ref.getFormat().order)
2953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
2963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case TextureFormat::RGBA:	separableConvolve<4, 4>(refFiltered, ref, shift, shift, kernel, kernel);	break;
2973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case TextureFormat::RGB:	separableConvolve<4, 3>(refFiltered, ref, shift, shift, kernel, kernel);	break;
2983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		default:
2993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			DE_ASSERT(DE_FALSE);
3003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
3013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	switch (cmp.getFormat().order)
3033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
3043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case TextureFormat::RGBA:	separableConvolve<4, 4>(cmpFiltered, cmp, shift, shift, kernel, kernel);	break;
3053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		case TextureFormat::RGB:	separableConvolve<4, 3>(cmpFiltered, cmp, shift, shift, kernel, kernel);	break;
3063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		default:
3073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			DE_ASSERT(DE_FALSE);
3083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
3093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
310ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	int			numSamples	= 0;
311ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	deUint64	distSum4	= 0ull;
3123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	// Clear error mask to green.
3143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	clear(errorMask, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
3153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	ConstPixelBufferAccess refAccess = refFiltered.getAccess();
3173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	ConstPixelBufferAccess cmpAccess = cmpFiltered.getAccess();
3183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for (int y = 1; y < height-1; y++)
3203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	{
3213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		for (int x = 1; x < width-1; x += params.maxSampleSkip > 0 ? (int)rnd.getInt(0, params.maxSampleSkip) : 1)
3223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		{
3231c3ae95bd7fefdffccc8afc5565897aa8400de83Pyry Haulos			const deUint32	minDist2RefToCmp	= distSquaredToNeighbor<4>(rnd, readUnorm8<4>(refAccess, x, y), cmpAccess, x, y);
3241c3ae95bd7fefdffccc8afc5565897aa8400de83Pyry Haulos			const deUint32	minDist2CmpToRef	= distSquaredToNeighbor<4>(rnd, readUnorm8<4>(cmpAccess, x, y), refAccess, x, y);
3251c3ae95bd7fefdffccc8afc5565897aa8400de83Pyry Haulos			const deUint32	minDist2			= de::min(minDist2RefToCmp, minDist2CmpToRef);
3261c3ae95bd7fefdffccc8afc5565897aa8400de83Pyry Haulos			const deUint64	newSum4				= distSum4 + minDist2*minDist2;
3273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
328ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos			distSum4	 = (newSum4 >= distSum4) ? newSum4 : ~0ull; // In case of overflow
3293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			numSamples	+= 1;
3303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			// Build error image.
332ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos			{
333ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const int	scale	= 255-MIN_ERR_THRESHOLD;
334ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const float	err2	= float(minDist2) / float(scale*scale);
335ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const float	err4	= err2*err2;
336ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const float	red		= err4 * 500.0f;
337ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const float	luma	= toGrayscale(cmp.getPixel(x, y));
338ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				const float	rF		= 0.7f + 0.3f*luma;
339ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos
340ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos				errorMask.setPixel(Vec4(red*rF, (1.0f-red)*rF, 0.0f, 1.0f), x, y);
341ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos			}
3423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		}
3433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	}
3443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
345ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	{
346ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		// Scale error sum based on number of samples taken
347ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		const double	pSamples	= double((width-2) * (height-2)) / double(numSamples);
348ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		const deUint64	colScale	= deUint64(255-MIN_ERR_THRESHOLD);
349ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		const deUint64	colScale4	= colScale*colScale*colScale*colScale;
3503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
351ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos		return float(double(distSum4) / double(colScale4) * pSamples);
352ec91da5e9ca1fb19d6853c9535332a0085df2922Pyry Haulos	}
3533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}
3543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
3553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} // tcu
356