Как вы?
Я пытаюсь создать PNG с нуля в Game Maker Studio 2, используя буферы, но получаю некоторые ошибки при использовании 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, установить битовую глубину на 8, тип цвета на 3 и т. д. и добавить все остальные фрагменты данных.
Мне также удалось заставить поле CRC работать, добавив скрипт CRC32 в Game Maker для вычисления правильных байтов.
Чего я пытаюсь добиться, так это создать простое изображение 2x2 с 4 разными цветами, используя цвета из фрагмента PLTE, но я получаю один красный квадрат (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);
Спасибо за ваше время!
Обновлено: пожалуйста, посмотрите на комментарий Марка Адлера для решения, там была хорошая дискуссия, и он помог мне на протяжении всего процесса.
Причина, по которой мой PNG не создавался, заключалась в следующем:
Нравиться:
0 0 1
0 2 3
В данных из блока IDAT отсутствовало сжатие zlib. Если вы используете Game Maker, не используйте функцию buffer_compress, вместо этого используйте расширение zlib, созданное YellowAfterlife ЗДЕСЬ.
Когда я сжимал данные, в буфер добавлялось 8 байт. Эти байты необходимо учитывать при расчете значений LENGTH и CRC фрагмента IDAT.
Действительно, вы не реализуете требуемое сжатие. Также каждая строка должна иметь префикс байта фильтра, поэтому вы сжимаете шесть байтов, а не четыре.
Необходимым сжатием для фрагментов IDAT является формат zlib (заголовок zlib и трейлер вокруг данных, сжатых с помощью deflate). Я не знаю, доступен ли zlib в среде, в которой вы программируете.
Используя нулевой тип фильтра (без фильтрации) для обеих строк, данные, которые вы будете сжимать, представляют собой шесть байтов 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
Кроме того, когда вы говорите «сжатие для фрагментов IDAT», вы имеете в виду весь фрагмент или только данные пикселей? Нужно ли применять сжатие zlib ко всему блоку IDAT, включая длину, тип, данные и CRC?
Данные фрагмента IDAT представляют собой поток zlib. Точнее, если есть несколько фрагментов IDAT, объединение данных фрагмента IDAT представляет собой один поток zlib. Эти данные представляют собой не только пиксели, но и один байт фильтра в начале каждой строки.
Там один байт фильтра в начале каждой строки. Для типа цвета 3 каждый пиксель представляет собой один байт, который является индексом в палитре. Таким образом, изображение 2x2 будет состоять из шести байтов.
Хорошо, я сделал то, что вы сказали о добавлении 1 байта для фильтра для каждой строки, а также попытался использовать функцию «buffer_compress», предоставленную Game Maker, но она не сработала, поэтому я нашел расширение от YellowAfterlife на Marketplace (zlib сжатие) и вроде сработало! Я сжал данные из IDAT (уровень 8). Я могу передать сообщение «сбой контрольной суммы заголовка сжатия», и теперь оно говорит: «zlib: дефлированный, окно 32 КБ, максимальное сжатие», так что на данный момент мы можем предположить, что сжатие сработало? Проблема в том, что теперь я получаю ошибку CRC в чанке IDAT :(
Кстати, это журнал checkpng: File: [01;37mtest.png[0m (123 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 13 zlib: deflated, 32K window, maximum compression CRC error in chunk IDAT (computed d668a65c, expected ef6c8c7d) [01;31mERRORS DETECTED[0m in test.png
Вычислите CRC для «IDAT» + сжатых данных. Используйте длину сжатых данных для длины фрагмента.
Да! Я сделал все, что вы мне сказали, и теперь он создает 8-битный PNG с индексированными цветами, как и ожидалось, большое спасибо! Я только что проверил некоторые документы и увидел в них ваше имя, я не знал, что вы являетесь одним из создателей формата PNG, какая честь! Извлеченные уроки: не забудьте про 1-байтовый фильтр в каждой строке данных в IDAT, обратите внимание на длину и правильно рассчитайте CRC (примите во внимание сжатие), а если вы используете Game Maker, используйте ZLIB расширение от YellowAfterlife для сжатия данных IDAT вместо использования функции «buffer_compress».
Я хотел бы добавить, что GMS2 buffer_compress вызывает zlib compress, поэтому его следует использовать, если вы знаете два байта заголовка и завершающие 4 байта CRC32. Раньше я использовал его для создания ZIP-файлов.
Привет @Mark Adler, спасибо за ответ. Итак, для каждой строки я должен добавить 2 значения U8 (2 байта), верно? Поскольку я использую тип цвета 3 (индексированный цвет), не должен ли он быть 3 байта вместо 6? (2 для фильтра, 1 для индекса цвета?). К сожалению, у меня нет доступа к библиотекам в Game Maker, но есть функция «buffer_compress», которую мне нужно сначала проверить, работает ли она. ссылка