11/*
2- * Copyright 2019-2022 Diligent Graphics LLC
2+ * Copyright 2019-2024 Diligent Graphics LLC
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2626
2727#include " SGILoader.h"
2828
29+ #include < cstring>
30+
2931#include " DataBlob.h"
3032#include " PlatformMisc.hpp"
3133#include " Errors.hpp"
@@ -82,13 +84,13 @@ bool LoadSGI(IDataBlob* pSGIData,
8284 return false ;
8385 }
8486
85- const auto Width = PlatformMisc::SwapBytes (Header.WidthBE );
86- const auto Height = PlatformMisc::SwapBytes (Header.HeightBE );
87- const auto NumChannels = PlatformMisc::SwapBytes (Header.ChannelsBE );
88- const auto BytesPerChannel = Header.BytePerPixelChannel ;
89- pDstImgDesc->Width = Width;
90- pDstImgDesc->Height = Height;
91- pDstImgDesc->NumComponents = NumChannels;
87+ const Uint32 Width = PlatformMisc::SwapBytes (Header.WidthBE );
88+ const Uint32 Height = PlatformMisc::SwapBytes (Header.HeightBE );
89+ const Uint32 NumChannels = PlatformMisc::SwapBytes (Header.ChannelsBE );
90+ const Uint32 BytesPerChannel = Header.BytePerPixelChannel ;
91+ pDstImgDesc->Width = Width;
92+ pDstImgDesc->Height = Height;
93+ pDstImgDesc->NumComponents = NumChannels;
9294
9395 switch (BytesPerChannel)
9496 {
@@ -113,72 +115,113 @@ bool LoadSGI(IDataBlob* pSGIData,
113115 pDstPixels->Resize (size_t {Height} * pDstImgDesc->RowStride );
114116 auto * pDstPtr = reinterpret_cast <Uint8*>(pDstPixels->GetDataPtr ());
115117
116- // Offsets table starts at byte 512 and is Height * NumChannels * 4 bytes long.
117- const auto TableSize = sizeof (Uint32) * Height * NumChannels;
118- const auto * OffsetTableBE = reinterpret_cast <const Uint32*>(pSrcPtr);
119- pSrcPtr += TableSize;
120- if (pSrcPtr > pDataEnd)
121- return false ;
122-
123- // Length table follows the offsets table and is the same size.
124- const auto * LengthTableBE = reinterpret_cast <const Uint32*>(pSrcPtr);
125- pSrcPtr += TableSize;
126- if (pSrcPtr > pDataEnd)
127- return false ;
118+ if (Header.Compression == 0 )
119+ {
120+ // Uncompressed SGI image
128121
129- VERIFY (Header.Compression , " Only RLE compressed files are currently supported" );
130- VERIFY (BytesPerChannel == 1 , " Only 8-bit images are currently supported" );
122+ const size_t UncompressedSize = size_t {Width} * Height * NumChannels * BytesPerChannel;
123+ if (pSrcPtr + UncompressedSize > pDataEnd)
124+ {
125+ LOG_ERROR_MESSAGE (" Not enough data for uncompressed image." );
126+ return false ;
127+ }
131128
132- const auto ReadLine = [Width, BytesPerChannel, NumChannels](auto * pDst, const auto * pLineDataStart, const auto * pLineDataEnd) //
129+ const size_t PlaneSize = size_t {Width} * Height * BytesPerChannel;
130+ for (size_t y = 0 ; y < Height; ++y)
131+ {
132+ for (size_t x = 0 ; x < Width; ++x)
133+ {
134+ // Copy each channel from the respective plane
135+ for (size_t c = 0 ; c < NumChannels; ++c)
136+ {
137+ const Uint8* pSrcPlane = pSrcPtr + PlaneSize * c;
138+ const size_t PixelIdx = y * Width + x;
139+
140+ // Copy channel
141+ std::memcpy (
142+ pDstPtr + (PixelIdx * NumChannels + c) * BytesPerChannel,
143+ pSrcPlane + PixelIdx * BytesPerChannel,
144+ BytesPerChannel);
145+ }
146+ }
147+ }
148+ }
149+ else if (Header.Compression == 1 )
133150 {
134- VERIFY_EXPR (sizeof (*pDst) == BytesPerChannel);
135- VERIFY_EXPR (sizeof (*pLineDataStart) == BytesPerChannel);
136- (void )BytesPerChannel;
151+ // RLE-compressed SGI image
137152
138- const auto * pSrc = pLineDataStart;
153+ // Offsets table starts at byte 512 and is Height * NumChannels * 4 bytes long.
154+ const auto TableSize = sizeof (Uint32) * Height * NumChannels;
155+ const auto * OffsetTableBE = reinterpret_cast <const Uint32*>(pSrcPtr);
156+ pSrcPtr += TableSize;
157+ if (pSrcPtr > pDataEnd)
158+ return false ;
159+
160+ // Length table follows the offsets table and is the same size.
161+ const auto * LengthTableBE = reinterpret_cast <const Uint32*>(pSrcPtr);
162+ pSrcPtr += TableSize;
163+ if (pSrcPtr > pDataEnd)
164+ return false ;
139165
140- Uint32 x = 0 ;
141- while (x < Width && pSrc < pLineDataEnd)
166+ // VERIFY(Header.Compression, "Only RLE compressed files are currently supported");
167+ VERIFY (BytesPerChannel == 1 , " Only 8-bit images are currently supported" );
168+
169+ const auto ReadLine = [Width, BytesPerChannel, NumChannels](auto * pDst, const auto * pLineDataStart, const auto * pLineDataEnd) //
142170 {
143- // The lowest 7 bits is the counter
144- int Count = *pSrc & 0x7F ;
171+ VERIFY_EXPR (sizeof (*pDst) == BytesPerChannel);
172+ VERIFY_EXPR (sizeof (*pLineDataStart) == BytesPerChannel);
173+ (void )BytesPerChannel;
145174
146- // If the high order bit of the first byte is 1, then the count is used to specify how many values to copy
147- // from the RLE data buffer.
148- // If the high order bit of the first byte is 0, then the count is used to specify how many times to repeat
149- // the value following the counter.
150- auto DistinctValues = (*pSrc & 0x80 ) != 0 ;
175+ const auto * pSrc = pLineDataStart;
151176
152- ++pSrc ;
153- while (Count > 0 && pSrc < pLineDataEnd)
177+ Uint32 x = 0 ;
178+ while (x < Width && pSrc < pLineDataEnd)
154179 {
155- *pDst = *pSrc;
156- if (DistinctValues || Count == 1 ) // Always move the pointer for the last value
157- ++pSrc;
158-
159- pDst += NumChannels;
160- --Count;
161- ++x;
180+ // The lowest 7 bits is the counter
181+ int Count = *pSrc & 0x7F ;
182+
183+ // If the high order bit of the first byte is 1, then the count is used to specify how many values to copy
184+ // from the RLE data buffer.
185+ // If the high order bit of the first byte is 0, then the count is used to specify how many times to repeat
186+ // the value following the counter.
187+ auto DistinctValues = (*pSrc & 0x80 ) != 0 ;
188+
189+ ++pSrc;
190+ while (Count > 0 && pSrc < pLineDataEnd)
191+ {
192+ *pDst = *pSrc;
193+ if (DistinctValues || Count == 1 ) // Always move the pointer for the last value
194+ ++pSrc;
195+
196+ pDst += NumChannels;
197+ --Count;
198+ ++x;
199+ }
162200 }
163- }
164- return x == Width;
165- };
201+ return x == Width;
202+ };
166203
167- for (Uint32 c = 0 ; c < NumChannels; ++c)
168- {
169- for (Uint32 y = 0 ; y < Height; ++y)
204+ for (Uint32 c = 0 ; c < NumChannels; ++c)
170205 {
171- // Each unsigned int in the offset table is the offset (from the file start) to the
172- // start of the compressed data of each scanline for each channel.
173- const auto RleOff = PlatformMisc::SwapBytes (OffsetTableBE[y + c * Height]);
174- // The size table tells the size of the compressed data (unsigned int) of each scanline.
175- const auto RleLen = PlatformMisc::SwapBytes (LengthTableBE[y + c * Height]);
176-
177- auto * DstLine = pDstPtr + y * size_t {pDstImgDesc->RowStride } + c;
178- if (!ReadLine (DstLine, pDataStart + RleOff, pSrcPtr + RleOff + RleLen))
179- return false ;
206+ for (Uint32 y = 0 ; y < Height; ++y)
207+ {
208+ // Each unsigned int in the offset table is the offset (from the file start) to the
209+ // start of the compressed data of each scanline for each channel.
210+ const auto RleOff = PlatformMisc::SwapBytes (OffsetTableBE[y + c * Height]);
211+ // The size table tells the size of the compressed data (unsigned int) of each scanline.
212+ const auto RleLen = PlatformMisc::SwapBytes (LengthTableBE[y + c * Height]);
213+
214+ auto * DstLine = pDstPtr + y * size_t {pDstImgDesc->RowStride } + c;
215+ if (!ReadLine (DstLine, pDataStart + RleOff, pSrcPtr + RleOff + RleLen))
216+ return false ;
217+ }
180218 }
181219 }
220+ else
221+ {
222+ LOG_ERROR_MESSAGE (" Unsupported compression value." );
223+ return false ;
224+ }
182225
183226 return true ;
184227}
0 commit comments