Files
UnrealEngineUWP/Engine/Source/Runtime/ImageWrapper/Private/Formats/TgaImageWrapper.cpp
charles bloom 87f1b32bad fix TGA not exporting UDIM
change the TGA exporter to use the Generic path
make TgaImageWrapper write 24 or 32 bit like the legacy exporter
delete the legacy exporter

#jira https://jira.it.epicgames.com/browse/UE-176541
#preflight https://horde.devtools.epicgames.com/job/63e43e2c9910415ae2319a38
#rb fabian.giesen

[CL 24104640 by charles bloom in ue5-main branch]
2023-02-09 15:14:48 -05:00

188 lines
5.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Formats/TgaImageWrapper.h"
#include "ImageWrapperPrivate.h"
#include "TgaImageSupport.h"
// CanSetRawFormat returns true if SetRaw will accept this format
bool FTgaImageWrapper::CanSetRawFormat(const ERGBFormat InFormat, const int32 InBitDepth) const
{
return ( InFormat == ERGBFormat::BGRA ) && ( InBitDepth == 8 );
}
// returns InFormat if supported, else maps to something supported
ERawImageFormat::Type FTgaImageWrapper::GetSupportedRawFormat(const ERawImageFormat::Type InFormat) const
{
return ERawImageFormat::BGRA8;
}
void FTgaImageWrapper::Compress(int32 Quality)
{
CompressedData.Reset();
FImageView Image;
if ( ! GetImageViewOfSetRawForCompress(Image) )
{
SetError(TEXT("No valid image to compress"));
return;
}
if ( Width > UINT16_MAX || Height > UINT16_MAX )
{
SetError(TEXT("Image is too large to export as TGA"));
return;
}
check( Image.Format == ERawImageFormat::BGRA8 );
int64 ImageRowBytes = Width * 4;
// write 32-bit if we have a non-all-255 alpha channel, else write 24 bit
bool bHasAlpha = FImageCore::DetectAlphaChannel(Image);
int64 OutBytesPerPixel = bHasAlpha ? 4 : 3;
int64 OutSurfaceBytes = (int64) Width * Height * OutBytesPerPixel;
int64 CompresedSize = sizeof(FTGAFileHeader) + OutSurfaceBytes;
CompressedData.SetNum(CompresedSize);
uint8 * CompressedPtr = CompressedData.GetData();
// http://www.paulbourke.net/dataformats/tga/
// https://en.wikipedia.org/wiki/Truevision_TGA
FTGAFileHeader header = { };
uint16 Width16 = IntCastChecked<uint16>( Width );
uint16 Height16 = IntCastChecked<uint16>( Height );
header.Width = INTEL_ORDER16( Width16 );
header.Height = INTEL_ORDER16( Height16 );
header.ImageTypeCode = 2;
header.BitsPerPixel = OutBytesPerPixel*8;
// Image descriptor (1 byte): bits 3-0 give the alpha channel depth, bits 5-4 give pixel ordering
header.ImageDescriptor = bHasAlpha ? 8 : 0;
memcpy(CompressedPtr,&header,sizeof(header));
CompressedPtr += sizeof(header);
// write rows BGRA , bottom up :
for(int32 y = Height-1; y>= 0;y--)
{
const uint8 * From = &RawData[y * ImageRowBytes];
if ( bHasAlpha )
{
check( OutBytesPerPixel == 4 );
check( RawData.Num() == OutSurfaceBytes );
memcpy(CompressedPtr,From,ImageRowBytes);
CompressedPtr += ImageRowBytes;
}
else
{
check( OutBytesPerPixel == 3 );
for(int32 x=0;x<Width;x++)
{
// copy RGB, skip A
CompressedPtr[0] = From[0];
CompressedPtr[1] = From[1];
CompressedPtr[2] = From[2];
From += 4;
CompressedPtr += 3;
}
}
}
// TGA file footer is optional :
/*
FTGAFileFooter Ftr;
FMemory::Memzero( &Ftr, sizeof(Ftr) );
FMemory::Memcpy( Ftr.Signature, "TRUEVISION-XFILE", 16 );
Ftr.TrailingPeriod = '.';
Ar.Serialize( &Ftr, sizeof(Ftr) );
*/
check( CompressedPtr == CompressedData.GetData() + CompressedData.Num() );
}
bool FTgaImageWrapper::SetCompressed(const void* InCompressedData, int64 InCompressedSize)
{
bool bResult = FImageWrapperBase::SetCompressed(InCompressedData, InCompressedSize);
return bResult && LoadTGAHeader();
}
void FTgaImageWrapper::Uncompress(const ERGBFormat InFormat, const int32 InBitDepth)
{
const int32 BytesPerPixel = ( InFormat == ERGBFormat::Gray ? 1 : 4 );
int64 TextureDataSize = (int64)Width * Height * BytesPerPixel;
RawData.Empty(TextureDataSize);
RawData.AddUninitialized(TextureDataSize);
uint32* TextureData = reinterpret_cast< uint32* >( RawData.GetData() );
FTGAFileHeader* TgaHeader = reinterpret_cast< FTGAFileHeader* >( CompressedData.GetData() );
if ( !DecompressTGA_helper( TgaHeader, CompressedData.Num(), TextureData, TextureDataSize ) )
{
SetError(TEXT("Error while decompressing a TGA"));
RawData.Reset();
}
}
bool FTgaImageWrapper::IsTGAHeader(const void * CompressedData,int64 CompressedDataLength)
{
const FTGAFileHeader* TgaHeader = (FTGAFileHeader*)CompressedData;
if ( CompressedDataLength >= sizeof( FTGAFileHeader ) &&
( (TgaHeader->ColorMapType == 0 && TgaHeader->ImageTypeCode == 2) ||
( TgaHeader->ColorMapType == 0 && TgaHeader->ImageTypeCode == 3 ) || // ImageTypeCode 3 is greyscale
( TgaHeader->ColorMapType == 0 && TgaHeader->ImageTypeCode == 10 ) ||
( TgaHeader->ColorMapType == 1 && TgaHeader->ImageTypeCode == 1 && TgaHeader->BitsPerPixel == 8 ) ) )
{
return true;
}
else
{
return false;
}
}
bool FTgaImageWrapper::LoadTGAHeader()
{
check( CompressedData.Num() );
if( ! IsTGAHeader(CompressedData.GetData(),CompressedData.Num()) )
{
return false;
}
const FTGAFileHeader* TgaHeader = (FTGAFileHeader*)CompressedData.GetData();
Width = TgaHeader->Width;
Height = TgaHeader->Height;
BitDepth = 8;
// must exactly match the logic in DecompressTGA_helper
if(TgaHeader->ColorMapType == 1 && TgaHeader->ImageTypeCode == 1 && TgaHeader->BitsPerPixel == 8)
{
Format = ERGBFormat::Gray;
}
else if(TgaHeader->ColorMapType == 0 && TgaHeader->ImageTypeCode == 3 && TgaHeader->BitsPerPixel == 8)
{
Format = ERGBFormat::Gray;
}
else
{
Format = ERGBFormat::BGRA;
}
ColorMapType = TgaHeader->ColorMapType;
ImageTypeCode = TgaHeader->ImageTypeCode;
// TgaHeader->BitsPerPixel is bits per *color*
return true;
}