Last active
September 9, 2019 20:46
-
-
Save equinox2k/209ba9e0c64cd584942d28853ba40541 to your computer and use it in GitHub Desktop.
Ico Image Encoder
This file contains 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 System; | |
using System.Linq; | |
using System.IO; | |
using SixLabors.ImageSharp; | |
using SixLabors.ImageSharp.Processing; | |
using SixLabors.ImageSharp.PixelFormats; | |
using SixLabors.ImageSharp.Advanced; | |
namespace EQL.IconAutosizer.ImageEncoders | |
{ | |
// https://en.wikipedia.org/wiki/ICO_%28file_format%29 | |
// https://fileformats.fandom.com/wiki/Icon | |
public static class IcoEncoder | |
{ | |
private static void ResizeImageToPngBinaryWriter(BinaryWriter binaryWriter, Image<Bgra32> image, int size, out int imageSize) | |
{ | |
using (var sizedImage = image.Clone(i => i.Resize(size, size))) | |
{ | |
var position = binaryWriter.BaseStream.Position; | |
sizedImage.SaveAsPng(binaryWriter.BaseStream); | |
imageSize = (int)(binaryWriter.BaseStream.Position - position); | |
} | |
} | |
private static void ResizeImageToBmpBinaryWriter(BinaryWriter binaryWriter, Image<Bgra32> image, int size, out int imageSize) | |
{ | |
using (var sizedImage = image.Clone(i => i.Resize(size, size).Flip(FlipMode.Vertical))) | |
{ | |
var position = binaryWriter.BaseStream.Position; | |
var pixelData = sizedImage.GetPixelSpan().ToArray(); | |
binaryWriter.Write((uint)40); | |
binaryWriter.Write(size); | |
binaryWriter.Write(size * 2); | |
binaryWriter.Write((ushort)1); | |
binaryWriter.Write((ushort)32); | |
binaryWriter.Write((uint)0); | |
binaryWriter.Write((uint)((size * size) * 5)); | |
binaryWriter.Write(0); | |
binaryWriter.Write(0); | |
binaryWriter.Write((uint)0); | |
binaryWriter.Write((uint)0); | |
foreach (var pixel in pixelData) | |
{ | |
binaryWriter.Write(pixel.B); | |
binaryWriter.Write(pixel.G); | |
binaryWriter.Write(pixel.R); | |
binaryWriter.Write(pixel.A); | |
} | |
binaryWriter.Write(new byte[size * size]); | |
imageSize = (int)(binaryWriter.BaseStream.Position - position); | |
} | |
} | |
public static void Encode(string filePath, Image<Bgra32> image) | |
{ | |
using (var fileStream = File.Create(filePath)) | |
{ | |
Encode(fileStream, image); | |
} | |
} | |
private static int[] GetIconSizes() | |
{ | |
return new int[] { 256, 48, 32, 16 }; | |
} | |
public static void Encode(Stream stream, Image<Bgra32> image) | |
{ | |
if (!stream.CanSeek) | |
{ | |
throw new Exception("Seekable stream is required"); | |
} | |
if (image.Width != image.Height || image.Width < 16) | |
{ | |
throw new Exception("Image should be square and at least 16px in size"); | |
} | |
var iconSizes = GetIconSizes().Where(i => i < image.Width).ToArray(); | |
using (var binaryWriter = new BinaryWriter(stream)) | |
{ | |
// Write IconDir | |
binaryWriter.Write((ushort)0); | |
binaryWriter.Write((ushort)1); | |
binaryWriter.Write((ushort)iconSizes.Length); | |
const int iconDirEntrySize = 16; | |
// Write IconDirEntries | |
foreach (var iconSize in iconSizes) | |
{ | |
binaryWriter.Write((byte)(iconSize == 256 ? 0 : iconSize)); | |
binaryWriter.Write((byte)(iconSize == 256 ? 0 : iconSize)); | |
binaryWriter.Write((byte)0); | |
binaryWriter.Write((byte)0); | |
binaryWriter.Write((ushort)0); | |
binaryWriter.Write((ushort)32); | |
binaryWriter.Write((uint)0); | |
binaryWriter.Write((uint)0); | |
} | |
var imageSize = 0; | |
// Write image data and update IconDirEntries | |
var imageDataOffset = 6 + (iconSizes.Length * iconDirEntrySize); | |
for (var i = 0; i < iconSizes.Length; i++) | |
{ | |
stream.Seek(0, SeekOrigin.End); | |
var iconSize = iconSizes[i]; | |
switch (iconSize) | |
{ | |
case 256: | |
ResizeImageToPngBinaryWriter(binaryWriter, image, iconSize, out imageSize); | |
break; | |
default: | |
ResizeImageToBmpBinaryWriter(binaryWriter, image, iconSize, out imageSize); | |
break; | |
} | |
stream.Position = 6 + (i * iconDirEntrySize) + 8; | |
binaryWriter.Write((uint)imageSize); | |
binaryWriter.Write((uint)imageDataOffset); | |
imageDataOffset += imageSize; | |
} | |
binaryWriter.Flush(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment