Engine source (Main branch up to CL 2026164)

This commit is contained in:
Tim Sweeney
2014-03-14 14:13:41 -04:00
commit 324683ce78
10199 changed files with 3908382 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "ImageWrapperPrivatePCH.h"
#include "BmpImageSupport.h"
/**
* BMP image wrapper class.
* This code was adapted from UTextureFactory::ImportTexture, but has not been throughly tested.
*/
FBmpImageWrapper::FBmpImageWrapper(bool bInHasHeader, bool bInHalfHeight)
: FImageWrapperBase()
, bHasHeader(bInHasHeader)
, bHalfHeight(bInHalfHeight)
{
}
void FBmpImageWrapper::Compress( int32 Quality )
{
checkf(false, TEXT("BMP compression not supported"));
}
void FBmpImageWrapper::Uncompress( const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
const uint8* Buffer = CompressedData.GetData();
if( !bHasHeader || ((CompressedData.Num()>=sizeof(FBitmapFileHeader)+sizeof(FBitmapInfoHeader)) && Buffer[0]=='B' && Buffer[1]=='M') )
{
UncompressBMPData(InFormat, InBitDepth);
}
}
void FBmpImageWrapper::UncompressBMPData( const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
const uint8* Buffer = CompressedData.GetData();
const FBitmapInfoHeader* bmhdr = NULL;
const uint8* Bits = NULL;
if(bHasHeader)
{
bmhdr = (FBitmapInfoHeader *)(Buffer + sizeof(FBitmapFileHeader));
Bits = Buffer + ((FBitmapFileHeader *)Buffer)->bfOffBits;
}
else
{
bmhdr = (FBitmapInfoHeader *)Buffer;
Bits = Buffer + sizeof(FBitmapInfoHeader);
}
if( bmhdr->biCompression != BCBI_RGB )
{
UE_LOG(LogImageWrapper, Error, TEXT("RLE compression of BMP images not supported") );
return;
}
if( bmhdr->biPlanes==1 && bmhdr->biBitCount==8 )
{
// Do palette.
const uint8* bmpal = (uint8*)CompressedData.GetData() + sizeof(FBitmapInfoHeader);
// Set texture properties.
Width = bmhdr->biWidth;
Height = bHalfHeight ? bmhdr->biHeight / 2 : bmhdr->biHeight;
Format = ERGBFormat::BGRA;
RawData.Empty(Height * Width * 4);
RawData.AddUninitialized(Height * Width * 4);
FColor* ImageData = (FColor*)RawData.GetData();
// If the number for color palette entries is 0, we need to default to 2^biBitCount entries. In this case 2^8 = 256
int32 clrPaletteCount = bmhdr->biClrUsed ? bmhdr->biClrUsed : 256;
TArray<FColor> Palette;
for( int32 i=0; i<clrPaletteCount; i++ )
Palette.Add(FColor( bmpal[i*4+2], bmpal[i*4+1], bmpal[i*4+0], 255 ));
while( Palette.Num()<256 )
Palette.Add(FColor(0,0,0,255));
// Copy upside-down scanlines.
int32 SizeX = Width;
int32 SizeY = Height;
for(int32 Y = 0;Y < Height;Y++)
{
for(int32 X = 0;X < Width;X++)
{
ImageData[(SizeY - Y - 1) * SizeX + X] = Palette[*(Bits + Y * Align(Width,4) + X)];
}
}
}
else if( bmhdr->biPlanes==1 && bmhdr->biBitCount==24 )
{
// Set texture properties.
Width = bmhdr->biWidth;
Height = bHalfHeight ? bmhdr->biHeight / 2 : bmhdr->biHeight;
Format = ERGBFormat::BGRA;
RawData.Empty(Height * Width * 4);
RawData.AddUninitialized(Height * Width * 4);
uint8* ImageData = RawData.GetData();
// Copy upside-down scanlines.
const uint8* Ptr = Bits;
for( int32 y=0; y<Height; y++ )
{
uint8* DestPtr = &ImageData[(Height - 1 - y) * Width * 4];
uint8* SrcPtr = (uint8*) &Ptr[y * Align(Width*3,4)];
for( int32 x=0; x<Width; x++ )
{
*DestPtr++ = *SrcPtr++;
*DestPtr++ = *SrcPtr++;
*DestPtr++ = *SrcPtr++;
*DestPtr++ = 0xFF;
}
}
}
else if( bmhdr->biPlanes==1 && bmhdr->biBitCount==32 )
{
// Set texture properties.
Width = bmhdr->biWidth;
Height = bHalfHeight ? bmhdr->biHeight / 2 : bmhdr->biHeight;
Format = ERGBFormat::BGRA;
RawData.Empty(Height * Width * 4);
RawData.AddUninitialized(Height * Width * 4);
uint8* ImageData = RawData.GetData();
// Copy upside-down scanlines.
const uint8* Ptr = Bits;
for( int32 y=0; y<Height; y++ )
{
uint8* DestPtr = &ImageData[(Height - 1 - y) * Width * 4];
uint8* SrcPtr = (uint8*) &Ptr[y * Width * 4];
for( int32 x=0; x<Width; x++ )
{
*DestPtr++ = *SrcPtr++;
*DestPtr++ = *SrcPtr++;
*DestPtr++ = *SrcPtr++;
*DestPtr++ = *SrcPtr++;
}
}
}
else if( bmhdr->biPlanes==1 && bmhdr->biBitCount==16 )
{
UE_LOG(LogImageWrapper, Error, TEXT("BMP 16 bit format no longer supported. Use terrain tools for importing/exporting heightmaps.") );
}
else
{
UE_LOG(LogImageWrapper, Error, TEXT("BMP uses an unsupported format (%i/%i)"), bmhdr->biPlanes, bmhdr->biBitCount );
}
}
bool FBmpImageWrapper::SetCompressed( const void* InCompressedData, int32 InCompressedSize )
{
bool bResult = FImageWrapperBase::SetCompressed( InCompressedData, InCompressedSize );
return bResult && (bHasHeader ? LoadBMPHeader() : LoadBMPInfoHeader()); // Fetch the variables from the header info
}
bool FBmpImageWrapper::LoadBMPHeader()
{
const FBitmapInfoHeader* bmhdr = (FBitmapInfoHeader *)(CompressedData.GetData() + sizeof(FBitmapFileHeader));
const FBitmapFileHeader* bmf = (FBitmapFileHeader *)(CompressedData.GetData() + 0);
if( (CompressedData.Num() >= sizeof(FBitmapFileHeader) + sizeof(FBitmapInfoHeader)) && CompressedData.GetData()[0] == 'B' && CompressedData.GetData()[1] == 'M' )
{
if( bmhdr->biCompression != BCBI_RGB )
{
return false;
}
if( bmhdr->biPlanes==1 && ( bmhdr->biBitCount==8 || bmhdr->biBitCount==24 || bmhdr->biBitCount==32 ) )
{
// Set texture properties.
Width = bmhdr->biWidth;
Height = bmhdr->biHeight;
Format = ERGBFormat::BGRA;
return true;
}
}
return false;
}
bool FBmpImageWrapper::LoadBMPInfoHeader()
{
const FBitmapInfoHeader* bmhdr = (FBitmapInfoHeader *)CompressedData.GetData();
if( bmhdr->biCompression != BCBI_RGB )
{
return false;
}
if( bmhdr->biPlanes==1 && ( bmhdr->biBitCount==8 || bmhdr->biBitCount==24 || bmhdr->biBitCount==32 ) )
{
// Set texture properties.
Width = bmhdr->biWidth;
Height = bmhdr->biHeight;
Format = ERGBFormat::BGRA;
return true;
}
return false;
}

View File

@@ -0,0 +1,54 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once
/**
* BMP implementation of the helper class
*/
class FBmpImageWrapper
: public FImageWrapperBase
{
public:
/**
* Default Constructor.
*/
FBmpImageWrapper(bool bInHasHeader = true, bool bInHalfHeight = false);
public:
// Begin FImageWrapper Interface
virtual void Compress( int32 Quality ) OVERRIDE;
virtual void Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth ) OVERRIDE;
virtual bool SetCompressed( const void* InCompressedData, int32 InCompressedSize ) OVERRIDE;
// End FImageWrapper Interface
/** Helper function used to uncompress BMP data from a buffer */
void UncompressBMPData( const ERGBFormat::Type InFormat, const int32 InBitDepth );
/**
* Load the header information, returns true if successful.
*
* @return true if successful
*/
bool LoadBMPHeader();
/**
* Load the sub-header information, returns true if successful.
*
* @return true if successful
*/
bool LoadBMPInfoHeader();
private:
/** Whether this file has a BMP file header */
bool bHasHeader;
/** BMP as a sub-format of ICO stores its height as half their actual size */
bool bHalfHeight;
};

View File

@@ -0,0 +1,163 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "ImageWrapperPrivatePCH.h"
#include "BmpImageSupport.h"
#pragma pack(push,1)
struct FIconDirEntry
{
uint8 bWidth; // Width, in pixels, of the image
uint8 bHeight; // Height, in pixels, of the image
uint8 bColorCount; // Number of colors in image (0 if >=8bpp)
uint8 bReserved; // Reserved ( must be 0)
uint16 wPlanes; // Color Planes
uint16 wBitCount; // Bits per pixel
uint32 dwBytesInRes; // How many bytes in this resource?
uint32 dwImageOffset; // Where in the file is this image?
};
#pragma pack(pop)
#pragma pack(push,1)
struct FIconDir
{
uint16 idReserved; // Reserved (must be 0)
uint16 idType; // Resource Type (1 for icons)
uint16 idCount; // How many images?
FIconDirEntry idEntries[1]; // An entry for each image (idCount of 'em)
};
#pragma pack(pop)
#pragma pack(push,1)
struct FRGBQuad
{
uint8 rgbBlue; // Blue channel
uint8 rgbGreen; // Green channel
uint8 rgbRed; // Red channel
uint8 rgbReserved; // Reserved (alpha)
};
#pragma pack(pop)
#pragma pack(push,1)
struct FIconImage
{
FBitmapInfoHeader icHeader; // DIB header
FRGBQuad icColors[1]; // Color table
uint8 icXOR[1]; // DIB bits for XOR mask
uint8 icAND[1]; // DIB bits for AND mask
};
#pragma pack(pop)
/**
* ICO image wrapper class.
*/
FIcoImageWrapper::FIcoImageWrapper()
: FImageWrapperBase()
{
}
void FIcoImageWrapper::Compress( int32 Quality )
{
checkf(false, TEXT("ICO compression not supported"));
}
void FIcoImageWrapper::Uncompress( const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
const uint8* Buffer = CompressedData.GetData();
if(ImageOffset != 0 && ImageSize != 0)
{
SubImageWrapper->Uncompress(InFormat, InBitDepth);
}
}
bool FIcoImageWrapper::SetCompressed( const void* InCompressedData, int32 InCompressedSize )
{
bool bResult = FImageWrapperBase::SetCompressed( InCompressedData, InCompressedSize );
return bResult && LoadICOHeader(); // Fetch the variables from the header info
}
bool FIcoImageWrapper::GetRaw( const ERGBFormat::Type InFormat, int32 InBitDepth, const TArray<uint8>*& OutRawData )
{
LastError.Empty();
Uncompress(InFormat, InBitDepth);
if (LastError.IsEmpty())
{
OutRawData = &SubImageWrapper->GetRawData();
}
return LastError.IsEmpty();
}
bool FIcoImageWrapper::LoadICOHeader()
{
const uint8* Buffer = CompressedData.GetData();
#if WITH_UNREALPNG
TSharedPtr<FPngImageWrapper> PngWrapper = MakeShareable(new FPngImageWrapper);
#endif
TSharedPtr<FBmpImageWrapper> BmpWrapper = MakeShareable(new FBmpImageWrapper(false, true));
bool bFoundImage = false;
const FIconDir* IconHeader = (FIconDir*)(Buffer);
if(IconHeader->idReserved == 0 && IconHeader->idType == 1)
{
// use the largest-width 32-bit dir entry we find
uint32 LargestWidth = 0;
const FIconDirEntry* IconDirEntry = IconHeader->idEntries;
for(int32 Entry = 0; Entry < (int32)IconHeader->idCount; Entry++, IconDirEntry++)
{
const uint32 RealWidth = IconDirEntry->bWidth == 0 ? 256 : IconDirEntry->bWidth;
if( IconDirEntry->wBitCount == 32 && RealWidth > LargestWidth )
{
#if WITH_UNREALPNG
if(PngWrapper->SetCompressed(Buffer + IconDirEntry->dwImageOffset, (int32)IconDirEntry->dwBytesInRes))
{
Width = PngWrapper->GetWidth();
Height = PngWrapper->GetHeight();
Format = PngWrapper->GetFormat();
LargestWidth = RealWidth;
bFoundImage = true;
bIsPng = true;
ImageOffset = IconDirEntry->dwImageOffset;
ImageSize = IconDirEntry->dwBytesInRes;
}
else
#endif
if(BmpWrapper->SetCompressed(Buffer + IconDirEntry->dwImageOffset, (int32)IconDirEntry->dwBytesInRes))
{
// otherwise this should be a BMP icon
Width = BmpWrapper->GetWidth();
Height = BmpWrapper->GetHeight() / 2; // ICO file spec says to divide by 2 here as height refers to combined image & mask height
Format = BmpWrapper->GetFormat();
LargestWidth = RealWidth;
bFoundImage = true;
bIsPng = false;
ImageOffset = IconDirEntry->dwImageOffset;
ImageSize = IconDirEntry->dwBytesInRes;
}
}
}
}
if(bFoundImage)
{
if(bIsPng)
{
SubImageWrapper = PngWrapper;
}
else
{
SubImageWrapper = BmpWrapper;
}
}
return bFoundImage;
}

View File

@@ -0,0 +1,54 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once
/**
* ICO implementation of the helper class
*/
class FIcoImageWrapper
: public FImageWrapperBase
{
public:
/**
* Default Constructor.
*/
FIcoImageWrapper();
public:
// Begin FImageWrapper Interface
virtual void Compress( int32 Quality ) OVERRIDE;
virtual void Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth ) OVERRIDE;
virtual bool SetCompressed( const void* InCompressedData, int32 InCompressedSize ) OVERRIDE;
virtual bool GetRaw( const ERGBFormat::Type InFormat, int32 InBitDepth, const TArray<uint8>*& OutRawData ) OVERRIDE;
// End FImageWrapper Interface
private:
/**
* Load the header information.
*
* @return true if successful
*/
bool LoadICOHeader();
private:
/** Sub-wrapper component, as icons that contain PNG or BMP data */
TSharedPtr<FImageWrapperBase> SubImageWrapper;
/** Offset into file that we use as image data */
uint32 ImageOffset;
/** Size of image data in file */
uint32 ImageSize;
/** Whether we should use PNG or BMP data */
bool bIsPng;
};

View File

@@ -0,0 +1,107 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ImageWrapperBase.cpp: Implements the FImageWrapperBase class.
=============================================================================*/
#include "ImageWrapperPrivatePCH.h"
/* FImageWrapperBase structors
*****************************************************************************/
FImageWrapperBase::FImageWrapperBase( )
: RawFormat(ERGBFormat::Invalid)
, RawBitDepth(0)
, Format(ERGBFormat::Invalid)
, BitDepth(0)
, Width(0)
, Height(0)
{ }
/* FImageWrapperBase interface
*****************************************************************************/
void FImageWrapperBase::Reset( )
{
LastError.Empty();
RawFormat = ERGBFormat::Invalid;
RawBitDepth = 0;
Format = ERGBFormat::Invalid;
BitDepth = 0;
Width = 0;
Height = 0;
}
void FImageWrapperBase::SetError( const TCHAR* ErrorMessage )
{
LastError = ErrorMessage;
}
/* IImageWrapper structors
*****************************************************************************/
const TArray<uint8>& FImageWrapperBase::GetCompressed(int32 Quality)
{
LastError.Empty();
Compress(Quality);
return CompressedData;
}
bool FImageWrapperBase::GetRaw( const ERGBFormat::Type InFormat, int32 InBitDepth, const TArray<uint8>*& OutRawData )
{
LastError.Empty();
Uncompress(InFormat, InBitDepth);
if (LastError.IsEmpty())
{
OutRawData = &RawData;
}
return LastError.IsEmpty();
}
bool FImageWrapperBase::SetCompressed( const void* InCompressedData, int32 InCompressedSize )
{
check(InCompressedData != NULL);
check(InCompressedSize > 0);
Reset();
RawData.Empty(); // Invalidates the raw data too
CompressedData.Empty( InCompressedSize );
CompressedData.AddUninitialized( InCompressedSize );
FMemory::Memcpy( CompressedData.GetTypedData(), InCompressedData, InCompressedSize );
return true;
}
bool FImageWrapperBase::SetRaw( const void* InRawData, int32 InRawSize, const int32 InWidth, const int32 InHeight, const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
check(InRawData != NULL);
check(InRawSize > 0);
check(InWidth > 0);
check(InHeight > 0);
Reset();
CompressedData.Empty(); // Invalidates the compressed data too
RawData.Empty(InRawSize);
RawData.AddUninitialized(InRawSize);
FMemory::Memcpy(RawData.GetTypedData(), InRawData, InRawSize);
RawFormat = InFormat;
RawBitDepth = InBitDepth;
Width = InWidth;
Height = InHeight;
return true;
}

View File

@@ -0,0 +1,110 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ImageWrapperBase.h: Declares the FImageWrapperBase class.
=============================================================================*/
#pragma once
/**
* The abstract helper class for handling the different image formats
*/
class FImageWrapperBase
: public IImageWrapper
{
public:
/**
* Default Constructor.
*/
FImageWrapperBase( );
public:
/**
* Compresses the data.
*/
virtual void Compress( int32 Quality ) = 0;
/**
* Resets the local variables.
*/
virtual void Reset( );
/**
* Sets last error message.
*/
virtual void SetError( const TCHAR* ErrorMessage );
/**
* Function to uncompress our data
*
* @param InFormat how we want to manipulate the RGB data
*/
virtual void Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth ) = 0;
public:
// Begin IImageWrapper interface
virtual const TArray<uint8>& GetCompressed( int32 Quality = 0 ) OVERRIDE;
virtual int32 GetBitDepth( ) const OVERRIDE
{
return BitDepth;
}
virtual ERGBFormat::Type GetFormat() const OVERRIDE
{
return Format;
}
virtual int32 GetHeight( ) const OVERRIDE
{
return Height;
}
virtual bool GetRaw( const ERGBFormat::Type InFormat, int32 InBitDepth, const TArray<uint8>*& OutRawData ) OVERRIDE;
virtual int32 GetWidth( ) const OVERRIDE
{
return Width;
}
virtual bool SetCompressed( const void* InCompressedData, int32 InCompressedSize ) OVERRIDE;
virtual bool SetRaw( const void* InRawData, int32 InRawSize, const int32 InWidth, const int32 InHeight, const ERGBFormat::Type InFormat, const int32 InBitDepth ) OVERRIDE;
// End IImageWrapper interface
const TArray<uint8>& GetRawData() const
{
return RawData;
}
protected:
/** Arrays of compressed/raw data */
TArray<uint8> RawData;
TArray<uint8> CompressedData;
/** Format of the raw data */
TEnumAsByte<ERGBFormat::Type> RawFormat;
int8 RawBitDepth;
/** Format of the image */
TEnumAsByte<ERGBFormat::Type> Format;
/** Bit depth of the image */
int8 BitDepth;
/** Width/Height of the image data */
int32 Width;
int32 Height;
/** Last Error Message. */
FString LastError;
};

View File

@@ -0,0 +1,71 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ImageWrapperModule.cpp: Implements the FImageWrapperModule class.
=============================================================================*/
#include "ImageWrapperPrivatePCH.h"
DEFINE_LOG_CATEGORY(LogImageWrapper);
/**
* Image Wrapper module
*/
class FImageWrapperModule
: public IImageWrapperModule
{
public:
// Begin IImageWrapperModule interface
virtual IImageWrapperPtr CreateImageWrapper( const EImageFormat::Type InFormat )
{
FImageWrapperBase* ImageWrapper = NULL;
// Allocate a helper for the format type
switch(InFormat)
{
#if WITH_UNREALPNG
case EImageFormat::PNG:
ImageWrapper = new FPngImageWrapper();
break;
#endif // WITH_UNREALPNG
#if WITH_UNREALJPEG
case EImageFormat::JPEG:
ImageWrapper = new FJpegImageWrapper();
break;
#endif //WITH_UNREALJPEG
case EImageFormat::BMP:
ImageWrapper = new FBmpImageWrapper();
break;
case EImageFormat::ICO:
ImageWrapper = new FIcoImageWrapper();
break;
default:
break;
}
return MakeShareable(ImageWrapper);
}
// End IImageWrapperModule interface
public:
// Begin IModuleInterface interface
virtual void StartupModule( ) OVERRIDE { }
virtual void ShutdownModule( ) OVERRIDE { }
// End IModuleInterface interface
};
IMPLEMENT_MODULE(FImageWrapperModule, ImageWrapper);

View File

@@ -0,0 +1,37 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ImageWrapperPrivatePCH.h: Pre-compiled header file for the ImageWrapper module.
=============================================================================*/
#pragma once
#include "../Public/ImageWrapper.h"
/* Dependencies
*****************************************************************************/
#include "Core.h"
#include "ModuleManager.h"
/* Private includes
*****************************************************************************/
DECLARE_LOG_CATEGORY_EXTERN(LogImageWrapper, Log, All);
#include "ImageWrapperBase.h"
#if WITH_UNREALJPEG
#include "JpegImageWrapper.h"
#endif
#if WITH_UNREALPNG
#include "PngImageWrapper.h"
#endif
#include "BmpImageWrapper.h"
#include "IcoImageWrapper.h"

View File

@@ -0,0 +1,167 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
JpegImageWrapper.h: Declares the FJpegImageWrapper class.
Source code for JPEG decompression from:
http://code.google.com/p/jpeg-compressor/
=============================================================================*/
#include "ImageWrapperPrivatePCH.h"
#if WITH_UNREALJPEG
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#endif
#include "jpgd.h"
#include "jpgd.cpp"
#include "jpge.h"
#include "jpge.cpp"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
DEFINE_LOG_CATEGORY_STATIC(JPEGLog, Log, All);
// Only allow one thread to use JPEG decoder at a time (it's not thread safe)
FCriticalSection GJPEGSection;
/* FJpegImageWrapper structors
*****************************************************************************/
FJpegImageWrapper::FJpegImageWrapper( )
: FImageWrapperBase()
{}
/* FImageWrapperBase interface
*****************************************************************************/
bool FJpegImageWrapper::SetCompressed( const void* InCompressedData, int32 InCompressedSize )
{
jpgd::jpeg_decoder_mem_stream jpeg_memStream( (uint8*)InCompressedData, InCompressedSize );
jpgd::jpeg_decoder decoder(&jpeg_memStream);
if ( decoder.get_error_code() != jpgd::JPGD_SUCCESS )
return false;
bool bResult = FImageWrapperBase::SetCompressed( InCompressedData, InCompressedSize );
// We don't support 16 bit jpegs
BitDepth = 8;
Width = decoder.get_width();
Height = decoder.get_height();
switch ( decoder.get_num_components() )
{
case 1:
Format = ERGBFormat::Gray;
break;
case 3:
Format = ERGBFormat::RGBA;
break;
default:
return false;
}
return bResult;
}
bool FJpegImageWrapper::SetRaw( const void* InRawData, int32 InRawSize, const int32 InWidth, const int32 InHeight, const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
check((InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::BGRA) && InBitDepth == 8);
bool bResult = FImageWrapperBase::SetRaw( InRawData, InRawSize, InWidth, InHeight, InFormat, InBitDepth );
return bResult;
}
void FJpegImageWrapper::Compress( int32 Quality )
{
if (CompressedData.Num() == 0)
{
FScopeLock JPEGLock(&GJPEGSection);
if (Quality == 0) {Quality = 85;}
ensure(Quality >= 1 && Quality <= 100);
Quality = FMath::Clamp(Quality, 1, 100);
check( RawData.Num() );
check( Width > 0 );
check( Height > 0 );
// re-order components if required - JPEGs expect RGBA
if(RawFormat == ERGBFormat::BGRA)
{
FColor* Colors = (FColor*)RawData.GetData();
const int32 NumColors = RawData.Num() / 4;
for(int32 ColorIndex = 0; ColorIndex < NumColors; ColorIndex++)
{
uint8 Temp = Colors[ColorIndex].B;
Colors[ColorIndex].B = Colors[ColorIndex].R;
Colors[ColorIndex].R = Temp;
}
}
CompressedData.Empty();
CompressedData.AddUninitialized(RawData.Num());
int OutBufferSize = CompressedData.Num();
jpge::params Parameters;
Parameters.m_quality = Quality;
bool bSuccess = jpge::compress_image_to_jpeg_file_in_memory(
CompressedData.GetTypedData(), OutBufferSize, Width, Height, 4, RawData.GetTypedData(), Parameters);
check(bSuccess);
CompressedData.RemoveAt(OutBufferSize, CompressedData.Num() - OutBufferSize);
}
}
void FJpegImageWrapper::Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth )
{
// Ensure we haven't already uncompressed the file.
if ( RawData.Num() != 0 )
{
return;
}
// Get the number of channels we need to extract
int Channels = 0;
if ( ( InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::BGRA ) && InBitDepth == 8 )
{
Channels = 4;
}
else if ( InFormat == ERGBFormat::Gray && InBitDepth == 8 )
{
Channels = 1;
}
else
{
check( false );
}
FScopeLock JPEGLock( &GJPEGSection );
check( CompressedData.Num() );
int32 NumColors;
uint8* OutData = jpgd::decompress_jpeg_image_from_memory(
CompressedData.GetTypedData(), CompressedData.Num(), &Width, &Height, &NumColors, Channels );
RawData.Empty();
RawData.AddUninitialized( Width * Height * Channels );
FMemory::Memcpy( RawData.GetTypedData(), OutData, RawData.Num() );
}
#endif //WITH_JPEG

View File

@@ -0,0 +1,38 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
JpegImageWrapper.h: Declares the FJpegImageWrapper class.
=============================================================================*/
#pragma once
#if WITH_UNREALJPEG
/**
* Uncompresses JPEG data to raw 24bit RGB image that can be used by Unreal textures
*/
class FJpegImageWrapper
: public FImageWrapperBase
{
public:
/**
* Default constructor.
*/
FJpegImageWrapper();
public:
// Begin FImageWrapperBase interface
virtual bool SetCompressed( const void* InCompressedData, int32 InCompressedSize ) OVERRIDE;
virtual bool SetRaw( const void* InRawData, int32 InRawSize, const int32 InWidth, const int32 InHeight, const ERGBFormat::Type InFormat, const int32 InBitDepth ) OVERRIDE;
virtual void Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth ) OVERRIDE;
virtual void Compress(int32 Quality) OVERRIDE;
// End FImageWrapperBase interface
};
#endif //WITH_JPEG

View File

@@ -0,0 +1,428 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PngImageWrapper.cpp: Implements the FPngImageWrapper class.
http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
The below code isn't documented as it's black box behavior is almost
entirely based on the sample from the libPNG documentation.
InitCompressed and InitRaw will set initial state and you will then be
able to fill in Raw or CompressedData by calling Uncompress or Compress
respectively.
=============================================================================*/
#include "ImageWrapperPrivatePCH.h"
#if WITH_UNREALPNG
/** Only allow one thread to use libpng at a time (it's not thread safe) */
FCriticalSection GPNGSection;
/* Local helper classes
*****************************************************************************/
/**
* Guard that safely releases PNG reader resources
*/
class PNGReadGuard
{
public:
PNGReadGuard( png_structp* InReadStruct, png_infop* InInfo )
: png_ptr(InReadStruct)
, info_ptr(InInfo)
, PNGRowPointers(NULL)
{
}
~PNGReadGuard()
{
if (PNGRowPointers != NULL)
{
png_free( *png_ptr, *PNGRowPointers );
}
png_destroy_read_struct( png_ptr, info_ptr, NULL );
}
void SetRowPointers( png_bytep** InRowPointers )
{
PNGRowPointers = InRowPointers;
}
private:
png_structp* png_ptr;
png_infop* info_ptr;
png_bytep** PNGRowPointers;
};
/**
* Guard that safely releases PNG Writer resources
*/
class PNGWriteGuard
{
public:
PNGWriteGuard( png_structp* InWriteStruct, png_infop* InInfo )
: PNGWriteStruct(InWriteStruct)
, info_ptr(InInfo)
, PNGRowPointers(NULL)
{
}
~PNGWriteGuard()
{
if (PNGRowPointers != NULL)
{
png_free( *PNGWriteStruct, *PNGRowPointers );
}
png_destroy_write_struct( PNGWriteStruct, info_ptr );
}
void SetRowPointers( png_bytep** InRowPointers )
{
PNGRowPointers = InRowPointers;
}
private:
png_structp* PNGWriteStruct;
png_infop* info_ptr;
png_bytep** PNGRowPointers;
};
/* FPngImageWrapper structors
*****************************************************************************/
FPngImageWrapper::FPngImageWrapper()
: FImageWrapperBase()
, ReadOffset( 0 )
, ColorType(0)
, Channels(0)
{ }
/* FImageWrapper interface
*****************************************************************************/
void FPngImageWrapper::Compress( int32 Quality )
{
if (!CompressedData.Num())
{
// thread safety
FScopeLock PNGLock(&GPNGSection);
check(RawData.Num());
check(Width > 0);
check(Height > 0);
// Reset to the beginning of file so we can use png_read_png(), which expects to start at the beginning.
ReadOffset = 0;
png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, this, FPngImageWrapper::user_error_fn, FPngImageWrapper::user_warning_fn );
check(png_ptr);
png_infop info_ptr = png_create_info_struct( png_ptr );
check(info_ptr);
PNGWriteGuard PNGGuard(&png_ptr, &info_ptr);
{
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
png_set_IHDR(png_ptr, info_ptr, Width, Height, RawBitDepth, (RawFormat == ERGBFormat::Gray) ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_write_fn(png_ptr, this, FPngImageWrapper::user_write_compressed, FPngImageWrapper::user_flush_data);
png_bytep* row_pointers = (png_bytep*) png_malloc( png_ptr, Height*sizeof(png_bytep) );
const uint32 PixelChannels = (RawFormat == ERGBFormat::Gray) ? 1 : 4;
const uint32 BytesPerPixel = (RawBitDepth * PixelChannels) / 8;
const uint32 BytesPerRow = BytesPerPixel * Width;
PNGGuard.SetRowPointers( &row_pointers );
for (int32 i = 0; i < Height; i++)
{
row_pointers[i]= &RawData[i * BytesPerRow];
}
png_set_rows(png_ptr, info_ptr, row_pointers);
uint32 Transform = (RawFormat == ERGBFormat::BGRA) ? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
// PNG files store 16-bit pixels in network byte order (big-endian, ie. most significant bits first).
#if PLATFORM_LITTLE_ENDIAN
// We're little endian so we need to swap
if (RawBitDepth == 16)
{
Transform |= PNG_TRANSFORM_SWAP_ENDIAN;
}
#endif
png_write_png(png_ptr, info_ptr, Transform, NULL);
}
}
}
void FPngImageWrapper::Reset( )
{
FImageWrapperBase::Reset();
ReadOffset = 0;
ColorType = 0;
Channels = 0;
}
bool FPngImageWrapper::SetCompressed( const void* InCompressedData, int32 InCompressedSize )
{
bool bResult = FImageWrapperBase::SetCompressed( InCompressedData, InCompressedSize );
return bResult && LoadPNGHeader(); // Fetch the variables from the header info
}
void FPngImageWrapper::Uncompress( const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
if( !RawData.Num() || InFormat != RawFormat || InBitDepth != RawBitDepth)
{
check( CompressedData.Num() );
UncompressPNGData( InFormat, InBitDepth );
}
}
void FPngImageWrapper::UncompressPNGData( const ERGBFormat::Type InFormat, const int32 InBitDepth )
{
// thread safety
FScopeLock PNGLock(&GPNGSection);
check( CompressedData.Num() );
check( Width > 0 );
check( Height > 0 );
// Note that PNGs on PC tend to be BGR
check( InFormat == ERGBFormat::BGRA || InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::Gray ) // Other formats unsupported at present
check( InBitDepth == 8 || InBitDepth == 16 ) // Other formats unsupported at present
// Reset to the beginning of file so we can use png_read_png(), which expects to start at the beginning.
ReadOffset = 0;
png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, FPngImageWrapper::user_error_fn, FPngImageWrapper::user_warning_fn );
check( png_ptr );
png_infop info_ptr = png_create_info_struct( png_ptr );
check( info_ptr );
PNGReadGuard PNGGuard( &png_ptr, &info_ptr );
{
if (ColorType == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png_ptr);
}
if ((ColorType & PNG_COLOR_MASK_COLOR) == 0 && BitDepth < 8)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
// Insert alpha channel with full opacity for RGB images without alpha
if ((ColorType & PNG_COLOR_MASK_ALPHA) == 0 && (InFormat == ERGBFormat::BGRA || InFormat == ERGBFormat::RGBA))
{
// png images don't set PNG_COLOR_MASK_ALPHA if they have alpha from a tRNS chunk, but png_set_add_alpha seems to be safe regardless
if ((ColorType & PNG_COLOR_MASK_COLOR) == 0)
{
png_set_tRNS_to_alpha(png_ptr);
}
else if (ColorType == PNG_COLOR_TYPE_PALETTE)
{
png_set_tRNS_to_alpha(png_ptr);
}
if (InBitDepth == 8)
{
png_set_add_alpha(png_ptr, 0xff , PNG_FILLER_AFTER);
}
else if (InBitDepth == 16)
{
png_set_add_alpha(png_ptr, 0xffff , PNG_FILLER_AFTER);
}
}
// Calculate Pixel Depth
const uint32 PixelChannels = (InFormat == ERGBFormat::Gray) ? 1 : 4;
const uint32 BytesPerPixel = (InBitDepth * PixelChannels) / 8;
const uint32 BytesPerRow = BytesPerPixel * Width;
RawData.Empty(Height * BytesPerRow);
RawData.AddUninitialized(Height * BytesPerRow);
png_set_read_fn( png_ptr, this, FPngImageWrapper::user_read_compressed );
png_bytep* row_pointers = (png_bytep*) png_malloc( png_ptr, Height*sizeof(png_bytep) );
PNGGuard.SetRowPointers(&row_pointers);
for (int32 i = 0; i < Height; i++)
{
row_pointers[i]= &RawData[i * BytesPerRow];
}
png_set_rows(png_ptr, info_ptr, row_pointers);
uint32 Transform = (InFormat == ERGBFormat::BGRA) ? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
// PNG files store 16-bit pixels in network byte order (big-endian, ie. most significant bits first).
#if PLATFORM_LITTLE_ENDIAN
// We're little endian so we need to swap
if (BitDepth == 16)
{
Transform |= PNG_TRANSFORM_SWAP_ENDIAN;
}
#endif
// Convert grayscale png to RGB if requested
if ((ColorType & PNG_COLOR_MASK_COLOR) == 0 &&
(InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::BGRA))
{
Transform |= PNG_TRANSFORM_GRAY_TO_RGB;
}
// Convert RGB png to grayscale if requested
if ((ColorType & PNG_COLOR_MASK_COLOR) != 0 && InFormat == ERGBFormat::Gray)
{
png_set_rgb_to_gray_fixed(png_ptr, 2 /* warn if image is in color */, -1, -1);
}
// Strip alpha channel if requested output is grayscale
if (InFormat == ERGBFormat::Gray)
{
// this is not necessarily the best option, instead perhaps:
// png_color background = {0,0,0};
// png_set_background(png_ptr, &background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
Transform |= PNG_TRANSFORM_STRIP_ALPHA;
}
// Reduce 16-bit to 8-bit if requested
if (BitDepth == 16 && InBitDepth == 8)
{
#if PNG_LIBPNG_VER >= 10504
check(0); // Needs testing
Transform |= PNG_TRANSFORM_SCALE_16;
#else
Transform |= PNG_TRANSFORM_STRIP_16;
#endif
}
// Increase 8-bit to 16-bit if requested
if (BitDepth <= 8 && InBitDepth == 16)
{
#if PNG_LIBPNG_VER >= 10504
check(0); // Needs testing
Transform |= PNG_TRANSFORM_EXPAND_16
#else
// Expanding 8-bit images to 16-bit via transform needs a libpng update
check(0);
#endif
}
png_read_png(png_ptr, info_ptr, Transform, NULL);
}
RawFormat = InFormat;
RawBitDepth = InBitDepth;
}
/* FPngImageWrapper implementation
*****************************************************************************/
bool FPngImageWrapper::IsPNG() const
{
check( CompressedData.Num() );
const int32 PNGSigSize = sizeof(png_size_t);
if (CompressedData.Num() > PNGSigSize)
{
png_size_t PNGSignature = *reinterpret_cast<const png_size_t*>(CompressedData.GetData());
return (0 == png_sig_cmp(reinterpret_cast<png_bytep>(&PNGSignature), 0, PNGSigSize));
}
return false;
}
bool FPngImageWrapper::LoadPNGHeader()
{
check(CompressedData.Num());
// Test whether the data this PNGLoader is pointing at is a PNG or not.
if (IsPNG())
{
// thread safety
FScopeLock PNGLock(&GPNGSection);
png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, this, FPngImageWrapper::user_error_fn, FPngImageWrapper::user_warning_fn );
check(png_ptr);
png_infop info_ptr = png_create_info_struct( png_ptr );
check(info_ptr);
PNGReadGuard PNGGuard(&png_ptr, &info_ptr);
{
png_set_read_fn(png_ptr, this, FPngImageWrapper::user_read_compressed);
png_read_info(png_ptr, info_ptr);
Width = info_ptr->width;
Height = info_ptr->height;
ColorType = info_ptr->color_type;
BitDepth = info_ptr->bit_depth;
Channels = info_ptr->channels;
Format = (ColorType & PNG_COLOR_MASK_COLOR) ? ERGBFormat::RGBA : ERGBFormat::Gray;
}
return true;
}
return false;
}
/* FPngImageWrapper static implementation
*****************************************************************************/
void FPngImageWrapper::user_read_compressed( png_structp png_ptr, png_bytep data, png_size_t length )
{
FPngImageWrapper* ctx = (FPngImageWrapper*) png_get_io_ptr(png_ptr);
check(ctx->ReadOffset + length <= (uint32)ctx->CompressedData.Num());
FMemory::Memcpy(data, &ctx->CompressedData[ctx->ReadOffset], length);
ctx->ReadOffset += length;
}
void FPngImageWrapper::user_write_compressed( png_structp png_ptr, png_bytep data, png_size_t length )
{
FPngImageWrapper* ctx = (FPngImageWrapper*) png_get_io_ptr(png_ptr);
int32 Offset = ctx->CompressedData.AddUninitialized( length);
FMemory::Memcpy(&ctx->CompressedData[Offset], data, length);
}
void FPngImageWrapper::user_flush_data( png_structp png_ptr )
{
}
void FPngImageWrapper::user_error_fn( png_structp png_ptr, png_const_charp error_msg )
{
FPngImageWrapper* ctx = (FPngImageWrapper*)png_get_io_ptr( png_ptr );
FString ErrorMsg = ANSI_TO_TCHAR(error_msg);
ctx->SetError(*ErrorMsg);
UE_LOG(LogImageWrapper, Error, TEXT("PNG Error: %s"), *ErrorMsg);
}
void FPngImageWrapper::user_warning_fn( png_structp png_ptr, png_const_charp warning_msg )
{
UE_LOG(LogImageWrapper, Warning, TEXT("PNG Warning: %s"), ANSI_TO_TCHAR(warning_msg));
}
#endif //WITH_UNREALPNG

View File

@@ -0,0 +1,85 @@
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PngImageWrapper.h: Declares the FPngImageWrapper class.
=============================================================================*/
#pragma once
#if WITH_UNREALPNG
#include "ThirdParty/zlib/zlib-1.2.5/Inc/zlib.h"
#include "ThirdParty/libPNG/libpng-1.5.2/png.h"
#include "ThirdParty/libPNG/libpng-1.5.2/pnginfo.h"
/**
* PNG implementation of the helper class
*/
class FPngImageWrapper
: public FImageWrapperBase
{
public:
/**
* Default Constructor.
*/
FPngImageWrapper();
public:
// Begin FImageWrapper Interface
virtual void Compress( int32 Quality ) OVERRIDE;
virtual void Reset( ) OVERRIDE;
virtual bool SetCompressed( const void* InCompressedData, int32 InCompressedSize ) OVERRIDE;
virtual void Uncompress( const ERGBFormat::Type InFormat, int32 InBitDepth ) OVERRIDE;
// End FImageWrapper Interface
/**
* Query whether this is a valid PNG type.
*
* @return true if data a PNG
*/
bool IsPNG() const;
/**
* Load the header information, returns true if successful.
*
* @return true if successful
*/
bool LoadPNGHeader();
/** Helper function used to uncompress PNG data from a buffer */
void UncompressPNGData( const ERGBFormat::Type InFormat, const int32 InBitDepth );
protected:
// Callbacks for the pnglibs
static void user_read_compressed( png_structp png_ptr, png_bytep data, png_size_t length );
static void user_write_compressed( png_structp png_ptr, png_bytep data, png_size_t length );
static void user_flush_data( png_structp png_ptr );
static void user_error_fn( png_structp png_ptr, png_const_charp error_msg );
static void user_warning_fn( png_structp png_ptr, png_const_charp warning_msg );
private:
// The read offset into our array.
int32 ReadOffset;
// The color type as defined in the header.
int32 ColorType;
// The number of channels.
uint8 Channels;
};
#endif //WITH_UNREALPNG