CPGFImage(3)

๐Ÿ“ฆ
version 7.21.7 ยท 4 versions
, in fedora 43

๐ŸŒธ CPGFImage โ€” the main character of libpgf, section 3 ๐ŸŒธ

okay so real talk: this is the page for CPGFImage(3) from libpgf, and she is the it-girl class of the whole library. No cap.


โœจ NAME โœจ

CPGFImage โ€” PGF main class. literally the whole vibe, she runs the show ๐Ÿ’…


๐Ÿซถ SYNOPSIS ๐Ÿซถ

#include <PGFimage.h>

๐Ÿ‘‘ Public Member Functions

  • CPGFImage () โ€” Standard constructor.
  • virtual ~CPGFImage () โ€” Destructor.
  • void Destroy ()
  • void Open (CPGFStream *stream)
  • bool IsOpen () const โ€” Returns true if the PGF has been opened for reading.
  • void Read (int level=0, CallbackPtr cb=nullptr, void *data=nullptr)
  • void Read (PGFRect &rect, int level=0, CallbackPtr cb=nullptr, void *data=nullptr)
  • void ReadPreview ()
  • void Reconstruct (int level=0)
  • void GetBitmap (int pitch, UINT8 *buff, BYTE bpp, int channelMap[]=nullptr, CallbackPtr cb=nullptr, void *data=nullptr) const
  • void GetYUV (int pitch, DataT *buff, BYTE bpp, int channelMap[]=nullptr, CallbackPtr cb=nullptr, void *data=nullptr) const
  • void ImportBitmap (int pitch, UINT8 *buff, BYTE bpp, int channelMap[]=nullptr, CallbackPtr cb=nullptr, void *data=nullptr)
  • void ImportYUV (int pitch, DataT *buff, BYTE bpp, int channelMap[]=nullptr, CallbackPtr cb=nullptr, void *data=nullptr)
  • void Write (CPGFStream *stream, UINT32 *nWrittenBytes=nullptr, CallbackPtr cb=nullptr, void *data=nullptr)
  • UINT32 WriteHeader (CPGFStream *stream)
  • UINT32 WriteImage (CPGFStream *stream, CallbackPtr cb=nullptr, void *data=nullptr)
  • UINT32 Write (int level, CallbackPtr cb=nullptr, void *data=nullptr)
  • void ConfigureEncoder (bool useOMP=true, bool favorSpeedOverSize=false)
  • void ConfigureDecoder (bool useOMP=true, UserdataPolicy policy=UP_CacheAll, UINT32 prefixSize=0)
  • void ResetStreamPos (bool startOfData)
  • void SetChannel (DataT *channel, int c=0)
  • void SetHeader (const PGFHeader &header, BYTE flags=0, const UINT8 *userData=0, UINT32 userDataLength=0)
  • void SetMaxValue (UINT32 maxValue)
  • void SetProgressMode (ProgressMode pm)
  • void SetRefreshCallback (RefreshCB callback, void *arg)
  • void SetColorTable (UINT32 iFirstColor, UINT32 nColors, const RGBQUAD *prgbColors)
  • DataT * GetChannel (int c=0)
  • void GetColorTable (UINT32 iFirstColor, UINT32 nColors, RGBQUAD *prgbColors) const
  • const RGBQUAD * GetColorTable () const
  • const PGFHeader * GetHeader () const
  • UINT32 GetMaxValue () const
  • UINT64 GetUserDataPos () const
  • const UINT8 * GetUserData (UINT32 &cachedSize, UINT32 *pTotalSize=nullptr) const
  • UINT32 GetEncodedHeaderLength () const
  • UINT32 GetEncodedLevelLength (int level) const
  • UINT32 ReadEncodedHeader (UINT8 *target, UINT32 targetLen) const
  • UINT32 ReadEncodedData (int level, UINT8 *target, UINT32 targetLen) const
  • UINT32 ChannelWidth (int c=0) const
  • UINT32 ChannelHeight (int c=0) const
  • BYTE ChannelDepth () const
  • UINT32 Width (int level=0) const
  • UINT32 Height (int level=0) const
  • BYTE Level () const
  • BYTE Levels () const
  • bool IsFullyRead () const โ€” Return true if all levels have been read.
  • BYTE Quality () const
  • BYTE Channels () const
  • BYTE Mode () const
  • BYTE BPP () const
  • bool ROIisSupported () const
  • PGFRect ComputeLevelROI () const
  • BYTE UsedBitsPerChannel () const
  • BYTE Version () const

๐ŸŒŸ Static Public Member Functions

  • static bool ImportIsSupported (BYTE mode)
  • static UINT32 LevelSizeL (UINT32 size, int level)
  • static UINT32 LevelSizeH (UINT32 size, int level)
  • static BYTE CodecMajorVersion (BYTE version=PGFVersion) โ€” Return major version.
  • static BYTE MaxChannelDepth (BYTE version=PGFVersion)

๐Ÿงš Protected Attributes

  • CWaveletTransform * m_wtChannel [MaxChannels] โ€” wavelet transformed color channels
  • DataT * m_channel [MaxChannels] โ€” untransformed channels in YUV format
  • CDecoder * m_decoder โ€” PGF decoder.
  • CEncoder * m_encoder โ€” PGF encoder.
  • UINT32 * m_levelLength โ€” length of each level in bytes; first level starts immediately after this array
  • UINT32 m_width [MaxChannels] โ€” width of each channel at current level
  • UINT32 m_height [MaxChannels] โ€” height of each channel at current level
  • PGFPreHeader m_preHeader โ€” PGF pre-header.
  • PGFHeader m_header โ€” PGF file header.
  • PGFPostHeader m_postHeader โ€” PGF post-header.
  • UINT64 m_userDataPos โ€” stream position of user data
  • int m_currentLevel โ€” transform level of current image
  • UINT32 m_userDataPolicy โ€” user data (metadata) policy during open
  • BYTE m_quant โ€” quantization parameter
  • bool m_downsample โ€” chrominance channels are downsampled
  • bool m_favorSpeedOverSize โ€” favor encoding speed over compression ratio
  • bool m_useOMPinEncoder โ€” use Open MP in encoder
  • bool m_useOMPinDecoder โ€” use Open MP in decoder
  • bool m_streamReinitialized โ€” stream has been reinitialized
  • PGFRect m_roi โ€” region of interest

๐Ÿ™ˆ Private Member Functions

  • void Init ()
  • void ComputeLevels ()
  • bool CompleteHeader ()
  • void RgbToYuv (int pitch, UINT8 *rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data)
  • void Downsample (int nChannel)
  • UINT32 UpdatePostHeaderSize ()
  • void WriteLevel ()
  • PGFRect GetAlignedROI (int c=0) const
  • void SetROI (PGFRect rect)
  • UINT8 Clamp4 (DataT v) const
  • UINT16 Clamp6 (DataT v) const
  • UINT8 Clamp8 (DataT v) const
  • UINT16 Clamp16 (DataT v) const
  • UINT32 Clamp31 (DataT v) const

๐Ÿ’œ Private Attributes

  • RefreshCB m_cb โ€” pointer to refresh callback procedure
  • void * m_cbArg โ€” refresh callback argument
  • double m_percent โ€” progress [0..1]
  • ProgressMode m_progressMode โ€” progress mode used in Read and Write; PM_Relative is default mode

๐Ÿฆ‹ Detailed Description ๐Ÿฆ‹

PGF main class.

PGF image class is the main class. You always need a PGF object for encoding or decoding image data.

  • Decoding: Open() Read() GetBitmap()
  • Encoding: SetHeader() ImportBitmap() Write()

๐Ÿ’– Author ๐Ÿ’–

C. Stamm, R. Spuler

Definition at line 53 of file PGFimage.h.


๐ŸŽ€ Constructor & Destructor Documentation ๐ŸŽ€

CPGFImage::CPGFImage ()

Standard constructor.

Definition at line 64 of file PGFimage.cpp.

64                      {
65         Init();
66 }

CPGFImage::~CPGFImage () [virtual]

Destructor.

Definition at line 117 of file PGFimage.cpp.

117                       {
118         m_currentLevel = -100; // unusual value used as marker in Destroy()
119         Destroy();
120 }

๐Ÿ”ฅ Member Function Documentation ๐Ÿ”ฅ

BYTE CPGFImage::BPP () const [inline]

Return the number of bits per pixel. Valid values can be 1, 8, 12, 16, 24, 32, 48, 64.

Returns

Number of bits per pixel.

Definition at line 461 of file PGFimage.h.

461 { return m_header.bpp; }

BYTE CPGFImage::ChannelDepth () const [inline]

Return bits per channel of the image's encoder.

Returns

Bits per channel

Definition at line 406 of file PGFimage.h.

406 { return MaxChannelDepth(m_preHeader.version); }

UINT32 CPGFImage::ChannelHeight (int c = 0) const [inline]

Return current image height of given channel in pixels. The returned height depends on the levels read so far and on ROI. โœจ (so yes, she's dynamic, she changes as you read more โ€” very main character of her)

Parameters

  • c โ€” A channel index

Returns

Channel height in pixels

Definition at line 401 of file PGFimage.h.

401 { ASSERT(c >= 0 && c < MaxChannels); return m_height[c]; }

BYTE CPGFImage::Channels () const [inline]

Return the number of image channels. An image of type RGB contains 3 image channels (B, G, R).

Returns

Number of image channels

Definition at line 448 of file PGFimage.h.

448 { return m_header.channels; }

UINT32 CPGFImage::ChannelWidth (int c = 0) const [inline]

Return current image width of given channel in pixels. The returned width depends on the levels read so far and on ROI.

Parameters

  • c โ€” A channel index

Returns

Channel width in pixels

Definition at line 394 of file PGFimage.h.

394 { ASSERT(c >= 0 && c < MaxChannels); return m_width[c]; }

UINT16 CPGFImage::Clamp16 (DataT v) const [inline], [private]

Definition at line 573 of file PGFimage.h.

573                                       {
574                 if (v & 0xFFFF0000) return (v < 0) ? (UINT16)0: (UINT16)65535; else return (UINT16)v;
575         }

UINT32 CPGFImage::Clamp31 (DataT v) const [inline], [private]

Definition at line 576 of file PGFimage.h.

576                                       {
577                 return (v < 0) ? 0 : (UINT32)v;
578         }

UINT8 CPGFImage::Clamp4 (DataT v) const [inline], [private]

Definition at line 563 of file PGFimage.h.

563                                     {
564                 if (v & 0xFFFFFFF0) return (v < 0) ? (UINT8)0: (UINT8)15; else return (UINT8)v;
565         }

UINT16 CPGFImage::Clamp6 (DataT v) const [inline], [private]

Definition at line 566 of file PGFimage.h.

566                                      {
567                 if (v & 0xFFFFFFC0) return (v < 0) ? (UINT16)0: (UINT16)63; else return (UINT16)v;
568         }

UINT8 CPGFImage::Clamp8 (DataT v) const [inline], [private]

Definition at line 569 of file PGFimage.h.

569                                     {
570                 // needs only one test in the normal case
571                 if (v & 0xFFFFFF00) return (v < 0) ? (UINT8)0 : (UINT8)255; else return (UINT8)v;
572         }

BYTE CPGFImage::CodecMajorVersion (BYTE version = PGFVersion) [static]

Return major version. Return codec major version.

Parameters

  • version โ€” pgf pre-header version number

Returns

PGF major of given version

Definition at line 768 of file PGFimage.cpp.

768                                               {
769         if (version & Version7) return 7;
770         if (version & Version6) return 6;
771         if (version & Version5) return 5;
772         if (version & Version2) return 2;
773         return 1;
774 }

bestie this is only part 1 of 18 and we have SO much more class to get through โ€” keep scrolling, we're not even warmed up ๐Ÿ’…

โœจ bool CPGFImage::CompleteHeader () [private]

Definition at line 218 of file PGFimage.cpp.

218                                {
219         // set current codec version
220         m_header.version = PGFVersionNumber(PGFMajorNumber, PGFYear, PGFWeek);
221
222         if (m_header.mode == ImageModeUnknown) {
223                 // undefined mode
224                 switch(m_header.bpp) {
225                 case 1: m_header.mode = ImageModeBitmap; break;
226                 case 8: m_header.mode = ImageModeGrayScale; break;
227                 case 12: m_header.mode = ImageModeRGB12; break;
228                 case 16: m_header.mode = ImageModeRGB16; break;
229                 case 24: m_header.mode = ImageModeRGBColor; break;
230                 case 32: m_header.mode = ImageModeRGBA; break;
231                 case 48: m_header.mode = ImageModeRGB48; break;
232                 default: m_header.mode = ImageModeRGBColor; break;
233                 }
234         }
235         if (!m_header.bpp) {
236                 // undefined bpp
237                 switch(m_header.mode) {
238                 case ImageModeBitmap:
239                         m_header.bpp = 1;
240                         break;
241                 case ImageModeIndexedColor:
242                 case ImageModeGrayScale:
243                         m_header.bpp = 8;
244                         break;
245                 case ImageModeRGB12:
246                         m_header.bpp = 12;
247                         break;
248                 case ImageModeRGB16:
249                 case ImageModeGray16:
250                         m_header.bpp = 16;
251                         break;
252                 case ImageModeRGBColor:
253                 case ImageModeLabColor:
254                         m_header.bpp = 24;
255                         break;
256                 case ImageModeRGBA:
257                 case ImageModeCMYKColor:
258                 case ImageModeGray32:
259                         m_header.bpp = 32;
260                         break;
261                 case ImageModeRGB48:
262                 case ImageModeLab48:
263                         m_header.bpp = 48;
264                         break;
265                 case ImageModeCMYK64:
266                         m_header.bpp = 64;
267                         break;
268                 default:
269                         ASSERT(false);
270                         m_header.bpp = 24;
271                 }
272         }
273         if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) {
274                 // change mode
275                 m_header.mode = ImageModeRGBA;
276         }
277         if (m_header.mode == ImageModeBitmap && m_header.bpp != 1) return false;
278         if (m_header.mode == ImageModeIndexedColor && m_header.bpp != 8) return false;
279         if (m_header.mode == ImageModeGrayScale && m_header.bpp != 8) return false;
280         if (m_header.mode == ImageModeGray16 && m_header.bpp != 16) return false;
281         if (m_header.mode == ImageModeGray32 && m_header.bpp != 32) return false;
282         if (m_header.mode == ImageModeRGBColor && m_header.bpp != 24) return false;
283         if (m_header.mode == ImageModeRGBA && m_header.bpp != 32) return false;
284         if (m_header.mode == ImageModeRGB12 && m_header.bpp != 12) return false;
285         if (m_header.mode == ImageModeRGB16 && m_header.bpp != 16) return false;
286         if (m_header.mode == ImageModeRGB48 && m_header.bpp != 48) return false;
287         if (m_header.mode == ImageModeLabColor && m_header.bpp != 24) return false;
288         if (m_header.mode == ImageModeLab48 && m_header.bpp != 48) return false;
289         if (m_header.mode == ImageModeCMYKColor && m_header.bpp != 32) return false;
290         if (m_header.mode == ImageModeCMYK64 && m_header.bpp != 64) return false;
291
292         // set number of channels
293         if (!m_header.channels) {
294                 switch(m_header.mode) {
295                 case ImageModeBitmap:
296                 case ImageModeIndexedColor:
297                 case ImageModeGrayScale:
298                 case ImageModeGray16:
299                 case ImageModeGray32:
300                         m_header.channels = 1;
301                         break;
302                 case ImageModeRGBColor:
303                 case ImageModeRGB12:
304                 case ImageModeRGB16:
305                 case ImageModeRGB48:
306                 case ImageModeLabColor:
307                 case ImageModeLab48:
308                         m_header.channels = 3;
309                         break;
310                 case ImageModeRGBA:
311                 case ImageModeCMYKColor:
312                 case ImageModeCMYK64:
313                         m_header.channels = 4;
314                         break;
315                 default:
316                         return false;
317                 }
318         }
319
320         // store used bits per channel
321         UINT8 bpc = m_header.bpp/m_header.channels;
322         if (bpc > 31) bpc = 31;
323         if (!m_header.usedBitsPerChannel || m_header.usedBitsPerChannel > bpc) {
324                 m_header.usedBitsPerChannel = bpc;
325         }
326
327         return true;
328 }

๐ŸŒธ PGFRect CPGFImage::ComputeLevelROI () const

Returns the ROI of channel 0 at the current level, in pixels. The returned rect is only valid after you've read a ROI, bestie. Don't come for me if you use it before that.

Returns: ROI in pixels

Definition at line 583 of file PGFimage.cpp.

583                                          {
584         if (m_currentLevel == 0) {
585                 return m_roi;
586         } else {
587                 const UINT32 rLeft = LevelSizeL(m_roi.left, m_currentLevel);
588                 const UINT32 rRight = LevelSizeL(m_roi.right, m_currentLevel);
589                 const UINT32 rTop = LevelSizeL(m_roi.top, m_currentLevel);
590                 const UINT32 rBottom = LevelSizeL(m_roi.bottom, m_currentLevel);
591                 return PGFRect(rLeft, rTop, rRight - rLeft, rBottom - rTop);
592         }
593 }

๐Ÿ’… void CPGFImage::ComputeLevels () [private]

Definition at line 854 of file PGFimage.cpp.

854                               {
855         const int maxThumbnailWidth = 20*FilterSize;
856         const int m = __min(m_header.width, m_header.height);
857         int s = m;
858
859         if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) {
860                 m_header.nLevels = 1;
861                 // compute a good value depending on the size of the image
862                 while (s > maxThumbnailWidth) {
863                         m_header.nLevels++;
864                         s >>= 1;
865                 }
866         }
867
868         int levels = m_header.nLevels; // we need a signed value during level reduction
869
870         // reduce number of levels if the image size is smaller than FilterSize*(2^levels)
871         s = FilterSize*(1 << levels);   // must be at least the double filter size because of subsampling
872         while (m < s) {
873                 levels--;
874                 s >>= 1;
875         }
876         if (levels > MaxLevel) m_header.nLevels = MaxLevel;
877         else if (levels < 0) m_header.nLevels = 0;
878         else m_header.nLevels = (UINT8)levels;
879
880         // used in Write when PM_Absolute
881         m_percent = pow(0.25, m_header.nLevels);
882
883         ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel);
884 }

๐Ÿฆ‹ void CPGFImage::ConfigureDecoder (bool useOMP = true, UserdataPolicy policy = UP_CacheAll, UINT32 prefixSize = 0) [inline]

Configures the decoder. Let's get into the parameters, because they matter.

Parameters:

  • useOMP โ€” use parallel threading with Open MP during decoding. Default value: true. It only influences the decoding if the codec has been compiled with OpenMP support. No OpenMP, no extra sparkle.
  • policy โ€” the file might contain user data (e.g. metadata). This policy defines the behaviour during Open(). UP_CacheAll: user data is read and stored completely in a new allocated memory block. It can be accessed by GetUserData(). UP_CachePrefix: only prefixSize bytes at the beginning of the user data are stored in a new allocated memory block. It can be accessed by GetUserData(). UP_Skip: user data is skipped and nothing is cached, periodt.
  • prefixSize โ€” is only used in combination with UP_CachePrefix. It defines the number of bytes cached.

Definition at line 260 of file PGFimage.h.

260 { ASSERT(prefixSize <= MaxUserDataSize);  m_useOMPinDecoder = useOMP; m_userDataPolicy = (UP_CachePrefix) ? prefixSize : 0xFFFFFFFF - policy; }

๐ŸŽ€ void CPGFImage::ConfigureEncoder (bool useOMP = true, bool favorSpeedOverSize = false) [inline]

Configures the encoder. Two little switches, that's it.

Parameters:

  • useOMP โ€” use parallel threading with Open MP during encoding. Default value: true. Influences the encoding only if the codec has been compiled with OpenMP support.
  • favorSpeedOverSize โ€” favors encoding speed over compression ratio. Default value: false.

Definition at line 250 of file PGFimage.h.

250 { m_useOMPinEncoder = useOMP; m_favorSpeedOverSize = favorSpeedOverSize; }

๐Ÿ’€ void CPGFImage::Destroy ()

Definition at line 124 of file PGFimage.cpp.

124                         {
125         for (int i = 0; i < m_header.channels; i++) {
126                 delete m_wtChannel[i]; // also deletes m_channel
127         }
128         delete[] m_postHeader.userData;
129         delete[] m_levelLength;
130         delete m_decoder;
131         delete m_encoder;
132
133         if (m_currentLevel != -100) Init();
134 }

void CPGFImage::Downsample (int nChannel) [private] ๐Ÿ’…

okay so this is the lil glow-down moment โ€” it literally shrinks the channel down to half size by averaging pixel blocks together. it's private, so bestie, you're not calling this yourself unless you're inside the class. Definition at line 810 of file PGFimage.cpp.

810                                  {
811         ASSERT(ch > 0);
812
813         const int w = m_width[0];
814         const int w2 = w/2;
815         const int h2 = m_height[0]/2;
816         const int oddW = w%2;                           // don't use bool -> problems with MaxSpeed optimization
817         const int oddH = m_height[0]%2;         // "
818         int loPos = 0;
819         int hiPos = w;
820         int sampledPos = 0;
821         DataT* buff = m_channel[ch]; ASSERT(buff);
822
823         for (int i=0; i < h2; i++) {
824                 for (int j=0; j < w2; j++) {
825                         // compute average of pixel block
826                         buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2;
827                         loPos += 2; hiPos += 2;
828                         sampledPos++;
829                 }
830                 if (oddW) {
831                         buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1;
832                         loPos++; hiPos++;
833                         sampledPos++;
834                 }
835                 loPos += w; hiPos += w;
836         }
837         if (oddH) {
838                 for (int j=0; j < w2; j++) {
839                         buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1;
840                         loPos += 2; hiPos += 2;
841                         sampledPos++;
842                 }
843                 if (oddW) {
844                         buff[sampledPos] = buff[loPos];
845                 }
846         }
847
848         // downsampled image has half width and half height
849         m_width[ch] = (m_width[ch] + 1)/2;
850         m_height[ch] = (m_height[ch] + 1)/2;
851 }

a few little lore drops from this one, because it's giving attention to detail:

  • ASSERT(ch > 0); โ€” channel index has to be positive, no zero-index shenanigans here.
  • oddW and oddH are int, not bool โ€” the comment says it's because of "problems with MaxSpeed optimization". the devs were like "don't use bool, i beg" and honestly iconic of them. ๐Ÿ’€
  • the average is a straight-up >> 2 on four pixels (that's divide-by-4, bestie, shifts stay winning).
  • m_width[ch] and m_height[ch] get halved with (x + 1)/2 at the end, so odd dimensions round up. and yes, the comment confirms: "downsampled image has half width and half height".

PGFRect CPGFImage::GetAlignedROI (int c = 0) const [private] ๐ŸŽ€

"Returns aligned ROI in pixels of current level of channel c" โ€” so it hands you back the region-of-interest rectangle, all nicely aligned, for whatever channel you ask about. ๐Ÿ”ฅ

Parameters

  • c โ€” A channel index

Definition at line 598 of file PGFimage.cpp.

598                                               {
599         PGFRect roi(0, 0, m_width[c], m_height[c]);
600
601         if (ROIisSupported()) {
602                 ASSERT(m_wtChannel[c]);
603
604                 roi = m_wtChannel[c]->GetAlignedROI(m_currentLevel);
605         }
606         ASSERT(roi.Width() == m_width[c]);
607         ASSERT(roi.Height() == m_height[c]);
608         return roi;
609 }

so it starts with a whole-image rect PGFRect roi(0, 0, m_width[c], m_height[c]) and then, if ROIisSupported(), it grabs the aligned ROI from the wavelet channel at m_currentLevel instead. either way it asserts that the width and height line up with m_width[c] and m_height[c] before returning. trust issues, but make it โœจvalidationโœจ.


void CPGFImage::GetBitmap (int pitch, UINT8 * buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void * data = nullptr) const ๐ŸŒˆ

this is the big one, bestie. Get image data in interleaved format: (ordering of RGB data is BGR[A]). ๐Ÿ’–

Upsampling, YUV to RGB transform and interleaving are done here to reduce the number of passes over the data.

okay now the pitch situation, listen up:

  • The absolute value of pitch is the number of bytes of an image row of the given image buffer.
  • If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
  • if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).

The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to provide a channelMap of size of expected channels (depending on image mode).

For example, PGF provides a channel sequence BGR in RGB color mode. If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.

โš ๏ธ It might throw an IOException. so like, wrap it, bestie, don't be out here raw-dogging exceptions. ๐Ÿ˜ญ

Parameters

  • pitch โ€” The number of bytes of a row of the image buffer.
  • buff โ€” An image buffer.
  • bpp โ€” The number of bits per pixel used in image buffer.
  • channelMap โ€” A integer array containing the mapping of PGF channel ordering to expected channel ordering.
  • cb โ€” A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.

okay bestie we are DEEP in the function body now and it's giving "the author left ALL the receipts" ๐Ÿ’… buckle up, because this part is a whole tour through the pixel-pushing machinery. Nothing is being skipped. Not one line. Not one assert.

       Definition at line 1789 of file PGFimage.cpp.
       1789                                                                                                                     {
       1790         ASSERT(buff);
       1791         UINT32 w = m_width[0];  // width of decoded image
       1792         UINT32 h = m_height[0]; // height of decoded image
       1793         UINT32 yw = w;                  // y-channel width
       1794         UINT32 uw = m_width[1]; // u-channel width
       1795         UINT32 roiOffsetX = 0;
       1796         UINT32 roiOffsetY = 0;
       1797         UINT32 yOffset = 0;
       1798         UINT32 uOffset = 0;

so first thing, we assert buff โ€” like, obviously, we need somewhere to put the pixels ๐Ÿซถ. Then the local crew rolls in: w and h get pulled straight from m_width[0] and m_height[0] (that's the width and height of the decoded image, per the comments, bestie โ€” the comments are doing the work here), yw starts life as w and is labelled the y-channel width, and uw comes from m_width[1] โ€” the u-channel width. Then four offsets โ€” roiOffsetX, roiOffsetY, yOffset, uOffset โ€” all start at 0. Clean slate energy โœจ.


       1800 #ifdef __PGFROISUPPORT__
       1801         const PGFRect& roi = GetAlignedROI(); // in pixels, roi is usually larger than levelRoi
       1802         ASSERT(w == roi.Width() && h == roi.Height());
       1803         const PGFRect levelRoi = ComputeLevelROI();
       1804         ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right);
       1805         ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom);
       1806
       1807         if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) {
       1808                 // ROI is used
       1809                 w = levelRoi.Width();
       1810                 h = levelRoi.Height();
       1811                 roiOffsetX = levelRoi.left - roi.left;
       1812                 roiOffsetY = levelRoi.top - roi.top;
       1813                 yOffset = roiOffsetX + roiOffsetY*yw;
       1814
       1815                 if (m_downsample) {
       1816                         const PGFRect& downsampledRoi = GetAlignedROI(1);
       1817                         uOffset = levelRoi.left/2 - downsampledRoi.left + (levelRoi.top/2 - downsampledRoi.top)*m_width[1];
       1818                 } else {
       1819                         uOffset = yOffset;
       1820                 }
       1821         }
       1822 #endif

okay so this whole chunk only exists if __PGFROISUPPORT__ is defined โ€” it's compiled out otherwise, iykyk. Inside: roi comes from GetAlignedROI(), and the comment is very clear that it's in pixels and that roi is usually larger than levelRoi. Then we assert that w matches roi.Width() and h matches roi.Height() โ€” fr fr, we're checking our homework ๐Ÿ“. levelRoi comes from ComputeLevelROI(), and two more asserts confirm roi fully contains levelRoi on both the left/right axis and the top/bottom axis.

Then the vibe check: if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) โ€” that's the "ROI is used" branch, per the comment ๐ŸŒธ. Inside it, w and h get overwritten with levelRoi.Width() and levelRoi.Height(), the x and y offsets are computed as the difference between levelRoi and roi edges, and yOffset becomes roiOffsetX + roiOffsetY*yw.

Then the downsample tea โ˜•: if m_downsample is truthy, we grab downsampledRoi = GetAlignedROI(1) and compute uOffset with all those halves โ€” levelRoi.left/2 - downsampledRoi.left + (levelRoi.top/2 - downsampledRoi.top)*m_width[1]. If not, uOffset = yOffset. Simple as that, no notes ๐Ÿ’….


       1824         const double dP = 1.0/h;
       1825         int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
       1826         if (channelMap == nullptr) channelMap = defMap;
       1827         DataT uAvg, vAvg;
       1828         double percent = 0;
       1829         UINT32 i, j;

dP is literally 1.0/h โ€” the per-row progress step, bestie, so the callback can count up nicely โœจ. defMap is the identity mapping { 0, 1, 2, 3, 4, 5, 6, 7 }, and we assert its length equals MaxChannels โ€” if MaxChannels ever changed, this assert would riot ๐Ÿ’€. If channelMap is nullptr, we swap in defMap. Then uAvg and vAvg (type DataT) get declared, percent starts at 0, and i and j are our loop girlies. Periodt.


       1831         switch(m_header.mode) {
       1832         case ImageModeBitmap:
       1833                 {
       1834                         ASSERT(m_header.channels == 1);
       1835                         ASSERT(m_header.bpp == 1);
       1836                         ASSERT(bpp == 1);
       1837
       1838                         const UINT32 w2 = (w + 7)/8;
       1839                         DataT* y = m_channel[0]; ASSERT(y);
       1840
       1841                         if (m_preHeader.version & Version7) {
       1842                                 // new unpacked version has a little better compression ratio
       1843                                 // since version 7
       1844                                 for (i = 0; i < h; i++) {
       1845                                         UINT32 cnt = 0;
       1846                                         for (j = 0; j < w2; j++) {
       1847                                                 UINT8 byte = 0;
       1848                                                 for (int k = 0; k < 8; k++) {
       1849                                                         byte <<= 1;
       1850                                                         UINT8 bit = 0;
       1851                                                         if (cnt < w) {
       1852                                                                 bit = y[yOffset + cnt] & 1;
       1853                                                         }
       1854                                                         byte |= bit;
       1855                                                         cnt++;
       1856                                                 }
       1857                                                 buff[j] = byte;
       1858                                         }
       1859                                         yOffset += yw;
       1860                                         buff += pitch;
       1861
       1862                                         if (cb) {
       1863                                                 percent += dP;
       1864                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       1865                                         }
       1866                                 }
       1867                         } else {
       1868                                 // old versions
       1869                                 // packed pixels: 8 pixel in 1 byte of channel[0]
       1870                                 if (!(m_preHeader.version & Version5)) yw = w2; // not version 5 or 6
       1871                                 yOffset = roiOffsetX/8 + roiOffsetY*yw; // 1 byte in y contains 8 pixel values
       1872                                 for (i = 0; i < h; i++) {
       1873                                         for (j = 0; j < w2; j++) {
       1874                                                 buff[j] = Clamp8(y[yOffset + j] + YUVoffset8);
       1875                                         }
       1876                                         yOffset += yw;
       1877                                         buff += pitch;
       1878
       1879                                         if (cb) {
       1880                                                 percent += dP;
       1881                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       1882                                         }
       1883                                 }
       1884                         }
       1885                         break;
       1886                 }

okay we're switching on m_header.mode and the first stop is ImageModeBitmap ๐Ÿ‘‘. Three asserts up top: m_header.channels == 1, m_header.bpp == 1, and bpp == 1 โ€” bitmap means ONE channel, ONE bit per pixel, no negotiation ๐ŸŽ€.

w2 = (w + 7)/8 is the byte-rounded row width (classic bit-packing math, we love her), and y points at m_channel[0] with an assert that it's not null.

Now the fork: if (m_preHeader.version & Version7) โ€” the comments say this is the new unpacked version and it "has a little better compression ratio" since version 7 โœจ. It walks row by row, and for each of the w2 output bytes it builds a byte from 8 bits: shift left, grab bit = y[yOffset + cnt] & 1 only if cnt < w (otherwise the bit stays 0 โ€” that's the padding, bestie), OR it in, and cnt++ every single iteration regardless. Then buff[j] = byte;. Row done โ†’ yOffset += yw; and buff += pitch;. And if cb exists, we bump percent += dP and call (*cb)(percent, true, data) โ€” and if it returns truthy, ReturnWithError(EscapePressed) ๐Ÿšจ yes the user can literally bail out mid-render.

The else is the old versions path: "packed pixels: 8 pixel in 1 byte of channel[0]" ๐ŸŒธ. Note the spicy detail โ€” if (!(m_preHeader.version & Version5)) yw = w2; with the comment "not version 5 or 6", so only non-5-and-non-6 versions get yw reassigned. Then yOffset = roiOffsetX/8 + roiOffsetY*yw; โ€” comment says "1 byte in y contains 8 pixel values" โ€” and each row is just buff[j] = Clamp8(y[yOffset + j] + YUVoffset8);. Same progress callback, same escape hatch. Same chaotic perfection ๐Ÿ’….


       1887         case ImageModeIndexedColor:
       1888         case ImageModeGrayScale:
       1889         case ImageModeHSLColor:
       1890         case ImageModeHSBColor:
       1891                 {
       1892                         ASSERT(m_header.channels >= 1);
       1893                         ASSERT(m_header.bpp == m_header.channels*8);
       1894                         ASSERT(bpp%8 == 0);
       1895
       1896                         UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
       1897
       1898                         for (i=0; i < h; i++) {
       1899                                 UINT32 yPos = yOffset;
       1900                                 cnt = 0;
       1901                                 for (j=0; j < w; j++) {
       1902                                         for (UINT32 c=0; c < m_header.channels; c++) {
       1903                                                 buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8);
       1904                                         }
       1905                                         cnt += channels;
       1906                                         yPos++;
       1907                                 }
       1908                                 yOffset += yw;
       1909                                 buff += pitch;
       1910
       1911                                 if (cb) {
       1912                                         percent += dP;
       1913                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       1914                                 }
       1915                         }
       1916                         break;
       1917                 }

four cases share one body: ImageModeIndexedColor, ImageModeGrayScale, ImageModeHSLColor, ImageModeHSBColor โ€” they're all 8-bit-per-channel coded, so they get the same treatment ๐Ÿซถ.

Asserts: m_header.channels >= 1, m_header.bpp == m_header.channels*8, and bpp%8 == 0. Then channels = bpp/8 with an assert that channels >= m_header.channels โ€” so the destination can have MORE channels than the source, which is exactly why channelMap is doing its little routing job ๐ŸŽ€.

The loop: for each row, reset yPos = yOffset and cnt = 0. For each of the w pixels, for each source channel c, write buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8); โ€” so the channel gets placed at the position channelMap[c] says, not just straight across. Then cnt += channels (note: channels, the destination stride) and yPos++. Row wrap: yOffset += yw; buff += pitch;. Same callback, same EscapePressed bail ๐Ÿ’….


       1918         case ImageModeGray16:
       1919                 {
       1920                         ASSERT(m_header.channels >= 1);
       1921                         ASSERT(m_header.bpp == m_header.channels*16);
       1922
       1923                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
       1924                         UINT32 cnt, channels;
       1925
       1926                         if (bpp%16 == 0) {
       1927                                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
       1928                                 UINT16 *buff16 = (UINT16 *)buff;
       1929                                 int pitch16 = pitch/2;
       1930                                 channels = bpp/16; ASSERT(channels >= m_header.channels);
       1931
       1932                                 for (i=0; i < h; i++) {
       1933                                         UINT32 yPos = yOffset;
       1934                                         cnt = 0;
       1935                                         for (j=0; j < w; j++) {
       1936                                                 for (UINT32 c=0; c < m_header.channels; c++) {
       1937                                                         buff16[cnt + channelMap[c]] = Clamp16((m_channel[c][yPos] + yuvOffset16) << shift);
       1938                                                 }
       1939                                                 cnt += channels;
       1940                                                 yPos++;
       1941                                         }
       1942                                         yOffset += yw;
       1943                                         buff16 += pitch16;
       1944
       1945                                         if (cb) {
       1946                                                 percent += dP;
       1947                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       1948                                         }
       1949                                 }
       1950                         } else {
       1951                                 ASSERT(bpp%8 == 0);
       1952                                 const int shift = __max(0, UsedBitsPerChannel() - 8);
       1953                                 channels = bpp/8; ASSERT(channels >= m_header.channels);
       1954
       1955                                 for (i=0; i < h; i++) {
       1956                                         UINT32 yPos = yOffset;
       1957                                         cnt = 0;
       1958                                         for (j=0; j < w; j++) {
       1959                                                 for (UINT32 c=0; c < m_header.channels; c++) {
       1960                                                         buff[cnt + channelMap[c]] = Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift);
       1961                                                 }
       1962                                                 cnt += channels;
       1963                                                 yPos++;
       1964                                         }
       1965                                         yOffset += yw;
       1966                                         buff += pitch;
       1967
       1968                                         if (cb) {
       1969                                                 percent += dP;
       1970                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       1971                                         }
       1972                                 }
       1973                         }
       1974                         break;
       1975                 }

ImageModeGray16 โ€” the 16-bit era, bestie ๐ŸŒˆ. Asserts: m_header.channels >= 1 and m_header.bpp == m_header.channels*16.

yuvOffset16 is 1 << (UsedBitsPerChannel() - 1) โ€” the midpoint of however many bits are actually used, no cap ๐ŸŽ€. Then we split on bpp%16 == 0:

  • Yes, 16-bit output: shift = 16 - UsedBitsPerChannel() with ASSERT(shift >= 0) โ€” if used bits exceeded 16 that assert would absolutely go off ๐Ÿ’€. buff16 reinterprets buff as UINT16 *, pitch16 = pitch/2 (because each unit is two bytes now, slay), channels = bpp/16 asserted >= m_header.channels. Then the same routing dance but with Clamp16((m_channel[c][yPos] + yuvOffset16) << shift) โ€” shift UP into the 16-bit space โœจ. cnt += channels, yPos++, yOffset += yw, buff16 += pitch16, progress callback, escape check. Chef's kiss ๐Ÿ‘‘
  • No, fallback: ASSERT(bpp%8 == 0); then shift = __max(0, UsedBitsPerChannel() - 8); โ€” note the __max(0, ...), so it never goes negative, that's the safety net ๐Ÿซถ. channels = bpp/8 asserted >= m_header.channels, and here it's Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift) โ€” shift DOWN into 8-bit land. Same increments, same callback, same EscapePressed exit. Periodt.

       1976         case ImageModeRGBColor:
       1977                 {
       1978                         ASSERT(m_header.channels == 3);
       1979                         ASSERT(m_header.bpp == m_header.channels*8);

and we land on ImageModeRGBColor right as the excerpt cuts out ๐Ÿ˜ญ โ€” but not before two asserts set the mood: m_header.channels == 3 and m_header.bpp == m_header.channels*8. Three channels, 8 bits each. The classic. We'll pick her up in the next part, bestie ๐Ÿ’–

...and we're picking up mid-conversion, bestie, right inside the ImageModeRGB32 case. The vibes? Impeccable. The math? Also impeccable. Let's go ๐Ÿซถ

       1980                         ASSERT(bpp%8 == 0);
       1981                         ASSERT(bpp >= m_header.bpp);
       1982
       1983                         DataT* y = m_channel[0]; ASSERT(y);
       1984                         DataT* u = m_channel[1]; ASSERT(u);
       1985                         DataT* v = m_channel[2]; ASSERT(v);
       1986                         UINT8 *buffg = &buff[channelMap[1]],
       1987                                   *buffr = &buff[channelMap[2]],
       1988                                   *buffb = &buff[channelMap[0]];
       1989                         UINT8 g;
       1990                         UINT32 cnt, channels = bpp/8;
       1991
       1992                         if (m_downsample) {
       1993                                 for (i=0; i < h; i++) {
       1994                                         UINT32 uPos = uOffset;
       1995                                         UINT32 yPos = yOffset;
       1996                                         cnt = 0;
       1997                                         for (j=0; j < w; j++) {
       1998                                                 // u and v are downsampled
       1999                                                 uAvg = u[uPos];
       2000                                                 vAvg = v[uPos];
       2001                                                 // Yuv
       2002                                                 buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
       2003                                                 buffr[cnt] = Clamp8(uAvg + g);
       2004                                                 buffb[cnt] = Clamp8(vAvg + g);
       2005                                                 cnt += channels;
       2006                                                 if (j & 1) uPos++;
       2007                                                 yPos++;
       2008                                         }
       2009                                         if (i & 1) uOffset += uw;
       2010                                         yOffset += yw;
       2011                                         buffb += pitch;
       2012                                         buffg += pitch;
       2013                                         buffr += pitch;
       2014
       2015                                         if (cb) {
       2016                                                 percent += dP;
       2017                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2018                                         }
       2019                                 }
       2020
       2021                         } else {
       2022                                 for (i=0; i < h; i++) {
       2023                                         cnt = 0;
       2024                                         UINT32 yPos = yOffset;
       2025                                         for (j = 0; j < w; j++) {
       2026                                                 uAvg = u[yPos];
       2027                                                 vAvg = v[yPos];
       2028                                                 // Yuv
       2029                                                 buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
       2030                                                 buffr[cnt] = Clamp8(uAvg + g);
       2031                                                 buffb[cnt] = Clamp8(vAvg + g);
       2032                                                 cnt += channels;
       2033                                                 yPos++;
       2034                                         }
       2035                                         yOffset += yw;
       2036                                         buffb += pitch;
       2037                                         buffg += pitch;
       2038                                         buffr += pitch;
       2039
       2040                                         if (cb) {
       2041                                                 percent += dP;
       2042                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2043                                         }
       2044                                 }
       2045                         }
       2046                         break;
       2047                 }

okay so quick vibe check on what just happened โœจ โ€” we grabbed the three channel pointers y, u, v out of m_channel[0..2] and asserted all three are real (no nulls allowed, periodt ๐Ÿ™…โ€โ™€๏ธ). Then the green/red/blue byte pointers get pointed into buff at channelMap[1], channelMap[2], and channelMap[0] respectively โ€” yes the mapping is gโ†’1, rโ†’2, bโ†’0, that's just how this file likes it, iykyk.

Then it splits on m_downsample:

  • downsampled path โ€” u and v get read at uPos, and uPos only steps forward if (j & 1), so on every other column. The uOffset only advances if (i & 1) too. That's the whole "u and v are downsampled" thing the comment is whispering about ๐Ÿ’…
  • not downsampled path โ€” everyone indexes with plain yPos, no funny business, no uOffset at all.

Both paths do the same YUVโ†’RGB dance: g comes from y[yPos] + YUVoffset8 - ((uAvg + vAvg) >> 2) and the comment is SCREAMING at you that it must be the logical shift operator, then buffr = Clamp8(uAvg + g) and buffb = Clamp8(vAvg + g). Everything gets clamped because overflow is not the moment ๐Ÿšซ

And cnt += channels where channels = bpp/8, so we're striding across the buffer properly. Plus if there's a callback cb, we bump percent += dP every row and if it comes back truthy we ReturnWithError(EscapePressed) โ€” yes bestie, the user can hit escape mid-decode and we respect it ๐Ÿ›‘

Now the 48-bit glow-up ๐Ÿ‘‘ โ€” same energy, twice the bits:

       2048         case ImageModeRGB48:
       2049                 {
       2050                         ASSERT(m_header.channels == 3);
       2051                         ASSERT(m_header.bpp == 48);
       2052
       2053                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
       2054
       2055                         DataT* y = m_channel[0]; ASSERT(y);
       2056                         DataT* u = m_channel[1]; ASSERT(u);
       2057                         DataT* v = m_channel[2]; ASSERT(v);
       2058                         UINT32 cnt, channels;
       2059                         DataT g;
       2060
       2061                         if (bpp >= 48 && bpp%16 == 0) {
       2062                                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
       2063                                 UINT16 *buff16 = (UINT16 *)buff;
       2064                                 int pitch16 = pitch/2;
       2065                                 channels = bpp/16; ASSERT(channels >= m_header.channels);
       2066
       2067                                 for (i=0; i < h; i++) {
       2068                                         UINT32 uPos = uOffset;
       2069                                         UINT32 yPos = yOffset;
       2070                                         cnt = 0;
       2071                                         for (j=0; j < w; j++) {
       2072                                                 uAvg = u[uPos];
       2073                                                 vAvg = v[uPos];
       2074                                                 // Yuv
       2075                                                 g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
       2076                                                 buff16[cnt + channelMap[1]] = Clamp16(g << shift);
       2077                                                 buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
       2078                                                 buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
       2079                                                 cnt += channels;
       2080                                                 if (!m_downsample || (j & 1)) uPos++;
       2081                                                 yPos++;
       2082                                         }
       2083                                         if (!m_downsample || (i & 1)) uOffset += uw;
       2084                                         yOffset += yw;
       2085                                         buff16 += pitch16;
       2086
       2087                                         if (cb) {
       2088                                                 percent += dP;
       2089                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2090                                         }
       2091                                 }
       2092                         } else {
       2093                                 ASSERT(bpp%8 == 0);
       2094                                 const int shift = __max(0, UsedBitsPerChannel() - 8);
       2095                                 channels = bpp/8; ASSERT(channels >= m_header.channels);
       2096
       2097                                 for (i=0; i < h; i++) {
       2098                                         UINT32 uPos = uOffset;
       2099                                         UINT32 yPos = yOffset;
       2100                                         cnt = 0;
       2101                                         for (j=0; j < w; j++) {
       2102                                                 uAvg = u[uPos];
       2103                                                 vAvg = v[uPos];
       2104                                                 // Yuv
       2105                                                 g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
       2106                                                 buff[cnt + channelMap[1]] = Clamp8(g >> shift);
       2107                                                 buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
       2108                                                 buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
       2109                                                 cnt += channels;
       2110                                                 if (!m_downsample || (j & 1)) uPos++;
       2111                                                 yPos++;
       2112                                         }
       2113                                         if (!m_downsample || (i & 1)) uOffset += uw;
       2114                                         yOffset += yw;
       2115                                         buff += pitch;
       2116
       2117                                         if (cb) {
       2118                                                 percent += dP;
       2119                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2120                                         }
       2121                                 }
       2122                         }
       2123                         break;
       2124                 }

The checks here are strict: m_header.channels == 3 and m_header.bpp == 48, no negotiating ๐Ÿ˜ค. And the offset constant is yuvOffset16 = 1 << (UsedBitsPerChannel() - 1) โ€” a 16-bit-flavoured offset this time.

Then two whole branches:

  1. bpp >= 48 && bpp%16 == 0 โ†’ we're in 16-bit land. shift = 16 - UsedBitsPerChannel() with an ASSERT(shift >= 0), the buffer gets cast to UINT16 *buff16, pitch16 = pitch/2, channels = bpp/16 (asserted >= m_header.channels). Values go through Clamp16 after a << shift. And notice the downsample guard flipped to if (!m_downsample || (j & 1)) uPos++; and if (!m_downsample || (i & 1)) uOffset += uw; โ€” so when downsampling is off everything advances every time, and when it's on it only advances on odd indices. Same soul, different syntax ๐Ÿฆ‹
  2. else โ†’ ASSERT(bpp%8 == 0), shift = __max(0, UsedBitsPerChannel() - 8), channels = bpp/8. Here it's back to 8-bit buff, Clamp8, and right-shifts >> shift. Same !m_downsample || (โ€ฆ) guards, same pitch walk.

Both branches still do the cb progress ping and can bail with EscapePressed. Iconic behaviour, honestly.

And then ๐ŸŽ€ Lab color enters the chat:

       2125         case ImageModeLabColor:
       2126                 {
       2127                         ASSERT(m_header.channels == 3);
       2128                         ASSERT(m_header.bpp == m_header.channels*8);
       2129                         ASSERT(bpp%8 == 0);
       2130
       2131                         DataT* l = m_channel[0]; ASSERT(l);
       2132                         DataT* a = m_channel[1]; ASSERT(a);
       2133                         DataT* b = m_channel[2]; ASSERT(b);
       2134                         UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
       2135
       2136                         for (i=0; i < h; i++) {
       2137                                 UINT32 uPos = uOffset;
       2138                                 UINT32 yPos = yOffset;
       2139                                 cnt = 0;
       2140                                 for (j=0; j < w; j++) {
       2141                                         uAvg = a[uPos];
       2142                                         vAvg = b[uPos];
       2143                                         buff[cnt + channelMap[0]] = Clamp8(l[yPos] + YUVoffset8);
       2144                                         buff[cnt + channelMap[1]] = Clamp8(uAvg + YUVoffset8);
       2145                                         buff[cnt + channelMap[2]] = Clamp8(vAvg + YUVoffset8);
       2146                                         cnt += channels;
       2147                                         if (!m_downsample || (j & 1)) uPos++;
       2148                                         yPos++;
       2149                                 }
       2150                                 if (!m_downsample || (i & 1)) uOffset += uw;
       2151                                 yOffset += yw;
       2152                                 buff += pitch;
       2153

Three asserts up top: m_header.channels == 3, m_header.bpp == m_header.channels*8, and bpp%8 == 0. Then l, a, b get pulled from m_channel[0], [1], [2] โ€” renamed locals, same slots โ€” each asserted non-null. channels = bpp/8 with the >= m_header.channels assert, same as always.

No YUV math gymnastics here โ€” it's just adding YUVoffset8 to each of l[yPos], a[uPos], b[uPos] and clamping to 8 bits, writing them into buff at channelMap[0], [1], [2]. The !m_downsample || (j & 1) / (i & 1) advance guards are here too, yOffset += yw, buff += pitch.

...and the block keeps going, so hold that thought bestie, we're not done with this case yet ๐Ÿ’œ

okay so we are DEEP in the encoder guts now โ€” past the point of no return, unpacking every channel into the output buffer one row at a time. Strap in bestie, this is the pixel-pushing part. ๐Ÿซถ
       2154                                 if (cb) {
       2155                                         percent += dP;
       2156                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2157                                 }
       2158                         }
       2159                         break;
       2160                 }

see that cb situation? Every so often we bump percent by dP and poke the progress callback โ€” and if it comes back true, we hit ReturnWithError(EscapePressed) and dip immediately. Respecting the escape key is the whole personality here. ๐Ÿ’…


       2161         case ImageModeLab48:
       2162                 {
       2163                         ASSERT(m_header.channels == 3);
       2164                         ASSERT(m_header.bpp == m_header.channels*16);
       2165
       2166                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
       2167
       2168                         DataT* l = m_channel[0]; ASSERT(l);
       2169                         DataT* a = m_channel[1]; ASSERT(a);
       2170                         DataT* b = m_channel[2]; ASSERT(b);
       2171                         UINT32 cnt, channels;
       2172
       2173                         if (bpp%16 == 0) {
       2174                                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
       2175                                 UINT16 *buff16 = (UINT16 *)buff;
       2176                                 int pitch16 = pitch/2;
       2177                                 channels = bpp/16; ASSERT(channels >= m_header.channels);
       2178
       2179                                 for (i=0; i < h; i++) {
       2180                                         UINT32 uPos = uOffset;
       2181                                         UINT32 yPos = yOffset;
       2182                                         cnt = 0;
       2183                                         for (j=0; j < w; j++) {
       2184                                                 uAvg = a[uPos];
       2185                                                 vAvg = b[uPos];
       2186                                                 buff16[cnt + channelMap[0]] = Clamp16((l[yPos] + yuvOffset16) << shift);
       2187                                                 buff16[cnt + channelMap[1]] = Clamp16((uAvg + yuvOffset16) << shift);
       2188                                                 buff16[cnt + channelMap[2]] = Clamp16((vAvg + yuvOffset16) << shift);
       2189                                                 cnt += channels;
       2190                                                 if (!m_downsample || (j & 1)) uPos++;
       2191                                                 yPos++;
       2192                                         }
       2193                                         if (!m_downsample || (i & 1)) uOffset += uw;
       2194                                         yOffset += yw;
       2195                                         buff16 += pitch16;
       2196
       2197                                         if (cb) {
       2198                                                 percent += dP;
       2199                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2200                                         }
       2201                                 }
       2202                         } else {
       2203                                 ASSERT(bpp%8 == 0);
       2204                                 const int shift = __max(0, UsedBitsPerChannel() - 8);
       2205                                 channels = bpp/8; ASSERT(channels >= m_header.channels);
       2206
       2207                                 for (i=0; i < h; i++) {
       2208                                         UINT32 uPos = uOffset;
       2209                                         UINT32 yPos = yOffset;
       2210                                         cnt = 0;
       2211                                         for (j=0; j < w; j++) {
       2212                                                 uAvg = a[uPos];
       2213                                                 vAvg = b[uPos];
       2214                                                 buff[cnt + channelMap[0]] = Clamp8((l[yPos] + yuvOffset16) >> shift);
       2215                                                 buff[cnt + channelMap[1]] = Clamp8((uAvg + yuvOffset16) >> shift);
       2216                                                 buff[cnt + channelMap[2]] = Clamp8((vAvg + yuvOffset16) >> shift);
       2217                                                 cnt += channels;
       2218                                                 if (!m_downsample || (j & 1)) uPos++;
       2219                                                 yPos++;
       2220                                         }
       2221                                         if (!m_downsample || (i & 1)) uOffset += uw;
       2222                                         yOffset += yw;
       2223                                         buff += pitch;
       2224
       2225                                         if (cb) {
       2226                                                 percent += dP;
       2227                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2228                                         }
       2229                                 }
       2230                         }
       2231                         break;
       2232                 }

ImageModeLab48 struts in with three channels โ€” l, a, b โ€” and immediately asserts m_header.bpp == m_header.channels*16. Then it splits into two whole roads: the bpp%16 == 0 road goes wide with UINT16 *buff16, pitch16 = pitch/2 and Clamp16; the else road asserts bpp%8 == 0, works out shift = __max(0, UsedBitsPerChannel() - 8) and packs bytes with Clamp8. Two roads, one iconic output. ๐Ÿฆ‹


       2233         case ImageModeRGBA:
       2234         case ImageModeCMYKColor:
       2235                 {
       2236                         ASSERT(m_header.channels == 4);
       2237                         ASSERT(m_header.bpp == m_header.channels*8);
       2238                         ASSERT(bpp%8 == 0);
       2239
       2240                         DataT* y = m_channel[0]; ASSERT(y);
       2241                         DataT* u = m_channel[1]; ASSERT(u);
       2242                         DataT* v = m_channel[2]; ASSERT(v);
       2243                         DataT* a = m_channel[3]; ASSERT(a);
       2244                         UINT8 g, aAvg;
       2245                         UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
       2246
       2247                         for (i=0; i < h; i++) {
       2248                                 UINT32 uPos = uOffset;
       2249                                 UINT32 yPos = yOffset;
       2250                                 cnt = 0;
       2251                                 for (j=0; j < w; j++) {
       2252                                         uAvg = u[uPos];
       2253                                         vAvg = v[uPos];
       2254                                         aAvg = Clamp8(a[uPos] + YUVoffset8);
       2255                                         // Yuv
       2256                                         buff[cnt + channelMap[1]] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
       2257                                         buff[cnt + channelMap[2]] = Clamp8(uAvg + g);
       2258                                         buff[cnt + channelMap[0]] = Clamp8(vAvg + g);
       2259                                         buff[cnt + channelMap[3]] = aAvg;
       2260                                         cnt += channels;
       2261                                         if (!m_downsample || (j & 1)) uPos++;
       2262                                         yPos++;
       2263                                 }
       2264                                 if (!m_downsample || (i & 1)) uOffset += uw;
       2265                                 yOffset += yw;
       2266                                 buff += pitch;
       2267
       2268                                 if (cb) {
       2269                                         percent += dP;
       2270                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2271                                 }
       2272                         }
       2273                         break;
       2274                 }

ImageModeRGBA and ImageModeCMYKColor share a single case โ€” besties sharing a locker, honestly. Four channels, m_header.bpp == m_header.channels*8, channels = bpp/8, and there's a UINT8 g, aAvg; holding it down. Also: that // must be logical shift operator comment is right there in the source and we do NOT touch it. Not the comment, not the (uAvg + vAvg ) space before the paren โ€” that whitespace is canon, babes. ๐Ÿ’€


       2275         case ImageModeCMYK64:
       2276                 {
       2277                         ASSERT(m_header.channels == 4);
       2278                         ASSERT(m_header.bpp == 64);
       2279
       2280                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
       2281
       2282                         DataT* y = m_channel[0]; ASSERT(y);
       2283                         DataT* u = m_channel[1]; ASSERT(u);
       2284                         DataT* v = m_channel[2]; ASSERT(v);
       2285                         DataT* a = m_channel[3]; ASSERT(a);
       2286                         DataT g, aAvg;
       2287                         UINT32 cnt, channels;
       2288
       2289                         if (bpp%16 == 0) {
       2290                                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
       2291                                 UINT16 *buff16 = (UINT16 *)buff;
       2292                                 int pitch16 = pitch/2;
       2293                                 channels = bpp/16; ASSERT(channels >= m_header.channels);
       2294
       2295                                 for (i=0; i < h; i++) {
       2296                                         UINT32 uPos = uOffset;
       2297                                         UINT32 yPos = yOffset;
       2298                                         cnt = 0;
       2299                                         for (j=0; j < w; j++) {
       2300                                                 uAvg = u[uPos];
       2301                                                 vAvg = v[uPos];
       2302                                                 aAvg = a[uPos] + yuvOffset16;
       2303                                                 // Yuv
       2304                                                 g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
       2305                                                 buff16[cnt + channelMap[1]] = Clamp16(g << shift);
       2306                                                 buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
       2307                                                 buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
       2308                                                 buff16[cnt + channelMap[3]] = Clamp16(aAvg << shift);
       2309                                                 cnt += channels;
       2310                                                 if (!m_downsample || (j & 1)) uPos++;
       2311                                                 yPos++;
       2312                                         }
       2313                                         if (!m_downsample || (i & 1)) uOffset += uw;
       2314                                         yOffset += yw;
       2315                                         buff16 += pitch16;
       2316
       2317                                         if (cb) {
       2318                                                 percent += dP;
       2319                                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2320                                         }
       2321                                 }
       2322                         } else {
       2323                                 ASSERT(bpp%8 == 0);
       2324                                 const int shift = __max(0, UsedBitsPerChannel() - 8);
       2325                                 channels = bpp/8; ASSERT(channels >= m_header.channels);
       2326
       2327                                 for (i=0; i < h; i++) {
       2328                                         UINT32 uPos = uOffset;

ImageModeCMYK64 is the last case standing โ€” four channels, m_header.bpp == 64, yuvOffset16 = 1 << (UsedBitsPerChannel() - 1), Clamp16, buff16 += pitch16, the whole 16-bit fit. And then the bpp%16 == 0 test fails and it slides into the else, asserts bpp%8 == 0, sets shift = __max(0, UsedBitsPerChannel() - 8)โ€ฆ and the for (i=0; i < h; i++) loop has just opened, UINT32 uPos = uOffset; and all. ๐Ÿ‘‘

and that is literally where we stop, mid-loop, mid-row, mid-vibe โ€” no closure, only momentum ๐Ÿ’–โœจ

okay bestie we are so deep in the guts of GetBitmap() right now, and honestly? it's giving brain surgery ๐Ÿง โœจ โ€” this is the bit where the actual pixels get shoved into your buffer. Picking up mid-loop in the YUVโ†’RGBA conversion, no rewind, no recap, we just keep going:

                                        UINT32 yPos = yOffset;
                                        cnt = 0;
                                        for (j=0; j < w; j++) {
                                                uAvg = u[uPos];
                                                vAvg = v[uPos];
                                                aAvg = a[uPos] + yuvOffset16;
                                                // Yuv
                                                g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
                                                buff[cnt + channelMap[1]] = Clamp8(g >> shift);
                                                buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
                                                buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
                                                buff[cnt + channelMap[3]] = Clamp8(aAvg >> shift);
                                                cnt += channels;
                                                if (!m_downsample || (j & 1)) uPos++;
                                                yPos++;
                                        }
                                        if (!m_downsample || (i & 1)) uOffset += uw;
                                        yOffset += yw;
                                        buff += pitch;

                                        if (cb) {
                                                percent += dP;
                                                if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        }
                                }
                        }
                        break;
                }

Little things worth pointing at with a stick, because they're lowkey iconic:

  • the line with g = ... carries a comment that is SCREAMING at you: // must be logical shift operator. noted. do not touch. ๐Ÿ™…โ€โ™€๏ธ
  • every single channel write goes through Clamp8, and the order is channelMap[1], channelMap[2], channelMap[0], channelMap[3] โ€” so like, not hardcoded, the map decides.
  • cnt += channels; โ€” the stride moves by however many channels we've got.
  • the chroma pointer uPos only moves when !m_downsample or (j & 1) โ€” and uOffset only moves when !m_downsample or (i & 1).
  • yOffset += yw; and buff += pitch; close out each row, and if there's a callback cb, it does percent += dP; and can bail out early by returning EscapePressed through ReturnWithError. ๐Ÿ’€

and then break; and the case block closes. bye bestie ๐Ÿซถ


now, gated content alert ๐ŸŒˆ โ€” the next whole mode only exists if __PGF32SUPPORT__ is defined. it's ImageModeGray32, it asserts m_header.channels == 1 and m_header.bpp == 32, and then it branches three whole ways on bpp:

#ifdef __PGF32SUPPORT__
        case ImageModeGray32:
                {
                        ASSERT(m_header.channels == 1);
                        ASSERT(m_header.bpp == 32);

                        const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
                        DataT* y = m_channel[0]; ASSERT(y);

                        if (bpp == 32) {
                                const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
                                UINT32 *buff32 = (UINT32 *)buff;
                                int pitch32 = pitch/4;

                                for (i=0; i < h; i++) {
                                        UINT32 yPos = yOffset;
                                        for (j = 0; j < w; j++) {
                                                buff32[j] = Clamp31((y[yPos++] + yuvOffset31) << shift);
                                        }
                                        yOffset += yw;
                                        buff32 += pitch32;

                                        if (cb) {
                                                percent += dP;
                                                if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        }
                                }
                        } else if (bpp == 16) {
                                const int usedBits = UsedBitsPerChannel();
                                UINT16 *buff16 = (UINT16 *)buff;
                                int pitch16 = pitch/2;

                                if (usedBits < 16) {
                                        const int shift = 16 - usedBits;
                                        for (i=0; i < h; i++) {
                                                UINT32 yPos = yOffset;
                                                for (j = 0; j < w; j++) {
                                                        buff16[j] = Clamp16((y[yPos++] + yuvOffset31) << shift);
                                                }
                                                yOffset += yw;
                                                buff16 += pitch16;

                                                if (cb) {
                                                        percent += dP;
                                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                                }
                                        }
                                } else {
                                        const int shift = __max(0, usedBits - 16);
                                        for (i=0; i < h; i++) {
                                                UINT32 yPos = yOffset;
                                                for (j = 0; j < w; j++) {
                                                        buff16[j] = Clamp16((y[yPos++] + yuvOffset31) >> shift);
                                                }
                                                yOffset += yw;
                                                buff16 += pitch16;

                                                if (cb) {
                                                        percent += dP;
                                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                                }
                                        }
                                }
                        } else {
                                ASSERT(bpp == 8);
                                const int shift = __max(0, UsedBitsPerChannel() - 8);

                                for (i=0; i < h; i++) {
                                        UINT32 yPos = yOffset;
                                        for (j = 0; j < w; j++) {
                                                buff[j] = Clamp8((y[yPos++] + yuvOffset31) >> shift);
                                        }
                                        yOffset += yw;
                                        buff += pitch;

                                        if (cb) {
                                                percent += dP;
                                                if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        }
                                }
                        }
                        break;
                }
#endif

okay so the tea โ˜• on this one:

  • yuvOffset31 = 1 << (UsedBitsPerChannel() - 1); is computed once at the top, and DataT* y = m_channel[0]; gets an ASSERT(y) for safety. only one channel, because gray. obviously.
  • bpp == 32: shift = 31 - UsedBitsPerChannel() with ASSERT(shift >= 0) because the math has to be sane, then a UINT32 *buff32 view with pitch32 = pitch/4, and every pixel is Clamp31((y[yPos++] + yuvOffset31) << shift). ๐Ÿ‘‘
  • bpp == 16: usedBits = UsedBitsPerChannel(), UINT16 *buff16, pitch16 = pitch/2, and then it splits AGAIN โ€” if usedBits < 16 it's shift = 16 - usedBits and a left shift << shift; otherwise shift = __max(0, usedBits - 16) and a right shift >> shift. both clamp through Clamp16. no cap, it really said "two more branches". ๐Ÿ˜ญ
  • anything else: ASSERT(bpp == 8); and shift = __max(0, UsedBitsPerChannel() - 8);, writing plain bytes with Clamp8.
  • all three paths keep the exact same progress-callback ritual โ€” percent += dP; and ReturnWithError(EscapePressed) if cb says stop.

then #endif and we're out of the gated zone. โœจ


next up: ImageModeRGB12. asserts on channels == 3, m_header.bpp == m_header.channels*4, bpp == m_header.channels*4, and !m_downsample. three channel pointers, one UINT16 yval, one UINT32 cnt:

        case ImageModeRGB12:
                {
                        ASSERT(m_header.channels == 3);
                        ASSERT(m_header.bpp == m_header.channels*4);
                        ASSERT(bpp == m_header.channels*4);
                        ASSERT(!m_downsample);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);
                        UINT16 yval;
                        UINT32 cnt;

                        for (i=0; i < h; i++) {
                                UINT32 yPos = yOffset;
                                cnt = 0;
                                for (j=0; j < w; j++) {
                                        // Yuv
                                        uAvg = u[yPos];
                                        vAvg = v[yPos];
                                        yval = Clamp4(y[yPos] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
                                        if (j%2 == 0) {
                                                buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4));
                                                cnt++;
                                                buff[cnt] = Clamp4(uAvg + yval);
                                        } else {
                                                buff[cnt] |= Clamp4(vAvg + yval) << 4;
                                                cnt++;
                                                buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));
                                                cnt++;
                                        }
                                        yPos++;
                                }
                                yOffset += yw;
                                buff += pitch;

                                if (cb) {
                                        percent += dP;
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                }
                        }
                        break;
                }

notes bestie, because this one is fancy:

  • Clamp4 all over the place and YUVoffset4, with that same // must be logical shift operator reminder on the >> 2.
  • it's packing nibbles ๐ŸŽ€ โ€” on even j it writes UINT8(Clamp4(vAvg + yval) | (yval << 4)) then cnt++ and buff[cnt] = Clamp4(uAvg + yval); on odd j it does buff[cnt] |= Clamp4(vAvg + yval) << 4;, cnt++, then buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));, cnt++.
  • yPos++ is unconditional here, and there's no m_downsample anything โ€” the assert already swore it off.
  • same callback, same EscapePressed, same break;. consistency is hot. ๐Ÿ’…

and then, right as it gets good, ImageModeRGB16. asserts channels == 3, m_header.bpp == 16, bpp == 16, !m_downsample. grab a snack ๐Ÿ“:

        case ImageModeRGB16:
                {
                        ASSERT(m_header.channels == 3);
                        ASSERT(m_header.bpp == 16);
                        ASSERT(bpp == 16);
                        ASSERT(!m_downsample);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);
                        UINT16 yval;
                        UINT16 *buff16 = (UINT16 *)buff;
                        int pitch16 = pitch/2;

                        for (i=0; i < h; i++) {
                                UINT32 yPos = yOffset;
                                for (j = 0; j < w; j++) {
                                        // Yuv
                                        uAvg = u[yPos];
                                        vAvg = v[yPos];
                                        yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
                                        buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1);
                                }
                                yOffset += yw;
                                buff16 += pitch16;

and that is literally where this part cuts off ๐Ÿ˜ญ โ€” mid-loop, mid-case, buff16 still moving. we pick it back up in the next one, bestie, don't go anywhere ๐Ÿซถ

okay bestie we're literally mid-function right now, so here's the rest of that code, no interruptions ๐Ÿ’…

       2510                                 if (cb) {
       2511                                         percent += dP;
       2512                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2513                                 }
       2514                         }
       2515                         break;
       2516                 }
       2517         default:
       2518                 ASSERT(false);
       2519         }
       2520
       2521 #ifdef _DEBUG
       2522         // display ROI (RGB) in debugger
       2523         roiimage.width = w;
       2524         roiimage.height = h;
       2525         if (pitch > 0) {
       2526                 roiimage.pitch = pitch;
       2527                 roiimage.data = buff;
       2528         } else {
       2529                 roiimage.pitch = -pitch;
       2530                 roiimage.data = buff + (h - 1)*pitch;
       2531         }
       2532 #endif
       2533
       2534 }

DataT * CPGFImage::GetChannel (int c = 0) [inline]

Okay so this one hands you back an internal YUV image channel, like it's passing you a lil snack ๐Ÿ“

Parameters

  • c โ€” A channel index

Returns

An internal YUV image channel

Definition at line 317 of file PGFimage.h.

317 { ASSERT(c >= 0 && c < MaxChannels); return m_channel[c]; }

const RGBQUAD * CPGFImage::GetColorTable () const [inline]

Returns

Address of color table ๐ŸŽจ (yes literally just the address, no drama, no cap)

Definition at line 330 of file PGFimage.h.

330 { return m_postHeader.clut; }

void CPGFImage::GetColorTable (UINT32 iFirstColor, UINT32 nColors, RGBQUAD * prgbColors) const

This one retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section. Heads up bestie: it might throw an IOException. It's giving "handle your exceptions, queen" ๐Ÿ˜ญ

Parameters

  • iFirstColor โ€” The color table index of the first entry to retrieve.
  • nColors โ€” The number of color table entries to retrieve.
  • prgbColors โ€” A pointer to the array of RGBQUAD structures to retrieve the color table entries.

Definition at line 1350 of file PGFimage.cpp.

       1350                                                                                            {
       1351         if (iFirstColor + nColors > ColorTableLen)      ReturnWithError(ColorTableError);
       1352
       1353         for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
       1354                 prgbColors[j] = m_postHeader.clut[i];
       1355         }
       1356 }

UINT32 CPGFImage::GetEncodedHeaderLength () const

Return the length of all encoded headers in bytes. Precondition: The PGF image has been opened with a call of Open(...). So likeโ€ฆ open it first, bestie, obviously ๐Ÿซถ

Returns

The length of all encoded headers in bytes

Definition at line 648 of file PGFimage.cpp.

       648                                                {
       649         ASSERT(m_decoder);
       650         return m_decoder->GetEncodedHeaderLength();
       651 }

UINT32 CPGFImage::GetEncodedLevelLength (int level) const [inline]

Return the length of an encoded PGF level in bytes. Precondition (again, iconic recurring theme): The PGF image has been opened with a call of Open(...). โœจ

Parameters

  • level โ€” The image level

Returns

The length of a PGF level in bytes

Definition at line 367 of file PGFimage.h.

367 { ASSERT(level >= 0 && level < m_header.nLevels); return m_levelLength[m_header.nLevels - level - 1]; }

const PGFHeader * CPGFImage::GetHeader () const [inline]

Return the PGF header structure. That's it, that's the whole vibe ๐Ÿ‘‘

Returns

A PGF header structure

Definition at line 335 of file PGFimage.h.

335 { return &m_header; }

UINT32 CPGFImage::GetMaxValue () const [inline]

Get maximum intensity value for image modes with more than eight bits per channel. Don't call this method before the PGF header has been read. Like, seriously, don't โ€” it'll be giving chaos ๐Ÿ’€

Returns

The maximum intensity value.

Definition at line 341 of file PGFimage.h.

341 { return (1 << m_header.usedBitsPerChannel) - 1; }

const UINT8 * CPGFImage::GetUserData (UINT32 & cachedSize, UINT32 * pTotalSize = nullptr) const

Return user data and size of user data. Precondition: The PGF image has been opened with a call of Open(...). ๐ŸŒธ

Parameters

  • cachedSize โ€” [out] Size of returned user data in bytes.
  • pTotalSize โ€” [optional out] Pointer to return the size of user data stored in image header in bytes.

Returns

A pointer to user data or nullptr if there is no user data available.

Return user data and size of user data. Precondition: The PGF image has been opened with a call of Open(...). In an encoder scenario don't call this method before WriteHeader(). ๐Ÿซถ

Parameters

  • cachedSize โ€” [out] Size of returned user data in bytes.
  • pTotalSize โ€” [optional out] Pointer to return the size of user data stored in image header in bytes.

Returns

A pointer to user data or nullptr if there is no user data available.

Definition at line 337 of file PGFimage.cpp.

       337                                                                                   {
       338         cachedSize = m_postHeader.cachedUserDataLen;
       339         if (pTotalSize) *pTotalSize = m_postHeader.userDataLen;
       340         return m_postHeader.userData;
       341 }

UINT64 CPGFImage::GetUserDataPos () const [inline]

Return the stream position of the user data or 0. Precondition: The PGF image has been opened with a call of Open(...). (yes bestie, the Open(...) lore continues, it's a whole saga ๐Ÿฆ‹)

Definition at line 346 of file PGFimage.h.

346 { return m_userDataPos; }

void CPGFImage::GetYUV (int pitch, DataT * buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void * data = nullptr) const

Get YUV image data in interleaved format: (ordering is YUV[A]). The absolute value of pitch is the number of bytes of an image row of the given image buffer. If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row). if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte). The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode. If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }. It might throw an IOException. ๐Ÿ˜ญ

Parameters

  • pitch โ€” The number of bytes of a row of the image buffer.
  • buff โ€” An image buffer.
  • bpp โ€” The number of bits per pixel used in image buffer.
  • channelMap โ€” A integer array containing the mapping of PGF channel ordering to expected channel ordering.
  • cb โ€” A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.

Get YUV image data in interleaved format: (ordering is YUV[A]). The absolute value of pitch is the number of bytes of an image row of the given image buffer. If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row). if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte). The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode. If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }. It might throw an IOException. ๐Ÿ™ˆ

Parameters

  • pitch โ€” The number of bytes of a row of the image buffer.
  • buff โ€” An image buffer.
  • bpp โ€” The number of bits per pixel used in image buffer.
  • channelMap โ€” A integer array containing the mapping of PGF channel ordering to expected channel ordering.
  • cb โ€” A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
       Definition at line 2550 of file PGFimage.cpp.
       2550                                                                                                                  {
       2551         ASSERT(buff);
       2552         const UINT32 w = m_width[0];
       2553         const UINT32 h = m_height[0];
       2554         const bool wOdd = (1 == w%2);
       2555         const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
       2556         const int pitch2 = pitch/DataTSize;
       2557         const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
       2558         const double dP = 1.0/h;
       2559
       2560         int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
       2561         if (channelMap == nullptr) channelMap = defMap;
       2562         int sampledPos = 0, yPos = 0;
       2563         DataT uAvg, vAvg;
       2564         double percent = 0;
       2565         UINT32 i, j;
       2566
       2567         if (m_header.channels == 3) {
       2568                 ASSERT(bpp%dataBits == 0);
       2569
       2570                 DataT* y = m_channel[0]; ASSERT(y);
       2571                 DataT* u = m_channel[1]; ASSERT(u);
       2572                 DataT* v = m_channel[2]; ASSERT(v);
       2573                 int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
       2574
       2575                 for (i=0; i < h; i++) {
       2576                         if (i%2) sampledPos -= (w + 1)/2;
       2577                         cnt = 0;
       2578                         for (j=0; j < w; j++) {
       2579                                 if (m_downsample) {
       2580                                         // image was downsampled
       2581                                         uAvg = u[sampledPos];
       2582                                         vAvg = v[sampledPos];
       2583                                 } else {
       2584                                         uAvg = u[yPos];
       2585                                         vAvg = v[yPos];
       2586                                 }
       2587                                 buff[cnt + channelMap[0]] = y[yPos];
       2588                                 buff[cnt + channelMap[1]] = uAvg;
       2589                                 buff[cnt + channelMap[2]] = vAvg;
       2590                                 yPos++;
       2591                                 cnt += channels;
       2592                                 if (j%2) sampledPos++;
       2593                         }
       2594                         buff += pitch2;
       2595                         if (wOdd) sampledPos++;
       2596
       2597                         if (cb) {
       2598                                 percent += dP;
       2599                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2600                         }
       2601                 }
       2602         } else if (m_header.channels == 4) {
       2603                 ASSERT(m_header.bpp == m_header.channels*8);
       2604                 ASSERT(bpp%dataBits == 0);
       2605
       2606                 DataT* y = m_channel[0]; ASSERT(y);
       2607                 DataT* u = m_channel[1]; ASSERT(u);
       2608                 DataT* v = m_channel[2]; ASSERT(v);
       2609                 DataT* a = m_channel[3]; ASSERT(a);
       2610                 UINT8 aAvg;
       2611                 int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
       2612
       2613                 for (i=0; i < h; i++) {
       2614                         if (i%2) sampledPos -= (w + 1)/2;
       2615                         cnt = 0;
       2616                         for (j=0; j < w; j++) {
       2617                                 if (m_downsample) {
       2618                                         // image was downsampled
       2619                                         uAvg = u[sampledPos];
       2620                                         vAvg = v[sampledPos];
       2621                                         aAvg = Clamp8(a[sampledPos] + yuvOffset);
       2622                                 } else {
       2623                                         uAvg = u[yPos];
       2624                                         vAvg = v[yPos];
       2625                                         aAvg = Clamp8(a[yPos] + yuvOffset);
       2626                                 }
       2627                                 // Yuv
       2628                                 buff[cnt + channelMap[0]] = y[yPos];
       2629                                 buff[cnt + channelMap[1]] = uAvg;
       2630                                 buff[cnt + channelMap[2]] = vAvg;
       2631                                 buff[cnt + channelMap[3]] = aAvg;
       2632                                 yPos++;
       2633                                 cnt += channels;
       2634                                 if (j%2) sampledPos++;
       2635                         }
       2636                         buff += pitch2;
       2637                         if (wOdd) sampledPos++;
       2638
       2639                         if (cb) {
       2640                                 percent += dP;
       2641                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       2642                         }
       2643                 }
       2644         }
       2645 }

๐ŸŒธ UINT32 CPGFImage::Height (int level = 0) const [inline] ๐ŸŒธ

Bestie, this one gives you the image height of channel 0 at whatever level you're asking about, measured in pixels. And the tea here? ๐Ÿ‘€ The height it hands back is completely independent of any Read-operations and any ROI. So like, it doesn't care what you've been reading or cropping โ€” it just tells you the truth. ๐Ÿ’…

Parameters

  • level โ€” a level

Returns

  • the image level height, in pixels

Definition at line 420 of file PGFimage.h.

       420 { ASSERT(level >= 0); return LevelSizeL(m_header.height, level); }

๐Ÿฆ‹ void CPGFImage::ImportBitmap (int pitch, UINT8 * buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void * data = nullptr) ๐Ÿฆ‹

Okay so this is the move where you pull in an image from a specific image buffer. โœจ Normally you call this before Write(...) and after SetHeader(...) โ€” that's the order, don't fight it.

The absolute value of pitch is the number of bytes of an image row. Here's the vibe check:

  • If pitch is negative, then buff points to the last row of a bottom-up image (first byte on the last row).
  • If pitch is positive, then buff points to the first row of a top-down image (first byte).

Now, the sequence of input channels in your input image buffer does not need to match what PGF expects. Fr fr. If the sequences are different you have to hand over a channelMap that's the size of the expected channels (which depends on the image mode). Example time ๐Ÿ’… โ€” PGF expects, in RGB color mode, a channel sequence of BGR. So if your image buffer has the channel sequence ARGB, then your channelMap looks like { 3, 2, 1, 0 }. Iconic.

Also heads up: it might throw an IOException. ๐Ÿ˜ญ

Parameters

  • pitch โ€” the number of bytes of a row of the image buffer.
  • buff โ€” an image buffer.
  • bpp โ€” the number of bits per pixel used in the image buffer.
  • channelMap โ€” an integer array holding the mapping of input channel ordering to the expected channel ordering.
  • cb โ€” a pointer to a callback procedure. The procedure gets called after each imported buffer row. If cb returns true, then it stops proceeding. ๐Ÿ›‘
  • data โ€” data pointer to a C++ class container to host the callback procedure.

Definition at line 792 of file PGFimage.cpp.

       792                                                                                                                  {
       793         ASSERT(buff);
       794         ASSERT(m_channel[0]);
       795
       796         // color transform
       797         RgbToYuv(pitch, buff, bpp, channelMap, cb, data);
       798
       799         if (m_downsample) {
       800                 // Subsampling of the chrominance and alpha channels
       801                 for (int i=1; i < m_header.channels; i++) {
       802                         Downsample(i);
       803                 }
       804         }
       805 }

๐ŸŽ€ bool CPGFImage::ImportIsSupported (BYTE mode) [static] ๐ŸŽ€

This is the vibe check for whether an import image mode is even valid. It's static, so it's just sitting there ready to tell you yes or no. โœจ

Parameters

  • mode โ€” the image mode

Returns

  • true if an image of the given mode can be imported with ImportBitmap(...)

Definition at line 1305 of file PGFimage.cpp.

       1305                                            {
       1306         size_t size = DataTSize;
       1307
       1308         if (size >= 2) {
       1309                 switch(mode) {
       1310                         case ImageModeBitmap:
       1311                         case ImageModeIndexedColor:
       1312                         case ImageModeGrayScale:
       1313                         case ImageModeRGBColor:
       1314                         case ImageModeCMYKColor:
       1315                         case ImageModeHSLColor:
       1316                         case ImageModeHSBColor:
       1317                         //case ImageModeDuotone:
       1318                         case ImageModeLabColor:
       1319                         case ImageModeRGB12:
       1320                         case ImageModeRGB16:
       1321                         case ImageModeRGBA:
       1322                                 return true;
       1323                 }
       1324         }
       1325         if (size >= 3) {
       1326                 switch(mode) {
       1327                         case ImageModeGray16:
       1328                         case ImageModeRGB48:
       1329                         case ImageModeLab48:
       1330                         case ImageModeCMYK64:
       1331                         //case ImageModeDuotone16:
       1332                                 return true;
       1333                 }
       1334         }
       1335         if (size >=4) {
       1336                 switch(mode) {
       1337                         case ImageModeGray32:
       1338                                 return true;
       1339                 }
       1340         }
       1341         return false;
       1342 }
Spotted it? ๐Ÿ‘€ Those //case ImageModeDuotone: and //case ImageModeDuotone16: lines are commented out in the source โ€” they are not supported here. Don't come for me, I'm just the messenger. ๐Ÿ’€

๐Ÿงš void CPGFImage::ImportYUV (int pitch, DataT * buff, BYTE bpp, int channelMap[] = nullptr, CallbackPtr cb = nullptr, void * data = nullptr) ๐Ÿงš

Now we're importing a YUV image from a specified image buffer. Same energy as before, bestie:

The absolute value of pitch is the number of bytes of an image row, and:

  • If pitch is negative, then buff points to the last row of a bottom-up image (first byte on the last row).
  • If pitch is positive, then buff points to the first row of a top-down image (first byte).

The sequence of input channels in the input image buffer doesn't have to match what PGF expects โ€” same deal as before. If the sequences differ, you provide a channelMap of the size of the expected channels (depends on the image mode). Example, since examples are everything ๐Ÿ’…: PGF expects, in RGB color mode, a channel sequence of BGR. If your provided image buffer holds the channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.

And yes, it might throw an IOException. ๐Ÿ˜ญ

Parameters

  • pitch โ€” the number of bytes of a row of the image buffer.
  • buff โ€” an image buffer.
  • bpp โ€” the number of bits per pixel used in the image buffer.
  • channelMap โ€” an integer array containing the mapping of input channel ordering to the expected channel ordering.
  • cb โ€” a pointer to a callback procedure. The procedure is called after each imported buffer row. If cb returns true, then it stops proceeding.
  • data โ€” data pointer to a C++ class container to host the callback procedure.
It might throw an IOException. Bestie, it's giving "handle your exceptions or perish". ๐Ÿ’€

Parameters

  • pitch โ€” how many bytes one row of the image buffer eats up.
  • buff โ€” the image buffer itself, the whole vibe.
  • bpp โ€” bits per pixel used in that image buffer.
  • channelMap โ€” an integer array holding the mapping from your input channel ordering to the expected channel ordering.
  • cb โ€” a pointer to a callback procedure. It gets called after each imported buffer row, and if cb returns true, the whole thing stops proceeding. Like, it taps out. โœ‹

Definition at line 2661 of file PGFimage.cpp.

2661                                                                                                               {
2662         ASSERT(buff);
2663         const double dP = 1.0/m_header.height;
2664         const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
2665         const int pitch2 = pitch/DataTSize;
2666         const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
2667
2668         int yPos = 0, cnt = 0;
2669         double percent = 0;
2670         int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
2671
2672         if (channelMap == nullptr) channelMap = defMap;
2673
2674         if (m_header.channels == 3)      {
2675                 ASSERT(bpp%dataBits == 0);
2676
2677                 DataT* y = m_channel[0]; ASSERT(y);
2678                 DataT* u = m_channel[1]; ASSERT(u);
2679                 DataT* v = m_channel[2]; ASSERT(v);
2680                 const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2681
2682                 for (UINT32 h=0; h < m_header.height; h++) {
2683                         if (cb) {
2684                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2685                                 percent += dP;
2686                         }
2687
2688                         cnt = 0;
2689                         for (UINT32 w=0; w < m_header.width; w++) {
2690                                 y[yPos] = buff[cnt + channelMap[0]];
2691                                 u[yPos] = buff[cnt + channelMap[1]];
2692                                 v[yPos] = buff[cnt + channelMap[2]];
2693                                 yPos++;
2694                                 cnt += channels;
2695                         }
2696                         buff += pitch2;
2697                 }
2698         } else if (m_header.channels == 4) {
2699                 ASSERT(bpp%dataBits == 0);
2700
2701                 DataT* y = m_channel[0]; ASSERT(y);
2702                 DataT* u = m_channel[1]; ASSERT(u);
2703                 DataT* v = m_channel[2]; ASSERT(v);
2704                 DataT* a = m_channel[3]; ASSERT(a);
2705                 const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2706
2707                 for (UINT32 h=0; h < m_header.height; h++) {
2708                         if (cb) {
2709                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2710                                 percent += dP;
2711                         }
2712
2713                         cnt = 0;
2714                         for (UINT32 w=0; w < m_header.width; w++) {
2715                                 y[yPos] = buff[cnt + channelMap[0]];
2716                                 u[yPos] = buff[cnt + channelMap[1]];
2717                                 v[yPos] = buff[cnt + channelMap[2]];
2718                                 a[yPos] = buff[cnt + channelMap[3]] - yuvOffset;
2719                                 yPos++;
2720                                 cnt += channels;
2721                         }
2722                         buff += pitch2;
2723                 }
2724         }
2725
2726         if (m_downsample) {
2727                 // Subsampling of the chrominance and alpha channels
2728                 for (int i=1; i < m_header.channels; i++) {
2729                         Downsample(i);
2730                 }
2731         }
2732 }

CPGFImage::Init () [private]

Definition at line 69 of file PGFimage.cpp.

69                      {
70         // init pointers
71         m_decoder = nullptr;
72         m_encoder = nullptr;
73         m_levelLength = nullptr;
74
75         // init members
76 #ifdef __PGFROISUPPORT__
77         m_streamReinitialized = false;
78 #endif
79         m_currentLevel = 0;
80         m_quant = 0;
81         m_userDataPos = 0;
82         m_downsample = false;
83         m_favorSpeedOverSize = false;
84         m_useOMPinEncoder = true;
85         m_useOMPinDecoder = true;
86         m_cb = nullptr;
87         m_cbArg = nullptr;
88         m_progressMode = PM_Relative;
89         m_percent = 0;
90         m_userDataPolicy = UP_CacheAll;
91
92         // init preHeader
93         memcpy(m_preHeader.magic, PGFMagic, 3);
94         m_preHeader.version = PGFVersion;
95         m_preHeader.hSize = 0;
96
97         // init postHeader
98         m_postHeader.userData = nullptr;
99         m_postHeader.userDataLen = 0;
100         m_postHeader.cachedUserDataLen = 0;
101
102         // init channels
103         for (int i = 0; i < MaxChannels; i++) {
104                 m_channel[i] = nullptr;
105                 m_wtChannel[i] = nullptr;
106         }
107
108         // set image width and height
109         for (int i = 0; i < MaxChannels; i++) {
110                 m_width[0] = 0;
111                 m_height[0] = 0;
112         }
113 }

bool CPGFImage::IsFullyRead () const [inline]

Returns true if all levels have been read. That's it, that's the whole function, she's an icon of brevity. ๐Ÿ‘‘

Definition at line 436 of file PGFimage.h.

436 { return m_currentLevel == 0; }

bool CPGFImage::IsOpen () const [inline]

Returns true if the PGF has been opened for reading. Simple. Clean. Slays. โœจ

Definition at line 77 of file PGFimage.h.

77 { return m_decoder != nullptr; }

BYTE CPGFImage::Level () const [inline]

Returns the current image level. Since Read(...) can be used to read each image level separately, it's genuinely helpful to know which level you're currently standing in, bestie. And plot twist: the current level immediately after Open(...) is Levels(). ๐ŸŒˆ

Returns

Current image level

Definition at line 427 of file PGFimage.h.

427 { return (BYTE)m_currentLevel; }

BYTE CPGFImage::Levels () const [inline]

Returns the number of image levels. Just the count. The headcount. ๐ŸŽ€

Returns

Number of image levels

Definition at line 432 of file PGFimage.h.

432 { return m_header.nLevels; }

UINT32 CPGFImage::LevelSizeH (UINT32 size, int level) [inline], [static]

Computes and returns the image width/height of the HH subband at a given level. High-pass math, low-key gorgeous. ๐Ÿฆ‹

Parameters

  • size โ€” the original image size (like, the width or height at level 0).
  • level โ€” an image level.

Returns

high pass size at given level in pixels

Definition at line 506 of file PGFimage.h.

506 { ASSERT(level >= 0); UINT32 d = 1 << (level - 1); return (size + d - 1) >> level; }

UINT32 CPGFImage::LevelSizeL (UINT32 size, int level) [inline], [static]

Computes and returns the image width/height of the LL subband at a given level. Same energy, different band. ๐Ÿ’œ

Parameters

  • size โ€” the original image size (e.g. width or height at level 0).
  • level โ€” an image level.

Returns

Image width/height at given level in pixels

Definition at line 499 of file PGFimage.h.

499 { ASSERT(level >= 0); UINT32 d = 1 << level; return (size + d - 1) >> level; }

BYTE CPGFImage::MaxChannelDepth (BYTE version = PGFVersion) [inline], [static]

Returns the maximum channel depth. Yes, version defaults to PGFVersion, we love a default. ๐Ÿซถ

Parameters

  • version โ€” pgf pre-header version number.

Returns

maximum channel depth in bit of given version (16 or 32 bit)

Definition at line 518 of file PGFimage.h.

518 { return (version & PGF32) ? 32 : 16; }

BYTE CPGFImage::Mode () const [inline]

Returns the image mode. An image mode is a predefined constant value (peep PGFtypes.h, iykyk) compatible with Adobe Photoshop, and it represents an image type and format. Basically the image's whole identity. ๐Ÿ’…

Returns

Image mode

Definition at line 455 of file PGFimage.h.

455 { return m_header.mode; }

void CPGFImage::Open (CPGFStream * stream)

Opens a PGF image at the current stream position: reads the pre-header, the header, and checks the image type. Precondition, and hear me out: the stream has been opened for reading. If not, that's on you, bestie. It might throw an IOException. ๐Ÿ˜ญ

Parameters

  • stream โ€” a PGF stream.
๐Ÿ“ source drop: Definition at line 141 of file PGFimage.cpp. โ€” buckle up bestie, this one is doing the ENTIRE opening routine and she's not even out of breath ๐Ÿ’…
       141                                        {
       142         ASSERT(stream);
       143
       144         // create decoder and read PGFPreHeader PGFHeader PGFPostHeader LevelLengths
       145         m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength,
       146                 m_userDataPos, m_useOMPinDecoder, m_userDataPolicy);
       147
       148         if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead);
       149
       150         // set current level
       151         m_currentLevel = m_header.nLevels;
       152
       153         // set image width and height
       154         m_width[0] = m_header.width;
       155         m_height[0] = m_header.height;
       156
       157         // complete header
       158         if (!CompleteHeader()) ReturnWithError(FormatCannotRead);
       159
       160         // interpret quant parameter
       161         if (m_header.quality > DownsampleThreshold &&
       162                 (m_header.mode == ImageModeRGBColor ||
       163                  m_header.mode == ImageModeRGBA ||
       164                  m_header.mode == ImageModeRGB48 ||
       165                  m_header.mode == ImageModeCMYKColor ||
       166                  m_header.mode == ImageModeCMYK64 ||
       167                  m_header.mode == ImageModeLabColor ||
       168                  m_header.mode == ImageModeLab48)) {
       169                 m_downsample = true;
       170                 m_quant = m_header.quality - 1;
       171         } else {
       172                 m_downsample = false;
       173                 m_quant = m_header.quality;
       174         }
       175
       176         // set channel dimensions (chrominance is subsampled by factor 2)
       177         if (m_downsample) {
       178                 for (int i=1; i < m_header.channels; i++) {
       179                         m_width[i] = (m_width[0] + 1) >> 1;
       180                         m_height[i] = (m_height[0] + 1) >> 1;
       181                 }
       182         } else {
       183                 for (int i=1; i < m_header.channels; i++) {
       184                         m_width[i] = m_width[0];
       185                         m_height[i] = m_height[0];
       186                 }
       187         }
       188
       189         if (m_header.nLevels > 0) {
       190                 // init wavelet subbands
       191                 for (int i=0; i < m_header.channels; i++) {
       192                         m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels);
       193                 }
       194
       195                 // used in Read when PM_Absolute
       196                 m_percent = pow(0.25, m_header.nLevels);
       197
       198         } else {
       199                 // very small image: we don't use DWT and encoding
       200
       201                 // read channels
       202                 for (int c=0; c < m_header.channels; c++) {
       203                         const UINT32 size = m_width[c]*m_height[c];
       204                         m_channel[c] = new(std::nothrow) DataT[size];
       205                         if (!m_channel[c]) ReturnWithError(InsufficientMemory);
       206
       207                         // read channel data from stream
       208                         for (UINT32 i=0; i < size; i++) {
       209                                 int count = DataTSize;
       210                                 stream->Read(&count, &m_channel[c][i]);
       211                                 if (count != DataTSize) ReturnWithError(MissingData);
       212                         }
       213                 }
       214         }
       215 }

BYTE CPGFImage::Quality () const [inline] โ€” the quality check ๐Ÿ’–

okay so this one is literally the shortest queen in the whole file and I'm obsessed. She just hands you back the PGF quality. That quality lives inbetween 0 and MaxQuality. And bestie, listen โ€” PGF quality 0 means lossless quality. Yes, ZERO is the lossless one. Don't let that trip you up, it's giving reverse psychology ๐Ÿ˜ญ๐Ÿ‘‘

Returns: PGF quality

๐Ÿ“ defined at line 442 of PGFimage.h
       442 { return m_header.quality; }

void CPGFImage::Read (int level = 0, CallbackPtr cb = nullptr, void * data = nullptr) โ€” read & decode some levels ๐ŸŒธ

okay so this is the main character Read. She reads and decodes some levels of a PGF image at the current stream position. โœจ

A PGF image is structured in levels, numbered between 0 and Levels() - 1. Each level can be seen as a single image, containing the same content as all other levels, but in a different size (width, height). The image size at level i is double the size (width, height) of the image at level i+1. The image at level 0 contains the original size. ๐Ÿ’…

โš ๏ธ Precondition, bestie: the PGF image has been opened with a call of Open(...). It might throw an IOException. Don't say I didn't warn you โ€” it's literally in the source.

Parameters:

  • level โ€” [0, nLevels) โ€” the image level of the resulting image in the internal image buffer.
  • cb โ€” a pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.
๐Ÿ“ defined at line 402 of PGFimage.cpp
       402                                                                 {
       403         ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
       404         ASSERT(m_decoder);
       405
       406 #ifdef __PGFROISUPPORT__
       407         if (ROIisSupported() && m_header.nLevels > 0) {
       408                 // new encoding scheme supporting ROI
       409                 PGFRect rect(0, 0, m_header.width, m_header.height);
       410                 Read(rect, level, cb, data);
       411                 return;
       412         }
       413 #endif
       414
       415         if (m_header.nLevels == 0) {
       416                 if (level == 0) {
       417                         // the data has already been read during open
       418                         // now update progress
       419                         if (cb) {
       420                                 if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed);
       421                         }
       422                 }
       423         } else {
       424                 const int levelDiff = m_currentLevel - level;
       425                 double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
       426
       427                 // encoding scheme without ROI
       428                 while (m_currentLevel > level) {
       429                         for (int i=0; i < m_header.channels; i++) {
       430                                 CWaveletTransform* wtChannel = m_wtChannel[i];
       431                                 ASSERT(wtChannel);
       432
       433                                 // decode file and write stream to m_wtChannel
       434                                 if (m_currentLevel == m_header.nLevels) {
       435                                         // last level also has LL band
       436                                         wtChannel->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
       437                                 }
       438                                 if (m_preHeader.version & Version5) {
       439                                         // since version 5
       440                                         wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant);
       441                                         wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant);
       442                                 } else {
       443                                         // until version 4
       444                                         m_decoder->DecodeInterleaved(wtChannel, m_currentLevel, m_quant);
       445                                 }
       446                                 wtChannel->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant);
       447                         }
       448
       449                         volatile OSError error = NoError; // volatile prevents optimizations
       450 #ifdef LIBPGF_USE_OPENMP
       451                         #pragma omp parallel for default(shared)
       452 #endif
       453                         for (int i=0; i < m_header.channels; i++) {
       454                                 // inverse transform from m_wtChannel to m_channel
       455                                 if (error == NoError) {
       456                                         OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
       457                                         if (err != NoError) error = err;
       458                                 }
       459                                 ASSERT(m_channel[i]);
       460                         }
       461                         if (error != NoError) ReturnWithError(error);
       462
       463                         // set new level: must be done before refresh callback
       464                         m_currentLevel--;
       465
       466                         // now we have to refresh the display
       467                         if (m_cb) m_cb(m_cbArg);
       468
       469                         // now update progress
       470                         if (cb) {
       471                                 percent *= 4;
       472                                 if (m_progressMode == PM_Absolute) m_percent = percent;
       473                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       474                         }
       475                 }
       476         }
       477 }

void CPGFImage::Read (PGFRect & rect, int level = 0, CallbackPtr cb = nullptr, void * data = nullptr) โ€” ROI reading ๐ŸŽ€

Read a rectangular region of interest of a PGF image at current stream position. The origin of the coordinate axis is the top-left corner of the image. All coordinates are measured in pixels. It might throw an IOException. ๐Ÿฆ‹

Parameters:

  • rect [inout] โ€” Rectangular region of interest (ROI) at level 0. The rect might be cropped.
  • level โ€” [0, nLevels) โ€” The image level of the resulting image in the internal image buffer.
  • cb โ€” A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.

okay and here's the encore, bestie โ€” the same queen saying it again but this time with extra "and decode" energy, and with the level range quietly dropped (yes, I noticed, and I'm keeping it EXACTLY as written, no cap ๐Ÿ˜ญ):

Read and decode rectangular region of interest (ROI) of a PGF image at current stream position. The origin of the coordinate axis is the top-left corner of the image. All coordinates are measured in pixels. It might throw an IOException. ๐Ÿ’œ

Parameters:

  • rect [inout] โ€” Rectangular region of interest (ROI) at level 0. The rect might be cropped.
  • level โ€” The image level of the resulting image in the internal image buffer.
  • cb โ€” A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.
       Definition at line 489 of file PGFimage.cpp.
       489                                                                                {
       490         ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
       491         ASSERT(m_decoder);
       492
       493         if (m_header.nLevels == 0 || !ROIisSupported()) {
       494                 rect.left = rect.top = 0;
       495                 rect.right = m_header.width; rect.bottom = m_header.height;
       496                 Read(level, cb, data);
       497         } else {
       498                 ASSERT(ROIisSupported());
       499                 // new encoding scheme supporting ROI
       500                 ASSERT(rect.left < m_header.width && rect.top < m_header.height);
       501
       502                 // check rectangle
       503                 if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width;
       504                 if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height;
       505
       506                 const int levelDiff = m_currentLevel - level;
       507                 double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
       508
       509                 // check level difference
       510                 if (levelDiff <= 0) {
       511                         // it is a new read call, probably with a new ROI
       512                         m_currentLevel = m_header.nLevels;
       513                         m_decoder->SetStreamPosToData();
       514                 }
       515
       516                 // enable ROI decoding and reading
       517                 SetROI(rect);
       518
       519                 while (m_currentLevel > level) {
       520                         for (int i=0; i < m_header.channels; i++) {
       521                                 CWaveletTransform* wtChannel = m_wtChannel[i];
       522                                 ASSERT(wtChannel);
       523
       524                                 // get number of tiles and tile indices
       525                                 const UINT32 nTiles = wtChannel->GetNofTiles(m_currentLevel); // independent of ROI
       526
       527                                 // decode file and write stream to m_wtChannel
       528                                 if (m_currentLevel == m_header.nLevels) { // last level also has LL band
       529                                         ASSERT(nTiles == 1);
       530                                         m_decoder->GetNextMacroBlock();
       531                                         wtChannel->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
       532                                 }
       533                                 for (UINT32 tileY=0; tileY < nTiles; tileY++) {
       534                                         for (UINT32 tileX=0; tileX < nTiles; tileX++) {
       535                                                 // check relevance of tile
       536                                                 if (wtChannel->TileIsRelevant(m_currentLevel, tileX, tileY)) {
       537                                                         m_decoder->GetNextMacroBlock();
       538                                                         wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
       539                                                         wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
       540                                                         wtChannel->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
       541                                                 } else {
       542                                                         // skip tile
       543                                                         m_decoder->SkipTileBuffer();
       544                                                 }
       545                                         }
       546                                 }
       547                         }
       548
       549                         volatile OSError error = NoError; // volatile prevents optimizations
       550 #ifdef LIBPGF_USE_OPENMP
       551                         #pragma omp parallel for default(shared)
       552 #endif
       553                         for (int i=0; i < m_header.channels; i++) {
       554                                 // inverse transform from m_wtChannel to m_channel
       555                                 if (error == NoError) {
       556                                         OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
       557                                         if (err != NoError) error = err;
       558                                 }
       559                                 ASSERT(m_channel[i]);
       560                         }
       561                         if (error != NoError) ReturnWithError(error);
       562
       563                         // set new level: must be done before refresh callback
       564                         m_currentLevel--;
       565
       566                         // now we have to refresh the display
       567                         if (m_cb) m_cb(m_cbArg);
       568
       569                         // now update progress
       570                         if (cb) {
       571                                 percent *= 4;
       572                                 if (m_progressMode == PM_Absolute) m_percent = percent;
       573                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
       574                         }
       575                 }
       576         }
       577 }

okay bestie, that whole thing is the ROI-aware reading path ๐Ÿ’– and it is a lot, I know. But look โ€” it's doing the most in the cutest way: if the image has no wavelet levels (m_header.nLevels == 0) or ROI just isn't supported, it yeets the rect to the full image and hands off to Read(level, cb, data) like a good little delegate. Otherwise? Bestie, it's tiling, it's inverse-transforming, it's calling the refresh callback (m_cb), it's ticking the progress callback (cb) and bailing with EscapePressed if you say so. ๐ŸŒธ


UINT32 CPGFImage::ReadEncodedData(int level, UINT8 *target, UINT32 targetLen) const ๐ŸŽ€

Reads the data of an encoded PGF level and copies it to a target buffer without decoding. No decoding, no drama, just straight-up byte handoff. โœจ

Precondition, and I'm serious: the PGF image has been opened with a call of Open(...). Also it might throw an IOException. ๐Ÿ˜ญ

Parameters

  • level โ€” the image level ๐Ÿ’…
  • target โ€” the target buffer ๐Ÿซถ
  • targetLen โ€” the length of the target buffer in bytes ๐Ÿ“

Returns

The number of bytes copied to the target buffer. ๐Ÿ”ฅ

Definition at line 707 of file PGFimage.cpp.

       707                                                                                   {
       708         ASSERT(level >= 0 && level < m_header.nLevels);
       709         ASSERT(target);
       710         ASSERT(targetLen > 0);
       711         ASSERT(m_decoder);
       712
       713         // reset stream position
       714         m_decoder->SetStreamPosToData();
       715
       716         // position stream
       717         UINT64 offset = 0;
       718
       719         for (int i=m_header.nLevels - 1; i > level; i--) {
       720                 offset += m_levelLength[m_header.nLevels - 1 - i];
       721         }
       722         m_decoder->Skip(offset);
       723
       724         // compute number of bytes to read
       725         UINT32 len = __min(targetLen, GetEncodedLevelLength(level));
       726
       727         // read data
       728         len = m_decoder->ReadEncodedData(target, len);
       729         ASSERT(len >= 0 && len <= targetLen);
       730
       731         return len;
       732 }

ngl the way it resets the stream to data, then walks the levels to build up offset and Skips past everything it doesn't want... it's giving precision. ๐ŸŽฏ And then __min(targetLen, GetEncodedLevelLength(level)) so you never get more than your buffer can hold. Main character behavior, honestly. ๐Ÿ‘‘


UINT32 CPGFImage::ReadEncodedHeader(UINT8 *target, UINT32 targetLen) const ๐Ÿฆ‹

Reads the encoded PGF header and copies it to a target buffer. The header, bestie! The little intro card of the whole file. ๐Ÿ’–

Precondition again, same energy: the PGF image has been opened with a call of Open(...). And yes, it might throw an IOException. ๐Ÿ™ˆ

Parameters

  • target โ€” the target buffer ๐ŸŒธ
  • targetLen โ€” the length of the target buffer in bytes ๐Ÿ“

Returns

The number of bytes copied to the target buffer. โญ

Definition at line 660 of file PGFimage.cpp.

       660                                                                          {
       661         ASSERT(target);
       662         ASSERT(targetLen > 0);
       663         ASSERT(m_decoder);
       664
       665         // reset stream position
       666         m_decoder->SetStreamPosToStart();
       667
       668         // compute number of bytes to read
       669         UINT32 len = __min(targetLen, GetEncodedHeaderLength());
       670
       671         // read data
       672         len = m_decoder->ReadEncodedData(target, len);
       673         ASSERT(len >= 0 && len <= targetLen);
       674
       675         return len;
       676 }

Note it's SetStreamPosToStart() here, not SetStreamPosToData() โ€” because the header lives at the start, obviously, and it's lowkey the one time we're allowed to be at the very beginning. ๐Ÿ˜Œ Same __min energy with GetEncodedHeaderLength(). Chef's kiss. ๐Ÿ’…


void CPGFImage::ReadPreview() [inline] ๐ŸŒˆ

Read and decode smallest level of a PGF image at current stream position. For details, please refert to Read(...) โ€” (yes, that's the original's typo, I'm keeping it, it's part of the lore now ๐Ÿซถ). Precondition: The PGF image has been opened with a call of Open(...). It might throw an IOException.

Definition at line 111 of file PGFimage.h.

       111 { Read(Levels() - 1); }

the way it's literally a one-liner. Read(Levels() - 1). That's it. That's the function. Ate and left no crumbs. ๐Ÿ“


void CPGFImage::Reconstruct(int level = 0) ๐Ÿ’œ

After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV to get a quick reconstruction (coded -> decoded image). It might throw an IOException. ๐Ÿ”ฅ

Parameters

  • level โ€” The image level of the resulting image in the internal image buffer.

โ€ฆand here's the rest of that function's body, bestie โ€” literally the code, no notes ๐Ÿ’…

       348                                        {
       349         if (m_header.nLevels == 0) {
       350                 // image didn't use wavelet transform
       351                 if (level == 0) {
       352                         for (int i=0; i < m_header.channels; i++) {
       353                                 ASSERT(m_wtChannel[i]);
       354                                 m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer();
       355                         }
       356                 }
       357         } else {
       358                 int currentLevel = m_header.nLevels;
       359
       360         #ifdef __PGFROISUPPORT__
       361                 if (ROIisSupported()) {
       362                         // enable ROI reading
       363                         SetROI(PGFRect(0, 0, m_header.width, m_header.height));
       364                 }
       365         #endif
       366
       367                 while (currentLevel > level) {
       368                         for (int i=0; i < m_header.channels; i++) {
       369                                 ASSERT(m_wtChannel[i]);
       370                                 // dequantize subbands
       371                                 if (currentLevel == m_header.nLevels) {
       372                                         // last level also has LL band
       373                                         m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant);
       374                                 }
       375                                 m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant);
       376                                 m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant);
       377                                 m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant);
       378
       379                                 // inverse transform from m_wtChannel to m_channel
       380                                 OSError err = m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
       381                                 if (err != NoError) ReturnWithError(err);
       382                                 ASSERT(m_channel[i]);
       383                         }
       384
       385                         currentLevel--;
       386                 }
       387         }
       388 }

void CPGFImage::ResetStreamPos (bool startOfData)

Okay so this one is your little rewind button ๐ŸŽ€ It resets the stream position โ€” either to the start of the PGF pre-header or to the start of the data. Pick your poison, girlie.

Important, no cap: you must not call this before Open() or before Write(). Like, genuinely not. It's giving "read the instructions first" energy ๐Ÿ˜ญ

Use this method after Read() if you wanna read the same image several times โ€” for example, reading different ROIs. Same pic, multiple passes, no drama ๐Ÿ’…

Parameters

  • startOfData โ€” true: you want to read the same image several times. false: resets stream position to the initial position.

Definition at line 682 of file PGFimage.cpp.

       682                                                {
       683         m_currentLevel = 0;
       684         if (startOfData) {
       685                 ASSERT(m_decoder);
       686                 m_decoder->SetStreamPosToData();
       687         } else {
       688                 if (m_decoder) {
       689                         m_decoder->SetStreamPosToStart();
       690                 } else if (m_encoder) {
       691                         m_encoder->SetStreamPosToStart();
       692                 } else {
       693                         ASSERT(false);
       694                 }
       695         }
       696 }

Not me explaining all this with zero shame ๐Ÿ™ˆ there's still more coming, so don't go anywhere โœจ

void CPGFImage::RgbToYuv (int pitch, UINT8 * rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void * data) [private] ๐Ÿ’ซ

Definition at line 1389 of file PGFimage.cpp.

okay bestie so this one is private, meaning it's the backstage crew โ€” you're not calling it yourself, it's just doing the vibes internally. It takes an RGB buffer and turns it into the YUV situation the rest of the class is obsessed with. Here's the whole thing, every line, no cuts:

1389                                                                                                          {
1390         ASSERT(buff);
1391         UINT32 yPos = 0, cnt = 0;
1392         double percent = 0;
1393         const double dP = 1.0/m_header.height;
1394         int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
1395
1396         if (channelMap == nullptr) channelMap = defMap;
1397
1398         switch(m_header.mode) {
1399         case ImageModeBitmap:
1400                 {
1401                         ASSERT(m_header.channels == 1);
1402                         ASSERT(m_header.bpp == 1);
1403                         ASSERT(bpp == 1);
1404
1405                         const UINT32 w = m_header.width;
1406                         const UINT32 w2 = (m_header.width + 7)/8;
1407                         DataT* y = m_channel[0]; ASSERT(y);
1408
1409                         // new unpacked version since version 7
1410                         for (UINT32 h = 0; h < m_header.height; h++) {
1411                                 if (cb) {
1412                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1413                                         percent += dP;
1414                                 }
1415                                 cnt = 0;
1416                                 for (UINT32 j = 0; j < w2; j++) {
1417                                         UINT8 byte = buff[j];
1418                                         for (int k = 0; k < 8; k++) {
1419                                                 UINT8 bit = (byte & 0x80) >> 7;
1420                                                 if (cnt < w) y[yPos++] = bit;
1421                                                 byte <<= 1;
1422                                                 cnt++;
1423                                         }
1424                                 }
1425                                 buff += pitch;
1426                         }
1427                         /* old version: packed values: 8 pixels in 1 byte
1428                         for (UINT32 h = 0; h < m_header.height; h++) {
1429                                 if (cb) {
1430                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1431                                         percent += dP;
1432                                 }
1433
1434                                 for (UINT32 j = 0; j < w2; j++) {
1435                                         y[yPos++] = buff[j] - YUVoffset8;
1436                                 }
1437                                 // version 5 and 6
1438                                 // for (UINT32 j = w2; j < w; j++) {
1439                                 //      y[yPos++] = YUVoffset8;
1440                                 //}
1441                                 buff += pitch;
1442                         }
1443                         */
1444                 }
1445                 break;
1446         case ImageModeIndexedColor:
1447         case ImageModeGrayScale:
1448         case ImageModeHSLColor:
1449         case ImageModeHSBColor:
1450         case ImageModeLabColor:
1451                 {
1452                         ASSERT(m_header.channels >= 1);
1453                         ASSERT(m_header.bpp == m_header.channels*8);
1454                         ASSERT(bpp%8 == 0);
1455                         const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1456
1457                         for (UINT32 h=0; h < m_header.height; h++) {
1458                                 if (cb) {
1459                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1460                                         percent += dP;
1461                                 }
1462
1463                                 cnt = 0;
1464                                 for (UINT32 w=0; w < m_header.width; w++) {
1465                                         for (int c=0; c < m_header.channels; c++) {
1466                                                 m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8;
1467                                         }
1468                                         cnt += channels;
1469                                         yPos++;
1470                                 }
1471                                 buff += pitch;
1472                         }
1473                 }
1474                 break;
1475         case ImageModeGray16:
1476         case ImageModeLab48:
1477                 {
1478                         ASSERT(m_header.channels >= 1);
1479                         ASSERT(m_header.bpp == m_header.channels*16);
1480                         ASSERT(bpp%16 == 0);
1481
1482                         UINT16 *buff16 = (UINT16 *)buff;
1483                         const int pitch16 = pitch/2;
1484                         const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1485                         const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1486                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1487
1488                         for (UINT32 h=0; h < m_header.height; h++) {
1489                                 if (cb) {
1490                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1491                                         percent += dP;
1492                                 }
1493
1494                                 cnt = 0;
1495                                 for (UINT32 w=0; w < m_header.width; w++) {
1496                                         for (int c=0; c < m_header.channels; c++) {
1497                                                 m_channel[c][yPos] = (buff16[cnt + channelMap[c]] >> shift) - yuvOffset16;
1498                                         }
1499                                         cnt += channels;
1500                                         yPos++;
1501                                 }
1502                                 buff16 += pitch16;
1503                         }
1504                 }
1505                 break;
1506         case ImageModeRGBColor:
1507                 {
1508                         ASSERT(m_header.channels == 3);
1509                         ASSERT(m_header.bpp == m_header.channels*8);
1510                         ASSERT(bpp%8 == 0);
1511
1512                         DataT* y = m_channel[0]; ASSERT(y);
1513                         DataT* u = m_channel[1]; ASSERT(u);
1514                         DataT* v = m_channel[2]; ASSERT(v);
1515                         const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1516                         UINT8 b, g, r;
1517
1518                         for (UINT32 h=0; h < m_header.height; h++) {
1519                                 if (cb) {
1520                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1521                                         percent += dP;
1522                                 }
1523
1524                                 cnt = 0;
1525                                 for (UINT32 w=0; w < m_header.width; w++) {
1526                                         b = buff[cnt + channelMap[0]];
1527                                         g = buff[cnt + channelMap[1]];
1528                                         r = buff[cnt + channelMap[2]];
1529                                         // Yuv
1530                                         y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
1531                                         u[yPos] = r - g;
1532                                         v[yPos] = b - g;
1533                                         yPos++;
1534                                         cnt += channels;
1535                                 }
1536                                 buff += pitch;
1537                         }
1538                 }
1539                 break;
1540         case ImageModeRGB48:
1541                 {
1542                         ASSERT(m_header.channels == 3);
1543                         ASSERT(m_header.bpp == m_header.channels*16);
1544                         ASSERT(bpp%16 == 0);
1545
1546                         UINT16 *buff16 = (UINT16 *)buff;
1547                         const int pitch16 = pitch/2;
1548                         const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1549                         const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1550                         const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1551
1552                         DataT* y = m_channel[0]; ASSERT(y);
1553                         DataT* u = m_channel[1]; ASSERT(u);
1554                         DataT* v = m_channel[2]; ASSERT(v);
1555                         UINT16 b, g, r;
1556
1557                         for (UINT32 h=0; h < m_header.height; h++) {
1558                                 if (cb) {
1559                                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1560                                         percent += dP;
1561                                 }
1562
1563                                 cnt = 0;
1564                                 for (UINT32 w=0; w < m_header.width; w++) {
1565                                         b = buff16[cnt + channelMap[0]] >> shift;
1566                                         g = buff16[cnt + channelMap[1]] >> shift;
1567                                         r = buff16[cnt + channelMap[2]] >> shift;
1568                                         // Yuv
1569                                         y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
1570                                         u[yPos] = r - g;
1571                                         v[yPos] = b - g;
1572                                         yPos++;
1573                                         cnt += channels;
1574                                 }
1575                                 buff16 += pitch16;
1576                         }
1577                 }
1578                 break;
1579         case ImageModeRGBA:
1580         case ImageModeCMYKColor:
1581                 {
1582                         ASSERT(m_header.channels == 4);

so like, let's vibe check what's actually happening here bestie ๐Ÿ’…

first thing: ASSERT(buff); โ€” no null buffers allowed, periodt. Then it sets up yPos = 0, cnt = 0, percent = 0, and const double dP = 1.0/m_header.height; โ€” that's the little progress step so the callback can tell you how far along we are. ๐Ÿซถ

then: int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; with an ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels); โ€” so that default channel map is exactly MaxChannels long, no cap. And if you passed in nullptr for channelMap, it just gets swapped for defMap:

if (channelMap == nullptr) channelMap = defMap;

then switch(m_header.mode) and we're off, one case at a time ๐Ÿ‘‘

  • ImageModeBitmap โ€” asserts m_header.channels == 1, m_header.bpp == 1, and bpp == 1. Grabs w = m_header.width, w2 = (m_header.width + 7)/8, and DataT* y = m_channel[0]. The comment literally says // new unpacked version since version 7 โ€” so every byte gets pulled apart into 8 bits, one at a time, (byte & 0x80) >> 7, and only written while cnt < w. Then buff += pitch.
also, there's a whole commented-out /* old version: packed values: 8 pixels in 1 byte */ block right there in the source, and it stays in the code โ€” plus a // version 5 and 6 bit inside it that's commented out too. Not me leaving the receipts in the file ๐Ÿ˜ญ
  • ImageModeIndexedColor, ImageModeGrayScale, ImageModeHSLColor, ImageModeHSBColor, ImageModeLabColor โ€” all sharing one case. Asserts m_header.channels >= 1, m_header.bpp == m_header.channels*8, bpp%8 == 0, and channels = bpp/8 has to be >= m_header.channels. Then per pixel per channel: m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8; โ€” and yes, cnt += channels; and yPos++; every single pixel, buff += pitch; every single row.
  • ImageModeGray16 and ImageModeLab48 โ€” the 16-bit girlies. Asserts m_header.channels >= 1, m_header.bpp == m_header.channels*16, bpp%16 == 0. Sets UINT16 *buff16 = (UINT16 *)buff;, pitch16 = pitch/2, channels = bpp/16, shift = 16 - UsedBitsPerChannel(), and yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);. Then: m_channel[c][yPos] = (buff16[cnt + channelMap[c]] >> shift) - yuvOffset16; โ€” buff16 += pitch16; per row.
  • ImageModeRGBColor โ€” asserts m_header.channels == 3, m_header.bpp == m_header.channels*8, bpp%8 == 0. Grabs y, u, v out of m_channel[0], [1], [2], with an ASSERT on each. Pulls b, g, r through channelMap[0], [1], [2], and does the actual math:
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
u[yPos] = r - g;
v[yPos] = b - g;

then yPos++;, cnt += channels;, buff += pitch;.

  • ImageModeRGB48 โ€” same energy but 16-bit. Asserts m_header.channels == 3, m_header.bpp == m_header.channels*16, bpp%16 == 0. buff16, pitch16 = pitch/2, channels = bpp/16, shift = 16 - UsedBitsPerChannel(), yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);, and y, u, v all asserted. b, g, r get shifted down with >> shift, then:
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
u[yPos] = r - g;
v[yPos] = b - g;

and buff16 += pitch16; per row.

  • ImageModeRGBA and ImageModeCMYKColor โ€” shared case, and it opens with ASSERT(m_header.channels == 4); before we even get anywhere elseโ€ฆ

and also, every single one of those row loops does this first:

if (cb) {
        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
        percent += dP;
}

so if you handed over a cb and it says "stop" (returns true), it bails out with EscapePressed โ€” fr fr, it just leaves โ€” otherwise percent creeps up by dP each row. ๐ŸŽ€

โ€ฆand we're still inside the giant switch, bestie ๐Ÿ’€ so here's the rest of the function body, exactly as it lives, no edits:

                        ASSERT(m_header.bpp == m_header.channels*8);
                        ASSERT(bpp%8 == 0);
                        const int channels = bpp/8; ASSERT(channels >= m_header.channels);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);
                        DataT* a = m_channel[3]; ASSERT(a);
                        UINT8 b, g, r;

                        for (UINT32 h=0; h < m_header.height; h++) {
                                if (cb) {
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        percent += dP;
                                }

                                cnt = 0;
                                for (UINT32 w=0; w < m_header.width; w++) {
                                        b = buff[cnt + channelMap[0]];
                                        g = buff[cnt + channelMap[1]];
                                        r = buff[cnt + channelMap[2]];
                                        // Yuv
                                        y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
                                        u[yPos] = r - g;
                                        v[yPos] = b - g;
                                        a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8;
                                        cnt += channels;
                                }
                                buff += pitch;
                        }
                }
                break;
        case ImageModeCMYK64:
                {
                        ASSERT(m_header.channels == 4);
                        ASSERT(m_header.bpp == m_header.channels*16);
                        ASSERT(bpp%16 == 0);

                        UINT16 *buff16 = (UINT16 *)buff;
                        const int pitch16 = pitch/2;
                        const int channels = bpp/16; ASSERT(channels >= m_header.channels);
                        const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
                        const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);
                        DataT* a = m_channel[3]; ASSERT(a);
                        UINT16 b, g, r;

                        for (UINT32 h=0; h < m_header.height; h++) {
                                if (cb) {
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        percent += dP;
                                }

                                cnt = 0;
                                for (UINT32 w=0; w < m_header.width; w++) {
                                        b = buff16[cnt + channelMap[0]] >> shift;
                                        g = buff16[cnt + channelMap[1]] >> shift;
                                        r = buff16[cnt + channelMap[2]] >> shift;
                                        // Yuv
                                        y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
                                        u[yPos] = r - g;
                                        v[yPos] = b - g;
                                        a[yPos++] = (buff16[cnt + channelMap[3]] >> shift) - yuvOffset16;
                                        cnt += channels;
                                }
                                buff16 += pitch16;
                        }
                }
                break;
#ifdef __PGF32SUPPORT__
        case ImageModeGray32:
                {
                        ASSERT(m_header.channels == 1);
                        ASSERT(m_header.bpp == 32);
                        ASSERT(bpp == 32);
                        ASSERT(DataTSize == sizeof(UINT32));

                        DataT* y = m_channel[0]; ASSERT(y);

                        UINT32 *buff32 = (UINT32 *)buff;
                        const int pitch32 = pitch/4;
                        const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
                        const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);

                        for (UINT32 h=0; h < m_header.height; h++) {
                                if (cb) {
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        percent += dP;
                                }

                                for (UINT32 w=0; w < m_header.width; w++) {
                                        y[yPos++] = (buff32[w] >> shift) - yuvOffset31;
                                }
                                buff32 += pitch32;
                        }
                }
                break;
#endif
        case ImageModeRGB12:
                {
                        ASSERT(m_header.channels == 3);
                        ASSERT(m_header.bpp == m_header.channels*4);
                        ASSERT(bpp == m_header.channels*4);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);

                        UINT8 rgb = 0, b, g, r;

                        for (UINT32 h=0; h < m_header.height; h++) {
                                if (cb) {
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        percent += dP;
                                }

                                cnt = 0;
                                for (UINT32 w=0; w < m_header.width; w++) {
                                        if (w%2 == 0) {
                                                // even pixel position
                                                rgb = buff[cnt];
                                                b = rgb & 0x0F;
                                                g = (rgb & 0xF0) >> 4;
                                                cnt++;
                                                rgb = buff[cnt];
                                                r = rgb & 0x0F;
                                        } else {
                                                // odd pixel position
                                                b = (rgb & 0xF0) >> 4;
                                                cnt++;
                                                rgb = buff[cnt];
                                                g = rgb & 0x0F;
                                                r = (rgb & 0xF0) >> 4;
                                                cnt++;
                                        }

                                        // Yuv
                                        y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4;
                                        u[yPos] = r - g;
                                        v[yPos] = b - g;
                                        yPos++;
                                }
                                buff += pitch;
                        }
                }
                break;
        case ImageModeRGB16:
                {
                        ASSERT(m_header.channels == 3);
                        ASSERT(m_header.bpp == 16);
                        ASSERT(bpp == 16);

                        DataT* y = m_channel[0]; ASSERT(y);
                        DataT* u = m_channel[1]; ASSERT(u);
                        DataT* v = m_channel[2]; ASSERT(v);

                        UINT16 *buff16 = (UINT16 *)buff;
                        UINT16 rgb, b, g, r;
                        const int pitch16 = pitch/2;

                        for (UINT32 h=0; h < m_header.height; h++) {
                                if (cb) {
                                        if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
                                        percent += dP;
                                }
                                for (UINT32 w=0; w < m_header.width; w++) {
                                        rgb = buff16[w];
                                        r = (rgb & 0xF800) >> 10;       // highest 5 bits
                                        g = (rgb & 0x07E0) >> 5;        // middle 6 bits
                                        b = (rgb & 0x001F) << 1;        // lowest 5 bits
                                        // Yuv
                                        y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6;
                                        u[yPos] = r - g;
                                        v[yPos] = b - g;
                                        yPos++;
                                }

                                buff16 += pitch16;
                        }
                }
                break;
        default:
                ASSERT(false);
        }
}

It's giving switch-statement-finale energy ๐ŸŽฌ โ€” every mode got its own little glow-up moment, and if nothing matches, ASSERT(false) says absolutely not, bestie, we do not do unknown image modes here.


๐Ÿฆ‹ bool CPGFImage::ROIisSupported () const [inline]

Vibe check: does this pgf image do Region Of Interest (ROI) or not? Returns true if the pgf image supports Region Of Interest (ROI).

Returns

true if the pgf image supports ROI.

Definition at line 466 of file PGFimage.h.

{ return (m_preHeader.version & PGFROI) == PGFROI; }

literally just a bitwise AND and a flex, iconic ๐Ÿ’…


๐ŸŽ€ void CPGFImage::SetChannel (DataT * channel, int c = 0) [inline]

Set internal PGF image buffer channel. you're handing the image a channel directly, bestie ๐Ÿซถ

Parameters

  • channel โ€” A YUV data channel
  • c โ€” A channel index

Definition at line 272 of file PGFimage.h.

272 { ASSERT(c >= 0 && c < MaxChannels); m_channel[c] = channel; }

void CPGFImage::SetColorTable (UINT32 iFirstColor, UINT32 nColors, const RGBQUAD * prgbColors) ๐ŸŽจ

Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut). It might throw an IOException. bestie, watch for that.

Parameters

  • iFirstColor โ€” The color table index of the first entry to set.
  • nColors โ€” The number of color table entries to set.
  • prgbColors โ€” A pointer to the array of RGBQUAD structures to set the color table entries.

Definition at line 1364 of file PGFimage.cpp.

1364                                                                                            {
1365         if (iFirstColor + nColors > ColorTableLen)      ReturnWithError(ColorTableError);
1366
1367         for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
1368                 m_postHeader.clut[i] = prgbColors[j];
1369         }
1370 }

void CPGFImage::SetHeader (const PGFHeader & header, BYTE flags = 0, const UINT8 * userData = 0, UINT32 userDataLength = 0) ๐Ÿ“‹

Set PGF header and user data. Precondition: The PGF image has been never opened with Open(...). It might throw an IOException.

Parameters

  • header โ€” A valid and already filled in PGF header structure
  • flags โ€” A combination of additional version flags. In case you use level-wise encoding then set flag = PGFROI.
  • userData โ€” A user-defined memory block containing any kind of cached metadata.
  • userDataLength โ€” The size of user-defined memory block in bytes

Definition at line 894 of file PGFimage.cpp.

894                                                                                                                  {
895         ASSERT(!m_decoder);     // current image must be closed
896         ASSERT(header.quality <= MaxQuality);
897         ASSERT(userDataLength <= MaxUserDataSize);
898
899         // init state
900 #ifdef __PGFROISUPPORT__
901         m_streamReinitialized = false;
902 #endif
903
904         // init preHeader
905         memcpy(m_preHeader.magic, PGFMagic, 3);
906         m_preHeader.version = PGFVersion | flags;
907         m_preHeader.hSize = HeaderSize;
908
909         // copy header
910         memcpy(&m_header, &header, HeaderSize);
911
912         // check quality
913         if (m_header.quality > MaxQuality) m_header.quality = MaxQuality;
914
915         // complete header
916         CompleteHeader();
917
918         // check and set number of levels
919         ComputeLevels();
920
921         // check for downsample
922         if (m_header.quality > DownsampleThreshold &&  (m_header.mode == ImageModeRGBColor ||
923                                                                                                         m_header.mode == ImageModeRGBA ||
924                                                                                                         m_header.mode == ImageModeRGB48 ||
925                                                                                                         m_header.mode == ImageModeCMYKColor ||
926                                                                                                         m_header.mode == ImageModeCMYK64 ||
927                                                                                                         m_header.mode == ImageModeLabColor ||
928                                                                                                         m_header.mode == ImageModeLab48)) {
929                 m_downsample = true;
930                 m_quant = m_header.quality - 1;
931         } else {
932                 m_downsample = false;
933                 m_quant = m_header.quality;
934         }
935
936         // update header size and copy user data
937         if (m_header.mode == ImageModeIndexedColor) {
938                 // update header size
939                 m_preHeader.hSize += ColorTableSize;
940         }
941         if (userDataLength && userData) {
942                 if (userDataLength > MaxUserDataSize) userDataLength = MaxUserDataSize;
943                 m_postHeader.userData = new(std::nothrow) UINT8[userDataLength];
944                 if (!m_postHeader.userData) ReturnWithError(InsufficientMemory);
945                 m_postHeader.userDataLen = m_postHeader.cachedUserDataLen = userDataLength;
946                 memcpy(m_postHeader.userData, userData, userDataLength);
947                 // update header size
948                 m_preHeader.hSize += userDataLength;
949         }
950
951         // allocate channels
952         for (int i=0; i < m_header.channels; i++) {
953                 // set current width and height
954                 m_width[i] = m_header.width;
955                 m_height[i] = m_header.height;
956
957                 // allocate channels
958                 ASSERT(!m_channel[i]);
959                 m_channel[i] = new(std::nothrow) DataT[m_header.width*m_header.height];
960                 if (!m_channel[i]) {
961                         if (i) i--;
962                         while(i) {
963                                 delete[] m_channel[i]; m_channel[i] = 0;
964                                 i--;
965                         }
966                         ReturnWithError(InsufficientMemory);
967                 }
968         }
969 }

void CPGFImage::SetMaxValue (UINT32 maxValue) ๐Ÿ”ข

Set maximum intensity value for image modes with more than eight bits per channel. Call this method after SetHeader, but before ImportBitmap. order matters, no cap.

Parameters

  • maxValue โ€” The maximum intensity value.

Definition at line 738 of file PGFimage.cpp.

738                                            {
739         const BYTE bpc = m_header.bpp/m_header.channels;
740         BYTE pot = 0;
741
742         while(maxValue > 0) {
743                 pot++;
744                 maxValue >>= 1;
745         }
746         // store bits per channel
747         if (pot > bpc) pot = bpc;
748         if (pot > 31) pot = 31;
749         m_header.usedBitsPerChannel = pot;
750 }

void CPGFImage::SetProgressMode (ProgressMode pm) [inline] ๐Ÿ“Š

Set progress mode used in Read and Write. Default mode is PM_Relative. This method must be called before Open() or SetHeader().

  • PM_Relative: 100% = level difference between current level and target level of Read/Write
  • PM_Absolute: 100% = number of levels

Definition at line 296 of file PGFimage.h.

296 { m_progressMode = pm; }

void CPGFImage::SetRefreshCallback (RefreshCB callback, void * arg) [inline] ๐Ÿฆ‹

Set refresh callback procedure and its parameter. The refresh callback is called during Read(...) after each level read. it's giving "notify me when it's ready" energy

Parameters

  • callback โ€” A refresh callback procedure
  • arg โ€” A parameter of the refresh callback procedure

Definition at line 303 of file PGFimage.h.

303 { m_cb = callback; m_cbArg = arg; }

void CPGFImage::SetROI (PGFRect rect) [private] ๐Ÿ”

Compute ROIs for each channel and each level <= current level Called inside of Read(rect, ...).

Parameters

  • rect โ€” rectangular region of interest (ROI) at level 0

Definition at line 615 of file PGFimage.cpp.

615                                    {
616         ASSERT(m_decoder);
617         ASSERT(ROIisSupported());
618         ASSERT(m_wtChannel[0]);
619
620         // store ROI for a later call of GetBitmap
621         m_roi = rect;
622
623         // enable ROI decoding
624         m_decoder->SetROI();
625
626         // prepare wavelet channels for using ROI
627         m_wtChannel[0]->SetROI(rect);
628
629         if (m_downsample && m_header.channels > 1) {
630                 // all further channels are downsampled, therefore downsample ROI
631                 rect.left >>= 1;
632                 rect.top >>= 1;
633                 rect.right = (rect.right + 1) >> 1;
634                 rect.bottom = (rect.bottom + 1) >> 1;
635         }
636         for (int i=1; i < m_header.channels; i++) {
637                 ASSERT(m_wtChannel[i]);
638                 m_wtChannel[i]->SetROI(rect);
639         }
640 }

UINT32 CPGFImage::UpdatePostHeaderSize () [private] ๐Ÿงฎ

Definition at line 1124 of file PGFimage.cpp.

1124                                        {
1125         ASSERT(m_encoder);
1126
1127         INT64 offset = m_encoder->ComputeOffset(); ASSERT(offset >= 0);
1128
1129         if (offset > 0) {
1130                 // update post-header size and rewrite pre-header
1131                 m_preHeader.hSize += (UINT32)offset;
1132                 m_encoder->UpdatePostHeaderSize(m_preHeader);
1133         }
1134
1135         // write dummy levelLength into stream
1136         return m_encoder->WriteLevelLength(m_levelLength);
1137 }

BYTE CPGFImage::UsedBitsPerChannel () const โœจ

Returns number of used bits per input/output image channel. Precondition: header must be initialized.

Returns

number of used bits per input/output image channel.

Definition at line 756 of file PGFimage.cpp.

756                                          {
757         const BYTE bpc = m_header.bpp/m_header.channels;
758
759         if (bpc > 8) {
760                 return m_header.usedBitsPerChannel;
761         } else {
762                 return bpc;
763         }
764 }

BYTE CPGFImage::Version () const [inline] ๐Ÿท๏ธ

Returns the used codec major version of a pgf image

Returns

PGF codec major version of this image

Definition at line 484 of file PGFimage.h.

484 { BYTE ver = CodecMajorVersion(m_preHeader.version); return (ver <= 7) ? ver : (BYTE)m_header.version.major; }

UINT32 CPGFImage::Width (int level = 0) const [inline] ๐Ÿ“

Return image width of channel 0 at given level in pixels. The returned width is independent of any Read-operations and ROI. so don't stress, it's not gonna ghost you ๐Ÿ’…

Parameters

  • level โ€” A level

Returns

Image level width in pixels

Definition at line 413 of file PGFimage.h.

413 { ASSERT(level >= 0); return LevelSizeL(m_header.width, level); }

void CPGFImage::Write (CPGFStream * stream, UINT32 * nWrittenBytes = nullptr, CallbackPtr cb = nullptr, void * data = nullptr)

okay bestie, this one is the whole entire image writer. It encodes and writes an entire PGF image โ€” header AND image โ€” at the current stream position. โœจ

A PGF image is structured in levels, numbered between 0 and Levels() - 1. Each level can be seen as a single image, containing the same content as all other levels, but in a different size (width, height). The image size at level i is double the size (width, height) of the image at level i+1. The image at level 0 contains the original size. ๐Ÿ’…

Precondition: the PGF image contains a valid header (see also SetHeader(...)). It might throw an IOException.

Parameters ๐ŸŒธ

  • stream โ€” A PGF stream
  • nWrittenBytes โ€” [in-out] The number of bytes written into stream are added to the input value.
  • cb โ€” A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.

Definition at line 1221 of file PGFimage.cpp.

1221                                                                                                  {
1222         ASSERT(stream);
1223         ASSERT(m_preHeader.hSize);
1224
1225         // create wavelet transform channels and encoder
1226         UINT32 nBytes = WriteHeader(stream);
1227
1228         // write image
1229         nBytes += WriteImage(stream, cb, data);
1230
1231         // return written bytes
1232         if (nWrittenBytes) *nWrittenBytes += nBytes;
1233 }

UINT32 CPGFImage::Write (int level, CallbackPtr cb = nullptr, void * data = nullptr)

okay so this overload is the down-to-a-specific-level girlie. It encodes and writes down to the given level at the current stream position. Same level lore as above: numbered between 0 and Levels() - 1, each level same content but different size (width, height), level i is double the size of i+1, and level 0 is the original size. ๐Ÿฆ‹

Preconditions: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before. Levels() > 0. The ROI encoding scheme must be used (see also SetHeader(...)). It might throw an IOException.

Parameters ๐Ÿ’–

  • level โ€” [0, nLevels) The image level of the resulting image in the internal image buffer.
  • cb โ€” A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
  • data โ€” Data Pointer to C++ class container to host callback procedure.

Returns ๐ŸŒˆ The number of bytes written into stream.

Definition at line 1250 of file PGFimage.cpp.

1250                                                                  {
1251         ASSERT(m_header.nLevels > 0);
1252         ASSERT(0 <= level && level < m_header.nLevels);
1253         ASSERT(m_encoder);
1254         ASSERT(ROIisSupported());
1255
1256         const int levelDiff = m_currentLevel - level;
1257         double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
1258         UINT32 nWrittenBytes = 0;
1259
1260         if (m_currentLevel == m_header.nLevels) {
1261                 // update post-header size, rewrite pre-header, and write dummy levelLength
1262                 nWrittenBytes = UpdatePostHeaderSize();
1263         } else {
1264                 // prepare for next level: save current file position, because the stream might have been reinitialized
1265                 if (m_encoder->ComputeBufferLength()) {
1266                         m_streamReinitialized = true;
1267                 }
1268         }
1269
1270         // encoding scheme with ROI
1271         while (m_currentLevel > level) {
1272                 WriteLevel();   // decrements m_currentLevel
1273
1274                 if (m_levelLength) {
1275                         nWrittenBytes += m_levelLength[m_header.nLevels - m_currentLevel - 1];
1276                 }
1277
1278                 // now update progress
1279                 if (cb) {
1280                         percent *= 4;
1281                         if (m_progressMode == PM_Absolute) m_percent = percent;
1282                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1283                 }
1284         }
1285
1286         // automatically closing
1287         if (m_currentLevel == 0) {
1288                 if (!m_streamReinitialized) {
1289                         // don't write level lengths, if the stream position changed inbetween two Write operations
1290                         m_encoder->UpdateLevelLength();
1291                 }
1292                 // delete encoder
1293                 delete m_encoder; m_encoder = nullptr;
1294         }
1295
1296         return nWrittenBytes;
1297 }

UINT32 CPGFImage::WriteHeader (CPGFStream * stream)

okay so the docs literally say this twice, and honestly? iconic behavior. We're keeping both because every detail matters, periodt.

First version: Create wavelet transform channels and encoder. Write header at current stream position. Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader(). This method is called inside of Write(stream, ...). It might throw an IOException.

Parameters ๐ŸŽ€

  • stream โ€” A PGF stream

Returns โญ The number of bytes written into stream.

Second version (yes, again, but with extra FWT tea): Create wavelet transform channels and encoder. Write header at current stream position. Performs forward FWT. Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader(). This method is called inside of Write(stream, ...). It might throw an IOException.

Parameters ๐Ÿฆ‹

  • stream โ€” A PGF stream

Returns ๐Ÿ’œ The number of bytes written into stream.

Definition at line 979 of file PGFimage.cpp.

979                                                 {
980         ASSERT(m_header.nLevels <= MaxLevel);
981         ASSERT(m_header.quality <= MaxQuality); // quality is already initialized
982
983         if (m_header.nLevels > 0) {
984                 volatile OSError error = NoError; // volatile prevents optimizations
985                 // create new wt channels
986 #ifdef LIBPGF_USE_OPENMP
987                 #pragma omp parallel for default(shared)
988 #endif
989                 for (int i=0; i < m_header.channels; i++) {
990                         DataT *temp = nullptr;
991                         if (error == NoError) {
992                                 if (m_wtChannel[i]) {
993                                         ASSERT(m_channel[i]);
994                                         // copy m_channel to temp
995                                         int size = m_height[i]*m_width[i];
996                                         temp = new(std::nothrow) DataT[size];
997                                         if (temp) {
998                                                 memcpy(temp, m_channel[i], size*DataTSize);
999                                                 delete m_wtChannel[i];  // also deletes m_channel
1000                                                 m_channel[i] = nullptr;
1001                                         } else {
1002                                                 error = InsufficientMemory;
1003                                         }
1004                                 }
1005                                 if (error == NoError) {
1006                                         if (temp) {
1007                                                 ASSERT(!m_channel[i]);
1008                                                 m_channel[i] = temp;
1009                                         }
1010                                         m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]);
1011                                         if (m_wtChannel[i]) {
1012                                         #ifdef __PGFROISUPPORT__
1013                                                 m_wtChannel[i]->SetROI(PGFRect(0, 0, m_width[i], m_height[i]));
1014                                         #endif
1015
1016                                                 // wavelet subband decomposition
1017                                                 for (int l=0; error == NoError && l < m_header.nLevels; l++) {
1018                                                         OSError err = m_wtChannel[i]->ForwardTransform(l, m_quant);
1019                                                         if (err != NoError) error = err;
1020                                                 }
1021                                         } else {
1022                                                 delete[] m_channel[i];
1023                                                 error = InsufficientMemory;
1024                                         }
1025                                 }
1026                         }
1027                 }
1028                 if (error != NoError) {
1029                         // free already allocated memory
1030                         for (int i=0; i < m_header.channels; i++) {
1031                                 delete m_wtChannel[i];
1032                         }
1033                         ReturnWithError(error);
1034                 }
1035
1036                 m_currentLevel = m_header.nLevels;
1037
1038                 // create encoder, write headers and user data, but not level-length area
1039                 m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
1040                 if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize();
1041
1042         #ifdef __PGFROISUPPORT__
1043                 if (ROIisSupported()) {
1044                         // new encoding scheme supporting ROI
1045                         m_encoder->SetROI();
1046                 }
1047         #endif
1048
1049         } else {
1050                 // very small image: we don't use DWT and encoding
1051
1052                 // create encoder, write headers and user data, but not level-length area
1053                 m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
1054         }
1055
1056         INT64 nBytes = m_encoder->ComputeHeaderLength();
1057         return (nBytes > 0) ? (UINT32)nBytes : 0;
1058 }

UINT32 CPGFImage::WriteImage (CPGFStream * stream, CallbackPtr cb = nullptr, void * data = nullptr)

okay bestie, this is THE moment โ€” you're encoding and writing an image at the current stream position. ๐Ÿ’… Call this method after WriteHeader(). And if you wanna write uncached metadata, do that after WriteHeader() and before WriteImage(). This method gets called inside of Write(stream, ...). It might throw an IOException, no cap.

Parameters

  • stream โ€” a PGF stream, obviously. ๐ŸŒŠ
  • cb โ€” a pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding. (so dramatic, but okay)
  • data โ€” Data pointer to C++ class container to host callback procedure. ๐Ÿซถ

Returns

The number of bytes written into stream. That's the tea. โ˜•

Definition at line 1150 of file PGFimage.cpp.

1150                                                                                {
1151         ASSERT(stream);
1152         ASSERT(m_preHeader.hSize);
1153
1154         int levels = m_header.nLevels;
1155         double percent = pow(0.25, levels);
1156
1157         // update post-header size, rewrite pre-header, and write dummy levelLength
1158         UINT32 nWrittenBytes = UpdatePostHeaderSize();
1159
1160         if (levels == 0) {
1161                 // for very small images: write channels uncoded
1162                 for (int c=0; c < m_header.channels; c++) {
1163                         const UINT32 size = m_width[c]*m_height[c];
1164
1165                         // write channel data into stream
1166                         for (UINT32 i=0; i < size; i++) {
1167                                 int count = DataTSize;
1168                                 stream->Write(&count, &m_channel[c][i]);
1169                         }
1170                 }
1171
1172                 // now update progress
1173                 if (cb) {
1174                         if ((*cb)(1, true, data)) ReturnWithError(EscapePressed);
1175                 }
1176
1177         } else {
1178                 // encode quantized wavelet coefficients and write to PGF file
1179                 // encode subbands, higher levels first
1180                 // color channels are interleaved
1181
1182                 // encode all levels
1183                 for (m_currentLevel = levels; m_currentLevel > 0; ) {
1184                         WriteLevel(); // decrements m_currentLevel
1185
1186                         // now update progress
1187                         if (cb) {
1188                                 percent *= 4;
1189                                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1190                         }
1191                 }
1192
1193                 // flush encoder and write level lengths
1194                 m_encoder->Flush();
1195         }
1196
1197         // update level lengths
1198         nWrittenBytes += m_encoder->UpdateLevelLength(); // return written image bytes
1199
1200         // delete encoder
1201         delete m_encoder; m_encoder = nullptr;
1202
1203         ASSERT(!m_encoder);
1204
1205         return nWrittenBytes;
1206 }

void CPGFImage::WriteLevel () [private]

Definition at line 1068 of file PGFimage.cpp.

1068                            {
1069         ASSERT(m_encoder);
1070         ASSERT(m_currentLevel > 0);
1071         ASSERT(m_header.nLevels > 0);
1072
1073 #ifdef __PGFROISUPPORT__
1074         if (ROIisSupported()) {
1075                 const int lastChannel = m_header.channels - 1;
1076
1077                 for (int i=0; i < m_header.channels; i++) {
1078                         // get number of tiles and tile indices
1079                         const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
1080                         const UINT32 lastTile = nTiles - 1;
1081
1082                         if (m_currentLevel == m_header.nLevels) {
1083                                 // last level also has LL band
1084                                 ASSERT(nTiles == 1);
1085                                 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
1086                                 m_encoder->EncodeTileBuffer(); // encode macro block with tile-end = true
1087                         }
1088                         for (UINT32 tileY=0; tileY < nTiles; tileY++) {
1089                                 for (UINT32 tileX=0; tileX < nTiles; tileX++) {
1090                                         // extract tile to macro block and encode already filled macro blocks with tile-end = false
1091                                         m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, true, tileX, tileY);
1092                                         m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, true, tileX, tileY);
1093                                         m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, true, tileX, tileY);
1094                                         if (i == lastChannel && tileY == lastTile && tileX == lastTile) {
1095                                                 // all necessary data are buffered. next call of EncodeTileBuffer will write the last piece of data of the current level.
1096                                                 m_encoder->SetEncodedLevel(--m_currentLevel);
1097                                         }
1098                                         m_encoder->EncodeTileBuffer(); // encode last macro block with tile-end = true
1099                                 }
1100                         }
1101                 }
1102         } else
1103 #endif
1104         {
1105                 for (int i=0; i < m_header.channels; i++) {
1106                         ASSERT(m_wtChannel[i]);
1107                         if (m_currentLevel == m_header.nLevels) {
1108                                 // last level also has LL band
1109                                 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
1110                         }
1111                         //encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4
1112                         m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder); // since version 5
1113                         m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder); // since version 5
1114                         m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder);
1115                 }
1116
1117                 // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
1118                 m_encoder->SetEncodedLevel(--m_currentLevel);
1119         }
1120 }

๐ŸŽ€ Member Data Documentation ๐ŸŽ€

RefreshCB CPGFImage::m_cb [private]

pointer to refresh callback procedure

Definition at line 545 of file PGFimage.h.

void* CPGFImage::m_cbArg [private]

refresh callback argument

Definition at line 546 of file PGFimage.h.

DataT* CPGFImage::m_channel[MaxChannels] [protected]

untransformed channels in YUV format

Definition at line 522 of file PGFimage.h.

int CPGFImage::m_currentLevel [protected]

transform level of current image

Definition at line 532 of file PGFimage.h.

CDecoder* CPGFImage::m_decoder [protected]

PGF decoder.

Definition at line 523 of file PGFimage.h.

bool CPGFImage::m_downsample [protected]

chrominance channels are downsampled

Definition at line 535 of file PGFimage.h.

CEncoder* CPGFImage::m_encoder [protected]

PGF encoder.

Definition at line 524 of file PGFimage.h.

bool CPGFImage::m_favorSpeedOverSize [protected]

favor encoding speed over compression ratio

Definition at line 536 of file PGFimage.h.

PGFHeader CPGFImage::m_header [protected]

PGF file header.

Definition at line 529 of file PGFimage.h.

UINT32 CPGFImage::m_height[MaxChannels] [protected]

height of each channel at current level

Definition at line 527 of file PGFimage.h.

UINT32* CPGFImage::m_levelLength [protected]

length of each level in bytes; first level starts immediately after this array

Definition at line 525 of file PGFimage.h.

double CPGFImage::m_percent [private]

progress [0..1]

Definition at line 547 of file PGFimage.h.

PGFPostHeader CPGFImage::m_postHeader [protected]

PGF post-header.

Definition at line 530 of file PGFimage.h.

PGFPreHeader CPGFImage::m_preHeader [protected]

PGF pre-header.

Definition at line 528 of file PGFimage.h.

ProgressMode CPGFImage::m_progressMode [private]

progress mode used in Read and Write; PM_Relative is default mode

Definition at line 548 of file PGFimage.h.

BYTE CPGFImage::m_quant [protected]

quantization parameter

Definition at line 534 of file PGFimage.h.

PGFRect CPGFImage::m_roi [protected]

region of interest

Definition at line 541 of file PGFimage.h.

bool CPGFImage::m_streamReinitialized [protected]

stream has been reinitialized

Definition at line 540 of file PGFimage.h.

bool CPGFImage::m_useOMPinDecoder [protected]

use Open MP in decoder

Definition at line 538 of file PGFimage.h.

bool CPGFImage::m_useOMPinEncoder [protected]

use Open MP in encoder

Definition at line 537 of file PGFimage.h.

UINT32 CPGFImage::m_userDataPolicy [protected]

user data (metadata) policy during open

Definition at line 533 of file PGFimage.h.

UINT64 CPGFImage::m_userDataPos [protected]

stream position of user data

Definition at line 531 of file PGFimage.h.

UINT32 CPGFImage::m_width[MaxChannels] [protected]

width of each channel at current level

Definition at line 526 of file PGFimage.h.

CWaveletTransform* CPGFImage::m_wtChannel[MaxChannels] [protected]

wavelet transformed color channels

Definition at line 521 of file PGFimage.h.


๐ŸŒธ Author ๐ŸŒธ

Generated automatically by Doxygen for libpgf from the source code. slay.


This iconic page is version 7.21.2, dated Thu Jul 24 2025 00:00:00, and it's CPGFImage(3). Bye bestie, go write some images! ๐Ÿ’–โœจ

๐Ÿ“ฆ where this page comes from