You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Engine source (Main branch up to CL 2026164)
This commit is contained in:
203
Engine/Source/Developer/ImageWrapper/Private/BmpImageWrapper.cpp
Normal file
203
Engine/Source/Developer/ImageWrapper/Private/BmpImageWrapper.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
163
Engine/Source/Developer/ImageWrapper/Private/IcoImageWrapper.cpp
Normal file
163
Engine/Source/Developer/ImageWrapper/Private/IcoImageWrapper.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
110
Engine/Source/Developer/ImageWrapper/Private/ImageWrapperBase.h
Normal file
110
Engine/Source/Developer/ImageWrapper/Private/ImageWrapperBase.h
Normal 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;
|
||||
};
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
428
Engine/Source/Developer/ImageWrapper/Private/PngImageWrapper.cpp
Normal file
428
Engine/Source/Developer/ImageWrapper/Private/PngImageWrapper.cpp
Normal 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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user