304 lines
12 KiB
C++
304 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Containers/Array.h"
|
|
#include "Math/UnrealMathSSE.h"
|
|
#include "MuR/Image.h"
|
|
/** This define enabled additional checks when using RLE compression. These checks have a lot of
|
|
* of overhead so it should usually be disabled.
|
|
*/
|
|
//#define MUTABLE_DEBUG_RLE
|
|
|
|
|
|
namespace mu
|
|
{
|
|
//---------------------------------------------------------------------------------------------
|
|
//! Compress to RLE if the compressed image fits in destData, which should be preallocated.
|
|
//! If it doesn't fit, it returns 0, and the content of destData is undefined.
|
|
//! This method doesn't allocate any memory.
|
|
//!
|
|
//! This RLE format compresses the data per row. At the beginning of the data there is the
|
|
//! total compressed data size (uint32_t) followed by the row offsets for direct access.
|
|
//! Every offset is a uint32_t starting at the begining of the compressed data.
|
|
//! Every row consists of a series of header + data. The headers is:
|
|
//! - a UINT16 with how many equal pixels are there
|
|
//! - a UINT8 of how many different pixels follow the equal pixels
|
|
//! - the value of the "equal" pixels
|
|
//! After the header there are as many pixel values as different pixels.
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void CompressRLE_L(uint32& OutCompressedSize, int32 Width, int32 Rows,
|
|
const uint8* BaseData,
|
|
uint8* DestData, uint32 DestDataSize);
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern uint32 UncompressRLE_L(int32 Width, int32 Rows,
|
|
const uint8* BaseData,
|
|
uint8* pDestData);
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
//! This RLE format compresses the data per row. At the beginning of the data there are the row
|
|
//! offsets for direct access.
|
|
//! Every row consists of a series of blocks. The blocks are:
|
|
//! - a UINT16 with how many 0 pixels are there
|
|
//! - a UINT16 with how many 1 pixels follow the 0 pixels
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void CompressRLE_L1(uint32& OutCompressedSize, int32 Width, int32 Rows,
|
|
const uint8* BaseData,
|
|
uint8* DestData,
|
|
uint32 DestDataSize);
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern uint32 UncompressRLE_L1(int32 Width, int32 Rows,
|
|
const uint8* pBaseData,
|
|
uint8* pDestData);
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
//! This RLE format compresses the data per row. At the beginning of the data there are the row
|
|
//! offsets for direct access. Every offset is a uint32_t.
|
|
//! Every row consists of a series of header + data. The headers is:
|
|
//! - a UINT16 with how many equal pixels / 4
|
|
//! - a UINT16 with how many different pixels / 4
|
|
//! - 4 UINT8 with the value of the "equal" pixels
|
|
//! After the header there are as many pixel values as different pixels.
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void CompressRLE_RGBA(uint32& OutCompressedSize, int32 Width, int32 Rows,
|
|
const uint8* pBaseDataByte,
|
|
uint8* DestData, uint32 DestDataSize);
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void UncompressRLE_RGBA(int32 Width, int32 Rows,
|
|
const uint8* pBaseData,
|
|
uint8* pDestDataB);
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
//! This RLE format compresses the data per row. At the beginning of the data there are the row
|
|
//! offsets for direct access. Every offset is a uint32_t.
|
|
//! Every row consists of a series of header + data. The headers is:
|
|
//! - a UINT16 with how many equal pixels / 4
|
|
//! - a UINT16 with how many different pixels / 4
|
|
//! - 4 UINT8 with the value of the "equal" pixels
|
|
//! After the header there are as many pixel values as different pixels.
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void CompressRLE_RGB(uint32& OutCompressedSize, int32 Width, int32 Rows,
|
|
const uint8* pBaseDataByte,
|
|
uint8* DestData, uint32 DestDataSize);
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
MUTABLERUNTIME_API extern void UncompressRLE_RGB(int32 Width, int32 Rows,
|
|
const uint8* pBaseData,
|
|
uint8* pDestDataB);
|
|
|
|
|
|
inline void UncompressRLE_L(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
uint32 CompressedSize = UncompressRLE_L(SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L));
|
|
|
|
SizeX = FMath::Max(1, FMath::DivideAndRoundUp(SizeX, 2));
|
|
SizeY = FMath::Max(1, FMath::DivideAndRoundUp(SizeY, 2));
|
|
}
|
|
}
|
|
|
|
inline void CompressRLE_L(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
bool bSuccess = false;
|
|
while (!bSuccess)
|
|
{
|
|
uint32 CompressedSize = 0;
|
|
CompressRLE_L(CompressedSize, SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L), DestImage->GetLODDataSize(L));
|
|
|
|
bSuccess = CompressedSize > 0;
|
|
if (bSuccess)
|
|
{
|
|
DestImage->DataStorage.ResizeLOD(L, CompressedSize);
|
|
}
|
|
else
|
|
{
|
|
const int32 MipMemorySize = DestImage->DataStorage.GetLOD(L).Num();
|
|
DestImage->DataStorage.ResizeLOD(L, FMath::Max(4, MipMemorySize * 2));
|
|
}
|
|
}
|
|
|
|
SizeX = FMath::DivideAndRoundUp(SizeX, 2);
|
|
SizeY = FMath::DivideAndRoundUp(SizeY, 2);
|
|
}
|
|
}
|
|
|
|
inline void CompressRLE_L1(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
bool bSuccess = false;
|
|
while (!bSuccess)
|
|
{
|
|
uint32 CompressedSize = 0;
|
|
CompressRLE_L1(CompressedSize, SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L), DestImage->GetLODDataSize(L));
|
|
|
|
bSuccess = CompressedSize > 0;
|
|
if (bSuccess)
|
|
{
|
|
DestImage->DataStorage.ResizeLOD(L, CompressedSize);
|
|
}
|
|
else
|
|
{
|
|
const int32 MipMemorySize = DestImage->DataStorage.GetLOD(L).Num();
|
|
DestImage->DataStorage.ResizeLOD(L, FMath::Max(4, MipMemorySize * 2));
|
|
}
|
|
}
|
|
|
|
SizeX = FMath::DivideAndRoundUp(SizeX, 2);
|
|
SizeY = FMath::DivideAndRoundUp(SizeY, 2);
|
|
}
|
|
}
|
|
|
|
inline void UncompressRLE_L1(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
uint32 CompressedSize = UncompressRLE_L1(SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L));
|
|
|
|
SizeX = FMath::Max(1, FMath::DivideAndRoundUp(SizeX, 2));
|
|
SizeY = FMath::Max(1, FMath::DivideAndRoundUp(SizeY, 2));
|
|
}
|
|
}
|
|
|
|
inline void CompressRLE_RGB(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
bool bSuccess = false;
|
|
while (!bSuccess)
|
|
{
|
|
uint32 CompressedSize = 0;
|
|
CompressRLE_RGB(CompressedSize, SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L), DestImage->GetLODDataSize(L));
|
|
|
|
bSuccess = CompressedSize > 0;
|
|
if (bSuccess)
|
|
{
|
|
DestImage->DataStorage.ResizeLOD(L, CompressedSize);
|
|
}
|
|
else
|
|
{
|
|
const int32 MipMemorySize = DestImage->DataStorage.GetLOD(L).Num();
|
|
DestImage->DataStorage.ResizeLOD(L, FMath::Max(4, MipMemorySize * 2));
|
|
}
|
|
}
|
|
|
|
SizeX = FMath::DivideAndRoundUp(SizeX, 2);
|
|
SizeY = FMath::DivideAndRoundUp(SizeY, 2);
|
|
}
|
|
}
|
|
|
|
inline void UncompressRLE_RGB(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
UncompressRLE_RGB(SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L));
|
|
|
|
SizeX = FMath::Max(1, FMath::DivideAndRoundUp(SizeX, 2));
|
|
SizeY = FMath::Max(1, FMath::DivideAndRoundUp(SizeY, 2));
|
|
}
|
|
}
|
|
|
|
inline void CompressRLE_RGBA(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
bool bSuccess = false;
|
|
while (!bSuccess)
|
|
{
|
|
uint32 CompressedSize = 0;
|
|
CompressRLE_RGBA(CompressedSize, SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L), DestImage->GetLODDataSize(L));
|
|
|
|
bSuccess = CompressedSize > 0;
|
|
if (bSuccess)
|
|
{
|
|
DestImage->DataStorage.ResizeLOD(L, CompressedSize);
|
|
}
|
|
else
|
|
{
|
|
const int32 MipMemorySize = DestImage->DataStorage.GetLOD(L).Num();
|
|
DestImage->DataStorage.ResizeLOD(L, FMath::Max(4, MipMemorySize * 2));
|
|
}
|
|
}
|
|
|
|
SizeX = FMath::DivideAndRoundUp(SizeX, 2);
|
|
SizeY = FMath::DivideAndRoundUp(SizeY, 2);
|
|
}
|
|
}
|
|
|
|
inline void UncompressRLE_RGBA(const FImage* BaseImage, FImage* DestImage)
|
|
{
|
|
check(DestImage->GetLODCount() <= BaseImage->GetLODCount());
|
|
check(DestImage->GetSize() == BaseImage->GetSize());
|
|
|
|
int32 SizeX = BaseImage->GetSizeX();
|
|
int32 SizeY = BaseImage->GetSizeY();
|
|
|
|
const int32 NumLODs = DestImage->GetLODCount();
|
|
for (int32 L = 0; L < NumLODs; ++L)
|
|
{
|
|
UncompressRLE_RGBA(SizeX, SizeY, BaseImage->GetLODData(L), DestImage->GetLODData(L));
|
|
|
|
SizeX = FMath::Max(1, FMath::DivideAndRoundUp(SizeX, 2));
|
|
SizeY = FMath::Max(1, FMath::DivideAndRoundUp(SizeY, 2));
|
|
}
|
|
}
|
|
|
|
}
|