ZLib PNG压缩,位深度为1



我有一个字节数组,其中每个字节都与图像的像素值相关。这些字节值要么是0,要么是255。数组是从左到右,然后从上到下排列的。从这个数组中,我想创建一个灰度级.PNG图像,深度为1。我不能对zlib使用任何包装器。

该函数在创建有效的png文件时起作用。但是,创建的图像不正确。我很确定(尽管可能是错误的)问题在于将数据打包到一个字节数组中,并将其传递给Zlib deflate函数。我已经通读了规范:https://www.w3.org/TR/PNG/但无济于事。

我有以下代码:

#include "zlib.h"
enum E_PNGImageType
{
  eGreyScale = 0,
  eTrueColour = 2,
  eIndexedColour = 3,
  eGreyScaleAlpha = 4,
  eTrueColourAlpha = 6
};
enum E_PNGBitDepth
{
  eOne = 1,
  eTwo = 2,
  eFour = 4,
  eEight = 8,
  eSixteen = 16
};
enum E_PNGCompressionMethod
{
  eDeflate = 0
};

enum E_PNGFilterMethod
{
  eAdaptive = 0
};
enum E_PNGInterlaceMethod
{
  eNone = 0,
  eAdam7 = 1
};
void CreatePNG(BYTE *pData, int iWidth, int iHeight)
{
  /* Convert each byte to a bit and package the bits into a byte */
  std::vector<BYTE> vBitData;
  int bit = 0;
  BYTE value = 0;
  vBitData.clear();
  for (int h = 0; h < iHeight; h++)
  {
    for (int w = 0; w < iWidth; w++)
    {
      if (pData[(h * iWidth) + w] > 0)
      {
        value += (1 << (7 - bit));
      }
      bit++;
      if (bit == 8)
      {
        bit = 0;
        vBitData.push_back(value);
        value = 0;
      }
    }
  }
  if (bit > 0)
  {
    vBitData.push_back(value);
  }
  GeneratePNGData(vBitData.data(), iWidth, iHeight, vBitData.size(), &vPNGData);
}
void GeneratePNGData(BYTE *pData, unsigned int uiWidth, unsigned int uiHeight, unsigned int uiSize, std::vector<BYTE> *pPNGData)
{
  int iCRCStartIndex;
  int iSize;
  unsigned int uiCRC;
  z_stream strm;
  const int C_BUFFER_SIZE = 20000;
  unsigned char tempBuffer[C_BUFFER_SIZE];
  int iLengthDataIndex;
  int iRes;
  pPNGData->clear();
  /* PNG Signature */
  pPNGData->push_back(137);
  pPNGData->push_back(80);
  pPNGData->push_back(78);
  pPNGData->push_back(71);
  pPNGData->push_back(13);
  pPNGData->push_back(10);
  pPNGData->push_back(26);
  pPNGData->push_back(10);
  /* IDHR Image Header */
  /* 4 Bytes: Chunk Length */
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(13);
  /* Checksum Start Index */
  iCRCStartIndex = pPNGData->size();
  /* 4 Bytes: Chunk Type */
  pPNGData->push_back(73);
  pPNGData->push_back(72);
  pPNGData->push_back(68);
  pPNGData->push_back(82);
  /* 4 Bytes: Chunk Data - Width */
  pPNGData->push_back(((BYTE*)&uiWidth)[3]);
  pPNGData->push_back(((BYTE*)&uiWidth)[2]);
  pPNGData->push_back(((BYTE*)&uiWidth)[1]);
  pPNGData->push_back(((BYTE*)&uiWidth)[0]);
  /* 4 Bytes: Chunk Data - Height */
  pPNGData->push_back(((BYTE*)&uiHeight)[3]);
  pPNGData->push_back(((BYTE*)&uiHeight)[2]);
  pPNGData->push_back(((BYTE*)&uiHeight)[1]);
  pPNGData->push_back(((BYTE*)&uiHeight)[0]);
  /* 1 Byte: Chunk Data - Bit Depth */
  pPNGData->push_back(E_PNGBitDepth::eOne);
  /* 1 Byte: Chunk Data - Colour Type */
  pPNGData->push_back(E_PNGImageType::eGreyScale);
  /* 1 Byte: Chunk Data - Compression Method */
  pPNGData->push_back(E_PNGCompressionMethod::eDeflate);
  /* 1 Byte: Chunk Data - Filter Method */
  pPNGData->push_back(E_PNGFilterMethod::eAdaptive);
  /* 1 Byte: Chunk Data - Interlace Method */
  pPNGData->push_back(E_PNGInterlaceMethod::eNone);
  /* Size of Data to Perform Checksum Over */
  iSize = pPNGData->size() - iCRCStartIndex;
  /* 4 Bytes: CRC */
  uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
  pPNGData->push_back(((BYTE*)&uiCRC)[3]);
  pPNGData->push_back(((BYTE*)&uiCRC)[2]);
  pPNGData->push_back(((BYTE*)&uiCRC)[1]);
  pPNGData->push_back(((BYTE*)&uiCRC)[0]);
  /* IDAT Image Data */
  /* Length Data Offset */
  iLengthDataIndex = pPNGData->size();
  /* 4 Bytes: Chunk Length */
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  /* Checksum Start Index */
  iCRCStartIndex = pPNGData->size();
  /* 4 Bytes: Chunk Type */
  pPNGData->push_back(73);
  pPNGData->push_back(68);
  pPNGData->push_back(65);
  pPNGData->push_back(84);
  /* Length Bytes: Chunk Data - Compressed Image Data */
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  deflateInit(&strm, Z_DEFLATED);
  strm.avail_in = uiSize;
  strm.next_in = pData;
  strm.avail_out = C_BUFFER_SIZE;
  strm.next_out = tempBuffer;
  iRes = deflate(&strm, Z_FINISH);
  if (iRes != Z_STREAM_END) MessageBox(NULL, "Error", "Error", 0);
  pPNGData->insert(pPNGData->end(), tempBuffer, tempBuffer + strm.total_out);
  deflateEnd(&strm);
  /* Now Length Is Know Edit Length Field */
  (*pPNGData)[iLengthDataIndex + 0] = ((BYTE*)&strm.total_out)[3];
  (*pPNGData)[iLengthDataIndex + 1] = ((BYTE*)&strm.total_out)[2];
  (*pPNGData)[iLengthDataIndex + 2] = ((BYTE*)&strm.total_out)[1];
  (*pPNGData)[iLengthDataIndex + 3] = ((BYTE*)&strm.total_out)[0];
  /* Size of Data to Perform Checksum Over */
  iSize = pPNGData->size() - iCRCStartIndex;
  /* 4 Bytes: CRC */
  uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
  pPNGData->push_back(((BYTE*)&uiCRC)[3]);
  pPNGData->push_back(((BYTE*)&uiCRC)[2]);
  pPNGData->push_back(((BYTE*)&uiCRC)[1]);
  pPNGData->push_back(((BYTE*)&uiCRC)[0]);
  /* IEND Image trailer */
  /* 4 Bytes: Chunk Length */
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  pPNGData->push_back(0);
  /* Checksum Start Index */
  iCRCStartIndex = pPNGData->size();
  /* 4 Bytes: Chunk Type */
  pPNGData->push_back(73);
  pPNGData->push_back(69);
  pPNGData->push_back(78);
  pPNGData->push_back(68);
  /* Size of Data to Perform Checksum Over */
  iSize = pPNGData->size() - iCRCStartIndex;
  /* 4 Bytes: CRC */
  uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
  pPNGData->push_back(((BYTE*)&uiCRC)[3]);
  pPNGData->push_back(((BYTE*)&uiCRC)[2]);
  pPNGData->push_back(((BYTE*)&uiCRC)[1]);
  pPNGData->push_back(((BYTE*)&uiCRC)[0]);
  /* Temp Debug Code */
  FILE* pFile;
  fopen_s(&pFile, "DEBUG_IMAGES\zzz_test_output.png", "wb");
  fwrite((*pPNGData).data(), 1, pPNGData->size(), pFile);
  fclose(pFile);
}

您需要在每行的开头(代码中h循环的开头)推送一个8位零。这是"过滤器类型"字节,在您的情况下应该是0,意思是"无过滤"。

IHDR中的过滤方法"0"只是说IDAT在每行的开头包含一个过滤字节。IHDR中提供了允许其他方法的选项,例如省略这些字节并为每行假设"0"过滤器类型。最后,PNG作者从未批准过除了每行过滤字节方法之外的任何方法。

此外,需要在h上的循环结束时检查位>0——只需将h循环上的右括号向下移动到它的下方,并在那里重置值=0,因此每行都将被填充以填充最后一个字节。

最新更新