如何使用缓冲区制作 PNG(8 位索引颜色)?获得"compression header fails checksum"



你好吗?

我正试图在Game Maker Studio 2上使用Buffers从头开始制作PNG,但我在使用pngcheck:时遇到了一些错误

File: [01;37mtest.png[0m (85 bytes)
chunk [40;33mIHDR[0m at offset 0x0000c, length 13
2 x 2 image, 8-bit palette, non-interlaced
chunk [40;33mPLTE[0m at offset 0x00025, length 12: 4 palette entries
chunk [40;33mIDAT[0m at offset 0x0003d, length 4
zlib: compression header fails checksum
zlib: inflate error = -3 (data error)

我在下面发布代码,以显示到目前为止我得到的东西。正如你所看到的,我已经设法添加了IHDR标头,将Bit Depth设置为8,将Color Type设置为3等,并添加了所有其他数据块。

我还在GameMaker中添加了一个CRC32脚本来计算正确的字节,从而使CRC字段正常工作。

我试图实现的是使用PLTE块中的颜色创建一个具有4种不同颜色的简单2x2图像,但我得到的是一个红色正方形(2x2(,以及pngcheck的上述错误。

我想我错过了";压缩";部分原因是,如果有人能帮我做这件事,那将是非常有帮助的。

// Variables
var File;
File = argument0;
// Loop Variables
var i;
// Create Buffer
var Buffer = buffer_create(1024, buffer_grow, 1);
// Signature
var Signature = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
for(i=0; i<8; i++){ buffer_write(Buffer, buffer_u8, Signature[i]); }

// --------------------------------------------------
// IHDR
// --------------------------------------------------
// Length
var IHDRLength = 13;
buffer_write(Buffer, buffer_u8, (IHDRLength >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IHDRLength >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IHDRLength >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IHDRLength & 255));
// CRC Position
var IHDRCRCPos = buffer_tell(Buffer);
// Type
var IHDRType = ["I", "H", "D", "R"];
for(i=0; i<4; i++){ buffer_write(Buffer, buffer_u8, ord(IHDRType[i])); }
// Data (Bytes): Width (4), Height (4), Bit Depth (1), Color Type (1), Compression Method (1), Filter Method (1), Interlace Method (1)
// Width
var Width = 2;
buffer_write(Buffer, buffer_u8, (Width >> 24) & 255);
buffer_write(Buffer, buffer_u8, (Width >> 16) & 255);
buffer_write(Buffer, buffer_u8, (Width >> 8) & 255);
buffer_write(Buffer, buffer_u8, (Width & 255));
// Height
var Height = 2;
buffer_write(Buffer, buffer_u8, (Height >> 24) & 255);
buffer_write(Buffer, buffer_u8, (Height >> 16) & 255);
buffer_write(Buffer, buffer_u8, (Height >> 8) & 255);
buffer_write(Buffer, buffer_u8, (Height & 255));
// Bit Depth
var BitDepth = 8;
buffer_write(Buffer, buffer_u8, BitDepth);
// Color Type
var ColorType = 3;
buffer_write(Buffer, buffer_u8, ColorType);
// Compression
var Compression = 0;
buffer_write(Buffer, buffer_u8, Compression);
// Filter
var Filter = 0;
buffer_write(Buffer, buffer_u8, Filter);
// Interlace
var Interlace = 0;
buffer_write(Buffer, buffer_u8, Interlace);
// CRC (Type+Data Bytes)
var IHDRCRC = CRC32(Buffer, IHDRCRCPos, 17);
buffer_write(Buffer, buffer_u8, (IHDRCRC >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IHDRCRC >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IHDRCRC >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IHDRCRC & 255));

// --------------------------------------------------
// PLTE
// --------------------------------------------------
// Length (Colors*3)
var PLTELength = 4*3;
buffer_write(Buffer, buffer_u8, (PLTELength >> 24) & 255);
buffer_write(Buffer, buffer_u8, (PLTELength >> 16) & 255);
buffer_write(Buffer, buffer_u8, (PLTELength >> 8) & 255);
buffer_write(Buffer, buffer_u8, (PLTELength & 255));
// CRC Position
var PLTECRCPos = buffer_tell(Buffer);
var ColorBytes = 0;
// Type
var PLTEType = ["P", "L", "T", "E"];
for(i=0; i<4; i++){ buffer_write(Buffer, buffer_u8, ord(PLTEType[i])); }
// Data (Bytes): R (1), G (1), B (1)
// PLTELength div Colors
// Color 1
buffer_write(Buffer, buffer_u8, 255);
buffer_write(Buffer, buffer_u8, 55);
buffer_write(Buffer, buffer_u8, 55);
ColorBytes += 3;
// Color 2
buffer_write(Buffer, buffer_u8, 55);
buffer_write(Buffer, buffer_u8, 255);
buffer_write(Buffer, buffer_u8, 55);
ColorBytes += 3;
// Color 3
buffer_write(Buffer, buffer_u8, 55);
buffer_write(Buffer, buffer_u8, 55);
buffer_write(Buffer, buffer_u8, 255);
ColorBytes += 3;
// Color 4
buffer_write(Buffer, buffer_u8, 0);
buffer_write(Buffer, buffer_u8, 0);
buffer_write(Buffer, buffer_u8, 0);
ColorBytes += 3;
// CRC
var PLTECRC = CRC32(Buffer, PLTECRCPos, 4+ColorBytes);
buffer_write(Buffer, buffer_u8, (PLTECRC >> 24) & 255);
buffer_write(Buffer, buffer_u8, (PLTECRC >> 16) & 255);
buffer_write(Buffer, buffer_u8, (PLTECRC >> 8) & 255);
buffer_write(Buffer, buffer_u8, (PLTECRC & 255));

// --------------------------------------------------
// IDAT
// --------------------------------------------------
// Length (Pixels*3)
var IDATLength = Width*Height;
buffer_write(Buffer, buffer_u8, (IDATLength >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IDATLength >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IDATLength >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IDATLength & 255));
// CRC Position
var IDATCRCPos = buffer_tell(Buffer);
var IDATBytes = 0;
// Type
var IDATType = ["I", "D", "A", "T"];
for(i=0; i<4; i++){ buffer_write(Buffer, buffer_u8, ord(IDATType[i])); }
// Data (Bytes): Color Index (1)
// Colors (Width > Height)
buffer_write(Buffer, buffer_u8, 0);
IDATBytes += 1;
buffer_write(Buffer, buffer_u8, 1);
IDATBytes += 1;
buffer_write(Buffer, buffer_u8, 2);
IDATBytes += 1;
buffer_write(Buffer, buffer_u8, 3);
IDATBytes += 1;
// CRC
var IDATCRC = CRC32(Buffer, IDATCRCPos, 4+IDATBytes);
buffer_write(Buffer, buffer_u8, (IDATCRC >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IDATCRC >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IDATCRC >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IDATCRC & 255));

// --------------------------------------------------
// IEND
// --------------------------------------------------
// Length
var IENDLength = 0;
buffer_write(Buffer, buffer_u8, (IENDLength >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IENDLength >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IENDLength >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IENDLength & 255));
// CRC Position
var IENDCRCPos = buffer_tell(Buffer);
// Type
var IENDType = ["I", "E", "N", "D"];
for(i=0; i<4; i++){ buffer_write(Buffer, buffer_u8, ord(IENDType[i])); }
// CRC
var IENDCRC = CRC32(Buffer, IENDCRCPos, 4);
buffer_write(Buffer, buffer_u8, (IENDCRC >> 24) & 255);
buffer_write(Buffer, buffer_u8, (IENDCRC >> 16) & 255);
buffer_write(Buffer, buffer_u8, (IENDCRC >> 8) & 255);
buffer_write(Buffer, buffer_u8, (IENDCRC & 255));
// Save Buffer
buffer_save(Buffer, "test.png");
// Delete Buffers
buffer_delete(Buffer);

谢谢你抽出时间!

解决方案

编辑:关于解决方案,请看Mark Adler的评论,那里有一个很好的讨论,他帮助我完成了整个过程。

我的PNG没有被创建的原因是:

  1. IDAT区块中的数据每行都缺少一个1字节的值,因此它是6字节的数据,而不是4字节

类似:

0 0 1
0 2 3
  1. 它在IDAT区块的数据中缺少zlib压缩。如果您使用的是Game Maker,请不要使用buffer_compression函数,而是使用YellowAfterlife HERE制作的zlib扩展。

  2. 当我压缩数据时,它向缓冲区添加了8个字节。在计算IDAT块的LENGTH和CRC值时,需要考虑这些字节。

实际上,您没有实现所需的压缩。此外,每一行都必须以一个过滤器字节为前缀,因此您将压缩六个字节,而不是四个。

IDAT块所需的压缩是zlib格式(压缩数据周围的zlib标头和尾部(。我不知道zlib是否或如何在您编程的环境中可用。

对两行使用零(无筛选(的筛选器类型,将压缩的数据为6个字节0 0 1 0 2 3。一旦你把它做好了,结果将是,十六进制:

89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52
00 00 00 02 00 00 00 02 08 03 00 00 00 45 68 fd
16 00 00 00 0c 50 4c 54 45 ff 37 37 37 ff 37 37
37 ff 00 00 00 5d 2a e4 7e 00 00 00 0e 49 44 41
54 78 9c 63 60 60 64 60 62 06 00 00 11 00 07 9e
a2 2a 12 00 00 00 00 49 45 4e 44 ae 42 60 82 

我想补充一点,GMS2buffer_compress确实调用zlibcompress,所以只要您知道CRC32的两个头部字节和尾部4个字节,就应该很好地使用。我以前用过它来生成ZIP文件。

最新更新