VPAT at higher bit depths

Yup, code snippet would be most useful…

Considering the symptoms that you describe I suspect that you have converted one pointer too many to unsigned short… Consider the following snippet:

 1   unsigned char *base = 0;
 2   PVPAT vpa = 0;
 3   GetImageVPA(img, 0, reinterpret_cast<void**>(&base), &vpa); 
 4   // loops over all lines and columns...
 5   for (y = 0 ; y < height ; ++y) 
 6   {
 7     // precalculated line pointer saves time...  
 8     unsigned char* line = base + vpa[y].YEntry;  
 9     for (x = 0 ; x < width ; ++x)
10     {
11       auto pPix = reinterpret_cast<unsigned char*>(line + vpa[x].XEntry);
12       // pPix points to the pixel now! do with it whatever pleases you...
13       ...
14     }
15   }

As it uses unsigned char* it’s evident that this is suitable for 8 bits per pixel only. You might be tempted to simply substitute all the unsigned char* with unsigned short* to make that bit of code 16 bit capable - but that’s wrong:

  • changing the type in line 1 will most likely make your code skip every other line (the exact behavior depends on the actual content of the VPAT)

  • changing the type in line 8 will most likely make your code skip every other pixel (again, this ultimately depends on the actual content of the VPAT)

There is exactly one line where you need to make that substitution: Line 11 where the reinterpret_cast happens - all the other parts need to continue using unsigned char*: The VPAT gives byte offsets; therefore for pointer arithmetics to work properly it must be carried out with a 1-byte type regardless of the pixel type. Only once you calculated the address of the pixel is it necessary to change the interpretation of that pointer. So for 16 bits per pixel the code should look like this:

 1   unsigned char *base = 0;
 2   PVPAT vpa = 0;
 3   GetImageVPA(img, 0, reinterpret_cast<void**>(&base), &vpa); 
 4   // loops over all lines and columns...
 5   for (y = 0 ; y < height ; ++y) 
 6   {
 7     // precalculated line pointer saves time...  
 8     unsigned char* line = base + vpa[y].YEntry;  
 9     for (x = 0 ; x < width ; ++x)
10     {
11       auto pPix = reinterpret_cast<unsigned short*>(line + vpa[x].XEntry);
12       // pPix points to the pixel now! do with it whatever pleases you...
13       ...
14     }
15   }

Confusing? If so, then try the following twist which (in my opinion) makes the code slightly more obvious because it emphasizes the difference between pixel address calculation and the pixel access. It’s also a tiny little bit safer because you cannot accidentally dereference a base or line pointer:

 1   intptr_t base = 0;
 2   PVPAT vpa = 0;
 3   GetImageVPA(img, 0, reinterpret_cast<void**>(&base), &vpa); 
 4   // loops over all lines and columns...
 5   for (y = 0 ; y < height ; ++y) 
 6   {
 7     // precalculated line pointer saves time...  
 8     auto line = base + vpa[y].YEntry;  
 9     for (x = 0 ; x < width ; ++x)
10     {
11       auto pPix = reinterpret_cast<unsigned short*>(line + vpa[x].XEntry);
12       // pPix points to the pixel now! do with it whatever pleases you...
13       ...
14     }
15   }
3 Likes