1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Image/BsPixelUtil.h"
4#include "Utility/BsBitwise.h"
5#include "Image/BsColor.h"
6#include "Math/BsMath.h"
7#include "Error/BsException.h"
8#include "Image/BsTexture.h"
9#include <nvtt.h>
10
11namespace bs
12{
13 /**
14 * Performs pixel data resampling using the point filter (nearest neighbor). Does not perform format conversions.
15 *
16 * @tparam elementSize Size of a single pixel in bytes.
17 */
18 template<UINT32 elementSize> struct NearestResampler
19 {
20 static void scale(const PixelData& source, const PixelData& dest)
21 {
22 UINT8* sourceData = source.getData();
23 UINT8* destPtr = dest.getData();
24
25 // Get steps for traversing source data in 16/48 fixed point format
26 UINT64 stepX = ((UINT64)source.getWidth() << 48) / dest.getWidth();
27 UINT64 stepY = ((UINT64)source.getHeight() << 48) / dest.getHeight();
28 UINT64 stepZ = ((UINT64)source.getDepth() << 48) / dest.getDepth();
29
30 UINT64 curZ = (stepZ >> 1) - 1; // Offset half a pixel to start at pixel center
31 for (UINT32 z = dest.getFront(); z < dest.getBack(); z++, curZ += stepZ)
32 {
33 UINT32 offsetZ = (UINT32)(curZ >> 48) * source.getSlicePitch();
34
35 UINT64 curY = (stepY >> 1) - 1; // Offset half a pixel to start at pixel center
36 for (UINT32 y = dest.getTop(); y < dest.getBottom(); y++, curY += stepY)
37 {
38 UINT32 offsetY = (UINT32)(curY >> 48) * source.getRowPitch();
39
40 UINT64 curX = (stepX >> 1) - 1; // Offset half a pixel to start at pixel center
41 for (UINT32 x = dest.getLeft(); x < dest.getRight(); x++, curX += stepX)
42 {
43 UINT32 offsetX = (UINT32)(curX >> 48);
44 UINT32 offsetBytes = elementSize*(offsetX + offsetY + offsetZ);
45
46 UINT8* curSourcePtr = sourceData + offsetBytes;
47
48 memcpy(destPtr, curSourcePtr, elementSize);
49 destPtr += elementSize;
50 }
51
52 destPtr += elementSize*dest.getRowSkip();
53 }
54
55 destPtr += elementSize*dest.getSliceSkip();
56 }
57 }
58 };
59
60 /** Performs pixel data resampling using the box filter (linear). Performs format conversions. */
61 struct LinearResampler
62 {
63 static void scale(const PixelData& source, const PixelData& dest)
64 {
65 UINT32 sourceElemSize = PixelUtil::getNumElemBytes(source.getFormat());
66 UINT32 destElemSize = PixelUtil::getNumElemBytes(dest.getFormat());
67
68 UINT8* sourceData = source.getData();
69 UINT8* destPtr = dest.getData();
70
71 // Get steps for traversing source data in 16/48 fixed point precision format
72 UINT64 stepX = ((UINT64)source.getWidth() << 48) / dest.getWidth();
73 UINT64 stepY = ((UINT64)source.getHeight() << 48) / dest.getHeight();
74 UINT64 stepZ = ((UINT64)source.getDepth() << 48) / dest.getDepth();
75
76 // Contains 16/16 fixed point precision format. Most significant
77 // 16 bits will contain the coordinate in the source image, and the
78 // least significant 16 bits will contain the fractional part of the coordinate
79 // that will be used for determining the blend amount.
80 UINT32 temp = 0;
81
82 UINT64 curZ = (stepZ >> 1) - 1; // Offset half a pixel to start at pixel center
83 for (UINT32 z = dest.getFront(); z < dest.getBack(); z++, curZ += stepZ)
84 {
85 temp = UINT32(curZ >> 32);
86 temp = (temp > 0x8000)? temp - 0x8000 : 0;
87 UINT32 sampleCoordZ1 = temp >> 16;
88 UINT32 sampleCoordZ2 = std::min(sampleCoordZ1 + 1, (UINT32)source.getDepth() - 1);
89 float sampleWeightZ = (temp & 0xFFFF) / 65536.0f;
90
91 UINT64 curY = (stepY >> 1) - 1; // Offset half a pixel to start at pixel center
92 for (UINT32 y = dest.getTop(); y < dest.getBottom(); y++, curY += stepY)
93 {
94 temp = (UINT32)(curY >> 32);
95 temp = (temp > 0x8000)? temp - 0x8000 : 0;
96 UINT32 sampleCoordY1 = temp >> 16;
97 UINT32 sampleCoordY2 = std::min(sampleCoordY1 + 1, (UINT32)source.getHeight() - 1);
98 float sampleWeightY = (temp & 0xFFFF) / 65536.0f;
99
100 UINT64 curX = (stepX >> 1) - 1; // Offset half a pixel to start at pixel center
101 for (UINT32 x = dest.getLeft(); x < dest.getRight(); x++, curX += stepX)
102 {
103 temp = (UINT32)(curX >> 32);
104 temp = (temp > 0x8000)? temp - 0x8000 : 0;
105 UINT32 sampleCoordX1 = temp >> 16;
106 UINT32 sampleCoordX2 = std::min(sampleCoordX1 + 1, (UINT32)source.getWidth() - 1);
107 float sampleWeightX = (temp & 0xFFFF) / 65536.0f;
108
109 Color x1y1z1, x2y1z1, x1y2z1, x2y2z1;
110 Color x1y1z2, x2y1z2, x1y2z2, x2y2z2;
111
112#define GETSOURCEDATA(x, y, z) sourceData + sourceElemSize*((x)+(y)*source.getRowPitch() + (z)*source.getSlicePitch())
113
114 PixelUtil::unpackColor(&x1y1z1, source.getFormat(), GETSOURCEDATA(sampleCoordX1, sampleCoordY1, sampleCoordZ1));
115 PixelUtil::unpackColor(&x2y1z1, source.getFormat(), GETSOURCEDATA(sampleCoordX2, sampleCoordY1, sampleCoordZ1));
116 PixelUtil::unpackColor(&x1y2z1, source.getFormat(), GETSOURCEDATA(sampleCoordX1, sampleCoordY2, sampleCoordZ1));
117 PixelUtil::unpackColor(&x2y2z1, source.getFormat(), GETSOURCEDATA(sampleCoordX2, sampleCoordY2, sampleCoordZ1));
118 PixelUtil::unpackColor(&x1y1z2, source.getFormat(), GETSOURCEDATA(sampleCoordX1, sampleCoordY1, sampleCoordZ2));
119 PixelUtil::unpackColor(&x2y1z2, source.getFormat(), GETSOURCEDATA(sampleCoordX2, sampleCoordY1, sampleCoordZ2));
120 PixelUtil::unpackColor(&x1y2z2, source.getFormat(), GETSOURCEDATA(sampleCoordX1, sampleCoordY2, sampleCoordZ2));
121 PixelUtil::unpackColor(&x2y2z2, source.getFormat(), GETSOURCEDATA(sampleCoordX2, sampleCoordY2, sampleCoordZ2));
122#undef GETSOURCEDATA
123
124 Color accum =
125 x1y1z1 * ((1.0f - sampleWeightX)*(1.0f - sampleWeightY)*(1.0f - sampleWeightZ)) +
126 x2y1z1 * ( sampleWeightX *(1.0f - sampleWeightY)*(1.0f - sampleWeightZ)) +
127 x1y2z1 * ((1.0f - sampleWeightX)* sampleWeightY *(1.0f - sampleWeightZ)) +
128 x2y2z1 * ( sampleWeightX * sampleWeightY *(1.0f - sampleWeightZ)) +
129 x1y1z2 * ((1.0f - sampleWeightX)*(1.0f - sampleWeightY)* sampleWeightZ ) +
130 x2y1z2 * ( sampleWeightX *(1.0f - sampleWeightY)* sampleWeightZ ) +
131 x1y2z2 * ((1.0f - sampleWeightX)* sampleWeightY * sampleWeightZ ) +
132 x2y2z2 * ( sampleWeightX * sampleWeightY * sampleWeightZ );
133
134 PixelUtil::packColor(accum, dest.getFormat(), destPtr);
135
136 destPtr += destElemSize;
137 }
138
139 destPtr += destElemSize * dest.getRowSkip();
140 }
141
142 destPtr += destElemSize * dest.getSliceSkip();
143 }
144 }
145 };
146
147
148 /**
149 * Performs pixel data resampling using the box filter (linear). Only handles float RGB or RGBA pixel data (32 bits per
150 * channel).
151 */
152 struct LinearResampler_Float32
153 {
154 static void scale(const PixelData& source, const PixelData& dest)
155 {
156 UINT32 numSourceChannels = PixelUtil::getNumElemBytes(source.getFormat()) / sizeof(float);
157 UINT32 numDestChannels = PixelUtil::getNumElemBytes(dest.getFormat()) / sizeof(float);
158
159 float* sourceData = (float*)source.getData();
160 float* destPtr = (float*)dest.getData();
161
162 // Get steps for traversing source data in 16/48 fixed point precision format
163 UINT64 stepX = ((UINT64)source.getWidth() << 48) / dest.getWidth();
164 UINT64 stepY = ((UINT64)source.getHeight() << 48) / dest.getHeight();
165 UINT64 stepZ = ((UINT64)source.getDepth() << 48) / dest.getDepth();
166
167 // Contains 16/16 fixed point precision format. Most significant
168 // 16 bits will contain the coordinate in the source image, and the
169 // least significant 16 bits will contain the fractional part of the coordinate
170 // that will be used for determining the blend amount.
171 UINT32 temp = 0;
172
173 UINT64 curZ = (stepZ >> 1) - 1; // Offset half a pixel to start at pixel center
174 for (UINT32 z = dest.getFront(); z < dest.getBack(); z++, curZ += stepZ)
175 {
176 temp = (UINT32)(curZ >> 32);
177 temp = (temp > 0x8000)? temp - 0x8000 : 0;
178 UINT32 sampleCoordZ1 = temp >> 16;
179 UINT32 sampleCoordZ2 = std::min(sampleCoordZ1 + 1, (UINT32)source.getDepth() - 1);
180 float sampleWeightZ = (temp & 0xFFFF) / 65536.0f;
181
182 UINT64 curY = (stepY >> 1) - 1; // Offset half a pixel to start at pixel center
183 for (UINT32 y = dest.getTop(); y < dest.getBottom(); y++, curY += stepY)
184 {
185 temp = (UINT32)(curY >> 32);
186 temp = (temp > 0x8000)? temp - 0x8000 : 0;
187 UINT32 sampleCoordY1 = temp >> 16;
188 UINT32 sampleCoordY2 = std::min(sampleCoordY1 + 1, (UINT32)source.getHeight() - 1);
189 float sampleWeightY = (temp & 0xFFFF) / 65536.0f;
190
191 UINT64 curX = (stepX >> 1) - 1; // Offset half a pixel to start at pixel center
192 for (UINT32 x = dest.getLeft(); x < dest.getRight(); x++, curX += stepX)
193 {
194 temp = (UINT32)(curX >> 32);
195 temp = (temp > 0x8000)? temp - 0x8000 : 0;
196 UINT32 sampleCoordX1 = temp >> 16;
197 UINT32 sampleCoordX2 = std::min(sampleCoordX1 + 1, (UINT32)source.getWidth() - 1);
198 float sampleWeightX = (temp & 0xFFFF) / 65536.0f;
199
200 // process R,G,B,A simultaneously for cache coherence?
201 float accum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
202
203
204#define ACCUM3(x,y,z,factor) \
205 { float f = factor; \
206 UINT32 offset = (x + y*source.getRowPitch() + z*source.getSlicePitch())*numSourceChannels; \
207 accum[0] += sourceData[offset + 0] * f; accum[1] += sourceData[offset + 1] * f; \
208 accum[2] += sourceData[offset + 2] * f; }
209
210#define ACCUM4(x,y,z,factor) \
211 { float f = factor; \
212 UINT32 offset = (x + y*source.getRowPitch() + z*source.getSlicePitch())*numSourceChannels; \
213 accum[0] += sourceData[offset + 0] * f; accum[1] += sourceData[offset + 1] * f; \
214 accum[2] += sourceData[offset + 2] * f; accum[3] += sourceData[offset + 3] * f; }
215
216 if (numSourceChannels == 3 || numDestChannels == 3)
217 {
218 // RGB
219 ACCUM3(sampleCoordX1, sampleCoordY1, sampleCoordZ1, (1.0f - sampleWeightX) * (1.0f - sampleWeightY) * (1.0f - sampleWeightZ));
220 ACCUM3(sampleCoordX2, sampleCoordY1, sampleCoordZ1, sampleWeightX * (1.0f - sampleWeightY) * (1.0f - sampleWeightZ));
221 ACCUM3(sampleCoordX1, sampleCoordY2, sampleCoordZ1, (1.0f - sampleWeightX) * sampleWeightY * (1.0f - sampleWeightZ));
222 ACCUM3(sampleCoordX2, sampleCoordY2, sampleCoordZ1, sampleWeightX * sampleWeightY * (1.0f - sampleWeightZ));
223 ACCUM3(sampleCoordX1, sampleCoordY1, sampleCoordZ2, (1.0f - sampleWeightX) * (1.0f - sampleWeightY) * sampleWeightZ);
224 ACCUM3(sampleCoordX2, sampleCoordY1, sampleCoordZ2, sampleWeightX * (1.0f - sampleWeightY) * sampleWeightZ);
225 ACCUM3(sampleCoordX1, sampleCoordY2, sampleCoordZ2, (1.0f - sampleWeightX) * sampleWeightY * sampleWeightZ);
226 ACCUM3(sampleCoordX2, sampleCoordY2, sampleCoordZ2, sampleWeightX * sampleWeightY * sampleWeightZ);
227 accum[3] = 1.0f;
228 }
229 else
230 {
231 // RGBA
232 ACCUM4(sampleCoordX1, sampleCoordY1, sampleCoordZ1, (1.0f - sampleWeightX) * (1.0f - sampleWeightY) * (1.0f - sampleWeightZ));
233 ACCUM4(sampleCoordX2, sampleCoordY1, sampleCoordZ1, sampleWeightX * (1.0f - sampleWeightY) * (1.0f - sampleWeightZ));
234 ACCUM4(sampleCoordX1, sampleCoordY2, sampleCoordZ1, (1.0f - sampleWeightX) * sampleWeightY * (1.0f - sampleWeightZ));
235 ACCUM4(sampleCoordX2, sampleCoordY2, sampleCoordZ1, sampleWeightX * sampleWeightY * (1.0f - sampleWeightZ));
236 ACCUM4(sampleCoordX1, sampleCoordY1, sampleCoordZ2, (1.0f - sampleWeightX) * (1.0f - sampleWeightY) * sampleWeightZ);
237 ACCUM4(sampleCoordX2, sampleCoordY1, sampleCoordZ2, sampleWeightX * (1.0f - sampleWeightY) * sampleWeightZ);
238 ACCUM4(sampleCoordX1, sampleCoordY2, sampleCoordZ2, (1.0f - sampleWeightX) * sampleWeightY * sampleWeightZ);
239 ACCUM4(sampleCoordX2, sampleCoordY2, sampleCoordZ2, sampleWeightX * sampleWeightY * sampleWeightZ);
240 }
241
242 memcpy(destPtr, accum, sizeof(float)*numDestChannels);
243
244#undef ACCUM3
245#undef ACCUM4
246
247 destPtr += numDestChannels;
248 }
249
250 destPtr += numDestChannels*dest.getRowSkip();
251 }
252
253 destPtr += numDestChannels*dest.getSliceSkip();
254 }
255 }
256 };
257
258
259
260 // byte linear resampler, does not do any format conversions.
261 // only handles pixel formats that use 1 byte per color channel.
262 // 2D only; punts 3D pixelboxes to default LinearResampler (slow).
263 // templated on bytes-per-pixel to allow compiler optimizations, such
264 // as unrolling loops and replacing multiplies with bitshifts
265
266 /**
267 * Performs pixel data resampling using the box filter (linear). Only handles pixel formats with one byte per channel.
268 * Does not perform format conversion.
269 *
270 * @tparam channels Number of channels in the pixel format.
271 */
272 template<UINT32 channels> struct LinearResampler_Byte
273 {
274 static void scale(const PixelData& source, const PixelData& dest)
275 {
276 // Only optimized for 2D
277 if (source.getDepth() > 1 || dest.getDepth() > 1)
278 {
279 LinearResampler::scale(source, dest);
280 return;
281 }
282
283 UINT8* sourceData = (UINT8*)source.getData();
284 UINT8* destPtr = (UINT8*)dest.getData();
285
286 // Get steps for traversing source data in 16/48 fixed point precision format
287 UINT64 stepX = ((UINT64)source.getWidth() << 48) / dest.getWidth();
288 UINT64 stepY = ((UINT64)source.getHeight() << 48) / dest.getHeight();
289
290 // Contains 16/16 fixed point precision format. Most significant
291 // 16 bits will contain the coordinate in the source image, and the
292 // least significant 16 bits will contain the fractional part of the coordinate
293 // that will be used for determining the blend amount.
294 UINT32 temp;
295
296 UINT64 curY = (stepY >> 1) - 1; // Offset half a pixel to start at pixel center
297 for (UINT32 y = dest.getTop(); y < dest.getBottom(); y++, curY += stepY)
298 {
299 temp = (UINT32)(curY >> 36);
300 temp = (temp > 0x800)? temp - 0x800: 0;
301 UINT32 sampleWeightY = temp & 0xFFF;
302 UINT32 sampleCoordY1 = temp >> 12;
303 UINT32 sampleCoordY2 = std::min(sampleCoordY1 + 1, (UINT32)source.getBottom() - source.getTop() - 1);
304
305 UINT32 sampleY1Offset = sampleCoordY1 * source.getRowPitch();
306 UINT32 sampleY2Offset = sampleCoordY2 * source.getRowPitch();
307
308 UINT64 curX = (stepX >> 1) - 1; // Offset half a pixel to start at pixel center
309 for (UINT32 x = dest.getLeft(); x < dest.getRight(); x++, curX += stepX)
310 {
311 temp = (UINT32)(curX >> 36);
312 temp = (temp > 0x800)? temp - 0x800 : 0;
313 UINT32 sampleWeightX = temp & 0xFFF;
314 UINT32 sampleCoordX1 = temp >> 12;
315 UINT32 sampleCoordX2 = std::min(sampleCoordX1 + 1, (UINT32)source.getRight() - source.getLeft() - 1);
316
317 UINT32 sxfsyf = sampleWeightX*sampleWeightY;
318 for (UINT32 k = 0; k < channels; k++)
319 {
320 UINT32 accum =
321 sourceData[(sampleCoordX1 + sampleY1Offset)*channels+k]*(0x1000000-(sampleWeightX<<12)-(sampleWeightY<<12)+sxfsyf) +
322 sourceData[(sampleCoordX2 + sampleY1Offset)*channels+k]*((sampleWeightX<<12)-sxfsyf) +
323 sourceData[(sampleCoordX1 + sampleY2Offset)*channels+k]*((sampleWeightY<<12)-sxfsyf) +
324 sourceData[(sampleCoordX2 + sampleY2Offset)*channels+k]*sxfsyf;
325
326 // Round up to byte size
327 *destPtr = (UINT8)((accum + 0x800000) >> 24);
328 destPtr++;
329 }
330 }
331 destPtr += channels*dest.getRowSkip();
332 }
333 }
334 };
335
336 /** Data describing a pixel format. */
337 struct PixelFormatDescription
338 {
339 const char* name; /**< Name of the format. */
340 UINT8 elemBytes; /**< Number of bytes one element (color value) uses. */
341 UINT32 flags; /**< PixelFormatFlags set by the pixel format. */
342 PixelComponentType componentType; /**< Data type of a single element of the format. */
343 UINT8 componentCount; /**< Number of elements in the format. */
344
345 UINT8 rbits, gbits, bbits, abits; /**< Number of bits per element in the format. */
346
347 UINT32 rmask, gmask, bmask, amask; /**< Masks used by packers/unpackers. */
348 UINT8 rshift, gshift, bshift, ashift; /**< Shifts used by packers/unpackers. */
349 };
350
351 /** A list of all available pixel formats. */
352 PixelFormatDescription _pixelFormats[PF_COUNT] = {
353 {"PF_UNKNOWN",
354 /* Bytes per element */
355 0,
356 /* Flags */
357 0,
358 /* Component type and count */
359 PCT_BYTE, 0,
360 /* rbits, gbits, bbits, abits */
361 0, 0, 0, 0,
362 /* Masks and shifts */
363 0, 0, 0, 0,
364 0, 0, 0, 0,
365 },
366 //-----------------------------------------------------------------------
367 {"PF_R8",
368 /* Bytes per element */
369 1,
370 /* Flags */
371 PFF_INTEGER | PFF_NORMALIZED,
372 /* Component type and count */
373 PCT_BYTE, 1,
374 /* rbits, gbits, bbits, abits */
375 8, 0, 0, 0,
376 /* Masks and shifts */
377 0x000000FF, 0, 0, 0,
378 0, 0, 0, 0,
379 },
380 //-----------------------------------------------------------------------
381 {"PF_RG8",
382 /* Bytes per element */
383 2,
384 /* Flags */
385 PFF_INTEGER | PFF_NORMALIZED,
386 /* Component type and count */
387 PCT_BYTE, 2,
388 /* rbits, gbits, bbits, abits */
389 8, 8, 0, 0,
390 /* Masks and shifts */
391 0x000000FF, 0x0000FF00, 0, 0,
392 0, 8, 0, 0,
393 },
394 //-----------------------------------------------------------------------
395 {"PF_RGB8",
396 /* Bytes per element */
397 4, // 4th byte is unused
398 /* Flags */
399 PFF_INTEGER | PFF_NORMALIZED,
400 /* Component type and count */
401 PCT_BYTE, 3,
402 /* rbits, gbits, bbits, abits */
403 8, 8, 8, 0,
404 /* Masks and shifts */
405 0x000000FF, 0x0000FF00, 0x00FF0000, 0,
406 0, 8, 16, 0,
407 },
408 //-----------------------------------------------------------------------
409 {"PF_BGR8",
410 /* Bytes per element */
411 4, // 4th byte is unused
412 /* Flags */
413 PFF_INTEGER | PFF_NORMALIZED,
414 /* Component type and count */
415 PCT_BYTE, 3,
416 /* rbits, gbits, bbits, abits */
417 8, 8, 8, 0,
418 /* Masks and shifts */
419 0x00FF0000, 0x0000FF00, 0x000000FF, 0,
420 16, 8, 0, 0,
421 },
422 //-----------------------------------------------------------------------
423 {}, // Deleted format
424 //-----------------------------------------------------------------------
425 {}, // Deleted format
426 //-----------------------------------------------------------------------
427 {"PF_BGRA8",
428 /* Bytes per element */
429 4,
430 /* Flags */
431 PFF_HASALPHA | PFF_INTEGER | PFF_NORMALIZED,
432 /* Component type and count */
433 PCT_BYTE, 4,
434 /* rbits, gbits, bbits, abits */
435 8, 8, 8, 8,
436 /* Masks and shifts */
437 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
438 16, 8, 0, 24,
439 },
440 //-----------------------------------------------------------------------
441 {"PF_RGBA8",
442 /* Bytes per element */
443 4,
444 /* Flags */
445 PFF_HASALPHA | PFF_INTEGER | PFF_NORMALIZED,
446 /* Component type and count */
447 PCT_BYTE, 4,
448 /* rbits, gbits, bbits, abits */
449 8, 8, 8, 8,
450 /* Masks and shifts */
451 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000,
452 0, 8, 16, 24,
453 },
454 //-----------------------------------------------------------------------
455 {}, // Deleted format
456 //-----------------------------------------------------------------------
457 {}, // Deleted format
458 //-----------------------------------------------------------------------
459 {}, // Deleted format
460 //-----------------------------------------------------------------------
461 {}, // Deleted format
462 //-----------------------------------------------------------------------
463 { "PF_BC1",
464 /* Bytes per element */
465 0,
466 /* Flags */
467 PFF_COMPRESSED | PFF_HASALPHA,
468 /* Component type and count */
469 PCT_BYTE, 3, // No alpha
470 /* rbits, gbits, bbits, abits */
471 0, 0, 0, 0,
472 /* Masks and shifts */
473 0, 0, 0, 0,
474 0, 0, 0, 0,
475 },
476 //-----------------------------------------------------------------------
477 { "PF_BC1a",
478 /* Bytes per element */
479 0,
480 /* Flags */
481 PFF_COMPRESSED,
482 /* Component type and count */
483 PCT_BYTE, 3,
484 /* rbits, gbits, bbits, abits */
485 0, 0, 0, 0,
486 /* Masks and shifts */
487 0, 0, 0, 0,
488 0, 0, 0, 0,
489 },
490 //-----------------------------------------------------------------------
491 { "PF_BC2",
492 /* Bytes per element */
493 0,
494 /* Flags */
495 PFF_COMPRESSED | PFF_HASALPHA,
496 /* Component type and count */
497 PCT_BYTE, 4,
498 /* rbits, gbits, bbits, abits */
499 0, 0, 0, 0,
500 /* Masks and shifts */
501 0, 0, 0, 0,
502 0, 0, 0, 0,
503 },
504 //-----------------------------------------------------------------------
505 { "PF_BC3",
506 /* Bytes per element */
507 0,
508 /* Flags */
509 PFF_COMPRESSED | PFF_HASALPHA,
510 /* Component type and count */
511 PCT_BYTE, 4,
512 /* rbits, gbits, bbits, abits */
513 0, 0, 0, 0,
514 /* Masks and shifts */
515 0, 0, 0, 0,
516 0, 0, 0, 0,
517 },
518 //-----------------------------------------------------------------------
519 { "PF_BC4",
520 /* Bytes per element */
521 0,
522 /* Flags */
523 PFF_COMPRESSED,
524 /* Component type and count */
525 PCT_BYTE, 1,
526 /* rbits, gbits, bbits, abits */
527 0, 0, 0, 0,
528 /* Masks and shifts */
529 0, 0, 0, 0,
530 0, 0, 0, 0,
531 },
532 //-----------------------------------------------------------------------
533 { "PF_BC5",
534 /* Bytes per element */
535 0,
536 /* Flags */
537 PFF_COMPRESSED,
538 /* Component type and count */
539 PCT_BYTE, 2,
540 /* rbits, gbits, bbits, abits */
541 0, 0, 0, 0,
542 /* Masks and shifts */
543 0, 0, 0, 0,
544 0, 0, 0, 0,
545 },
546 //-----------------------------------------------------------------------
547 { "PF_BC6H",
548 /* Bytes per element */
549 0,
550 /* Flags */
551 PFF_COMPRESSED,
552 /* Component type and count */
553 PCT_FLOAT16, 3,
554 /* rbits, gbits, bbits, abits */
555 0, 0, 0, 0,
556 /* Masks and shifts */
557 0, 0, 0, 0,
558 0, 0, 0, 0,
559 },
560 //-----------------------------------------------------------------------
561 { "PF_BC7",
562 /* Bytes per element */
563 0,
564 /* Flags */
565 PFF_COMPRESSED | PFF_HASALPHA,
566 /* Component type and count */
567 PCT_BYTE, 4,
568 /* rbits, gbits, bbits, abits */
569 0, 0, 0, 0,
570 /* Masks and shifts */
571 0, 0, 0, 0,
572 0, 0, 0, 0,
573 },
574 //-----------------------------------------------------------------------
575 {"PF_R16F",
576 /* Bytes per element */
577 2,
578 /* Flags */
579 PFF_FLOAT,
580 /* Component type and count */
581 PCT_FLOAT16, 1,
582 /* rbits, gbits, bbits, abits */
583 16, 0, 0, 0,
584 /* Masks and shifts */
585 0x0000FFFF, 0, 0, 0,
586 0, 0, 0, 0,
587 },
588 //-----------------------------------------------------------------------
589 {"PF_RG16F",
590 /* Bytes per element */
591 4,
592 /* Flags */
593 PFF_FLOAT,
594 /* Component type and count */
595 PCT_FLOAT16, 2,
596 /* rbits, gbits, bbits, abits */
597 16, 16, 0, 0,
598 /* Masks and shifts */
599 0x0000FFFF, 0xFFFF0000, 0, 0,
600 0, 16, 0, 0,
601 },
602 //-----------------------------------------------------------------------
603 { }, // Deleted format
604 //-----------------------------------------------------------------------
605 { "PF_RGBA16F",
606 /* Bytes per element */
607 8,
608 /* Flags */
609 PFF_FLOAT | PFF_HASALPHA,
610 /* Component type and count */
611 PCT_FLOAT16, 4,
612 /* rbits, gbits, bbits, abits */
613 16, 16, 16, 16,
614 /* Masks and shifts */
615 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0xFFFF0000,
616 0, 16, 0, 16,
617 },
618 //-----------------------------------------------------------------------
619 {"PF_R32F",
620 /* Bytes per element */
621 4,
622 /* Flags */
623 PFF_FLOAT,
624 /* Component type and count */
625 PCT_FLOAT32, 1,
626 /* rbits, gbits, bbits, abits */
627 32, 0, 0, 0,
628 /* Masks and shifts */
629 0xFFFFFFFF, 0, 0, 0,
630 0, 0, 0, 0,
631 },
632 //-----------------------------------------------------------------------
633 {"PF_RG32F",
634 /* Bytes per element */
635 8,
636 /* Flags */
637 PFF_FLOAT,
638 /* Component type and count */
639 PCT_FLOAT32, 2,
640 /* rbits, gbits, bbits, abits */
641 32, 32, 0, 0,
642 /* Masks and shifts */
643 0xFFFFFFFF, 0xFFFFFFFF, 0, 0,
644 0, 0, 0, 0,
645 },
646 //-----------------------------------------------------------------------
647 { "PF_RGB32F",
648 /* Bytes per element */
649 12,
650 /* Flags */
651 PFF_FLOAT,
652 /* Component type and count */
653 PCT_FLOAT32, 3,
654 /* rbits, gbits, bbits, abits */
655 32, 32, 32, 0,
656 /* Masks and shifts */
657 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0,
658 0, 0, 0, 0,
659 },
660 //-----------------------------------------------------------------------
661 { "PF_RGBA32F",
662 /* Bytes per element */
663 16,
664 /* Flags */
665 PFF_FLOAT | PFF_HASALPHA,
666 /* Component type and count */
667 PCT_FLOAT32, 4,
668 /* rbits, gbits, bbits, abits */
669 32, 32, 32, 32,
670 /* Masks and shifts */
671 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
672 0, 0, 0, 0,
673 },
674 //-----------------------------------------------------------------------
675 {"PF_D32_S8X24",
676 /* Bytes per element */
677 8,
678 /* Flags */
679 PFF_DEPTH | PFF_NORMALIZED,
680 /* Component type and count */
681 PCT_FLOAT32, 2,
682 /* rbits, gbits, bbits, abits */
683 32, 8, 0, 0,
684 /* Masks and shifts */
685 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000,
686 0, 0, 0, 0,
687 },
688 //-----------------------------------------------------------------------
689 {"PF_D24_S8",
690 /* Bytes per element */
691 4,
692 /* Flags */
693 PFF_DEPTH | PFF_INTEGER | PFF_NORMALIZED,
694 /* Component type and count */
695 PCT_INT, 2,
696 /* rbits, gbits, bbits, abits */
697 24, 8, 0, 0,
698 /* Masks and shifts */
699 0x00FFFFFF, 0x0FF0000, 0x00000000, 0x00000000,
700 0, 24, 0, 0,
701 },
702 //-----------------------------------------------------------------------
703 {"PF_D32",
704 /* Bytes per element */
705 4,
706 /* Flags */
707 PFF_DEPTH | PFF_FLOAT,
708 /* Component type and count */
709 PCT_FLOAT32, 1,
710 /* rbits, gbits, bbits, abits */
711 32, 0, 0, 0,
712 /* Masks and shifts */
713 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
714 0, 0, 0, 0,
715 },
716 //-----------------------------------------------------------------------
717 {"PF_D16",
718 /* Bytes per element */
719 2,
720 /* Flags */
721 PFF_DEPTH | PFF_INTEGER | PFF_NORMALIZED,
722 /* Component type and count */
723 PCT_SHORT, 1,
724 /* rbits, gbits, bbits, abits */
725 16, 0, 0, 0,
726 /* Masks and shifts */
727 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000,
728 0, 0, 0, 0,
729 },
730 //-----------------------------------------------------------------------
731 { "PF_RG11B10F",
732 /* Bytes per element */
733 4,
734 /* Flags */
735 PFF_FLOAT,
736 /* Component type and count */
737 PCT_PACKED_R11G11B10, 1,
738 /* rbits, gbits, bbits, abits */
739 11, 11, 10, 0,
740 /* Masks and shifts */
741 0x000007FF, 0x003FF800, 0xFFC00000, 0,
742 0, 11, 22, 0,
743 },
744 //-----------------------------------------------------------------------
745 { "PF_RGB10A2",
746 /* Bytes per element */
747 4,
748 /* Flags */
749 PFF_INTEGER | PFF_NORMALIZED | PFF_HASALPHA,
750 /* Component type and count */
751 PCT_PACKED_R10G10B10A2, 1,
752 /* rbits, gbits, bbits, abits */
753 10, 10, 10, 2,
754 /* Masks and shifts */
755 0x000003FF, 0x000FFC00, 0x3FF00000, 0xC0000000,
756 0, 10, 20, 30,
757 },
758 //-----------------------------------------------------------------------
759 { "PF_R8I",
760 /* Bytes per element */
761 1,
762 /* Flags */
763 PFF_INTEGER | PFF_SIGNED,
764 /* Component type and count */
765 PCT_BYTE, 1,
766 /* rbits, gbits, bbits, abits */
767 8, 0, 0, 0,
768 /* Masks and shifts */
769 0x000000FF, 0, 0, 0,
770 0, 0, 0, 0,
771 },
772 //-----------------------------------------------------------------------
773 { "PF_RG8I",
774 /* Bytes per element */
775 2,
776 /* Flags */
777 PFF_INTEGER | PFF_SIGNED,
778 /* Component type and count */
779 PCT_BYTE, 2,
780 /* rbits, gbits, bbits, abits */
781 8, 8, 0, 0,
782 /* Masks and shifts */
783 0x000000FF, 0x0000FF00, 0, 0,
784 0, 8, 0, 0,
785 },
786 //-----------------------------------------------------------------------
787 { "PF_RGBA8I",
788 /* Bytes per element */
789 4,
790 /* Flags */
791 PFF_INTEGER | PFF_SIGNED | PFF_HASALPHA,
792 /* Component type and count */
793 PCT_BYTE, 4,
794 /* rbits, gbits, bbits, abits */
795 8, 8, 8, 8,
796 /* Masks and shifts */
797 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000,
798 0, 8, 16, 24,
799 },
800 //-----------------------------------------------------------------------
801 { "PF_R8U",
802 /* Bytes per element */
803 1,
804 /* Flags */
805 PFF_INTEGER,
806 /* Component type and count */
807 PCT_BYTE, 1,
808 /* rbits, gbits, bbits, abits */
809 8, 0, 0, 0,
810 /* Masks and shifts */
811 0x000000FF, 0, 0, 0,
812 0, 0, 0, 0,
813 },
814 //-----------------------------------------------------------------------
815 { "PF_RG8U",
816 /* Bytes per element */
817 2,
818 /* Flags */
819 PFF_INTEGER,
820 /* Component type and count */
821 PCT_BYTE, 2,
822 /* rbits, gbits, bbits, abits */
823 8, 8, 0, 0,
824 /* Masks and shifts */
825 0x000000FF, 0x0000FF00, 0, 0,
826 0, 8, 0, 0,
827 },
828 //-----------------------------------------------------------------------
829 { "PF_RGBA8U",
830 /* Bytes per element */
831 4,
832 /* Flags */
833 PFF_INTEGER | PFF_HASALPHA,
834 /* Component type and count */
835 PCT_BYTE, 4,
836 /* rbits, gbits, bbits, abits */
837 8, 8, 8, 8,
838 /* Masks and shifts */
839 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000,
840 0, 8, 16, 24,
841 },
842 //-----------------------------------------------------------------------
843 { "PF_R8S",
844 /* Bytes per element */
845 1,
846 /* Flags */
847 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED,
848 /* Component type and count */
849 PCT_BYTE, 1,
850 /* rbits, gbits, bbits, abits */
851 8, 0, 0, 0,
852 /* Masks and shifts */
853 0x000000FF, 0, 0, 0,
854 0, 0, 0, 0,
855 },
856 //-----------------------------------------------------------------------
857 { "PF_RG8S",
858 /* Bytes per element */
859 2,
860 /* Flags */
861 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED,
862 /* Component type and count */
863 PCT_BYTE, 2,
864 /* rbits, gbits, bbits, abits */
865 8, 8, 0, 0,
866 /* Masks and shifts */
867 0x000000FF, 0x0000FF00, 0, 0,
868 0, 8, 0, 0,
869 },
870 //-----------------------------------------------------------------------
871 { "PF_RGBA8S",
872 /* Bytes per element */
873 4,
874 /* Flags */
875 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED | PFF_HASALPHA,
876 /* Component type and count */
877 PCT_BYTE, 4,
878 /* rbits, gbits, bbits, abits */
879 8, 8, 8, 8,
880 /* Masks and shifts */
881 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000,
882 0, 8, 16, 24,
883 },
884 //-----------------------------------------------------------------------
885 { "PF_R16I",
886 /* Bytes per element */
887 2,
888 /* Flags */
889 PFF_INTEGER | PFF_SIGNED,
890 /* Component type and count */
891 PCT_SHORT, 1,
892 /* rbits, gbits, bbits, abits */
893 16, 0, 0, 0,
894 /* Masks and shifts */
895 0x0000FFFF, 0, 0, 0,
896 0, 0, 0, 0,
897 },
898 //-----------------------------------------------------------------------
899 { "PF_RG16I",
900 /* Bytes per element */
901 4,
902 /* Flags */
903 PFF_INTEGER | PFF_SIGNED,
904 /* Component type and count */
905 PCT_SHORT, 2,
906 /* rbits, gbits, bbits, abits */
907 16, 16, 0, 0,
908 /* Masks and shifts */
909 0x0000FFFF, 0xFFFF0000, 0, 0,
910 0, 16, 0, 0,
911 },
912 //-----------------------------------------------------------------------
913 { "PF_RGBA16I",
914 /* Bytes per element */
915 8,
916 /* Flags */
917 PFF_INTEGER | PFF_SIGNED | PFF_HASALPHA,
918 /* Component type and count */
919 PCT_SHORT, 4,
920 /* rbits, gbits, bbits, abits */
921 16, 16, 16, 16,
922 /* Masks and shifts */
923 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0xFFFF0000,
924 0, 16, 0, 16,
925 },
926 //-----------------------------------------------------------------------
927 { "PF_R16U",
928 /* Bytes per element */
929 2,
930 /* Flags */
931 PFF_INTEGER,
932 /* Component type and count */
933 PCT_SHORT, 1,
934 /* rbits, gbits, bbits, abits */
935 16, 0, 0, 0,
936 /* Masks and shifts */
937 0x0000FFFF, 0, 0, 0,
938 0, 0, 0, 0,
939 },
940 //-----------------------------------------------------------------------
941 { "PF_RG16U",
942 /* Bytes per element */
943 4,
944 /* Flags */
945 PFF_INTEGER,
946 /* Component type and count */
947 PCT_SHORT, 2,
948 /* rbits, gbits, bbits, abits */
949 16, 16, 0, 0,
950 /* Masks and shifts */
951 0x0000FFFF, 0xFFFF0000, 0, 0,
952 0, 16, 0, 0,
953 },
954 //-----------------------------------------------------------------------
955 { "PF_RGBA16U",
956 /* Bytes per element */
957 8,
958 /* Flags */
959 PFF_INTEGER | PFF_HASALPHA,
960 /* Component type and count */
961 PCT_SHORT, 4,
962 /* rbits, gbits, bbits, abits */
963 16, 16, 16, 16,
964 /* Masks and shifts */
965 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0xFFFF0000,
966 0, 16, 0, 16,
967 },
968 //-----------------------------------------------------------------------
969 { "PF_R32I",
970 /* Bytes per element */
971 4,
972 /* Flags */
973 PFF_INTEGER,
974 /* Component type and count */
975 PCT_INT, 1,
976 /* rbits, gbits, bbits, abits */
977 32, 0, 0, 0,
978 /* Masks and shifts */
979 0xFFFFFFFF, 0, 0, 0,
980 0, 0, 0, 0,
981 },
982 //-----------------------------------------------------------------------
983 { "PF_RG32I",
984 /* Bytes per element */
985 8,
986 /* Flags */
987 PFF_INTEGER | PFF_SIGNED,
988 /* Component type and count */
989 PCT_INT, 2,
990 /* rbits, gbits, bbits, abits */
991 32, 32, 0, 0,
992 /* Masks and shifts */
993 0xFFFFFFFF, 0xFFFFFFFF, 0, 0,
994 0, 0, 0, 0,
995 },
996 //-----------------------------------------------------------------------
997 { "PF_RGB32I",
998 /* Bytes per element */
999 12,
1000 /* Flags */
1001 PFF_INTEGER | PFF_SIGNED,
1002 /* Component type and count */
1003 PCT_INT, 3,
1004 /* rbits, gbits, bbits, abits */
1005 32, 32, 32, 0,
1006 /* Masks and shifts */
1007 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0,
1008 0, 0, 0, 0,
1009 },
1010 //-----------------------------------------------------------------------
1011 { "PF_RGBA32I",
1012 /* Bytes per element */
1013 16,
1014 /* Flags */
1015 PFF_INTEGER | PFF_SIGNED | PFF_HASALPHA,
1016 /* Component type and count */
1017 PCT_INT, 4,
1018 /* rbits, gbits, bbits, abits */
1019 32, 32, 32, 32,
1020 /* Masks and shifts */
1021 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
1022 0, 0, 0, 0
1023 },
1024 //-----------------------------------------------------------------------
1025 { "PF_R32U",
1026 /* Bytes per element */
1027 4,
1028 /* Flags */
1029 PFF_INTEGER,
1030 /* Component type and count */
1031 PCT_INT, 1,
1032 /* rbits, gbits, bbits, abits */
1033 32, 0, 0, 0,
1034 /* Masks and shifts */
1035 0xFFFFFFFF, 0, 0, 0,
1036 0, 0, 0, 0,
1037 },
1038 //-----------------------------------------------------------------------
1039 { "PF_RG32U",
1040 /* Bytes per element */
1041 8,
1042 /* Flags */
1043 PFF_INTEGER,
1044 /* Component type and count */
1045 PCT_INT, 2,
1046 /* rbits, gbits, bbits, abits */
1047 32, 32, 0, 0,
1048 /* Masks and shifts */
1049 0xFFFFFFFF, 0xFFFFFFFF, 0, 0,
1050 0, 0, 0, 0,
1051 },
1052 //-----------------------------------------------------------------------
1053 { "PF_RGB32U",
1054 /* Bytes per element */
1055 12,
1056 /* Flags */
1057 PFF_INTEGER,
1058 /* Component type and count */
1059 PCT_INT, 3,
1060 /* rbits, gbits, bbits, abits */
1061 32, 32, 32, 0,
1062 /* Masks and shifts */
1063 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0,
1064 0, 0, 0, 0,
1065 },
1066 //-----------------------------------------------------------------------
1067 { "PF_RGBA32U",
1068 /* Bytes per element */
1069 16,
1070 /* Flags */
1071 PFF_INTEGER | PFF_HASALPHA,
1072 /* Component type and count */
1073 PCT_INT, 4,
1074 /* rbits, gbits, bbits, abits */
1075 32, 32, 32, 32,
1076 /* Masks and shifts */
1077 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
1078 0, 0, 0, 0
1079 },
1080 //-----------------------------------------------------------------------
1081 { "PF_R16S",
1082 /* Bytes per element */
1083 2,
1084 /* Flags */
1085 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED,
1086 /* Component type and count */
1087 PCT_SHORT, 1,
1088 /* rbits, gbits, bbits, abits */
1089 16, 0, 0, 0,
1090 /* Masks and shifts */
1091 0x0000FFFF, 0, 0, 0,
1092 0, 0, 0, 0,
1093 },
1094 //-----------------------------------------------------------------------
1095 { "PF_RG16S",
1096 /* Bytes per element */
1097 4,
1098 /* Flags */
1099 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED,
1100 /* Component type and count */
1101 PCT_SHORT, 2,
1102 /* rbits, gbits, bbits, abits */
1103 16, 16, 0, 0,
1104 /* Masks and shifts */
1105 0x0000FFFF, 0xFFFF0000, 0, 0,
1106 0, 16, 0, 0,
1107 },
1108 //-----------------------------------------------------------------------
1109 { "PF_RGBA16S",
1110 /* Bytes per element */
1111 8,
1112 /* Flags */
1113 PFF_INTEGER | PFF_NORMALIZED | PFF_SIGNED | PFF_HASALPHA,
1114 /* Component type and count */
1115 PCT_SHORT, 4,
1116 /* rbits, gbits, bbits, abits */
1117 16, 16, 16, 16,
1118 /* Masks and shifts */
1119 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0xFFFF0000,
1120 0, 16, 0, 16,
1121 },
1122 //-----------------------------------------------------------------------
1123 { "PF_R16",
1124 /* Bytes per element */
1125 2,
1126 /* Flags */
1127 PFF_INTEGER | PFF_NORMALIZED,
1128 /* Component type and count */
1129 PCT_SHORT, 1,
1130 /* rbits, gbits, bbits, abits */
1131 16, 0, 0, 0,
1132 /* Masks and shifts */
1133 0x0000FFFF, 0, 0, 0,
1134 0, 0, 0, 0
1135 },
1136 //-----------------------------------------------------------------------
1137 { "PF_RG16",
1138 /* Bytes per element */
1139 4,
1140 /* Flags */
1141 PFF_INTEGER | PFF_NORMALIZED,
1142 /* Component type and count */
1143 PCT_SHORT, 2,
1144 /* rbits, gbits, bbits, abits */
1145 16, 16, 0, 0,
1146 /* Masks and shifts */
1147 0x0000FFFF, 0xFFFF0000, 0, 0,
1148 0, 16, 0, 0
1149 },
1150 //-----------------------------------------------------------------------
1151 { "PF_RGB16",
1152 /* Bytes per element */
1153 6,
1154 /* Flags */
1155 PFF_INTEGER | PFF_NORMALIZED,
1156 /* Component type and count */
1157 PCT_SHORT, 3,
1158 /* rbits, gbits, bbits, abits */
1159 16, 16, 16, 0,
1160 /* Masks and shifts */
1161 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0,
1162 0, 16, 0, 0
1163 },
1164 //-----------------------------------------------------------------------
1165 { "PF_RGBA16",
1166 /* Bytes per element */
1167 8,
1168 /* Flags */
1169 PFF_INTEGER | PFF_NORMALIZED | PFF_HASALPHA,
1170 /* Component type and count */
1171 PCT_SHORT, 4,
1172 /* rbits, gbits, bbits, abits */
1173 16, 16, 16, 16,
1174 /* Masks and shifts */
1175 0x0000FFFF, 0xFFFF0000, 0x0000FFFF, 0xFFFF0000,
1176 0, 16, 0, 16
1177 },
1178 };
1179
1180 static inline const PixelFormatDescription &getDescriptionFor(const PixelFormat fmt)
1181 {
1182 const int ord = (int)fmt;
1183 assert(ord >= 0 && ord < PF_COUNT);
1184
1185 return _pixelFormats[ord];
1186 }
1187
1188 /** Handles compression output from NVTT library for a single image. */
1189 struct NVTTCompressOutputHandler : public nvtt::OutputHandler
1190 {
1191 NVTTCompressOutputHandler(UINT8* buffer, UINT32 sizeBytes)
1192 :buffer(buffer), bufferWritePos(buffer), bufferEnd(buffer + sizeBytes)
1193 { }
1194
1195 void beginImage(int size, int width, int height, int depth, int face, int miplevel) override
1196 { }
1197
1198 bool writeData(const void* data, int size) override
1199 {
1200 assert((bufferWritePos + size) <= bufferEnd);
1201 memcpy(bufferWritePos, data, size);
1202 bufferWritePos += size;
1203
1204 return true;
1205 }
1206
1207 void endImage() override
1208 { }
1209
1210 UINT8* buffer;
1211 UINT8* bufferWritePos;
1212 UINT8* bufferEnd;
1213 };
1214
1215 /** Handles output from NVTT library for a mip-map chain. */
1216 struct NVTTMipmapOutputHandler : public nvtt::OutputHandler
1217 {
1218 NVTTMipmapOutputHandler(const Vector<SPtr<PixelData>>& buffers)
1219 :buffers(buffers), bufferWritePos(nullptr), bufferEnd(nullptr)
1220 { }
1221
1222 void beginImage(int size, int width, int height, int depth, int face, int miplevel) override
1223 {
1224 assert(miplevel >= 0 && miplevel < (int)buffers.size());
1225 assert((UINT32)size == buffers[miplevel]->getConsecutiveSize());
1226
1227 activeBuffer = buffers[miplevel];
1228
1229 bufferWritePos = activeBuffer->getData();
1230 bufferEnd = bufferWritePos + activeBuffer->getConsecutiveSize();
1231 }
1232
1233 bool writeData(const void* data, int size) override
1234 {
1235 assert((bufferWritePos + size) <= bufferEnd);
1236 memcpy(bufferWritePos, data, size);
1237 bufferWritePos += size;
1238
1239 return true;
1240 }
1241
1242 void endImage() override
1243 { }
1244
1245 Vector<SPtr<PixelData>> buffers;
1246 SPtr<PixelData> activeBuffer;
1247
1248 UINT8* bufferWritePos;
1249 UINT8* bufferEnd;
1250 };
1251
1252 nvtt::Format toNVTTFormat(PixelFormat format)
1253 {
1254 switch (format)
1255 {
1256 case PF_BC1:
1257 return nvtt::Format_BC1;
1258 case PF_BC1a:
1259 return nvtt::Format_BC1a;
1260 case PF_BC2:
1261 return nvtt::Format_BC2;
1262 case PF_BC3:
1263 return nvtt::Format_BC3;
1264 case PF_BC4:
1265 return nvtt::Format_BC4;
1266 case PF_BC5:
1267 return nvtt::Format_BC5;
1268 case PF_BC6H:
1269 return nvtt::Format_BC6;
1270 case PF_BC7:
1271 return nvtt::Format_BC7;
1272 default: // Unsupported format
1273 return nvtt::Format_BC3;
1274 }
1275 }
1276
1277 nvtt::Quality toNVTTQuality(CompressionQuality quality)
1278 {
1279 switch (quality)
1280 {
1281 case CompressionQuality::Fastest:
1282 return nvtt::Quality_Fastest;
1283 case CompressionQuality::Highest:
1284 return nvtt::Quality_Highest;
1285 case CompressionQuality::Normal:
1286 return nvtt::Quality_Normal;
1287 case CompressionQuality::Production:
1288 return nvtt::Quality_Normal;
1289 }
1290
1291 // Unknown quality level
1292 return nvtt::Quality_Normal;
1293 }
1294
1295 nvtt::AlphaMode toNVTTAlphaMode(AlphaMode alphaMode)
1296 {
1297 switch (alphaMode)
1298 {
1299 case AlphaMode::None:
1300 return nvtt::AlphaMode_None;
1301 case AlphaMode::Premultiplied:
1302 return nvtt::AlphaMode_Premultiplied;
1303 case AlphaMode::Transparency:
1304 return nvtt::AlphaMode_Transparency;
1305 }
1306
1307 // Unknown alpha mode
1308 return nvtt::AlphaMode_None;
1309 }
1310
1311 nvtt::WrapMode toNVTTWrapMode(MipMapWrapMode wrapMode)
1312 {
1313 switch (wrapMode)
1314 {
1315 case MipMapWrapMode::Clamp:
1316 return nvtt::WrapMode_Clamp;
1317 case MipMapWrapMode::Mirror:
1318 return nvtt::WrapMode_Mirror;
1319 case MipMapWrapMode::Repeat:
1320 return nvtt::WrapMode_Repeat;
1321 }
1322
1323 // Unknown alpha mode
1324 return nvtt::WrapMode_Mirror;
1325 }
1326
1327 UINT32 PixelUtil::getNumElemBytes(PixelFormat format)
1328 {
1329 return getDescriptionFor(format).elemBytes;
1330 }
1331
1332 UINT32 PixelUtil::getMemorySize(UINT32 width, UINT32 height, UINT32 depth, PixelFormat format)
1333 {
1334 if(isCompressed(format))
1335 {
1336 switch(format)
1337 {
1338 // BC formats work by dividing the image into 4x4 blocks, then encoding each
1339 // 4x4 block with a certain number of bytes.
1340 case PF_BC1:
1341 case PF_BC1a:
1342 case PF_BC4:
1343 return ((width+3)/4)*((height+3)/4)*8 * depth;
1344 case PF_BC2:
1345 case PF_BC3:
1346 case PF_BC5:
1347 case PF_BC6H:
1348 case PF_BC7:
1349 return ((width+3)/4)*((height+3)/4)*16 * depth;
1350
1351 default:
1352 BS_EXCEPT(InvalidParametersException, "Invalid compressed pixel format");
1353 return 0;
1354 }
1355 }
1356
1357 return width*height*depth*getNumElemBytes(format);
1358 }
1359
1360 void PixelUtil::getPitch(UINT32 width, UINT32 height, UINT32 depth, PixelFormat format,
1361 UINT32& rowPitch, UINT32& depthPitch)
1362 {
1363 if (isCompressed(format))
1364 {
1365 switch (format)
1366 {
1367 // BC formats work by dividing the image into 4x4 blocks, then encoding each
1368 // 4x4 block with a certain number of bytes.
1369 case PF_BC1:
1370 case PF_BC1a:
1371 case PF_BC4:
1372 case PF_BC2:
1373 case PF_BC3:
1374 case PF_BC5:
1375 case PF_BC6H:
1376 case PF_BC7:
1377 rowPitch = div(width + 3, 4).quot * 4;
1378 depthPitch = div(height + 3, 4).quot * 4 * rowPitch;
1379 return;
1380
1381 default:
1382 BS_EXCEPT(InvalidParametersException, "Invalid compressed pixel format");
1383 return;
1384 }
1385 }
1386
1387 rowPitch = width;
1388 depthPitch = width * height;
1389 }
1390
1391 void PixelUtil::getSizeForMipLevel(UINT32 width, UINT32 height, UINT32 depth, UINT32 mipLevel,
1392 UINT32& mipWidth, UINT32& mipHeight, UINT32& mipDepth)
1393 {
1394 mipWidth = width;
1395 mipHeight = height;
1396 mipDepth = depth;
1397
1398 for (UINT32 i = 0; i < mipLevel; i++)
1399 {
1400 if (mipWidth != 1) mipWidth /= 2;
1401 if (mipHeight != 1) mipHeight /= 2;
1402 if (mipDepth != 1) mipDepth /= 2;
1403 }
1404 }
1405
1406 UINT32 PixelUtil::getNumElemBits(PixelFormat format)
1407 {
1408 return getDescriptionFor(format).elemBytes * 8;
1409 }
1410
1411 UINT32 PixelUtil::getFlags(PixelFormat format)
1412 {
1413 return getDescriptionFor(format).flags;
1414 }
1415
1416 bool PixelUtil::hasAlpha(PixelFormat format)
1417 {
1418 return (PixelUtil::getFlags(format) & PFF_HASALPHA) > 0;
1419 }
1420
1421 bool PixelUtil::isFloatingPoint(PixelFormat format)
1422 {
1423 return (PixelUtil::getFlags(format) & PFF_FLOAT) > 0;
1424 }
1425
1426 bool PixelUtil::isCompressed(PixelFormat format)
1427 {
1428 return (PixelUtil::getFlags(format) & PFF_COMPRESSED) > 0;
1429 }
1430
1431 bool PixelUtil::isNormalized(PixelFormat format)
1432 {
1433 return (PixelUtil::getFlags(format) & PFF_NORMALIZED) > 0;
1434 }
1435
1436 bool PixelUtil::isDepth(PixelFormat format)
1437 {
1438 return (PixelUtil::getFlags(format) & PFF_DEPTH) > 0;
1439 }
1440
1441 bool PixelUtil::checkFormat(PixelFormat& format, TextureType texType, int usage)
1442 {
1443 // First check just the usage since it's the most limiting factor
1444
1445 //// Depth-stencil only supports depth formats
1446 if ((usage & TU_DEPTHSTENCIL) != 0)
1447 {
1448 if (isDepth(format))
1449 return true;
1450
1451 format = PF_D32_S8X24;
1452 return false;
1453 }
1454
1455 //// Render targets support everything but compressed & depth-stencil formats
1456 if ((usage & TU_RENDERTARGET) != 0)
1457 {
1458 if (!isDepth(format) && !isCompressed(format))
1459 return true;
1460
1461 format = PF_RGBA8;
1462 return false;
1463 }
1464
1465 //// Load-store textures support everything but compressed & depth-stencil formats
1466 if ((usage & TU_LOADSTORE) != 0)
1467 {
1468 if (!isDepth(format) && !isCompressed(format))
1469 return true;
1470
1471 format = PF_RGBA8;
1472 return false;
1473 }
1474
1475 //// Sampled texture support depends on texture type
1476 switch (texType)
1477 {
1478 case TEX_TYPE_1D:
1479 {
1480 // 1D textures support anything but depth & compressed formats
1481 if (!isDepth(format) && !isCompressed(format))
1482 return true;
1483
1484 format = PF_RGBA8;
1485 return false;
1486 }
1487 case TEX_TYPE_3D:
1488 {
1489 // 3D textures support anything but depth & compressed formats
1490 if (!isDepth(format))
1491 return true;
1492
1493 format = PF_RGBA8;
1494 return false;
1495 }
1496 default: // 2D & cube
1497 {
1498 // 2D/cube textures support anything but depth formats
1499 if (!isDepth(format))
1500 return true;
1501
1502 format = PF_RGBA8;
1503 return false;
1504 }
1505 }
1506 }
1507
1508 bool PixelUtil::isValidExtent(UINT32 width, UINT32 height, UINT32 depth, PixelFormat format)
1509 {
1510 if(isCompressed(format))
1511 {
1512 switch(format)
1513 {
1514 case PF_BC1:
1515 case PF_BC2:
1516 case PF_BC1a:
1517 case PF_BC3:
1518 case PF_BC4:
1519 case PF_BC5:
1520 case PF_BC6H:
1521 case PF_BC7:
1522 return ((width & 3) == 0 && (height & 3) == 0 && depth == 1);
1523 default:
1524 return true;
1525 }
1526 }
1527 else
1528 {
1529 return true;
1530 }
1531 }
1532
1533 void PixelUtil::getBitDepths(PixelFormat format, int(&rgba)[4])
1534 {
1535 const PixelFormatDescription& des = getDescriptionFor(format);
1536 rgba[0] = des.rbits;
1537 rgba[1] = des.gbits;
1538 rgba[2] = des.bbits;
1539 rgba[3] = des.abits;
1540 }
1541
1542 void PixelUtil::getBitMasks(PixelFormat format, UINT32(&rgba)[4])
1543 {
1544 const PixelFormatDescription& des = getDescriptionFor(format);
1545 rgba[0] = des.rmask;
1546 rgba[1] = des.gmask;
1547 rgba[2] = des.bmask;
1548 rgba[3] = des.amask;
1549 }
1550
1551 void PixelUtil::getBitShifts(PixelFormat format, UINT8(&rgba)[4])
1552 {
1553 const PixelFormatDescription& des = getDescriptionFor(format);
1554 rgba[0] = des.rshift;
1555 rgba[1] = des.gshift;
1556 rgba[2] = des.bshift;
1557 rgba[3] = des.ashift;
1558 }
1559
1560 String PixelUtil::getFormatName(PixelFormat srcformat)
1561 {
1562 return getDescriptionFor(srcformat).name;
1563 }
1564
1565 bool PixelUtil::isAccessible(PixelFormat srcformat)
1566 {
1567 if (srcformat == PF_UNKNOWN)
1568 return false;
1569
1570 UINT32 flags = getFlags(srcformat);
1571 return !((flags & PFF_COMPRESSED) || (flags & PFF_DEPTH));
1572 }
1573
1574 PixelComponentType PixelUtil::getElementType(PixelFormat format)
1575 {
1576 const PixelFormatDescription& des = getDescriptionFor(format);
1577 return des.componentType;
1578 }
1579
1580 UINT32 PixelUtil::getNumElements(PixelFormat format)
1581 {
1582 const PixelFormatDescription& des = getDescriptionFor(format);
1583 return des.componentCount;
1584 }
1585
1586 UINT32 PixelUtil::getMaxMipmaps(UINT32 width, UINT32 height, UINT32 depth, PixelFormat format)
1587 {
1588 UINT32 count = 0;
1589 if ((width > 0) && (height > 0))
1590 {
1591 while (!(width == 1 && height == 1 && depth == 1))
1592 {
1593 if (width > 1) width = width / 2;
1594 if (height > 1) height = height / 2;
1595 if (depth > 1) depth = depth / 2;
1596
1597 count++;
1598 }
1599 }
1600
1601 return count;
1602 }
1603
1604 void PixelUtil::packColor(const Color& color, PixelFormat format, void* dest)
1605 {
1606 packColor(color.r, color.g, color.b, color.a, format, dest);
1607 }
1608
1609 void PixelUtil::packColor(UINT8 r, UINT8 g, UINT8 b, UINT8 a, PixelFormat format, void* dest)
1610 {
1611 const PixelFormatDescription &des = getDescriptionFor(format);
1612
1613 if (des.flags & PFF_INTEGER)
1614 {
1615 // Shortcut for integer formats packing
1616 UINT32 value = ((Bitwise::fixedToFixed(r, 8, des.rbits) << des.rshift) & des.rmask) |
1617 ((Bitwise::fixedToFixed(g, 8, des.gbits) << des.gshift) & des.gmask) |
1618 ((Bitwise::fixedToFixed(b, 8, des.bbits) << des.bshift) & des.bmask) |
1619 ((Bitwise::fixedToFixed(a, 8, des.abits) << des.ashift) & des.amask);
1620
1621 // And write to memory
1622 Bitwise::intWrite(dest, des.elemBytes, value);
1623 }
1624 else
1625 {
1626 // Convert to float
1627 packColor((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)a / 255.0f, format, dest);
1628 }
1629 }
1630
1631 void PixelUtil::packColor(float r, float g, float b, float a, const PixelFormat format, void* dest)
1632 {
1633 // Special cases
1634 if (format == PF_RG11B10F)
1635 {
1636 UINT32 value;
1637 value = Bitwise::floatToFloat11(r);
1638 value |= Bitwise::floatToFloat11(g) << 11;
1639 value |= Bitwise::floatToFloat10(b) << 22;
1640
1641 ((UINT32*)dest)[0] = value;
1642 return;
1643 }
1644
1645 if (format == PF_RGB10A2)
1646 {
1647 LOGERR("packColor() not implemented for format \"" + getFormatName(PF_RGB10A2) + "\".");
1648 return;
1649 }
1650
1651 // All other formats handled in a generic way
1652 const PixelFormatDescription& des = getDescriptionFor(format);
1653 assert(des.componentCount <= 4);
1654
1655 float inputs[] = { r, g, b, a };
1656 UINT8 bits[] = { des.rbits, des.gbits, des.bbits, des.abits };
1657 UINT32 masks[] = { des.rmask, des.gmask, des.bmask, des.amask };
1658 UINT8 shifts[] = { des.rshift, des.gshift, des.bshift, des.ashift };
1659
1660 memset(dest, 0, des.elemBytes);
1661
1662 UINT32 curBit = 0;
1663 UINT32 prevDword = 0;
1664 UINT32 dwordValue = 0;
1665 for (UINT32 i = 0; i < des.componentCount; i++)
1666 {
1667 UINT32 curDword = curBit / 32;
1668
1669 // New dword reached, write current one and advance
1670 if(curDword > prevDword)
1671 {
1672 UINT32* curDst = ((UINT32*)dest) + prevDword;
1673 Bitwise::intWrite(curDst, 4, dwordValue);
1674
1675 dwordValue = 0;
1676 prevDword = curDword;
1677 }
1678
1679 if (des.flags & PFF_INTEGER)
1680 {
1681 if (des.flags & PFF_NORMALIZED)
1682 {
1683 if (des.flags & PFF_SIGNED)
1684 dwordValue |= (Bitwise::snormToUint(inputs[i], bits[i]) << shifts[i]) & masks[i];
1685 else
1686 dwordValue |= (Bitwise::unormToUint(inputs[i], bits[i]) << shifts[i]) & masks[i];
1687 }
1688 else
1689 {
1690 // Note: Casting integer to float. A better option would be to have a separate unpackColor that has
1691 // integer output parameters.
1692 dwordValue |= (((UINT32)inputs[i]) << shifts[i]) & masks[i];
1693 }
1694 }
1695 else if (des.flags & PFF_FLOAT)
1696 {
1697 // Note: Not handling unsigned floats
1698
1699 if (des.componentType == PCT_FLOAT16)
1700 dwordValue |= (Bitwise::floatToHalf(inputs[i]) << shifts[i]) & masks[i];
1701 else
1702 dwordValue |= *(UINT32*)&inputs[i];
1703 }
1704 else
1705 {
1706 LOGERR("packColor() not implemented for format \"" + getFormatName(format) + "\".");
1707 return;
1708 }
1709
1710 curBit += bits[i];
1711 }
1712
1713 // Write last dword
1714 UINT32 numBytes = std::min((prevDword + 1) * 4, (UINT32)des.elemBytes) - (prevDword * 4);
1715 UINT32* curDst = ((UINT32*)dest) + prevDword;
1716 Bitwise::intWrite(curDst, numBytes, dwordValue);
1717 }
1718
1719 void PixelUtil::unpackColor(Color* color, PixelFormat format, const void* src)
1720 {
1721 unpackColor(&color->r, &color->g, &color->b, &color->a, format, src);
1722 }
1723
1724 void PixelUtil::unpackColor(UINT8* r, UINT8* g, UINT8* b, UINT8* a, PixelFormat format, const void* src)
1725 {
1726 const PixelFormatDescription &des = getDescriptionFor(format);
1727
1728 if (des.flags & PFF_INTEGER)
1729 {
1730 // Shortcut for integer formats unpacking
1731 const UINT32 value = Bitwise::intRead(src, des.elemBytes);
1732
1733 *r = (UINT8)Bitwise::fixedToFixed((value & des.rmask) >> des.rshift, des.rbits, 8);
1734 *g = (UINT8)Bitwise::fixedToFixed((value & des.gmask) >> des.gshift, des.gbits, 8);
1735 *b = (UINT8)Bitwise::fixedToFixed((value & des.bmask) >> des.bshift, des.bbits, 8);
1736
1737 if (des.flags & PFF_HASALPHA)
1738 {
1739 *a = (UINT8)Bitwise::fixedToFixed((value & des.amask) >> des.ashift, des.abits, 8);
1740 }
1741 else
1742 {
1743 *a = 255; // No alpha, default a component to full
1744 }
1745 }
1746 else
1747 {
1748 // Do the operation with the more generic floating point
1749 float rr, gg, bb, aa;
1750 unpackColor(&rr, &gg, &bb, &aa, format, src);
1751
1752 *r = (UINT8)Bitwise::unormToUint(rr, 8);
1753 *g = (UINT8)Bitwise::unormToUint(gg, 8);
1754 *b = (UINT8)Bitwise::unormToUint(bb, 8);
1755 *a = (UINT8)Bitwise::unormToUint(aa, 8);
1756 }
1757 }
1758
1759 void PixelUtil::unpackColor(float* r, float* g, float* b, float* a, PixelFormat format, const void* src)
1760 {
1761 // Special cases
1762 if(format == PF_RG11B10F)
1763 {
1764 UINT32 value = ((UINT32*)src)[0];
1765 *r = Bitwise::float11ToFloat(value);
1766 *g = Bitwise::float11ToFloat(value >> 11);
1767 *b = Bitwise::float10ToFloat(value >> 22);
1768
1769 return;
1770 }
1771
1772 if(format == PF_RGB10A2)
1773 {
1774 LOGERR("unpackColor() not implemented for format \"" + getFormatName(PF_RGB10A2) + "\".");
1775 return;
1776 }
1777
1778 // All other formats handled in a generic way
1779 const PixelFormatDescription& des = getDescriptionFor(format);
1780 assert(des.componentCount <= 4);
1781
1782 float* outputs[] = { r, g, b, a };
1783 UINT8 bits[] = { des.rbits, des.gbits, des.bbits, des.abits };
1784 UINT32 masks[] = { des.rmask, des.gmask, des.bmask, des.amask };
1785 UINT8 shifts[] = { des.rshift, des.gshift, des.bshift, des.ashift };
1786
1787 UINT32 curBit = 0;
1788 for(UINT32 i = 0; i < des.componentCount; i++)
1789 {
1790 UINT32 curDword = curBit / 32;
1791 UINT32 numBytes = std::min((curDword + 1) * 4, (UINT32)des.elemBytes) - (curDword * 4);
1792
1793 UINT32* curSrc = ((UINT32*)src) + curDword;
1794 UINT32 value = Bitwise::intRead(curSrc, numBytes);
1795 if(des.flags & PFF_INTEGER)
1796 {
1797 if(des.flags & PFF_NORMALIZED)
1798 {
1799 if (des.flags & PFF_SIGNED)
1800 *outputs[i] = Bitwise::uintToSnorm((value & masks[i]) >> shifts[i], bits[i]);
1801 else
1802 *outputs[i] = Bitwise::uintToUnorm((value & masks[i]) >> shifts[i], bits[i]);
1803 }
1804 else
1805 {
1806 // Note: Casting integer to float. A better option would be to have a separate unpackColor that has
1807 // integer output parameters.
1808 *outputs[i] = (float)((value & masks[i]) >> shifts[i]);
1809 }
1810 }
1811 else if(des.flags & PFF_FLOAT)
1812 {
1813 // Note: Not handling unsigned floats
1814
1815 if (des.componentType == PCT_FLOAT16)
1816 *outputs[i] = Bitwise::halfToFloat((UINT16)((value & masks[i]) >> shifts[i]));
1817 else
1818 *outputs[i] = *(float*)&value;
1819 }
1820 else
1821 {
1822 LOGERR("unpackColor() not implemented for format \"" + getFormatName(format) + "\".");
1823 return;
1824 }
1825
1826 curBit += bits[i];
1827 }
1828
1829 // Fill empty components
1830 for (UINT32 i = des.componentCount; i < 3; i++)
1831 *outputs[i] = 0.0f;
1832
1833 if (des.componentCount < 4)
1834 *outputs[3] = 1.0f;
1835 }
1836
1837 void PixelUtil::packDepth(float depth, const PixelFormat format, void* dest)
1838 {
1839 if (!isDepth(format))
1840 {
1841 LOGERR("Cannot convert depth to " + getFormatName(format) + ": it is not a depth format");
1842 return;
1843 }
1844
1845 LOGERR("Method is not implemented");
1846 //TODO implement depth packing
1847 }
1848
1849 float PixelUtil::unpackDepth(PixelFormat format, void* src)
1850 {
1851 if (!isDepth(format))
1852 {
1853 LOGERR("Cannot unpack from " + getFormatName(format) + ": it is not a depth format");
1854 return 0;
1855 }
1856
1857 UINT32* color = (UINT32 *)src;
1858 UINT32 masked = 0;
1859 switch (format)
1860 {
1861 case PF_D24S8:
1862 return static_cast<float>(*color & 0x00FFFFFF) / (float)16777216;
1863 break;
1864 case PF_D16:
1865 return static_cast<float>(*color & 0xFFFF) / (float)65536;
1866 break;
1867 case PF_D32:
1868 masked = *color & 0xFFFFFFFF;
1869 return *((float*)&masked);
1870 break;
1871 case PF_D32_S8X24:
1872 masked = *color & 0xFFFFFFFF;
1873 return *( (float*) &masked );
1874 break;
1875 default:
1876 LOGERR("Cannot unpack from " + getFormatName(format));
1877 return 0;
1878 break;
1879 }
1880 }
1881
1882 void PixelUtil::bulkPixelConversion(const PixelData &src, PixelData &dst)
1883 {
1884 assert(src.getWidth() == dst.getWidth() &&
1885 src.getHeight() == dst.getHeight() &&
1886 src.getDepth() == dst.getDepth());
1887
1888 // Check for compressed formats, we don't support decompression
1889 if (PixelUtil::isCompressed(src.getFormat()))
1890 {
1891 if (src.getFormat() == dst.getFormat())
1892 {
1893 memcpy(dst.getData(), src.getData(), src.getConsecutiveSize());
1894 return;
1895 }
1896 else
1897 {
1898 LOGERR("bulkPixelConversion() cannot be used to compress or decompress images");
1899 return;
1900 }
1901 }
1902
1903 // Check for compression
1904 if (PixelUtil::isCompressed(dst.getFormat()))
1905 {
1906 if (src.getFormat() == dst.getFormat())
1907 {
1908 memcpy(dst.getData(), src.getData(), src.getConsecutiveSize());
1909 return;
1910 }
1911 else
1912 {
1913 CompressionOptions co;
1914 co.format = dst.getFormat();
1915 compress(src, dst, co);
1916
1917 return;
1918 }
1919 }
1920
1921 // The easy case
1922 if (src.getFormat() == dst.getFormat())
1923 {
1924 // Everything consecutive?
1925 if (src.isConsecutive() && dst.isConsecutive())
1926 {
1927 memcpy(dst.getData(), src.getData(), src.getConsecutiveSize());
1928 return;
1929 }
1930
1931 const UINT32 srcPixelSize = PixelUtil::getNumElemBytes(src.getFormat());
1932 const UINT32 dstPixelSize = PixelUtil::getNumElemBytes(dst.getFormat());
1933 UINT8 *srcptr = static_cast<UINT8*>(src.getData())
1934 + (src.getLeft() + src.getTop() * src.getRowPitch() + src.getFront() * src.getSlicePitch()) * srcPixelSize;
1935 UINT8 *dstptr = static_cast<UINT8*>(dst.getData())
1936 + (dst.getLeft() + dst.getTop() * dst.getRowPitch() + dst.getFront() * dst.getSlicePitch()) * dstPixelSize;
1937
1938 // Calculate pitches+skips in bytes
1939 const UINT32 srcRowPitchBytes = src.getRowPitch()*srcPixelSize;
1940 const UINT32 srcSliceSkipBytes = src.getSliceSkip()*srcPixelSize;
1941
1942 const UINT32 dstRowPitchBytes = dst.getRowPitch()*dstPixelSize;
1943 const UINT32 dstSliceSkipBytes = dst.getSliceSkip()*dstPixelSize;
1944
1945 // Otherwise, copy per row
1946 const UINT32 rowSize = src.getWidth()*srcPixelSize;
1947 for (UINT32 z = src.getFront(); z < src.getBack(); z++)
1948 {
1949 for (UINT32 y = src.getTop(); y < src.getBottom(); y++)
1950 {
1951 memcpy(dstptr, srcptr, rowSize);
1952
1953 srcptr += srcRowPitchBytes;
1954 dstptr += dstRowPitchBytes;
1955 }
1956
1957 srcptr += srcSliceSkipBytes;
1958 dstptr += dstSliceSkipBytes;
1959 }
1960
1961 return;
1962 }
1963
1964 UINT32 srcPixelSize = PixelUtil::getNumElemBytes(src.getFormat());
1965 UINT32 dstPixelSize = PixelUtil::getNumElemBytes(dst.getFormat());
1966 UINT8 *srcptr = static_cast<UINT8*>(src.getData())
1967 + (src.getLeft() + src.getTop() * src.getRowPitch() + src.getFront() * src.getSlicePitch()) * srcPixelSize;
1968 UINT8 *dstptr = static_cast<UINT8*>(dst.getData())
1969 + (dst.getLeft() + dst.getTop() * dst.getRowPitch() + dst.getFront() * dst.getSlicePitch()) * dstPixelSize;
1970
1971 // Calculate pitches+skips in bytes
1972 UINT32 srcRowSkipBytes = src.getRowSkip()*srcPixelSize;
1973 UINT32 srcSliceSkipBytes = src.getSliceSkip()*srcPixelSize;
1974 UINT32 dstRowSkipBytes = dst.getRowSkip()*dstPixelSize;
1975 UINT32 dstSliceSkipBytes = dst.getSliceSkip()*dstPixelSize;
1976
1977 // The brute force fallback
1978 float r, g, b, a;
1979 for (UINT32 z = src.getFront(); z < src.getBack(); z++)
1980 {
1981 for (UINT32 y = src.getTop(); y < src.getBottom(); y++)
1982 {
1983 for (UINT32 x = src.getLeft(); x < src.getRight(); x++)
1984 {
1985 unpackColor(&r, &g, &b, &a, src.getFormat(), srcptr);
1986 packColor(r, g, b, a, dst.getFormat(), dstptr);
1987
1988 srcptr += srcPixelSize;
1989 dstptr += dstPixelSize;
1990 }
1991
1992 srcptr += srcRowSkipBytes;
1993 dstptr += dstRowSkipBytes;
1994 }
1995
1996 srcptr += srcSliceSkipBytes;
1997 dstptr += dstSliceSkipBytes;
1998 }
1999 }
2000
2001 void PixelUtil::flipComponentOrder(PixelData& data)
2002 {
2003 if (isCompressed(data.getFormat()))
2004 {
2005 LOGERR("flipComponentOrder() not supported on compressed images.");
2006 return;
2007 }
2008
2009 const PixelFormatDescription& pfd = getDescriptionFor(data.getFormat());
2010 if(pfd.elemBytes > 4)
2011 {
2012 LOGERR("flipComponentOrder() only supported on 4 byte or smaller pixel formats.");
2013 return;
2014 }
2015
2016 if (pfd.componentCount <= 1) // Nothing to flip
2017 return;
2018
2019 bool bitCountMismatch = false;
2020 if (pfd.rbits != pfd.gbits)
2021 bitCountMismatch = true;
2022
2023 if(pfd.componentCount > 2 && pfd.rbits != pfd.bbits)
2024 bitCountMismatch = true;
2025
2026 if (pfd.componentCount > 3 && pfd.rbits != pfd.abits)
2027 bitCountMismatch = true;
2028
2029 if(bitCountMismatch)
2030 {
2031 LOGERR("flipComponentOrder() not supported for formats that don't have the same number of bytes for all components.");
2032 return;
2033 }
2034
2035 struct CompData
2036 {
2037 UINT32 mask;
2038 UINT8 shift;
2039 };
2040
2041 std::array<CompData, 4> compData =
2042 {{
2043 { pfd.rmask, pfd.rshift },
2044 { pfd.gmask, pfd.gshift },
2045 { pfd.bmask, pfd.bshift },
2046 { pfd.amask, pfd.ashift }
2047 }};
2048
2049 // Ensure unused components are at the end, after sort
2050 if (pfd.componentCount < 4)
2051 compData[3].shift = 0xFF;
2052
2053 if (pfd.componentCount < 3)
2054 compData[2].shift = 0xFF;
2055
2056 std::sort(compData.begin(), compData.end(),
2057 [&](const CompData& lhs, const CompData& rhs) { return lhs.shift < rhs.shift; }
2058 );
2059
2060 UINT8* dataPtr = data.getData();
2061
2062 UINT32 pixelSize = pfd.elemBytes;
2063 UINT32 rowSkipBytes = data.getRowSkip()*pixelSize;
2064 UINT32 sliceSkipBytes = data.getSliceSkip()*pixelSize;
2065
2066 for (UINT32 z = 0; z < data.getDepth(); z++)
2067 {
2068 for (UINT32 y = 0; y < data.getHeight(); y++)
2069 {
2070 for (UINT32 x = 0; x < data.getWidth(); x++)
2071 {
2072 if(pfd.componentCount == 2)
2073 {
2074 UINT64 pixelData = 0;
2075 memcpy(&pixelData, dataPtr, pixelSize);
2076
2077 UINT64 output = 0;
2078 output |= (pixelData & compData[1].mask) >> compData[1].shift;
2079 output |= (pixelData & compData[0].mask) << compData[1].shift;
2080
2081 memcpy(dataPtr, &output, pixelSize);
2082 }
2083 else if(pfd.componentCount == 3)
2084 {
2085 UINT64 pixelData = 0;
2086 memcpy(&pixelData, dataPtr, pixelSize);
2087
2088 UINT64 output = 0;
2089 output |= (pixelData & compData[2].mask) >> compData[2].shift;
2090 output |= (pixelData & compData[0].mask) << compData[2].shift;
2091
2092 memcpy(dataPtr, &output, pixelSize);
2093 }
2094 else if(pfd.componentCount == 4)
2095 {
2096 UINT64 pixelData = 0;
2097 memcpy(&pixelData, dataPtr, pixelSize);
2098
2099 UINT64 output = 0;
2100 output |= (pixelData & compData[3].mask) >> compData[3].shift;
2101 output |= (pixelData & compData[0].mask) << compData[3].shift;
2102
2103 output |= (pixelData & compData[2].mask) >> (compData[2].shift - compData[1].shift);
2104 output |= (pixelData & compData[1].mask) << (compData[2].shift - compData[1].shift);
2105
2106 memcpy(dataPtr, &output, pixelSize);
2107 }
2108
2109 dataPtr += pixelSize;
2110 }
2111
2112 dataPtr += rowSkipBytes;
2113 }
2114
2115 dataPtr += sliceSkipBytes;
2116 }
2117 }
2118
2119 void PixelUtil::scale(const PixelData& src, PixelData& scaled, Filter filter)
2120 {
2121 assert(PixelUtil::isAccessible(src.getFormat()));
2122 assert(PixelUtil::isAccessible(scaled.getFormat()));
2123
2124 PixelData temp;
2125 switch (filter)
2126 {
2127 default:
2128 case FILTER_NEAREST:
2129 if(src.getFormat() == scaled.getFormat())
2130 {
2131 // No intermediate buffer needed
2132 temp = scaled;
2133 }
2134 else
2135 {
2136 // Allocate temporary buffer of destination size in source format
2137 temp = PixelData(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.getFormat());
2138 temp.allocateInternalBuffer();
2139 }
2140
2141 // No conversion
2142 switch (PixelUtil::getNumElemBytes(src.getFormat()))
2143 {
2144 case 1: NearestResampler<1>::scale(src, temp); break;
2145 case 2: NearestResampler<2>::scale(src, temp); break;
2146 case 3: NearestResampler<3>::scale(src, temp); break;
2147 case 4: NearestResampler<4>::scale(src, temp); break;
2148 case 6: NearestResampler<6>::scale(src, temp); break;
2149 case 8: NearestResampler<8>::scale(src, temp); break;
2150 case 12: NearestResampler<12>::scale(src, temp); break;
2151 case 16: NearestResampler<16>::scale(src, temp); break;
2152 default:
2153 // Never reached
2154 assert(false);
2155 }
2156
2157 if(temp.getData() != scaled.getData())
2158 {
2159 // Blit temp buffer
2160 PixelUtil::bulkPixelConversion(temp, scaled);
2161
2162 temp.freeInternalBuffer();
2163 }
2164
2165 break;
2166
2167 case FILTER_LINEAR:
2168 switch (src.getFormat())
2169 {
2170 case PF_RG8:
2171 case PF_RGB8: case PF_BGR8:
2172 case PF_RGBA8: case PF_BGRA8:
2173 if(src.getFormat() == scaled.getFormat())
2174 {
2175 // No intermediate buffer needed
2176 temp = scaled;
2177 }
2178 else
2179 {
2180 // Allocate temp buffer of destination size in source format
2181 temp = PixelData(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.getFormat());
2182 temp.allocateInternalBuffer();
2183 }
2184
2185 // No conversion
2186 switch (PixelUtil::getNumElemBytes(src.getFormat()))
2187 {
2188 case 1: LinearResampler_Byte<1>::scale(src, temp); break;
2189 case 2: LinearResampler_Byte<2>::scale(src, temp); break;
2190 case 3: LinearResampler_Byte<3>::scale(src, temp); break;
2191 case 4: LinearResampler_Byte<4>::scale(src, temp); break;
2192 default:
2193 // Never reached
2194 assert(false);
2195 }
2196
2197 if(temp.getData() != scaled.getData())
2198 {
2199 // Blit temp buffer
2200 PixelUtil::bulkPixelConversion(temp, scaled);
2201 temp.freeInternalBuffer();
2202 }
2203
2204 break;
2205 case PF_RGB32F:
2206 case PF_RGBA32F:
2207 if (scaled.getFormat() == PF_RGB32F || scaled.getFormat() == PF_RGBA32F)
2208 {
2209 // float32 to float32, avoid unpack/repack overhead
2210 LinearResampler_Float32::scale(src, scaled);
2211 break;
2212 }
2213 // Else, fall through
2214 default:
2215 // Fallback case, slow but works
2216 LinearResampler::scale(src, scaled);
2217 }
2218 break;
2219 }
2220 }
2221
2222 void PixelUtil::copy(const PixelData& src, PixelData& dst, UINT32 offsetX, UINT32 offsetY, UINT32 offsetZ)
2223 {
2224 if(src.getFormat() != dst.getFormat())
2225 {
2226 LOGERR("Source format is different from destination format for copy(). This operation cannot be used for "
2227 "a format conversion. Aborting copy.");
2228 return;
2229 }
2230
2231 UINT32 right = offsetX + dst.getWidth();
2232 UINT32 bottom = offsetY + dst.getHeight();
2233 UINT32 back = offsetZ + dst.getDepth();
2234
2235 if(right > src.getWidth() || bottom > src.getHeight() || back > src.getDepth())
2236 {
2237 LOGERR("Provided offset or destination size is too large and is referencing pixels that are out of bounds"
2238 " on the source texture. Aborting copy().");
2239 return;
2240 }
2241
2242 UINT8* srcPtr = (UINT8*)src.getData() + offsetZ * src.getSlicePitch();
2243 UINT8* dstPtr = (UINT8*)dst.getData();
2244
2245 UINT32 elemSize = getNumElemBytes(dst.getFormat());
2246 UINT32 rowSize = dst.getWidth() * elemSize;
2247
2248 for(UINT32 z = 0; z < dst.getDepth(); z++)
2249 {
2250 UINT8* srcRowPtr = srcPtr + offsetY * src.getRowPitch() * elemSize;
2251 UINT8* dstRowPtr = dstPtr;
2252
2253 for(UINT32 y = 0; y < dst.getHeight(); y++)
2254 {
2255 memcpy(dstRowPtr, srcRowPtr + offsetX * elemSize, rowSize);
2256
2257 srcRowPtr += src.getRowPitch() * elemSize;
2258 dstRowPtr += dst.getRowPitch() * elemSize;
2259 }
2260
2261 srcPtr += src.getSlicePitch() * elemSize;
2262 dstPtr += dst.getSlicePitch() * elemSize;
2263 }
2264 }
2265
2266 void PixelUtil::mirror(PixelData& pixelData, MirrorMode mode)
2267 {
2268 UINT32 width = pixelData.getWidth();
2269 UINT32 height = pixelData.getHeight();
2270 UINT32 depth = pixelData.getDepth();
2271
2272 UINT32 elemSize = getNumElemBytes(pixelData.getFormat());
2273
2274 if (mode.isSet(MirrorModeBits::Z))
2275 {
2276 UINT32 sliceSize = width * height * elemSize;
2277 UINT8* sliceTemp = bs_stack_alloc<UINT8>(sliceSize);
2278
2279 UINT8* dataPtr = pixelData.getData();
2280 UINT32 halfDepth = depth / 2;
2281 for (UINT32 z = 0; z < halfDepth; z++)
2282 {
2283 UINT32 srcZ = z * sliceSize;
2284 UINT32 dstZ = (depth - z - 1) * sliceSize;
2285
2286 memcpy(sliceTemp, &dataPtr[dstZ], sliceSize);
2287 memcpy(&dataPtr[dstZ], &dataPtr[srcZ], sliceSize);
2288 memcpy(&dataPtr[srcZ], sliceTemp, sliceSize);
2289 }
2290
2291 // Note: If flipping Y or X as well I could do it here without an extra set of memcpys
2292
2293 bs_stack_free(sliceTemp);
2294 }
2295
2296 if(mode.isSet(MirrorModeBits::Y))
2297 {
2298 UINT32 rowSize = width * elemSize;
2299 UINT8* rowTemp = bs_stack_alloc<UINT8>(rowSize);
2300
2301 UINT8* slicePtr = pixelData.getData();
2302 for (UINT32 z = 0; z < depth; z++)
2303 {
2304 UINT32 halfHeight = height / 2;
2305 for (UINT32 y = 0; y < halfHeight; y++)
2306 {
2307 UINT32 srcY = y * rowSize;
2308 UINT32 dstY = (height - y - 1) * rowSize;
2309
2310 memcpy(rowTemp, &slicePtr[dstY], rowSize);
2311 memcpy(&slicePtr[dstY], &slicePtr[srcY], rowSize);
2312 memcpy(&slicePtr[srcY], rowTemp, rowSize);
2313 }
2314
2315 // Note: If flipping X as well I could do it here without an extra set of memcpys
2316
2317 slicePtr += pixelData.getSlicePitch() * elemSize;
2318 }
2319
2320 bs_stack_free(rowTemp);
2321 }
2322
2323 if (mode.isSet(MirrorModeBits::X))
2324 {
2325 UINT8* elemTemp = bs_stack_alloc<UINT8>(elemSize);
2326
2327 UINT8* slicePtr = pixelData.getData();
2328 for (UINT32 z = 0; z < depth; z++)
2329 {
2330 UINT8* rowPtr = slicePtr;
2331 for (UINT32 y = 0; y < height; y++)
2332 {
2333 UINT32 halfWidth = width / 2;
2334 for (UINT32 x = 0; x < halfWidth; x++)
2335 {
2336 UINT32 srcX = x * elemSize;
2337 UINT32 dstX = (width - x - 1) * elemSize;
2338
2339 memcpy(elemTemp, &rowPtr[dstX], elemSize);
2340 memcpy(&rowPtr[dstX], &rowPtr[srcX], elemSize);
2341 memcpy(&rowPtr[srcX], elemTemp, elemSize);
2342 }
2343
2344 rowPtr += pixelData.getRowPitch() * elemSize;
2345 }
2346
2347 slicePtr += pixelData.getSlicePitch() * elemSize;
2348 }
2349
2350 bs_stack_free(elemTemp);
2351 }
2352 }
2353
2354 float linearToSRGB(float x)
2355 {
2356 if (x <= 0.0f)
2357 return 0.0f;
2358 else if (x >= 1.0f)
2359 return 1.0f;
2360 else if (x < 0.0031308f)
2361 return x * 12.92f;
2362 else
2363 return std::pow(x, 1.0f / 2.4f) * 1.055f - 0.055f;
2364 }
2365
2366 float SRGBToLinear(float x)
2367 {
2368 if (x <= 0.0f)
2369 return 0.0f;
2370 else if (x >= 1.0f)
2371 return 1.0f;
2372 else if (x < 0.04045f)
2373 return x / 12.92f;
2374 else
2375 return std::pow((x + 0.055f) / 1.055f, 2.4f);
2376 }
2377
2378 Color PixelUtil::linearToSRGB(const bs::Color& color)
2379 {
2380 return Color(
2381 bs::linearToSRGB(color.r),
2382 bs::linearToSRGB(color.g),
2383 bs::linearToSRGB(color.b),
2384 color.a);
2385 }
2386
2387 Color PixelUtil::SRGBToLinear(const bs::Color& color)
2388 {
2389 return Color(
2390 bs::SRGBToLinear(color.r),
2391 bs::SRGBToLinear(color.g),
2392 bs::SRGBToLinear(color.b),
2393 color.a);
2394 }
2395
2396 void PixelUtil::linearToSRGB(PixelData& pixelData)
2397 {
2398 UINT32 depth = pixelData.getDepth();
2399 UINT32 height = pixelData.getHeight();
2400 UINT32 width = pixelData.getWidth();
2401
2402 UINT32 pixelSize = PixelUtil::getNumElemBytes(pixelData.getFormat());
2403 UINT8* data = pixelData.getData();
2404
2405 for (UINT32 z = 0; z < depth; z++)
2406 {
2407 UINT32 zDataIdx = z * pixelData.getSlicePitch() * pixelSize;
2408
2409 for (UINT32 y = 0; y < height; y++)
2410 {
2411 UINT32 yDataIdx = y * pixelData.getRowPitch() * pixelSize;
2412
2413 for (UINT32 x = 0; x < width; x++)
2414 {
2415 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
2416 UINT8* dest = data + dataIdx;
2417
2418 Color color;
2419
2420 PixelUtil::unpackColor(&color, pixelData.getFormat(), dest);
2421 color = linearToSRGB(color);
2422 PixelUtil::packColor(color, pixelData.getFormat(), dest);
2423 }
2424 }
2425 }
2426 }
2427
2428 void PixelUtil::SRGBToLinear(PixelData& pixelData)
2429 {
2430 UINT32 depth = pixelData.getDepth();
2431 UINT32 height = pixelData.getHeight();
2432 UINT32 width = pixelData.getWidth();
2433
2434 UINT32 pixelSize = PixelUtil::getNumElemBytes(pixelData.getFormat());
2435 UINT8* data = pixelData.getData();
2436
2437 for (UINT32 z = 0; z < depth; z++)
2438 {
2439 UINT32 zDataIdx = z * pixelData.getSlicePitch() * pixelSize;
2440
2441 for (UINT32 y = 0; y < height; y++)
2442 {
2443 UINT32 yDataIdx = y * pixelData.getRowPitch() * pixelSize;
2444
2445 for (UINT32 x = 0; x < width; x++)
2446 {
2447 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
2448 UINT8* dest = data + dataIdx;
2449
2450 Color color;
2451
2452 PixelUtil::unpackColor(&color, pixelData.getFormat(), dest);
2453 color = SRGBToLinear(color);
2454 PixelUtil::packColor(color, pixelData.getFormat(), dest);
2455 }
2456 }
2457 }
2458 }
2459
2460 void PixelUtil::compress(const PixelData& src, PixelData& dst, const CompressionOptions& options)
2461 {
2462 if (!isCompressed(options.format))
2463 {
2464 LOGERR("Compression failed. Destination format is not a valid compressed format.")
2465 return;
2466 }
2467
2468 if (src.getDepth() != 1)
2469 {
2470 LOGERR("Compression failed. 3D texture compression not supported.")
2471 return;
2472 }
2473
2474 if (isCompressed(src.getFormat()))
2475 {
2476 LOGERR("Compression failed. Source data cannot be compressed.");
2477 return;
2478 }
2479
2480 PixelFormat interimFormat = options.format == PF_BC6H ? PF_RGBA32F : PF_BGRA8;
2481
2482 PixelData interimData(src.getWidth(), src.getHeight(), 1, interimFormat);
2483 interimData.allocateInternalBuffer();
2484 bulkPixelConversion(src, interimData);
2485
2486 nvtt::InputOptions io;
2487 io.setTextureLayout(nvtt::TextureType_2D, src.getWidth(), src.getHeight());
2488 io.setMipmapGeneration(false);
2489 io.setAlphaMode(toNVTTAlphaMode(options.alphaMode));
2490 io.setNormalMap(options.isNormalMap);
2491
2492 if (interimFormat == PF_RGBA32F)
2493 io.setFormat(nvtt::InputFormat_RGBA_32F);
2494 else
2495 io.setFormat(nvtt::InputFormat_BGRA_8UB);
2496
2497 if (options.isSRGB)
2498 io.setGamma(2.2f, 2.2f);
2499 else
2500 io.setGamma(1.0f, 1.0f);
2501
2502 io.setMipmapData(interimData.getData(), src.getWidth(), src.getHeight());
2503
2504 nvtt::CompressionOptions co;
2505 co.setFormat(toNVTTFormat(options.format));
2506 co.setQuality(toNVTTQuality(options.quality));
2507
2508 NVTTCompressOutputHandler outputHandler(dst.getData(), dst.getConsecutiveSize());
2509
2510 nvtt::OutputOptions oo;
2511 oo.setOutputHeader(false);
2512 oo.setOutputHandler(&outputHandler);
2513
2514 nvtt::Compressor compressor;
2515 if (!compressor.process(io, co, oo))
2516 {
2517 LOGERR("Compression failed. Internal error.");
2518 return;
2519 }
2520 }
2521
2522 Vector<SPtr<PixelData>> PixelUtil::genMipmaps(const PixelData& src, const MipMapGenOptions& options)
2523 {
2524 Vector<SPtr<PixelData>> outputMipBuffers;
2525
2526 if (src.getDepth() != 1)
2527 {
2528 LOGERR("Mipmap generation failed. 3D texture formats not supported.")
2529 return outputMipBuffers;
2530 }
2531
2532 if (isCompressed(src.getFormat()))
2533 {
2534 LOGERR("Mipmap generation failed. Source data cannot be compressed.")
2535 return outputMipBuffers;
2536 }
2537
2538 if (!Bitwise::isPow2(src.getWidth()) || !Bitwise::isPow2(src.getHeight()))
2539 {
2540 LOGERR("Mipmap generation failed. Texture width & height must be powers of 2.");
2541 return outputMipBuffers;
2542 }
2543
2544 PixelFormat interimFormat = isFloatingPoint(src.getFormat()) ? PF_RGBA32F : PF_BGRA8;
2545
2546 PixelData interimData(src.getWidth(), src.getHeight(), 1, interimFormat);
2547 interimData.allocateInternalBuffer();
2548 bulkPixelConversion(src, interimData);
2549
2550 if (interimFormat != PF_RGBA32F)
2551 flipComponentOrder(interimData);
2552
2553 nvtt::InputOptions io;
2554 io.setTextureLayout(nvtt::TextureType_2D, src.getWidth(), src.getHeight());
2555 io.setMipmapGeneration(true);
2556 io.setNormalMap(options.isNormalMap);
2557 io.setNormalizeMipmaps(options.normalizeMipmaps);
2558 io.setWrapMode(toNVTTWrapMode(options.wrapMode));
2559
2560 if (interimFormat == PF_RGBA32F)
2561 io.setFormat(nvtt::InputFormat_RGBA_32F);
2562 else
2563 io.setFormat(nvtt::InputFormat_BGRA_8UB);
2564
2565 if (options.isSRGB)
2566 io.setGamma(2.2f, 2.2f);
2567 else
2568 io.setGamma(1.0f, 1.0f);
2569
2570 io.setMipmapData(interimData.getData(), src.getWidth(), src.getHeight());
2571
2572 nvtt::CompressionOptions co;
2573 co.setFormat(nvtt::Format_RGBA);
2574
2575 if (interimFormat == PF_RGBA32F)
2576 {
2577 co.setPixelType(nvtt::PixelType_Float);
2578 co.setPixelFormat(32, 32, 32, 32);
2579 }
2580 else
2581 {
2582 co.setPixelType(nvtt::PixelType_UnsignedNorm);
2583 co.setPixelFormat(32, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF);
2584 }
2585
2586 UINT32 numMips = getMaxMipmaps(src.getWidth(), src.getHeight(), 1, src.getFormat());
2587
2588 Vector<SPtr<PixelData>> rgbaMipBuffers;
2589
2590 // Note: This can be done more effectively without creating so many temp buffers
2591 // and working with the original formats directly, but it would complicate the code
2592 // too much at the moment.
2593 UINT32 curWidth = src.getWidth();
2594 UINT32 curHeight = src.getHeight();
2595 for (UINT32 i = 0; i < numMips; i++)
2596 {
2597 rgbaMipBuffers.push_back(bs_shared_ptr_new<PixelData>(curWidth, curHeight, 1, interimFormat));
2598 rgbaMipBuffers.back()->allocateInternalBuffer();
2599
2600 if (curWidth > 1)
2601 curWidth = curWidth / 2;
2602
2603 if (curHeight > 1)
2604 curHeight = curHeight / 2;
2605 }
2606
2607 rgbaMipBuffers.push_back(bs_shared_ptr_new<PixelData>(curWidth, curHeight, 1, interimFormat));
2608 rgbaMipBuffers.back()->allocateInternalBuffer();
2609
2610 NVTTMipmapOutputHandler outputHandler(rgbaMipBuffers);
2611
2612 nvtt::OutputOptions oo;
2613 oo.setOutputHeader(false);
2614 oo.setOutputHandler(&outputHandler);
2615
2616 nvtt::Compressor compressor;
2617 if (!compressor.process(io, co, oo))
2618 {
2619 LOGERR("Mipmap generation failed. Internal error.");
2620 return outputMipBuffers;
2621 }
2622
2623 interimData.freeInternalBuffer();
2624
2625 for (UINT32 i = 0; i < (UINT32)rgbaMipBuffers.size(); i++)
2626 {
2627 SPtr<PixelData> argbBuffer = rgbaMipBuffers[i];
2628 SPtr<PixelData> outputBuffer = bs_shared_ptr_new<PixelData>(argbBuffer->getWidth(), argbBuffer->getHeight(), 1, src.getFormat());
2629 outputBuffer->allocateInternalBuffer();
2630
2631 bulkPixelConversion(*argbBuffer, *outputBuffer);
2632 argbBuffer->freeInternalBuffer();
2633
2634 outputMipBuffers.push_back(outputBuffer);
2635 }
2636
2637 return outputMipBuffers;
2638 }
2639}
2640