Files
2025-05-18 13:04:45 +08:00

982 lines
26 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DatasmithDefinitions.h"
#include "DatasmithRuntimeUtils.h"
// Borrowed from Engine/Plugins/Enterprise/DatasmithImporter/Source/DatasmithImporter/Private/Utility/DatasmithTextureResize.cpp
#if WITH_FREEIMAGE_LIB
#include "HAL/PlatformFile.h"
#include "HAL/PlatformFileManager.h"
#include "Math/Float16.h"
#include "Misc/Paths.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#endif // PLATFORM_WINDOWS
THIRD_PARTY_INCLUDES_START
#include "FreeImage.h"
THIRD_PARTY_INCLUDES_END
#if PLATFORM_WINDOWS
# define TCHAR_TO_FICHAR TCHAR_TO_WCHAR
# define FreeImage_GetFIFFromFilename FreeImage_GetFIFFromFilenameU
# define FreeImage_GetFileType FreeImage_GetFileTypeU
# define FreeImage_Load FreeImage_LoadU
# define FreeImage_Save FreeImage_SaveU
#else
# define TCHAR_TO_FICHAR TCHAR_TO_UTF8
#endif // PLATFORM_WINDOWS
#endif // WITH_FREEIMAGE_LIB
namespace DatasmithRuntime
{
#if WITH_FREEIMAGE_LIB
class FFreeImageWrapper
{
public:
static bool IsValid() { return FreeImageDllHandle != nullptr; }
static void FreeImage_Initialise(); // Loads and inits FreeImage on first call
private:
static void* FreeImageDllHandle; // Lazy init on first use, never release for now
};
#endif
void ImageReaderInitialize()
{
#if WITH_FREEIMAGE_LIB
FFreeImageWrapper::FreeImage_Initialise();
#endif
}
#if WITH_FREEIMAGE_LIB
bool GetTextureDataInternal(FIBITMAP* Bitmap, FREE_IMAGE_FORMAT FileType, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData);
void GetBitmapPixelInfo(FIBITMAP* Bitmap, int32& BitsPerPixel, int32& ChannelCount)
{
BitsPerPixel = FreeImage_GetBPP(Bitmap);
ChannelCount = -1;
switch (FreeImage_GetImageType(Bitmap))
{
case FIT_RGB16:
case FIT_RGBF:
{
ChannelCount = 3;
break;
}
case FIT_RGBAF:
case FIT_RGBA16:
{
ChannelCount = 4;
break;
}
case FIT_UINT16:
case FIT_INT16:
{
ChannelCount = BitsPerPixel / 16;
break;
}
case FIT_UINT32:
case FIT_INT32:
case FIT_FLOAT:
{
ChannelCount = BitsPerPixel / 32;
break;
}
case FIT_DOUBLE:
{
ChannelCount = BitsPerPixel / 64;
break;
}
case FIT_COMPLEX:
{
ChannelCount = BitsPerPixel / 128;
break;
}
case FIT_BITMAP:
{
if(BitsPerPixel == 16)
{
// Make sure we are in RGB16 565 mode or RGB16 555 mode
// That should be the only cases for a FIF_BITMAP
unsigned RedMask = FreeImage_GetRedMask(Bitmap);
unsigned GreenMask = FreeImage_GetGreenMask(Bitmap);
unsigned BlueMask = FreeImage_GetBlueMask(Bitmap);
if ( (RedMask == FI16_565_RED_MASK && GreenMask == FI16_565_GREEN_MASK && BlueMask == FI16_565_BLUE_MASK)
|| (RedMask == FI16_555_RED_MASK && GreenMask == FI16_555_GREEN_MASK && BlueMask == FI16_555_BLUE_MASK))
{
ChannelCount = 3;
}
}
else
{
ChannelCount = BitsPerPixel == 32 ? 4 : (BitsPerPixel == 24 ? 3 : 1);
}
break;
}
case FIT_UNKNOWN:
default:
{
break;
}
}
}
int32 PreviousPowerOfTwo(int32 Reference)
{
int32 PreviousValue = 2;
int32 NextValue = PreviousValue << 1;
while (Reference > NextValue)
{
PreviousValue = NextValue;
NextValue = PreviousValue << 1;
}
return PreviousValue;
}
int32 NextPowerOfTwo(int32 Reference)
{
return PreviousPowerOfTwo(Reference) << 1;
}
int32 NearestPowerOfTwo(int32 Reference)
{
int32 PreviousValue = PreviousPowerOfTwo(Reference);
int32 NextValue = PreviousValue << 1;
if ((NextValue - Reference)<(Reference - PreviousValue))
{
return NextValue;
}
else
{
return PreviousValue;
}
}
int32 ToPowerOfTwo(int32 Reference, EDSResizeTextureMode ResizeTexturesMode)
{
switch (ResizeTexturesMode)
{
case EDSResizeTextureMode::NearestPowerOfTwo:
return NearestPowerOfTwo(Reference);
case EDSResizeTextureMode::PreviousPowerOfTwo:
return PreviousPowerOfTwo(Reference);
case EDSResizeTextureMode::NextPowerOfTwo:
return NextPowerOfTwo(Reference);
default:
return Reference;
}
}
class NinePoints
{
public:
NinePoints(){ aa = ab = ac = ba = bb = bc = ca = cb = cc = 0; }
float aa, ab, ac, ba, bb, bc, ca, cb, cc;
};
int32 Tile(int32 x, int32 MaxTile)
{
if (x < 0)
{
x = MaxTile - 1;
}
if (x >= MaxTile)
{
x = 0;
}
return x;
}
bool PyramidProcess(FIBITMAP* Grey, int32 Level /*= 1*/)
{
int32 Height = FreeImage_GetHeight(Grey);
int32 Width = FreeImage_GetWidth(Grey);
if (Height <= 8 || Width <= 8 || Level >3)
{
return false;
}
FIBITMAP* GreyDown = FreeImage_Rescale(Grey, Width / 4, Height / 4, FREE_IMAGE_FILTER::FILTER_LANCZOS3);
if (!PyramidProcess(GreyDown, Level + 1))
{
FreeImage_Unload(GreyDown);
return true;
}
FIBITMAP* GreyUp = FreeImage_Rescale(GreyDown, Width, Height, FREE_IMAGE_FILTER::FILTER_BICUBIC);
for (int32 Index = 0; Index < Height; ++Index)
{
float* Bits = (float*)FreeImage_GetScanLine(Grey, Index);
float* bitsUp = (float*)FreeImage_GetScanLine(GreyUp, Index);
for (int32 x = 0; x < Width; x++)
{
Bits[x] = (Bits[x] * 0.5f + bitsUp[x] * 0.5f) + bitsUp[x] * 1.5f;
}
}
FreeImage_Unload(GreyDown);
FreeImage_Unload(GreyUp);
return true;
}
FIBITMAP* ConvertToNormalMap(FIBITMAP* Bump, bool bIsHighRange)
{
int32 Height = FreeImage_GetHeight(Bump);
int32 Width = FreeImage_GetWidth(Bump);
FIBITMAP* GreyPyramid = FreeImage_ConvertToFloat(Bump);
FIBITMAP* Normal = FreeImage_ConvertToRGBAF(Bump);
FreeImage_Unload(Bump);
if (!PyramidProcess(GreyPyramid, 1))
{
return nullptr;
}
TArray<NinePoints> Values;
Values.AddDefaulted(Height * Width);
float MinVal = 99999;
float MaxVal = -99999;
for (int32 Index = 0; Index < Height; ++Index)
{
float* Bits = reinterpret_cast< float*>(FreeImage_GetScanLine(GreyPyramid, Index));
for (int32 x = 0; x < Width; x++)
{ //memorize colors in a table
Values[Index*Width + x].bb = Bits[x];
if (Values[Index*Width + x].bb > MaxVal)
{
MaxVal = Values[Index*Width + x].bb;
}
if (Values[Index*Width + x].bb < MinVal)
{
MinVal = Values[Index*Width + x].bb;
}
}
}
FreeImage_Unload(GreyPyramid);
for (int32 Index = 0; Index < Height; ++Index)
{
for (int32 x = 0; x < Width; x++) //equalize
{
Values[Index*Width + x].bb = (Values[Index*Width + x].bb - MinVal) / (MaxVal - MinVal);
}
}
for (int32 Index = 0; Index < Height; ++Index)
{
for (int32 x = 0; x < Width; x++)
{ //memorize colors in a table
Values[Index*Width + x].aa = Values[Tile(Index - 1, Height)*Width + Tile(x - 1, Width)].bb;
Values[Index*Width + x].ba = Values[Tile(Index - 1, Height)*Width + x].bb;
Values[Index*Width + x].ca = Values[Tile(Index - 1, Height)*Width + Tile(x + 1, Width)].bb;
Values[Index*Width + x].ab = Values[ Index*Width + Tile(x - 1, Width)].bb;
Values[Index*Width + x].cb = Values[ Index*Width + Tile(x + 1, Width)].bb;
Values[Index*Width + x].ac = Values[Tile(Index + 1, Height)*Width + Tile(x - 1, Width)].bb;
Values[Index*Width + x].bc = Values[Tile(Index + 1, Height)*Width + x].bb;
Values[Index*Width + x].cc = Values[Tile(Index + 1, Height)*Width + Tile(x + 1, Width)].bb;
}
}
float Scale = 1.0f;
float MaxValueN = 0.f;
for (int32 Index = 0; Index < Height; ++Index)
{
FIRGBAF*Bits = (FIRGBAF*)FreeImage_GetScanLine(Normal, Index);
for (int32 x = 0; x < Width; x++)
{ //memorize colors in a table
int32 pos = Index*Width + x;
Bits[x].red = (Scale* -(Values[pos].cc - Values[pos].ac + 2.0f*(Values[pos].cb - Values[pos].ab) + Values[pos].ca - Values[pos].aa));
if (FMath::Abs(Bits[x].red) > MaxValueN)
{
MaxValueN = FMath::Abs(Bits[x].red);
}
Bits[x].green = -(Scale* -(Values[pos].aa - Values[pos].ac + 2.0f*(Values[pos].ba - Values[pos].bc) + Values[pos].ca - Values[pos].cc));
if (FMath::Abs(Bits[x].green) > MaxValueN)
{
MaxValueN = FMath::Abs(Bits[x].green);
}
Bits[x].blue = 1.0f;
Bits[x].alpha = Values[Index*Width + x].bb;
}
}
MaxValueN = 1.0f / MaxValueN;
for (int32 Index = 0; Index < Height; ++Index)
{
FIRGBAF* Bits = (FIRGBAF*)FreeImage_GetScanLine(Normal, Index);
for (int32 x = 0; x < Width; x++)
{ //memorize colors in a table
Bits[x].red = Bits[x].red * MaxValueN;
if (Bits[x].red > 0)
{
Bits[x].red = FMath::Sqrt(Bits[x].red);
}
if (Bits[x].red < 0)
{
Bits[x].red = -FMath::Sqrt(FMath::Abs(Bits[x].red));
}
Bits[x].green = Bits[x].green * MaxValueN;
if (Bits[x].green > 0)
{
Bits[x].green = FMath::Sqrt(Bits[x].green);
}
if (Bits[x].green < 0)
{
Bits[x].green = -FMath::Sqrt(FMath::Abs(Bits[x].green));
}
Bits[x].blue = 0.5f + 0.5f * FMath::Sqrt(1 - (Bits[x].red)*(Bits[x].red) + (Bits[x].green)*(Bits[x].green));
Bits[x].green = 0.5f + 0.5f * Bits[x].green;
Bits[x].red = 0.5f + 0.5f * Bits[x].red;
}
}
if (bIsHighRange)
{
return Normal;
}
FIBITMAP* Normal8 = FreeImage_Allocate(Width, Height, 4 * sizeof(DWORD), 0, 0, 0);
for (int32 Index = 0; Index < Height; ++Index)
{
FIRGBAF* Bits = (FIRGBAF*)FreeImage_GetScanLine(Normal, Index);
for (int32 x = 0; x < Width; x++)
{ //memorize colors in a table
RGBQUAD Color;
Color.rgbRed = (BYTE)(255.f * (FMath::Max(0.0f, FMath::Min(1.0f, Bits[x].red))));
Color.rgbGreen = (BYTE)(255.f *(1.0 - (FMath::Max(0.0f, FMath::Min(1.0f, Bits[x].green)))));
Color.rgbBlue = (BYTE)(255.f * (FMath::Max(0.0f, FMath::Min(1.0f, Bits[x].blue))));
FreeImage_SetPixelColor(Normal8, x, Index, &Color);
}
}
FreeImage_Unload(Normal);
return Normal8;
}
template<typename T>
void CopyData(FIBITMAP* Image, FTextureData& TextureData)
{
ensure(false);
}
template<>
void CopyData<BYTE>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const int32 Pitch = FreeImage_GetPitch(Image);
TextureData.Pitch = Pitch;
TFunction<void(const BYTE *FIBytes, uint8*)> ProcessPixels;
const int32 BitsPerPixel = FreeImage_GetBPP(Image);
if (BitsPerPixel == 32)
{
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
TextureData.PixelFormat = EPixelFormat::PF_B8G8R8A8;
#if FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_BGR
ProcessPixels = [Width = TextureData.Width](const BYTE *FIBytes, uint8* Buffer) -> void
{
for (int32 Index = 0; Index < Width; ++Index, FIBytes += 4, Buffer += 4)
{
Buffer[0] = FIBytes[FI_RGBA_BLUE];
Buffer[1] = FIBytes[FI_RGBA_GREEN];
Buffer[2] = FIBytes[FI_RGBA_RED];
Buffer[3] = FIBytes[FI_RGBA_ALPHA];
}
};
#endif // FREEIMAGE_COLORORDER
}
else if (BitsPerPixel == 24)
{
TextureData.Pitch = TextureData.Width * 4;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
TextureData.PixelFormat = EPixelFormat::PF_B8G8R8A8;
FMemory::Memzero(TextureData.ImageData, TextureData.Height * TextureData.Pitch);
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
ProcessPixels = [Width = TextureData.Width](const BYTE *FIBytes, uint8* Buffer) -> void
{
FI_RGBA_RED;
for (int32 Index = 0; Index < Width; ++Index, FIBytes += 3, Buffer += 4)
{
Buffer[0] = FIBytes[0];
Buffer[1] = FIBytes[1];
Buffer[2] = FIBytes[2];
Buffer[3] = UINT8_MAX;
}
};
#else
ProcessPixels = [Width = TextureData.Width](const BYTE *FIBytes, uint8* Buffer) -> void
{
FI_RGBA_RED;
for (int32 Index = 0; Index < Width; ++Index, FIBytes += 3, Buffer += 4)
{
Buffer[0] = FIBytes[FI_RGBA_BLUE];
Buffer[1] = FIBytes[FI_RGBA_GREEN];
Buffer[2] = FIBytes[FI_RGBA_RED];
Buffer[3] = UINT8_MAX;
}
};
#endif // FREEIMAGE_COLORORDER
}
else if (BitsPerPixel == 16)
{
TextureData.Pitch = Pitch;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
TextureData.PixelFormat = EPixelFormat::PF_G16;
}
else
{
ensure(BitsPerPixel == 8);
TextureData.Pitch = ((TextureData.Width + 3) / 4 ) * 4;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
TextureData.PixelFormat = EPixelFormat::PF_G8;
}
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
if (ProcessPixels)
{
ProcessPixels(FIBytes, Buffer);
}
else
{
FMemory::Memzero(Buffer, TextureData.Pitch);
FMemory::Memcpy(Buffer, FIBytes, Pitch);
}
}
}
template<>
void CopyData<uint16>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const int32 Pitch = FreeImage_GetPitch(Image);
const bool bIsSigned = FreeImage_GetImageType(Image) == FIT_INT16;
TextureData.PixelFormat = EPixelFormat::PF_G16;
TextureData.Pitch = Pitch;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * Pitch, 0x20);
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
if (bIsSigned)
{
const int16* FIPixels = (const int16*)FIBytes;
uint16* Pixels = (uint16*)Buffer;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, ++Pixels, ++FIPixels)
{
// Clamp at 0 and remap between 0 & UINT16_MAX
const int32 PixelValue = (FMath::Max((int32)*FIPixels, 0) * 2) - 1;
*Pixels = (uint16)FMath::Max(PixelValue, 0);
}
}
else
{
FMemory::Memcpy(Buffer, FIBytes, Pitch);
}
}
}
template<>
void CopyData<uint32>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const int32 Pitch = FreeImage_GetPitch(Image);
const bool bIsUnsigned = FreeImage_GetImageType(Image) == FIT_UINT32;
TextureData.PixelFormat = EPixelFormat::PF_G16;
TextureData.Pitch = ((TextureData.Width * sizeof(uint16) + 3) / 4) * 4;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
uint16* Pixels = (uint16*)Buffer;
if (bIsUnsigned)
{
const uint32* FIPixels = (const uint32*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, ++Pixels, ++FIPixels)
{
*Pixels = (uint16)(*FIPixels >> 16);
}
}
else
{
const int32* FIPixels = (const int32*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, ++Pixels, ++FIPixels)
{
// Clamp between 0 and UINT16_MAX
*Pixels = (uint16)(FMath::Max(*FIPixels, 0) >> 15);
}
}
}
}
template<>
void CopyData<float>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const bool bIsDouble = FreeImage_GetImageType(Image) == FIT_DOUBLE;
TextureData.PixelFormat = EPixelFormat::PF_R16F;
TextureData.Pitch = (((TextureData.Width * sizeof(FFloat16)) + 3) / 4) * 4;
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
FFloat16* Pixels = (FFloat16*)Buffer;
if (bIsDouble)
{
const double* FIPixels = (const double*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, ++Pixels, ++FIPixels)
{
*Pixels = (float)(*FIPixels);
}
}
else
{
const float* FIPixels = (const float*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, ++Pixels, ++FIPixels)
{
*Pixels = *FIPixels;
}
}
}
}
template<>
void CopyData<FIRGB16>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const int32 Pitch = FreeImage_GetPitch(Image);
const bool bHasAlpha = FreeImage_GetImageType(Image) == FIT_RGBA16;
TextureData.PixelFormat = EPixelFormat::PF_R16G16B16A16_UINT;
TextureData.Pitch = bHasAlpha ? Pitch : TextureData.Width * 4 * sizeof(uint16);
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
if (bHasAlpha)
{
FMemory::Memcpy(Buffer, FIBytes, Pitch);
}
else
{
const FIRGB16* FIPixels = (const FIRGB16*)FIBytes;
uint16* Pixels = (uint16*)Buffer;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, Pixels += 4, ++FIPixels)
{
Pixels[0] = (*FIPixels).red;
Pixels[1] = (*FIPixels).green;
Pixels[2] = (*FIPixels).blue;
Pixels[3] = UINT16_MAX;
}
}
}
}
template<>
void CopyData<FIRGBF>(FIBITMAP* Image, FTextureData& TextureData)
{
check(TextureData.ImageData == nullptr);
const int32 Pitch = FreeImage_GetPitch(Image);
const bool bHasAlpha = FreeImage_GetImageType(Image) == FIT_RGBAF;
TextureData.PixelFormat = EPixelFormat::PF_A32B32G32R32F;
TextureData.Pitch = TextureData.Width * 4 * sizeof(float);
TextureData.ImageData = (uint8*)FMemory::Malloc(TextureData.Height * TextureData.Pitch, 0x20);
uint8* Buffer = TextureData.ImageData;
for(int32 Index = TextureData.Height - 1; Index >= 0 ; --Index, Buffer += TextureData.Pitch)
{
const BYTE *FIBytes = (BYTE *)FreeImage_GetScanLine(Image, Index);
float* Pixels = (float*)Buffer;
if (bHasAlpha)
{
const FIRGBAF* FIPixels = (const FIRGBAF*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, Pixels += 4, ++FIPixels)
{
Pixels[0] = (*FIPixels).red;
Pixels[1] = (*FIPixels).green;
Pixels[2] = (*FIPixels).blue;
Pixels[3] = (*FIPixels).alpha;
}
}
else
{
const FIRGBF* FIPixels = (const FIRGBF*)FIBytes;
for (int32 PixelIndex = 0; PixelIndex < TextureData.Width; ++PixelIndex, Pixels += 4, ++FIPixels)
{
Pixels[0] = (*FIPixels).red;
Pixels[1] = (*FIPixels).green;
Pixels[2] = (*FIPixels).blue;
Pixels[3] = 1.f;
}
}
}
}
bool GetTextureDataFromFile(const TCHAR* Filename, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::GetTextureData);
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.FileExists(Filename) || FString(Filename).Len() < 3)
{
return false;
}
FFreeImageWrapper::FreeImage_Initialise();
if ( !FFreeImageWrapper::IsValid() )
{
//if(!(FFileHelper::LoadFileToArray(OutImageData, Filename) && OutImageData.Num() > 0))
//{
// return EDSTextureUtilsError::FileReadIssue;
//}
//return EDSTextureUtilsError::FreeImageNotFound;
return false;
}
//FinalImage format
FREE_IMAGE_FORMAT FileType = FIF_UNKNOWN;
//check the file signature and deduce its format
FileType = FreeImage_GetFileType(TCHAR_TO_FICHAR(Filename), 0);
//if still unknown, try to guess the file format from the file extension
if (FileType == FIF_UNKNOWN)
{
FileType = FreeImage_GetFIFFromFilename(TCHAR_TO_FICHAR(Filename));
}
//if still unknown, return failure
if (FileType == FIF_UNKNOWN)
{
return false;
}
//check that the plugin has reading capabilities and load the file
if (!FreeImage_FIFSupportsReading(FileType))
{
return false;
}
//pointer to the FinalImage, once loaded
FIBITMAP* Bitmap = FreeImage_Load(FileType, TCHAR_TO_FICHAR(Filename), 0);
//if the FinalImage failed to load, return failure
if (!Bitmap)
{
return false;
}
return GetTextureDataInternal(Bitmap, FileType, Mode, MaxSize, bGenerateNormalMap, TextureData);
}
bool GetTextureDataFromBuffer(TArray<uint8>& Bytes, EDatasmithTextureFormat Format, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::GetTextureData);
FFreeImageWrapper::FreeImage_Initialise();
if ( !FFreeImageWrapper::IsValid() )
{
//if(!(FFileHelper::LoadFileToArray(OutImageData, Filename) && OutImageData.Num() > 0))
//{
// return EDSTextureUtilsError::FileReadIssue;
//}
//return EDSTextureUtilsError::FreeImageNotFound;
return false;
}
// FinalImage format
FREE_IMAGE_FORMAT FileType = Format == EDatasmithTextureFormat::JPEG ? FIF_JPEG : FIF_PNG;
// check that the plugin has reading capabilities and load the file
if (!FreeImage_FIFSupportsReading(FileType))
{
return false;
}
FIMEMORY *MemoryBuffer = FreeImage_OpenMemory(Bytes.GetData(), Bytes.Num());
// Ensure the file types match
ensure(FreeImage_GetFileTypeFromMemory(MemoryBuffer, 0) == FileType);
// pointer to the FinalImage, once loaded
FIBITMAP *Bitmap = FreeImage_LoadFromMemory(FileType, MemoryBuffer, 0);
// Close the memory stream
FreeImage_CloseMemory(MemoryBuffer);
// Safe to free array now
Bytes.Empty();
return GetTextureDataInternal(Bitmap, FileType, Mode, MaxSize, bGenerateNormalMap, TextureData);
}
bool GetTextureDataInternal(FIBITMAP *Bitmap, FREE_IMAGE_FORMAT FileType, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DatasmithRuntime::GetTextureData);
//if the FinalImage failed to load, return failure
if (!Bitmap)
{
return false;
}
//get the FinalImage Width and Height
const uint32 OriginalWidth = FreeImage_GetWidth(Bitmap);
const uint32 OriginalHeight = FreeImage_GetHeight(Bitmap);
{
//retrieve the FinalImage data
BYTE* Bits = FreeImage_GetBits(Bitmap);
//if this somehow one of these failed (they shouldn't), return failure
if ((Bits == 0) || (OriginalWidth == 0) || (OriginalHeight == 0))
{
return false;
}
}
if (bGenerateNormalMap)
{
Bitmap = ConvertToNormalMap(Bitmap, FileType == FIF_EXR || FileType == FIF_HDR);
if (!Bitmap)
{
return false;
}
}
uint32 NewWidth = OriginalWidth;
uint32 NewHeight = OriginalHeight;
if (OriginalHeight > MaxSize && OriginalHeight > OriginalWidth)
{
NewWidth = FMath::RoundToInt((float)MaxSize * (float)OriginalWidth / (float)OriginalHeight);
NewHeight = MaxSize;
}
else if (OriginalWidth > MaxSize && OriginalWidth >= OriginalHeight)
{
NewHeight = FMath::RoundToInt((float)MaxSize * (float)OriginalHeight / (float)OriginalWidth);
NewWidth = MaxSize;
}
FIBITMAP* RescaledImage = nullptr;
if (Mode != EDSResizeTextureMode::NoResize)
{
NewWidth = ToPowerOfTwo(NewWidth, Mode);
NewHeight = ToPowerOfTwo(NewHeight, Mode);
// Skip resize if not necessary
RescaledImage = (NewHeight != OriginalHeight || NewWidth != OriginalWidth) ? FreeImage_Rescale(Bitmap, NewWidth, NewHeight, FREE_IMAGE_FILTER::FILTER_LANCZOS3) : Bitmap;
}
else
{
RescaledImage = Bitmap;
}
FIBITMAP* FinalImage = RescaledImage;
if (bGenerateNormalMap == true)
{
FinalImage = FreeImage_ConvertTo32Bits(RescaledImage);
check(FreeImage_GetImageType(FinalImage) == FIT_BITMAP);
}
bool bResult = true;
TextureData.Width = NewWidth;
TextureData.Height = NewHeight;
TextureData.Pitch = FreeImage_GetPitch(FinalImage);
const int32 BitsPerPixel = FreeImage_GetBPP(FinalImage);
TextureData.BytesPerPixel = FMath::Max(1, BitsPerPixel / 8);
check(TextureData.Width && TextureData.Height);
switch(FreeImage_GetImageType(FinalImage))
{
case FIT_BITMAP:
{
CopyData<BYTE>(FinalImage, TextureData);
break;
}
case FIT_UINT16:
case FIT_INT16:
{
CopyData<uint16>(FinalImage, TextureData);
break;
}
case FIT_UINT32:
case FIT_INT32:
{
CopyData<uint32>(FinalImage, TextureData);
break;
}
case FIT_FLOAT:
case FIT_DOUBLE:
{
CopyData<float>(FinalImage, TextureData);
break;
}
case FIT_RGB16:
case FIT_RGBA16:
{
CopyData<FIRGB16>(FinalImage, TextureData);
break;
}
case FIT_RGBAF:
case FIT_RGBF:
{
CopyData<FIRGBF>(FinalImage, TextureData);
break;
}
case FIT_COMPLEX:
default:
{
ensure(false);
bResult = false;
break;
}
}
if (FinalImage != RescaledImage)
{
FreeImage_Unload(FinalImage);
}
// Free rescaled FinalImage data if applicable
if (RescaledImage != Bitmap)
{
FreeImage_Unload(RescaledImage);
}
//Free FreeImage's copy of the data
FreeImage_Unload(Bitmap);
return bResult;
}
void* FFreeImageWrapper::FreeImageDllHandle = nullptr;
void FFreeImageWrapper::FreeImage_Initialise()
{
if ( FreeImageDllHandle != nullptr )
{
return;
}
// Push/PopDllDirectory are soooooo not threadsafe!
// Must load library in main thread before doing parallel processing
check(IsInGameThread());
if ( FreeImageDllHandle == nullptr )
{
FString FreeImageDir = FPaths::Combine( FPaths::EngineDir(), TEXT("Binaries/ThirdParty/FreeImage"), FPlatformProcess::GetBinariesSubdirectory() );
FString FreeImageLibDir = FPaths::Combine( FreeImageDir, TEXT( FREEIMAGE_LIB_FILENAME ));
FPlatformProcess::PushDllDirectory( *FreeImageDir );
FreeImageDllHandle = FPlatformProcess::GetDllHandle( *FreeImageLibDir );
FPlatformProcess::PopDllDirectory( *FreeImageDir );
}
if ( FreeImageDllHandle )
{
::FreeImage_Initialise();
}
}
#else
bool GetTextureDataFromFile(const TCHAR* Filename, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData)
{
return false;
}
bool GetTextureDataFromBuffer(TArray<uint8>& Bytes, EDatasmithTextureFormat Format, EDSResizeTextureMode Mode, uint32 MaxSize, bool bGenerateNormalMap, FTextureData& TextureData)
{
return false;
}
#endif
} // End of namespace DatasmithRuntime
#if WITH_FREEIMAGE_LIB
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#endif