位深度为 1 的 ZLib PNG 压缩
ZLib PNG Compression with a bit depth of 1
我有一个字节数组,其中每个字节都与图像的像素值相关。这些字节值恰好是 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);
}
您需要在每一行的开头(在您的代码中 h 循环的开头)压入一个 8 位零。那是 "filter type" 字节,在你的情况下应该是 0,意思是 "no filtering"。
IHDR 中的过滤方法“0”只是说 IDAT 在每一行的开头包含一个过滤字节。 IHDR 中的选项允许其他方法,例如省略那些字节并假设每行的过滤器类型为“0”。最后,PNG 作者除了 filter-byte-per-row 方法之外从未批准任何方法。
此外,还需要在 h 的循环结束时对 bit>0 进行检查——只需将 h 循环上的右括号向下移动到它的下方,并在那里也重置 value=0,所以每行将被填充以填充最后一个字节。
我有一个字节数组,其中每个字节都与图像的像素值相关。这些字节值恰好是 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);
}
您需要在每一行的开头(在您的代码中 h 循环的开头)压入一个 8 位零。那是 "filter type" 字节,在你的情况下应该是 0,意思是 "no filtering"。
IHDR 中的过滤方法“0”只是说 IDAT 在每一行的开头包含一个过滤字节。 IHDR 中的选项允许其他方法,例如省略那些字节并假设每行的过滤器类型为“0”。最后,PNG 作者除了 filter-byte-per-row 方法之外从未批准任何方法。
此外,还需要在 h 的循环结束时对 bit>0 进行检查——只需将 h 循环上的右括号向下移动到它的下方,并在那里也重置 value=0,所以每行将被填充以填充最后一个字节。