ZLib PNG压缩,深度为1

时间:2016-07-28 17:18:02

标签: c++ png zlib

我有一个字节数组,其中每个字节与我的图像的像素值相关。这些字节值正好是0或255.数组从左到右,然后从上到下打包。从这个数组我想创建一个深度为1的灰度.PNG图像。我不能使用任何包装器到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);
}

1 个答案:

答案 0 :(得分:2)

您需要在每行的开头(在代码中的h的循环开始处)推送一个8位零。这是“过滤器类型”字节,在您的情况下应为0,表示“无过滤”。

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

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