Skip to content

Instantly share code, notes, and snippets.

@equinox2k
Last active September 9, 2019 20:46
Show Gist options
  • Save equinox2k/209ba9e0c64cd584942d28853ba40541 to your computer and use it in GitHub Desktop.
Save equinox2k/209ba9e0c64cd584942d28853ba40541 to your computer and use it in GitHub Desktop.
Ico Image Encoder
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