Files
UnrealEngine/Engine/Plugins/Mutable/Source/MutableRuntime/Private/MuR/ImageRLE.h
2025-05-18 13:04:45 +08:00

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));
}
}
}