395 lines
8.4 KiB
C++
395 lines
8.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Factories/TIFFLoader.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogTIFFLoader, Log, All);
|
|
|
|
|
|
// disable warnings about myself :
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
|
|
#if WITH_FREEIMAGE_LIB
|
|
|
|
#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
|
|
|
|
class FFreeImageWrapper
|
|
{
|
|
public:
|
|
static bool IsValid() { return FreeImageDllHandle != nullptr; }
|
|
|
|
static void FreeImage_Initialise(bool bLoadLocalPluginsOnly); // Loads and inits FreeImage on first call
|
|
|
|
private:
|
|
static void* FreeImageDllHandle; // Lazy init on first use, never release for now
|
|
};
|
|
|
|
void* FFreeImageWrapper::FreeImageDllHandle = nullptr;
|
|
|
|
void FFreeImageWrapper::FreeImage_Initialise(bool bLoadLocalPluginsOnly)
|
|
{
|
|
if (FreeImageDllHandle != nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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((BOOL)bLoadLocalPluginsOnly);
|
|
}
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
#include "Windows/HideWindowsPlatformTypes.h"
|
|
#endif // PLATFORM_WINDOWS
|
|
|
|
FTiffLoadHelper::FTiffLoadHelper()
|
|
{
|
|
FFreeImageWrapper::FreeImage_Initialise(false);
|
|
if (!FFreeImageWrapper::IsValid())
|
|
{
|
|
SetError(TEXT("Can't initialize FreeImage"));
|
|
return;
|
|
}
|
|
|
|
bIsValid = true;
|
|
}
|
|
|
|
FTiffLoadHelper::~FTiffLoadHelper()
|
|
{
|
|
if (Memory)
|
|
{
|
|
FreeImage_CloseMemory(Memory);
|
|
}
|
|
|
|
if (Bitmap)
|
|
{
|
|
FreeImage_Unload(Bitmap);
|
|
}
|
|
}
|
|
|
|
bool FTiffLoadHelper::Load(const uint8 * Buffer, uint32 Length)
|
|
{
|
|
FREE_IMAGE_FORMAT FileType = FIF_TIFF;
|
|
|
|
Memory = FreeImage_OpenMemory(const_cast<uint8*>(Buffer), Length);
|
|
Bitmap = FreeImage_LoadFromMemory(FileType, Memory, 0);
|
|
|
|
if (!Bitmap)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// FreeImage keeps all images upside-down
|
|
if (!ensure(FreeImage_FlipVertical(Bitmap)))
|
|
{
|
|
UE_LOG(LogTIFFLoader, Error, TEXT("Can't flip TIFF image"));
|
|
}
|
|
|
|
int32 BitsPerPixel = FreeImage_GetBPP(Bitmap);
|
|
|
|
bool bIsSourceSupported = true;
|
|
// source is grayscale(one channel), although this doesn't mean that all grayscale formats will be converted to G8
|
|
// high-bpp formats will use 16-bit-per-channel
|
|
int32 bIsSourceGrayScale = false;
|
|
bool bIsSource16BitsPerChannel = false;
|
|
// some FreeImage formats(signed integers) have limited support for conversion
|
|
bool bShouldConvertToByte = false;
|
|
bool bIsSourceFloatingPoint = false;
|
|
|
|
int32 ImageType = FreeImage_GetImageType(Bitmap);
|
|
switch (ImageType)
|
|
{
|
|
case FIT_RGB16:
|
|
case FIT_RGBA16:
|
|
{
|
|
bIsSource16BitsPerChannel = true;
|
|
break;
|
|
}
|
|
|
|
case FIT_INT16:
|
|
{
|
|
bShouldConvertToByte = true;
|
|
bIsSourceGrayScale = (BitsPerPixel / 16) == 1;
|
|
break;
|
|
}
|
|
case FIT_INT32:
|
|
{
|
|
bShouldConvertToByte = true;
|
|
bIsSourceGrayScale = (BitsPerPixel / 32) == 1;
|
|
break;
|
|
}
|
|
case FIT_FLOAT:
|
|
case FIT_DOUBLE:
|
|
case FIT_RGBAF:
|
|
case FIT_RGBF:
|
|
{
|
|
bIsSourceFloatingPoint = true;
|
|
break;
|
|
}
|
|
case FIT_COMPLEX:
|
|
{
|
|
bIsSourceSupported = false;
|
|
break;
|
|
}
|
|
|
|
|
|
case FIT_UINT16:
|
|
{
|
|
bIsSource16BitsPerChannel = true;
|
|
bIsSourceGrayScale = (BitsPerPixel / 16) == 1;
|
|
break;
|
|
}
|
|
case FIT_UINT32:
|
|
{
|
|
bIsSource16BitsPerChannel = true;
|
|
bIsSourceGrayScale = (BitsPerPixel / 32) == 1;
|
|
break;
|
|
}
|
|
|
|
case FIT_BITMAP:
|
|
{
|
|
// treat 1-bit dib as grayscale
|
|
bIsSourceGrayScale = BitsPerPixel == 1;
|
|
break;
|
|
}
|
|
case FIT_UNKNOWN:
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bIsSourceSupported)
|
|
{
|
|
UE_LOG(LogTIFFLoader, Error, TEXT("Unsupported TIFF format"));
|
|
return false;
|
|
}
|
|
|
|
Width = FreeImage_GetWidth(Bitmap);
|
|
Height = FreeImage_GetHeight(Bitmap);
|
|
|
|
if (bIsSourceFloatingPoint)
|
|
{
|
|
// Floating point images converted to RGBA16F
|
|
RawData.SetNumUninitialized(Height * Width * 4 * sizeof(FFloat16));
|
|
|
|
TextureSourceFormat = TSF_RGBA16F;
|
|
CompressionSettings = TC_HDR_Compressed;
|
|
bSRGB = false;
|
|
|
|
FIBITMAP* ConvertedBitmap = FreeImage_ConvertToType(Bitmap, FIT_RGBAF, true);
|
|
if (ConvertedBitmap)
|
|
{
|
|
BYTE* Bits = FreeImage_GetBits(ConvertedBitmap);
|
|
int32 Pitch = FreeImage_GetPitch(ConvertedBitmap);
|
|
for (int Y = 0; Y < Height; Y++)
|
|
{
|
|
BYTE* ScanLine = Bits + Pitch * Y;
|
|
FIRGBAF* Pixels = (FIRGBAF*)ScanLine;
|
|
for (int X = 0; X < Width; X++)
|
|
{
|
|
FIRGBAF P = Pixels[X];
|
|
FFloat16* TargetPixel = ((FFloat16*)RawData.GetData()) + (X + Y * Width) * 4;
|
|
TargetPixel[0].Set(P.red);
|
|
TargetPixel[1].Set(P.green);
|
|
TargetPixel[2].Set(P.blue);
|
|
TargetPixel[3].Set(P.alpha);
|
|
}
|
|
}
|
|
|
|
FreeImage_Unload(ConvertedBitmap);
|
|
}
|
|
}
|
|
else if (bIsSourceGrayScale)
|
|
{
|
|
// Grayscale images converted to either G8 or RGBA16
|
|
if (bIsSource16BitsPerChannel)
|
|
{
|
|
ConvertToRGBA16();
|
|
}
|
|
{
|
|
RawData.SetNumUninitialized(Height * Width * 4);
|
|
|
|
TextureSourceFormat = TSF_G8;
|
|
CompressionSettings = TC_Grayscale;
|
|
bSRGB = false;
|
|
|
|
FIBITMAP* ConvertedBitmap;
|
|
if (bShouldConvertToByte) {
|
|
ConvertedBitmap = FreeImage_ConvertToStandardType(Bitmap, true);
|
|
}
|
|
else
|
|
{
|
|
ConvertedBitmap = FreeImage_ConvertToGreyscale(Bitmap);
|
|
}
|
|
|
|
if (ConvertedBitmap)
|
|
{
|
|
|
|
BYTE* Bits = FreeImage_GetBits(ConvertedBitmap);
|
|
int32 Pitch = FreeImage_GetPitch(ConvertedBitmap);
|
|
for (int Y = 0; Y < Height; Y++)
|
|
{
|
|
BYTE* ScanLine = Bits + Pitch * Y;
|
|
uint8* TargetPixels = ((uint8*)RawData.GetData()) + Y * Width;
|
|
for (int X = 0; X < Width; X++)
|
|
{
|
|
uint8 P = ScanLine[X];
|
|
TargetPixels[X] = P;
|
|
}
|
|
}
|
|
FreeImage_Unload(ConvertedBitmap);
|
|
}
|
|
}
|
|
}
|
|
else // rgb(a)
|
|
{
|
|
if (bIsSource16BitsPerChannel)
|
|
{
|
|
ConvertToRGBA16();
|
|
}
|
|
else
|
|
{
|
|
// Convert to RGBA(8-bit)
|
|
|
|
RawData.SetNumUninitialized(Height * Width * 4);
|
|
|
|
TextureSourceFormat = TSF_BGRA8;
|
|
CompressionSettings = TC_Default;
|
|
bSRGB = true;
|
|
|
|
FIBITMAP* ConvertedBitmap = FreeImage_ConvertTo32Bits(Bitmap);
|
|
if (ConvertedBitmap)
|
|
{
|
|
|
|
BYTE* Bits = FreeImage_GetBits(ConvertedBitmap);
|
|
int32 Pitch = FreeImage_GetPitch(ConvertedBitmap);
|
|
for (int Y = 0; Y < Height; Y++)
|
|
{
|
|
BYTE* ScanLine = Bits + Pitch * Y;
|
|
for (int X = 0; X < Width; X++)
|
|
{
|
|
uint8* P = ScanLine + X * 4;
|
|
uint8* TargetPixel = ((uint8*)RawData.GetData()) + (X + Y * Width) * 4;
|
|
// FI_RGBA_X - cross-platform way to retrieve channels
|
|
TargetPixel[0] = P[FI_RGBA_BLUE];
|
|
TargetPixel[1] = P[FI_RGBA_GREEN];
|
|
TargetPixel[2] = P[FI_RGBA_RED];
|
|
TargetPixel[3] = P[FI_RGBA_ALPHA];
|
|
}
|
|
}
|
|
|
|
FreeImage_Unload(ConvertedBitmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FTiffLoadHelper::ConvertToRGBA16()
|
|
{
|
|
RawData.SetNumUninitialized(Height * Width * 4 * 2);
|
|
|
|
TextureSourceFormat = TSF_RGBA16;
|
|
CompressionSettings = TC_Default;
|
|
bSRGB = false;
|
|
|
|
FIBITMAP* ConvertedBitmap = FreeImage_ConvertToType(Bitmap, FIT_RGBA16, true);
|
|
if (ConvertedBitmap)
|
|
{
|
|
BYTE* Bits = FreeImage_GetBits(ConvertedBitmap);
|
|
int32 Pitch = FreeImage_GetPitch(ConvertedBitmap);
|
|
for (int Y = 0; Y < Height; Y++)
|
|
{
|
|
BYTE* ScanLine = Bits + Pitch * Y;
|
|
FIRGBA16* Pixels = (FIRGBA16*)ScanLine;
|
|
uint16* TargetScanLine = ((uint16*)RawData.GetData()) + Y * Width * 4;
|
|
for (int X = 0; X < Width; X++)
|
|
{
|
|
FIRGBA16 P = Pixels[X];
|
|
uint16* TargetPixel = TargetScanLine + X * 4;
|
|
TargetPixel[0] = P.red;
|
|
TargetPixel[1] = P.green;
|
|
TargetPixel[2] = P.blue;
|
|
TargetPixel[3] = P.alpha;
|
|
}
|
|
}
|
|
|
|
FreeImage_Unload(ConvertedBitmap);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FTiffLoadHelper::SetError(const FString& InErrorMessage)
|
|
{
|
|
ErrorMessage = InErrorMessage;
|
|
}
|
|
|
|
FString FTiffLoadHelper::GetError()
|
|
{
|
|
return ErrorMessage;
|
|
}
|
|
|
|
bool FTiffLoadHelper::IsValid()
|
|
{
|
|
return bIsValid;
|
|
}
|
|
|
|
#else // WITH_FREEIMAGE_LIB
|
|
|
|
FTiffLoadHelper::FTiffLoadHelper()
|
|
{
|
|
}
|
|
|
|
FTiffLoadHelper::~FTiffLoadHelper()
|
|
{
|
|
}
|
|
|
|
bool FTiffLoadHelper::Load(const uint8 * Buffer, uint32 Length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool FTiffLoadHelper::ConvertToRGBA16()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void FTiffLoadHelper::SetError(const FString& InErrorMessage)
|
|
{
|
|
}
|
|
|
|
FString FTiffLoadHelper::GetError()
|
|
{
|
|
return TEXT("FreeImage not supported on this platform");
|
|
}
|
|
|
|
bool FTiffLoadHelper::IsValid()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#endif // WITH_FREEIMAGE_LIB
|
|
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|