20 using System.IO.Compression;
21 using System.Runtime.InteropServices;
22 using System.Threading;
26 internal static class PNGEncoder
28 public unsafe
static void SavePNG(
byte* image,
int width,
int height,
bool hasAlpha, Stream fs, FilterModes filter,
int threadCount = 0)
32 threadCount = filter == FilterModes.Adaptive ? Math.Max(1, Math.Min(width / 600, Environment.ProcessorCount - 2)) : 1;
36 fs.Write(
new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, 0, 8);
40 using (MemoryStream ihdr =
new MemoryStream(13))
42 ihdr.WriteASCIIString(
"IHDR");
44 ihdr.WriteInt(height);
60 ihdr.Seek(0, SeekOrigin.Begin);
63 fs.WriteUInt(CRC32.ComputeCRC(ihdr));
71 filteredImage = FilterImageData(image, width, height, hasAlpha ? 4 : 3, FilterModes.Adaptive, threadCount);
75 filteredImage = FilterImageData(image, width, height, hasAlpha ? 4 : 3, FilterModes.Adaptive);
78 using (MemoryStream compressedImage = StreamUtils.ZLibCompress(filteredImage, height * (width * (hasAlpha ? 4 : 3) + 1)))
80 compressedImage.Seek(0, SeekOrigin.Begin);
81 fs.WriteUInt((uint)compressedImage.Length);
82 fs.WriteASCIIString(
"IDAT");
83 compressedImage.Seek(0, SeekOrigin.Begin);
84 compressedImage.CopyTo(fs);
86 fs.WriteUInt(CRC32.ComputeCRC(compressedImage.GetBuffer(), (
int)compressedImage.Length,
new byte[] { 73, 68, 65, 84 }));
89 Marshal.FreeHGlobal(filteredImage);
93 fs.WriteASCIIString(
"IEND");
94 fs.Write(
new byte[] { 0xAE, 0x42, 0x60, 0x82 }, 0, 4);
98 internal enum FilterModes
108 internal unsafe
static void FilterRow(
byte* image,
int width,
int pixelSize,
int stride,
int y, FilterModes filter,
byte* destinationImage,
byte* tempBuffer)
110 int startIndex = y * stride;
111 int destinationIndex = y * (stride + 1);
113 if (filter == FilterModes.None)
115 destinationImage[destinationIndex++] = 0;
116 for (
int x = 0; x < width; x++)
118 destinationImage[destinationIndex++] = image[startIndex++];
119 destinationImage[destinationIndex++] = image[startIndex++];
120 destinationImage[destinationIndex++] = image[startIndex++];
124 destinationImage[destinationIndex++] = image[startIndex++];
129 else if (filter == FilterModes.Sub)
131 destinationImage[destinationIndex++] = 1;
132 for (
int x = 0; x < width; x++)
136 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
137 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
138 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
141 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
146 destinationImage[destinationIndex++] = image[startIndex++];
147 destinationImage[destinationIndex++] = image[startIndex++];
148 destinationImage[destinationIndex++] = image[startIndex++];
151 destinationImage[destinationIndex++] = image[startIndex++];
156 else if (filter == FilterModes.Up)
158 destinationImage[destinationIndex++] = 2;
159 for (
int x = 0; x < width; x++)
163 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
164 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
165 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
168 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
173 destinationImage[destinationIndex++] = image[startIndex++];
174 destinationImage[destinationIndex++] = image[startIndex++];
175 destinationImage[destinationIndex++] = image[startIndex++];
178 destinationImage[destinationIndex++] = image[startIndex++];
183 else if (filter == FilterModes.Average)
185 destinationImage[destinationIndex++] = 3;
186 for (
int x = 0; x < width; x++)
190 destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
191 destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
192 destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
195 destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
198 else if (x > 0 && y == 0)
200 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
201 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
202 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
205 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
208 else if (x == 0 && y > 0)
210 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
211 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
212 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
215 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
220 destinationImage[destinationIndex++] = image[startIndex++];
221 destinationImage[destinationIndex++] = image[startIndex++];
222 destinationImage[destinationIndex++] = image[startIndex++];
225 destinationImage[destinationIndex++] = image[startIndex++];
230 else if (filter == FilterModes.Paeth)
232 destinationImage[destinationIndex++] = 4;
233 for (
int x = 0; x < width; x++)
237 destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
238 destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
239 destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
242 destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
245 else if (x > 0 && y == 0)
247 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
248 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
249 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
252 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
255 else if (x == 0 && y > 0)
257 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
258 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
259 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
262 destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
267 destinationImage[destinationIndex++] = image[startIndex++];
268 destinationImage[destinationIndex++] = image[startIndex++];
269 destinationImage[destinationIndex++] = image[startIndex++];
272 destinationImage[destinationIndex++] = image[startIndex++];
277 else if (filter == FilterModes.Adaptive)
287 for (
int x = 0; x < width; x++)
289 for (
int i = 0; i < pixelSize; i++)
292 tempBuffer[tempIndex] = image[startIndex];
297 tempBuffer[tempIndex + stride] = (byte)(image[startIndex] - image[startIndex - pixelSize]);
301 tempBuffer[tempIndex + stride] = image[startIndex];
307 tempBuffer[tempIndex + stride * 2] = (byte)(image[startIndex] - image[startIndex - stride]);
311 tempBuffer[tempIndex + stride * 2] = image[startIndex];
317 tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex - stride]) / 2);
319 else if (x > 0 && y == 0)
321 tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - image[startIndex - pixelSize] / 2);
323 else if (x == 0 && y > 0)
325 tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - image[startIndex - stride] / 2);
329 tempBuffer[tempIndex + stride * 3] = image[startIndex];
335 tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex - pixelSize - stride]));
337 else if (x > 0 && y == 0)
339 tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - image[startIndex - pixelSize]);
341 else if (x == 0 && y > 0)
343 tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - image[startIndex - stride]);
347 tempBuffer[tempIndex + stride * 4] = image[startIndex];
350 none += tempBuffer[tempIndex];
351 sub += tempBuffer[tempIndex + stride];
352 up += tempBuffer[tempIndex + stride * 2];
353 average += tempBuffer[tempIndex + stride * 3];
354 paeth += tempBuffer[tempIndex + stride * 4];
360 if (paeth <= average && paeth <= up && paeth <= sub && paeth <= none)
362 destinationImage[destinationIndex++] = 4;
363 tempIndex = stride * 4;
364 for (
int i = 0; i < stride; i++)
366 destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
369 else if (average <= up && average <= sub && average <= none)
371 destinationImage[destinationIndex++] = 3;
372 tempIndex = stride * 3;
373 for (
int i = 0; i < stride; i++)
375 destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
378 else if (up <= sub && up <= none)
380 destinationImage[destinationIndex++] = 2;
381 tempIndex = stride * 2;
382 for (
int i = 0; i < stride; i++)
384 destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
387 else if (sub <= none)
389 destinationImage[destinationIndex++] = 1;
391 for (
int i = 0; i < stride; i++)
393 destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
398 destinationImage[destinationIndex++] = 0;
400 for (
int i = 0; i < stride; i++)
402 destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
408 internal unsafe
static IntPtr FilterImageData(
byte* image,
int width,
int height,
int pixelSize, FilterModes filter)
410 IntPtr filteredMemory = Marshal.AllocHGlobal(height * (1 + width * pixelSize));
414 if (filter == FilterModes.Adaptive)
416 tempBuffer = Marshal.AllocHGlobal(5 * width * pixelSize);
420 tempBuffer = IntPtr.Zero;
423 int stride = width * pixelSize;
425 byte* filteredPointer = (
byte*)filteredMemory;
427 byte* tempPointer = (
byte*)tempBuffer;
429 for (
int y = 0; y < height; y++)
431 FilterRow(image, width, pixelSize, stride, y, filter, filteredPointer, tempPointer);
434 Marshal.FreeHGlobal(tempBuffer);
436 return filteredMemory;
439 internal unsafe
static IntPtr FilterImageData(
byte* image,
int width,
int height,
int pixelSize, FilterModes filter,
int threadCount)
441 threadCount = Math.Min(threadCount, height);
443 IntPtr filteredMemory = Marshal.AllocHGlobal(height * (1 + width * pixelSize));
445 int stride = width * pixelSize;
447 byte* filteredPointer = (
byte*)filteredMemory;
449 IntPtr[] tempBuffers =
new IntPtr[threadCount];
451 Thread[] threads =
new Thread[threadCount];
452 int[] rowsByThread =
new int[threadCount];
453 EventWaitHandle[] signalsFromThreads =
new EventWaitHandle[threadCount];
454 EventWaitHandle[] signalsToThreads =
new EventWaitHandle[threadCount];
456 int firstNeededRow = threadCount;
458 for (
int i = 0; i < threadCount; i++)
461 signalsFromThreads[i] =
new EventWaitHandle(
false, EventResetMode.ManualReset);
462 signalsToThreads[i] =
new EventWaitHandle(
false, EventResetMode.ManualReset);
464 if (filter == FilterModes.Adaptive)
466 tempBuffers[i] = Marshal.AllocHGlobal(5 * width * pixelSize);
470 tempBuffers[i] = IntPtr.Zero;
473 byte* tempPointer = (
byte*)tempBuffers[i];
477 threads[i] =
new Thread(() =>
479 while (rowsByThread[threadIndex] >= 0)
481 FilterRow(image, width, pixelSize, stride, rowsByThread[threadIndex], filter, filteredPointer, tempPointer);
482 EventWaitHandle.SignalAndWait(signalsFromThreads[threadIndex], signalsToThreads[threadIndex]);
483 signalsToThreads[threadIndex].Reset();
490 for (
int i = 0; i < threadCount; i++)
495 while (finished < threadCount)
497 int threadInd = EventWaitHandle.WaitAny(signalsFromThreads);
499 signalsFromThreads[threadInd].Reset();
501 if (firstNeededRow < height)
503 rowsByThread[threadInd] = firstNeededRow;
505 signalsToThreads[threadInd].Set();
509 rowsByThread[threadInd] = -1;
510 signalsToThreads[threadInd].Set();
515 for (
int i = 0; i < threadCount; i++)
517 if (filter == FilterModes.Adaptive)
519 Marshal.FreeHGlobal(tempBuffers[i]);
523 return filteredMemory;
527 internal static byte PaethPredictor(
byte a,
byte b,
byte c)
529 int p = (int)a + (
int)b - (int)c;
530 int pa = Math.Abs(p - (
int)a);
531 int pb = Math.Abs(p - (
int)b);
532 int pc = Math.Abs(p - (
int)c);
534 if (pa <= pb && pa <= pc)
550 internal static class CRC32
553 private static readonly uint[] crc_table =
new uint[256];
556 private static bool crc_table_computed =
false;
559 private static void MakeCRCtable()
564 for (n = 0; n < 256; n++)
567 for (k = 0; k < 8; k++)
571 c = 0xedb88320 ^ (c >> 1);
580 crc_table_computed =
true;
588 private static uint UpdateCRC(uint crc, Stream buf)
593 if (!crc_table_computed)
598 buf.Seek(0, SeekOrigin.Begin);
600 for (n = 0; n < buf.Length; n++)
602 c = crc_table[(c ^ (byte)buf.ReadByte()) & 0xff] ^ (c >> 8);
607 private static uint UpdateCRC(uint crc,
byte[] buf,
int length)
612 if (!crc_table_computed)
617 for (n = 0; n < length; n++)
619 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
625 public static uint ComputeCRC(Stream buf)
627 return UpdateCRC(0xffffffff, buf) ^ 0xffffffff;
630 public static uint ComputeCRC(
byte[] buf,
int length,
byte[] prefix)
632 uint temp = UpdateCRC(0xffffffff, prefix, prefix.Length);
633 return UpdateCRC(temp, buf, length) ^ 0xffffffff;
637 internal static class StreamUtils
639 public static void WriteASCIIString(
this Stream sr,
string val)
641 foreach (
char c
in val)
643 sr.WriteByte((
byte)c);
647 internal static MemoryStream ZLibCompress(IntPtr memoryAddress,
int memoryLength)
649 MemoryStream compressedStream =
new MemoryStream();
650 compressedStream.Write(
new byte[] { 0x78, 0x01 }, 0, 2);
652 byte[] buffer =
new byte[1024];
654 AdlerHolder adlerHolder =
new AdlerHolder();
656 using (DeflateStream deflate =
new DeflateStream(compressedStream, CompressionLevel.Optimal,
true))
658 while (memoryLength > 0)
660 int currentBytes = Math.Min(memoryLength, 1024);
661 Marshal.Copy(memoryAddress, buffer, 0, currentBytes);
662 deflate.Write(buffer, 0, currentBytes);
663 memoryLength -= currentBytes;
664 memoryAddress = IntPtr.Add(memoryAddress, currentBytes);
665 Adler32(buffer, currentBytes, adlerHolder);
670 uint checksum = (adlerHolder.s2 << 16) + adlerHolder.s1;
672 compressedStream.Write(
new byte[] { (
byte)((checksum >> 24) & 255), (byte)((checksum >> 16) & 255), (
byte)((checksum >> 8) & 255), (byte)(checksum & 255) }, 0, 4);
674 compressedStream.Seek(0, SeekOrigin.Begin);
676 return compressedStream;
679 internal static uint Adler32(Stream contentStream)
686 while ((readByte = contentStream.ReadByte()) >= 0)
688 s1 = (s1 + (byte)readByte) % 65521U;
689 s2 = (s2 + s1) % 65521U;
692 return (s2 << 16) + s1;
696 internal class AdlerHolder
708 internal static void Adler32(
byte[] buffer,
int length, AdlerHolder holder)
710 for (
int i = 0; i < length; i++)
712 holder.s1 = (holder.s1 + buffer[i]) % 65521U;
713 holder.s2 = (holder.s2 + holder.s1) % 65521U;