10#define STB_IMAGE_IMPLEMENTATION
11#define STBI_FAILURE_USERMSG
14#define STB_IMAGE_RESIZE_IMPLEMENTATION
15#include "stb_image_resize.h"
16#define STB_IMAGE_WRITE_IMPLEMENTATION
17#include "stb_image_write.h"
25#include <glm/detail/type_half.hpp>
30namespace nvttHelpers {
32 void error(nvtt::Error e)
override {
33 static const char* nvttErrors[] = {
36 "UNSUPPORTED_FEATURE",
40 "UNSUPPORTED_OUTPUT_FORMAT",
42 static_assert(std::size(nvttErrors) ==
static_cast<size_t>(nvtt::Error::Error_Count));
58 nvtt::Format
_format = nvtt::Format::Format_BC1;
69 virtual void beginImage(
int size,
int width,
int height,
int depth, [[maybe_unused]]
int face,
int miplevel)
override
84 virtual bool writeData(
const void* data,
int size)
override
92 [[nodiscard]]
static bool isBC1n(
const nvtt::Format format,
const bool isNormalMap)
noexcept
94 return isNormalMap && format == nvtt::Format_BC1;
97 [[nodiscard]]
static nvtt::Format
getNVTTFormat(
const ImageOutputFormat outputFormat,
const bool isNormalMap,
const bool hasAlpha,
const bool isGreyscale)
noexcept
103 return isNormalMap ? nvtt::Format::Format_BC3n : hasAlpha ? nvtt::Format::Format_BC3 : nvtt::Format::Format_BC1;
109 switch (outputFormat)
124 return isNormalMap ? nvtt::Format::Format_BC5 : isGreyscale ? nvtt::Format::Format_BC4 : nvtt::Format::Format_BC7;
137 return nvtt::MipmapFilter_Box;
147 if (isDDS && isCube) {
158 [[nodiscard]]
inline bool checkError(
string& messageInOut)
noexcept {
161 ILenum error = ilGetError();
162 bool stopInDebugger =
true;
163 messageInOut.resize(0);
164 while (error != IL_NO_ERROR)
169 stopInDebugger =
false;
172 messageInOut.append(
"\n").append( iluErrorString(error) );
174 ILenum nextError = ilGetError();
175 while (error == nextError)
177 nextError = ilGetError();
187 s_useUpperLeftOrigin = upperLeftOrigin;
192 ilEnable(IL_FILE_OVERWRITE);
193 ilSetInteger(IL_KEEP_DXTC_DATA, IL_TRUE);
195 iluImageParameter(ILU_FILTER, ILU_SCALE_MITCHELL);
203 return s_useUpperLeftOrigin;
209 return layer.
allocateMip(
data, size, width, height, depth, numComponents);
216 return loadFromFile(context, srgb, refWidth, refHeight, path,
name, options);
221 #define stbi__float2int(x) ((int) (x))
229 stbi__uint16* output = (stbi__uint16*)stbi__malloc_mad4( x, y, comp,
sizeof(stbi__uint16), 0 );
230 if ( output == NULL )
247 for (
int i = 0; i < x * y; ++i )
252 float z = (float)pow( data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i ) * 65536 + 0.5f;
254 if ( z > 65536 ) z = 65536;
259 float z = data[i * comp + k] * 65536 + 0.5f;
261 if ( z > 65536 ) z = 65536;
277 glm::detail::hdata* output = (glm::detail::hdata*)stbi__malloc_mad4( x, y, comp,
sizeof(glm::detail::hdata), 0 );
278 if ( output == NULL )
295 for (
int i = 0; i < x * y; ++i )
300 output[i * comp + k] = glm::detail::toFloat16( data[i * comp + k]);
304 output[i * comp + k] = glm::detail::toFloat16( data[i * comp + k] );
319 glm::detail::hdata* output = (glm::detail::hdata*)stbi__malloc_mad4( x, y, comp,
sizeof( glm::detail::hdata ), 0 );
320 if ( output == NULL )
337 for (
int i = 0; i < x * y; ++i )
339 for (
int k = 0; k < n; ++k )
341 output[i * comp + k] = glm::detail::toFloat16( (
float)(pow( data[i * comp + k] / 255.0f, stbi__l2h_gamma ) * stbi__l2h_scale));
346 for (
int i = 0; i < x * y; ++i )
348 output[i * comp + n] = glm::detail::toFloat16( data[i * comp + n] / 255.0f );
363 glm::detail::hdata* output = (glm::detail::hdata*)stbi__malloc_mad4( x, y, comp,
sizeof( glm::detail::hdata ), 0 );
365 if ( output == NULL )
382 for (
int i = 0; i < x * y; ++i )
384 for (
int k = 0; k < n; ++k )
386 output[i * comp + k] = glm::detail::toFloat16((
float)(pow( data[i * comp + k] / 65536.0f, stbi__l2h_gamma ) * stbi__l2h_scale));
391 for (
int i = 0; i < x * y; ++i )
393 output[i * comp + n] = glm::detail::toFloat16( data[i * comp + n] / 65536.0f );
408 float* output = (
float*)stbi__malloc_mad4( x, y, comp,
sizeof(
float ), 0 );
409 if ( output == NULL )
426 for (
int i = 0; i < x * y; ++i )
428 for (
int k = 0; k < n; ++k )
430 output[i * comp + k] = (float)(pow( data[i * comp + k] / 65536.0f, stbi__l2h_gamma ) * stbi__l2h_scale);
435 for (
int i = 0; i < x * y; ++i )
437 output[i * comp + n] = data[i * comp + n] / 65536.0f;
445 template<
typename To,
typename From>
453 To* output = (To*)stbi__malloc_mad4( x, y, comp,
sizeof(To), 0 );
454 if ( output == NULL )
471 for (
int i = 0; i < x * y; ++i )
476 output[i * comp + k] =
static_cast<To
>( data[i * comp + k] );
480 output[i * comp + k] =
static_cast<To
>( data[i * comp + k] );
507 FILE* f = stbi__fopen(
fullPath.c_str(),
"rb");
515 I32 width = 0, height = 0, comp = 0;
517 stbi_uc* dataLDR =
nullptr;
518 stbi__uint16* data16Bit =
nullptr;
519 F32* dataHDR =
nullptr;
520 U32* dataUINT =
nullptr;
521 glm::detail::hdata* dataHalf =
nullptr;
523 if ( stbi_is_hdr_from_file( f ) == 1 )
527 else if ( stbi_is_16_bit_from_file( f ) == 1 )
553 if ( useCache || createDDS )
555 STUBBED(
"Get rid of DevIL completely! It is really really bad for DDS handling (quality/performance) compared to the alternatives -Ionut");
561 const string cacheFilePath = (cachePath / cacheFileName).
string();
569 Task* ddsConversionTask =
nullptr;
576 nvtt::Context context;
577 context.enableCudaAcceleration(
true);
580 bool hasAlpha =
false;
581 if (image.load(
fullPath.c_str(), &hasAlpha))
583 constexpr bool isGreyScale = false;
584 const nvtt::Format outputFormat = nvttHelpers::getNVTTFormat(options._outputFormat, options._isNormalMap, hasAlpha, isGreyScale);
587 nvtt::CompressionOptions compressionOptions;
588 compressionOptions.setFormat(outputFormat);
589 compressionOptions.setQuality(options._fastCompression ? nvtt::Quality::Quality_Fastest : nvtt::Quality::Quality_Normal);
590 if (outputFormat == nvtt::Format_BC6)
592 compressionOptions.setPixelType(nvtt::PixelType_UnsignedFloat);
594 else if (outputFormat == nvtt::Format_BC2)
597 compressionOptions.setQuantization(
false,
true,
false);
599 else if (outputFormat == nvtt::Format_BC1a)
602 compressionOptions.setQuantization(
false,
true,
true, 127);
607 compressionOptions.setColorWeights(1, 1, 0);
610 nvtt::OutputOptions outputOptions;
611 outputOptions.setFileName(cacheFilePath.c_str() );
613 outputOptions.setErrorHandler(&errorHandler);
614 if (outputFormat == nvtt::Format_BC6 || outputFormat == nvtt::Format_BC7)
616 outputOptions.setContainer(nvtt::Container_DDS10);
620 outputOptions.setContainer(nvtt::Container_DDS);
624 outputOptions.setSrgbFlag(true);
629 if (!context.outputHeader(image, image.countMipmaps(), compressionOptions, outputOptions))
631 DIVIDE_UNEXPECTED_CALL();
637 image.normalizeNormalMap();
641 if (hasAlpha && options._alphaChannelTransparency)
643 coverage = image.alphaTestCoverage(Config::ALPHA_DISCARD_THRESHOLD);
644 image.setAlphaMode(nvtt::AlphaMode::AlphaMode_Transparency);
648 image.setAlphaMode(nvtt::AlphaMode::AlphaMode_None);
651 if (!context.compress(image, 0, 0, compressionOptions, outputOptions))
653 DIVIDE_UNEXPECTED_CALL();
660 while (image.buildNextMipmap(nvttHelpers::getNVTTMipFilter(options._mipFilter)))
662 if (options._isNormalMap)
664 image.normalizeNormalMap();
668 if (hasAlpha && options._alphaChannelTransparency)
670 image.scaleAlphaToCoverage(coverage, Config::ALPHA_DISCARD_THRESHOLD);
674 context.compress(image, 0, m, compressionOptions, outputOptions);
691 if ( ddsConversionTask !=
nullptr && (options._waitForDDSConversion || _loadingData._loadedDDSData) )
694 ddsConversionTask =
nullptr;
697 if (
fileExists( ResourcePath{ cacheFilePath }) && !ddsConversionTask )
699 if (loadDDS_NVTT( srgb, refWidth, refHeight, cachePath, cacheFileName ))
701 _loadingData._loadedDDSData =
true;
702 ++_loadingData._fileIndex;
710 if (
deleteFile( cachePath, cacheFileName ) != FileError::NONE)
715 return loadFromFile( context, srgb, refWidth, refHeight, path, name, options,
true );
726 _hasDummyAlphaChannel =
false;
729 if (stbi_info_from_file(f, &x, &y, &n) && n == 3)
731 _hasDummyAlphaChannel =
true;
736 const auto req_comp = _hasDummyAlphaChannel ? STBI_rgb_alpha : STBI_default;
737 switch ( _sourceDataType )
739 case SourceDataType::FLOAT:
741 dataHDR = stbi_loadf_from_file( f, &width, &height, &comp, req_comp );
744 case SourceDataType::SHORT:
746 data16Bit = stbi_load_from_file_16( f, &width, &height, &comp, req_comp );
749 case SourceDataType::BYTE:
751 dataLDR = stbi_load_from_file( f, &width, &height, &comp, req_comp );
757 if (dataHDR ==
nullptr && data16Bit ==
nullptr && dataLDR ==
nullptr)
763 if (_hasDummyAlphaChannel)
768 ImageLayer& layer = _layers.emplace_back();
770 if (refWidth != 0 && refHeight != 0 && (refWidth != width || refHeight != height))
772 if ( data16Bit !=
nullptr)
774 U16* resizedData16 = (
U16*)STBI_MALLOC(
to_size(refWidth)* refHeight * (_bpp / 8));
775 const I32 ret = stbir_resize_uint16_generic(data16Bit, width, height, 0,
776 resizedData16, refWidth, refHeight, 0,
778 STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR,
783 stbi_image_free(data16Bit);
784 data16Bit = resizedData16;
787 else if ( dataHDR !=
nullptr )
789 F32* resizedDataHDR = (
F32*)STBI_MALLOC(
to_size(refWidth) * refHeight * (_bpp / 8));
790 const I32 ret = stbir_resize_float(dataHDR, width, height, 0, resizedDataHDR, refWidth, refHeight, 0, comp);
794 stbi_image_free(dataHDR);
795 dataHDR = resizedDataHDR;
800 U8* resizedDataLDR = (
U8*)STBI_MALLOC(
to_size(refWidth) * refHeight * (_bpp / 8));
801 const I32 ret = srgb ? stbir_resize_uint8_srgb(dataLDR, width, height, 0, resizedDataLDR, refWidth, refHeight, 0, comp, -1, 0)
802 : stbir_resize_uint8(dataLDR, width, height, 0, resizedDataLDR, refWidth, refHeight, 0, comp);
806 stbi_image_free(dataLDR);
807 dataLDR = resizedDataLDR;
814 stbi_ldr_to_hdr_scale( 1.f );
815 stbi_ldr_to_hdr_gamma( 1.f );
817 switch (_requestedDataFormat)
821 switch ( _sourceDataType )
823 case SourceDataType::BYTE:
break;
824 case SourceDataType::SHORT:
826 dataLDR = stbi__convert_16_to_8( data16Bit, width, height, comp );
829 case SourceDataType::FLOAT:
831 dataLDR = stbi__hdr_to_ldr( dataHDR, width, height, comp );
836 _sourceDataType = SourceDataType::BYTE;
840 switch ( _sourceDataType )
842 case SourceDataType::BYTE:
844 data16Bit = stbi__convert_8_to_16( dataLDR, width, height, comp );
847 case SourceDataType::SHORT:
break;
848 case SourceDataType::FLOAT:
855 _sourceDataType = SourceDataType::SHORT;
859 switch ( _sourceDataType )
861 case SourceDataType::BYTE:
863 dataUINT = stbi_convert<U32, stbi_uc>(dataLDR, width, height, comp);
866 case SourceDataType::SHORT:
868 dataUINT = stbi_convert<U32, stbi__uint16>( data16Bit, width, height, comp );
871 case SourceDataType::FLOAT:
875 dataUINT = stbi_convert<U32, stbi__uint16>( data16Bit, width, height, comp );
880 _sourceDataType = SourceDataType::UINT;
890 switch ( _sourceDataType )
892 case SourceDataType::BYTE:
897 case SourceDataType::SHORT:
902 case SourceDataType::FLOAT:
909 _sourceDataType = SourceDataType::HALF;
913 switch ( _sourceDataType )
915 case SourceDataType::BYTE:
917 dataHDR = stbi__ldr_to_hdr( dataLDR, width, height, comp );
920 case SourceDataType::SHORT:
925 case SourceDataType::FLOAT:
break;
928 _sourceDataType = SourceDataType::FLOAT;
933 _dataType = _requestedDataFormat;
936 DIVIDE_ASSERT(comp != 3,
"RGB textures (e.g. 24bit) not supported due to Vulkan limitations");
948 _bpp =
to_U8( ((dataUINT !=
nullptr || dataHDR !=
nullptr) ? 32u : (data16Bit !=
nullptr || dataHalf !=
nullptr) ? 16u : 8u) * comp );
951 const size_t dataSize =
to_size(width) * height * comp;
952 if (dataHDR !=
nullptr)
954 ret = layer.allocateMip(dataHDR, dataSize,
to_U16(width),
to_U16(height), 1u,
to_U8(comp));
955 stbi_image_free(dataHDR);
957 else if ( dataUINT !=
nullptr)
959 ret = layer.allocateMip( dataUINT, dataSize,
to_U16(width),
to_U16(height), 1u,
to_U8(comp));
960 stbi_image_free( dataUINT );
962 else if (data16Bit !=
nullptr)
964 ret = layer.allocateMip(data16Bit, dataSize,
to_U16(width),
to_U16(height), 1u,
to_U8(comp));
965 stbi_image_free(data16Bit);
967 else if ( dataHalf !=
nullptr)
969 ret = layer.allocateMip( dataHalf, dataSize,
to_U16(width),
to_U16(height), 1u,
to_U8(comp));
970 stbi_image_free( dataHalf );
972 else if (dataLDR !=
nullptr)
974 ret = layer.allocateMip(dataLDR, dataSize,
to_U16(width),
to_U16(height), 1u,
to_U8(comp));
975 stbi_image_free(dataLDR);
982 ++_loadingData._fileIndex;
986bool ImageData::loadDDS_NVTT([[maybe_unused]]
const bool srgb,
const U16 refWidth,
const U16 refHeight,
const ResourcePath& path,
const std::string_view name)
989 return loadDDS_IL(srgb, refWidth, refHeight, path, name);
992bool ImageData::loadDDS_IL([[maybe_unused]]
const bool srgb,
const U16 refWidth,
const U16 refHeight,
const ResourcePath& path,
const std::string_view name)
999 ilGenImages(1, &imageID);
1000 ilBindImage(imageID);
1002 ilDeleteImage(imageID);
1003 if ( checkError( devilErrors ) )
1009 if (checkError( devilErrors ))
1015 const string fullPath = (path / name).
string();
1016 if (ilLoadImage(fullPath.c_str() ) == IL_FALSE)
1018 if ( checkError( devilErrors ) )
1026 const ILint dxtFormat = ilGetInteger(IL_DXTC_DATA_FORMAT);
1027 const bool compressed = dxtFormat == IL_DXT1 ||
1028 dxtFormat == IL_DXT1A||
1029 dxtFormat == IL_DXT2 ||
1030 dxtFormat == IL_DXT3 ||
1031 dxtFormat == IL_DXT4 ||
1032 dxtFormat == IL_DXT5;
1035 const auto flipActiveMip = [&]() {
1036 if (!s_useUpperLeftOrigin)
1040 ilFlipSurfaceDxtcData();
1047 if ( checkError( devilErrors ) )
1055 iluGetImageInfo(&imageInfo);
1056 if ( checkError( devilErrors ) )
1062 if (imageInfo.Type != IL_BYTE && imageInfo.Type != IL_UNSIGNED_BYTE &&
1063 imageInfo.Type != IL_FLOAT &&
1064 imageInfo.Type != IL_UNSIGNED_SHORT && imageInfo.Type != IL_SHORT) {
1065 ilConvertImage(imageInfo.Format, IL_FLOAT);
1066 imageInfo.Type = IL_FLOAT;
1067 if ( checkError( devilErrors ) )
1073 ILint channelCount = ilGetInteger(IL_IMAGE_CHANNELS);
1075 if (imageInfo.Format == IL_COLOUR_INDEX || (channelCount == 3 && imageInfo.Type == IL_UNSIGNED_BYTE) )
1079 ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
1080 imageInfo.Format = IL_RGBA;
1081 imageInfo.Type = IL_UNSIGNED_BYTE;
1083 if ( checkError( devilErrors ) )
1089 size_t storageSizeFactor = 1u;
1090 switch (imageInfo.Type) {
1093 _sourceDataType = SourceDataType::BYTE;
1095 case IL_UNSIGNED_BYTE:
1097 _sourceDataType = SourceDataType::BYTE;
1101 _sourceDataType = SourceDataType::SHORT;
1102 storageSizeFactor = 2;
1104 case IL_UNSIGNED_SHORT:
1106 _sourceDataType = SourceDataType::SHORT;
1107 storageSizeFactor = 2;
1111 _sourceDataType = SourceDataType::FLOAT;
1112 storageSizeFactor = 4;
1115 default:
return false;
1119 if (refWidth != 0 && refHeight != 0 && (imageInfo.Width != refWidth || imageInfo.Height != refHeight))
1121 if (iluScale(refWidth, refHeight, imageInfo.Depth))
1123 imageInfo.Width = refWidth;
1124 imageInfo.Height = refHeight;
1126 if ( checkError( devilErrors ) )
1133 switch (dxtFormat) {
1149 switch (imageInfo.Format) {
1151 case IL_COLOUR_INDEX:
1158 case IL_LUMINANCE_ALPHA:
1176 const ILint numImages = ilGetInteger(IL_NUM_IMAGES) + 1;
1178 const bool isCube = ilGetInteger(IL_IMAGE_CUBEFLAGS) != 0 || numImages % 6 == 0;
1180 ILint numFaces = ilGetInteger(IL_NUM_FACES) + 1;
1182 _bpp = imageInfo.Bpp * 8;
1184 _layers.reserve(_layers.size() +
to_size(numFaces) * numImages);
1185 for (ILint image = 0; image < numImages; ++image) {
1192 numFaces = ilGetInteger(IL_NUM_FACES) + 1;
1194 for (
I32 f = 0; f < numFaces; ++f) {
1197 const I32 face = determineFace(f,
true, isCube);
1200 for (ILuint m = 0u; m <= imageInfo.NumMips; ++m)
1203 ilBindImage(imageID);
1204 ilActiveImage(image);
1208 if ( checkError( devilErrors ) )
1212 const ILint width = ilGetInteger(IL_IMAGE_WIDTH);
1213 const ILint height = ilGetInteger(IL_IMAGE_HEIGHT);
1214 const ILint depth = ilGetInteger(IL_IMAGE_DEPTH);
1215 const ILuint size = compressed ? ilGetDXTCData(
nullptr, 0, dxtFormat )
1216 :
static_cast<ILuint
>(ilGetInteger( IL_IMAGE_SIZE_OF_DATA ));
1218 if ( checkError( devilErrors ) )
1228 :
to_U8(channelCount * storageSizeFactor));
1231 ilGetDXTCData(data, size, dxtFormat);
1235 memcpy(data, ilGetData(), size);
1237 if ( checkError( devilErrors ) )
1243 if ( compressed && image == 0 && face == 0 && m == 0 )
1245 _decompressedData.resize(
to_size( imageInfo.Width ) * imageInfo.Height * imageInfo.Depth * 4 );
1246 ilCopyPixels( 0, 0, 0, imageInfo.Width, imageInfo.Height, imageInfo.Depth, IL_RGBA, IL_UNSIGNED_BYTE, _decompressedData.data() );
1247 if ( checkError( devilErrors ) )
1262 getColour(x, y, returnColour.
r, returnColour.
g, returnColour.
b, returnColour.
a, mipLevel);
1263 return returnColour;
1275void ImageData::getColourComponent(
const I32 x,
const I32 y,
const U8 comp,
U8& c,
const U32 layer,
const U8 mipLevel)
const {
1276 assert(comp >= 0 && comp < 4);
1278 assert(_layers.size() > layer);
1285 const LayerData* mip = _layers[layer].getMip(mipLevel);
1292 c = _decompressedData[idx];
1294 switch(_sourceDataType )
1296 case SourceDataType::BYTE :
1298 c = U8ToU8Colour(
static_cast<const U8*
>(mip->
data())[idx] );
1300 case SourceDataType::SHORT:
1302 c = U16ToU8Colour(
static_cast<const U16*
>(mip->
data())[idx] );
1304 case SourceDataType::HALF:
1306 c = HalfToU8Colour(
static_cast<const glm::detail::hdata*
>(mip->
data())[idx] );
1308 case SourceDataType::FLOAT:
1310 c = F32ToU8Colour(
static_cast<const F32*
>(mip->
data())[idx] );
1312 case SourceDataType::UINT:
1314 c = U32ToU8Colour(
static_cast<const U32*
>(mip->
data())[idx] );
1320void ImageData::getColour(
const I32 x,
const I32 y,
U8& r,
U8& g,
U8& b,
U8& a,
const U32 layer,
const U8 mipLevel)
const {
1322 assert(_layers.size() > layer);
1324 const LayerData* mip = _layers[layer].getMip(mipLevel);
1331 const U8* src = _decompressedData.data();
1337 switch ( _sourceDataType )
1339 case SourceDataType::BYTE:
1341 const U8* src =
static_cast<U8*
>(mip->
data());
1342 r = U8ToU8Colour( src[idx + 0] );
1343 g = U8ToU8Colour( src[idx + 1] );
1344 b = U8ToU8Colour( src[idx + 2] );
1347 case SourceDataType::SHORT:
1349 const U16* src =
static_cast<U16*
>(mip->
data());
1350 r = U16ToU8Colour( src[idx + 0] );
1351 g = U16ToU8Colour( src[idx + 1] );
1352 b = U16ToU8Colour( src[idx + 2] );
1355 case SourceDataType::HALF:
1357 const U8* src =
static_cast<U8*
>(mip->
data());
1358 r = HalfToU8Colour( src[idx + 0] );
1359 g = HalfToU8Colour( src[idx + 1] );
1360 b = HalfToU8Colour( src[idx + 2] );
1361 a =
HasAlphaChannel( _format ) ? HalfToU8Colour( src[idx + 3] ) : 255;
1363 case SourceDataType::FLOAT:
1365 const F32* src =
static_cast<F32*
>(mip->
data());
1366 r = F32ToU8Colour( src[idx + 0] );
1367 g = F32ToU8Colour( src[idx + 1] );
1368 b = F32ToU8Colour( src[idx + 2] );
1371 case SourceDataType::UINT:
1373 const U8* src =
static_cast<U8*
>(mip->
data());
1374 r = U32ToU8Colour( src[idx + 0] );
1375 g = U32ToU8Colour( src[idx + 1] );
1376 b = U32ToU8Colour( src[idx + 2] );
1385 template<
size_t src_comp_num,
bool source_is_BGR>
1391 for (
I32 j = height - 1; j >= 0; --j )
1393 const Byte* src = sourceBuffer + (bytesPerPixel * width * j);
1394 Byte* dst = destBuffer + (3 * width * (height - 1 - j));
1396 for (
U16 i = 0u; i < width; ++i)
1398 *dst++ = *(src + (source_is_BGR ? 2 : 0));
1400 if constexpr ( src_comp_num > 1 )
1402 *dst++ = *(src + 1);
1404 if constexpr (src_comp_num > 2 )
1406 *dst++ = *(src + (source_is_BGR ? 0 : 2));
1410 src += bytesPerPixel;
1418 if ( width == 0u || height == 0 || numberOfComponents == 0 )
1424 Byte* dest = pix.data();
1426 switch (numberOfComponents )
1432 case 1: sourceIsBGR ? flipAndConvertToRGB8<1, true>( imageData, dest, width, height, bytesPerPixel ) : flipAndConvertToRGB8<1, false>( imageData, dest, width, height, bytesPerPixel );
break;
1433 case 2: sourceIsBGR ? flipAndConvertToRGB8<2, true>( imageData, dest, width, height, bytesPerPixel ) : flipAndConvertToRGB8<2, false>( imageData, dest, width, height, bytesPerPixel );
break;
1434 case 3: sourceIsBGR ? flipAndConvertToRGB8<3, true>( imageData, dest, width, height, bytesPerPixel ) : flipAndConvertToRGB8<3, false>( imageData, dest, width, height, bytesPerPixel );
break;
1435 case 4: sourceIsBGR ? flipAndConvertToRGB8<4, true>( imageData, dest, width, height, bytesPerPixel ) : flipAndConvertToRGB8<4, false>( imageData, dest, width, height, bytesPerPixel );
break;
1441 case SaveImageFormat::PNG:
return stbi_write_png(filename.
string().c_str(), width, height, 3, pix.data(), width * 3 *
sizeof(
Byte)) == 1;
1442 case SaveImageFormat::BMP:
return stbi_write_bmp(filename.
string().c_str(), width, height, 3, pix.data()) == 1;
1443 case SaveImageFormat::TGA:
return stbi_write_tga(filename.
string().c_str(), width, height, 3, pix.data()) == 1;
1444 case SaveImageFormat::JPG:
return stbi_write_jpg(filename.
string().c_str(), width, height, 3, pix.data(), 85) == 1;
1451bool SaveImageHDR(
const ResourcePath& filename,
const U16 width,
const U16 height,
const U8 numberOfComponents, [[maybe_unused]]
const U8 bytesPerPixel, [[maybe_unused]]
const bool sourceIsBGR,
const F32* imageData)
1453 return stbi_write_hdr(filename.
string().c_str(), width, height, numberOfComponents, imageData) == 1;
static bool UseTextureDDSCache() noexcept
Str StringFormat(const char *fmt, Args &&...args)
std::lock_guard< mutex > LockGuard
bool hasExtension(const ResourcePath &filePath, const std::string_view extensionNoDot)
constexpr U16 to_U16(const T value)
void Wait(const Task &task, TaskPool &pool)
bool DebugBreak(const bool condition) noexcept
Task * CreateTask(Predicate &&threadedFunction, bool allowedInIdle=true)
FileError createDirectory(const ResourcePath &path)
eastl::vector< Type > vector
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
void Start(Task &task, TaskPool &pool, TaskPriority priority=TaskPriority::DONT_CARE, const DELEGATE< void > &onCompletionFunction={})
constexpr size_t to_size(const T value)
FileError deleteFile(const ResourcePath &filePath, const std::string_view fileName)
bool fileExists(const ResourcePath &filePathAndName)
bool IsCompressed(GFXImageFormat format) noexcept
bool HasAlphaChannel(GFXImageFormat format) noexcept
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void warnfn(const char *format, T &&... args)
StringReturnType< N > string() const noexcept