Created
September 20, 2019 18:20
-
-
Save equinox2k/5b7f593c6442a5c81d9c570c04dce5d5 to your computer and use it in GitHub Desktop.
PNG Optimization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using SixLabors.ImageSharp; | |
| using System; | |
| using System.IO; | |
| using System.Linq; | |
| using SixLabors.ImageSharp.Formats.Png; | |
| using SixLabors.ImageSharp.PixelFormats; | |
| using SixLabors.ImageSharp.Advanced; | |
| namespace TestCatalog | |
| { | |
| public class PNGOptimizer | |
| { | |
| private byte[] _imageData; | |
| internal PNGOptimizer(byte[] imageData) | |
| { | |
| _imageData = imageData; | |
| } | |
| private static uint ReverseBytes(uint value) | |
| { | |
| return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 | (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24; | |
| } | |
| private static byte[] RemoveChunks(Stream inputStream) | |
| { | |
| var IHDR = new byte[] { (byte)'I', (byte)'H', (byte)'D', (byte)'R' }; | |
| var PLTE = new byte[] { (byte)'P', (byte)'L', (byte)'T', (byte)'E' }; | |
| var IEND = new byte[] { (byte)'I', (byte)'E', (byte)'N', (byte)'D' }; | |
| var tRNS = new byte[] { (byte)'t', (byte)'R', (byte)'N', (byte)'S' }; | |
| var IDAT = new byte[] { (byte)'I', (byte)'D', (byte)'A', (byte)'T' }; | |
| using (var outputStream = new MemoryStream()) | |
| { | |
| using (var binaryReader = new BinaryReader(inputStream)) | |
| { | |
| using (var binaryWriter = new BinaryWriter(outputStream)) | |
| { | |
| var header = binaryReader.ReadBytes(8); | |
| var expectedHeader = new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }; | |
| if (!header.SequenceEqual(expectedHeader)) | |
| { | |
| throw new Exception("Unexpected header"); | |
| } | |
| binaryWriter.Write(header); | |
| while (true) | |
| { | |
| var length = ReverseBytes(binaryReader.ReadUInt32()); | |
| var type = binaryReader.ReadBytes(4); | |
| if (!type.SequenceEqual(IHDR) && !type.SequenceEqual(PLTE) && !type.SequenceEqual(IEND) && !type.SequenceEqual(tRNS) && !type.SequenceEqual(IDAT)) | |
| { | |
| var saved = length + 4; | |
| binaryReader.BaseStream.Position = binaryReader.BaseStream.Position + saved; | |
| } | |
| else | |
| { | |
| binaryWriter.Write(ReverseBytes(length)); | |
| binaryWriter.Write(type); | |
| binaryWriter.Write(binaryReader.ReadBytes((int)length + 4)); | |
| if (type.SequenceEqual(IEND)) | |
| { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return outputStream.ToArray(); | |
| } | |
| } | |
| public static PNGOptimizer Load(string filePath) | |
| { | |
| using (var fileStream = File.OpenRead(filePath)) | |
| { | |
| return Load(fileStream); | |
| } | |
| } | |
| public static PNGOptimizer Load(Stream inputStream) | |
| { | |
| using (var image = Image.Load<Rgba32>(inputStream)) | |
| { | |
| var span = image.GetPixelSpan(); | |
| for (int i = 0; i < span.Length; i++) | |
| { | |
| Rgba32 rgba32 = default; | |
| span[i].ToRgba32(ref rgba32); | |
| if (rgba32.A == 0) | |
| { | |
| rgba32.R = 0; | |
| rgba32.G = 0; | |
| rgba32.B = 0; | |
| } | |
| span[i].FromRgba32(rgba32); | |
| } | |
| using (var processStream = new MemoryStream()) | |
| { | |
| var pngEncoder = new PngEncoder | |
| { | |
| ColorType = PngColorType.Palette, | |
| BitDepth = PngBitDepth.Bit8, | |
| FilterMethod = PngFilterMethod.Adaptive, | |
| InterlaceMethod = PngInterlaceMode.None, | |
| CompressionLevel = 9 | |
| }; | |
| image.Save(processStream, pngEncoder); | |
| processStream.Position = 0; | |
| return new PNGOptimizer(RemoveChunks(processStream)); | |
| } | |
| } | |
| } | |
| public void Save(string filePath) | |
| { | |
| using (var fileStream = File.Create(filePath)) | |
| { | |
| Save(fileStream); | |
| } | |
| } | |
| public void Save(Stream fileStream) | |
| { | |
| using (var binaryWriter = new BinaryWriter(fileStream)) | |
| { | |
| binaryWriter.Write(_imageData); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment