SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
ImageFormats.h
Go to the documentation of this file.
1// ReSharper disable CppDFAUnreachableFunctionCall
2// ReSharper disable CppRedundantParentheses
3// ReSharper disable CppRedundantQualifier
4
5#pragma once
6
7#include <tuple>
8
9#include <sourcepp/Math.h>
10
11namespace vtfpp {
12
80
82
89[[nodiscard]] constexpr int8_t red(ImageFormat format) {
90 switch (format) {
91 using enum ImageFormat;
92 case R32F:
93 case RG3232F:
94 case RGB323232F:
95 case RGBA32323232F:
96 return 32;
97 case R16F:
98 case RG1616F:
99 case RGBA16161616F:
100 case RGBA16161616:
102 return 16;
103 case RGBA1010102:
104 case BGRA1010102:
105 return 10;
106 case RGBA8888:
108 case ABGR8888:
110 case RGB888:
112 case BGR888:
114 case I8:
116 case IA88:
117 case P8:
120 case ARGB8888:
122 case BGRA8888:
125 case BGRX8888:
128 case UV88:
129 case UVWQ8888:
130 case UVLX8888:
131 case RGBX8888:
132 case R8:
133 return 8;
134 case RGB565:
135 case BGR565:
136 case BGRX5551:
138 case BGRA5551:
139 return 5;
140 case BGRA4444:
141 return 4;
142 case A8:
143 case EMPTY:
144 return 0;
145 case DXT1:
146 case DXT3:
147 case DXT5:
149 case ATI2N:
150 case ATI1N:
151 case BC7:
152 case BC6H:
153 return -1;
154 }
155 return 0;
156}
157
163[[nodiscard]] constexpr int8_t decompressedRed(ImageFormat format) {
164 // This is merely for convenience, the true size may be different depending on the data
165 switch (format) {
166 using enum ImageFormat;
167 case DXT1:
169 case DXT3:
170 case DXT5:
171 case ATI2N:
172 case ATI1N:
173 case BC7:
174 return 8;
175 case BC6H:
176 return 16;
177 default:
178 break;
179 }
180 return red(format);
181}
182
189[[nodiscard]] constexpr int8_t green(ImageFormat format) {
190 switch (format) {
191 using enum ImageFormat;
192 case RG3232F:
193 case RGB323232F:
194 case RGBA32323232F:
195 return 32;
196 case RG1616F:
197 case RGBA16161616F:
198 case RGBA16161616:
200 return 16;
201 case RGBA1010102:
202 case BGRA1010102:
203 return 10;
204 case RGBA8888:
206 case ABGR8888:
208 case RGB888:
210 case BGR888:
214 case ARGB8888:
216 case BGRA8888:
219 case BGRX8888:
222 case UV88:
223 case UVWQ8888:
224 case UVLX8888:
225 case RGBX8888:
226 return 8;
227 case RGB565:
228 case BGR565:
229 return 6;
230 case BGRX5551:
232 case BGRA5551:
233 return 5;
234 case BGRA4444:
235 return 4;
236 case I8:
238 case IA88:
239 case P8:
240 case R32F:
241 case A8:
242 case EMPTY:
243 case R16F:
244 case R8:
245 return 0;
246 case DXT1:
247 case DXT3:
248 case DXT5:
250 case ATI2N:
251 case ATI1N:
252 case BC7:
253 case BC6H:
254 return -1;
255 }
256 return 0;
257}
258
264[[nodiscard]] constexpr int8_t decompressedGreen(ImageFormat format) {
265 // This is merely for convenience, the true size may be different depending on the data
266 switch (format) {
267 using enum ImageFormat;
268 case DXT1:
270 case DXT3:
271 case DXT5:
272 case ATI2N:
273 case ATI1N:
274 case BC7:
275 return 8;
276 case BC6H:
277 return 16;
278 default:
279 break;
280 }
281 return green(format);
282}
283
290[[nodiscard]] constexpr int8_t blue(ImageFormat format) {
291 switch (format) {
292 using enum ImageFormat;
293 case RGB323232F:
294 case RGBA32323232F:
295 return 32;
296 case RGBA16161616F:
297 case RGBA16161616:
299 return 16;
300 case RGBA1010102:
301 case BGRA1010102:
302 return 10;
303 case RGBA8888:
305 case ABGR8888:
307 case RGB888:
309 case BGR888:
313 case ARGB8888:
315 case BGRA8888:
318 case BGRX8888:
321 case UVWQ8888:
322 case UVLX8888:
323 case RGBX8888:
324 return 8;
325 case RGB565:
326 case BGR565:
327 case BGRX5551:
329 case BGRA5551:
330 return 5;
331 case BGRA4444:
332 return 4;
333 case I8:
335 case IA88:
336 case P8:
337 case UV88:
338 case R32F:
339 case A8:
340 case EMPTY:
341 case RG3232F:
342 case RG1616F:
343 case R16F:
344 case R8:
345 return 0;
346 case DXT1:
347 case DXT3:
348 case DXT5:
350 case ATI2N:
351 case ATI1N:
352 case BC7:
353 case BC6H:
354 return -1;
355 }
356 return 0;
357}
358
364[[nodiscard]] constexpr int8_t decompressedBlue(ImageFormat format) {
365 // This is merely for convenience, the true size may be different depending on the data
366 switch (format) {
367 using enum ImageFormat;
368 case DXT1:
370 case DXT3:
371 case DXT5:
372 case ATI2N:
373 case ATI1N:
374 case BC7:
375 return 8;
376 case BC6H:
377 return 16;
378 default:
379 break;
380 }
381 return blue(format);
382}
383
390[[nodiscard]] constexpr int8_t alpha(ImageFormat format) {
391 switch (format) {
392 using enum ImageFormat;
393 case RGBA32323232F:
394 return 32;
395 case RGBA16161616F:
396 case RGBA16161616:
398 return 16;
399 case RGBA8888:
401 case ABGR8888:
403 case IA88:
404 case ARGB8888:
406 case BGRA8888:
409 case BGRX8888:
412 case UVWQ8888:
413 case UVLX8888:
414 case RGBX8888:
415 return 8;
416 case BGRA4444:
417 return 4;
418 case RGBA1010102:
419 case BGRA1010102:
420 return 2;
421 case BGRX5551:
423 case BGRA5551:
424 return 1;
425 case RGB888:
427 case BGR888:
429 case P8:
430 case I8:
434 case UV88:
435 case RGB565:
436 case BGR565:
437 case R32F:
438 case RGB323232F:
439 case A8:
440 case EMPTY:
441 case RG3232F:
442 case RG1616F:
443 case R16F:
444 case R8:
445 return 0;
446 case DXT1:
447 case DXT3:
448 case DXT5:
450 case ATI2N:
451 case ATI1N:
452 case BC7:
453 case BC6H:
454 return -1;
455 }
456 return 0;
457}
458
464[[nodiscard]] constexpr int8_t decompressedAlpha(ImageFormat format) {
465 // This is merely for convenience, the true size may be different depending on the data
466 switch (format) {
467 using enum ImageFormat;
468 case DXT5:
469 case BC7:
470 return 8;
471 case DXT3:
472 return 4;
474 return 1;
475 case DXT1:
476 case ATI2N:
477 case ATI1N:
478 case BC6H:
479 return 0;
480 default:
481 break;
482 }
483 return alpha(format);
484}
485
492[[nodiscard]] constexpr uint8_t bpp(ImageFormat format) {
493 switch (format) {
494 using enum ImageFormat;
495 case RGBA32323232F:
496 return 128;
497 case RGB323232F:
498 return 96;
499 case RGBA16161616F:
500 case RGBA16161616:
502 case RG3232F:
503 return 64;
504 case RGBA8888:
506 case ABGR8888:
508 case ARGB8888:
510 case BGRA8888:
513 case BGRX8888:
516 case UVLX8888:
517 case R32F:
518 case UVWQ8888:
519 case RGBX8888:
520 case RGBA1010102:
521 case BGRA1010102:
522 case RG1616F:
523 return 32;
524 case RGB888:
526 case BGR888:
530 return 24;
531 case RGB565:
532 case BGR565:
533 case IA88:
534 case BGRX5551:
536 case BGRA4444:
537 case BGRA5551:
538 case UV88:
539 case R16F:
540 return 16;
541 case I8:
543 case P8:
544 case A8:
545 case DXT3:
546 case DXT5:
547 case BC7:
548 case BC6H:
549 case ATI2N:
550 case R8:
551 return 8;
552 case ATI1N:
553 case DXT1:
555 return 4;
556 case EMPTY:
557 return 0;
558 }
559 return 0;
560}
561
570[[nodiscard]] constexpr ImageFormat containerFormat(ImageFormat format) {
571 switch (format) {
572 using enum ImageFormat;
573 case R32F:
574 case RG3232F:
575 case RGB323232F:
576 case R16F:
577 case RG1616F:
578 case RGBA16161616F:
579 case RGBA32323232F:
580 case BC6H:
581 return RGBA32323232F;
582 case RGBA16161616:
584 case RGBA1010102:
585 case BGRA1010102:
586 return RGBA16161616;
587 case RGBA8888:
589 case ABGR8888:
591 case RGB888:
593 case BGR888:
597 case ARGB8888:
599 case BGRA8888:
602 case BGRX8888:
605 case UVWQ8888:
606 case UVLX8888:
607 case RGB565:
608 case BGR565:
609 case BGRX5551:
611 case BGRA5551:
612 case BGRA4444:
613 case I8:
615 case IA88:
616 case P8:
617 case UV88:
618 case A8:
619 case DXT1:
620 case DXT3:
621 case DXT5:
623 case ATI2N:
624 case ATI1N:
625 case RGBX8888:
626 case R8:
627 case BC7:
628 return RGBA8888;
629 case EMPTY:
630 break;
631 }
632 return ImageFormat::EMPTY;
633}
634
640[[nodiscard]] constexpr bool large(ImageFormat format) {
642}
643
649[[nodiscard]] constexpr bool decimal(ImageFormat format) {
651}
652
658[[nodiscard]] constexpr bool compressed(ImageFormat format) {
659 return red(format) == -1;
660}
661
667[[nodiscard]] constexpr bool transparent(ImageFormat format) {
668 const auto a = alpha(format);
669 if (a < 0) {
670 switch (format) {
671 using enum ImageFormat;
672 case DXT3:
673 case DXT5:
675 case ATI2N:
676 case ATI1N:
677 case BC7:
678 case BC6H:
679 return true;
680 default:
681 break;
682 }
683 return false;
684 }
685 switch (format) {
686 using enum ImageFormat;
689 return true;
690 case BGRX8888:
693 case BGRX5551:
695 case UVLX8888:
696 case RGBX8888:
697 return false;
698 default:
699 break;
700 }
701 return a != 0;
702}
703
709[[nodiscard]] constexpr bool opaque(ImageFormat format) {
710 return !transparent(format);
711}
712
718[[nodiscard]] constexpr bool console(ImageFormat format) {
719 switch (format) {
720 using enum ImageFormat;
733 return true;
734 default:
735 break;
736 }
737 return false;
738}
739
740} // namespace ImageFormatDetails
741
743
753[[nodiscard]] constexpr uint16_t getMipDim(uint8_t mip, uint16_t dim, bool addCompressedFormatPadding = false) {
754 if (!dim) {
755 dim = 1;
756 }
757 for (int i = 0; i < mip && dim > 1; i++) {
758 dim >>= 1;
759 }
760 if (addCompressedFormatPadding) {
762 }
763 return dim;
764}
765
774[[nodiscard]] constexpr std::pair<uint16_t, uint16_t> getMipDims(uint8_t mip, uint16_t width, uint16_t height, bool addCompressedFormatPadding = false) {
775 for (int i = 0; i < mip && (width > 1 || height > 1); i++) {
776 if ((width >>= 1) < 1) width = 1;
777 if ((height >>= 1) < 1) height = 1;
778 }
779 if (addCompressedFormatPadding) {
780 width += sourcepp::math::paddingForAlignment(4, width);
781 height += sourcepp::math::paddingForAlignment(4, height);
782 }
783 return {width, height};
784}
785
795[[nodiscard]] constexpr std::tuple<uint16_t, uint16_t, uint16_t> getMipDims(uint8_t mip, uint16_t width, uint16_t height, uint16_t depth, bool addCompressedFormatPadding = false) {
796 for (int i = 0; i < mip && (width > 1 || height > 1 || depth > 1); i++) {
797 if ((width >>= 1) < 1) width = 1;
798 if ((height >>= 1) < 1) height = 1;
799 if ((depth >>= 1) < 1) depth = 1;
800 }
801 if (addCompressedFormatPadding) {
802 width += sourcepp::math::paddingForAlignment(4, width);
803 height += sourcepp::math::paddingForAlignment(4, height);
804 }
805 return {width, height, depth};
806}
807
817[[nodiscard]] constexpr uint8_t getMaximumMipCount(uint16_t width, uint16_t height, uint16_t depth = 1) {
818 uint8_t numMipLevels = 1;
819 if (width && height && depth) {
820 while (width > 1 || height > 1 || depth > 1) {
821 if ((width >>= 1) < 1) width = 1;
822 if ((height >>= 1) < 1) height = 1;
823 if ((depth >>= 1) < 1) depth = 1;
824 numMipLevels++;
825 }
826 }
827 return numMipLevels;
828}
829
830} // namespace ImageDimensions
831
832namespace ImageFormatDetails {
833
842[[nodiscard]] constexpr uint32_t getDataLength(ImageFormat format, uint16_t width, uint16_t height, uint16_t depth = 1) {
843 if (ImageFormatDetails::compressed(format)) {
844 return ((width + 3) / 4) * ((height + 3) / 4) * depth * bpp(format) * 2;
845 }
846 return width * height * depth * (bpp(format) / 8);
847}
848
861[[nodiscard]] constexpr uint32_t getDataLength(ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth = 1) {
862 uint32_t length = 0;
863 for (int i = mipCount - 1; i >= 0; i--) {
864 const auto [mipWidth, mipHeight, mipDepth] = ImageDimensions::getMipDims(i, width, height, depth);
865 length += ImageFormatDetails::getDataLength(format, mipWidth, mipHeight, mipDepth) * frameCount * faceCount;
866 }
867 return length;
868}
869
886[[nodiscard]] constexpr uint32_t getDataLengthXBOX(bool padded, ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth = 1) {
887 uint32_t length = 0;
888 for (int j = 0; j < frameCount; j++) {
889 for (int i = 0; i < mipCount; i++) {
890 const auto [mipWidth, mipHeight, mipDepth] = ImageDimensions::getMipDims(i, width, height, depth);
891 length += ImageFormatDetails::getDataLength(format, mipWidth, mipHeight, mipDepth) * faceCount;
892 }
893 if (padded && j + 1 != frameCount && length > 512) {
894 length += sourcepp::math::paddingForAlignment(512, length);
895 }
896 }
897 return length;
898}
899
917[[nodiscard]] constexpr bool getDataPosition(uint32_t& offset, uint32_t& length, ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice = 0, uint16_t depth = 1) {
918 offset = 0;
919 length = 0;
920 for (int i = mipCount - 1; i >= 0; i--) {
921 const auto [mipWidth, mipHeight, mipDepth] = ImageDimensions::getMipDims(i, width, height, depth);
922 for (int j = 0; j < frameCount; j++) {
923 for (int k = 0; k < faceCount; k++) {
924 for (int l = 0; l < mipDepth; l++) {
925 const auto imageSize = ImageFormatDetails::getDataLength(format, mipWidth, mipHeight);
926 if (i == mip && j == frame && k == face && l == slice) {
927 length = imageSize;
928 return true;
929 }
930 offset += imageSize;
931 }
932 }
933 }
934 }
935 return false;
936}
937
959[[nodiscard]] constexpr bool getDataPositionXbox(uint32_t& offset, uint32_t& length, bool padded, ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice = 0, uint16_t depth = 1) {
960 offset = 0;
961 length = 0;
962 for (int j = 0; j < frameCount; j++) {
963 for (int k = 0; k < faceCount; k++) {
964 for (int i = 0; i < mipCount; i++) {
965 const auto [mipWidth, mipHeight, mipDepth] = ImageDimensions::getMipDims(i, width, height, depth);
966 for (int l = 0; l < mipDepth; l++) {
967 const auto imageSize = ImageFormatDetails::getDataLength(format, mipWidth, mipHeight);
968 if (i == mip && j == frame && k == face && l == slice) {
969 length = imageSize;
970 return true;
971 }
972 offset += imageSize;
973 }
974 }
975 }
976 if (padded && j + 1 != frameCount && offset > 512) {
977 offset += sourcepp::math::paddingForAlignment(512, offset);
978 }
979 }
980 return false;
981}
982
983} // namespace ImageFormatDetails
984
985} // namespace vtfpp
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
Definition Math.h:61
constexpr uint8_t getMaximumMipCount(uint16_t width, uint16_t height, uint16_t depth=1)
Calculate the largest mip count a texture with the given width, height, and depth can contain.
constexpr std::pair< uint16_t, uint16_t > getMipDims(uint8_t mip, uint16_t width, uint16_t height, bool addCompressedFormatPadding=false)
Get the width and height at a given mip level.
constexpr uint16_t getMipDim(uint8_t mip, uint16_t dim, bool addCompressedFormatPadding=false)
Get the dimension at a given mip level.
constexpr uint32_t getDataLength(ImageFormat format, uint16_t width, uint16_t height, uint16_t depth=1)
Calculate the amount of data required to store a texture with the given format and dimensions.
constexpr bool getDataPosition(uint32_t &offset, uint32_t &length, ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice=0, uint16_t depth=1)
Find the position of a specific mip, frame, face, and slice within a texture.
constexpr int8_t decompressedGreen(ImageFormat format)
Get the number of bits of precision of the green channel in the given format regardless of compressio...
constexpr int8_t alpha(ImageFormat format)
Get the number of bits of precision of the alpha channel in the given format.
constexpr bool large(ImageFormat format)
Check if the given format is representable by RGBA8888 without losing data.
constexpr int8_t green(ImageFormat format)
Get the number of bits of precision of the green channel in the given format.
constexpr int8_t decompressedBlue(ImageFormat format)
Get the number of bits of precision of the blue channel in the given format regardless of compression...
constexpr ImageFormat containerFormat(ImageFormat format)
Find a container format for the given format, a format that is more commonly understood within this l...
constexpr bool transparent(ImageFormat format)
Check if the given format can store transparency.
constexpr uint32_t getDataLengthXBOX(bool padded, ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth=1)
Calculate the amount of data required to store an XBOX platform texture with the given format,...
constexpr int8_t decompressedRed(ImageFormat format)
Get the number of bits of precision of the red channel in the given format regardless of compression.
constexpr int8_t decompressedAlpha(ImageFormat format)
Get the number of bits of precision of the alpha channel in the given format regardless of compressio...
constexpr int8_t red(ImageFormat format)
Get the number of bits of precision of the red channel in the given format.
constexpr bool decimal(ImageFormat format)
Checks if the given format stores floating points in its channels.
constexpr uint8_t bpp(ImageFormat format)
Find the bits per pixel of the given format.
constexpr bool getDataPositionXbox(uint32_t &offset, uint32_t &length, bool padded, ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice=0, uint16_t depth=1)
Find the position of a specific mip, frame, face, and slice within an XBOX platform texture.
constexpr bool console(ImageFormat format)
Check if the given format is exclusively used on console platforms.
constexpr bool opaque(ImageFormat format)
Check if the given format cannot store transparency.
constexpr bool compressed(ImageFormat format)
Check if the given format is a compressed format (DXT1, DXT3, DXT5, ATI1N, ATI2N, BC7,...
constexpr int8_t blue(ImageFormat format)
Get the number of bits of precision of the blue channel in the given format.
Definition HOT.h:11