Skip to content

Instantly share code, notes, and snippets.

@uyjulian
Created July 28, 2023 00:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uyjulian/79d724bce94dabd488129ad4dc39a50d to your computer and use it in GitHub Desktop.
Save uyjulian/79d724bce94dabd488129ad4dc39a50d to your computer and use it in GitHub Desktop.
using System;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Text;
namespace PureCSharpPngWriter
{
public static class PureCSharpPngWriterProgram
{
private static uint Adler32(byte[] bytes) {
const uint a32mod = 65521;
uint s1 = 1, s2 = 0;
foreach (byte b in bytes) {
s1 = (s1 + b) % a32mod;
s2 = (s2 + s1) % a32mod;
}
return ((s2 << 16) + s1);
}
private static uint[] crc32_tab = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
private static uint UpdateCRC32(byte[] buf, uint old_crc) {
uint crc = old_crc;
for (int n = 0; n < buf.Length; n += 1) {
crc ^= buf[n];
crc = (crc >> 4) ^ crc32_tab[crc & 15];
crc = (crc >> 4) ^ crc32_tab[crc & 15];
}
return crc;
}
private static void PutUInt32BE(byte[] buf, uint offset, uint val) {
buf[offset + 0] = (byte)((val & 0xFF000000) >> 24);
buf[offset + 1] = (byte)((val & 0x00FF0000) >> 16);
buf[offset + 2] = (byte)((val & 0x0000FF00) >> 8);
buf[offset + 3] = (byte)((val & 0x000000FF) >> 0);
}
private static void WritePNGChunk(Stream f, byte[] identBuf, byte[] buf) {
byte[] size = new byte[4];
PutUInt32BE(size, 0, (uint)buf.Length);
byte[] crcBuf = new byte[4];
uint crc = 0xFFFFFFFF;
crc = UpdateCRC32(identBuf, crc);
crc = UpdateCRC32(buf, crc);
crc ^= 0xFFFFFFFF;
PutUInt32BE(crcBuf, 0, crc);
f.Write(size, 0, size.Length);
f.Write(identBuf, 0, identBuf.Length);
f.Write(buf, 0, buf.Length);
f.Write(crcBuf, 0, crcBuf.Length);
}
private static byte[] UInt32LEArrayToByteArray(uint[] inbuf) {
byte[] outbuf = new byte[inbuf.Length * 4];
for (var i = 0; i < inbuf.Length; i += 1) {
uint val = inbuf[i];
outbuf[(i * 4) + 3] = (byte)((val & 0xFF000000) >> 24);
outbuf[(i * 4) + 2] = (byte)((val & 0x00FF0000) >> 16);
outbuf[(i * 4) + 1] = (byte)((val & 0x0000FF00) >> 8);
outbuf[(i * 4) + 0] = (byte)((val & 0x000000FF) >> 0);
}
return outbuf;
}
static void Main(string[] args)
{
byte[] pngHeader = new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
byte[] pngIHDR = new byte[13];
byte[] pngIDAT = new byte[0];
byte[] pngIEND = new byte[0];
byte[] pngChunkIdIHDR = new byte[] { 0x49, 0x48, 0x44, 0x52 };
byte[] pngChunkIdIDAT = new byte[] { 0x49, 0x44, 0x41, 0x54 };
byte[] pngChunkIdIEND = new byte[] { 0x49, 0x45, 0x4E, 0x44 };
uint[] pixels = new uint[256 * 256];
for (var i = 0; i < pixels.Length; i += 1)
{
pixels[i] = (((uint)0 & 0xFF) << 0) | (((uint)0 & 0xFF) << 8) | (((uint)0xFF & 0xFF) << 16) | (((uint)0xFF & 0xFF) << 24);
}
byte[] pixelsBytes = UInt32LEArrayToByteArray(pixels);
int imageWidth = 256;
int imageHeight = 256;
PutUInt32BE(pngIHDR, 0, (uint)imageWidth); // width
PutUInt32BE(pngIHDR, 4, (uint)imageHeight); // height
pngIHDR[8] = 8; // depth
pngIHDR[9] = 6; // color type
pngIHDR[10] = 0; // compression
pngIHDR[11] = 0; // filter
pngIHDR[12] = 0; // interlace
using (var cbio = new MemoryStream(pixelsBytes.Length)) {
cbio.WriteByte(0x78);
cbio.WriteByte(0x9C);
using (var cbio2 = new MemoryStream(pixelsBytes.Length + imageHeight)) {
for (int row = 0; row < imageHeight; row += 1) {
cbio2.WriteByte(0x00);
cbio2.Write(pixelsBytes, ((imageHeight - (row + 1)) * imageWidth) * 4, imageWidth * 4);
}
cbio2.Seek(0, SeekOrigin.Begin);
byte[] filteredPixelsBytes = new byte[cbio2.Length];
cbio2.Read(filteredPixelsBytes, 0, (int)cbio2.Length);
using (var cbiod = new DeflateStream(cbio, CompressionMode.Compress, true)) {
cbiod.Write(filteredPixelsBytes, 0, filteredPixelsBytes.Length);
}
byte[] adlerBuf = new byte[4];
PutUInt32BE(adlerBuf, 0, Adler32(filteredPixelsBytes));
cbio.Write(adlerBuf, 0, adlerBuf.Length);
}
cbio.Seek(0, SeekOrigin.Begin);
pngIDAT = new byte[cbio.Length];
cbio.Read(pngIDAT, 0, (int)cbio.Length);
}
using (var wf = File.Open("outfile.png", FileMode.Create)) {
wf.Write(pngHeader, 0, pngHeader.Length);
WritePNGChunk(wf, pngChunkIdIHDR, pngIHDR);
WritePNGChunk(wf, pngChunkIdIDAT, pngIDAT);
WritePNGChunk(wf, pngChunkIdIEND, pngIEND);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment