File: WindowsIconGenerator.cs
Web Access
Project: src\src\SingleProject\Resizetizer\src\Resizetizer.csproj (Microsoft.Maui.Resizetizer)
using System;
using System.IO;
using SkiaSharp;
 
namespace Microsoft.Maui.Resizetizer
{
	/// <summary>
	/// Generates a .ico file for the image.
	/// </summary>
	internal class WindowsIconGenerator
	{
		public WindowsIconGenerator(ResizeImageInfo info, string intermediateOutputPath, ILogger logger)
		{
			Info = info;
			Logger = logger;
			IntermediateOutputPath = intermediateOutputPath;
		}
 
		public ResizeImageInfo Info { get; private set; }
		public string IntermediateOutputPath { get; private set; }
		public ILogger Logger { get; private set; }
 
		public ResizedImageInfo Generate()
		{
			string destinationFolder = IntermediateOutputPath;
 
			string fileName = Path.GetFileNameWithoutExtension(Info.OutputName);
			string destination = Path.Combine(destinationFolder, $"{fileName}.ico");
			Directory.CreateDirectory(destinationFolder);
 
			var (sourceExists, sourceModified) = Utils.FileExists(Info.Filename);
			var (destinationExists, destinationModified) = Utils.FileExists(destination);
 
			Logger.Log($"Generating ICO: {destination}");
 
			var tools = new SkiaSharpAppIconTools(Info, Logger);
			var dpi = new DpiPath(fileName, 1.0m, size: new SKSize(64, 64));
 
			if (destinationModified > sourceModified)
			{
				Logger.Log($"Skipping `{Info.Filename}` => `{destination}` file is up to date.");
				return new ResizedImageInfo { Dpi = dpi, Filename = destination };
			}
 
			MemoryStream memoryStream = new MemoryStream();
			tools.Resize(dpi, destination, () => memoryStream);
			memoryStream.Position = 0;
 
			int numberOfImages = 1;
			using BinaryWriter writer = new BinaryWriter(File.Create(destination));
			writer.Write((short)0x0); // Reserved. Must always be 0.
			writer.Write((short)0x1); // Specifies image type: 1 for icon (.ICO) image
			writer.Write((short)numberOfImages); // Specifies number of images in the file.
 
			writer.Write((byte)dpi.Size.Value.Width);
			writer.Write((byte)dpi.Size.Value.Height);
			writer.Write((byte)0x0); // Specifies number of colors in the color palette
			writer.Write((byte)0x0); // Reserved. Should be 0
			writer.Write((short)0x1); // Specifies color planes. Should be 0 or 1
			writer.Write((short)0x8); // Specifies bits per pixel.
			writer.Write((int)memoryStream.Length); // Specifies the size of the image's data in bytes
 
			int offset = 6 + (16 * numberOfImages); // + length of previous images
			writer.Write(offset); // Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
 
			// write png data for each image
			memoryStream.CopyTo(writer.BaseStream);
			writer.Flush();
 
			return new ResizedImageInfo { Dpi = dpi, Filename = destination };
		}
	}
}