16#include <unordered_map>
18#ifdef SOURCEPP_BUILD_WITH_TBB
22#ifdef SOURCEPP_BUILD_WITH_THREADS
26#include <compressonator.h>
28#ifdef VTFPP_SUPPORT_QOI
29#define QOI_IMPLEMENTATION
37#define STB_IMAGE_IMPLEMENTATION
38#define STB_IMAGE_STATIC
39#define STBI_NO_FAILURE_STRINGS
43#define STB_IMAGE_RESIZE_IMPLEMENTATION
44#define STB_IMAGE_RESIZE_STATIC
45#include <stb_image_resize2.h>
47#define STB_IMAGE_WRITE_IMPLEMENTATION
48#define STB_IMAGE_WRITE_STATIC
49#define STBI_WRITE_NO_STDIO
50#include <stb_image_write.h>
52#ifdef VTFPP_SUPPORT_EXR
53#define TINYEXR_IMPLEMENTATION 1
54#ifdef SOURCEPP_BUILD_WITH_THREADS
55#define TINYEXR_USE_THREAD 1
57#define TINYEXR_USE_THREAD 0
62#ifdef VTFPP_SUPPORT_WEBP
63#include <webp/decode.h>
64#include <webp/encode.h>
72[[nodiscard]]
constexpr CMP_FORMAT imageFormatToCompressonatorFormat(
ImageFormat format) {
77 return CMP_FORMAT_RGBA_8888;
79 return CMP_FORMAT_ABGR_8888;
81 return CMP_FORMAT_RGB_888;
83 return CMP_FORMAT_BGR_888;
86 return CMP_FORMAT_R_8;
88 return CMP_FORMAT_ARGB_8888;
90 return CMP_FORMAT_BGRA_8888;
93 return CMP_FORMAT_DXT1;
95 return CMP_FORMAT_DXT3;
97 return CMP_FORMAT_DXT5;
99 return CMP_FORMAT_RG_8;
101 return CMP_FORMAT_R_16F;
103 return CMP_FORMAT_RG_16F;
105 return CMP_FORMAT_RGBA_16F;
107 return CMP_FORMAT_RGBA_16;
109 return CMP_FORMAT_R_32F;
111 return CMP_FORMAT_RG_32F;
113 return CMP_FORMAT_RGB_32F;
115 return CMP_FORMAT_RGBA_32F;
117 return CMP_FORMAT_ATI2N;
119 return CMP_FORMAT_ATI1N;
121 return CMP_FORMAT_RGBA_1010102;
123 return CMP_FORMAT_R_8;
125 return CMP_FORMAT_BC7;
127 return CMP_FORMAT_BC6H_SF;
154 return CMP_FORMAT_Unknown;
156 return CMP_FORMAT_Unknown;
159[[nodiscard]]
constexpr int imageFormatToSTBIRPixelLayout(
ImageFormat format) {
180 return STBIR_1CHANNEL;
188 return STBIR_2CHANNEL;
231[[nodiscard]]
constexpr int imageFormatToSTBIRDataType(
ImageFormat format,
bool srgb =
false) {
252 return srgb ? STBIR_TYPE_UINT8_SRGB : STBIR_TYPE_UINT8;
256 return STBIR_TYPE_HALF_FLOAT;
258 return STBIR_TYPE_UINT16;
263 return STBIR_TYPE_FLOAT;
298 if (imageData.empty()) {
302 uint16_t unpaddedWidth = width, unpaddedHeight = height;
303 std::vector<std::byte> paddedImageData;
305 uint16_t paddingWidth = (4 - (width % 4)) % 4, paddingHeight = (4 - (height % 4)) % 4;
308 imageData = paddedImageData;
310 width += paddingWidth;
311 height += paddingHeight;
314 CMP_Texture srcTexture{};
315 srcTexture.dwSize =
sizeof(srcTexture);
316 srcTexture.dwWidth = width;
317 srcTexture.dwHeight = height;
319 srcTexture.format = ::imageFormatToCompressonatorFormat(oldFormat);
320 srcTexture.dwDataSize = imageData.size();
322 srcTexture.pData =
const_cast<CMP_BYTE*
>(
reinterpret_cast<const CMP_BYTE*
>(imageData.data()));
324 CMP_Texture destTexture{};
325 destTexture.dwSize =
sizeof(destTexture);
326 destTexture.dwWidth = width;
327 destTexture.dwHeight = height;
329 destTexture.format = ::imageFormatToCompressonatorFormat(newFormat);
330 destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture);
332 std::vector<std::byte> destData;
333 destData.resize(destTexture.dwDataSize);
334 destTexture.pData =
reinterpret_cast<CMP_BYTE*
>(destData.data());
336 CMP_CompressOptions options{};
337 options.dwSize =
sizeof(options);
338 if (quality >= 0.f) {
339 options.fquality = std::min(quality, 1.f);
341 options.fquality = 0.1f;
343 options.fquality = 1.f;
346 if (options.bDXT1UseAlpha) {
347 options.nAlphaThreshold = 128;
350 if (CMP_ConvertTexture(&srcTexture, &destTexture, &options,
nullptr) != CMP_OK) {
359#ifdef SOURCEPP_BUILD_WITH_TBB
360 std::execution::par_unseq,
363 const auto nX = static_cast<float>(pixel.r) / 255.f * 2.f - 1.f;
364 const auto nY = static_cast<float>(pixel.g) / 255.f * 2.f - 1.f;
365 return {pixel.r, pixel.g, static_cast<uint8_t>(std::clamp(1.f - (nX * nX) - (nY * nY), 0.f, 1.f) * 255.f), pixel.a};
368 if (unpaddedWidth % 4 != 0 || unpaddedHeight % 4 != 0) {
375[[nodiscard]] std::vector<std::byte> convertImageDataToRGBA8888(std::span<const std::byte> imageData,
ImageFormat format) {
378 if (imageData.empty()) {
383 return {imageData.begin(), imageData.end()};
386 std::vector<std::byte> newData;
390 #define VTFPP_REMAP_TO_8(value, shift) math::remap<uint8_t>((value), (1 << (shift)) - 1, (1 << 8) - 1)
392 #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \
393 std::span<const ImagePixel::InputType> imageDataSpan{reinterpret_cast<const ImagePixel::InputType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \
394 std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA8888 { \
395 return {(r), (g), (b), (a)}; \
397#ifdef SOURCEPP_BUILD_WITH_TBB
398 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::par_unseq)
400 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a)
402 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \
403 case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break
442 #undef VTFPP_CASE_CONVERT_AND_BREAK
444 #undef VTFPP_CONVERT_DETAIL
445 #undef VTFPP_REMAP_TO_8
450[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA8888(std::span<const std::byte> imageData,
ImageFormat format) {
453 if (imageData.empty()) {
458 return {imageData.begin(), imageData.end()};
462 std::vector<std::byte> newData;
465 #define VTFPP_REMAP_FROM_8(value, shift) math::remap<uint8_t>((value), (1 << 8) - 1, (1 << (shift)) - 1)
467#ifdef SOURCEPP_BUILD_WITH_TBB
468 #define VTFPP_CONVERT(InputType, ...) \
469 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
470 std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \
471 return __VA_ARGS__; \
474 #define VTFPP_CONVERT(InputType, ...) \
475 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
476 std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \
477 return __VA_ARGS__; \
480 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \
481 case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break
520 #undef VTFPP_CASE_CONVERT_AND_BREAK
522 #undef VTFPP_REMAP_FROM_8
527[[nodiscard]] std::vector<std::byte> convertImageDataToRGBA16161616(std::span<const std::byte> imageData,
ImageFormat format) {
530 if (imageData.empty()) {
535 return {imageData.begin(), imageData.end()};
538 std::vector<std::byte> newData;
542 #define VTFPP_REMAP_TO_16(value, shift) math::remap<uint16_t>((value), (1 << (shift)) - 1, (1 << 16) - 1)
544 #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \
545 std::span<const ImagePixel::InputType> imageDataSpan{reinterpret_cast<const ImagePixel::InputType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \
546 std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA16161616 { \
547 return { static_cast<uint16_t>(r), static_cast<uint16_t>(g), static_cast<uint16_t>(b), static_cast<uint16_t>(a) }; \
549#ifdef SOURCEPP_BUILD_WITH_TBB
550 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::par_unseq)
552 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a)
554 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \
555 case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break
557 #define VTFPP_CONVERT_REMAP(InputType, r, g, b, a) \
559 if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) > 1) { \
560 VTFPP_CONVERT(InputType, \
561 VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \
562 VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \
563 VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \
564 VTFPP_REMAP_TO_16((a), ImageFormatDetails::alpha(ImageFormat::InputType))); \
565 } else if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) == 1) { \
566 VTFPP_CONVERT(InputType, \
567 VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \
568 VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \
569 VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \
572 VTFPP_CONVERT(InputType, \
573 VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \
574 VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \
575 VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \
579 #define VTFPP_CASE_CONVERT_REMAP_AND_BREAK(InputType, r, g, b, a) \
580 case InputType: { VTFPP_CONVERT_REMAP(InputType, r, g, b, a); } \
591 #undef VTFPP_CASE_CONVERT_REMAP_AND_BREAK
592 #undef VTFPP_CONVERT_REMAP
593 #undef VTFPP_CASE_CONVERT_AND_BREAK
595 #undef VTFPP_CONVERT_DETAIL
596 #undef VTFPP_REMAP_TO_16
601[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA16161616(std::span<const std::byte> imageData,
ImageFormat format) {
604 if (imageData.empty()) {
609 return {imageData.begin(), imageData.end()};
613 std::vector<std::byte> newData;
616 #define VTFPP_REMAP_FROM_16(value, shift) static_cast<uint8_t>(math::remap<uint16_t>((value), (1 << 16) - 1, (1 << (shift)) - 1))
618#ifdef SOURCEPP_BUILD_WITH_TBB
619 #define VTFPP_CONVERT(InputType, ...) \
620 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
621 std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::InputType { \
622 return __VA_ARGS__; \
625 #define VTFPP_CONVERT(InputType, ...) \
626 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
627 std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::InputType { \
628 return __VA_ARGS__; \
631 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \
632 case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break
642 #undef VTFPP_CASE_CONVERT_AND_BREAK
644 #undef VTFPP_REMAP_FROM_16
649[[nodiscard]] std::vector<std::byte> convertImageDataToRGBA32323232F(std::span<const std::byte> imageData,
ImageFormat format) {
650 if (imageData.empty()) {
655 return {imageData.begin(), imageData.end()};
658 std::vector<std::byte> newData;
662 #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \
663 std::span<const ImagePixel::InputType> imageDataSpan{reinterpret_cast<const ImagePixel::InputType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \
664 std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA32323232F { return {(r), (g), (b), (a)}; })
665#ifdef SOURCEPP_BUILD_WITH_TBB
666 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::par_unseq)
668 #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a)
670 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \
671 case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break
684 #undef VTFPP_CASE_CONVERT_AND_BREAK
686 #undef VTFPP_CONVERT_DETAIL
691[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA32323232F(std::span<const std::byte> imageData,
ImageFormat format) {
694 if (imageData.empty()) {
699 return {imageData.begin(), imageData.end()};
703 std::vector<std::byte> newData;
706#ifdef SOURCEPP_BUILD_WITH_TBB
707 #define VTFPP_CONVERT(InputType, ...) \
708 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
709 std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::InputType { \
710 return __VA_ARGS__; \
713 #define VTFPP_CONVERT(InputType, ...) \
714 std::span<ImagePixel::InputType> newDataSpan{reinterpret_cast<ImagePixel::InputType*>(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \
715 std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::InputType { \
716 return __VA_ARGS__; \
719 #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \
720 case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break
733 #undef VTFPP_CASE_CONVERT_AND_BREAK
739[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA8888ToRGBA32323232F(std::span<const std::byte> imageData) {
740 if (imageData.empty()) {
744 std::vector<std::byte> newData;
750#ifdef SOURCEPP_BUILD_WITH_TBB
751 std::execution::par_unseq,
755 static_cast<float>(pixel.r) / static_cast<float>((1 << 8) - 1),
756 static_cast<float>(pixel.g) / static_cast<float>((1 << 8) - 1),
757 static_cast<float>(pixel.b) / static_cast<float>((1 << 8) - 1),
758 static_cast<float>(pixel.a) / static_cast<float>((1 << 8) - 1),
765[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA32323232FToRGBA8888(std::span<const std::byte> imageData) {
766 if (imageData.empty()) {
770 std::vector<std::byte> newData;
776#ifdef SOURCEPP_BUILD_WITH_TBB
777 std::execution::par_unseq,
781 static_cast<uint8_t>(std::clamp(pixel.r, 0.f, 1.f) * ((1 << 8) - 1)),
782 static_cast<uint8_t>(std::clamp(pixel.g, 0.f, 1.f) * ((1 << 8) - 1)),
783 static_cast<uint8_t>(std::clamp(pixel.b, 0.f, 1.f) * ((1 << 8) - 1)),
784 static_cast<uint8_t>(std::clamp(pixel.a, 0.f, 1.f) * ((1 << 8) - 1)),
791[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA8888ToRGBA16161616(std::span<const std::byte> imageData) {
792 if (imageData.empty()) {
796 std::vector<std::byte> newData;
802#ifdef SOURCEPP_BUILD_WITH_TBB
803 std::execution::par_unseq,
807 math::remap<uint16_t>(pixel.r, (1 << 8) - 1, (1 << 16) - 1),
808 math::remap<uint16_t>(pixel.g, (1 << 8) - 1, (1 << 16) - 1),
809 math::remap<uint16_t>(pixel.b, (1 << 8) - 1, (1 << 16) - 1),
810 math::remap<uint16_t>(pixel.a, (1 << 8) - 1, (1 << 16) - 1),
817[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA16161616ToRGBA8888(std::span<const std::byte> imageData) {
818 if (imageData.empty()) {
822 std::vector<std::byte> newData;
828#ifdef SOURCEPP_BUILD_WITH_TBB
829 std::execution::par_unseq,
833 static_cast<uint8_t>(math::remap<uint16_t>(pixel.r, (1 << 16) - 1, (1 << 8) - 1)),
834 static_cast<uint8_t>(math::remap<uint16_t>(pixel.g, (1 << 16) - 1, (1 << 8) - 1)),
835 static_cast<uint8_t>(math::remap<uint16_t>(pixel.b, (1 << 16) - 1, (1 << 8) - 1)),
836 static_cast<uint8_t>(math::remap<uint16_t>(pixel.a, (1 << 16) - 1, (1 << 8) - 1)),
843[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA32323232FToRGBA16161616(std::span<const std::byte> imageData) {
844 if (imageData.empty()) {
848 std::vector<std::byte> newData;
854#ifdef SOURCEPP_BUILD_WITH_TBB
855 std::execution::par_unseq,
859 static_cast<uint16_t>(std::clamp(pixel.r, 0.f, 1.f) * ((1 << 16) - 1)),
860 static_cast<uint16_t>(std::clamp(pixel.g, 0.f, 1.f) * ((1 << 16) - 1)),
861 static_cast<uint16_t>(std::clamp(pixel.b, 0.f, 1.f) * ((1 << 16) - 1)),
862 static_cast<uint16_t>(std::clamp(pixel.a, 0.f, 1.f) * ((1 << 16) - 1)),
869[[nodiscard]] std::vector<std::byte> convertImageDataFromRGBA16161616ToRGBA32323232F(std::span<const std::byte> imageData) {
870 if (imageData.empty()) {
874 std::vector<std::byte> newData;
880#ifdef SOURCEPP_BUILD_WITH_TBB
881 std::execution::par_unseq,
885 static_cast<float>(pixel.r) / static_cast<float>((1 << 16) - 1),
886 static_cast<float>(pixel.g) / static_cast<float>((1 << 16) - 1),
887 static_cast<float>(pixel.b) / static_cast<float>((1 << 16) - 1),
888 static_cast<float>(pixel.a) / static_cast<float>((1 << 16) - 1),
902 if (oldFormat == newFormat) {
903 return {imageData.begin(), imageData.end()};
906 std::vector<std::byte> newData;
910 newData = ::convertImageDataUsingCompressonator(imageData, oldFormat, intermediaryOldFormat, width, height, quality);
912 switch (intermediaryOldFormat) {
920 if (intermediaryOldFormat == newFormat) {
925 if (intermediaryOldFormat != intermediaryNewFormat) {
928 newData = ::convertImageDataFromRGBA8888ToRGBA16161616(newData);
930 newData = ::convertImageDataFromRGBA8888ToRGBA32323232F(newData);
936 newData = ::convertImageDataFromRGBA16161616ToRGBA8888(newData);
938 newData = ::convertImageDataFromRGBA16161616ToRGBA32323232F(newData);
944 newData = ::convertImageDataFromRGBA32323232FToRGBA8888(newData);
946 newData = ::convertImageDataFromRGBA32323232FToRGBA16161616(newData);
955 if (intermediaryNewFormat == newFormat) {
960 newData = ::convertImageDataUsingCompressonator(newData, intermediaryNewFormat, newFormat, width, height, quality);
962 switch (intermediaryNewFormat) {
978 if (oldFormat == newFormat) {
979 return {imageData.begin(), imageData.end()};
983 for(
int mip = mipCount - 1; mip >= 0; mip--) {
985 for (
int frame = 0; frame < frameCount; frame++) {
986 for (
int face = 0; face < faceCount; face++) {
987 for (
int slice = 0; slice < mipDepth; slice++) {
988 if (uint32_t oldOffset, oldLength;
ImageFormatDetails::getDataPosition(oldOffset, oldLength, oldFormat, mip, mipCount, frame, frameCount, face, faceCount, width, height, slice, depth)) {
990 if (uint32_t newOffset, newLength;
ImageFormatDetails::getDataPosition(newOffset, newLength, newFormat, mip, mipCount, frame, frameCount, face, faceCount, width, height, slice, depth) && newLength == convertedImageData.size()) {
991 std::memcpy(out.data() + newOffset, convertedImageData.data(), newLength);
1007 resolution = height;
1010 std::span imageDataRGBA32323232F{
reinterpret_cast<const float*
>(imageData.data()),
reinterpret_cast<const float*
>(imageData.data() + imageData.size())};
1012 std::vector<std::byte> possiblyConvertedDataOrEmptyDontUseMeDirectly;
1015 imageDataRGBA32323232F = {
reinterpret_cast<const float*
>(possiblyConvertedDataOrEmptyDontUseMeDirectly.data()),
reinterpret_cast<const float*
>(possiblyConvertedDataOrEmptyDontUseMeDirectly.data() + possiblyConvertedDataOrEmptyDontUseMeDirectly.size())};
1020 static constexpr std::array<std::array<math::Vec3f, 3>, 6> startRightUp = {{
1021 {{{ 1.0f, -1.0f, -1.0f}, { 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}}},
1022 {{{ 1.0f, 1.0f, 1.0f}, { 0.0f,-1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}}},
1023 {{{ 1.0f, 1.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}, { 0.0f,-1.0f, 0.0f}}},
1024 {{{-1.0f, -1.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}, { 0.0f, 1.0f, 0.0f}}},
1025 {{{ 1.0f, -1.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}},
1026 {{{ 1.0f, 1.0f, -1.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}},
1029 std::array<std::vector<std::byte>, 6> faceData;
1031#ifdef SOURCEPP_BUILD_WITH_THREADS
1032 const auto faceExtraction = [&](
int i) {
1034 for (
int i = 0; i < faceData.size(); i++) {
1036 const auto start = startRightUp[i][0];
1037 const auto right = startRightUp[i][1];
1038 const auto up = startRightUp[i][2];
1041 std::span face{
reinterpret_cast<float*
>(faceData[i].data()),
reinterpret_cast<float*
>(faceData[i].data() + faceData[i].size())};
1043 for (
int row = 0; row < resolution; row++) {
1044 for (
int col = 0; col < resolution; col++) {
1045 math::Vec3f pixelDirection3d{
1046 start[0] + (
static_cast<float>(col) * 2.f + 0.5f) /
static_cast<float>(resolution) * right[0] + (
static_cast<float>(row) * 2.f + 0.5f) /
static_cast<float>(resolution) * up[0],
1047 start[1] + (
static_cast<float>(col) * 2.f + 0.5f) /
static_cast<float>(resolution) * right[1] + (
static_cast<float>(row) * 2.f + 0.5f) /
static_cast<float>(resolution) * up[1],
1048 start[2] + (
static_cast<float>(col) * 2.f + 0.5f) /
static_cast<float>(resolution) * right[2] + (
static_cast<float>(row) * 2.f + 0.5f) /
static_cast<float>(resolution) * up[2],
1050 const float azimuth = std::atan2(pixelDirection3d[0], -pixelDirection3d[2]) +
math::pi_f32;
1051 const float elevation = std::atan(pixelDirection3d[1] / std::sqrt(pixelDirection3d[0] * pixelDirection3d[0] + pixelDirection3d[2] * pixelDirection3d[2])) +
math::pi_f32 / 2.f;
1052 const float colHdri = (azimuth /
math::pi_f32 / 2.f) *
static_cast<float>(width);
1053 const float rowHdri = (elevation /
math::pi_f32) *
static_cast<float>(height);
1055 const int colNearest = std::clamp(
static_cast<int>(colHdri), 0, width - 1);
1056 const int rowNearest = std::clamp(
static_cast<int>(rowHdri), 0, height - 1);
1057 face[col * 4 + resolution * row * 4 + 0] = imageDataRGBA32323232F[colNearest * 4 + width * rowNearest * 4 + 0];
1058 face[col * 4 + resolution * row * 4 + 1] = imageDataRGBA32323232F[colNearest * 4 + width * rowNearest * 4 + 1];
1059 face[col * 4 + resolution * row * 4 + 2] = imageDataRGBA32323232F[colNearest * 4 + width * rowNearest * 4 + 2];
1060 face[col * 4 + resolution * row * 4 + 3] = imageDataRGBA32323232F[colNearest * 4 + width * rowNearest * 4 + 3];
1062 float intCol, intRow;
1064 float factorCol = std::modf(colHdri - 0.5f, &intCol);
1065 float factorRow = std::modf(rowHdri - 0.5f, &intRow);
1066 const int low_idx_row =
static_cast<int>(intRow);
1067 const int low_idx_column =
static_cast<int>(intCol);
1068 int high_idx_column;
1069 if (factorCol < 0.f) {
1072 high_idx_column = width - 1;
1073 }
else if (low_idx_column == width - 1) {
1075 high_idx_column = 0;
1077 high_idx_column = low_idx_column + 1;
1080 if (factorRow < 0.f || low_idx_row == height - 1) {
1081 high_idx_row = low_idx_row;
1084 high_idx_row = low_idx_row + 1;
1086 factorCol = std::abs(factorCol);
1087 factorRow = std::abs(factorRow);
1088 const float f1 = (1 - factorRow) * (1 - factorCol);
1089 const float f2 = factorRow * (1 - factorCol);
1090 const float f3 = (1 - factorRow) * factorCol;
1091 const float f4 = factorRow * factorCol;
1092 for (
int j = 0; j < 4; j++) {
1093 face[col * 4 + resolution * row * 4 + j] =
1094 imageDataRGBA32323232F[low_idx_column * 4 + width * low_idx_row * 4 + j] * f1 +
1095 imageDataRGBA32323232F[low_idx_column * 4 + width * high_idx_row * 4 + j] * f2 +
1096 imageDataRGBA32323232F[high_idx_column * 4 + width * low_idx_row * 4 + j] * f3 +
1097 imageDataRGBA32323232F[high_idx_column * 4 + width * high_idx_row * 4 + j] * f4;
1106#ifdef SOURCEPP_BUILD_WITH_THREADS
1108 std::array faceFutures{
1109 std::async(std::launch::async, faceExtraction, 0),
1110 std::async(std::launch::async, faceExtraction, 1),
1111 std::async(std::launch::async, faceExtraction, 2),
1112 std::async(std::launch::async, faceExtraction, 3),
1113 std::async(std::launch::async, faceExtraction, 4),
1114 std::async(std::launch::async, faceExtraction, 5),
1116 for (
auto& future : faceFutures) {
1126#ifdef VTFPP_SUPPORT_EXR
1137 std::vector<std::byte> out;
1138 auto stbWriteFunc = [](
void* out_,
void* data,
int size) {
1139 std::copy_n(
static_cast<std::byte*
>(data), size, std::back_inserter(*
static_cast<std::vector<std::byte>*
>(out_)));
1145 switch (fileFormat) {
1148 stbi_write_png_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), imageData.data(), 0);
1150 stbi_write_png_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), imageData.data(), 0);
1153 stbi_write_png_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), rgb.data(), 0);
1156 stbi_write_png_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), rgba.data(), 0);
1162 stbi_write_jpg_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), imageData.data(), 95);
1165 stbi_write_jpg_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), rgb.data(), 95);
1171 stbi_write_bmp_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), imageData.data());
1173 stbi_write_bmp_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), imageData.data());
1176 stbi_write_bmp_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), rgb.data());
1179 stbi_write_bmp_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), rgba.data());
1185 stbi_write_tga_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), imageData.data());
1187 stbi_write_tga_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), imageData.data());
1190 stbi_write_tga_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGB888), rgb.data());
1193 stbi_write_tga_to_func(stbWriteFunc, &out, width, height,
sizeof(
ImagePixel::RGBA8888), rgba.data());
1197#ifdef VTFPP_SUPPORT_WEBP
1198 case FileFormat::WEBP: {
1200 WebPConfigInit(&config);
1201 WebPConfigPreset(&config, WEBP_PRESET_DRAWING, 75.f);
1202 WebPConfigLosslessPreset(&config, 6);
1205 if (!WebPPictureInit(&pic)) {
1209 pic.height = height;
1210 if (!WebPPictureAlloc(&pic)) {
1215 WebPPictureImportRGB(&pic,
reinterpret_cast<const uint8_t*
>(imageData.data()),
static_cast<int>(width *
sizeof(
ImagePixel::RGB888)));
1217 WebPPictureImportRGBA(&pic,
reinterpret_cast<const uint8_t*
>(imageData.data()),
static_cast<int>(width *
sizeof(
ImagePixel::RGBA8888)));
1220 WebPPictureImportRGB(&pic,
reinterpret_cast<const uint8_t*
>(rgb.data()),
static_cast<int>(width *
sizeof(
ImagePixel::RGB888)));
1223 WebPPictureImportRGBA(&pic,
reinterpret_cast<const uint8_t*
>(rgba.data()),
static_cast<int>(width *
sizeof(
ImagePixel::RGBA8888)));
1226 WebPMemoryWriter writer;
1227 WebPMemoryWriterInit(&writer);
1228 pic.writer = &WebPMemoryWrite;
1229 pic.custom_ptr = &writer;
1231 int ok = WebPEncode(&config, &pic);
1232 WebPPictureFree(&pic);
1234 WebPMemoryWriterClear(&writer);
1238 if (writer.mem && writer.size) {
1239 out.resize(writer.size);
1240 std::memcpy(out.data(), writer.mem, writer.size);
1242 WebPMemoryWriterClear(&writer);
1246#ifdef VTFPP_SUPPORT_QOI
1247 case FileFormat::QOI: {
1248 qoi_desc descriptor{
1252 .colorspace = QOI_SRGB,
1254 void* qoiData =
nullptr;
1257 descriptor.channels = 3;
1258 qoiData = qoi_encode(imageData.data(), &descriptor, &qoiDataLen);
1260 descriptor.channels = 4;
1261 qoiData = qoi_encode(imageData.data(), &descriptor, &qoiDataLen);
1264 descriptor.channels = 3;
1265 qoiData = qoi_encode(rgb.data(), &descriptor, &qoiDataLen);
1268 descriptor.channels = 4;
1269 qoiData = qoi_encode(rgba.data(), &descriptor, &qoiDataLen);
1271 if (qoiData && qoiDataLen) {
1272 out.resize(qoiDataLen);
1273 std::memcpy(out.data(), qoiData, qoiDataLen);
1288#ifdef VTFPP_SUPPORT_EXR
1289 case FileFormat::EXR: {
1291 InitEXRHeader(&header);
1293 std::vector<std::byte> rawData;
1303 rawData = {imageData.begin(), imageData.end()};
1307 header.channels =
static_cast<EXRChannelInfo*
>(std::malloc(header.num_channels *
sizeof(EXRChannelInfo)));
1308 header.pixel_types =
static_cast<int*
>(malloc(header.num_channels *
sizeof(
int)));
1309 header.requested_pixel_types =
static_cast<int*
>(malloc(header.num_channels *
sizeof(
int)));
1311 switch (header.num_channels) {
1313 header.channels[0].name[0] =
'A';
1314 header.channels[1].name[0] =
'B';
1315 header.channels[2].name[0] =
'G';
1316 header.channels[3].name[0] =
'R';
1319 header.channels[0].name[0] =
'B';
1320 header.channels[1].name[0] =
'G';
1321 header.channels[2].name[0] =
'R';
1324 header.channels[0].name[0] =
'G';
1325 header.channels[1].name[0] =
'R';
1328 header.channels[0].name[0] =
'R';
1331 FreeEXRHeader(&header);
1334 for (
int i = 0; i < header.num_channels; i++) {
1335 header.channels[i].name[1] =
'\0';
1338 int pixelType = (
ImageFormatDetails::red(format) / 8) ==
sizeof(half) ? TINYEXR_PIXELTYPE_HALF : TINYEXR_PIXELTYPE_FLOAT;
1339 for (
int i = 0; i < header.num_channels; i++) {
1340 header.pixel_types[i] = pixelType;
1341 header.requested_pixel_types[i] = pixelType;
1344 std::vector<std::vector<std::byte>> images(header.num_channels);
1345 std::vector<void*> imagePtrs(header.num_channels);
1346 switch (header.num_channels) {
1348 if (pixelType == TINYEXR_PIXELTYPE_HALF) {
1361 if (pixelType == TINYEXR_PIXELTYPE_HALF) {
1363 FreeEXRHeader(&header);
1371 if (pixelType == TINYEXR_PIXELTYPE_HALF) {
1380 images[0] = rawData;
1383 FreeEXRHeader(&header);
1386 for (
int i = 0; i < header.num_channels; i++) {
1387 imagePtrs[i] = images[i].data();
1391 InitEXRImage(&image);
1392 image.width = width;
1393 image.height = height;
1394 image.images =
reinterpret_cast<unsigned char**
>(imagePtrs.data());
1395 image.num_channels = header.num_channels;
1397 unsigned char* data =
nullptr;
1398 const char* err =
nullptr;
1400 size_t size = SaveEXRImageToMemory(&image, &header, &data, &err);
1402 FreeEXRErrorMessage(err);
1403 FreeEXRHeader(&header);
1407 out = {
reinterpret_cast<std::byte*
>(data),
reinterpret_cast<std::byte*
>(data) + size};
1411 FreeEXRHeader(&header);
1424using stb_ptr = std::unique_ptr<T, void(*)(
void*)>;
1429 stbi_convert_iphone_png_to_rgb(
true);
1437#ifdef VTFPP_SUPPORT_EXR
1439 if (EXRVersion version; ParseEXRVersionFromMemory(&version,
reinterpret_cast<const unsigned char*
>(fileData.data()), fileData.size()) == TINYEXR_SUCCESS) {
1440 if (version.multipart || version.non_image) {
1445 InitEXRHeader(&header);
1446 const char* err =
nullptr;
1447 if (ParseEXRHeaderFromMemory(&header, &version,
reinterpret_cast<const unsigned char*
>(fileData.data()), fileData.size(), &err) != TINYEXR_SUCCESS) {
1448 FreeEXRErrorMessage(err);
1453 if (header.num_channels < 1) {
1454 FreeEXRHeader(&header);
1459 std::unordered_map<std::string_view, int> channelIndices{{
"R", -1}, {
"G", -1}, {
"B", -1}, {
"A", -1}, {
"Y", -1}};
1463 auto channelType = header.pixel_types[0];
1464 for (
int i = 1; i < header.num_channels; i++) {
1466 if (header.pixel_types[i] > channelType && channelIndices.contains(header.channels[i].name)) {
1467 channelType = header.pixel_types[i];
1471 if (channelType == TINYEXR_PIXELTYPE_UINT) {
1472 channelType = TINYEXR_PIXELTYPE_HALF;
1476 for (
int i = 0; i < header.num_channels; i++) {
1477 if (channelIndices.contains(header.channels[i].name)) {
1478 channelIndices[header.channels[i].name] = i;
1481 if (channelIndices[
"Y"] >= 0) {
1482 if (channelIndices[
"A"] >= 0) {
1485 if (channelType == TINYEXR_PIXELTYPE_HALF) {
1487 channelType = TINYEXR_PIXELTYPE_FLOAT;
1491 channelIndices[
"R"] = channelIndices[
"Y"];
1492 channelIndices[
"G"] = channelIndices[
"Y"];
1493 channelIndices[
"B"] = channelIndices[
"Y"];
1494 }
else if (channelIndices[
"A"] >= 0) {
1496 }
else if (channelIndices[
"B"] >= 0) {
1497 if (channelType == TINYEXR_PIXELTYPE_HALF) {
1499 channelType = TINYEXR_PIXELTYPE_FLOAT;
1502 }
else if (channelIndices[
"G"] >= 0) {
1504 }
else if (channelIndices[
"R"] >= 0) {
1507 FreeEXRHeader(&header);
1512 for (
int i = 0; i < header.num_channels; i++) {
1513 if (header.pixel_types[i] != channelType && channelIndices.contains(header.channels[i].name)) {
1514 header.requested_pixel_types[i] = channelType;
1519 InitEXRImage(&image);
1520 if (LoadEXRImageFromMemory(&image, &header,
reinterpret_cast<const unsigned char*
>(fileData.data()), fileData.size(), &err) != TINYEXR_SUCCESS) {
1521 FreeEXRErrorMessage(err);
1522 FreeEXRHeader(&header);
1526 width = image.width;
1527 height = image.height;
1531 const auto populateBuffer = [
1539 r=channelIndices[
"R"],
1540 g=channelIndices[
"G"],
1541 b=channelIndices[
"B"],
1542 a=channelIndices[
"A"],
1546 const auto channelCount = hasRed + hasGreen + hasBlue + hasAlpha;
1548 std::span out{
reinterpret_cast<C*
>(combinedChannels.data()), combinedChannels.size() /
sizeof(C)};
1550 for (
int t = 0; t < image.num_tiles; t++) {
1551 auto** src =
reinterpret_cast<C**
>(image.tiles[t].images);
1552 for (
int j = 0; j < header.tile_size_y; j++) {
1553 for (
int i = 0; i < header.tile_size_x; i++) {
1554 const auto ii =
static_cast<uint64_t
>(image.tiles[t].offset_x) * header.tile_size_x + i;
1555 const auto jj =
static_cast<uint64_t
>(image.tiles[t].offset_y) * header.tile_size_y + j;
1556 const auto idx = ii + jj * image.width;
1558 if (ii >= image.width || jj >= image.height) {
1562 const auto srcIdx = j *
static_cast<uint64_t
>(header.tile_size_x) + i;
1563 if (r >= 0) out[idx * channelCount + 0] = src[r][srcIdx];
1564 else if (hasRed) out[idx * channelCount + 0] = 0.f;
1565 if (g >= 0) out[idx * channelCount + 1] = src[g][srcIdx];
1566 else if (hasGreen) out[idx * channelCount + 1] = 0.f;
1567 if (b >= 0) out[idx * channelCount + 2] = src[b][srcIdx];
1568 else if (hasBlue) out[idx * channelCount + 2] = 0.f;
1569 if (a >= 0) out[idx * channelCount + 3] = src[a][srcIdx];
1570 else if (hasAlpha) out[idx * channelCount + 3] = 1.f;
1575 auto** src =
reinterpret_cast<C**
>(image.images);
1576 for (uint64_t i = 0; i < width * height; i++) {
1577 if (r >= 0) out[i * channelCount + 0] = src[r][i];
1578 else if (hasRed) out[i * channelCount + 0] = 0.f;
1579 if (g >= 0) out[i * channelCount + 1] = src[g][i];
1580 else if (hasGreen) out[i * channelCount + 1] = 0.f;
1581 if (b >= 0) out[i * channelCount + 2] = src[b][i];
1582 else if (hasBlue) out[i * channelCount + 2] = 0.f;
1583 if (a >= 0) out[i * channelCount + 3] = src[a][i];
1584 else if (hasAlpha) out[i * channelCount + 3] = 1.f;
1588 if (channelType == TINYEXR_PIXELTYPE_HALF) {
1589 populateBuffer.operator()<half>();
1591 populateBuffer.operator()<
float>();
1594 FreeEXRImage(&image);
1595 FreeEXRHeader(&header);
1596 return combinedChannels;
1601 if (stbi_is_hdr_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()))) {
1602 const ::stb_ptr<float> stbImage{
1603 stbi_loadf_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()), &width, &height, &channels, 0),
1619#ifdef VTFPP_SUPPORT_WEBP
1621 if (WebPBitstreamFeatures features; fileData.size() > 12 &&
static_cast<char>(fileData[8]) ==
'W' &&
static_cast<char>(fileData[9]) ==
'E' &&
static_cast<char>(fileData[10]) ==
'B' &&
static_cast<char>(fileData[11]) ==
'P' && WebPGetFeatures(
reinterpret_cast<const uint8_t*
>(fileData.data()), fileData.size(), &features) == VP8_STATUS_OK) {
1622 width = features.width;
1623 height = features.height;
1627 std::vector<std::byte> out;
1628 if (features.has_alpha) {
1631 if (!WebPDecodeRGBAInto(
reinterpret_cast<const uint8_t*
>(fileData.data()), fileData.size(),
reinterpret_cast<uint8_t*
>(out.data()), out.size(), width * (
ImageFormatDetails::bpp(format) / 8))) {
1637 if (!WebPDecodeRGBInto(
reinterpret_cast<const uint8_t*
>(fileData.data()), fileData.size(),
reinterpret_cast<uint8_t*
>(out.data()), out.size(), width * (
ImageFormatDetails::bpp(format) / 8))) {
1646 if (fileData.size() > 3 &&
static_cast<char>(fileData[0]) ==
'G' &&
static_cast<char>(fileData[1]) ==
'I' &&
static_cast<char>(fileData[2]) ==
'F') {
1647 const ::stb_ptr<stbi_uc> stbImage{
1648 stbi_load_gif_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()),
nullptr, &width, &height, &frameCount, &channels, 0),
1651 if (!stbImage || !frameCount) {
1661 return {
reinterpret_cast<std::byte*
>(stbImage.get()),
reinterpret_cast<std::byte*
>(stbImage.get() + (
ImageFormatDetails::getDataLength(format, width, height) * frameCount))};
1667 stbi__start_mem(&s,
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()));
1668 if (stbi__png_test(&s)) {
1670 const auto apngDecoder = [&format, &width, &height, &frameCount]<
typename P>(
const auto& stbImage, std::size_t dirOffset) -> std::vector<std::byte> {
1671 auto* dir =
reinterpret_cast<stbi__apng_directory*
>(stbImage.get() + dirOffset);
1672 if (dir->type != STBI__STRUCTURE_TYPE_APNG_DIRECTORY) {
1677 frameCount =
static_cast<int>(dir->num_frames);
1679 static constexpr auto calcPixelOffset = [](uint32_t offsetX, uint32_t offsetY, uint32_t width_) {
1680 return ((offsetY * width_) + offsetX) *
sizeof(P);
1684 static constexpr auto copyImageData = [](std::span<std::byte> dst, uint32_t dstWidth, uint32_t , std::span<const std::byte> src, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcOffsetX, uint32_t srcOffsetY) {
1685 for (uint32_t y = 0; y < srcHeight; y++) {
1687#ifdef SOURCEPP_BUILD_WITH_TBB
1688 std::execution::unseq,
1690 src.data() + calcPixelOffset( 0, y, srcWidth),
1691 src.data() + calcPixelOffset( srcWidth, y, srcWidth),
1692 dst.data() + calcPixelOffset(srcOffsetX, srcOffsetY + y, dstWidth));
1697 static constexpr auto copyImageSubRectData = [](std::span<std::byte> dst, std::span<const std::byte> src, uint32_t imgWidth, uint32_t , uint32_t subWidth, uint32_t subHeight, uint32_t subOffsetX, uint32_t subOffsetY) {
1698 for (uint32_t y = subOffsetY; y < subOffsetY + subHeight; y++) {
1700#ifdef SOURCEPP_BUILD_WITH_TBB
1701 std::execution::unseq,
1703 src.data() + calcPixelOffset(subOffsetX, y, imgWidth),
1704 src.data() + calcPixelOffset(subOffsetX + subWidth, y, imgWidth),
1705 dst.data() + calcPixelOffset(subOffsetX, y, imgWidth));
1709 static constexpr auto clearImageData = [](std::span<std::byte> dst, uint32_t dstWidth, uint32_t , uint32_t clrWidth, uint32_t clrHeight, uint32_t clrOffsetX, uint32_t clrOffsetY) {
1710 for (uint32_t y = 0; y < clrHeight; y++) {
1712#ifdef SOURCEPP_BUILD_WITH_TBB
1713 std::execution::unseq,
1715 dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth),
1716 dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth) + (clrWidth *
sizeof(P)),
1717 dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth),
1718 [](std::byte) {
return std::byte{0}; });
1722 static constexpr auto overlayImageData = [](std::span<std::byte> dst, uint32_t dstWidth, uint32_t , std::span<const std::byte> src, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcOffsetX, uint32_t srcOffsetY) {
1723 for (uint32_t y = 0; y < srcHeight; y++) {
1724 const auto* sp =
reinterpret_cast<const uint8_t*
>(src.data() + calcPixelOffset(0, y, srcWidth));
1725 auto* dp =
reinterpret_cast<uint8_t*
>(dst.data() + calcPixelOffset(srcOffsetX, srcOffsetY + y, dstWidth));
1726 for (uint32_t x = 0; x < srcWidth; x++, sp += 4, dp += 4) {
1730 if ((sp[3] == 0xff) || (dp[3] == 0)) {
1731 std::copy_n(sp,
sizeof(P), dp);
1733 const int u = sp[3] * 0xff;
1734 const int v = (0xff - sp[3]) * dp[3];
1735 const int al = u + v;
1736 dp[0] = (sp[0] * u + dp[0] * v) / al;
1737 dp[1] = (sp[1] * u + dp[1] * v) / al;
1738 dp[2] = (sp[2] * u + dp[2] * v) / al;
1746 const uint64_t fullFrameSize =
sizeof(P) * width * height;
1747 uint64_t currentFrameSize = 0;
1748 std::vector<std::byte> out(fullFrameSize * frameCount);
1749 uint64_t srcFrameOffset = 0;
1750 uint64_t dstFrameOffset = 0;
1751 for (uint32_t i = 0; i < dir->num_frames; i++) {
1752 const auto& frame = dir->frames[i];
1753 currentFrameSize =
sizeof(P) * frame.width * frame.height;
1756 if (frame.width == width && frame.height == height && frame.x_offset == 0 && frame.y_offset == 0 && frame.blend_op == STBI_APNG_blend_op_source) {
1757 std::memcpy(out.data() + dstFrameOffset, stbImage.get() + srcFrameOffset, fullFrameSize);
1760 if (frame.blend_op == STBI_APNG_blend_op_source || (i == 0 && frame.blend_op == STBI_APNG_blend_op_over)) {
1761 copyImageData({out.data() + dstFrameOffset, out.data() + dstFrameOffset + fullFrameSize}, width, height, {
reinterpret_cast<const std::byte*
>(stbImage.get() + srcFrameOffset),
reinterpret_cast<const std::byte*
>(stbImage.get() + srcFrameOffset + currentFrameSize)}, frame.width, frame.height, frame.x_offset, frame.y_offset);
1762 }
else if (frame.blend_op == STBI_APNG_blend_op_over) {
1763 overlayImageData({out.data() + dstFrameOffset, out.data() + dstFrameOffset + fullFrameSize}, width, height, {
reinterpret_cast<const std::byte*
>(stbImage.get() + srcFrameOffset),
reinterpret_cast<const std::byte*
>(stbImage.get() + srcFrameOffset + currentFrameSize)}, frame.width, frame.height, frame.x_offset, frame.y_offset);
1769 dstFrameOffset += fullFrameSize;
1770 srcFrameOffset += currentFrameSize;
1773 if (i == dir->num_frames - 1) {
1778 copyImageData({out.data() + dstFrameOffset, out.data() + dstFrameOffset + fullFrameSize}, width, height, {out.data() + dstFrameOffset - fullFrameSize, out.data() + dstFrameOffset}, width, height, 0, 0);
1781 if (frame.dispose_op == STBI_APNG_dispose_op_background || (i == 0 && frame.dispose_op == STBI_APNG_dispose_op_previous)) {
1782 clearImageData({out.data() + dstFrameOffset, out.data() + dstFrameOffset + fullFrameSize}, width, height, frame.width, frame.height, frame.x_offset, frame.y_offset);
1783 }
else if (frame.dispose_op == STBI_APNG_dispose_op_previous) {
1784 copyImageSubRectData({out.data() + dstFrameOffset, out.data() + dstFrameOffset + fullFrameSize}, {out.data() + dstFrameOffset - fullFrameSize, out.data() + dstFrameOffset}, width, height, frame.width, frame.height, frame.x_offset, frame.y_offset);
1785 }
else if (frame.dispose_op != STBI_APNG_dispose_op_none) {
1792 static const char *dispose_ops[] = {
1793 "STBI_APNG_dispose_op_none",
1794 "STBI_APNG_dispose_op_background",
1795 "STBI_APNG_dispose_op_previous",
1798 static const char *blend_ops[] = {
1799 "STBI_APNG_blend_op_source",
1800 "STBI_APNG_blend_op_over",
1803 fprintf(stderr,
"dir_offset : %zu\n", dirOffset);
1804 fprintf(stderr,
"dir.type : %.*s\n", 4, (
unsigned char *) &dir->type);
1805 fprintf(stderr,
"dir.num_frames : %u\n", dir->num_frames);
1806 fprintf(stderr,
"dir.default_image_is_first_frame : %s\n",
1807 dir->default_image_is_first_frame ?
"yes" :
"no");
1808 fprintf(stderr,
"dir.num_plays : %u\n", dir->num_plays);
1810 for (
int i = 0; i < dir->num_frames; ++i) {
1811 stbi__apng_frame_directory_entry *frame = &dir->frames[i];
1813 fprintf(stderr,
"frame : %u\n", i);
1814 fprintf(stderr,
" width : %u\n", frame->width);
1815 fprintf(stderr,
" height : %u\n", frame->height);
1816 fprintf(stderr,
" x_offset : %u\n", frame->x_offset);
1817 fprintf(stderr,
" y_offset : %u\n", frame->y_offset);
1818 fprintf(stderr,
" delay_num : %u\n", frame->delay_num);
1819 fprintf(stderr,
" delay_den : %u\n", frame->delay_den);
1820 fprintf(stderr,
" dispose_op : %s\n", dispose_ops[frame->dispose_op]);
1821 fprintf(stderr,
" blend_op : %s\n", blend_ops[frame->blend_op]);
1827 std::size_t dirOffset = 0;
1828 if (stbi__png_is16(&s)) {
1829 const ::stb_ptr<stbi_us> stbImage{
1830 stbi__apng_load_16bit(&s, &width, &height, &channels, STBI_rgb_alpha, &dirOffset),
1833 if (stbImage && dirOffset) {
1837 const ::stb_ptr<stbi_uc> stbImage{
1838 stbi__apng_load_8bit(&s, &width, &height, &channels, STBI_rgb_alpha, &dirOffset),
1841 if (stbImage && dirOffset) {
1848#ifdef VTFPP_SUPPORT_QOI
1851 qoi_desc descriptor;
1852 const ::stb_ptr<std::byte> qoiImage{
1853 static_cast<std::byte*
>(qoi_decode(fileData.data(),
static_cast<int>(fileData.size()), &descriptor, 0)),
1859 width =
static_cast<int>(descriptor.width);
1860 height =
static_cast<int>(descriptor.height);
1861 channels = descriptor.channels;
1872 if (stbi_is_16_bit_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()))) {
1873 const ::stb_ptr<stbi_us> stbImage{
1874 stbi_load_16_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()), &width, &height, &channels, 0),
1880 if (channels == 4) {
1882 }
else if (channels >= 1 && channels < 4) {
1889 std::span inPixels{stbImage.get(), outPixels.size()};
1891#ifdef SOURCEPP_BUILD_WITH_TBB
1892 std::execution::par_unseq,
1895 return {pixel, 0, 0, 0xffff};
1904 std::span inPixels{
reinterpret_cast<RG1616*
>(stbImage.get()), outPixels.size()};
1906#ifdef SOURCEPP_BUILD_WITH_TBB
1907 std::execution::par_unseq,
1910 return {pixel.r, pixel.g, 0, 0xffff};
1920 std::span inPixels{
reinterpret_cast<RGB161616*
>(stbImage.get()), outPixels.size()};
1922#ifdef SOURCEPP_BUILD_WITH_TBB
1923 std::execution::par_unseq,
1926 return {pixel.r, pixel.g, pixel.b, 0xffff};
1940 const ::stb_ptr<stbi_uc> stbImage{
1941 stbi_load_from_memory(
reinterpret_cast<const stbi_uc*
>(fileData.data()),
static_cast<int>(fileData.size()), &width, &height, &channels, 0),
1977 STBIR_RESIZE resize;
1978 const auto setEdgeModesAndFiltersAndDoResize = [edge, filter, &resize] {
1979 stbir_set_edgemodes(&resize,
static_cast<stbir_edge
>(edge),
static_cast<stbir_edge
>(edge));
1988 stbir_set_filters(&resize,
static_cast<stbir_filter
>(filter),
static_cast<stbir_filter
>(filter));
1992 static constexpr auto KAISER_BETA = [](
float s) {
2001 static constexpr auto KAISER_FILTER = [](
float x,
float s,
void*) ->
float {
2002 if (x < -1.f || x > 1.f) {
2007 static constexpr auto KAISER_SUPPORT = [](
float s,
void*) ->
float {
2008 const float baseSupport = KAISER_BETA(s) / 2.f;
2010 return std::max(2.f, baseSupport - 0.5f);
2012 return std::max(3.f, baseSupport);
2014 stbir_set_filter_callbacks(&resize, KAISER_FILTER, KAISER_SUPPORT, KAISER_FILTER, KAISER_SUPPORT);
2018 static constexpr auto NICE_RADIUS = 3.f;
2019 static constexpr auto SINC = [](
float x) ->
float {
2020 if (x == 0.f)
return 1.f;
2024 static constexpr auto NICE_FILTER = [](
float x, float,
void*) ->
float {
2025 if (x >= NICE_RADIUS || x <= -NICE_RADIUS)
return 0.f;
2026 return SINC(x) * SINC(x / NICE_RADIUS);
2030 static constexpr auto NICE_SUPPORT = [](
float invScale,
void*) ->
float {
2031 return invScale * NICE_RADIUS;
2033 stbir_set_filter_callbacks(&resize, NICE_FILTER, NICE_SUPPORT, NICE_FILTER, NICE_SUPPORT);
2037 stbir_resize_extended(&resize);
2040 const auto pixelLayout = ::imageFormatToSTBIRPixelLayout(format);
2041 if (pixelLayout == -1) {
2045 stbir_resize_init(&resize, in.data(), width, height,
ImageFormatDetails::bpp(containerFormat) / 8 * width, intermediary.data(), newWidth, newHeight,
ImageFormatDetails::bpp(containerFormat) / 8 * newWidth,
static_cast<stbir_pixel_layout
>(::imageFormatToSTBIRPixelLayout(containerFormat)),
static_cast<stbir_datatype
>(::imageFormatToSTBIRDataType(containerFormat, srgb)));
2046 setEdgeModesAndFiltersAndDoResize();
2050 stbir_resize_init(&resize, imageData.data(), width, height,
ImageFormatDetails::bpp(format) / 8 * width, out.data(), newWidth, newHeight,
ImageFormatDetails::bpp(format) / 8 * newWidth,
static_cast<stbir_pixel_layout
>(pixelLayout),
static_cast<stbir_datatype
>(::imageFormatToSTBIRDataType(format, srgb)));
2051 setEdgeModesAndFiltersAndDoResize();
2055std::vector<std::byte>
ImageConversion::resizeImageDataStrict(std::span<const std::byte> imageData,
ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t& widthOut,
ResizeMethod widthResize, uint16_t height, uint16_t newHeight, uint16_t& heightOut,
ResizeMethod heightResize,
bool srgb,
ResizeFilter filter,
ResizeEdge edge) {
2061 return resizeImageData(imageData, format, width, widthOut, height, heightOut, srgb, filter, edge);
2065std::vector<std::byte>
ImageConversion::cropImageData(std::span<const std::byte> imageData,
ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t xOffset, uint16_t height, uint16_t newHeight, uint16_t yOffset) {
2066 if (imageData.empty() || format ==
ImageFormat::EMPTY || xOffset + newWidth >= width || yOffset + newHeight >= height) {
2072 return convertImageDataToFormat(
cropImageData(
convertImageDataToFormat(imageData, format, container, width, height), container, width, newWidth, xOffset, height, newHeight, yOffset), container, format, newWidth, newHeight);
2076 std::vector<std::byte> out(pixelSize * newWidth * newHeight);
2077 for (uint16_t y = yOffset; y < yOffset + newHeight; y++) {
2078 std::memcpy(out.data() + (((y - yOffset) * newWidth) * pixelSize), imageData.data() + (((y * width) + xOffset) * pixelSize), newWidth * pixelSize);
2088 if (!widthPad && !heightPad) {
2089 return {imageData.begin(), imageData.end()};
2094 return convertImageDataToFormat(
padImageData(
convertImageDataToFormat(imageData, format, container, width, height), container, width, widthPad, height, heightPad), container, format, width + widthPad, height + heightPad);
2098 std::vector<std::byte> out(pixelSize * (width + widthPad) * (height + heightPad));
2101 for (uint16_t y = 0; y < height; y++) {
2102 std::memcpy(out.data() + ((y * (width + widthPad)) * pixelSize), imageData.data() + ((y * width) * pixelSize), width * pixelSize);
2106 for (
int y = 0; y < height + heightPad; y++) {
2107 for (
int x = 0; x < width + widthPad; x++) {
2108 if (x >= width && y >= height) {
2109 std::memcpy(out.data() + ((y * (width + widthPad) + x) * pixelSize), imageData.data() + (((height - 1) * (width) + (width - 1)) * pixelSize), pixelSize);
2110 }
else if (x >= width) {
2111 std::memcpy(out.data() + ((y * (width + widthPad) + x) * pixelSize), imageData.data() + ((y * (width) + (width - 1)) * pixelSize), pixelSize);
2112 }
else if (y >= height) {
2113 std::memcpy(out.data() + ((y * (width + widthPad) + x) * pixelSize), imageData.data() + (((height - 1) * (width) + x) * pixelSize), pixelSize);
2127 return {imageData.begin(), imageData.end()};
2132 return convertImageDataToFormat(
gammaCorrectImageData(
convertImageDataToFormat(imageData, format, container, width, height), container, width, height, gamma), container, format, width, height);
2135 static constexpr auto calculateGammaLUT = [](
float gamma_, uint8_t channelSize) -> std::array<uint8_t, 256> {
2136 const auto maxSize =
static_cast<float>((1 << channelSize) - 1);
2137 std::array<uint8_t, 256> gammaLUT{};
2138 for (
int i = 0; i < gammaLUT.size(); i++) {
2139 gammaLUT[i] =
static_cast<uint8_t
>(std::clamp(std::pow((
static_cast<float>(i) + 0.5f) / maxSize, gamma_) * maxSize - 0.5f, 0.f, maxSize));
2144 #define VTFPP_CREATE_GAMMA_LUTS(InputType) \
2145 std::unordered_map<uint8_t, std::array<uint8_t, 256>> gammaLUTs; \
2146 if constexpr (ImageFormatDetails::red(ImageFormat::InputType) > 0) { \
2147 if (!gammaLUTs.contains(ImageFormatDetails::red(ImageFormat::InputType))) { \
2148 gammaLUTs[ImageFormatDetails::red(ImageFormat::InputType)] = calculateGammaLUT(gamma, ImageFormatDetails::red(ImageFormat::InputType)); \
2151 if constexpr (ImageFormatDetails::green(ImageFormat::InputType) > 0) { \
2152 if (!gammaLUTs.contains(ImageFormatDetails::green(ImageFormat::InputType))) { \
2153 gammaLUTs[ImageFormatDetails::green(ImageFormat::InputType)] = calculateGammaLUT(gamma, ImageFormatDetails::green(ImageFormat::InputType)); \
2156 if constexpr (ImageFormatDetails::blue(ImageFormat::InputType) > 0) { \
2157 if (!gammaLUTs.contains(ImageFormatDetails::blue(ImageFormat::InputType))) { \
2158 gammaLUTs[ImageFormatDetails::blue(ImageFormat::InputType)] = calculateGammaLUT(gamma, ImageFormatDetails::blue(ImageFormat::InputType)); \
2162 #define VTFPP_APPLY_GAMMA_RED(value) \
2163 static_cast<decltype(value)>(gammaLUTs.at(ImageFormatDetails::red(PIXEL_TYPE::FORMAT))[value])
2165 #define VTFPP_APPLY_GAMMA_GREEN(value) \
2166 static_cast<decltype(value)>(gammaLUTs.at(ImageFormatDetails::green(PIXEL_TYPE::FORMAT))[value])
2168 #define VTFPP_APPLY_GAMMA_BLUE(value) \
2169 static_cast<decltype(value)>(gammaLUTs.at(ImageFormatDetails::blue(PIXEL_TYPE::FORMAT))[value])
2171 std::vector<std::byte> out(imageData.size());
2173#ifdef SOURCEPP_BUILD_WITH_TBB
2174 #define VTFPP_GAMMA_CORRECT(InputType, ...) \
2175 std::span imageDataSpan{reinterpret_cast<const ImagePixel::InputType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \
2176 std::span outSpan{reinterpret_cast<ImagePixel::InputType*>(out.data()), out.size() / sizeof(ImagePixel::InputType)}; \
2177 std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixel::InputType pixel) -> ImagePixel::InputType { \
2178 using PIXEL_TYPE = ImagePixel::InputType; \
2179 return __VA_ARGS__; \
2182 #define VTFPP_GAMMA_CORRECT(InputType, ...) \
2183 std::span imageDataSpan{reinterpret_cast<const ImagePixel::InputType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \
2184 std::span outSpan{reinterpret_cast<ImagePixel::InputType*>(out.data()), out.size() / sizeof(ImagePixel::InputType)}; \
2185 std::transform(imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixel::InputType pixel) -> ImagePixel::InputType { \
2186 using PIXEL_TYPE = ImagePixel::InputType; \
2187 return __VA_ARGS__; \
2190 #define VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(InputType, ...) \
2191 case InputType: { VTFPP_CREATE_GAMMA_LUTS(InputType) VTFPP_GAMMA_CORRECT(InputType, __VA_ARGS__); } break
2198 VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(
RGB888_BLUESCREEN, pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff ? ImagePixel::RGB888_BLUESCREEN{0, 0, 0xff} : ImagePixel::RGB888_BLUESCREEN{VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b)});
2200 VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(
BGR888_BLUESCREEN, pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff ? ImagePixel::BGR888_BLUESCREEN{0, 0, 0xff} : ImagePixel::BGR888_BLUESCREEN{VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r)});
2216 #undef VTFPP_CASE_GAMMA_CORRECT_AND_BREAK
2217 #undef VTFPP_GAMMA_CORRECT
2218 #undef VTFPP_APPLY_GAMMA_BLUE
2219 #undef VTFPP_APPLY_GAMMA_GREEN
2220 #undef VTFPP_APPLY_GAMMA_RED
2221 #undef VTFPP_CREATE_GAMMA_LUTS
2232 return {imageData.begin(), imageData.end()};
2237 return convertImageDataToFormat(
invertGreenChannelForImageData(
convertImageDataToFormat(imageData, format, container, width, height), container, width, height), container, format, width, height);
2240 #define VTFPP_INVERT_GREEN(PixelType, ChannelName, ...) \
2241 static constexpr auto channelSize = ImageFormatDetails::green(ImagePixel::PixelType::FORMAT); \
2242 std::span imageDataSpan{reinterpret_cast<const ImagePixel::PixelType*>(imageData.data()), imageData.size() / sizeof(ImagePixel::PixelType)}; \
2243 std::span outSpan{reinterpret_cast<ImagePixel::PixelType*>(out.data()), out.size() / sizeof(ImagePixel::PixelType)}; \
2244 std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [](ImagePixel::PixelType pixel) -> ImagePixel::PixelType { \
2245 if constexpr (std::same_as<decltype(pixel.ChannelName), float> || std::same_as<decltype(pixel.ChannelName), half>) { \
2246 pixel.ChannelName = static_cast<decltype(pixel.ChannelName)>(static_cast<float>(static_cast<uint64_t>(1) << channelSize) - 1.f - static_cast<float>(pixel.ChannelName)); \
2248 if constexpr (channelSize >= sizeof(uint32_t) * 8) { \
2249 pixel.ChannelName = static_cast<decltype(pixel.ChannelName)>((static_cast<uint64_t>(1) << channelSize) - 1 - static_cast<uint32_t>(pixel.ChannelName)); \
2251 pixel.ChannelName = static_cast<decltype(pixel.ChannelName)>(static_cast<uint32_t>(1 << channelSize) - 1 - static_cast<uint32_t>(pixel.ChannelName)); \
2256#ifdef SOURCEPP_BUILD_WITH_TBB
2257 #define VTFPP_INVERT_GREEN_CASE(PixelType) \
2258 case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, g, std::execution::par_unseq); break; }
2259 #define VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE(PixelType, ChannelName) \
2260 case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, ChannelName, std::execution::par_unseq); break; }
2262 #define VTFPP_INVERT_GREEN_CASE(PixelType) \
2263 case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, g); } break
2264 #define VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE(PixelType, ChannelName) \
2265 case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, ChannelName); } break
2268 std::vector<std::byte> out(imageData.size());
2310 #undef VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE
2311 #undef VTFPP_INVERT_GREEN_CASE
2312 #undef VTFPP_INVERT_GREEN
#define VTFPP_APPLY_GAMMA_BLUE(value)
#define VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(InputType,...)
#define VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE(PixelType, ChannelName)
#define VTFPP_APPLY_GAMMA_RED(value)
#define VTFPP_APPLY_GAMMA_GREEN(value)
#define VTFPP_REMAP_TO_8(value, shift)
#define VTFPP_REMAP_FROM_8(value, shift)
#define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a)
#define VTFPP_CASE_CONVERT_REMAP_AND_BREAK(InputType, r, g, b, a)
#define VTFPP_REMAP_FROM_16(value, shift)
#define VTFPP_INVERT_GREEN_CASE(PixelType)
#define SOURCEPP_DEBUG_BREAK
Create a breakpoint in debug.
constexpr T nearestPowerOf2(T n)
constexpr double kaiserWindow(double x, double b)
consteval uint32_t makeFourCC(const char fourCC[4])
Creates a FourCC identifier from a string of 4 characters.
std::vector< std::byte > convertFileToImageData(std::span< const std::byte > fileData, ImageFormat &format, int &width, int &height, int &frameCount)
std::vector< std::byte > convertImageDataToFile(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, FileFormat fileFormat=FileFormat::DEFAULT)
Converts image data to the given file format (PNG or EXR by default).
void setResizedDims(uint16_t &width, ResizeMethod widthResize, uint16_t &height, ResizeMethod heightResize)
Set the new image dimensions given a resize method.
std::array< std::vector< std::byte >, 6 > convertHDRIToCubeMap(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, uint16_t resolution=0, bool bilinear=true)
Converts an HDRI into a cubemap.
std::vector< std::byte > convertImageDataToFormat(std::span< const std::byte > imageData, ImageFormat oldFormat, ImageFormat newFormat, uint16_t width, uint16_t height, float quality=DEFAULT_COMPRESSED_QUALITY)
Converts an image from one format to another.
uint16_t getResizedDim(uint16_t n, ResizeMethod method)
Get the new image size given a resize method.
constexpr float DEFAULT_COMPRESSED_QUALITY
std::vector< std::byte > convertSeveralImageDataToFormat(std::span< const std::byte > imageData, ImageFormat oldFormat, ImageFormat newFormat, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth, float quality=DEFAULT_COMPRESSED_QUALITY)
Converts several images from one format to another.
std::vector< std::byte > gammaCorrectImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, float gamma)
Perform gamma correction on the given image data. Will not perform gamma correction if the input imag...
@ NICE
Valve NICE filtering, equivalent to Lanczos-3.
FileFormat getDefaultFileFormatForImageFormat(ImageFormat format)
PNG for integer formats, EXR for floating point formats.
std::vector< std::byte > padImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t widthPad, uint16_t height, uint16_t heightPad)
Pad the given image with pixels that are the same color as the edge. Padding is applied to the right ...
std::vector< std::byte > extractChannelFromImageData(std::span< const std::byte > imageData, auto P::*channel)
Extracts a single channel from the given image data.
std::vector< std::byte > cropImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t xOffset, uint16_t height, uint16_t newHeight, uint16_t yOffset)
Crops the given image to the new dimensions. If the image format is compressed it will be converted t...
std::vector< std::byte > resizeImageDataStrict(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t &widthOut, ResizeMethod widthResize, uint16_t height, uint16_t newHeight, uint16_t &heightOut, ResizeMethod heightResize, bool srgb, ResizeFilter filter, ResizeEdge edge=ResizeEdge::CLAMP)
Resize given image data to the new dimensions, where the new width and height are governed by the res...
std::vector< std::byte > invertGreenChannelForImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height)
Invert the green channel. Meant for converting normal maps between OpenGL and DirectX formats.
std::vector< std::byte > resizeImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t height, uint16_t newHeight, bool srgb, ResizeFilter filter, ResizeEdge edge=ResizeEdge::CLAMP)
Resize given image data to the new dimensions.
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.
@ CONSOLE_ARGB8888_LINEAR
@ CONSOLE_BGRX8888_LINEAR
@ CONSOLE_RGBA8888_LINEAR
@ CONSOLE_ABGR8888_LINEAR
@ CONSOLE_BGRX5551_LINEAR
@ CONSOLE_BGRA8888_LINEAR
@ CONSOLE_RGBA16161616_LINEAR