/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Texture utilities.
 *//*--------------------------------------------------------------------*/

#include "tcuTextureUtil.hpp"
#include "tcuVectorUtil.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "deMemory.h"

#include <limits>

namespace tcu
{

static inline float sRGBChannelToLinear (float cs)
{
	if (cs <= 0.04045)
		return cs / 12.92f;
	else
		return deFloatPow((cs + 0.055f) / 1.055f, 2.4f);
}

static const deUint32 s_srgb8Lut[256] =
{
#include "tcuSRGB8Lut.inl"
};

static inline float sRGB8ChannelToLinear (deUint32 cs)
{
	DE_ASSERT(cs < 256);

	// \note This triggers UB, but in practice it doesn't cause any problems
	return ((const float*)s_srgb8Lut)[cs];
}

static inline float linearChannelToSRGB (float cl)
{
	if (cl <= 0.0f)
		return 0.0f;
	else if (cl < 0.0031308f)
		return 12.92f*cl;
	else if (cl < 1.0f)
		return 1.055f*deFloatPow(cl, 0.41666f) - 0.055f;
	else
		return 1.0f;
}

//! Convert sRGB to linear colorspace
Vec4 sRGBToLinear (const Vec4& cs)
{
	return Vec4(sRGBChannelToLinear(cs[0]),
				sRGBChannelToLinear(cs[1]),
				sRGBChannelToLinear(cs[2]),
				cs[3]);
}

Vec4 sRGB8ToLinear (const UVec4& cs)
{
	return Vec4(sRGB8ChannelToLinear(cs[0]),
				sRGB8ChannelToLinear(cs[1]),
				sRGB8ChannelToLinear(cs[2]),
				1.0f);
}

Vec4 sRGBA8ToLinear (const UVec4& cs)
{
	return Vec4(sRGB8ChannelToLinear(cs[0]),
				sRGB8ChannelToLinear(cs[1]),
				sRGB8ChannelToLinear(cs[2]),
				(float)cs[3] / 255.0f);
}

//! Convert from linear to sRGB colorspace
Vec4 linearToSRGB (const Vec4& cl)
{
	return Vec4(linearChannelToSRGB(cl[0]),
				linearChannelToSRGB(cl[1]),
				linearChannelToSRGB(cl[2]),
				cl[3]);
}

bool isSRGB (TextureFormat format)
{
	// make sure to update this if type table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);

	return	format.order == TextureFormat::sR		||
			format.order == TextureFormat::sRG		||
			format.order == TextureFormat::sRGB		||
			format.order == TextureFormat::sRGBA	||
			format.order == TextureFormat::sBGR		||
			format.order == TextureFormat::sBGRA;
}

bool isCombinedDepthStencilType (TextureFormat::ChannelType type)
{
	// make sure to update this if type table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	return	type == TextureFormat::UNSIGNED_INT_16_8_8			||
			type == TextureFormat::UNSIGNED_INT_24_8			||
			type == TextureFormat::UNSIGNED_INT_24_8_REV		||
			type == TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV;
}

bool hasStencilComponent (TextureFormat::ChannelOrder order)
{
	DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);

	switch (order)
	{
		case TextureFormat::S:
		case TextureFormat::DS:
			return true;

		default:
			return false;
	}
}

bool hasDepthComponent (TextureFormat::ChannelOrder order)
{
	DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);

	switch (order)
	{
		case TextureFormat::D:
		case TextureFormat::DS:
			return true;

		default:
			return false;
	}
}

//! Get texture channel class for format
TextureChannelClass getTextureChannelClass (TextureFormat::ChannelType channelType)
{
	// make sure this table is updated if format table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	switch (channelType)
	{
		case TextureFormat::SNORM_INT8:						return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
		case TextureFormat::SNORM_INT16:					return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
		case TextureFormat::SNORM_INT32:					return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
		case TextureFormat::UNORM_INT8:						return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_INT16:					return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_INT24:					return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_INT32:					return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_BYTE_44:					return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_SHORT_565:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_SHORT_555:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_SHORT_4444:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_SHORT_5551:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNORM_SHORT_1555:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::UNSIGNED_BYTE_44:				return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_SHORT_565:				return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_SHORT_4444:			return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_SHORT_5551:			return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNORM_INT_101010:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::SNORM_INT_1010102_REV:			return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
		case TextureFormat::UNORM_INT_1010102_REV:			return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
		case TextureFormat::SIGNED_INT_1010102_REV:			return TEXTURECHANNELCLASS_SIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT_1010102_REV:		return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return TEXTURECHANNELCLASS_FLOATING_POINT;
		case TextureFormat::UNSIGNED_INT_999_E5_REV:		return TEXTURECHANNELCLASS_FLOATING_POINT;
		case TextureFormat::UNSIGNED_INT_16_8_8:			return TEXTURECHANNELCLASS_LAST;					//!< packed unorm16-x8-uint8
		case TextureFormat::UNSIGNED_INT_24_8:				return TEXTURECHANNELCLASS_LAST;					//!< packed unorm24-uint8
		case TextureFormat::UNSIGNED_INT_24_8_REV:			return TEXTURECHANNELCLASS_LAST;					//!< packed unorm24-uint8
		case TextureFormat::SIGNED_INT8:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
		case TextureFormat::SIGNED_INT16:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
		case TextureFormat::SIGNED_INT32:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT8:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT16:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT24:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::UNSIGNED_INT32:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		case TextureFormat::HALF_FLOAT:						return TEXTURECHANNELCLASS_FLOATING_POINT;
		case TextureFormat::FLOAT:							return TEXTURECHANNELCLASS_FLOATING_POINT;
		case TextureFormat::FLOAT64:						return TEXTURECHANNELCLASS_FLOATING_POINT;
		case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return TEXTURECHANNELCLASS_LAST;					//!< packed float32-pad24-uint8
		default:
			DE_FATAL("Unknown channel type");
			return TEXTURECHANNELCLASS_LAST;
	}
}

bool isAccessValid (TextureFormat format, TextureAccessType type)
{
	DE_ASSERT(isValid(format));

	if (format.order == TextureFormat::DS)
	{
		// It is never allowed to access combined depth-stencil format with getPixel().
		// Instead either getPixDepth() or getPixStencil(), or effective depth- or stencil-
		// access must be used.
		return false;
	}
	else if (format.order == TextureFormat::D)
		return type == TEXTUREACCESSTYPE_FLOAT;
	else if (format.order == TextureFormat::S)
		return type == TEXTUREACCESSTYPE_UNSIGNED_INT;
	else
	{
		// A few packed color formats have access type restrictions
		if (format.type == TextureFormat::UNSIGNED_INT_11F_11F_10F_REV ||
			format.type == TextureFormat::UNSIGNED_INT_999_E5_REV)
			return type == TEXTUREACCESSTYPE_FLOAT;
		else
			return true;
	}
}

/*--------------------------------------------------------------------*//*!
 * \brief Get access to subregion of pixel buffer
 * \param access	Parent access object
 * \param x			X offset
 * \param y			Y offset
 * \param z			Z offset
 * \param width		Width
 * \param height	Height
 * \param depth		Depth
 * \return Access object that targets given subregion of parent access object
 *//*--------------------------------------------------------------------*/
ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
{
	DE_ASSERT(de::inBounds(x, 0, access.getWidth()));
	DE_ASSERT(de::inRange(x+width, x+1, access.getWidth()));

	DE_ASSERT(de::inBounds(y, 0, access.getHeight()));
	DE_ASSERT(de::inRange(y+height, y+1, access.getHeight()));

	DE_ASSERT(de::inBounds(z, 0, access.getDepth()));
	DE_ASSERT(de::inRange(z+depth, z+1, access.getDepth()));

	return ConstPixelBufferAccess(access.getFormat(), tcu::IVec3(width, height, depth), access.getPitch(),
								  (const deUint8*)access.getDataPtr() + access.getPixelPitch()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
}

/*--------------------------------------------------------------------*//*!
 * \brief Get access to subregion of pixel buffer
 * \param access	Parent access object
 * \param x			X offset
 * \param y			Y offset
 * \param z			Z offset
 * \param width		Width
 * \param height	Height
 * \param depth		Depth
 * \return Access object that targets given subregion of parent access object
 *//*--------------------------------------------------------------------*/
PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
{
	DE_ASSERT(de::inBounds(x, 0, access.getWidth()));
	DE_ASSERT(de::inRange(x+width, x+1, access.getWidth()));

	DE_ASSERT(de::inBounds(y, 0, access.getHeight()));
	DE_ASSERT(de::inRange(y+height, y+1, access.getHeight()));

	DE_ASSERT(de::inBounds(z, 0, access.getDepth()));
	DE_ASSERT(de::inRange(z+depth, z+1, access.getDepth()));

	return PixelBufferAccess(access.getFormat(), tcu::IVec3(width, height, depth), access.getPitch(),
							 (deUint8*)access.getDataPtr() + access.getPixelPitch()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
}

/*--------------------------------------------------------------------*//*!
 * \brief Get access to subregion of pixel buffer
 * \param access	Parent access object
 * \param x			X offset
 * \param y			Y offset
 * \param width		Width
 * \param height	Height
 * \return Access object that targets given subregion of parent access object
 *//*--------------------------------------------------------------------*/
PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int width, int height)
{
	return getSubregion(access, x, y, 0, width, height, 1);
}

/*--------------------------------------------------------------------*//*!
 * \brief Get access to subregion of pixel buffer
 * \param access	Parent access object
 * \param x			X offset
 * \param y			Y offset
 * \param width		Width
 * \param height	Height
 * \return Access object that targets given subregion of parent access object
 *//*--------------------------------------------------------------------*/
ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int width, int height)
{
	return getSubregion(access, x, y, 0, width, height, 1);
}

/*--------------------------------------------------------------------*//*!
 * \brief Flip rows in Y direction
 * \param access Access object
 * \return Modified access object where Y coordinates are reversed
 *//*--------------------------------------------------------------------*/
PixelBufferAccess flipYAccess (const PixelBufferAccess& access)
{
	const int			rowPitch		= access.getRowPitch();
	const int			offsetToLast	= rowPitch*(access.getHeight()-1);
	const tcu::IVec3	pitch			(access.getPixelPitch(), -rowPitch, access.getSlicePitch());

	return PixelBufferAccess(access.getFormat(), access.getSize(), pitch, (deUint8*)access.getDataPtr() + offsetToLast);
}

/*--------------------------------------------------------------------*//*!
 * \brief Flip rows in Y direction
 * \param access Access object
 * \return Modified access object where Y coordinates are reversed
 *//*--------------------------------------------------------------------*/
ConstPixelBufferAccess flipYAccess (const ConstPixelBufferAccess& access)
{
	const int			rowPitch		= access.getRowPitch();
	const int			offsetToLast	= rowPitch*(access.getHeight()-1);
	const tcu::IVec3	pitch			(access.getPixelPitch(), -rowPitch, access.getSlicePitch());

	return ConstPixelBufferAccess(access.getFormat(), access.getSize(), pitch, (deUint8*)access.getDataPtr() + offsetToLast);
}

static Vec2 getFloatChannelValueRange (TextureFormat::ChannelType channelType)
{
	// make sure this table is updated if format table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	float cMin = 0.0f;
	float cMax = 0.0f;

	switch (channelType)
	{
		// Signed normalized formats.
		case TextureFormat::SNORM_INT8:
		case TextureFormat::SNORM_INT16:
		case TextureFormat::SNORM_INT32:
		case TextureFormat::SNORM_INT_1010102_REV:			cMin = -1.0f;			cMax = 1.0f;			break;

		// Unsigned normalized formats.
		case TextureFormat::UNORM_INT8:
		case TextureFormat::UNORM_INT16:
		case TextureFormat::UNORM_INT24:
		case TextureFormat::UNORM_INT32:
		case TextureFormat::UNORM_BYTE_44:
		case TextureFormat::UNORM_SHORT_565:
		case TextureFormat::UNORM_SHORT_555:
		case TextureFormat::UNORM_SHORT_4444:
		case TextureFormat::UNORM_SHORT_5551:
		case TextureFormat::UNORM_SHORT_1555:
		case TextureFormat::UNORM_INT_101010:
		case TextureFormat::UNORM_INT_1010102_REV:			cMin = 0.0f;			cMax = 1.0f;			break;

		// Misc formats.
		case TextureFormat::SIGNED_INT8:					cMin = -128.0f;			cMax = 127.0f;			break;
		case TextureFormat::SIGNED_INT16:					cMin = -32768.0f;		cMax = 32767.0f;		break;
		case TextureFormat::SIGNED_INT32:					cMin = -2147483648.0f;	cMax = 2147483647.0f;	break;
		case TextureFormat::UNSIGNED_INT8:					cMin = 0.0f;			cMax = 255.0f;			break;
		case TextureFormat::UNSIGNED_INT16:					cMin = 0.0f;			cMax = 65535.0f;		break;
		case TextureFormat::UNSIGNED_INT24:					cMin = 0.0f;			cMax = 16777215.0f;		break;
		case TextureFormat::UNSIGNED_INT32:					cMin = 0.0f;			cMax = 4294967295.f;	break;
		case TextureFormat::HALF_FLOAT:						cMin = -1e3f;			cMax = 1e3f;			break;
		case TextureFormat::FLOAT:							cMin = -1e5f;			cMax = 1e5f;			break;
		case TextureFormat::FLOAT64:						cMin = -1e5f;			cMax = 1e5f;			break;
		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	cMin = 0.0f;			cMax = 1e4f;			break;
		case TextureFormat::UNSIGNED_INT_999_E5_REV:		cMin = 0.0f;			cMax = 1e5f;			break;
		case TextureFormat::UNSIGNED_BYTE_44:				cMin = 0.0f;			cMax = 15.f;			break;
		case TextureFormat::UNSIGNED_SHORT_4444:			cMin = 0.0f;			cMax = 15.f;			break;

		default:
			DE_ASSERT(false);
	}

	return Vec2(cMin, cMax);
}

/*--------------------------------------------------------------------*//*!
 * \brief Get standard parameters for testing texture format
 *
 * Returns TextureFormatInfo that describes good parameters for exercising
 * given TextureFormat. Parameters include value ranges per channel and
 * suitable lookup scaling and bias in order to reduce result back to
 * 0..1 range.
 *//*--------------------------------------------------------------------*/
TextureFormatInfo getTextureFormatInfo (const TextureFormat& format)
{
	// Special cases.
	if (format.type == TextureFormat::UNSIGNED_INT_1010102_REV)
		return TextureFormatInfo(Vec4(	     0.0f,		    0.0f,		    0.0f,		 0.0f),
								 Vec4(	  1023.0f,		 1023.0f,		 1023.0f,		 3.0f),
								 Vec4(1.0f/1023.f,	1.0f/1023.0f,	1.0f/1023.0f,	1.0f/3.0f),
								 Vec4(	     0.0f,		    0.0f,		    0.0f,		 0.0f));
	if (format.type == TextureFormat::SIGNED_INT_1010102_REV)
		return TextureFormatInfo(Vec4(	  -512.0f,		 -512.0f,		 -512.0f,		-2.0f),
								 Vec4(	   511.0f,		  511.0f,		  511.0f,		 1.0f),
								 Vec4(1.0f/1023.f,	1.0f/1023.0f,	1.0f/1023.0f,	1.0f/3.0f),
								 Vec4(	     0.5f,		    0.5f,		    0.5f,		 0.5f));
	else if (format.order == TextureFormat::D || format.order == TextureFormat::DS)
		return TextureFormatInfo(Vec4(0.0f,	0.0f,	0.0f,	0.0f),
								 Vec4(1.0f,	1.0f,	1.0f,	0.0f),
								 Vec4(1.0f,	1.0f,	1.0f,	1.0f),
								 Vec4(0.0f,	0.0f,	0.0f,	0.0f)); // Depth / stencil formats.
	else if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551))
		return TextureFormatInfo(Vec4(0.0f, 0.0f, 0.0f, 0.5f),
								 Vec4(1.0f, 1.0f, 1.0f, 1.5f),
								 Vec4(1.0f, 1.0f, 1.0f, 1.0f),
								 Vec4(0.0f, 0.0f, 0.0f, 0.0f));
	else if (format.type == TextureFormat::UNSIGNED_SHORT_5551)
		return TextureFormatInfo(Vec4(	   0.0f,		  0.0f,		  0.0f,	0.0f),
								 Vec4(	  31.0f,		 31.0f,		 31.0f,	1.0f),
								 Vec4(1.0f/31.f,	1.0f/31.0f,	1.0f/31.0f,	1.0f),
								 Vec4(	   0.0f,		  0.0f,		  0.0f,	0.0f));
	else if (format.type == TextureFormat::UNSIGNED_SHORT_565)
		return TextureFormatInfo(Vec4(	   0.0f,		  0.0f,		  0.0f,	0.0f),
								 Vec4(	  31.0f,		 63.0f,		 31.0f,	0.0f),
								 Vec4(1.0f/31.f,	1.0f/63.0f,	1.0f/31.0f,	1.0f),
								 Vec4(	   0.0f,		  0.0f,		  0.0f,	0.0f));

	const Vec2						cRange		= getFloatChannelValueRange(format.type);
	const TextureSwizzle::Channel*	map			= getChannelReadSwizzle(format.order).components;
	const BVec4						chnMask		= BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
	const float						scale		= 1.0f / (cRange[1] - cRange[0]);
	const float						bias		= -cRange[0] * scale;

	return TextureFormatInfo(select(cRange[0],	0.0f, chnMask),
							 select(cRange[1],	0.0f, chnMask),
							 select(scale,		1.0f, chnMask),
							 select(bias,		0.0f, chnMask));
}

IVec4 getFormatMinIntValue (const TextureFormat& format)
{
	DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_SIGNED_INTEGER);

	switch (format.type)
	{
		case TextureFormat::SIGNED_INT8:	return IVec4(std::numeric_limits<deInt8>::min());
		case TextureFormat::SIGNED_INT16:	return IVec4(std::numeric_limits<deInt16>::min());
		case TextureFormat::SIGNED_INT32:	return IVec4(std::numeric_limits<deInt32>::min());

		default:
			DE_FATAL("Invalid channel type");
			return IVec4(0);
	}
}

IVec4 getFormatMaxIntValue (const TextureFormat& format)
{
	DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_SIGNED_INTEGER);

	switch (format.type)
	{
		case TextureFormat::SIGNED_INT8:	return IVec4(std::numeric_limits<deInt8>::max());
		case TextureFormat::SIGNED_INT16:	return IVec4(std::numeric_limits<deInt16>::max());
		case TextureFormat::SIGNED_INT32:	return IVec4(std::numeric_limits<deInt32>::max());

		default:
			DE_FATAL("Invalid channel type");
			return IVec4(0);
	}
}

UVec4 getFormatMaxUintValue (const TextureFormat& format)
{
	DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_UNSIGNED_INTEGER);

	if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT_1010102_REV))
		return UVec4(1023u, 1023u, 1023u, 3u);

	switch (format.type)
	{
		case TextureFormat::UNSIGNED_INT8:	return UVec4(std::numeric_limits<deUint8>::max());
		case TextureFormat::UNSIGNED_INT16:	return UVec4(std::numeric_limits<deUint16>::max());
		case TextureFormat::UNSIGNED_INT24:	return UVec4(0xffffffu);
		case TextureFormat::UNSIGNED_INT32:	return UVec4(std::numeric_limits<deUint32>::max());

		default:
			DE_FATAL("Invalid channel type");
			return UVec4(0);
	}
}

static IVec4 getChannelBitDepth (TextureFormat::ChannelType channelType)
{
	// make sure this table is updated if format table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	switch (channelType)
	{
		case TextureFormat::SNORM_INT8:						return IVec4(8);
		case TextureFormat::SNORM_INT16:					return IVec4(16);
		case TextureFormat::SNORM_INT32:					return IVec4(32);
		case TextureFormat::UNORM_INT8:						return IVec4(8);
		case TextureFormat::UNORM_INT16:					return IVec4(16);
		case TextureFormat::UNORM_INT24:					return IVec4(24);
		case TextureFormat::UNORM_INT32:					return IVec4(32);
		case TextureFormat::UNORM_BYTE_44:					return IVec4(4,4,0,0);
		case TextureFormat::UNORM_SHORT_565:				return IVec4(5,6,5,0);
		case TextureFormat::UNORM_SHORT_4444:				return IVec4(4);
		case TextureFormat::UNORM_SHORT_555:				return IVec4(5,5,5,0);
		case TextureFormat::UNORM_SHORT_5551:				return IVec4(5,5,5,1);
		case TextureFormat::UNORM_SHORT_1555:				return IVec4(1,5,5,5);
		case TextureFormat::UNSIGNED_BYTE_44:				return IVec4(4,4,0,0);
		case TextureFormat::UNSIGNED_SHORT_565:				return IVec4(5,6,5,0);
		case TextureFormat::UNSIGNED_SHORT_4444:			return IVec4(4);
		case TextureFormat::UNSIGNED_SHORT_5551:			return IVec4(5,5,5,1);
		case TextureFormat::UNORM_INT_101010:				return IVec4(10,10,10,0);
		case TextureFormat::SNORM_INT_1010102_REV:			return IVec4(10,10,10,2);
		case TextureFormat::UNORM_INT_1010102_REV:			return IVec4(10,10,10,2);
		case TextureFormat::SIGNED_INT8:					return IVec4(8);
		case TextureFormat::SIGNED_INT16:					return IVec4(16);
		case TextureFormat::SIGNED_INT32:					return IVec4(32);
		case TextureFormat::UNSIGNED_INT8:					return IVec4(8);
		case TextureFormat::UNSIGNED_INT16:					return IVec4(16);
		case TextureFormat::UNSIGNED_INT24:					return IVec4(24);
		case TextureFormat::UNSIGNED_INT32:					return IVec4(32);
		case TextureFormat::SIGNED_INT_1010102_REV:			return IVec4(10,10,10,2);
		case TextureFormat::UNSIGNED_INT_1010102_REV:		return IVec4(10,10,10,2);
		case TextureFormat::UNSIGNED_INT_16_8_8:			return IVec4(16,8,0,0);
		case TextureFormat::UNSIGNED_INT_24_8:				return IVec4(24,8,0,0);
		case TextureFormat::UNSIGNED_INT_24_8_REV:			return IVec4(24,8,0,0);
		case TextureFormat::HALF_FLOAT:						return IVec4(16);
		case TextureFormat::FLOAT:							return IVec4(32);
		case TextureFormat::FLOAT64:						return IVec4(64);
		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return IVec4(11,11,10,0);
		case TextureFormat::UNSIGNED_INT_999_E5_REV:		return IVec4(9,9,9,0);
		case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return IVec4(32,8,0,0);
		default:
			DE_ASSERT(false);
			return IVec4(0);
	}
}

IVec4 getTextureFormatBitDepth (const TextureFormat& format)
{
	const IVec4						chnBits		= getChannelBitDepth(format.type);
	const TextureSwizzle::Channel*	map			= getChannelReadSwizzle(format.order).components;
	const BVec4						chnMask		= BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
	const IVec4						chnSwz		= IVec4((chnMask[0]) ? ((int)map[0]) : (0),
														(chnMask[1]) ? ((int)map[1]) : (0),
														(chnMask[2]) ? ((int)map[2]) : (0),
														(chnMask[3]) ? ((int)map[3]) : (0));

	return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
}

static IVec4 getChannelMantissaBitDepth (TextureFormat::ChannelType channelType)
{
	// make sure this table is updated if format table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	switch (channelType)
	{
		case TextureFormat::SNORM_INT8:
		case TextureFormat::SNORM_INT16:
		case TextureFormat::SNORM_INT32:
		case TextureFormat::UNORM_INT8:
		case TextureFormat::UNORM_INT16:
		case TextureFormat::UNORM_INT24:
		case TextureFormat::UNORM_INT32:
		case TextureFormat::UNORM_BYTE_44:
		case TextureFormat::UNORM_SHORT_565:
		case TextureFormat::UNORM_SHORT_4444:
		case TextureFormat::UNORM_SHORT_555:
		case TextureFormat::UNORM_SHORT_5551:
		case TextureFormat::UNORM_SHORT_1555:
		case TextureFormat::UNSIGNED_BYTE_44:
		case TextureFormat::UNSIGNED_SHORT_565:
		case TextureFormat::UNSIGNED_SHORT_4444:
		case TextureFormat::UNSIGNED_SHORT_5551:
		case TextureFormat::UNORM_INT_101010:
		case TextureFormat::SNORM_INT_1010102_REV:
		case TextureFormat::UNORM_INT_1010102_REV:
		case TextureFormat::SIGNED_INT8:
		case TextureFormat::SIGNED_INT16:
		case TextureFormat::SIGNED_INT32:
		case TextureFormat::UNSIGNED_INT8:
		case TextureFormat::UNSIGNED_INT16:
		case TextureFormat::UNSIGNED_INT24:
		case TextureFormat::UNSIGNED_INT32:
		case TextureFormat::SIGNED_INT_1010102_REV:
		case TextureFormat::UNSIGNED_INT_1010102_REV:
		case TextureFormat::UNSIGNED_INT_16_8_8:
		case TextureFormat::UNSIGNED_INT_24_8:
		case TextureFormat::UNSIGNED_INT_24_8_REV:
		case TextureFormat::UNSIGNED_INT_999_E5_REV:
			return getChannelBitDepth(channelType);

		case TextureFormat::HALF_FLOAT:						return IVec4(10);
		case TextureFormat::FLOAT:							return IVec4(23);
		case TextureFormat::FLOAT64:						return IVec4(52);
		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return IVec4(6,6,5,0);
		case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return IVec4(23,8,0,0);
		default:
			DE_ASSERT(false);
			return IVec4(0);
	}
}

IVec4 getTextureFormatMantissaBitDepth (const TextureFormat& format)
{
	const IVec4						chnBits		= getChannelMantissaBitDepth(format.type);
	const TextureSwizzle::Channel*	map			= getChannelReadSwizzle(format.order).components;
	const BVec4						chnMask		= BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
														deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
	const IVec4						chnSwz		= IVec4((chnMask[0]) ? ((int)map[0]) : (0),
														(chnMask[1]) ? ((int)map[1]) : (0),
														(chnMask[2]) ? ((int)map[2]) : (0),
														(chnMask[3]) ? ((int)map[3]) : (0));

	return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
}

BVec4 getTextureFormatChannelMask (const TextureFormat& format)
{
	const TextureSwizzle::Channel* const map = getChannelReadSwizzle(format.order).components;
	return BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
				 deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
				 deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
				 deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
}

static inline float linearInterpolate (float t, float minVal, float maxVal)
{
	return minVal + (maxVal - minVal) * t;
}

static inline Vec4 linearInterpolate (float t, const Vec4& a, const Vec4& b)
{
	return a + (b - a) * t;
}

enum
{
	CLEAR_OPTIMIZE_THRESHOLD		= 128,
	CLEAR_OPTIMIZE_MAX_PIXEL_SIZE	= 8
};

inline void fillRow (const PixelBufferAccess& dst, int y, int z, int pixelSize, const deUint8* pixel)
{
	DE_ASSERT(dst.getPixelPitch() == pixelSize); // only tightly packed

	deUint8*	dstPtr	= (deUint8*)dst.getPixelPtr(0, y, z);
	int			width	= dst.getWidth();

	if (pixelSize == 8 && deIsAlignedPtr(dstPtr, pixelSize))
	{
		deUint64 val;
		memcpy(&val, pixel, sizeof(val));

		for (int i = 0; i < width; i++)
			((deUint64*)dstPtr)[i] = val;
	}
	else if (pixelSize == 4 && deIsAlignedPtr(dstPtr, pixelSize))
	{
		deUint32 val;
		memcpy(&val, pixel, sizeof(val));

		for (int i = 0; i < width; i++)
			((deUint32*)dstPtr)[i] = val;
	}
	else
	{
		for (int i = 0; i < width; i++)
			for (int j = 0; j < pixelSize; j++)
				dstPtr[i*pixelSize+j] = pixel[j];
	}
}

void clear (const PixelBufferAccess& access, const Vec4& color)
{
	const int	pixelSize				= access.getFormat().getPixelSize();
	const int	pixelPitch				= access.getPixelPitch();
	const bool	rowPixelsTightlyPacked	= (pixelSize == pixelPitch);

	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE && rowPixelsTightlyPacked)
	{
		// Convert to destination format.
		union
		{
			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
			deUint64	u64; // Forces 64-bit alignment.
		} pixel;
		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);

		for (int z = 0; z < access.getDepth(); z++)
			for (int y = 0; y < access.getHeight(); y++)
				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
	}
	else
	{
		for (int z = 0; z < access.getDepth(); z++)
			for (int y = 0; y < access.getHeight(); y++)
				for (int x = 0; x < access.getWidth(); x++)
					access.setPixel(color, x, y, z);
	}
}

void clear (const PixelBufferAccess& access, const IVec4& color)
{
	const int	pixelSize				= access.getFormat().getPixelSize();
	const int	pixelPitch				= access.getPixelPitch();
	const bool	rowPixelsTightlyPacked	= (pixelSize == pixelPitch);

	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE && rowPixelsTightlyPacked)
	{
		// Convert to destination format.
		union
		{
			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
			deUint64	u64; // Forces 64-bit alignment.
		} pixel;
		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);

		for (int z = 0; z < access.getDepth(); z++)
			for (int y = 0; y < access.getHeight(); y++)
				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
	}
	else
	{
		for (int z = 0; z < access.getDepth(); z++)
			for (int y = 0; y < access.getHeight(); y++)
				for (int x = 0; x < access.getWidth(); x++)
					access.setPixel(color, x, y, z);
	}
}

void clear (const PixelBufferAccess& access, const UVec4& color)
{
	clear(access, color.cast<deInt32>());
}

void clearDepth (const PixelBufferAccess& access, float depth)
{
	DE_ASSERT(access.getFormat().order == TextureFormat::DS || access.getFormat().order == TextureFormat::D);

	clear(getEffectiveDepthStencilAccess(access, Sampler::MODE_DEPTH), tcu::Vec4(depth, 0.0f, 0.0f, 0.0f));
}

void clearStencil (const PixelBufferAccess& access, int stencil)
{
	DE_ASSERT(access.getFormat().order == TextureFormat::DS || access.getFormat().order == TextureFormat::S);

	clear(getEffectiveDepthStencilAccess(access, Sampler::MODE_STENCIL), tcu::UVec4(stencil, 0u, 0u, 0u));
}

static void fillWithComponentGradients1D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
	DE_ASSERT(access.getHeight() == 1);
	for (int x = 0; x < access.getWidth(); x++)
	{
		float s = ((float)x + 0.5f) / (float)access.getWidth();

		float r = linearInterpolate(s, minVal.x(), maxVal.x());
		float g = linearInterpolate(s, minVal.y(), maxVal.y());
		float b = linearInterpolate(s, minVal.z(), maxVal.z());
		float a = linearInterpolate(s, minVal.w(), maxVal.w());

		access.setPixel(tcu::Vec4(r, g, b, a), x, 0);
	}
}

static void fillWithComponentGradients2D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
	for (int y = 0; y < access.getHeight(); y++)
	{
		for (int x = 0; x < access.getWidth(); x++)
		{
			float s = ((float)x + 0.5f) / (float)access.getWidth();
			float t = ((float)y + 0.5f) / (float)access.getHeight();

			float r = linearInterpolate((      s  +       t) *0.5f, minVal.x(), maxVal.x());
			float g = linearInterpolate((      s  + (1.0f-t))*0.5f, minVal.y(), maxVal.y());
			float b = linearInterpolate(((1.0f-s) +       t) *0.5f, minVal.z(), maxVal.z());
			float a = linearInterpolate(((1.0f-s) + (1.0f-t))*0.5f, minVal.w(), maxVal.w());

			access.setPixel(tcu::Vec4(r, g, b, a), x, y);
		}
	}
}

static void fillWithComponentGradients3D (const PixelBufferAccess& dst, const Vec4& minVal, const Vec4& maxVal)
{
	for (int z = 0; z < dst.getDepth(); z++)
	{
		for (int y = 0; y < dst.getHeight(); y++)
		{
			for (int x = 0; x < dst.getWidth(); x++)
			{
				float s = ((float)x + 0.5f) / (float)dst.getWidth();
				float t = ((float)y + 0.5f) / (float)dst.getHeight();
				float p = ((float)z + 0.5f) / (float)dst.getDepth();

				float r = linearInterpolate(s,						minVal.x(), maxVal.x());
				float g = linearInterpolate(t,						minVal.y(), maxVal.y());
				float b = linearInterpolate(p,						minVal.z(), maxVal.z());
				float a = linearInterpolate(1.0f - (s+t+p)/3.0f,	minVal.w(), maxVal.w());

				dst.setPixel(tcu::Vec4(r, g, b, a), x, y, z);
			}
		}
	}
}

void fillWithComponentGradients (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
	if (isCombinedDepthStencilType(access.getFormat().type))
	{
		const bool hasDepth		= access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::D;
		const bool hasStencil	= access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::S;

		DE_ASSERT(hasDepth || hasStencil);

		// For combined formats, treat D and S as separate channels
		if (hasDepth)
			fillWithComponentGradients(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH), minVal, maxVal);
		if (hasStencil)
			fillWithComponentGradients(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL), minVal.swizzle(3,2,1,0), maxVal.swizzle(3,2,1,0));
	}
	else
	{
		if (access.getHeight() == 1 && access.getDepth() == 1)
			fillWithComponentGradients1D(access, minVal, maxVal);
		else if (access.getDepth() == 1)
			fillWithComponentGradients2D(access, minVal, maxVal);
		else
			fillWithComponentGradients3D(access, minVal, maxVal);
	}
}

static void fillWithGrid1D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
	for (int x = 0; x < access.getWidth(); x++)
	{
		int mx = (x / cellSize) % 2;

		if (mx)
			access.setPixel(colorB, x, 0);
		else
			access.setPixel(colorA, x, 0);
	}
}

static void fillWithGrid2D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
	for (int y = 0; y < access.getHeight(); y++)
	{
		for (int x = 0; x < access.getWidth(); x++)
		{
			int mx = (x / cellSize) % 2;
			int my = (y / cellSize) % 2;

			if (mx ^ my)
				access.setPixel(colorB, x, y);
			else
				access.setPixel(colorA, x, y);
		}
	}
}

static void fillWithGrid3D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
	for (int z = 0; z < access.getDepth(); z++)
	{
		for (int y = 0; y < access.getHeight(); y++)
		{
			for (int x = 0; x < access.getWidth(); x++)
			{
				int mx = (x / cellSize) % 2;
				int my = (y / cellSize) % 2;
				int mz = (z / cellSize) % 2;

				if (mx ^ my ^ mz)
					access.setPixel(colorB, x, y, z);
				else
					access.setPixel(colorA, x, y, z);
			}
		}
	}
}

void fillWithGrid (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
	if (isCombinedDepthStencilType(access.getFormat().type))
	{
		const bool hasDepth		= access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::D;
		const bool hasStencil	= access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::S;

		DE_ASSERT(hasDepth || hasStencil);

		// For combined formats, treat D and S as separate channels
		if (hasDepth)
			fillWithGrid(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH), cellSize, colorA, colorB);
		if (hasStencil)
			fillWithGrid(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL), cellSize, colorA.swizzle(3,2,1,0), colorB.swizzle(3,2,1,0));
	}
	else
	{
		if (access.getHeight() == 1 && access.getDepth() == 1)
			fillWithGrid1D(access, cellSize, colorA, colorB);
		else if (access.getDepth() == 1)
			fillWithGrid2D(access, cellSize, colorA, colorB);
		else
			fillWithGrid3D(access, cellSize, colorA, colorB);
	}
}

void fillWithRepeatableGradient (const PixelBufferAccess& access, const Vec4& colorA, const Vec4& colorB)
{
	for (int y = 0; y < access.getHeight(); y++)
	{
		for (int x = 0; x < access.getWidth(); x++)
		{
			float s = ((float)x + 0.5f) / (float)access.getWidth();
			float t = ((float)y + 0.5f) / (float)access.getHeight();

			float a = s > 0.5f ? (2.0f - 2.0f*s) : 2.0f*s;
			float b = t > 0.5f ? (2.0f - 2.0f*t) : 2.0f*t;

			float p = deFloatClamp(deFloatSqrt(a*a + b*b), 0.0f, 1.0f);
			access.setPixel(linearInterpolate(p, colorA, colorB), x, y);
		}
	}
}

void fillWithRGBAQuads (const PixelBufferAccess& dst)
{
	TCU_CHECK_INTERNAL(dst.getDepth() == 1);
	int width	= dst.getWidth();
	int height	= dst.getHeight();
	int	left	= width/2;
	int top		= height/2;

	clear(getSubregion(dst, 0,		0,		0, left,		top,		1),	Vec4(1.0f, 0.0f, 0.0f, 1.0f));
	clear(getSubregion(dst, left,	0,		0, width-left,	top,		1),	Vec4(0.0f, 1.0f, 0.0f, 1.0f));
	clear(getSubregion(dst, 0,		top,	0, left,		height-top,	1), Vec4(0.0f, 0.0f, 1.0f, 0.0f));
	clear(getSubregion(dst, left,	top,	0, width-left,	height-top, 1), Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}

// \todo [2012-11-13 pyry] There is much better metaballs code in CL SIR value generators.
void fillWithMetaballs (const PixelBufferAccess& dst, int numBalls, deUint32 seed)
{
	TCU_CHECK_INTERNAL(dst.getDepth() == 1);
	std::vector<Vec2>	points(numBalls);
	de::Random			rnd(seed);

	for (int i = 0; i < numBalls; i++)
	{
		float x = rnd.getFloat();
		float y = rnd.getFloat();
		points[i] = (Vec2(x, y));
	}

	for (int y = 0; y < dst.getHeight(); y++)
	for (int x = 0; x < dst.getWidth(); x++)
	{
		Vec2 p((float)x/(float)dst.getWidth(), (float)y/(float)dst.getHeight());

		float sum = 0.0f;
		for (std::vector<Vec2>::const_iterator i = points.begin(); i != points.end(); i++)
		{
			Vec2	d = p - *i;
			float	f = 0.01f / (d.x()*d.x() + d.y()*d.y());

			sum += f;
		}

		dst.setPixel(Vec4(sum), x, y);
	}
}

void copy (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src)
{
	DE_ASSERT(src.getSize() == dst.getSize());

	const int	width				= dst.getWidth();
	const int	height				= dst.getHeight();
	const int	depth				= dst.getDepth();

	const int	srcPixelSize		= src.getFormat().getPixelSize();
	const int	dstPixelSize		= dst.getFormat().getPixelSize();
	const int	srcPixelPitch		= src.getPixelPitch();
	const int	dstPixelPitch		= dst.getPixelPitch();
	const bool	srcTightlyPacked	= (srcPixelSize == srcPixelPitch);
	const bool	dstTightlyPacked	= (dstPixelSize == dstPixelPitch);

	const bool	srcHasDepth			= (src.getFormat().order == tcu::TextureFormat::DS || src.getFormat().order == tcu::TextureFormat::D);
	const bool	srcHasStencil		= (src.getFormat().order == tcu::TextureFormat::DS || src.getFormat().order == tcu::TextureFormat::S);
	const bool	dstHasDepth			= (dst.getFormat().order == tcu::TextureFormat::DS || dst.getFormat().order == tcu::TextureFormat::D);
	const bool	dstHasStencil		= (dst.getFormat().order == tcu::TextureFormat::DS || dst.getFormat().order == tcu::TextureFormat::S);

	if (src.getFormat() == dst.getFormat() && srcTightlyPacked && dstTightlyPacked)
	{
		// Fast-path for matching formats.
		for (int z = 0; z < depth; z++)
		for (int y = 0; y < height; y++)
			deMemcpy(dst.getPixelPtr(0, y, z), src.getPixelPtr(0, y, z), srcPixelSize*width);
	}
	else if (src.getFormat() == dst.getFormat())
	{
		// Bit-exact copy for matching formats.
		for (int z = 0; z < depth; z++)
		for (int y = 0; y < height; y++)
		for (int x = 0; x < width; x++)
			deMemcpy(dst.getPixelPtr(x, y, z), src.getPixelPtr(x, y, z), srcPixelSize);
	}
	else if (srcHasDepth || srcHasStencil || dstHasDepth || dstHasStencil)
	{
		DE_ASSERT((srcHasDepth && dstHasDepth) || (srcHasStencil && dstHasStencil)); // must have at least one common channel

		if (dstHasDepth && srcHasDepth)
		{
			for (int z = 0; z < depth; z++)
			for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++)
				dst.setPixDepth(src.getPixDepth(x, y, z), x, y, z);
		}
		else if (dstHasDepth && !srcHasDepth)
		{
			// consistency with color copies
			tcu::clearDepth(dst, 0.0f);
		}

		if (dstHasStencil && srcHasStencil)
		{
			for (int z = 0; z < depth; z++)
			for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++)
				dst.setPixStencil(src.getPixStencil(x, y, z), x, y, z);
		}
		else if (dstHasStencil && !srcHasStencil)
		{
			// consistency with color copies
			tcu::clearStencil(dst, 0u);
		}
	}
	else
	{
		TextureChannelClass		srcClass	= getTextureChannelClass(src.getFormat().type);
		TextureChannelClass		dstClass	= getTextureChannelClass(dst.getFormat().type);
		bool					srcIsInt	= srcClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || srcClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
		bool					dstIsInt	= dstClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || dstClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;

		if (srcIsInt && dstIsInt)
		{
			for (int z = 0; z < depth; z++)
			for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++)
				dst.setPixel(src.getPixelInt(x, y, z), x, y, z);
		}
		else
		{
			for (int z = 0; z < depth; z++)
			for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++)
				dst.setPixel(src.getPixel(x, y, z), x, y, z);
		}
	}
}

void scale (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, Sampler::FilterMode filter)
{
	DE_ASSERT(filter == Sampler::NEAREST || filter == Sampler::LINEAR);

	Sampler sampler(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE,
					filter, filter, 0.0f, false);

	float sX = (float)src.getWidth() / (float)dst.getWidth();
	float sY = (float)src.getHeight() / (float)dst.getHeight();
	float sZ = (float)src.getDepth() / (float)dst.getDepth();

	if (dst.getDepth() == 1 && src.getDepth() == 1)
	{
		for (int y = 0; y < dst.getHeight(); y++)
		for (int x = 0; x < dst.getWidth(); x++)
			dst.setPixel(src.sample2D(sampler, filter, ((float)x+0.5f)*sX, ((float)y+0.5f)*sY, 0), x, y);
	}
	else
	{
		for (int z = 0; z < dst.getDepth(); z++)
		for (int y = 0; y < dst.getHeight(); y++)
		for (int x = 0; x < dst.getWidth(); x++)
			dst.setPixel(src.sample3D(sampler, filter, ((float)x+0.5f)*sX, ((float)y+0.5f)*sY, ((float)z+0.5f)*sZ), x, y, z);
	}
}

void estimatePixelValueRange (const ConstPixelBufferAccess& access, Vec4& minVal, Vec4& maxVal)
{
	const TextureFormat& format = access.getFormat();

	switch (getTextureChannelClass(format.type))
	{
		case TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
			// Normalized unsigned formats.
			minVal = Vec4(0.0f);
			maxVal = Vec4(1.0f);
			break;

		case TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
			// Normalized signed formats.
			minVal = Vec4(-1.0f);
			maxVal = Vec4(+1.0f);
			break;

		default:
			// \note Samples every 4/8th pixel.
			minVal = Vec4(std::numeric_limits<float>::max());
			maxVal = Vec4(std::numeric_limits<float>::min());

			for (int z = 0; z < access.getDepth(); z += 2)
			{
				for (int y = 0; y < access.getHeight(); y += 2)
				{
					for (int x = 0; x < access.getWidth(); x += 2)
					{
						Vec4 p = access.getPixel(x, y, z);

						minVal[0] = (deFloatIsNaN(p[0]) ? minVal[0] : de::min(minVal[0], p[0]));
						minVal[1] = (deFloatIsNaN(p[1]) ? minVal[1] : de::min(minVal[1], p[1]));
						minVal[2] = (deFloatIsNaN(p[2]) ? minVal[2] : de::min(minVal[2], p[2]));
						minVal[3] = (deFloatIsNaN(p[3]) ? minVal[3] : de::min(minVal[3], p[3]));

						maxVal[0] = (deFloatIsNaN(p[0]) ? maxVal[0] : de::max(maxVal[0], p[0]));
						maxVal[1] = (deFloatIsNaN(p[1]) ? maxVal[1] : de::max(maxVal[1], p[1]));
						maxVal[2] = (deFloatIsNaN(p[2]) ? maxVal[2] : de::max(maxVal[2], p[2]));
						maxVal[3] = (deFloatIsNaN(p[3]) ? maxVal[3] : de::max(maxVal[3], p[3]));
					}
				}
			}
			break;
	}
}

void computePixelScaleBias (const ConstPixelBufferAccess& access, Vec4& scale, Vec4& bias)
{
	Vec4 minVal, maxVal;
	estimatePixelValueRange(access, minVal, maxVal);

	const float eps = 0.0001f;

	for (int c = 0; c < 4; c++)
	{
		if (maxVal[c] - minVal[c] < eps)
		{
			scale[c]	= (maxVal[c] < eps) ? 1.0f : (1.0f / maxVal[c]);
			bias[c]		= (c == 3) ? (1.0f - maxVal[c]*scale[c]) : (0.0f - minVal[c]*scale[c]);
		}
		else
		{
			scale[c]	= 1.0f / (maxVal[c] - minVal[c]);
			bias[c]		= 0.0f - minVal[c]*scale[c];
		}
	}
}

int getCubeArrayFaceIndex (CubeFace face)
{
	DE_ASSERT((int)face >= 0 && face < CUBEFACE_LAST);

	switch (face)
	{
		case CUBEFACE_POSITIVE_X:	return 0;
		case CUBEFACE_NEGATIVE_X:	return 1;
		case CUBEFACE_POSITIVE_Y:	return 2;
		case CUBEFACE_NEGATIVE_Y:	return 3;
		case CUBEFACE_POSITIVE_Z:	return 4;
		case CUBEFACE_NEGATIVE_Z:	return 5;

		default:
			return -1;
	}
}

deUint32 packRGB999E5 (const tcu::Vec4& color)
{
	const int	mBits	= 9;
	const int	eBits	= 5;
	const int	eBias	= 15;
	const int	eMax	= (1<<eBits)-1;
	const float	maxVal	= (float)(((1<<mBits) - 1) * (1<<(eMax-eBias))) / (float)(1<<mBits);

	float	rc		= deFloatClamp(color[0], 0.0f, maxVal);
	float	gc		= deFloatClamp(color[1], 0.0f, maxVal);
	float	bc		= deFloatClamp(color[2], 0.0f, maxVal);
	float	maxc	= de::max(rc, de::max(gc, bc));
	int		expp	= de::max(-eBias - 1, deFloorFloatToInt32(deFloatLog2(maxc))) + 1 + eBias;
	float	e		= deFloatPow(2.0f, (float)(expp-eBias-mBits));
	int		maxs	= deFloorFloatToInt32(maxc / e + 0.5f);

	deUint32	exps	= maxs == (1<<mBits) ? expp+1 : expp;
	deUint32	rs		= (deUint32)deClamp32(deFloorFloatToInt32(rc / e + 0.5f), 0, (1<<9)-1);
	deUint32	gs		= (deUint32)deClamp32(deFloorFloatToInt32(gc / e + 0.5f), 0, (1<<9)-1);
	deUint32	bs		= (deUint32)deClamp32(deFloorFloatToInt32(bc / e + 0.5f), 0, (1<<9)-1);

	DE_ASSERT((exps & ~((1<<5)-1)) == 0);
	DE_ASSERT((rs & ~((1<<9)-1)) == 0);
	DE_ASSERT((gs & ~((1<<9)-1)) == 0);
	DE_ASSERT((bs & ~((1<<9)-1)) == 0);

	return rs | (gs << 9) | (bs << 18) | (exps << 27);
}

// Sampler utils

static const void* addOffset (const void* ptr, int numBytes)
{
	return (const deUint8*)ptr + numBytes;
}

static void* addOffset (void* ptr, int numBytes)
{
	return (deUint8*)ptr + numBytes;
}

template <typename AccessType>
static AccessType toSamplerAccess (const AccessType& baseAccess, Sampler::DepthStencilMode mode)
{
	// make sure to update this if type table is updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38);

	if (!isCombinedDepthStencilType(baseAccess.getFormat().type))
		return baseAccess;
	else
	{
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
		const deUint32 uint32ByteOffsetBits0To8		= 0; //!< least significant byte in the lowest address
		const deUint32 uint32ByteOffsetBits0To24	= 0;
		const deUint32 uint32ByteOffsetBits8To32	= 1;
		const deUint32 uint32ByteOffsetBits16To32	= 2;
		const deUint32 uint32ByteOffsetBits24To32	= 3;
#else
		const deUint32 uint32ByteOffsetBits0To8		= 3; //!< least significant byte in the highest address
		const deUint32 uint32ByteOffsetBits0To24	= 1;
		const deUint32 uint32ByteOffsetBits8To32	= 0;
		const deUint32 uint32ByteOffsetBits16To32	= 0;
		const deUint32 uint32ByteOffsetBits24To32	= 0;
#endif

		// Sampled channel must exist
		DE_ASSERT(baseAccess.getFormat().order == TextureFormat::DS ||
				  (mode == Sampler::MODE_DEPTH && baseAccess.getFormat().order == TextureFormat::D) ||
				  (mode == Sampler::MODE_STENCIL && baseAccess.getFormat().order == TextureFormat::S));

		// combined formats have multiple channel classes, detect on sampler settings
		switch (baseAccess.getFormat().type)
		{
			case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:
			{
				if (mode == Sampler::MODE_DEPTH)
				{
					// select the float component
					return AccessType(TextureFormat(TextureFormat::D, TextureFormat::FLOAT),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  baseAccess.getDataPtr());
				}
				else if (mode == Sampler::MODE_STENCIL)
				{
					// select the uint 8 component
					return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), 4 + uint32ByteOffsetBits0To8));
				}
				else
				{
					// unknown sampler mode
					DE_ASSERT(false);
					return AccessType();
				}
			}

			case TextureFormat::UNSIGNED_INT_16_8_8:
			{
				if (mode == Sampler::MODE_DEPTH)
				{
					// select the unorm16 component
					return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT16),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits16To32));
				}
				else if (mode == Sampler::MODE_STENCIL)
				{
					// select the uint 8 component
					return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To8));
				}
				else
				{
					// unknown sampler mode
					DE_ASSERT(false);
					return AccessType();
				}
			}

			case TextureFormat::UNSIGNED_INT_24_8:
			{
				if (mode == Sampler::MODE_DEPTH)
				{
					// select the unorm24 component
					return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT24),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits8To32));
				}
				else if (mode == Sampler::MODE_STENCIL)
				{
					// select the uint 8 component
					return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To8));
				}
				else
				{
					// unknown sampler mode
					DE_ASSERT(false);
					return AccessType();
				}
			}

			case TextureFormat::UNSIGNED_INT_24_8_REV:
			{
				if (mode == Sampler::MODE_DEPTH)
				{
					// select the unorm24 component
					return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT24),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To24));
				}
				else if (mode == Sampler::MODE_STENCIL)
				{
					// select the uint 8 component
					return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
									  baseAccess.getSize(),
									  baseAccess.getPitch(),
									  addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits24To32));
				}
				else
				{
					// unknown sampler mode
					DE_ASSERT(false);
					return AccessType();
				}
			}

			default:
			{
				// unknown combined format
				DE_ASSERT(false);
				return AccessType();
			}
		}
	}
}

PixelBufferAccess getEffectiveDepthStencilAccess (const PixelBufferAccess& baseAccess, Sampler::DepthStencilMode mode)
{
	return toSamplerAccess<PixelBufferAccess>(baseAccess, mode);
}

ConstPixelBufferAccess getEffectiveDepthStencilAccess (const ConstPixelBufferAccess& baseAccess, Sampler::DepthStencilMode mode)
{
	return toSamplerAccess<ConstPixelBufferAccess>(baseAccess, mode);
}

TextureFormat getEffectiveDepthStencilTextureFormat (const TextureFormat& baseFormat, Sampler::DepthStencilMode mode)
{
	return toSamplerAccess(ConstPixelBufferAccess(baseFormat, IVec3(0, 0, 0), DE_NULL), mode).getFormat();
}

template <typename ViewType>
ViewType getEffectiveTView (const ViewType& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	storage.resize(src.getNumLevels());

	ViewType view = ViewType(src.getNumLevels(), &storage[0]);

	for (int levelNdx = 0; levelNdx < src.getNumLevels(); ++levelNdx)
		storage[levelNdx] = tcu::getEffectiveDepthStencilAccess(src.getLevel(levelNdx), sampler.depthStencilMode);

	return view;
}

tcu::TextureCubeView getEffectiveTView (const tcu::TextureCubeView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	storage.resize(tcu::CUBEFACE_LAST * src.getNumLevels());

	const tcu::ConstPixelBufferAccess* storagePtrs[tcu::CUBEFACE_LAST] =
	{
		&storage[0 * src.getNumLevels()],
		&storage[1 * src.getNumLevels()],
		&storage[2 * src.getNumLevels()],
		&storage[3 * src.getNumLevels()],
		&storage[4 * src.getNumLevels()],
		&storage[5 * src.getNumLevels()],
	};

	tcu::TextureCubeView view = tcu::TextureCubeView(src.getNumLevels(), storagePtrs);

	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; ++faceNdx)
	for (int levelNdx = 0; levelNdx < src.getNumLevels(); ++levelNdx)
		storage[faceNdx * src.getNumLevels() + levelNdx] = tcu::getEffectiveDepthStencilAccess(src.getLevelFace(levelNdx, (tcu::CubeFace)faceNdx), sampler.depthStencilMode);

	return view;
}

tcu::Texture1DView getEffectiveTextureView (const tcu::Texture1DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::Texture2DView getEffectiveTextureView (const tcu::Texture2DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::Texture3DView getEffectiveTextureView (const tcu::Texture3DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::Texture1DArrayView getEffectiveTextureView (const tcu::Texture1DArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::Texture2DArrayView getEffectiveTextureView (const tcu::Texture2DArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::TextureCubeView getEffectiveTextureView (const tcu::TextureCubeView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

tcu::TextureCubeArrayView getEffectiveTextureView (const tcu::TextureCubeArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
	return getEffectiveTView(src, storage, sampler);
}

//! Returns the effective swizzle of a border color. The effective swizzle is the
//! equal to first writing an RGBA color with a write swizzle and then reading
//! it back using a read swizzle, i.e. BorderSwizzle(c) == readSwizzle(writeSwizzle(C))
static const TextureSwizzle& getBorderColorReadSwizzle (TextureFormat::ChannelOrder order)
{
	// make sure to update these tables when channel orders are updated
	DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);

	static const TextureSwizzle INV		= {{ TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle R		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle A		= {{ TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_3	}};
	static const TextureSwizzle I		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0	}};
	static const TextureSwizzle L		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle LA		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_3	}};
	static const TextureSwizzle RG		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_1,		TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle RA		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_3	}};
	static const TextureSwizzle RGB		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_1,		TextureSwizzle::CHANNEL_2,		TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle RGBA	= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_1,		TextureSwizzle::CHANNEL_2,		TextureSwizzle::CHANNEL_3	}};
	static const TextureSwizzle D		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ONE	}};
	static const TextureSwizzle S		= {{ TextureSwizzle::CHANNEL_0,		TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ZERO,	TextureSwizzle::CHANNEL_ONE	}};

	const TextureSwizzle* swizzle;

	switch (order)
	{
		case TextureFormat::R:			swizzle = &R;		break;
		case TextureFormat::A:			swizzle = &A;		break;
		case TextureFormat::I:			swizzle = &I;		break;
		case TextureFormat::L:			swizzle = &L;		break;
		case TextureFormat::LA:			swizzle = &LA;		break;
		case TextureFormat::RG:			swizzle = &RG;		break;
		case TextureFormat::RA:			swizzle = &RA;		break;
		case TextureFormat::RGB:		swizzle = &RGB;		break;
		case TextureFormat::RGBA:		swizzle = &RGBA;	break;
		case TextureFormat::ARGB:		swizzle = &RGBA;	break;
		case TextureFormat::BGR:		swizzle = &RGB;		break;
		case TextureFormat::BGRA:		swizzle = &RGBA;	break;
		case TextureFormat::sR:			swizzle = &R;		break;
		case TextureFormat::sRG:		swizzle = &RG;		break;
		case TextureFormat::sRGB:		swizzle = &RGB;		break;
		case TextureFormat::sRGBA:		swizzle = &RGBA;	break;
		case TextureFormat::sBGR:		swizzle = &RGB;		break;
		case TextureFormat::sBGRA:		swizzle = &RGBA;	break;
		case TextureFormat::D:			swizzle = &D;		break;
		case TextureFormat::S:			swizzle = &S;		break;

		case TextureFormat::DS:
			DE_ASSERT(false); // combined depth-stencil border color?
			swizzle = &INV;
			break;

		default:
			DE_ASSERT(false);
			swizzle = &INV;
			break;
	}

#ifdef DE_DEBUG

	{
		// check that BorderSwizzle(c) == readSwizzle(writeSwizzle(C))
		const TextureSwizzle& readSwizzle	= getChannelReadSwizzle(order);
		const TextureSwizzle& writeSwizzle	= getChannelWriteSwizzle(order);

		for (int ndx = 0; ndx < 4; ++ndx)
		{
			TextureSwizzle::Channel writeRead = readSwizzle.components[ndx];
			if (deInRange32(writeRead, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE)
				writeRead = writeSwizzle.components[(int)writeRead];
			DE_ASSERT(writeRead == swizzle->components[ndx]);
		}
	}

#endif

	return *swizzle;
}

static tcu::UVec4 getNBitUnsignedIntegerVec4MaxValue (const tcu::IVec4& numBits)
{
	return tcu::UVec4((numBits[0] > 0) ? (deUintMaxValue32(numBits[0])) : (0),
					  (numBits[1] > 0) ? (deUintMaxValue32(numBits[1])) : (0),
					  (numBits[2] > 0) ? (deUintMaxValue32(numBits[2])) : (0),
					  (numBits[3] > 0) ? (deUintMaxValue32(numBits[3])) : (0));
}

static tcu::IVec4 getNBitSignedIntegerVec4MaxValue (const tcu::IVec4& numBits)
{
	return tcu::IVec4((numBits[0] > 0) ? (deIntMaxValue32(numBits[0])) : (0),
					  (numBits[1] > 0) ? (deIntMaxValue32(numBits[1])) : (0),
					  (numBits[2] > 0) ? (deIntMaxValue32(numBits[2])) : (0),
					  (numBits[3] > 0) ? (deIntMaxValue32(numBits[3])) : (0));
}

static tcu::IVec4 getNBitSignedIntegerVec4MinValue (const tcu::IVec4& numBits)
{
	return tcu::IVec4((numBits[0] > 0) ? (deIntMinValue32(numBits[0])) : (0),
					  (numBits[1] > 0) ? (deIntMinValue32(numBits[1])) : (0),
					  (numBits[2] > 0) ? (deIntMinValue32(numBits[2])) : (0),
					  (numBits[3] > 0) ? (deIntMinValue32(numBits[3])) : (0));
}

static tcu::Vec4 getTextureBorderColorFloat (const TextureFormat& format, const Sampler& sampler)
{
	const tcu::TextureChannelClass	channelClass 	= getTextureChannelClass(format.type);
	const TextureSwizzle::Channel*	channelMap		= getBorderColorReadSwizzle(format.order).components;
	const bool						isFloat			= channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT;
	const bool						isSigned		= channelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
	const float						valueMin		= (isSigned) ? (-1.0f) : (0.0f);
	const float						valueMax		= 1.0f;
	Vec4							result;

	DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT ||
			  channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ||
			  channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT);

	for (int c = 0; c < 4; c++)
	{
		const TextureSwizzle::Channel map = channelMap[c];
		if (map == TextureSwizzle::CHANNEL_ZERO)
			result[c] = 0.0f;
		else if (map == TextureSwizzle::CHANNEL_ONE)
			result[c] = 1.0f;
		else if (isFloat)
		{
			// floating point values are not clamped
			result[c] = sampler.borderColor.getAccess<float>()[(int)map];
		}
		else
		{
			// fixed point values are clamped to a representable range
			result[c] = de::clamp(sampler.borderColor.getAccess<float>()[(int)map], valueMin, valueMax);
		}
	}

	return result;
}

static tcu::IVec4 getTextureBorderColorInt (const TextureFormat& format, const Sampler& sampler)
{
	const tcu::TextureChannelClass	channelClass 	= getTextureChannelClass(format.type);
	const TextureSwizzle::Channel*	channelMap		= getBorderColorReadSwizzle(format.order).components;
	const IVec4						channelBits		= getChannelBitDepth(format.type);
	const IVec4						valueMin		= getNBitSignedIntegerVec4MinValue(channelBits);
	const IVec4						valueMax		= getNBitSignedIntegerVec4MaxValue(channelBits);
	IVec4							result;

	DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER);
	DE_UNREF(channelClass);

	for (int c = 0; c < 4; c++)
	{
		const TextureSwizzle::Channel map = channelMap[c];
		if (map == TextureSwizzle::CHANNEL_ZERO)
			result[c] = 0;
		else if (map == TextureSwizzle::CHANNEL_ONE)
			result[c] = 1;
		else
		{
			// integer values are clamped to a representable range
			result[c] = de::clamp(sampler.borderColor.getAccess<deInt32>()[(int)map], valueMin[(int)map], valueMax[(int)map]);
		}
	}

	return result;
}

static tcu::UVec4 getTextureBorderColorUint (const TextureFormat& format, const Sampler& sampler)
{
	const tcu::TextureChannelClass	channelClass 	= getTextureChannelClass(format.type);
	const TextureSwizzle::Channel*	channelMap		= getBorderColorReadSwizzle(format.order).components;
	const IVec4						channelBits		= getChannelBitDepth(format.type);
	const UVec4						valueMax		= getNBitUnsignedIntegerVec4MaxValue(channelBits);
	UVec4							result;

	DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER);
	DE_UNREF(channelClass);

	for (int c = 0; c < 4; c++)
	{
		const TextureSwizzle::Channel map = channelMap[c];
		if (map == TextureSwizzle::CHANNEL_ZERO)
			result[c] = 0;
		else if (map == TextureSwizzle::CHANNEL_ONE)
			result[c] = 1;
		else
		{
			// integer values are clamped to a representable range
			result[c] = de::min(sampler.borderColor.getAccess<deUint32>()[(int)map], valueMax[(int)map]);
		}
	}

	return result;
}

template <typename ScalarType>
tcu::Vector<ScalarType, 4> sampleTextureBorder (const TextureFormat& format, const Sampler& sampler)
{
	const tcu::TextureChannelClass channelClass = getTextureChannelClass(format.type);

	switch (channelClass)
	{
		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
			return getTextureBorderColorFloat(format, sampler).cast<ScalarType>();

		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
			return getTextureBorderColorInt(format, sampler).cast<ScalarType>();

		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
			return getTextureBorderColorUint(format, sampler).cast<ScalarType>();

		default:
			DE_ASSERT(false);
			return tcu::Vector<ScalarType, 4>();
	}
}

// instantiation
template tcu::Vector<float, 4>		sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);
template tcu::Vector<deInt32, 4>	sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);
template tcu::Vector<deUint32, 4>	sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);

} // tcu