// Copyright Epic Games, Inc. All Rights Reserved. #include "MuR/ImageRLE.h" #include "HAL/UnrealMemory.h" #include "Misc/AssertionMacros.h" namespace mu { //--------------------------------------------------------------------------------------------- void CompressRLE_L( uint32& OutCompressedSize, int32 width, int32 rows, const uint8* pBaseData, uint8* destData, uint32 destDataSize ) { const uint8* InitialBaseData = pBaseData; uint8* rle = destData; uint32 maxSize = destDataSize; // The first uint32 will be the total mip size. // Then there is an offset from the initial data pointer for each line. uint32 offset = sizeof( uint32 ) * ( rows + 1 ); // Could happen in an image of 1x100, for example. if ( offset >= maxSize ) { OutCompressedSize = 0; return; } for ( int32 r = 0; r < rows; ++r ) { uint32* pOffset = (uint32*)&rle[sizeof( uint32 ) * ( r + 1 )]; FMemory::Memmove( pOffset, &offset, sizeof( uint32 ) ); const uint8* pBaseRowEnd = pBaseData + width; while ( pBaseData != pBaseRowEnd ) { // Count equal pixels uint8 equalPixel = *pBaseData; uint16 equal = 0; while ( pBaseData != pBaseRowEnd && equal < 65535 && pBaseData[0] == equalPixel ) { pBaseData++; equal++; } // Count different pixels uint8 different = 0; const uint8* pDifferentPixels = pBaseData; while ( pBaseData < pBaseRowEnd - 1 && different < 255 && // Last in the row, or different from next ( pBaseData == pBaseRowEnd || pBaseData[0] != pBaseData[1] ) ) { pBaseData++; different++; } // Copy header if ( maxSize < offset + 4 ) { OutCompressedSize = 0; return; } FMemory::Memmove( &rle[offset], &equal, sizeof( uint16 ) ); offset += 2; rle[offset] = different; ++offset; // Copy the equal pixel rle[offset] = equalPixel; ++offset; // Copy the different pixels if ( different ) { if ( maxSize < offset + different ) { OutCompressedSize = 0; return; } FMemory::Memmove( &rle[offset], pDifferentPixels, different ); offset += different; } } } uint32* pTotalSize = (uint32*)rle; *pTotalSize = offset; #ifdef MUTABLE_DEBUG_RLE { TArray Temp; Temp.SetNum(width*rows); UncompressRLE_L(width,rows,destData,Temp.GetData()); int32 Difference = FMemory::Memcmp(Temp.GetData(), InitialBaseData, width * rows); if (Difference) { // Different pos. SIZE_T Delta = 0; for ( ; Delta 0) check(false); } } #endif // succeded OutCompressedSize = offset; } uint32 UncompressRLE_L( int32 width, int32 rows, const uint8* pStartBaseData, uint8* pStartDestData ) { // Empty data. if (!pStartBaseData) { FMemory::Memset(pStartDestData, 0, width * rows); return 0; } const uint8* pBaseData = pStartBaseData; uint8* pDestData = pStartDestData; pBaseData += sizeof(uint32); // Total mip size pBaseData += rows*sizeof(uint32); // Size of each row. for ( int32 r=0; r rle; rle.Reserve( (width * rows) / 2 ); uint32 offset = sizeof(uint32)*(rows+1); rle.SetNum( offset, EAllowShrinking::No ); for ( int32 r=0; r rle; rle.Reserve( (width*rows) ); const uint32* pBaseData = (const uint32*)pBaseDataByte; rle.SetNum( rows*4, EAllowShrinking::No); uint32 offset = sizeof(uint32)*rows; for ( int32 r=0; rpBaseRowEnd-4 || pBaseData[0]!=pBaseData[1] || pBaseData[0]!=pBaseData[2] || pBaseData[0]!=pBaseData[3] ) ) { pBaseData += FMath::Min(int64(4), int64(pBaseRowEnd - pBaseData)); different++; } // Copy header rle.SetNum( rle.Num()+8, EAllowShrinking::No); FMemory::Memmove(&rle[ offset ], &equal, sizeof(uint16)); offset += 2; FMemory::Memmove(&rle[ offset ], &different, sizeof(uint16)); offset += 2; FMemory::Memmove(&rle[ offset ], &equalPixel, sizeof(uint32)); offset += 4; // Copy the different pixels if (different) { // If we are at the end of a row, maybe there isn't a block of 4 pixels uint16 BytesToCopy = FMath::Min(different * 4 * 4, uint16(pBaseRowEnd - pDifferentPixels) * 4); rle.SetNum( rle.Num()+ BytesToCopy, EAllowShrinking::No); FMemory::Memmove( &rle[offset], pDifferentPixels, BytesToCopy); offset += BytesToCopy; } } if (offset > DestDataSize) { break; } } OutCompressedSize = DestDataSize >= offset ? offset : 0; if (OutCompressedSize > 0) { FMemory::Memmove(DestData, rle.GetData(), offset); } } void UncompressRLE_RGBA( int32 width, int32 rows, const uint8* pBaseData, uint8* pDestDataB ) { uint32* pDestData = reinterpret_cast( pDestDataB ); pBaseData += rows*sizeof(uint32); int32 pendingPixels = width*rows; for ( int32 r=0; r rle; rle.Reserve(width*rows); const UINT24* pBaseData = (const UINT24*)pBaseDataByte; rle.SetNum( rows*4, EAllowShrinking::No ); uint32 offset = sizeof(uint32)*rows; for ( int32 r=0; rpBaseRowEnd-4 || pBaseData[0]!=pBaseData[1] || pBaseData[0]!=pBaseData[2] || pBaseData[0]!=pBaseData[3] ) ) { pBaseData+=FMath::Min(int64(4), int64(pBaseRowEnd-pBaseData)); different++; } // Copy header rle.SetNum( rle.Num()+8, EAllowShrinking::No); FMemory::Memmove( &rle[offset], &equal, sizeof(uint16) ); offset += 2; FMemory::Memmove( &rle[offset], &different, sizeof(uint16) ); offset += 2; FMemory::Memmove( &rle[offset], &equalPixel, sizeof(UINT24) ); offset += 4; // Copy the different pixels if (different) { // If we are at the end of a row, maybe there isn't a block of 4 pixels uint16 BytesToCopy = FMath::Min(different * 4 * 3, uint16(pBaseRowEnd- pDifferentPixels)*3 ); rle.SetNum( rle.Num()+BytesToCopy, EAllowShrinking::No ); FMemory::Memmove( &rle[offset], pDifferentPixels, BytesToCopy ); offset += BytesToCopy; } } if (offset > DestDataSize) { break; } } OutCompressedSize = DestDataSize >= offset ? offset : 0; if (OutCompressedSize > 0) { FMemory::Memmove(DestData, rle.GetData(), offset); } } void UncompressRLE_RGB(int32 width, int32 rows, const uint8* pBaseData, uint8* pDestDataB) { check(pBaseData && pDestDataB); UINT24* pDestData = reinterpret_cast( pDestDataB ); pBaseData += rows*sizeof(uint32); for ( int32 r=0; r