Advertisement
Bunny83

PNGTools.cs

Nov 30th, 2017 (edited)
1,072
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.51 KB | None | 0 0
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using UnityEngine;
  4.  
  5. namespace B83.Image
  6. {
  7.     public enum EChunkType : uint
  8.     {
  9.         IHDR = 0x49484452,
  10.         sRBG = 0x73524742,
  11.         gAMA = 0x67414D41,
  12.         cHRM = 0x6348524D,
  13.         pHYs = 0x70485973,
  14.         iCCP = 0x69434350,
  15.         IDAT = 0x49444154,
  16.         IEND = 0x49454E44,
  17.     }
  18.  
  19.     public class PNGFile
  20.     {
  21.         public static ulong m_Signature = 0x89504E470D0A1A0AU; //
  22.         public ulong Signature;
  23.         public List<PNGChunk> chunks;
  24.         public int FindChunk(EChunkType aType, int aStartIndex = 0)
  25.         {
  26.             if (chunks == null)
  27.                 return -1;
  28.             for(int i = aStartIndex; i < chunks.Count; i++)
  29.             {
  30.                 if (chunks[i].type == aType)
  31.                     return i;
  32.             }
  33.             return -1;
  34.         }
  35.     }
  36.  
  37.     public class PNGChunk
  38.     {
  39.         public uint length;
  40.         public EChunkType type;
  41.         public byte[] data;
  42.         public uint crc;
  43.         public uint CalcCRC()
  44.         {
  45.             var crc = PNGTools.UpdateCRC(0xFFFFFFFF, (uint)type);
  46.             crc = PNGTools.UpdateCRC(crc, data);
  47.             return crc ^ 0xFFFFFFFF;
  48.         }
  49.     }
  50.  
  51.     public class PNGTools
  52.     {
  53.         static uint[] crc_table = new uint[256];
  54.         static PNGTools()
  55.         {
  56.             for (int n = 0; n < 256; n++)
  57.             {
  58.                 uint c = (uint)n;
  59.                 for (int k = 0; k < 8; k++)
  60.                 {
  61.                     if ((c & 1) > 0)
  62.                         c = 0xedb88320 ^ (c >> 1);
  63.                     else
  64.                         c = c >> 1;
  65.                 }
  66.                 crc_table[n] = c;
  67.             }
  68.         }
  69.  
  70.         public static uint UpdateCRC(uint crc, byte aData)
  71.         {
  72.             return crc_table[(crc ^ aData) & 0xff] ^ (crc >> 8);
  73.         }
  74.  
  75.         public static uint UpdateCRC(uint crc, uint aData)
  76.         {
  77.             crc = crc_table[(crc ^ ((aData >> 24) & 0xFF)) & 0xff] ^ (crc >> 8);
  78.             crc = crc_table[(crc ^ ((aData >> 16) & 0xFF)) & 0xff] ^ (crc >> 8);
  79.             crc = crc_table[(crc ^ ((aData >> 8) & 0xFF)) & 0xff] ^ (crc >> 8);
  80.             crc = crc_table[(crc ^ (aData & 0xFF)) & 0xff] ^ (crc >> 8);
  81.             return crc;
  82.         }
  83.  
  84.  
  85.         public static uint UpdateCRC(uint crc, byte[] buf)
  86.         {
  87.             for (int n = 0; n < buf.Length; n++)
  88.                 crc = crc_table[(crc ^ buf[n]) & 0xff] ^ (crc >> 8);
  89.             return crc;
  90.         }
  91.  
  92.         public static uint CalculateCRC(byte[] aBuf)
  93.         {
  94.             return UpdateCRC(0xffffffff, aBuf) ^ 0xffffffff;
  95.         }
  96.         public static List<PNGChunk> ReadChunks(BinaryReader aReader)
  97.         {
  98.             var res = new List<PNGChunk>();
  99.             while (aReader.BaseStream.Position < aReader.BaseStream.Length - 4)
  100.             {
  101.                 var chunk = new PNGChunk();
  102.                 chunk.length = aReader.ReadUInt32BE();
  103.                 if (aReader.BaseStream.Position >= aReader.BaseStream.Length - 4 - chunk.length)
  104.                     break;
  105.                 res.Add(chunk);
  106.                 chunk.type = (EChunkType)aReader.ReadUInt32BE();
  107.                 chunk.data = aReader.ReadBytes((int)chunk.length);
  108.                 chunk.crc = aReader.ReadUInt32BE();
  109.  
  110.                 uint crc = chunk.CalcCRC();
  111.  
  112.                 if ((chunk.crc ^ crc) != 0)
  113.                     Debug.Log("Chunk CRC wrong. Got 0x" + chunk.crc.ToString("X8") + " expected 0x" + crc.ToString("X8"));
  114.                 if (chunk.type == EChunkType.IEND)
  115.                     break;
  116.             }
  117.             return res;
  118.         }
  119.  
  120.         public static PNGFile ReadPNGFile(BinaryReader aReader)
  121.         {
  122.             if (aReader == null || aReader.BaseStream.Position >= aReader.BaseStream.Length - 8)
  123.                 return null;
  124.             var file = new PNGFile();
  125.             file.Signature = aReader.ReadUInt64BE();
  126.             file.chunks = ReadChunks(aReader);
  127.             return file;
  128.         }
  129.         public static void WritePNGFile(PNGFile aFile, BinaryWriter aWriter)
  130.         {
  131.             aWriter.WriteUInt64BE(PNGFile.m_Signature);
  132.             foreach (var chunk in aFile.chunks)
  133.             {
  134.                 aWriter.WriteUInt32BE((uint)chunk.data.Length);
  135.                 aWriter.WriteUInt32BE((uint)chunk.type);
  136.                 aWriter.Write(chunk.data);
  137.                 aWriter.WriteUInt32BE(chunk.crc);
  138.             }
  139.         }
  140.  
  141.         public static void SetPPM(PNGFile aFile, uint aXPPM, uint aYPPM)
  142.         {
  143.             int pos = aFile.FindChunk(EChunkType.pHYs);
  144.             PNGChunk chunk;
  145.             if (pos > 0)
  146.             {
  147.                 chunk = aFile.chunks[pos];
  148.                 if (chunk.data == null || chunk.data.Length < 9)
  149.                     throw new System.Exception("PNG: pHYs chunk data size is too small. It should be at least 9 bytes");
  150.             }
  151.             else
  152.             {
  153.                 chunk = new PNGChunk();
  154.                 chunk.type = EChunkType.pHYs;
  155.                 chunk.length = 9;
  156.                 chunk.data = new byte[9];
  157.                 aFile.chunks.Insert(1, chunk);
  158.             }
  159.             var data = chunk.data;
  160.             data[0] = (byte)((aXPPM >> 24) & 0xFF);
  161.             data[1] = (byte)((aXPPM >> 16) & 0xFF);
  162.             data[2] = (byte)((aXPPM >> 8) & 0xFF);
  163.             data[3] = (byte)((aXPPM) & 0xFF);
  164.  
  165.             data[4] = (byte)((aYPPM >> 24) & 0xFF);
  166.             data[5] = (byte)((aYPPM >> 16) & 0xFF);
  167.             data[6] = (byte)((aYPPM >> 8) & 0xFF);
  168.             data[7] = (byte)((aYPPM) & 0xFF);
  169.  
  170.             data[8] = 1;
  171.             chunk.crc = chunk.CalcCRC();
  172.         }
  173.  
  174.         public static byte[] ChangePPM(byte[] aPNGData, uint aXPPM, uint aYPPM)
  175.         {
  176.             PNGFile file;
  177.             using (var stream = new MemoryStream(aPNGData))
  178.             using (var reader = new BinaryReader(stream))
  179.             {
  180.                 file = ReadPNGFile(reader);
  181.             }
  182.             SetPPM(file, aXPPM, aYPPM);
  183.             using (var stream = new MemoryStream())
  184.             using (var writer = new BinaryWriter(stream))
  185.             {
  186.                 WritePNGFile(file, writer);
  187.                 return stream.ToArray();
  188.             }
  189.         }
  190.         public static byte[] ChangePPI(byte[] aPNGData, float aXPPI, float aYPPI)
  191.         {
  192.             return ChangePPM(aPNGData, (uint)(aXPPI * 39.3701f), (uint)(aYPPI * 39.3701f));
  193.         }
  194.     }
  195.  
  196.     public static class BinaryReaderWriterExt
  197.     {
  198.         public static uint ReadUInt32BE(this BinaryReader aReader)
  199.         {
  200.             return ((uint)aReader.ReadByte() << 24) | ((uint)aReader.ReadByte() << 16)
  201.                 | ((uint)aReader.ReadByte() << 8) | ((uint)aReader.ReadByte());
  202.         }
  203.         public static ulong ReadUInt64BE(this BinaryReader aReader)
  204.         {
  205.             return (ulong)aReader.ReadUInt32BE()<<32 | aReader.ReadUInt32BE();
  206.         }
  207.         public static void WriteUInt32BE(this BinaryWriter aWriter, uint aValue)
  208.         {
  209.             aWriter.Write((byte)((aValue >> 24) & 0xFF));
  210.             aWriter.Write((byte)((aValue >> 16) & 0xFF));
  211.             aWriter.Write((byte)((aValue >> 8 ) & 0xFF));
  212.             aWriter.Write((byte)((aValue      ) & 0xFF));
  213.         }
  214.         public static void WriteUInt64BE(this BinaryWriter aWriter, ulong aValue)
  215.         {
  216.             aWriter.WriteUInt32BE((uint)(aValue >> 32));
  217.             aWriter.WriteUInt32BE((uint)(aValue));
  218.         }
  219.     }
  220. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement