CWaveletTransform(3)

πŸ“¦
version 7.21.7 Β· 4 versions
, in fedora 43

✨ CWaveletTransform ✨ β€” the libpgf manual, bestie edition πŸ’…

okay so this is the page for CWaveletTransform(3) from libpgf, and it's giving wavelet energy 🌊. Buckle up.


🌸 NAME 🌸

CWaveletTransform β€” PGF wavelet transform. that's it, that's the whole vibe. 🫢


✨ SYNOPSIS ✨

#include <WaveletTransform.h>

πŸ‘‘ Public Member Functions

  • CWaveletTransform (UINT32 width, UINT32 height, int levels, DataT *data=nullptr)
  • ~CWaveletTransform () β€” Destructor.
  • OSError ForwardTransform (int level, int quant)
  • OSError InverseTransform (int level, UINT32 *width, UINT32 *height, DataT **data)
  • CSubband * GetSubband (int level, Orientation orientation)

πŸ™ˆ Private Member Functions

  • void Destroy ()
  • void InitSubbands (UINT32 width, UINT32 height, DataT *data)
  • void ForwardRow (DataT *buff, UINT32 width)
  • void InverseRow (DataT *buff, UINT32 width)
  • void InterleavedToSubbands (int destLevel, DataT *loRow, DataT *hiRow, UINT32 width)
  • void SubbandsToInterleaved (int srcLevel, DataT *loRow, DataT *hiRow, UINT32 width)

πŸ’œ Private Attributes

  • int m_nLevels β€” number of LL levels: one more than header.nLevels in PGFimage
  • CSubband(* m_subband )[NSubbands] β€” quadtree of subbands: LL HL LH HH

🧚 Friends

  • class CSubband

🌈 Detailed Description 🌈

PGF wavelet transform.

PGF wavelet transform class.

Author

  • C. Stamm, R. Spuler

Definition at line 55 of file WaveletTransform.h.


πŸŽ€ Constructor & Destructor Documentation πŸŽ€

CWaveletTransform::CWaveletTransform (UINT32 width, UINT32 height, int levels, DataT * data = nullptr)

Constructor: Constructs a wavelet transform pyramid of given size and levels. no cap, it literally builds you a whole pyramid πŸ”οΈβœ¨

Parameters

  • width β€” The width of the original image (at level 0) in pixels
  • height β€” The height of the original image (at level 0) in pixels
  • levels β€” The number of levels (>= 0)
  • data β€” Input data of subband LL at level 0

Definition at line 40 of file WaveletTransform.cpp.

41 : m_nLevels(levels + 1) // m_nLevels in CPGFImage determines the number of FWT steps; this.m_nLevels determines the number subband-planes
42 , m_subband(nullptr)
43 #ifdef __PGFROISUPPORT__
44 , m_indices(nullptr)
45 #endif
46 {
47     ASSERT(m_nLevels > 0 && m_nLevels <= MaxLevel + 1);
48     InitSubbands(width, height, data);
49 }

CWaveletTransform::~CWaveletTransform () [inline]

Destructor. it says bye πŸ‘‹, cleans up its mess, iconic behaviour.

Definition at line 69 of file WaveletTransform.h.

69 { Destroy(); }

πŸ’– Member Function Documentation πŸ’–

void CWaveletTransform::Destroy () [inline], [private]

Definition at line 125 of file WaveletTransform.h.

125                        {
126     delete[] m_subband; m_subband = nullptr;
127 #ifdef __PGFROISUPPORT__
128     delete[] m_indices; m_indices = nullptr;
129 #endif
130 }

void CWaveletTransform::ForwardRow (DataT * buff, UINT32 width) [private]

Definition at line 180 of file WaveletTransform.cpp.

180                                                            {
181     if (width >= FilterSize) {
182         UINT32 i = 3;
183
184         // left border handling
185         src[1] -= ((src[0] + src[2] + c1) >> 1); // high pass
186         src[0] += ((src[1] + c1) >> 1); // low pass
187
188         // middle part
189         for (; i < width-1; i += 2) {
190             src[i] -= ((src[i-1] + src[i+1] + c1) >> 1); // high pass
191             src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass
192         }
193
194         // right border handling
195         if (width & 1) {
196             src[i-1] += ((src[i-2] + c1) >> 1); // low pass
197         } else {
198             src[i] -= src[i-1]; // high pass
199             src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass
200         }
201     }
202 }

OSError CWaveletTransform::ForwardTransform (int level, int quant)

Compute fast forward wavelet transform of LL subband at given level and stores result in all 4 subbands of level + 1. the way it just... sends it forward πŸ”₯

Parameters

  • level β€” A wavelet transform pyramid level (>= 0 && < Levels())
  • quant β€” A quantization value (linear scalar quantization)

Returns

  • error in case of a memory allocation problem 😭 (yes, that's the only thing it's stressing about here)

Definition at line 88 of file WaveletTransform.cpp.

88                                                                 {
89     ASSERT(level >= 0 && level < m_nLevels - 1);
90     const int destLevel = level + 1;
91     ASSERT(m_subband[destLevel]);
92     CSubband* srcBand = &m_subband[level][LL]; ASSERT(srcBand);
93     const UINT32 width = srcBand->GetWidth();
94     const UINT32 height = srcBand->GetHeight();
95     DataT* src = srcBand->GetBuffer(); ASSERT(src);
96     DataT *row0, *row1, *row2, *row3;
97
98     // Allocate memory for next transform level
99     for (int i=0; i < NSubbands; i++) {
100         if (!m_subband[destLevel][i].AllocMemory()) return InsufficientMemory;
101     }
102
103     if (height >= FilterSize) { // changed from FilterSizeH to FilterSize
104         // top border handling
105         row0 = src; row1 = row0 + width; row2 = row1 + width;
106         ForwardRow(row0, width);
107         ForwardRow(row1, width);
108         ForwardRow(row2, width);
109         for (UINT32 k=0; k < width; k++) {
110             row1[k] -= ((row0[k] + row2[k] + c1) >> 1); // high pass
111             row0[k] += ((row1[k] + c1) >> 1); // low pass
112         }
113         InterleavedToSubbands(destLevel, row0, row1, width);
114         row0 = row1; row1 = row2; row2 += width; row3 = row2 + width;
115
116         // middle part
117         for (UINT32 i=3; i < height-1; i += 2) {
118             ForwardRow(row2, width);
119             ForwardRow(row3, width);
120             for (UINT32 k=0; k < width; k++) {
121                 row2[k] -= ((row1[k] + row3[k] + c1) >> 1); // high pass filter
122                 row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass filter
123             }
124             InterleavedToSubbands(destLevel, row1, row2, width);
125             row0 = row2; row1 = row3; row2 = row3 + width; row3 = row2 + width;
126         }
127
128         // bottom border handling
129         if (height & 1) {
130             for (UINT32 k=0; k < width; k++) {
131                 row1[k] += ((row0[k] + c1) >> 1); // low pass
132             }
133             InterleavedToSubbands(destLevel, row1, nullptr, width);
134             row0 = row1; row1 += width;
135         } else {
136             ForwardRow(row2, width);
137             for (UINT32 k=0; k < width; k++) {
138                 row2[k] -= row1[k]; // high pass
139                 row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass
140             }
141             InterleavedToSubbands(destLevel, row1, row2, width);
142             row0 = row1; row1 = row2; row2 += width;
143         }
144     } else {
145         // if height is too small
146         row0 = src; row1 = row0 + width;
147         // first part
148         for (UINT32 k=0; k < height; k += 2) {
149             ForwardRow(row0, width);
150             ForwardRow(row1, width);
151             InterleavedToSubbands(destLevel, row0, row1, width);
152             row0 += width << 1; row1 += width << 1;
153         }
154         // bottom
155         if (height & 1) {
156             InterleavedToSubbands(destLevel, row0, nullptr, width);
157         }
158     }
159
160     if (quant > 0) {
161         // subband quantization (without LL)
162         for (int i=1; i < NSubbands; i++) {
163             m_subband[destLevel][i].Quantize(quant);
164         }
165         // LL subband quantization
166         if (destLevel == m_nLevels - 1) {
167             m_subband[destLevel][LL].Quantize(quant);
168         }
169     }
170
171     // free source band
172     srcBand->FreeMemory();
173     return NoError;
174 }

CSubband * CWaveletTransform::GetSubband (int level, Orientation orientation) [inline]

Get pointer to one of the 4 subband at a given level. gimme the pointer bestie πŸ“

Parameters

  • level β€” A wavelet transform pyramid level (>= 0 && <= Levels())
  • orientation β€” A quarter of the subband (LL, LH, HL, HH)

Definition at line 93 of file WaveletTransform.h.

93                                                                  {
94     ASSERT(level >= 0 && level < m_nLevels);
95     return &m_subband[level][orientation];
96 }

void CWaveletTransform::InitSubbands (UINT32 width, UINT32 height, DataT * data) [private]

Definition at line 53 of file WaveletTransform.cpp.

53                                                                              {
54     if (m_subband) Destroy();
55
56     // create subbands
57     m_subband = new CSubband[m_nLevels][NSubbands];
58
59     // init subbands
60     UINT32 loWidth = width;
61     UINT32 hiWidth = width;
62     UINT32 loHeight = height;
63     UINT32 hiHeight = height;
64
65     for (int level = 0; level < m_nLevels; level++) {
66         m_subband[level][LL].Initialize(loWidth, loHeight, level, LL);   // LL
67         m_subband[level][HL].Initialize(hiWidth, loHeight, level, HL);   //    HL
68         m_subband[level][LH].Initialize(loWidth, hiHeight, level, LH);   // LH
69         m_subband[level][HH].Initialize(hiWidth, hiHeight, level, HH);   //    HH
70         hiWidth = loWidth >> 1;                 hiHeight = loHeight >> 1;
71         loWidth = (loWidth + 1) >> 1;   loHeight = (loHeight + 1) >> 1;
72     }
73     if (data) {
74         m_subband[0][LL].SetBuffer(data);
75     }
76 }

🧚 void CWaveletTransform::InterleavedToSubbands (int destLevel, DataT * loRow, DataT * hiRow, UINT32 width) [private]

okay so Definition at line 206 of file WaveletTransform.cpp. This one is the little sorting goblin 🧺 β€” it takes your interleaved rows and untangles them into the four subbands. Here's the actual source, verbatim, no cap:

206                                                                                                      {
207         const UINT32 wquot = width >> 1;
208         const bool wrem = (width & 1);
209         CSubband &ll = m_subband[destLevel][LL], &hl = m_subband[destLevel][HL];
210         CSubband &lh = m_subband[destLevel][LH], &hh = m_subband[destLevel][HH];
211
212         if (hiRow) {
213                 for (UINT32 i=0; i < wquot; i++) {
214                         ll.WriteBuffer(*loRow++);        // first access, than increment
215                         hl.WriteBuffer(*loRow++);
216                         lh.WriteBuffer(*hiRow++);        // first access, than increment
217                         hh.WriteBuffer(*hiRow++);
218                 }
219                 if (wrem) {
220                         ll.WriteBuffer(*loRow);
221                         lh.WriteBuffer(*hiRow);
222                 }
223         } else {
224                 for (UINT32 i=0; i < wquot; i++) {
225                         ll.WriteBuffer(*loRow++);        // first access, than increment
226                         hl.WriteBuffer(*loRow++);
227                 }
228                 if (wrem) ll.WriteBuffer(*loRow);
229         }
230 }

so basically: wquot is width >> 1 and wrem is (width & 1) β€” that's the "is there one pixel left over" check, bestie πŸ’…. It grabs references to ll, hl, lh and hh out of m_subband[destLevel][…], and then:

  • when hiRow is real (not null): it walks wquot times, and every pass it hands one value to ll and one to hl from *loRow, plus one to lh and one to hh from *hiRow β€” incrementing after each access, the classic "first access, than increment" move (yes, that's how the comment says it, iykyk). If wrem is true, the leftover single value goes to ll and lh only. πŸ¦‹
  • when hiRow is null: same loop but it only writes ll and hl from *loRow, and the leftover (if any) goes to ll alone. 🌸

πŸ’€ void CWaveletTransform::InverseRow (DataT * buff, UINT32 width) [private]

Definition at line 419 of file WaveletTransform.cpp. The lifting-scheme glow-up, but backwards ✨ β€” and yes, we're keeping it character-for-character, whatever it says:

419                                                             {
420         if (width >= FilterSize) {
421                 UINT32 i = 2;
422
423                 // left border handling
424                 dest[0] -= ((dest[1] + c1) >> 1); // even
425
426                 // middle part
427                 for (; i < width - 1; i += 2) {
428                         dest[i] -= ((dest[i-1] + dest[i+1] + c2) >> 2); // even
429                         dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd
430                 }
431
432                 // right border handling
433                 if (width & 1) {
434                         dest[i] -= ((dest[i-1] + c1) >> 1); // even
435                         dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd
436                 } else {
437                         dest[i-1] += dest[i-2]; // odd
438                 }
439         }
440 }

so the tea β˜•: this only runs its little routine when width >= FilterSize. If that's a no, it just vibes and does nothing. If it is a yes:

  1. It starts i at 2.
  2. Left border handling β€” dest[0] -= ((dest[1] + c1) >> 1); (the even one).
  3. Middle part β€” loops while i < width - 1, stepping by 2, doing the even update dest[i] -= ((dest[i-1] + dest[i+1] + c2) >> 2); and then the odd update dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); 🌈
  4. Right border handling β€” if width & 1 (odd width, so there's a straggler), it does dest[i] -= ((dest[i-1] + c1) >> 1); then dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1);. Otherwise (even width) it's just dest[i-1] += dest[i-2];. πŸŽ€
Not me side-eyeing that the parameter is named buff while the body writes to dest πŸ™ˆ β€” that is literally what's in the source, bestie, I'm not touching a single character of it. Do not come for me. πŸ’…

πŸ‘‘ OSError CWaveletTransform::InverseTransform (int level, UINT32 * width, UINT32 * height, DataT ** data)

okay THIS one is the main character ✨ β€” it computes the fast inverse wavelet transform of all 4 subbands of a given level and stores the result in the LL subband of level - 1. periodt. πŸ’–

Parameters 🫢

  • level β€” a wavelet transform pyramid level, and it's gotta satisfy > 0 && <= Levels().
  • width β€” a pointer to the returned width of subband LL (in pixels).
  • height β€” a pointer to the returned height of subband LL (in pixels).
  • data β€” a pointer to the returned array of image data.

Returns πŸŽ€

  • an error in case of a memory allocation problem.

so yeah, if malloc ghosts you, you get an error back. that's the whole vibe check for this one 😭 β€” more of the page is coming, stay tuned bestie πŸ’œ

okay bestie we're still deep in Member Function Documentation πŸ’– β€” this is the next member function body, and it lives at Definition at line 245 of file WaveletTransform.cpp. It's giving whole entire inverse-transform situation. No skimming, every line stays, let's go 🧚✨

245                                                                                             {
246         ASSERT(srcLevel > 0 && srcLevel < m_nLevels);
247         const int destLevel = srcLevel - 1;
248         ASSERT(m_subband[destLevel]);
249         CSubband* destBand = &m_subband[destLevel][LL];
250         UINT32 width, height;
251
252         // allocate memory for the results of the inverse transform
253         if (!destBand->AllocMemory()) return InsufficientMemory;
254         DataT *origin = destBand->GetBuffer(), *row0, *row1, *row2, *row3;
255
256 #ifdef __PGFROISUPPORT__
257         PGFRect destROI = destBand->GetAlignedROI();
258         const UINT32 destWidth  = destROI.Width();  // destination buffer width
259         const UINT32 destHeight = destROI.Height(); // destination buffer height
260         width = destWidth;              // destination working width
261         height = destHeight;    // destination working height
262
263         // update destination ROI
264         if (destROI.top & 1) {
265                 destROI.top++;
266                 origin += destWidth;
267                 height--;
268         }
269         if (destROI.left & 1) {
270                 destROI.left++;
271                 origin++;
272                 width--;
273         }
274
275         // init source buffer position
276         const UINT32 leftD = destROI.left >> 1;
277         const UINT32 left0 = m_subband[srcLevel][LL].GetAlignedROI().left;
278         const UINT32 left1 = m_subband[srcLevel][HL].GetAlignedROI().left;
279         const UINT32 topD = destROI.top >> 1;
280         const UINT32 top0 = m_subband[srcLevel][LL].GetAlignedROI().top;
281         const UINT32 top1 = m_subband[srcLevel][LH].GetAlignedROI().top;
282         ASSERT(m_subband[srcLevel][LH].GetAlignedROI().left == left0);
283         ASSERT(m_subband[srcLevel][HH].GetAlignedROI().left == left1);
284         ASSERT(m_subband[srcLevel][HL].GetAlignedROI().top == top0);
285         ASSERT(m_subband[srcLevel][HH].GetAlignedROI().top == top1);
286
287         UINT32 srcOffsetX[2] = { 0, 0 };
288         UINT32 srcOffsetY[2] = { 0, 0 };
289
290         if (leftD >= __max(left0, left1)) {
291                 srcOffsetX[0] = leftD - left0;
292                 srcOffsetX[1] = leftD - left1;
293         } else {
294                 if (left0 <= left1) {
295                         const UINT32 dx = (left1 - leftD) << 1;
296                         destROI.left += dx;
297                         origin += dx;
298                         width -= dx;
299                         srcOffsetX[0] = left1 - left0;
300                 } else {
301                         const UINT32 dx = (left0 - leftD) << 1;
302                         destROI.left += dx;
303                         origin += dx;
304                         width -= dx;
305                         srcOffsetX[1] = left0 - left1;
306                 }
307         }
308         if (topD >= __max(top0, top1)) {
309                 srcOffsetY[0] = topD - top0;
310                 srcOffsetY[1] = topD - top1;
311         } else {
312                 if (top0 <= top1) {
313                         const UINT32 dy = (top1 - topD) << 1;
314                         destROI.top += dy;
315                         origin += dy*destWidth;
316                         height -= dy;
317                         srcOffsetY[0] = top1 - top0;
318                 } else {
319                         const UINT32 dy = (top0 - topD) << 1;
320                         destROI.top += dy;
321                         origin += dy*destWidth;
322                         height -= dy;
323                         srcOffsetY[1] = top0 - top1;
324                 }
325         }
326
327         m_subband[srcLevel][LL].InitBuffPos(srcOffsetX[0], srcOffsetY[0]);
328         m_subband[srcLevel][HL].InitBuffPos(srcOffsetX[1], srcOffsetY[0]);
329         m_subband[srcLevel][LH].InitBuffPos(srcOffsetX[0], srcOffsetY[1]);
330         m_subband[srcLevel][HH].InitBuffPos(srcOffsetX[1], srcOffsetY[1]);
331
332 #else
333         width = destBand->GetWidth();
334         height = destBand->GetHeight();
335         PGFRect destROI(0, 0, width, height);
336         const UINT32 destWidth = width; // destination buffer width
337         const UINT32 destHeight = height; // destination buffer height
338
339         // init source buffer position
340         for (int i = 0; i < NSubbands; i++) {
341                 m_subband[srcLevel][i].InitBuffPos();
342         }
343 #endif
344
345         if (destHeight >= FilterSize) { // changed from FilterSizeH to FilterSize
346                 // top border handling
347                 row0 = origin; row1 = row0 + destWidth;
348                 SubbandsToInterleaved(srcLevel, row0, row1, width);
349                 for (UINT32 k = 0; k < width; k++) {
350                         row0[k] -= ((row1[k] + c1) >> 1); // even
351                 }
352
353                 // middle part
354                 row2 = row1 + destWidth; row3 = row2 + destWidth;
355                 for (UINT32 i = destROI.top + 2; i < destROI.bottom - 1; i += 2) {
356                         SubbandsToInterleaved(srcLevel, row2, row3, width);
357                         for (UINT32 k = 0; k < width; k++) {
358                                 row2[k] -= ((row1[k] + row3[k] + c2) >> 2); // even
359                                 row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd
360                         }
361                         InverseRow(row0, width);
362                         InverseRow(row1, width);
363                         row0 = row2; row1 = row3; row2 = row1 + destWidth; row3 = row2 + destWidth;
364                 }
365
366                 // bottom border handling
367                 if (height & 1) {
368                         SubbandsToInterleaved(srcLevel, row2, nullptr, width);
369                         for (UINT32 k = 0; k < width; k++) {
370                                 row2[k] -= ((row1[k] + c1) >> 1); // even
371                                 row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd
372                         }
373                         InverseRow(row0, width);
374                         InverseRow(row1, width);
375                         InverseRow(row2, width);
376                         row0 = row1; row1 = row2; row2 += destWidth;
377                 } else {
378                         for (UINT32 k = 0; k < width; k++) {
379                                 row1[k] += row0[k];
380                         }
381                         InverseRow(row0, width);
382                         InverseRow(row1, width);
383                         row0 = row1; row1 += destWidth;
384                 }
385         } else {
386                 // height is too small
387                 row0 = origin; row1 = row0 + destWidth;
388                 // first part
389                 for (UINT32 k = 0; k < height; k += 2) {
390                         SubbandsToInterleaved(srcLevel, row0, row1, width);
391                         InverseRow(row0, width);
392                         InverseRow(row1, width);
393                         row0 += destWidth << 1; row1 += destWidth << 1;
394                 }
395                 // bottom
396                 if (height & 1) {
397                         SubbandsToInterleaved(srcLevel, row0, nullptr, width);
398                         InverseRow(row0, width);
399                 }
400         }
401
402         // free memory of the current srcLevel
403         for (int i = 0; i < NSubbands; i++) {
404                 m_subband[srcLevel][i].FreeMemory();
405         }
406
407         // return info
408         *w = destWidth;
409         *h = destHeight;
410         *data = destBand->GetBuffer();
411         return NoError;
412 }

πŸŽ€ okay so what did we just read

Not me about to break down 168 lines of C++ like it's a group project. Sit down, hydrate, we're doing this πŸ“

the guardrails, lines 245–254 πŸ‘‘

  • Line 245 is just the opening {, iconic, minimal, no notes.
  • ASSERT(srcLevel > 0 && srcLevel < m_nLevels); β€” bestie, srcLevel has to be strictly greater than 0 and strictly less than m_nLevels. If it isn't, the assert screams.
  • const int destLevel = srcLevel - 1; β€” we're stepping down one level.
  • ASSERT(m_subband[destLevel]); β€” that destination level better actually exist, fr fr.
  • CSubband* destBand = &m_subband[destLevel][LL]; β€” the destination band is the LL subband of destLevel.
  • UINT32 width, height; β€” declared, uninitialised, mysterious, we love a setup.
  • Then the memory moment: if (!destBand->AllocMemory()) return InsufficientMemory; β€” if allocation fails, it bails out and returns InsufficientMemory. That's the receipts, straight up.
  • DataT *origin = destBand->GetBuffer(), *row0, *row1, *row2, *row3; β€” origin is the destination buffer, and row0–row3 are our four rolling row pointers.

the __PGFROISUPPORT__ era, lines 256–331 πŸ¦‹

  • Everything from 256 to 331 only compiles #ifdef __PGFROISUPPORT__.
  • PGFRect destROI = destBand->GetAlignedROI(); then destWidth / destHeight come off the ROI's Width() and Height(). width and height start as copies of those, described as the destination working width and height.
  • The // update destination ROI bit: if destROI.top & 1 (odd top, bestie) it bumps destROI.top++, slides origin += destWidth, and drops height--. If destROI.left & 1 it bumps destROI.left++, does origin++, and drops width--. Odd edges get trimmed off the top-left corner, periodt.
  • // init source buffer position β€” leftD is destROI.left >> 1, left0 is the LL subband's aligned ROI left, left1 is the HL subband's aligned ROI left. Same pattern vertically: topD is destROI.top >> 1, top0 is LL's aligned ROI top, top1 is LH's aligned ROI top.
  • Then four ASSERTs that are basically the vibe check: LH's left must equal left0, HH's left must equal left1, HL's top must equal top0, HH's top must equal top1. Everyone lines up or we riot.
  • UINT32 srcOffsetX[2] = { 0, 0 }; and UINT32 srcOffsetY[2] = { 0, 0 }; β€” two slots each, both starting at zero.
  • The leftD >= __max(left0, left1) branch is the chill one: just fill srcOffsetX[0] = leftD - left0 and srcOffsetX[1] = leftD - left1.
  • The else is the spicy one. If left0 <= left1, it computes const UINT32 dx = (left1 - leftD) << 1;, then shifts destROI.left += dx, origin += dx, width -= dx, and sets srcOffsetX[0] = left1 - left0. Otherwise it computes dx from left0 - leftD, does the same three shifts, and sets srcOffsetX[1] = left0 - left1. Two totally different dx's, don't mix them up πŸ’…
  • Same exact energy vertically with topD >= __max(top0, top1): srcOffsetY[0] = topD - top0, srcOffsetY[1] = topD - top1. Else branch: if top0 <= top1 then dy = (top1 - topD) << 1, shift destROI.top, origin += dy*destWidth, height -= dy, and srcOffsetY[0] = top1 - top0; otherwise dy = (top0 - topD) << 1, same shifts, and srcOffsetY[1] = top0 - top1.
  • Finally the four InitBuffPos calls, each one mapping a subband to its own X/Y combo: LL gets (srcOffsetX[0], srcOffsetY[0]), HL gets (srcOffsetX[1], srcOffsetY[0]), LH gets (srcOffsetX[0], srcOffsetY[1]), HH gets (srcOffsetX[1], srcOffsetY[1]). Chef's kiss, so tidy ✨

the non-ROI era, aka the #else, lines 332–343 🌸

  • No ROI support? Fine. width = destBand->GetWidth(); and height = destBand->GetHeight();
  • PGFRect destROI(0, 0, width, height); gets constructed from scratch, and destWidth / destHeight are const UINT32 copies of width / height.
  • // init source buffer position β€” a loop for (int i = 0; i < NSubbands; i++) calling m_subband[srcLevel][i].InitBuffPos(); on every single subband, no arguments, no favours. Simple. Effective. Slay.

the actual number-crunching, lines 345–400 πŸ”₯

  • if (destHeight >= FilterSize) β€” and yes, the comment is literally // changed from FilterSizeH to FilterSize, keep it, it's part of the lore.
  • top border handling: row0 = origin; row1 = row0 + destWidth; then SubbandsToInterleaved(srcLevel, row0, row1, width); and a loop where every row0[k] -= ((row1[k] + c1) >> 1); β€” tagged // even.
  • middle part: row2 = row1 + destWidth; row3 = row2 + destWidth; and then the loop for (UINT32 i = destROI.top + 2; i < destROI.bottom - 1; i += 2). Inside: SubbandsToInterleaved(srcLevel, row2, row3, width);, then for each k, row2[k] -= ((row1[k] + row3[k] + c2) >> 2); (// even) and row1[k] += ((row0[k] + row2[k] + c1) >> 1); (// odd). Then InverseRow(row0, width); and InverseRow(row1, width); β€” and then everybody shuffles down a row: row0 = row2; row1 = row3; row2 = row1 + destWidth; row3 = row2 + destWidth;
  • bottom border handling: if height & 1 (odd height, of course), SubbandsToInterleaved(srcLevel, row2, nullptr, width); β€” yes, nullptr where row3 would go β€” then row2[k] -= ((row1[k] + c1) >> 1); (// even) and row1[k] += ((row0[k] + row2[k] + c1) >> 1); (// odd), then InverseRow on row0, row1 and row2, then row0 = row1; row1 = row2; row2 += destWidth;
  • If the height is even instead: just row1[k] += row0[k]; for every k, InverseRow(row0, width);, InverseRow(row1, width);, then row0 = row1; row1 += destWidth;
  • height is too small (the else, and it says so: // height is too small): row0 = origin; row1 = row0 + destWidth;, then // first part loops for (UINT32 k = 0; k < height; k += 2) doing SubbandsToInterleaved(srcLevel, row0, row1, width);, InverseRow(row0, width);, InverseRow(row1, width); and marching row0 += destWidth << 1; row1 += destWidth << 1;. Then // bottom: if height & 1, one last SubbandsToInterleaved(srcLevel, row0, nullptr, width); followed by InverseRow(row0, width);

cleanup + the receipts, lines 402–412 πŸ‘‘

  • // free memory of the current srcLevel β€” loop for (int i = 0; i < NSubbands; i++) and call m_subband[srcLevel][i].FreeMemory(); on every subband. We do not leak in this house πŸ™ˆ
  • // return info β€” then the handoff: *w = destWidth;, *h = destHeight;, *data = destBand->GetBuffer();, and return NoError;
  • Line 412 closes the body with a single }. Iconic. Restrained. The end of this one function...

okay so bestie, we're still in the Member Function Documentation section, and here's the private function SubbandsToInterleaved. It's defined at line 444 of WaveletTransform.cpp, and it's private, so hands off unless you're inside the class, mwah. πŸ’…

   void CWaveletTransform::SubbandsToInterleaved (int srcLevel, DataT * loRow,
       DataT * hiRow, UINT32 width) [private]
       Definition at line 444 of file WaveletTransform.cpp.
       444                                                                                                     {
       445         const UINT32 wquot = width >> 1;
       446         const bool wrem = (width & 1);
       447         CSubband &ll = m_subband[srcLevel][LL], &hl = m_subband[srcLevel][HL];
       448         CSubband &lh = m_subband[srcLevel][LH], &hh = m_subband[srcLevel][HH];
       449
       450         if (hiRow) {
       451         #ifdef __PGFROISUPPORT__
       452                 const bool storePos = wquot < ll.BufferWidth();
       453                 UINT32 llPos = 0, hlPos = 0, lhPos = 0, hhPos = 0;
       454
       455                 if (storePos) {
       456                         // save current src buffer positions
       457                         llPos = ll.GetBuffPos();
       458                         hlPos = hl.GetBuffPos();
       459                         lhPos = lh.GetBuffPos();
       460                         hhPos = hh.GetBuffPos();
       461                 }
       462         #endif
       463
       464                 for (UINT32 i=0; i < wquot; i++) {
       465                         *loRow++ = ll.ReadBuffer();// first access, than increment
       466                         *loRow++ = hl.ReadBuffer();// first access, than increment
       467                         *hiRow++ = lh.ReadBuffer();// first access, than increment
       468                         *hiRow++ = hh.ReadBuffer();// first access, than increment
       469                 }
       470
       471                 if (wrem) {
       472                         *loRow++ = ll.ReadBuffer();// first access, than increment
       473                         *hiRow++ = lh.ReadBuffer();// first access, than increment
       474                 }
       475
       476         #ifdef __PGFROISUPPORT__
       477                 if (storePos) {
       478                         // increment src buffer positions
       479                         ll.IncBuffRow(llPos);
       480                         hl.IncBuffRow(hlPos);
       481                         lh.IncBuffRow(lhPos);
       482                         hh.IncBuffRow(hhPos);
       483                 }
       484         #endif
       485
       486         } else {
       487         #ifdef __PGFROISUPPORT__
       488                 const bool storePos = wquot < ll.BufferWidth();
       489                 UINT32 llPos = 0, hlPos = 0;
       490
       491                 if (storePos) {
       492                         // save current src buffer positions
       493                         llPos = ll.GetBuffPos();
       494                         hlPos = hl.GetBuffPos();
       495                 }
       496         #endif
       497
       498                 for (UINT32 i=0; i < wquot; i++) {
       499                         *loRow++ = ll.ReadBuffer();// first access, than increment
       500                         *loRow++ = hl.ReadBuffer();// first access, than increment
       501                 }
       502                 if (wrem) *loRow++ = ll.ReadBuffer();
       503
       504         #ifdef __PGFROISUPPORT__
       505                 if (storePos) {
       506                         // increment src buffer positions
       507                         ll.IncBuffRow(llPos);
       508                         hl.IncBuffRow(hlPos);
       509                 }
       510         #endif
       511         }
       512 }

and that's the whole function, no cap. it's giving private, it's giving line 444. πŸ’–


So like, CWaveletTransform is besties with CSubband. They're friends, it's official. This friendship was declared at line 56 of WaveletTransform.h. Slay. πŸ’…

  • friend class CSubband [friend] β€” Definition at line 56 of file WaveletTransform.h.

πŸ“¦ MEMBER DATA DOCUMENTATION πŸ“¦

  • int CWaveletTransform::m_nLevels [private] β€” this is the number of LL levels, and it's literally one more than header.nLevels in PGFimage. Defined at line 141 of WaveletTransform.h. iconic. ✨
  • CSubband(* CWaveletTransform::m_subband)[NSubbands] [private] β€” this is a quadtree of subbands, and the subbands are LL, HL, LH, HH. Defined at line 142 of WaveletTransform.h. periodt. πŸ’…

✍️ AUTHOR ✍️

This whole page was generated automatically by Doxygen for libpgf straight from the source code. No human wrote this, but I'm here to make it cute. πŸ’–


That's the tea, bestie. This page is CWaveletTransform(3), version 7.21.2, dated Thu Jul 24 2025 00:00:00. Stay slay, and remember: keep every detail, no cap. πŸ’…βœ¨

πŸ“¦ where this page comes from