2021-05-17 08:46:26 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Formats/HdrImageWrapper.h"
2022-03-15 18:29:37 -04:00
# include "ImageWrapperPrivate.h"
2021-05-17 08:46:26 -04:00
# define LOCTEXT_NAMESPACE "HdrImageWrapper"
namespace UE : : ImageWrapper : : Private : : HdrImageWrapper
{
2022-10-12 11:19:42 -04:00
FText GetMalformedHeaderErrorMessage ( )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return LOCTEXT ( " MalformedHeader " , " The file header is malformed. The HDR image is likely corrupted. " ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
FText GetEndOfBufferErrorMessage ( )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return LOCTEXT ( " EndOFBufferError " , " Reached the end of the buffer before finishing decompressing the HDR. The HDR image is likely corrupted. " ) ;
}
FText GetMalformedScanlineErrorMessage ( )
{
return LOCTEXT ( " MalformedScanline " , " Compressed data for HDR scanline is malformed. The HDR image is likely to be corrupted. " ) ;
2021-05-17 08:46:26 -04:00
}
}
bool FHdrImageWrapper : : SetCompressedFromView ( TArrayView64 < const uint8 > Data )
{
2022-03-15 18:29:37 -04:00
// CompressedData is just a View, does not take a copy
2021-05-17 08:46:26 -04:00
CompressedData = Data ;
if ( CompressedData . Num ( ) < 11 )
{
2022-10-12 11:19:42 -04:00
return FailHeaderParsing ( ) ;
2021-05-17 08:46:26 -04:00
}
const uint8 * FileDataPtr = CompressedData . GetData ( ) ;
char Line [ 256 ] ;
2022-10-12 11:19:42 -04:00
if ( ! GetHeaderLine ( FileDataPtr , Line ) )
{
return FailHeaderParsing ( ) ;
}
2021-05-17 08:46:26 -04:00
2022-03-15 18:29:37 -04:00
if ( FCStringAnsi : : Strcmp ( Line , " #?RADIANCE " ) ! = 0 & &
FCStringAnsi : : Strcmp ( Line , " #?RGBE " ) ! = 0 )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailHeaderParsing ( ) ;
}
// Read header lines: free-form, keep going until we hit a blank line
bool bHasFormat = false ;
for ( ; ; )
{
if ( ! GetHeaderLine ( FileDataPtr , Line ) )
{
return FailHeaderParsing ( ) ;
}
// Blank line denotes end of header
if ( ! Line [ 0 ] )
break ;
const char * Cursor = Line ;
// Format specified?
if ( ParseMatchString ( Cursor , " FORMAT= " ) )
{
bHasFormat = true ;
// Currently we only support RGBE
if ( FCStringAnsi : : Strcmp ( Cursor , " 32-bit_rle_rgbe " ) ! = 0 )
{
SetAndLogError ( LOCTEXT ( " WrongFormatError " , " The HDR image uses an unsupported format. Only the 32-bit_rle_rgbe format is supported. " ) ) ;
FreeCompressedData ( ) ;
return false ;
}
}
}
// If we got through the header without it mentioning a format, the file is malformed.
if ( ! bHasFormat )
{
return FailHeaderParsing ( ) ;
}
// Read one more line which specifies the resolution
if ( ! GetHeaderLine ( FileDataPtr , Line ) )
{
return FailHeaderParsing ( ) ;
}
// If we allow enormous images, we risk int overflows on the size calcs, even with int64s.
// As of this writing (Oct 2022) there is no demand for reading huge HDR files, and allocating
// memory to enormous images is going to make the editor choke besides. Limiting
// pixel counts to 65536*65536 (which caps images at 16GiB) seems reasonable for now.
const int MaxImageDimension = 65536 ;
int ImageWidth ;
int ImageHeight ;
if ( ! ParseImageSize ( Line , & ImageWidth , & ImageHeight ) | |
ImageWidth < = 0 | | ImageWidth > MaxImageDimension | |
ImageHeight < = 0 | | ImageHeight > MaxImageDimension )
{
// If we don't like the resolution line (our parser is very strict), log what it was
// as a breadcrumb for debugging.
FString BadResolutionLine ( Line ) ;
UE_LOG ( LogImageWrapper , Display , TEXT ( " HDR bad resolution line was: \" %s \" " ) , * BadResolutionLine ) ;
SetAndLogError ( LOCTEXT ( " InvalidSizeError " , " The HDR image specifies an invalid size. " ) ) ;
2021-05-17 08:46:26 -04:00
FreeCompressedData ( ) ;
return false ;
}
2022-10-12 11:19:42 -04:00
Width = ImageWidth ;
Height = ImageHeight ;
RGBDataStart = FileDataPtr ;
return true ;
2021-05-17 08:46:26 -04:00
}
bool FHdrImageWrapper : : SetCompressed ( const void * InCompressedData , int64 InCompressedSize )
{
2022-03-15 18:29:37 -04:00
// takes copy
2021-05-17 08:46:26 -04:00
CompressedDataHolder . Reset ( InCompressedSize ) ;
2022-10-12 11:19:42 -04:00
CompressedDataHolder . Append ( ( const uint8 * ) InCompressedData , InCompressedSize ) ;
2021-05-17 08:46:26 -04:00
return SetCompressedFromView ( MakeArrayView ( CompressedDataHolder ) ) ;
}
2022-03-15 18:29:37 -04:00
// CanSetRawFormat returns true if SetRaw will accept this format
bool FHdrImageWrapper : : CanSetRawFormat ( const ERGBFormat InFormat , const int32 InBitDepth ) const
2021-05-17 08:46:26 -04:00
{
2022-03-15 18:29:37 -04:00
return InFormat = = ERGBFormat : : BGRE & & InBitDepth = = 8 ;
2021-05-17 08:46:26 -04:00
}
2022-03-15 18:29:37 -04:00
// returns InFormat if supported, else maps to something supported
ERawImageFormat : : Type FHdrImageWrapper : : GetSupportedRawFormat ( const ERawImageFormat : : Type InFormat ) const
2021-05-17 08:46:26 -04:00
{
2022-03-15 18:29:37 -04:00
// only writes one format :
return ERawImageFormat : : BGRE8 ;
}
bool FHdrImageWrapper : : SetRaw ( const void * InRawData , int64 InRawSize , const int32 InWidth , const int32 InHeight , const ERGBFormat InFormat , const int32 InBitDepth , const int32 InBytesPerRow )
{
if ( ! CanSetRawFormat ( InFormat , InBitDepth ) )
{
UE_LOG ( LogImageWrapper , Warning , TEXT ( " ImageWrapper unsupported format; check CanSetRawFormat; %d x %d " ) , ( int ) InFormat , InBitDepth ) ;
return false ;
}
2022-10-12 11:19:42 -04:00
if ( InWidth < = 0 | | InHeight < = 0 )
{
UE_LOG ( LogImageWrapper , Warning , TEXT ( " ImageWrapper HDR unsupported size %d x %d " ) , InWidth , InHeight ) ;
return false ;
}
2022-03-15 18:29:37 -04:00
RawDataHolder . Empty ( InRawSize ) ;
RawDataHolder . Append ( ( const uint8 * ) InRawData , InRawSize ) ;
Width = InWidth ;
Height = InHeight ;
check ( InFormat = = ERGBFormat : : BGRE ) ;
check ( InBitDepth = = 8 ) ;
return true ;
2021-05-17 08:46:26 -04:00
}
TArray64 < uint8 > FHdrImageWrapper : : GetCompressed ( int32 Quality )
{
2022-03-15 18:29:37 -04:00
// must have set BGRE8 raw data :
2022-10-12 11:19:42 -04:00
int64 NumPixels = ( int64 ) Width * Height ;
2022-03-15 18:29:37 -04:00
check ( RawDataHolder . Num ( ) = = NumPixels * 4 ) ;
char Header [ MAX_SPRINTF ] ;
2022-10-12 11:19:42 -04:00
int32 HeaderLen = FCStringAnsi : : Sprintf ( Header , " #?RADIANCE \n FORMAT=32-bit_rle_rgbe \n \n -Y %d +X %d \n " , Height , Width ) ;
if ( HeaderLen < = 0 ) // Error during Sprintf for whatever reason?
{
return TArray64 < uint8 > ( ) ;
}
2022-03-15 18:29:37 -04:00
FreeCompressedData ( ) ;
TArray64 < uint8 > CompressedDataArray ;
2022-10-12 11:19:42 -04:00
CompressedDataArray . SetNumUninitialized ( HeaderLen + NumPixels * 4 ) ;
2022-03-15 18:29:37 -04:00
uint8 * CompressedPtr = CompressedDataArray . GetData ( ) ;
memcpy ( CompressedPtr , Header , HeaderLen ) ;
CompressedPtr + = HeaderLen ;
// just put the BGRE8 bytes
2022-10-12 11:19:42 -04:00
// but need to swizzle to RGBE8 order:
const uint8 * FromBytes = RawDataHolder . GetData ( ) ;
2022-03-15 18:29:37 -04:00
for ( int64 i = 0 ; i < NumPixels ; i + + )
{
2022-10-12 11:19:42 -04:00
CompressedPtr [ i * 4 + 0 ] = FromBytes [ i * 4 + 2 ] ;
CompressedPtr [ i * 4 + 1 ] = FromBytes [ i * 4 + 1 ] ;
CompressedPtr [ i * 4 + 2 ] = FromBytes [ i * 4 + 0 ] ;
CompressedPtr [ i * 4 + 3 ] = FromBytes [ i * 4 + 3 ] ;
2022-03-15 18:29:37 -04:00
}
return MoveTemp ( CompressedDataArray ) ;
2021-05-17 08:46:26 -04:00
}
bool FHdrImageWrapper : : GetRaw ( const ERGBFormat InFormat , int32 InBitDepth , TArray64 < uint8 > & OutRawData )
{
if ( InFormat ! = ERGBFormat : : BGRE | | InBitDepth ! = 8 )
{
2022-10-12 11:19:42 -04:00
SetAndLogError ( LOCTEXT ( " UnSupportedFormatORBitDepth " , " The format and/or the bit depth is not supported by the HdrImageWrapper. Only the BGRE format and a bitdepth of 8 is supported " ) ) ;
2021-05-17 08:46:26 -04:00
return false ;
}
if ( ! IsCompressedImageValid ( ) )
{
return false ;
}
2022-10-12 11:19:42 -04:00
const int64 SizeRawImageInBytes = ( int64 ) Width * Height * 4 ;
2021-05-17 08:46:26 -04:00
OutRawData . Reset ( SizeRawImageInBytes ) ;
OutRawData . AddUninitialized ( SizeRawImageInBytes ) ;
const uint8 * FileDataPtr = RGBDataStart ;
2022-10-12 11:19:42 -04:00
const uint8 * FileDataEnd = CompressedData . GetData ( ) + CompressedData . Num ( ) ;
2021-05-17 08:46:26 -04:00
for ( int32 Y = 0 ; Y < Height ; + + Y )
{
2022-10-12 11:19:42 -04:00
if ( ! DecompressScanline ( & ( OutRawData [ ( int64 ) Width * Y * 4 ] ) , FileDataPtr , FileDataEnd ) )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
OutRawData . Empty ( ) ;
2021-05-17 08:46:26 -04:00
return false ;
}
}
return true ;
}
int32 FHdrImageWrapper : : GetWidth ( ) const
{
return Width ;
}
int32 FHdrImageWrapper : : GetHeight ( ) const
{
return Height ;
}
int32 FHdrImageWrapper : : GetBitDepth ( ) const
{
return 8 ;
}
ERGBFormat FHdrImageWrapper : : GetFormat ( ) const
{
return ERGBFormat : : BGRE ;
}
const FText & FHdrImageWrapper : : GetErrorMessage ( ) const
{
return ErrorMessage ;
}
2022-10-12 11:19:42 -04:00
void FHdrImageWrapper : : SetAndLogError ( const FText & InText )
{
ErrorMessage = InText ;
UE_LOG ( LogImageWrapper , Error , TEXT ( " %s " ) , * InText . ToString ( ) ) ;
}
bool FHdrImageWrapper : : FailHeaderParsing ( )
{
SetAndLogError ( UE : : ImageWrapper : : Private : : HdrImageWrapper : : GetMalformedHeaderErrorMessage ( ) ) ;
FreeCompressedData ( ) ;
return false ;
}
bool FHdrImageWrapper : : FailUnexpectedEOB ( )
{
SetAndLogError ( UE : : ImageWrapper : : Private : : HdrImageWrapper : : GetEndOfBufferErrorMessage ( ) ) ;
return false ;
}
bool FHdrImageWrapper : : FailMalformedScanline ( )
{
SetAndLogError ( UE : : ImageWrapper : : Private : : HdrImageWrapper : : GetMalformedScanlineErrorMessage ( ) ) ;
return false ;
}
2021-05-17 08:46:26 -04:00
bool FHdrImageWrapper : : GetHeaderLine ( const uint8 * & BufferPos , char Line [ 256 ] )
{
const uint8 * EndOfBuffer = CompressedData . GetData ( ) + CompressedData . Num ( ) ;
2022-10-12 11:19:42 -04:00
for ( int i = 0 ; i < 256 ; + + i )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
// May never read past end of buffer
check ( BufferPos < = EndOfBuffer ) ; // == may happen, > is a bug.
2021-05-17 08:46:26 -04:00
if ( BufferPos > = EndOfBuffer )
{
return false ;
}
2022-10-12 11:19:42 -04:00
if ( * BufferPos = = 0 )
{
// We don't allow NUL characters in header, that's malformed
return false ;
}
else if ( * BufferPos = = ' \n ' ) // Line break -> end of line in HDR (but CRs are not significant!)
{
// Terminate the line, we're good to go, but do consume the newline.
// We insert the terminating 0 here, and this is the only path where we return true,
// thus guaranteeing that successfully returned lines are 0-terminated.
BufferPos + + ;
Line [ i ] = 0 ;
return true ;
}
// All other characters go into the line verbatim.
Line [ i ] = * BufferPos + + ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
// Falling out of the loop after reading 256 chars, we consider a malformed line.
return false ;
}
2021-05-17 08:46:26 -04:00
2022-10-12 11:19:42 -04:00
// InOutCursor points to current parse cursor, into a 0-terminated string.
// InExpected is a 0-terminated string that we expect an exact match to.
//
// On success, return true and point InOutCursor at the end of the matched string.
// On failure, return false and leave InOutCursor where it is.
bool FHdrImageWrapper : : ParseMatchString ( const char * & InOutCursor , const char * InExpected )
{
const char * Cursor = InOutCursor ;
while ( * InExpected )
{
if ( * Cursor ! = * InExpected )
{
return false ;
}
// The two bytes match, advance.
+ + Cursor ;
+ + InExpected ;
}
InOutCursor = Cursor ;
2021-05-17 08:46:26 -04:00
return true ;
}
2022-10-12 11:19:42 -04:00
// Like atoi, but we return the final cursor, have an error reporting mechanism
// for overflows, and don't accept signed numbers (or '+' signs for that matter).
//
// InOutCursor points into a properly 0-terminated string, so we can just keep
// reading while chars are between '0' and '9' (since NUL is not).
bool FHdrImageWrapper : : ParsePositiveInt ( const char * & InOutCursor , int * OutValue )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
// We require the string to start with a digit
if ( * InOutCursor < ' 0 ' | | * InOutCursor > ' 9 ' )
{
return false ;
}
int Value = * InOutCursor - ' 0 ' ; // can't overflow.
+ + InOutCursor ;
// Keep consuming digits in the digit string
while ( * InOutCursor > = ' 0 ' & & * InOutCursor < = ' 9 ' )
{
int Digit = * InOutCursor - ' 0 ' ;
+ + InOutCursor ;
int64 NewValue = ( int64 ) Value * 10 + Digit ;
// Overflow?
if ( NewValue > TNumericLimits < int > : : Max ( ) )
{
return false ;
}
// We just checked it's in range.
Value = ( int ) NewValue ;
}
* OutValue = Value ;
return true ;
}
// InLine is known 0-terminated.
bool FHdrImageWrapper : : ParseImageSize ( const char * InLine , int * OutWidth , int * OutHeight )
{
// We only support the (default) -Y <height> +X <width> form of the image size.
if ( ! ParseMatchString ( InLine , " -Y " ) )
{
return false ;
}
if ( ! ParsePositiveInt ( InLine , OutHeight ) )
{
return false ;
}
if ( ! ParseMatchString ( InLine , " +X " ) )
{
return false ;
}
if ( ! ParsePositiveInt ( InLine , OutWidth ) )
{
return false ;
}
return * InLine = = 0 ; // All bytes in line must be consumed
}
// Trivial helper func to make repetitive error checks easier to read.
// InCursor <= InEnd is assumed; check that we can read InAmount more bytes without
// going past InEnd.
bool FHdrImageWrapper : : HaveBytes ( const uint8 * InCursor , const uint8 * InEnd , int InAmount )
{
// InCursor <= InEnd; InEnd - InCursor gives us the number of bytes left.
// (Do it this way instead of "InCursor + InAmount <= InEnd" because the latter
// might overflow.)
return InEnd - InCursor > = InAmount ;
}
bool FHdrImageWrapper : : DecompressScanline ( uint8 * Out , const uint8 * & In , const uint8 * InEnd )
{
// minimum and maximum scanline length for encoding
2021-05-17 08:46:26 -04:00
const int32 MINELEN = 8 ;
const int32 MAXELEN = 0x7fff ;
2022-10-12 11:19:42 -04:00
if ( Width < MINELEN | | Width > MAXELEN )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return OldDecompressScanline ( Out , In , InEnd , Width , false ) ;
}
if ( ! HaveBytes ( In , InEnd , 1 ) )
{
return false ;
2021-05-17 08:46:26 -04:00
}
uint8 Red = * In ;
if ( Red ! = 2 )
{
2022-10-12 11:19:42 -04:00
return OldDecompressScanline ( Out , In , InEnd , Width , false ) ;
2021-05-17 08:46:26 -04:00
}
+ + In ;
2022-10-12 11:19:42 -04:00
if ( ! HaveBytes ( In , InEnd , 3 ) )
{
return false ;
}
2021-05-17 08:46:26 -04:00
uint8 Green = * In + + ;
uint8 Blue = * In + + ;
2022-10-12 11:19:42 -04:00
uint8 Exponent = * In + + ;
2021-05-17 08:46:26 -04:00
2022-10-12 11:19:42 -04:00
if ( Green ! = 2 | | ( Blue & 128 ) )
2021-05-17 08:46:26 -04:00
{
* Out + + = Blue ;
* Out + + = Green ;
* Out + + = Red ;
2022-10-12 11:19:42 -04:00
* Out + + = Exponent ;
return OldDecompressScanline ( Out , In , InEnd , Width - 1 , true ) ;
2021-05-17 08:46:26 -04:00
}
for ( uint32 ChannelRead = 0 ; ChannelRead < 4 ; + + ChannelRead )
{
// The file is in RGBE but we decompress in BGRE So swap the red and blue
uint8 CurrentToWrite = ChannelRead ;
if ( ChannelRead = = 0 )
{
CurrentToWrite = 2 ;
}
else if ( ChannelRead = = 2 )
{
CurrentToWrite = 0 ;
}
2022-10-12 11:19:42 -04:00
const uint8 * LocalIn = In ;
2021-05-17 08:46:26 -04:00
uint8 * OutSingleChannel = Out + CurrentToWrite ;
int32 MultiRunIndex = 0 ;
2022-10-12 11:19:42 -04:00
2021-05-17 08:46:26 -04:00
while ( MultiRunIndex < Width )
{
2022-10-12 11:19:42 -04:00
if ( ! HaveBytes ( LocalIn , InEnd , 1 ) )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailUnexpectedEOB ( ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
uint8 Current = * LocalIn + + ;
2021-05-17 08:46:26 -04:00
if ( Current > 128 )
{
2022-10-12 11:19:42 -04:00
// Actual run
int Count = Current & 0x7f ;
2021-05-17 08:46:26 -04:00
2022-10-12 11:19:42 -04:00
if ( ! HaveBytes ( LocalIn , InEnd , 1 ) )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailUnexpectedEOB ( ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
Current = * LocalIn + + ;
2021-05-17 08:46:26 -04:00
2022-10-12 11:19:42 -04:00
// Run needs to stay within scan line.
if ( Width - MultiRunIndex < Count )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailMalformedScanline ( ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
for ( int RunIndex = 0 ; RunIndex < Count ; + + RunIndex )
2021-05-17 08:46:26 -04:00
{
* OutSingleChannel = Current ;
OutSingleChannel + = 4 ;
}
MultiRunIndex + = Count ;
}
else
{
2022-10-12 11:19:42 -04:00
// Literal run.
int Count = Current ;
2021-05-17 08:46:26 -04:00
2022-10-12 11:19:42 -04:00
// Do one check up front whether we have enough data bytes following
if ( ! HaveBytes ( LocalIn , InEnd , Count ) )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailUnexpectedEOB ( ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
// Literal run needs to stay within scan line.
if ( Width - MultiRunIndex < Count )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailMalformedScanline ( ) ;
}
for ( int RunIndex = 0 ; RunIndex < Count ; + + RunIndex )
{
// All buffer checks were done up front.
* OutSingleChannel = * LocalIn + + ;
2021-05-17 08:46:26 -04:00
OutSingleChannel + = 4 ;
}
MultiRunIndex + = Count ;
}
}
2022-10-12 11:19:42 -04:00
In = LocalIn ;
2021-05-17 08:46:26 -04:00
}
return true ;
}
2022-10-12 11:19:42 -04:00
bool FHdrImageWrapper : : OldDecompressScanline ( uint8 * Out , const uint8 * & InCodedScanline , const uint8 * InEnd , int32 Length , bool bInitialRunAllowed )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
const uint8 * In = InCodedScanline ; // Copy to local var
2021-05-17 08:46:26 -04:00
int32 Shift = 0 ;
2022-10-12 11:19:42 -04:00
// If an initial run is not allowed, set Shift to 32, which will make us fail if the first thing we
// see in this scanline is a run, but will be cleared after the first non-run pixel.
if ( ! bInitialRunAllowed )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
Shift = 32 ;
}
while ( Length > 0 )
{
if ( ! HaveBytes ( In , InEnd , 4 ) )
{
return FailUnexpectedEOB ( ) ;
}
2021-05-17 08:46:26 -04:00
uint8 Red = * In + + ;
uint8 Green = * In + + ;
uint8 Blue = * In + + ;
uint8 Exponent = * In + + ;
2022-10-12 11:19:42 -04:00
if ( Red = = 1 & & Green = = 1 & & Blue = = 1 )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
// It's not illegal to hit Shift=32 (say after a non-trivial run with Shift=24 in a giant image),
// but since we have a 32-bit width limit, there is just no legitimate reason to have another run
// once Shift=32, it should always be followed by literals.
//
// We also use this to catch runs at the start of a scanline when there's no pixels to repeat yet,
// by initializing Shift=32 on the first pixel of a scanline (we sometimes get called with one pixel
// already decoded, so this is conditional).
if ( Shift > = 32 )
2021-05-17 08:46:26 -04:00
{
2022-10-12 11:19:42 -04:00
return FailMalformedScanline ( ) ;
2021-05-17 08:46:26 -04:00
}
2022-10-12 11:19:42 -04:00
// Doing Count calculation in 64 bits to avoid overflow concerns when Shift=24.
int64 Count = ( int64 ) Exponent < < Shift ;
if ( Count > Length )
{
return FailMalformedScanline ( ) ;
}
Length - = Count ;
// Read previous pixel. See comments on top of function and handling of Shift >= 32 above for why
// we are guaranteed to have a previous pixel in the scanline when we get here.
2021-05-17 08:46:26 -04:00
Red = * ( Out - 4 ) ;
Green = * ( Out - 3 ) ;
Blue = * ( Out - 2 ) ;
Exponent = * ( Out - 1 ) ;
2022-10-12 11:19:42 -04:00
while ( Count > 0 )
2021-05-17 08:46:26 -04:00
{
* Out + + = Blue ;
* Out + + = Green ;
* Out + + = Red ;
* Out + + = Exponent ;
2022-10-12 11:19:42 -04:00
- - Count ;
2021-05-17 08:46:26 -04:00
}
Shift + = 8 ;
}
else
{
* Out + + = Blue ;
* Out + + = Green ;
* Out + + = Red ;
* Out + + = Exponent ;
Shift = 0 ;
2022-10-12 11:19:42 -04:00
- - Length ;
2021-05-17 08:46:26 -04:00
}
}
2022-10-12 11:19:42 -04:00
// On successful decode, copy read cursor back
InCodedScanline = In ;
2021-05-17 08:46:26 -04:00
return true ;
}
bool FHdrImageWrapper : : IsCompressedImageValid ( ) const
{
return CompressedData . Num ( ) > 0 & & RGBDataStart ;
}
void FHdrImageWrapper : : FreeCompressedData ( )
{
CompressedData = TArrayView64 < const uint8 > ( ) ;
RGBDataStart = nullptr ;
CompressedDataHolder . Empty ( ) ;
}
# undef LOCTEXT_NAMESPACE