我有一个字节数组,其中每个字节与我的图像的像素值相关。这些字节值正好是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);
}
答案 0 :(得分:2)
您需要在每行的开头(在代码中的h的循环开始处)推送一个8位零。这是“过滤器类型”字节,在您的情况下应为0,表示“无过滤”。
IHDR中的过滤器方法“0”只是说IDAT在每行的开头包含一个过滤字节。 IHDR中的选项允许其他方法,例如省略这些字节并假设每行的“0”过滤器类型。最后,PNG作者从未批准过滤字节每行方法以外的任何方法。
还需要在h的循环结束时检查位> 0 - 只需将h循环上的右括号向下移动,并在那里重置值= 0,因此每一行将被填充以填补最后一个字节。