1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Texture utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuTextureUtil.hpp"
25#include "tcuVectorUtil.hpp"
26#include "deRandom.hpp"
27#include "deMath.h"
28#include "deMemory.h"
29
30#include <limits>
31
32namespace tcu
33{
34
35static inline float sRGBChannelToLinear (float cs)
36{
37	if (cs <= 0.04045)
38		return cs / 12.92f;
39	else
40		return deFloatPow((cs + 0.055f) / 1.055f, 2.4f);
41}
42
43static inline float linearChannelToSRGB (float cl)
44{
45	if (cl <= 0.0f)
46		return 0.0f;
47	else if (cl < 0.0031308f)
48		return 12.92f*cl;
49	else if (cl < 1.0f)
50		return 1.055f*deFloatPow(cl, 0.41666f) - 0.055f;
51	else
52		return 1.0f;
53}
54
55//! Convert sRGB to linear colorspace
56Vec4 sRGBToLinear (const Vec4& cs)
57{
58	return Vec4(sRGBChannelToLinear(cs[0]),
59				sRGBChannelToLinear(cs[1]),
60				sRGBChannelToLinear(cs[2]),
61				cs[3]);
62}
63
64//! Convert from linear to sRGB colorspace
65Vec4 linearToSRGB (const Vec4& cl)
66{
67	return Vec4(linearChannelToSRGB(cl[0]),
68				linearChannelToSRGB(cl[1]),
69				linearChannelToSRGB(cl[2]),
70				cl[3]);
71}
72
73//! Get texture channel class for format
74TextureChannelClass getTextureChannelClass (TextureFormat::ChannelType channelType)
75{
76	switch (channelType)
77	{
78		case TextureFormat::SNORM_INT8:						return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
79		case TextureFormat::SNORM_INT16:					return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
80		case TextureFormat::UNORM_INT8:						return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
81		case TextureFormat::UNORM_INT16:					return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
82		case TextureFormat::UNORM_SHORT_565:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
83		case TextureFormat::UNORM_SHORT_555:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
84		case TextureFormat::UNORM_SHORT_4444:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
85		case TextureFormat::UNORM_SHORT_5551:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
86		case TextureFormat::UNORM_INT_101010:				return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
87		case TextureFormat::UNORM_INT_1010102_REV:			return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
88		case TextureFormat::UNSIGNED_INT_1010102_REV:		return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
89		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return TEXTURECHANNELCLASS_FLOATING_POINT;
90		case TextureFormat::UNSIGNED_INT_999_E5_REV:		return TEXTURECHANNELCLASS_FLOATING_POINT;
91		case TextureFormat::SIGNED_INT8:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
92		case TextureFormat::SIGNED_INT16:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
93		case TextureFormat::SIGNED_INT32:					return TEXTURECHANNELCLASS_SIGNED_INTEGER;
94		case TextureFormat::UNSIGNED_INT8:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
95		case TextureFormat::UNSIGNED_INT16:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
96		case TextureFormat::UNSIGNED_INT32:					return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
97		case TextureFormat::HALF_FLOAT:						return TEXTURECHANNELCLASS_FLOATING_POINT;
98		case TextureFormat::FLOAT:							return TEXTURECHANNELCLASS_FLOATING_POINT;
99		default:											return TEXTURECHANNELCLASS_LAST;
100	}
101}
102
103/*--------------------------------------------------------------------*//*!
104 * \brief Get access to subregion of pixel buffer
105 * \param access	Parent access object
106 * \param x			X offset
107 * \param y			Y offset
108 * \param z			Z offset
109 * \param width		Width
110 * \param height	Height
111 * \param depth		Depth
112 * \return Access object that targets given subregion of parent access object
113 *//*--------------------------------------------------------------------*/
114ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
115{
116	DE_ASSERT(de::inBounds(x, 0, access.getWidth())		&& de::inRange(x+width,		x, access.getWidth()));
117	DE_ASSERT(de::inBounds(y, 0, access.getHeight())	&& de::inRange(y+height,	y, access.getHeight()));
118	DE_ASSERT(de::inBounds(z, 0, access.getDepth())		&& de::inRange(z+depth,		z, access.getDepth()));
119	return ConstPixelBufferAccess(access.getFormat(), width, height, depth, access.getRowPitch(), access.getSlicePitch(),
120								  (const deUint8*)access.getDataPtr() + access.getFormat().getPixelSize()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
121}
122
123/*--------------------------------------------------------------------*//*!
124 * \brief Get access to subregion of pixel buffer
125 * \param access	Parent access object
126 * \param x			X offset
127 * \param y			Y offset
128 * \param z			Z offset
129 * \param width		Width
130 * \param height	Height
131 * \param depth		Depth
132 * \return Access object that targets given subregion of parent access object
133 *//*--------------------------------------------------------------------*/
134PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
135{
136	DE_ASSERT(de::inBounds(x, 0, access.getWidth())		&& de::inRange(x+width,		x, access.getWidth()));
137	DE_ASSERT(de::inBounds(y, 0, access.getHeight())	&& de::inRange(y+height,	y, access.getHeight()));
138	DE_ASSERT(de::inBounds(z, 0, access.getDepth())		&& de::inRange(z+depth,		z, access.getDepth()));
139	return PixelBufferAccess(access.getFormat(), width, height, depth, access.getRowPitch(), access.getSlicePitch(),
140							 (deUint8*)access.getDataPtr() + access.getFormat().getPixelSize()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
141}
142
143/*--------------------------------------------------------------------*//*!
144 * \brief Get access to subregion of pixel buffer
145 * \param access	Parent access object
146 * \param x			X offset
147 * \param y			Y offset
148 * \param width		Width
149 * \param height	Height
150 * \return Access object that targets given subregion of parent access object
151 *//*--------------------------------------------------------------------*/
152PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int width, int height)
153{
154	return getSubregion(access, x, y, 0, width, height, 1);
155}
156
157/*--------------------------------------------------------------------*//*!
158 * \brief Get access to subregion of pixel buffer
159 * \param access	Parent access object
160 * \param x			X offset
161 * \param y			Y offset
162 * \param width		Width
163 * \param height	Height
164 * \return Access object that targets given subregion of parent access object
165 *//*--------------------------------------------------------------------*/
166ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int width, int height)
167{
168	return getSubregion(access, x, y, 0, width, height, 1);
169}
170
171/*--------------------------------------------------------------------*//*!
172 * \brief Flip rows in Y direction
173 * \param access Access object
174 * \return Modified access object where Y coordinates are reversed
175 *//*--------------------------------------------------------------------*/
176PixelBufferAccess flipYAccess (const PixelBufferAccess& access)
177{
178	const int	rowPitch		= access.getRowPitch();
179	const int	offsetToLast	= rowPitch*(access.getHeight()-1);
180
181	return PixelBufferAccess(access.getFormat(), access.getWidth(), access.getHeight(), access.getDepth(),
182							 -rowPitch, access.getSlicePitch(), (deUint8*)access.getDataPtr() + offsetToLast);
183}
184
185/*--------------------------------------------------------------------*//*!
186 * \brief Flip rows in Y direction
187 * \param access Access object
188 * \return Modified access object where Y coordinates are reversed
189 *//*--------------------------------------------------------------------*/
190ConstPixelBufferAccess flipYAccess (const ConstPixelBufferAccess& access)
191{
192	const int	rowPitch		= access.getRowPitch();
193	const int	offsetToLast	= rowPitch*(access.getHeight()-1);
194
195	return ConstPixelBufferAccess(access.getFormat(), access.getWidth(), access.getHeight(), access.getDepth(),
196								  -rowPitch, access.getSlicePitch(), (const deUint8*)access.getDataPtr() + offsetToLast);
197}
198
199static Vec2 getChannelValueRange (TextureFormat::ChannelType channelType)
200{
201	float cMin = 0.0f;
202	float cMax = 0.0f;
203
204	switch (channelType)
205	{
206		// Signed normalized formats.
207		case TextureFormat::SNORM_INT8:
208		case TextureFormat::SNORM_INT16:					cMin = -1.0f;			cMax = 1.0f;			break;
209
210		// Unsigned normalized formats.
211		case TextureFormat::UNORM_INT8:
212		case TextureFormat::UNORM_INT16:
213		case TextureFormat::UNORM_SHORT_565:
214		case TextureFormat::UNORM_SHORT_4444:
215		case TextureFormat::UNORM_INT_101010:
216		case TextureFormat::UNORM_INT_1010102_REV:			cMin = 0.0f;			cMax = 1.0f;			break;
217
218		// Misc formats.
219		case TextureFormat::SIGNED_INT8:					cMin = -128.0f;			cMax = 127.0f;			break;
220		case TextureFormat::SIGNED_INT16:					cMin = -32768.0f;		cMax = 32767.0f;		break;
221		case TextureFormat::SIGNED_INT32:					cMin = -2147483648.0f;	cMax = 2147483647.0f;	break;
222		case TextureFormat::UNSIGNED_INT8:					cMin = 0.0f;			cMax = 255.0f;			break;
223		case TextureFormat::UNSIGNED_INT16:					cMin = 0.0f;			cMax = 65535.0f;		break;
224		case TextureFormat::UNSIGNED_INT32:					cMin = 0.0f;			cMax = 4294967295.f;	break;
225		case TextureFormat::HALF_FLOAT:						cMin = -1e3f;			cMax = 1e3f;			break;
226		case TextureFormat::FLOAT:							cMin = -1e5f;			cMax = 1e5f;			break;
227		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	cMin = 0.0f;			cMax = 1e4f;			break;
228		case TextureFormat::UNSIGNED_INT_999_E5_REV:		cMin = 0.0f;			cMax = 1e5f;			break;
229
230		default:
231			DE_ASSERT(false);
232	}
233
234	return Vec2(cMin, cMax);
235}
236
237/*--------------------------------------------------------------------*//*!
238 * \brief Get standard parameters for testing texture format
239 *
240 * Returns TextureFormatInfo that describes good parameters for exercising
241 * given TextureFormat. Parameters include value ranges per channel and
242 * suitable lookup scaling and bias in order to reduce result back to
243 * 0..1 range.
244 *//*--------------------------------------------------------------------*/
245TextureFormatInfo getTextureFormatInfo (const TextureFormat& format)
246{
247	// Special cases.
248	if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT_1010102_REV))
249		return TextureFormatInfo(Vec4(	    0.0f,		    0.0f,		    0.0f,		 0.0f),
250								 Vec4(	 1023.0f,		 1023.0f,		 1023.0f,		 3.0f),
251								 Vec4(1.0f/1023.f,	1.0f/1023.0f,	1.0f/1023.0f,	1.0f/3.0f),
252								 Vec4(	    0.0f,		    0.0f,		    0.0f,		 0.0f));
253	else if (format.order == TextureFormat::D || format.order == TextureFormat::DS)
254		return TextureFormatInfo(Vec4(0.0f,	0.0f,	0.0f,	0.0f),
255								 Vec4(1.0f,	1.0f,	1.0f,	0.0f),
256								 Vec4(1.0f,	1.0f,	1.0f,	1.0f),
257								 Vec4(0.0f,	0.0f,	0.0f,	0.0f)); // Depth / stencil formats.
258	else if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551))
259		return TextureFormatInfo(Vec4(0.0f, 0.0f, 0.0f, 0.5f),
260								 Vec4(1.0f, 1.0f, 1.0f, 1.5f),
261								 Vec4(1.0f, 1.0f, 1.0f, 1.0f),
262								 Vec4(0.0f, 0.0f, 0.0f, 0.0f));
263
264	Vec2	cRange		= getChannelValueRange(format.type);
265	BVec4	chnMask		= BVec4(false);
266
267	switch (format.order)
268	{
269		case TextureFormat::R:		chnMask = BVec4(true,	false,	false,	false);		break;
270		case TextureFormat::A:		chnMask = BVec4(false,	false,	false,	true);		break;
271		case TextureFormat::L:		chnMask = BVec4(true,	true,	true,	false);		break;
272		case TextureFormat::LA:		chnMask = BVec4(true,	true,	true,	true);		break;
273		case TextureFormat::RG:		chnMask = BVec4(true,	true,	false,	false);		break;
274		case TextureFormat::RGB:	chnMask = BVec4(true,	true,	true,	false);		break;
275		case TextureFormat::RGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
276		case TextureFormat::sRGB:	chnMask = BVec4(true,	true,	true,	false);		break;
277		case TextureFormat::sRGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
278		case TextureFormat::D:		chnMask = BVec4(true,	true,	true,	false);		break;
279		case TextureFormat::DS:		chnMask = BVec4(true,	true,	true,	true);		break;
280		default:
281			DE_ASSERT(false);
282	}
283
284	float	scale	= 1.0f / (cRange[1] - cRange[0]);
285	float	bias	= -cRange[0] * scale;
286
287	return TextureFormatInfo(select(cRange[0],	0.0f, chnMask),
288							 select(cRange[1],	0.0f, chnMask),
289							 select(scale,		1.0f, chnMask),
290							 select(bias,		0.0f, chnMask));
291}
292
293static IVec4 getChannelBitDepth (TextureFormat::ChannelType channelType)
294{
295	switch (channelType)
296	{
297		case TextureFormat::SNORM_INT8:						return IVec4(8);
298		case TextureFormat::SNORM_INT16:					return IVec4(16);
299		case TextureFormat::SNORM_INT32:					return IVec4(32);
300		case TextureFormat::UNORM_INT8:						return IVec4(8);
301		case TextureFormat::UNORM_INT16:					return IVec4(16);
302		case TextureFormat::UNORM_INT32:					return IVec4(32);
303		case TextureFormat::UNORM_SHORT_565:				return IVec4(5,6,5,0);
304		case TextureFormat::UNORM_SHORT_4444:				return IVec4(4);
305		case TextureFormat::UNORM_SHORT_555:				return IVec4(5,5,5,0);
306		case TextureFormat::UNORM_SHORT_5551:				return IVec4(5,5,5,1);
307		case TextureFormat::UNORM_INT_101010:				return IVec4(10,10,10,0);
308		case TextureFormat::UNORM_INT_1010102_REV:			return IVec4(10,10,10,2);
309		case TextureFormat::SIGNED_INT8:					return IVec4(8);
310		case TextureFormat::SIGNED_INT16:					return IVec4(16);
311		case TextureFormat::SIGNED_INT32:					return IVec4(32);
312		case TextureFormat::UNSIGNED_INT8:					return IVec4(8);
313		case TextureFormat::UNSIGNED_INT16:					return IVec4(16);
314		case TextureFormat::UNSIGNED_INT32:					return IVec4(32);
315		case TextureFormat::UNSIGNED_INT_1010102_REV:		return IVec4(10,10,10,2);
316		case TextureFormat::UNSIGNED_INT_24_8:				return IVec4(24,0,0,8);
317		case TextureFormat::HALF_FLOAT:						return IVec4(16);
318		case TextureFormat::FLOAT:							return IVec4(32);
319		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return IVec4(11,11,10,0);
320		case TextureFormat::UNSIGNED_INT_999_E5_REV:		return IVec4(9,9,9,0);
321		case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return IVec4(32,0,0,8);
322		default:
323			DE_ASSERT(false);
324			return IVec4(0);
325	}
326}
327
328IVec4 getTextureFormatBitDepth (const TextureFormat& format)
329{
330	IVec4	chnBits		= getChannelBitDepth(format.type);
331	BVec4	chnMask		= BVec4(false);
332	IVec4	chnSwz		(0,1,2,3);
333
334	switch (format.order)
335	{
336		case TextureFormat::R:		chnMask = BVec4(true,	false,	false,	false);		break;
337		case TextureFormat::A:		chnMask = BVec4(false,	false,	false,	true);		break;
338		case TextureFormat::RA:		chnMask = BVec4(true,	false,	false,	true);		break;
339		case TextureFormat::L:		chnMask = BVec4(true,	true,	true,	false);		break;
340		case TextureFormat::I:		chnMask = BVec4(true,	true,	true,	true);		break;
341		case TextureFormat::LA:		chnMask = BVec4(true,	true,	true,	true);		break;
342		case TextureFormat::RG:		chnMask = BVec4(true,	true,	false,	false);		break;
343		case TextureFormat::RGB:	chnMask = BVec4(true,	true,	true,	false);		break;
344		case TextureFormat::RGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
345		case TextureFormat::BGRA:	chnMask = BVec4(true,	true,	true,	true);		chnSwz = IVec4(2, 1, 0, 3);	break;
346		case TextureFormat::ARGB:	chnMask = BVec4(true,	true,	true,	true);		chnSwz = IVec4(1, 2, 3, 0);	break;
347		case TextureFormat::sRGB:	chnMask = BVec4(true,	true,	true,	false);		break;
348		case TextureFormat::sRGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
349		case TextureFormat::D:		chnMask = BVec4(true,	false,	false,	false);		break;
350		case TextureFormat::DS:		chnMask = BVec4(true,	false,	false,	true);		break;
351		case TextureFormat::S:		chnMask = BVec4(false,	false,	false,	true);		break;
352		default:
353			DE_ASSERT(false);
354	}
355
356	return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
357}
358
359static IVec4 getChannelMantissaBitDepth (TextureFormat::ChannelType channelType)
360{
361	switch (channelType)
362	{
363		case TextureFormat::SNORM_INT8:
364		case TextureFormat::SNORM_INT16:
365		case TextureFormat::SNORM_INT32:
366		case TextureFormat::UNORM_INT8:
367		case TextureFormat::UNORM_INT16:
368		case TextureFormat::UNORM_INT32:
369		case TextureFormat::UNORM_SHORT_565:
370		case TextureFormat::UNORM_SHORT_4444:
371		case TextureFormat::UNORM_SHORT_555:
372		case TextureFormat::UNORM_SHORT_5551:
373		case TextureFormat::UNORM_INT_101010:
374		case TextureFormat::UNORM_INT_1010102_REV:
375		case TextureFormat::SIGNED_INT8:
376		case TextureFormat::SIGNED_INT16:
377		case TextureFormat::SIGNED_INT32:
378		case TextureFormat::UNSIGNED_INT8:
379		case TextureFormat::UNSIGNED_INT16:
380		case TextureFormat::UNSIGNED_INT32:
381		case TextureFormat::UNSIGNED_INT_1010102_REV:
382		case TextureFormat::UNSIGNED_INT_24_8:
383		case TextureFormat::UNSIGNED_INT_999_E5_REV:
384			return getChannelBitDepth(channelType);
385
386		case TextureFormat::HALF_FLOAT:						return IVec4(10);
387		case TextureFormat::FLOAT:							return IVec4(23);
388		case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	return IVec4(6,6,5,0);
389		case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return IVec4(23,0,0,8);
390		default:
391			DE_ASSERT(false);
392			return IVec4(0);
393	}
394}
395
396IVec4 getTextureFormatMantissaBitDepth (const TextureFormat& format)
397{
398	IVec4	chnBits		= getChannelMantissaBitDepth(format.type);
399	BVec4	chnMask		= BVec4(false);
400	IVec4	chnSwz		(0,1,2,3);
401
402	switch (format.order)
403	{
404		case TextureFormat::R:		chnMask = BVec4(true,	false,	false,	false);		break;
405		case TextureFormat::A:		chnMask = BVec4(false,	false,	false,	true);		break;
406		case TextureFormat::RA:		chnMask = BVec4(true,	false,	false,	true);		break;
407		case TextureFormat::L:		chnMask = BVec4(true,	true,	true,	false);		break;
408		case TextureFormat::I:		chnMask = BVec4(true,	true,	true,	true);		break;
409		case TextureFormat::LA:		chnMask = BVec4(true,	true,	true,	true);		break;
410		case TextureFormat::RG:		chnMask = BVec4(true,	true,	false,	false);		break;
411		case TextureFormat::RGB:	chnMask = BVec4(true,	true,	true,	false);		break;
412		case TextureFormat::RGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
413		case TextureFormat::BGRA:	chnMask = BVec4(true,	true,	true,	true);		chnSwz = IVec4(2, 1, 0, 3);	break;
414		case TextureFormat::ARGB:	chnMask = BVec4(true,	true,	true,	true);		chnSwz = IVec4(1, 2, 3, 0);	break;
415		case TextureFormat::sRGB:	chnMask = BVec4(true,	true,	true,	false);		break;
416		case TextureFormat::sRGBA:	chnMask = BVec4(true,	true,	true,	true);		break;
417		case TextureFormat::D:		chnMask = BVec4(true,	false,	false,	false);		break;
418		case TextureFormat::DS:		chnMask = BVec4(true,	false,	false,	true);		break;
419		case TextureFormat::S:		chnMask = BVec4(false,	false,	false,	true);		break;
420		default:
421			DE_ASSERT(false);
422	}
423
424	return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
425}
426
427static inline float linearInterpolate (float t, float minVal, float maxVal)
428{
429	return minVal + (maxVal - minVal) * t;
430}
431
432static inline Vec4 linearInterpolate (float t, const Vec4& a, const Vec4& b)
433{
434	return a + (b - a) * t;
435}
436
437enum
438{
439	CLEAR_OPTIMIZE_THRESHOLD		= 128,
440	CLEAR_OPTIMIZE_MAX_PIXEL_SIZE	= 8
441};
442
443inline void fillRow (const PixelBufferAccess& dst, int y, int z, int pixelSize, const deUint8* pixel)
444{
445	deUint8*	dstPtr	= (deUint8*)dst.getDataPtr() + z*dst.getSlicePitch() + y*dst.getRowPitch();
446	int			width	= dst.getWidth();
447
448	if (pixelSize == 8 && deIsAlignedPtr(dstPtr, pixelSize) && deIsAlignedPtr(dstPtr, pixelSize))
449	{
450		deUint64 val;
451		memcpy(&val, pixel, sizeof(val));
452
453		for (int i = 0; i < width; i++)
454			((deUint64*)dstPtr)[i] = val;
455	}
456	else if (pixelSize == 4 && deIsAlignedPtr(dstPtr, pixelSize) && deIsAlignedPtr(dstPtr, pixelSize))
457	{
458		deUint32 val;
459		memcpy(&val, pixel, sizeof(val));
460
461		for (int i = 0; i < width; i++)
462			((deUint32*)dstPtr)[i] = val;
463	}
464	else
465	{
466		for (int i = 0; i < width; i++)
467			for (int j = 0; j < pixelSize; j++)
468				dstPtr[i*pixelSize+j] = pixel[j];
469	}
470}
471
472void clear (const PixelBufferAccess& access, const Vec4& color)
473{
474	int pixelSize = access.getFormat().getPixelSize();
475	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
476		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE)
477	{
478		// Convert to destination format.
479		union
480		{
481			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
482			deUint64	u64; // Forces 64-bit alignment.
483		} pixel;
484		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
485		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);
486
487		for (int z = 0; z < access.getDepth(); z++)
488			for (int y = 0; y < access.getHeight(); y++)
489				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
490	}
491	else
492	{
493		for (int z = 0; z < access.getDepth(); z++)
494			for (int y = 0; y < access.getHeight(); y++)
495				for (int x = 0; x < access.getWidth(); x++)
496					access.setPixel(color, x, y, z);
497	}
498}
499
500void clear (const PixelBufferAccess& access, const IVec4& color)
501{
502	int pixelSize = access.getFormat().getPixelSize();
503	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
504		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE)
505	{
506		// Convert to destination format.
507		union
508		{
509			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
510			deUint64	u64; // Forces 64-bit alignment.
511		} pixel;
512		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
513		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);
514
515		for (int z = 0; z < access.getDepth(); z++)
516			for (int y = 0; y < access.getHeight(); y++)
517				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
518	}
519	else
520	{
521		for (int z = 0; z < access.getDepth(); z++)
522			for (int y = 0; y < access.getHeight(); y++)
523				for (int x = 0; x < access.getWidth(); x++)
524					access.setPixel(color, x, y, z);
525	}
526}
527
528void clearDepth (const PixelBufferAccess& access, float depth)
529{
530	int pixelSize = access.getFormat().getPixelSize();
531	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
532		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE)
533	{
534		// Convert to destination format.
535		union
536		{
537			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
538			deUint64	u64; // Forces 64-bit alignment.
539		} pixel;
540		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
541		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixDepth(depth, 0, 0);
542
543		for (int z = 0; z < access.getDepth(); z++)
544			for (int y = 0; y < access.getHeight(); y++)
545				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
546	}
547	else
548	{
549		for (int z = 0; z < access.getDepth(); z++)
550			for (int y = 0; y < access.getHeight(); y++)
551				for (int x = 0; x < access.getWidth(); x++)
552					access.setPixDepth(depth, x, y, z);
553	}
554}
555
556void clearStencil (const PixelBufferAccess& access, int stencil)
557{
558	int pixelSize = access.getFormat().getPixelSize();
559	if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
560		pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE)
561	{
562		// Convert to destination format.
563		union
564		{
565			deUint8		u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
566			deUint64	u64; // Forces 64-bit alignment.
567		} pixel;
568		DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
569		PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixStencil(stencil, 0, 0);
570
571		for (int z = 0; z < access.getDepth(); z++)
572			for (int y = 0; y < access.getHeight(); y++)
573				fillRow(access, y, z, pixelSize, &pixel.u8[0]);
574	}
575	else
576	{
577		for (int z = 0; z < access.getDepth(); z++)
578			for (int y = 0; y < access.getHeight(); y++)
579				for (int x = 0; x < access.getWidth(); x++)
580					access.setPixStencil(stencil, x, y, z);
581	}
582}
583
584static void fillWithComponentGradients1D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
585{
586	DE_ASSERT(access.getHeight() == 1);
587	for (int x = 0; x < access.getWidth(); x++)
588	{
589		float s	= ((float)x + 0.5f) / (float)access.getWidth();
590
591		float r	= linearInterpolate(s, minVal.x(), maxVal.x());
592		float g = linearInterpolate(s, minVal.y(), maxVal.y());
593		float b = linearInterpolate(s, minVal.z(), maxVal.z());
594		float a = linearInterpolate(s, minVal.w(), maxVal.w());
595
596		access.setPixel(tcu::Vec4(r, g, b, a), x, 0);
597	}
598}
599
600static void fillWithComponentGradients2D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
601{
602	for (int y = 0; y < access.getHeight(); y++)
603	{
604		for (int x = 0; x < access.getWidth(); x++)
605		{
606			float s	= ((float)x + 0.5f) / (float)access.getWidth();
607			float t	= ((float)y + 0.5f) / (float)access.getHeight();
608
609			float r	= linearInterpolate((      s  +       t) *0.5f, minVal.x(), maxVal.x());
610			float g = linearInterpolate((      s  + (1.0f-t))*0.5f, minVal.y(), maxVal.y());
611			float b = linearInterpolate(((1.0f-s) +       t) *0.5f, minVal.z(), maxVal.z());
612			float a = linearInterpolate(((1.0f-s) + (1.0f-t))*0.5f, minVal.w(), maxVal.w());
613
614			access.setPixel(tcu::Vec4(r, g, b, a), x, y);
615		}
616	}
617}
618
619static void fillWithComponentGradients3D (const PixelBufferAccess& dst, const Vec4& minVal, const Vec4& maxVal)
620{
621	for (int z = 0; z < dst.getDepth(); z++)
622	{
623		for (int y = 0; y < dst.getHeight(); y++)
624		{
625			for (int x = 0; x < dst.getWidth(); x++)
626			{
627				float s = ((float)x + 0.5f) / (float)dst.getWidth();
628				float t = ((float)y + 0.5f) / (float)dst.getHeight();
629				float p = ((float)z + 0.5f) / (float)dst.getDepth();
630
631				float r = linearInterpolate(s,						minVal.x(), maxVal.x());
632				float g = linearInterpolate(t,						minVal.y(), maxVal.y());
633				float b = linearInterpolate(p,						minVal.z(), maxVal.z());
634				float a = linearInterpolate(1.0f - (s+t+p)/3.0f,	minVal.w(), maxVal.w());
635
636				dst.setPixel(tcu::Vec4(r, g, b, a), x, y, z);
637			}
638		}
639	}
640}
641
642void fillWithComponentGradients (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
643{
644	if (access.getHeight() == 1 && access.getDepth() == 1)
645		fillWithComponentGradients1D(access, minVal, maxVal);
646	else if (access.getDepth() == 1)
647		fillWithComponentGradients2D(access, minVal, maxVal);
648	else
649		fillWithComponentGradients3D(access, minVal, maxVal);
650}
651
652void fillWithGrid1D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
653{
654	for (int x = 0; x < access.getWidth(); x++)
655	{
656		int mx = (x / cellSize) % 2;
657
658		if (mx)
659			access.setPixel(colorB, x, 0);
660		else
661			access.setPixel(colorA, x, 0);
662	}
663}
664
665void fillWithGrid2D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
666{
667	for (int y = 0; y < access.getHeight(); y++)
668	{
669		for (int x = 0; x < access.getWidth(); x++)
670		{
671			int mx = (x / cellSize) % 2;
672			int my = (y / cellSize) % 2;
673
674			if (mx ^ my)
675				access.setPixel(colorB, x, y);
676			else
677				access.setPixel(colorA, x, y);
678		}
679	}
680}
681
682void fillWithGrid3D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
683{
684	for (int z = 0; z < access.getDepth(); z++)
685	{
686		for (int y = 0; y < access.getHeight(); y++)
687		{
688			for (int x = 0; x < access.getWidth(); x++)
689			{
690				int mx = (x / cellSize) % 2;
691				int my = (y / cellSize) % 2;
692				int mz = (z / cellSize) % 2;
693
694				if (mx ^ my ^ mz)
695					access.setPixel(colorB, x, y, z);
696				else
697					access.setPixel(colorA, x, y, z);
698			}
699		}
700	}
701}
702
703void fillWithGrid (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
704{
705	if (access.getHeight() == 1 && access.getDepth() == 1)
706		fillWithGrid1D(access, cellSize, colorA, colorB);
707	else if (access.getDepth() == 1)
708		fillWithGrid2D(access, cellSize, colorA, colorB);
709	else
710		fillWithGrid3D(access, cellSize, colorA, colorB);
711}
712
713void fillWithRepeatableGradient (const PixelBufferAccess& access, const Vec4& colorA, const Vec4& colorB)
714{
715	for (int y = 0; y < access.getHeight(); y++)
716	{
717		for (int x = 0; x < access.getWidth(); x++)
718		{
719			float s = ((float)x + 0.5f) / (float)access.getWidth();
720			float t = ((float)y + 0.5f) / (float)access.getHeight();
721
722			float a = s > 0.5f ? (2.0f - 2.0f*s) : 2.0f*s;
723			float b = t > 0.5f ? (2.0f - 2.0f*t) : 2.0f*t;
724
725			float p = deFloatClamp(deFloatSqrt(a*a + b*b), 0.0f, 1.0f);
726			access.setPixel(linearInterpolate(p, colorA, colorB), x, y);
727		}
728	}
729}
730
731void fillWithRGBAQuads (const PixelBufferAccess& dst)
732{
733	TCU_CHECK_INTERNAL(dst.getDepth() == 1);
734	int width	= dst.getWidth();
735	int height	= dst.getHeight();
736	int	left	= width/2;
737	int top		= height/2;
738
739	clear(getSubregion(dst, 0,		0,		0, left,		top,		1),	Vec4(1.0f, 0.0f, 0.0f, 1.0f));
740	clear(getSubregion(dst, left,	0,		0, width-left,	top,		1),	Vec4(0.0f, 1.0f, 0.0f, 1.0f));
741	clear(getSubregion(dst, 0,		top,	0, left,		height-top,	1), Vec4(0.0f, 0.0f, 1.0f, 0.0f));
742	clear(getSubregion(dst, left,	top,	0, width-left,	height-top, 1), Vec4(0.5f, 0.5f, 0.5f, 1.0f));
743}
744
745// \todo [2012-11-13 pyry] There is much better metaballs code in CL SIR value generators.
746void fillWithMetaballs (const PixelBufferAccess& dst, int numBalls, deUint32 seed)
747{
748	TCU_CHECK_INTERNAL(dst.getDepth() == 1);
749	std::vector<Vec2>	points(numBalls);
750	de::Random			rnd(seed);
751
752	for (int i = 0; i < numBalls; i++)
753	{
754		float x = rnd.getFloat();
755		float y = rnd.getFloat();
756		points[i] = (Vec2(x, y));
757	}
758
759	for (int y = 0; y < dst.getHeight(); y++)
760	for (int x = 0; x < dst.getWidth(); x++)
761	{
762		Vec2 p((float)x/(float)dst.getWidth(), (float)y/(float)dst.getHeight());
763
764		float sum = 0.0f;
765		for (std::vector<Vec2>::const_iterator i = points.begin(); i != points.end(); i++)
766		{
767			Vec2	d = p - *i;
768			float	f = 0.01f / (d.x()*d.x() + d.y()*d.y());
769
770			sum += f;
771		}
772
773		dst.setPixel(Vec4(sum), x, y);
774	}
775}
776
777void copy (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src)
778{
779	int		width		= dst.getWidth();
780	int		height		= dst.getHeight();
781	int		depth		= dst.getDepth();
782
783	DE_ASSERT(src.getWidth() == width && src.getHeight() == height && src.getDepth() == depth);
784
785	if (src.getFormat() == dst.getFormat())
786	{
787		// Fast-path for matching formats.
788		int pixelSize = src.getFormat().getPixelSize();
789
790		for (int z = 0; z < depth; z++)
791		for (int y = 0; y < height; y++)
792			deMemcpy((deUint8*)dst.getDataPtr()			+ z*dst.getSlicePitch() + y*dst.getRowPitch(),
793					 (const deUint8*)src.getDataPtr()	+ z*src.getSlicePitch() + y*src.getRowPitch(),
794					 pixelSize*width);
795	}
796	else
797	{
798		TextureChannelClass		srcClass	= getTextureChannelClass(src.getFormat().type);
799		TextureChannelClass		dstClass	= getTextureChannelClass(dst.getFormat().type);
800		bool					srcIsInt	= srcClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || srcClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
801		bool					dstIsInt	= dstClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || dstClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
802
803		if (srcIsInt && dstIsInt)
804		{
805			for (int z = 0; z < depth; z++)
806			for (int y = 0; y < height; y++)
807			for (int x = 0; x < width; x++)
808				dst.setPixel(src.getPixelInt(x, y, z), x, y, z);
809		}
810		else
811		{
812			for (int z = 0; z < depth; z++)
813			for (int y = 0; y < height; y++)
814			for (int x = 0; x < width; x++)
815				dst.setPixel(src.getPixel(x, y, z), x, y, z);
816		}
817	}
818}
819
820void scale (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, Sampler::FilterMode filter)
821{
822	DE_ASSERT(filter == Sampler::NEAREST || filter == Sampler::LINEAR);
823
824	Sampler sampler(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE,
825					filter, filter, 0.0f, false);
826
827	float sX = (float)src.getWidth() / (float)dst.getWidth();
828	float sY = (float)src.getHeight() / (float)dst.getHeight();
829	float sZ = (float)src.getDepth() / (float)dst.getDepth();
830
831	if (dst.getDepth() == 1 && src.getDepth() == 1)
832	{
833		for (int y = 0; y < dst.getHeight(); y++)
834		for (int x = 0; x < dst.getWidth(); x++)
835			dst.setPixel(src.sample2D(sampler, filter, (x+0.5f)*sX, (y+0.5f)*sY, 0), x, y);
836	}
837	else
838	{
839		for (int z = 0; z < dst.getDepth(); z++)
840		for (int y = 0; y < dst.getHeight(); y++)
841		for (int x = 0; x < dst.getWidth(); x++)
842			dst.setPixel(src.sample3D(sampler, filter, (x+0.5f)*sX, (y+0.5f)*sY, (z+0.5f)*sZ), x, y, z);
843	}
844}
845
846void estimatePixelValueRange (const ConstPixelBufferAccess& access, Vec4& minVal, Vec4& maxVal)
847{
848	const TextureFormat& format = access.getFormat();
849
850	switch (format.type)
851	{
852		case TextureFormat::UNORM_INT8:
853		case TextureFormat::UNORM_INT16:
854			// Normalized unsigned formats.
855			minVal = Vec4(0.0f);
856			maxVal = Vec4(1.0f);
857			break;
858
859		case TextureFormat::SNORM_INT8:
860		case TextureFormat::SNORM_INT16:
861			// Normalized signed formats.
862			minVal = Vec4(-1.0f);
863			maxVal = Vec4(+1.0f);
864			break;
865
866		default:
867			// \note Samples every 4/8th pixel.
868			minVal = Vec4(std::numeric_limits<float>::max());
869			maxVal = Vec4(std::numeric_limits<float>::min());
870
871			for (int z = 0; z < access.getDepth(); z += 2)
872			{
873				for (int y = 0; y < access.getHeight(); y += 2)
874				{
875					for (int x = 0; x < access.getWidth(); x += 2)
876					{
877						Vec4 p = access.getPixel(x, y, z);
878
879						minVal[0] = de::min(minVal[0], p[0]);
880						minVal[1] = de::min(minVal[1], p[1]);
881						minVal[2] = de::min(minVal[2], p[2]);
882						minVal[3] = de::min(minVal[3], p[3]);
883
884						maxVal[0] = de::max(maxVal[0], p[0]);
885						maxVal[1] = de::max(maxVal[1], p[1]);
886						maxVal[2] = de::max(maxVal[2], p[2]);
887						maxVal[3] = de::max(maxVal[3], p[3]);
888					}
889				}
890			}
891			break;
892	}
893}
894
895void computePixelScaleBias (const ConstPixelBufferAccess& access, Vec4& scale, Vec4& bias)
896{
897	Vec4 minVal, maxVal;
898	estimatePixelValueRange(access, minVal, maxVal);
899
900	const float eps = 0.0001f;
901
902	for (int c = 0; c < 4; c++)
903	{
904		if (maxVal[c] - minVal[c] < eps)
905		{
906			scale[c]	= (maxVal[c] < eps) ? 1.0f : (1.0f / maxVal[c]);
907			bias[c]		= (c == 3) ? (1.0f - maxVal[c]*scale[c]) : (0.0f - minVal[c]*scale[c]);
908		}
909		else
910		{
911			scale[c]	= 1.0f / (maxVal[c] - minVal[c]);
912			bias[c]		= 0.0f - minVal[c]*scale[c];
913		}
914	}
915}
916
917int getCubeArrayFaceIndex (CubeFace face)
918{
919	DE_ASSERT((int)face >= 0 && face < CUBEFACE_LAST);
920
921	switch (face)
922	{
923		case CUBEFACE_POSITIVE_X:	return 0;
924		case CUBEFACE_NEGATIVE_X:	return 1;
925		case CUBEFACE_POSITIVE_Y:	return 2;
926		case CUBEFACE_NEGATIVE_Y:	return 3;
927		case CUBEFACE_POSITIVE_Z:	return 4;
928		case CUBEFACE_NEGATIVE_Z:	return 5;
929
930		default:
931			return -1;
932	}
933}
934
935} // tcu
936