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