Files
UnrealEngineUWP/Engine/Source/Runtime/ImageWrapper/Private/ImageWrapperBase.cpp
charles bloom 04ffabc485 ImageWrapper and import/export refactor
FImage is now the standard preferred type for a bag of pixels
FImageView can point at pixels without owning an allocation
ERawImageFormat (FImage) converts to ETextureSourceFormat
FImageUtils provides generic load/save and get/set from FImage
major cleanup in the ImageWrappers
new preferred API is through ImageWrapperModule Compress/Decompress
SetRaw/GetRaw functions cleaned up to not have undefined behavior on unexpected formats
ImageWrapper output added for HDR,BMP,TGA
RGBA32F format added and supported throughout import/export
EditorFactories import/export made more generic, most image types handled the same way using FImage now
Deprecate old TSF RGBA order pixel formats
Fix many crashes or bad handling of unusual pixel formats
Pixel access functions should be used instead of switches on pixel type

#preflight 6230ade7e65a7e65d68a187c
#rb julien.stjean,martins.mozeiko,dan.thompson,fabian.giesen

[CL 19397199 by charles bloom in ue5-main branch]
2022-03-15 18:29:37 -04:00

379 lines
7.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ImageWrapperBase.h"
#include "ImageWrapperPrivate.h"
/* FImageWrapperBase structors
*****************************************************************************/
FImageWrapperBase::FImageWrapperBase()
: RawFormat(ERGBFormat::Invalid)
, RawBitDepth(0)
, RawBytesPerRow(0)
, Format(ERGBFormat::Invalid)
, BitDepth(0)
, Width(0)
, Height(0)
, NumFrames_DEPRECATED(1)
, Framerate_DEPRECATED(0)
{ }
/* FImageWrapperBase interface
*****************************************************************************/
void FImageWrapperBase::Reset()
{
LastError.Empty();
// not reset ?
//TArray64<uint8> RawData;
//TArray64<uint8> CompressedData;
RawFormat = ERGBFormat::Invalid;
RawBitDepth = 0;
RawBytesPerRow = 0;
Format = ERGBFormat::Invalid;
BitDepth = 0;
Width = 0;
Height = 0;
NumFrames_DEPRECATED = 1;
Framerate_DEPRECATED = 0;
}
void FImageWrapperBase::SetError(const TCHAR* ErrorMessage)
{
LastError = ErrorMessage;
}
/* IImageWrapper structors
*****************************************************************************/
TArray64<uint8> FImageWrapperBase::GetCompressed(int32 Quality)
{
LastError.Empty();
Compress(Quality);
return MoveTemp(CompressedData);
}
bool FImageWrapperBase::GetRaw(const ERGBFormat InFormat, int32 InBitDepth, TArray64<uint8>& OutRawData)
{
LastError.Empty();
Uncompress(InFormat, InBitDepth);
if (LastError.IsEmpty())
{
OutRawData = MoveTemp(RawData);
if ( OutRawData.IsEmpty() )
{
return false;
}
}
return LastError.IsEmpty();
}
bool FImageWrapperBase::SetCompressed(const void* InCompressedData, int64 InCompressedSize)
{
if(InCompressedSize > 0 && InCompressedData != nullptr)
{
Reset();
RawData.Empty(); // Invalidates the raw data too
CompressedData.Empty(InCompressedSize);
CompressedData.AddUninitialized(InCompressedSize);
FMemory::Memcpy(CompressedData.GetData(), InCompressedData, InCompressedSize);
return true;
}
return false;
}
bool FImageWrapperBase::SetRaw(const void* InRawData, int64 InRawSize, const int32 InWidth, const int32 InHeight, const ERGBFormat InFormat, const int32 InBitDepth, const int32 InBytesPerRow)
{
check(InRawData != NULL);
check(InRawSize > 0);
check(InWidth > 0);
check(InHeight > 0);
check(InBytesPerRow >= 0);
Reset();
CompressedData.Empty(); // Invalidates the compressed data too
if ( ! CanSetRawFormat(InFormat,InBitDepth) )
{
UE_LOG(LogImageWrapper, Warning, TEXT("ImageWrapper unsupported format; check CanSetRawFormat; %d x %d"), (int)InFormat,InBitDepth);
return false;
}
RawData.Empty(InRawSize);
RawData.AddUninitialized(InRawSize);
FMemory::Memcpy(RawData.GetData(), InRawData, InRawSize);
RawFormat = InFormat;
RawBitDepth = InBitDepth;
RawBytesPerRow = InBytesPerRow;
Width = InWidth;
Height = InHeight;
return true;
}
bool FImageWrapperBase::SetAnimationInfo_DEPRECATED(int32 InNumFrames, int32 InFramerate)
{
NumFrames_DEPRECATED = InNumFrames;
Framerate_DEPRECATED = InFramerate;
return true;
}
ERawImageFormat::Type IImageWrapper::ConvertRGBFormat(ERGBFormat RGBFormat,int BitDepth,bool * bIsExactMatch)
{
bool bIsExactMatchDummy;
if ( ! bIsExactMatch )
{
bIsExactMatch = &bIsExactMatchDummy;
}
switch(RGBFormat)
{
case ERGBFormat::RGBA:
if ( BitDepth == 8 )
{
*bIsExactMatch = false; // needs RB swap
return ERawImageFormat::BGRA8;
}
else if ( BitDepth == 16 )
{
*bIsExactMatch = true;
return ERawImageFormat::RGBA16;
}
break;
case ERGBFormat::BGRA:
if ( BitDepth == 8 )
{
*bIsExactMatch = true;
return ERawImageFormat::BGRA8;
}
else if ( BitDepth == 16 )
{
*bIsExactMatch = false; // needs RB swap
return ERawImageFormat::RGBA16;
}
break;
case ERGBFormat::Gray:
if ( BitDepth == 8 )
{
*bIsExactMatch = true;
return ERawImageFormat::G8;
}
else if ( BitDepth == 16 )
{
*bIsExactMatch = true;
return ERawImageFormat::G16;
}
break;
case ERGBFormat::BGRE:
if ( BitDepth == 8 )
{
*bIsExactMatch = true;
return ERawImageFormat::BGRE8;
}
break;
case ERGBFormat::RGBAF:
if ( BitDepth == 16 )
{
*bIsExactMatch = true;
return ERawImageFormat::RGBA16F;
}
else if ( BitDepth == 32 )
{
*bIsExactMatch = true;
return ERawImageFormat::RGBA32F;
}
break;
case ERGBFormat::GrayF:
if ( BitDepth == 16 )
{
*bIsExactMatch = true;
return ERawImageFormat::R16F;
}
else if ( BitDepth == 32 )
{
*bIsExactMatch = false; // no single channel F32
return ERawImageFormat::RGBA32F; // promote F32 to 4xF32
}
break;
default:
break;
}
UE_LOG(LogImageWrapper, Warning, TEXT("ConvertRGBFormat not handled : %d,%d"), (int)RGBFormat,BitDepth );
*bIsExactMatch = false;
return ERawImageFormat::Invalid;
}
void IImageWrapper::ConvertRawImageFormat(ERawImageFormat::Type RawFormat, ERGBFormat & OutFormat,int & OutBitDepth)
{
switch(RawFormat)
{
case ERawImageFormat::G8:
OutFormat = ERGBFormat::Gray;
OutBitDepth = 8;
break;
case ERawImageFormat::BGRA8:
OutFormat = ERGBFormat::BGRA;
OutBitDepth = 8;
break;
case ERawImageFormat::BGRE8:
OutFormat = ERGBFormat::BGRE;
OutBitDepth = 8;
break;
case ERawImageFormat::RGBA16:
OutFormat = ERGBFormat::RGBA;
OutBitDepth = 16;
break;
case ERawImageFormat::RGBA16F:
OutFormat = ERGBFormat::RGBAF;
OutBitDepth = 16;
break;
case ERawImageFormat::RGBA32F:
OutFormat = ERGBFormat::RGBAF;
OutBitDepth = 32;
break;
case ERawImageFormat::G16:
OutFormat = ERGBFormat::Gray;
OutBitDepth = 16;
break;
case ERawImageFormat::R16F:
OutFormat = ERGBFormat::GrayF;
OutBitDepth = 16;
break;
default:
check(0);
break;
}
}
bool IImageWrapper::GetRawImage(FImage & OutImage)
{
TArray64<uint8> OutRawData;
if ( ! GetRaw(OutRawData) )
{
return false;
}
int Width = GetWidth();
int Height = GetHeight();
ERGBFormat RGBFormat = GetFormat();
int BitDepth = GetBitDepth();
bool bExactMatch;
ERawImageFormat::Type RawFormat = GetClosestRawImageFormat(&bExactMatch);
if ( RawFormat == ERawImageFormat::Invalid )
{
return false;
}
EGammaSpace GammaSpace = EGammaSpace::Linear;
if ( GetSRGB() )
{
GammaSpace = EGammaSpace::sRGB;
}
if ( bExactMatch )
{
// no conversion required
OutImage.RawData = MoveTemp(OutRawData);
OutImage.SizeX = Width;
OutImage.SizeY = Height;
OutImage.NumSlices = 1;
OutImage.Format = RawFormat;
OutImage.GammaSpace = GammaSpace;
}
else
{
OutImage.Init( Width, Height, RawFormat, GammaSpace );
FImageView SrcImage = OutImage;
SrcImage.RawData = OutRawData.GetData();
switch(RGBFormat)
{
case ERGBFormat::RGBA:
{
// RGBA8 -> BGRA8
check( BitDepth == 8 );
check( RawFormat == ERawImageFormat::BGRA8 );
FImageCore::CopyImageRGBABGRA(SrcImage, OutImage );
break;
}
case ERGBFormat::BGRA:
{
// BGRA16 -> RGBA16
check( BitDepth == 16 );
check( RawFormat == ERawImageFormat::RGBA16 );
FImageCore::CopyImageRGBABGRA(SrcImage, OutImage );
break;
}
case ERGBFormat::GrayF:
{
/*
// 1 channel F32 -> F16 :
// because ERawImageFormat has no 1 channel F32 currently
check( BitDepth == 32 );
check( RawFormat == ERawImageFormat::R16F );
const float * Src = (const float *)OutRawData.GetData();
uint16 * Dst = (uint16 *)OutImage.RawData.GetData();
int64 NumPixels = OutImage.GetNumPixels();
for(int64 i=0;i<NumPixels;i++)
{
// clamp in F16 range? does StoreHalf do that?
FPlatformMath::StoreHalf(&Dst[i], Src[i]);
}
*/
// 1 channel F32 -> 4xF32 :
check( BitDepth == 32 );
check( RawFormat == ERawImageFormat::RGBA32F );
const float * Src = (const float *)OutRawData.GetData();
FLinearColor * Dst = (FLinearColor *)OutImage.RawData.GetData();
int64 NumPixels = OutImage.GetNumPixels();
for(int64 i=0;i<NumPixels;i++)
{
Dst[i] = FLinearColor( Src[i],Src[i],Src[i],1.f );
}
break;
}
default:
check(0);
return false;
}
}
return true;
}