Home c++ Chunk of IDAT PNG format, how to load PNG

Chunk of IDAT PNG format, how to load PNG

Author

Date

Category

I need to extract color information from a png image, including the alpha channel. If you open the picture in a hex editor, the data I need will be in the IDAT block. True, they are encrypted there. Can anyone know how to decrypt this block using C++ tools?


Answer 1, authority 100%

They are not encrypted there. They are packed 🙂

The format itself is described – IDAT section and compression .

Here are just pens to unpack – it will take a long time. I recommend using a ready-made project – libpng , which you can download for your platform. Or use ready-made wrappers – ImageMagick , OpenCV , Qt – they provide access to pixels and additional information.


Answer 2, authority 50%

There is a very good and small library – Lodepng – I use it to work with textures in OpenGL. Provides pretty good features.


Answer 3, authority 50%

The IDAT block can be unpacked with the zlib library, which is available wherever possible)

In JavaScript it looks like this (along with pixel decoding):

zlib.inflate (this.imgData, function (err, data) {
  var byte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft;
  if (err) throw err;
  pixelBytes = _this.pixelBitlength / 8;
  scanlineLength = pixelBytes * _this.width;
  pixels = new Buffer (scanlineLength * _this.height);
  length = data.length;
  row = 0;
  pos = 0;
  c = 0;
  while (pos & lt; length) {
    switch (data [pos ++]) {
      case 0:
        for (i = 0; i & lt; scanlineLength; i + = 1) {
          pixels [C++] = data [pos ++];
        }
        break;
      case 1:
        for (i = 0; i & lt; scanlineLength; i + = 1) {
          byte = data [pos ++];
          left = i & lt; pixelBytes? 0: pixels [c - pixelBytes];
          pixels [C++] = (byte + left)% 256;
        }
        break;
      case 2:
        for (i = 0; i & lt; scanlineLength; i + = 1) {
          byte = data [pos ++];
          col = (i - (i% pixelBytes)) / pixelBytes;
          upper = row & amp; & amp; pixels [(row - 1) * scanlineLength + col * pixelBytes + (i% pixelBytes)];
          pixels [C++] = (upper + byte)% 256;
        }
        break;
      case 3:
        for (i = 0; i & lt; scanlineLength; i + = 1) {
          byte = data [pos ++];
          col = (i - (i% pixelBytes)) / pixelBytes;
          left = i & lt; pixelBytes? 0: pixels [c - pixelBytes];
          upper = row & amp; & amp; pixels [(row - 1) * scanlineLength + col * pixelBytes + (i% pixelBytes)];
          pixels [C++] = (byte + Math.floor ((left + upper) / 2))% 256;
        }
        break;
      case 4:
        for (i = 0; i & lt; scanlineLength; i + = 1) {
          byte = data [pos ++];
          col = (i - (i% pixelBytes)) / pixelBytes;
          left = i & lt; pixelBytes? 0: pixels [c - pixelBytes];
          if (row === 0) {
            upper = upperLeft = 0;
          } else {
            upper = pixels [(row - 1) * scanlineLength + col * pixelBytes + (i% pixelBytes)];
            upperLeft = col & amp; & amp; pixels [(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i% pixelBytes)];
          }
          p = left + upper - upperLeft;
          pa = Math.abs (p - left);
          pb = Math.abs (p - upper); 
PC = Math.abs (P - Upperleft);
          If (PA & LT; = PB & amp; & amp; PA & LT; = PC) {
            paeth = left;
          } ELSE If (PB & LT; = PC) {
            Paeth = Upper;
          } else {
            Paeth = Upperleft;
          }
          Pixels [C++] = (BYTE + PAETH)% 256;
        }
        Break;
      Default:
        Throw New Error ("Invalid Filter Algorithm:" + Data [POS - 1]);
    }
    ROW ++;
  }
  Callback (Pixels);
});

Taken from: https://github.com/devongovettt/png. JS / Blob / Master / PNG-Node.js # L171


Answer 4, Authority 17%

The IDAT unit can be unpaved by the ZLIB library, which is everywhere where you can only) but what to do next – find it difficult. The answer is swallowed in codes, and a good implementation from “LODEPNG”. But still, since this part is difficult to dig up – I will describe the more detailed.

  1. Read (or write filter) All Idat sections that are near
  2. We skip 2 bytes with IDAT (there is a compression method specified by cm. libpng.org )
  3. Unpack everything in one piece + Reserve more Buffer IMG for RGB (RGBA)
  4. png_postprocessscanlines (IMG, IDAT, image_width, image_height, 0);
  5. Unfilter – (allows you to find some repetitive lines depicting)
  6. Remove Padding Bits – in other words alignment
  7. Adam7 Deinterlice – makes a “consecutive approximation” if the interlace_method = 8 flag is installed in the title. Detail https://ru.wikipedia.org/wiki/adam7

Implementation from step 4 to 7, I will show (this is the decryption IDAT)

unsigned PNG_POSTPROCEssCanLines (unsigned char * out, unsigned char * in, unsigned w, unsigned h, const void * info_png) {
 Unsigned ERR;
 / * This Function Converts The Filtered-Padded-Interlaced Data Into Pure 2D Image Buffer with the PNG's Colortype.
 Steps:
 *) If no adam7: 1) unfilter 2) Remove Padding Bits (= POSIBLE EXTRA BITS PER SCANLINE IF BPP & LT; 8)
 *) If Adam7: 1) 7x Unfilter 2) 7x Remove Padding Bits 3) Adam7_DeinterLace
 Note: The in Buffer Will Be Overwritten with Intermediate Data! * /
 // 2017.
 unsigned BPP = PNG_GETBPP (); // LODEPNG_GET_BPP (& amp; info_png- & gt; color);
 // 2017 IF (BPP == 0) RETURN 31; / * Error: Invalid Colortype * /
 if (interlace_method == 0) {
  If (BPP & LT; 8 & amp; & amp; W * BPP! = ((W * BPP + 7) / 8) * 8) {
   if ((ERR = PNG_UNFILTER (IN, IN, W, H, BPP))! = 0) RETURN ERR;
    PNG_REMOVEPADDINGBITS (OUT, IN, W * BPP, ((W * BPP + 7) / 8) * 8, h);
  }
  / * We Can Immediatly Filter Into The Out Buffer, No Other Steps Needed * /
  ELSE if ((Err = PNG_UNFILTER (OUT, IN, W, H, BPP))! = 0) Return ERR;
 }
 Else / * Interlace_Method IS 1 (Adam7) * /
 {
  unsigned password [7], PASSH [7]; Size_t Filter_PassStart [8], padded_passstart [8], PassStart [8];
  unsigned i, j;
  PNG_ADAM7_GETPASSVALUESSTART, PASSH, FILTER_PASSSTART, PADDED_PASSSTART, PASSSTART, W, H, BPP);
  for (i = 0; i & lt; 7; i ++)
  {
   if ((ERR = PNG_UNFILTER (& amp; in [Padded_passstart [i]], & amp; in [filter_passstart [i]], passw [i], passh [i], bpp))! = 0) Return ERR;
   / * TODO: Possible Efficiency Improvement: If in this Reduced Image The Bits Fit Nicely In 1 Scanline,
   Move Bytes Instad of Bits OR Move Not At All * /
   IF (BPP & LT; 8)
   {
    / * Remove Padding Bits in Scanlines; After Thre Still May Be Padding
    Bits Between The Different Reduced Images: Each Reduced Image Still Starts Nicely at A BYTE * /
    png_removepaddingbits (& amp; in [passstart [i]], & amp; in [Padded_passstart [i]], Passw [i] * BPP,
             ((Passw [i] * BPP + 7) / 8) * 8, PASSH [I]);
   }
  }
  PNG_ADAM7_DEINTERLACE (OUT, IN, W, H, BPP);
 }
 Return 0;
}
INT PNG_GETBPP () {
 // Return GetnumColorchannels (Colortype) * BitDepth; 
Switch (/ * Colortype * / Data.png.characteristics [1] / * byte 0x19 with file PNG * /) {/ * [0] = BitDepth * /
  Case 0: Return 1 * Data.png.characteristics [0]; / * byte 0x18 from the PNG * / / * Gray file * /
  Case 2: Return 3 * Data.png.characteristics [0]; / * RGB * /
  Case 3: Return 1 * Data.png.characteristics [0]; / * palette * /
  Case 4: Return 2 * Data.png.characteristics [0]; / * grey + alpha * /
  CASE 6: RETURN 4 * DATA.PNG.Characteristics [0]; / * RGBA * /
 }
Unsigned PNG_UNFILTER (Unsigned Char * Out, Const unsigned char * in, unsigned w, unsigned h, unsigned BPP) {
 / *
 For PNG Filter Method 0
 This Function Unfilters A Single Image (E.G. WITHOUT INTERLACING THIS IS CALLED ONCE, WITH ADAM7 SEVEN TIMES)
 Out Must Have Enough Bytes Allocated Already, In Must Have the Scanlines + 1 FiltertyPe Byte Per Scanline
 W and H Are Image Dimensions OR Dimensions of Reduced Image, BPP IS BITS PER PIXEL
 IN AND OUTRE ALLOWED TO BE THE SAME MEMORY ADDRESS (BUT AEN'T THE SAME SIZE IN HAS THE EXTRA FILTER Bytes)
 * /
 unsigned y;
 unsigned char * prevline = 0;
 unsigned err = 0;
 / * BYTEWIDTH IS USED FOR FILTERING, IS 1 WHEN BPP & LT; 8, Number of Bytes Per Pixel OtherWise * /
 Size_t ByteWidth = (BPP + 7) / 8;
 Size_t LineBytes = (W * BPP + 7) / 8;
 SIZE_T I;
 for (y = 0; y & lt; h; y ++)
 {
  Size_t Outindex = Linebytes * Y;
  Size_t Inindex = (1 + Linebytes) * y; / * The Extra FilterByte Added to Each Row * /
  unsigned char filtertype = in [inindex];
  // __ Emit __ (0xcc);
  // if ((ERR = PNG_UNFILTERSCANLINE (& amp; out [outindex], & amp; in [inindex + 1], prevline, bytewidth, filtertype, linebytes))! = 0) Return ERR;
  unsigned char * recon = Out + Outindex;
  Const unsigned char * scanline = in + inindex + 1;
  const unsigned char * precon = prevline;
  Size_T Length = Linebytes;
  // unsigned char * recon, const unsigned char * precanline, Const unsigned char * precon, Size_t Bytewidth, Unsigned Char FilteType, Size_T Length
 Switch (FiltertyPe)
 {
  Case 0: for (i = 0; i & lt; length; i ++) Recon [i] = Scanline [i]; Break;
  Case 1:
   for (i = 0; i & lt; bytewidth; i ++) recon [i] = scanline [i];
   For (I = BYTEWIDTH; I & LT; LENGTH; I ++) Recon [i] = Scanline [i] + Recon [i - bytewidth];
   Break;
  Case 2:
   If (Precon)
    for (i = 0; i & lt; length; i ++) recon [i] = scanline [i] + precon [i];
   else for (i = 0; i & lt; length; i ++) recon [i] = scanline [i];
   Break;
  Case 3:
   If (Precon)
   {
    for (i = 0; i & lt; bytewidth; i ++) recon [i] = scanline [i] + precon [i] / 2;
    For (I = BYTEWIDTH; I & LT; LENGTH; I ++) Recon [i] = Scanline [i] + ((Recon [i - BYTEWIDTH] + Precon [i]) / 2);
   }
   ELSE.
   {
    for (i = 0; i & lt; bytewidth; i ++) recon [i] = scanline [i];
    For (I = BYTEWIDTH; I & LT; LENGTH; I ++) Recon [i] = Scanline [i] + recon [i - bytewidth] / 2;
   }
   Break;
  Case 4:
   If (Precon)
   {
    for (i = 0; i & lt; bytewidth; i ++)
     Recon [i] = (Scanline [i] + Precon [i]); / * PaethPredictor (0, Precon [i], 0) Is Always Precon [i] * /
    for (I = BYTEWIDTH; I & LT; LENGTH; I ++)
     Recon [i] = (Scanline [i] + PNG_PAETHPREDICTOR (Recon [I - BYTEWIDTH], Precon [i], Precon [I - BYTEWIDTH]));
   }
   ELSE.
   {
    for (i = 0; i & lt; bytewidth; i ++)
     Recon [i] = Scanline [i];
    for (I = BYTEWIDTH; I & LT; LENGTH; I ++)
     / * Paethpredictor (Recon [I - BYTEWIDTH], 0, 0) IS ALWAYS RECON [I - BYTEWIDTH] * /
     Recon [i] = (Scanline [i] + recon [i - byotewidth]);
   }
   Break;
  Default: Return 36; / * Error: UNEXISTING FILTER TYPE GIVEN * /
  }
  prevline = & amp; out [outindex];
 }
 Return 0;
}
unsigned png_removepaddingbits (unsigned char * out, const unsigned char * in, size_t olinebits, size_t ilinebits, unsigned h)
{
 unsigned y;
 Unsigned Char Bit;
 size_t diff = ilinebits - olinebits;
 Size_t ibp = 0, OBP = 0; / * INPUT AND OUTPUT BIT POINTS * /
 for (y = 0; y & lt; h; y ++)
 {
  Size_t x;
  for (x = 0; x & lt; olinebits; x ++) {
   // Bit = In [IBP & GT; & GT; 3] & amp; (1 & gt; & gt; (7- (IBP & amp; 7))); // unsigned char bit = ReadBitFromReversedStream (& amp; IBP, IN); 
// if (Bit) out [OBP & GT; & GT; 3] | = (1 & gt; & gt; (7- (OBP & AMP; 7))); // setBitofreversedStream (& amp; OBP, OUT, BIT);
   if (in [IBP & GT; & gt; 3] & amp; (1 & gt; & gt; (7- (IBP & AMP; 7)))) OUT [OBP & GT; & GT; 3] | = (1 & gt; & gt; (7- (OBP & AMP; 7)));
   IBP ++; OBP ++;
  }
  IBP + = DIFF;
 }
}
static const unsigned char adam7_ix [7] = {0.4,0,2,0,1,0};
Static const unsigned char adam7_iy [7] = {0,0,4,2,0,1};
Static Const unsigned char adam7_dx [7] = {8,8,4,4,2,2,1};
Static Const unsigned char adam7_dy [7] = {8,8,8,4,4,2,2};
Void PNG_ADAM7_GETPASSVALUES (unsigned passw [7], Unsigned Passh [7], Size_t Filter_PassStart [8], Size_T Padded_PassStart [8], Size_t Passstart [8], unsigned w, unsigned h, unsigned BPP)
{unsigned i; / * Curl cycles in Define * /
 / * Calculate Width and Height In Pixels of Each Pass * /
 #Define PNG_AD71L (I) if ((Passw [i] = (W + (4- (I / 2)) * 2 - ((i & amp; 1) * ((5-i) + (i == 5))) - 1) / ((4- (I / 2)) * 2)) == 0) PASSH [0] = 0; \
        ELSE IF ((Passh [i] = (H + (I & LT; 3)? 8: (8 - ((i + 1) & amp; 6)) - (i == 2)? 4: ((i == 4) ? 2: (i == 6)? 1: 0) -1) / ((i & lt; 3)? 8: (8 - ((i + 1) & amp; 6)))) == 0) Passw [0 ] = 0;
 PNG_AD71L (0); PNG_AD71L (1); PNG_AD71L (2); PNG_AD71L (3); PNG_AD71L (4); PNG_AD71L (5); PNG_AD71L (6);
 filter_passstart [0] = padded_passstart [0] = passstart [0] = 0;
 #Undef PNG_ADL71L
 #Define PNG_ADL71L (i) filter_passstart [i + 1] = filter_passstart [i] + ((Passw [i] & amp; & amp; passh [i])? Passh [i] * (1 + (Passw [i] * BPP + 7) / 8): 0); / * if passw [i] IS 0, IT's 0 bytes, not 1 (no filtertype-byte) * / \
 padded_passstart [i + 1] = padded_passstart [i] + Passh [i] * ((Passw [i] * BPP + 7) / 8); / * Bits Padded If Needed to Fill Full Byte AT End of Each Scanline * / \
 passstart [i + 1] = passstart [i] + (Passh [i] * Passw [i] * BPP + 7) / 8; / * Only Padded AT End of Reduced Image * /
 PNG_AD71L (0); PNG_AD71L (1); PNG_AD71L (2); PNG_AD71L (3); PNG_AD71L (4); PNG_AD71L (5); PNG_AD71L (6);
 #Undef PNG_ADL71L
}
Void PNG_ADAM7_DEINTERLACE (unsigned char * out, const unsigned char * in, unsigned w, unsigned h, unsigned BPP)
{
 unsigned password [7], PASSH [7];
 Size_t Filter_PassStart [8], padded_passstart [8], PassStart [8];
 unsigned i;
 PNG_ADAM7_GETPASSVALUESSTART, PASSH, FILTER_PASSSTART, PADDED_PASSSTART, PASSSTART, W, H, BPP);
 if (BPP & GT; = 8)
 {
  for (i = 0; i & lt; 7; i ++)
  {
   unsigned x, y, b;
   size_t byotewidth = BPP / 8;
   For (y = 0; y & lt; Passh [i]; y ++)
   For (x = 0; x & lt; passw [i]; x ++)
   {
    size_t pixelinstart = passstart [i] + (y * passw [i] + x) * Bytewidth;
    size_t pixeloutstart = ((adam7_iy [i] + y * adam7_dy [i]) * W + Adam7_IX [i] + x * adam7_dx [i]) * bywidth;
    for (b = 0; b & lt; byotewidth; b ++)
     out [pixeloutstart + b] = in [pixelinstart + b];
   }
  }
 }
 ELSE / * BPP & LT; 8: Adam7 with Pixels & LT; 8 Bit IS A Bit Trickier: With Bit Pointers * /
 {
  for (i = 0; i & lt; 7; i ++)
  {
   unsigned x, y, b;
   unsigned ilinebits = BPP * PASSW [i];
   unsigned olinebits = bpp * w;
   Size_t OBP, IBP; / * Bit Pointers * /
   For (y = 0; y & lt; Passh [i]; y ++)
   For (x = 0; x & lt; passw [i]; x ++)
   {
    ibp = (8 * passstart [i]) + (y * ilinebits + x * bpp);
    OBP = (adam7_iy [i] + y * adam7_dy [i]) * OlineBits + (adam7_ix [i] + x * adam7_dx [i]) * BPP;
    FOR (B = 0; B & LT; BPP; B ++)
    {
     // unsigned char bit = ReadBitFromReversedStream (& amp; IBP, IN);
     / * Note That This Function Assumes The Out Buffer IS Completely 0, USE SetBitofreversedStream OtherWise * /
     // setBitofreversedStream0 (& amp; OBP, OUT, BIT);
     if (in [IBP & GT; & gt; 3] & amp; (1 & gt; & gt; (7- (IBP & AMP; 7)))) OUT [OBP & GT; & GT; 3] | = (1 & gt; & gt; (7- (OBP & AMP; 7)));
     IBP ++; OBP ++;
    }
   }
  }
 }
}
Unsigned Char PNG_PAETHPREDICTOR (Short A, Short B, Short C) {
 Short PA = ABS (B - C);
 Short PB = ABS (A - C);
 Short PC = ABS (A + B - C - C);
 if (PC & LT; PA & AMP; & amp; PC & LT; PB) Return (unsigned char) c;
 Return (PB & LT; PA)? (Unsigned Char) B: (unsigned char) a;
}

After running PNG_POSTPROCEssCanLines , we get a picture with bytes of the BPP depth, which is no longer difficult to withdraw with full-time tools on the screen.
“Extra” connections removed. Many treatments raised, left only 7 PNG_POSTPROcessScanLines, PNG_GETBPP, PNG_UNFILTER, PNG_REMOVEPADDINGBITS, PNG_ADAM7_GETPASSVALES, PNG_ADAM7_DEINTERLACE, PNG_PAETHPREDICTOR.

Use something like this

if (tag == 1413563465 / * IDAT * /) {
  D- & gt; read ((char *) & amp; i, 2); // Skip the 2nd byte. It is possible to check the "method"
  i = data.png.image_width * data.png.image_height * png_getbpp ();
  Initdeflate (D, 8192); DeflateRead (& amp; x_data, i + 256); // somehow divert
  img = malloc (i);
  PNG_POSTPROCESCANLINES (IMG, X_DATA, image_width, image_height, 0);
  For (j = 0; j & lt; image_heigth; j ++)
   for (i = 0; j & lt; image_width; i ++) {
    // output img on screen
    }
  }

Links

  1. http://lodev.org/lodepng/

  2. https://github.com/lvandeve/lodepng

  3. http://www.libpng.org

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions