Skip to content

Instantly share code, notes, and snippets.

@darkfall
Created January 22, 2012 07:15
Show Gist options
  • Save darkfall/1656050 to your computer and use it in GitHub Desktop.
Save darkfall/1656050 to your computer and use it in GitHub Desktop.
A simple class that converts a image to a icon in c# without losing image color data, unlike System.Drawing.Icon; ico with png data requires Windows Vista or above
class PngIconConverter
{
/* input image with width = height is suggested to get the best result */
/* png support in icon was introduced in Windows Vista */
public static bool Convert(System.IO.Stream input_stream, System.IO.Stream output_stream, int size, bool keep_aspect_ratio = false)
{
System.Drawing.Bitmap input_bit = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(input_stream);
if (input_bit != null)
{
int width, height;
if (keep_aspect_ratio)
{
width = size;
height = input_bit.Height / input_bit.Width * size;
}
else
{
width = height = size;
}
System.Drawing.Bitmap new_bit = new System.Drawing.Bitmap(input_bit, new System.Drawing.Size(width, height));
if (new_bit != null)
{
// save the resized png into a memory stream for future use
System.IO.MemoryStream mem_data = new System.IO.MemoryStream();
new_bit.Save(mem_data, System.Drawing.Imaging.ImageFormat.Png);
System.IO.BinaryWriter icon_writer = new System.IO.BinaryWriter(output_stream);
if (output_stream != null && icon_writer != null)
{
// 0-1 reserved, 0
icon_writer.Write((byte)0);
icon_writer.Write((byte)0);
// 2-3 image type, 1 = icon, 2 = cursor
icon_writer.Write((short)1);
// 4-5 number of images
icon_writer.Write((short)1);
// image entry 1
// 0 image width
icon_writer.Write((byte)width);
// 1 image height
icon_writer.Write((byte)height);
// 2 number of colors
icon_writer.Write((byte)0);
// 3 reserved
icon_writer.Write((byte)0);
// 4-5 color planes
icon_writer.Write((short)0);
// 6-7 bits per pixel
icon_writer.Write((short)32);
// 8-11 size of image data
icon_writer.Write((int)mem_data.Length);
// 12-15 offset of image data
icon_writer.Write((int)(6 + 16));
// write image data
// png data must contain the whole png data file
icon_writer.Write(mem_data.ToArray());
icon_writer.Flush();
return true;
}
}
return false;
}
return false;
}
public static bool Convert(string input_image, string output_icon, int size, bool keep_aspect_ratio = false)
{
System.IO.FileStream input_stream = new System.IO.FileStream(input_image, System.IO.FileMode.Open);
System.IO.FileStream output_stream = new System.IO.FileStream(output_icon, System.IO.FileMode.OpenOrCreate);
bool result = Convert(input_stream, output_stream, size, keep_aspect_ratio);
input_stream.Close();
output_stream.Close();
return result;
}
}
@prepare
Copy link

prepare commented May 20, 2019

Hello @darkfall, @mdiller
Could you provide a license for your source code?

@darkfall
Copy link
Author

Hello @darkfall, @mdiller
Could you provide a license for your source code?

CC-BY-SA. Feel free to use it

@prepare
Copy link

prepare commented May 20, 2019

Thank you 👍

@mdiller
Copy link

mdiller commented May 20, 2019

Hello @darkfall, @mdiller
Could you provide a license for your source code?

As darkfall said, CC-BY-SA. Feel free to use my modifications!

@zzxxhhzxh
Copy link

This was super useful to me. I modified it to create multiple images in one ico of varying sizes.

/// <summary>
/// Adapted from this gist: https://gist.github.com/darkfall/1656050
/// Provides helper methods for imaging
/// </summary>
public static class ImagingHelper
{
    /// <summary>
    /// Converts a PNG image to a icon (ico) with all the sizes windows likes
    /// </summary>
    /// <param name="inputBitmap">The input bitmap</param>
    /// <param name="output">The output stream</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Bitmap inputBitmap, Stream output)
    {
        if (inputBitmap == null)
            return false;

        int[] sizes = new int[] { 256, 48, 32, 16 };

        // Generate bitmaps for all the sizes and toss them in streams
        List<MemoryStream> imageStreams = new List<MemoryStream>();
        foreach (int size in sizes)
        {
            Bitmap newBitmap = ResizeImage(inputBitmap, size, size);
            if (newBitmap == null)
                return false;
            MemoryStream memoryStream = new MemoryStream();
            newBitmap.Save(memoryStream, ImageFormat.Png);
            imageStreams.Add(memoryStream);
        }

        BinaryWriter iconWriter = new BinaryWriter(output);
        if (output == null || iconWriter == null)
            return false;

        int offset = 0;

        // 0-1 reserved, 0
        iconWriter.Write((byte)0);
        iconWriter.Write((byte)0);

        // 2-3 image type, 1 = icon, 2 = cursor
        iconWriter.Write((short)1);

        // 4-5 number of images
        iconWriter.Write((short)sizes.Length);

        offset += 6 + (16 * sizes.Length);

        for (int i = 0; i < sizes.Length; i++)
        {
            // image entry 1
            // 0 image width
            iconWriter.Write((byte)sizes[i]);
            // 1 image height
            iconWriter.Write((byte)sizes[i]);

            // 2 number of colors
            iconWriter.Write((byte)0);

            // 3 reserved
            iconWriter.Write((byte)0);

            // 4-5 color planes
            iconWriter.Write((short)0);

            // 6-7 bits per pixel
            iconWriter.Write((short)32);

            // 8-11 size of image data
            iconWriter.Write((int)imageStreams[i].Length);

            // 12-15 offset of image data
            iconWriter.Write((int)offset);

            offset += (int)imageStreams[i].Length;
        }

        for (int i = 0; i < sizes.Length; i++)
        {
            // write image data
            // png data must contain the whole png data file
            iconWriter.Write(imageStreams[i].ToArray());
            imageStreams[i].Close();
        }

        iconWriter.Flush();

        return true;
    }

    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="input">The input stream</param>
    /// <param name="output">The output stream</param
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Stream input, Stream output)
    {
        Bitmap inputBitmap = (Bitmap)Bitmap.FromStream(input);
        return ConvertToIcon(inputBitmap, output);
    }

    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="inputPath">The input path</param>
    /// <param name="outputPath">The output path</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(string inputPath, string outputPath)
    {
        using (FileStream inputStream = new FileStream(inputPath, FileMode.Open))
        using (FileStream outputStream = new FileStream(outputPath, FileMode.OpenOrCreate))
        {
            return ConvertToIcon(inputStream, outputStream);
        }
    }



    /// <summary>
    /// Converts an image to a icon (ico)
    /// </summary>
    /// <param name="inputImage">The input image</param>
    /// <param name="outputPath">The output path</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Image inputImage, string outputPath)
    {
        using (FileStream outputStream = new FileStream(outputPath, FileMode.OpenOrCreate))
        {
            return ConvertToIcon(new Bitmap(inputImage), outputStream);
        }
    }


    /// <summary>
    /// Resize the image to the specified width and height.
    /// Found on stackoverflow: https://stackoverflow.com/questions/1922040/resize-an-image-c-sharp
    /// </summary>
    /// <param name="image">The image to resize.</param>
    /// <param name="width">The width to resize to.</param>
    /// <param name="height">The height to resize to.</param>
    /// <returns>The resized image.</returns>
    public static Bitmap ResizeImage(Image image, int width, int height)
    {
        var destRect = new Rectangle(0, 0, width, height);
        var destImage = new Bitmap(width, height);

        destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }

        return destImage;
    }
}

I use your code to create a icon including some sizes like 3232, 4848, 256*256. But if I use var bmp = (Bitmap)Image.FromFile(iconFilePath).Clone(), it failed, shows"GDI+ error". So how to open the icon file correctly?

@jamesbrindle
Copy link

This is absolutely perfect!

@lextm
Copy link

lextm commented May 6, 2020

Converted the logic to PowerShell,

function ConvertTo-Icon
{
    <#
    .Synopsis
        Converts image to icons
    .Description
        Converts an image to an icon
    .Example
        ConvertTo-Icon -File .\Logo.png -OutputFile .\Favicon.ico
    #>
    [CmdletBinding()]
    param(
    # The file
    [Parameter(Mandatory=$true, Position=0,ValueFromPipelineByPropertyName=$true)]
    [Alias('Fullname')]
    [string]$File,
   
    # If provided, will output the icon to a location
    [Parameter(Position=1, ValueFromPipelineByPropertyName=$true)]
    [string]$OutputFile
    )
    
    begin {
        Add-Type -AssemblyName System.Windows.Forms, System.Drawing   
    }
    
    process {
        #region Load Icon
        $resolvedFile = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($file)
        if (-not $resolvedFile) { return }
        $inputBitmap = [Drawing.Image]::FromFile($resolvedFile)
        $width = $inputBitmap.Width
        $height = $inputBitmap.Height
        $size = New-Object Drawing.Size $width, $height
        $newBitmap = New-Object Drawing.Bitmap $inputBitmap, $size
        #endregion Load Icon

        #region Save Icon                     
        $memoryStream = New-Object System.IO.MemoryStream
        $newBitmap.Save($memoryStream, [System.Drawing.Imaging.ImageFormat]::Png)

        $resolvedOutputFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($outputFile)
        $output = [IO.File]::Create("$resolvedOutputFile")
        
        $iconWriter = New-Object System.IO.BinaryWriter($output)
        # 0-1 reserved, 0
        $iconWriter.Write([byte]0)
        $iconWriter.Write([byte]0)

        # 2-3 image type, 1 = icon, 2 = cursor
        $iconWriter.Write([short]1);

        # 4-5 number of images
        $iconWriter.Write([short]1);

        # image entry 1
        # 0 image width
        $iconWriter.Write([byte]$width);
        # 1 image height
        $iconWriter.Write([byte]$height);

        # 2 number of colors
        $iconWriter.Write([byte]0);

        # 3 reserved
        $iconWriter.Write([byte]0);

        # 4-5 color planes
        $iconWriter.Write([short]0);

        # 6-7 bits per pixel
        $iconWriter.Write([short]32);

        # 8-11 size of image data
        $iconWriter.Write([int]$memoryStream.Length);

        # 12-15 offset of image data
        $iconWriter.Write([int](6 + 16));

        # write image data
        # png data must contain the whole png data file
        $iconWriter.Write($memoryStream.ToArray());

        $iconWriter.Flush();
        $output.Close()               
        #endregion Save Icon

        #region Cleanup
        $memoryStream.Dispose()
        $newBitmap.Dispose()
        $inputBitmap.Dispose()
        #endregion Cleanup
    }
}

@SuLign
Copy link

SuLign commented May 24, 2020

/// <summary>
/// Provides helper methods for imaging
/// </summary>
public static class ImagingHelper
{
    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="input">The input stream</param>
    /// <param name="output">The output stream</param>
    /// <param name="size">The size (16x16 px by default)</param>
    /// <param name="preserveAspectRatio">Preserve the aspect ratio</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Stream input, Stream output, int size = 16, bool preserveAspectRatio = false)
    {
        Bitmap inputBitmap = (Bitmap)Bitmap.FromStream(input);
        if (inputBitmap != null)
        {
            int width, height;
            if (preserveAspectRatio)
            {
                width = size;
                height = inputBitmap.Height / inputBitmap.Width * size;
            }
            else
            {
                width = height = size;
            }
            Bitmap newBitmap = new Bitmap(inputBitmap, new Size(width, height));
            if (newBitmap != null)
            {
                // save the resized png into a memory stream for future use
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    newBitmap.Save(memoryStream, ImageFormat.Png);

                    BinaryWriter iconWriter = new BinaryWriter(output);
                    if (output != null && iconWriter != null)
                    {
                        // 0-1 reserved, 0
                        iconWriter.Write((byte)0);
                        iconWriter.Write((byte)0);

                        // 2-3 image type, 1 = icon, 2 = cursor
                        iconWriter.Write((short)1);

                        // 4-5 number of images
                        iconWriter.Write((short)1);

                        // image entry 1
                        // 0 image width
                        iconWriter.Write((byte)width);
                        // 1 image height
                        iconWriter.Write((byte)height);

                        // 2 number of colors
                        iconWriter.Write((byte)0);

                        // 3 reserved
                        iconWriter.Write((byte)0);

                        // 4-5 color planes
                        iconWriter.Write((short)0);

                        // 6-7 bits per pixel
                        iconWriter.Write((short)32);

                        // 8-11 size of image data
                        iconWriter.Write((int)memoryStream.Length);

                        // 12-15 offset of image data
                        iconWriter.Write((int)(6 + 16));

                        // write image data
                        // png data must contain the whole png data file
                        iconWriter.Write(memoryStream.ToArray());

                        iconWriter.Flush();

                        return true;
                    }
                }
            }
            return false;
        }
        return false;
    }

    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="inputPath">The input path</param>
    /// <param name="outputPath">The output path</param>
    /// <param name="size">The size (16x16 px by default)</param>
    /// <param name="preserveAspectRatio">Preserve the aspect ratio</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(string inputPath, string outputPath, int size = 16, bool preserveAspectRatio = false)
    {
        using (FileStream inputStream = new FileStream(inputPath, FileMode.Open))
        using (FileStream outputStream = new FileStream(outputPath, FileMode.OpenOrCreate))
        {
            return ConvertToIcon(inputStream, outputStream, size, preserveAspectRatio);
        }
    }
}

Thank You

@tristian2
Copy link

this is great TY

@jessiewestlake
Copy link

PowerShell Compatible Version

Although @lextm ported the code to PowerShell (many props, Sir), they did not include all the functionality added later by other contributors such as @kipusoep, @bananbl, and @mdiller.

Here is a usable version with the additional modifications and adapted to work with PowerShell.

I didn't have time to convert all the code to PowerShell syntax like @lextm did, so I added it as a custom type and included the required dependencies and command syntax that can be difficult to figure out with PowerShell. After adding the custom type, I wrapped it in a PowerShell function inspired by @lextm.

Parameters/Parameter Sets should be added later to take full advantage of all the constructors and for adjusting other things like size, etc.

The Preserve Aspect Ratio from @ChuckSavage wasn't included in the other C# code (possibly they accounted for it in another way), so I didn't end up adding it here yet, but maybe later; or someone else could do it.

Compatibility

OS Version PowerShell Version .NET Version ❔ ✔️ ❌
Windows 10, ver. 2004, build 19041.572 Windows PowerShell 5.1 4.8 ✔️

License

As per @darkfall, this work is licensed under CC BY-SA 4.0 BY: Credit must be given to you, the creator. SA: Adaptations must be shared under the same terms.

Code

$TypeDefinition = @'
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Collections.Generic;
using System.Drawing.Drawing2D;

/// <summary>
/// Adapted from this gist: https://gist.github.com/darkfall/1656050
/// Provides helper methods for imaging
/// </summary>
public static class ImagingHelper
{
    /// <summary>
    /// Converts a PNG image to a icon (ico) with all the sizes windows likes
    /// </summary>
    /// <param name="inputBitmap">The input bitmap</param>
    /// <param name="output">The output stream</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Bitmap inputBitmap, Stream output)
    {
        if (inputBitmap == null)
            return false;

        int[] sizes = new int[] { 256, 48, 32, 16 };

        // Generate bitmaps for all the sizes and toss them in streams
        List<MemoryStream> imageStreams = new List<MemoryStream>();
        foreach (int size in sizes)
        {
            Bitmap newBitmap = ResizeImage(inputBitmap, size, size);
            if (newBitmap == null)
                return false;
            MemoryStream memoryStream = new MemoryStream();
            newBitmap.Save(memoryStream, ImageFormat.Png);
            imageStreams.Add(memoryStream);
        }

        BinaryWriter iconWriter = new BinaryWriter(output);
        if (output == null || iconWriter == null)
            return false;

        int offset = 0;

        // 0-1 reserved, 0
        iconWriter.Write((byte)0);
        iconWriter.Write((byte)0);

        // 2-3 image type, 1 = icon, 2 = cursor
        iconWriter.Write((short)1);

        // 4-5 number of images
        iconWriter.Write((short)sizes.Length);

        offset += 6 + (16 * sizes.Length);

        for (int i = 0; i < sizes.Length; i++)
        {
            // image entry 1
            // 0 image width
            iconWriter.Write((byte)sizes[i]);
            // 1 image height
            iconWriter.Write((byte)sizes[i]);

            // 2 number of colors
            iconWriter.Write((byte)0);

            // 3 reserved
            iconWriter.Write((byte)0);

            // 4-5 color planes
            iconWriter.Write((short)0);

            // 6-7 bits per pixel
            iconWriter.Write((short)32);

            // 8-11 size of image data
            iconWriter.Write((int)imageStreams[i].Length);

            // 12-15 offset of image data
            iconWriter.Write((int)offset);

            offset += (int)imageStreams[i].Length;
        }

        for (int i = 0; i < sizes.Length; i++)
        {
            // write image data
            // png data must contain the whole png data file
            iconWriter.Write(imageStreams[i].ToArray());
            imageStreams[i].Close();
        }

        iconWriter.Flush();

        return true;
    }

    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="input">The input stream</param>
    /// <param name="output">The output stream</param
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Stream input, Stream output)
    {
        Bitmap inputBitmap = (Bitmap)Bitmap.FromStream(input);
        return ConvertToIcon(inputBitmap, output);
    }

    /// <summary>
    /// Converts a PNG image to a icon (ico)
    /// </summary>
    /// <param name="inputPath">The input path</param>
    /// <param name="outputPath">The output path</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(string inputPath, string outputPath)
    {
        using (FileStream inputStream = new FileStream(inputPath, FileMode.Open))
        using (FileStream outputStream = new FileStream(outputPath, FileMode.OpenOrCreate))
        {
            return ConvertToIcon(inputStream, outputStream);
        }
    }



    /// <summary>
    /// Converts an image to a icon (ico)
    /// </summary>
    /// <param name="inputImage">The input image</param>
    /// <param name="outputPath">The output path</param>
    /// <returns>Wether or not the icon was succesfully generated</returns>
    public static bool ConvertToIcon(Image inputImage, string outputPath)
    {
        using (FileStream outputStream = new FileStream(outputPath, FileMode.OpenOrCreate))
        {
            return ConvertToIcon(new Bitmap(inputImage), outputStream);
        }
    }


    /// <summary>
    /// Resize the image to the specified width and height.
    /// Found on stackoverflow: https://stackoverflow.com/questions/1922040/resize-an-image-c-sharp
    /// </summary>
    /// <param name="image">The image to resize.</param>
    /// <param name="width">The width to resize to.</param>
    /// <param name="height">The height to resize to.</param>
    /// <returns>The resized image.</returns>
    public static Bitmap ResizeImage(Image image, int width, int height)
    {
        var destRect = new Rectangle(0, 0, width, height);
        var destImage = new Bitmap(width, height);

        destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }

        return destImage;
    }
}
'@

Add-Type -TypeDefinition $TypeDefinition -ReferencedAssemblies 'System.Drawing','System.IO','System.Collections'

<#
.Synopsis
    Converts .PNG images to icons
.Description
    Converts a .PNG image to an icon
.Example
    ConvertTo-Icon -Path .\Logo.png -Destination .\Favicon.ico
#>
Function ConvertTo-Icon
{
    [CmdletBinding()]
    param(
    # The file
    [Parameter(Mandatory=$true, Position=0,ValueFromPipelineByPropertyName=$true)]
    [Alias('Fullname','File')]
    [string]$Path,
   
    # If provided, will output the icon to a location
    [Parameter(Position=1, ValueFromPipelineByPropertyName=$true)]
    [Alias('OutputFile')]
    [string]$Destination
    )
    
    Begin
    {
        If (-Not 'ImagingHelper' -as [Type])
        {
            Throw 'The custom "ImagingHelper" type is not loaded'
        }
    }
    
    Process
    {
        #region Resolve Path
        $ResolvedFile = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path)
        If (-not $ResolvedFile)
        {
            return
        }
        #endregion        

        [ImagingHelper]::ConvertToIcon($ResolvedFile[0].Path,$Destination)
    }
    End
    {
    }
}

@sakul-the-one
Copy link

Hello @darkfall, @mdiller, can I use your code to a DLL I'm developing? You could get power to change the following Code in the DLL if you wanna to change something

@mdiller
Copy link

mdiller commented Apr 22, 2021

Sure!

@sakul-the-one
Copy link

sakul-the-one commented Apr 23, 2021

great, thx, here is btw the respondity for the DLL: CSBeginnerHelp

@tkefauver
Copy link

Thanks for this!

If you get EndOfStreamException when using the Stream output do this before using it:
output.Position = 0;

@cktgh
Copy link

cktgh commented Feb 21, 2024

Unfortunately this cannot be used on Powershell Core, System.Drawing.Bitmap is not available.

@EvertKuijpers
Copy link

VB.Net version

''' <summary>
''' Public function convertToIcon converts a bitmap image to a icon image stream with all the sizes windows likes.
''' </summary>
Public Class imagingHelper

    ''' <summary>
    ''' Adapted from this gist: https://gist.github.com/darkfall/1656050
    ''' VB.Net version by me, EvertKuijpers of The Netherlands, also known as EAHMK, on october 19, 2024
    ''' Based on the CS.Net version by mdiller on Mar 10, 2018
    ''' Visual Basic .Net Compatible Version
    ''' 
    ''' This work is licensed under CC BY-SA 4.0, https://creativecommons.org/licenses/by-sa/4.0/.
    ''' 
    ''' I do not use imports (System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging and System.IO),
    ''' because I am convinced using full names of objects makes code easier to read.
    ''' This way there is no doubt about the namespace that an object stems from.
    ''' </summary>
    Private commentsLocation As Boolean

    ''' <summary>
    ''' Converts a bitmap image to a icon image stream with all the sizes windows likes.
    ''' </summary>
    ''' <param name="inputBitmap">The input bitmap.</param>
    ''' <param name="output">The output stream.</param>
    ''' <returns>Whether or not the icon was succesfully generated.</returns>
    Public Function convertToIcon(inputBitmap As Drawing.Bitmap, ByRef output As IO.Stream) As Boolean
        If inputBitmap Is Nothing Then
            Return False
        End If

        Dim sizes As Int32() = {256, 48, 32, 16}

        Do ' Generate bitmaps for all the sizes and toss them in streams
            Dim imageStreams As New List(Of IO.MemoryStream)
            For Each size As Int32 In sizes
                Dim newBitmap As Drawing.Bitmap = Me.resizeImage(image:=inputBitmap, width:=size, height:=size)
                If newBitmap Is Nothing Then
                    Return False
                End If
                Dim memoryStream As New IO.MemoryStream
                Call newBitmap.Save(stream:=memoryStream, format:=Drawing.Imaging.ImageFormat.Png)
                Call imageStreams.Add(item:=memoryStream)
            Next

            Dim iconWriter As New IO.BinaryWriter(output:=output)
            If output Is Nothing _
            Or iconWriter Is Nothing Then
                Return False
            End If

            Dim offset As Int32 = 0

            ' 0-1 reserved, 0
            Call iconWriter.Write(value:=CByte(0)) ' 8 bits
            Call iconWriter.Write(value:=CByte(0))

            ' 2-3 image type, 1 = icon, 2 = cursor
            Call iconWriter.Write(value:=CShort(1)) ' 16 bits

            ' 4-5 number of images
            Call iconWriter.Write(value:=CShort(sizes.Length))

            offset += 6 + (16 * sizes.Length)

            For i As Int32 = 0 To sizes.Length - 1 Step 1
                ' image entry 1
                Dim sizeToWrite As Int32 = sizes(i)
                If sizeToWrite > 255 Then
                    sizeToWrite = 0 ' Stands for 256
                End If
                ' 0 image width
                Call iconWriter.Write(value:=CByte(sizeToWrite))
                ' 1 image height
                Call iconWriter.Write(value:=CByte(sizeToWrite))

                ' 2 number of colors
                Call iconWriter.Write(value:=CByte(0))

                ' 3 reserved
                Call iconWriter.Write(value:=CByte(0))

                ' 4-5 color planes
                Call iconWriter.Write(value:=CShort(0))

                ' 6-7 bits per pixel
                Call iconWriter.Write(value:=CShort(32))

                ' 8-11 size of image data
                Call iconWriter.Write(value:=CInt(imageStreams(index:=i).Length)) ' 32 bits

                ' 12-15 offset of image data
                Call iconWriter.Write(value:=CInt(offset))

                offset += imageStreams(index:=i).Length
            Next

            For i As Int32 = 0 To sizes.Length - 1 Step 1
                ' write image data
                ' png data must contain the whole png data file
                Call iconWriter.Write(imageStreams(index:=i).ToArray)
                Call imageStreams(index:=i).Close()
            Next

            Call iconWriter.Flush()

            Exit Do
        Loop

        Return True
    End Function

    ''' <summary>
    ''' Resize the image to the specified width and height.
    ''' Found on stackoverflow: https://stackoverflow.com/questions/1922040/resize-an-image-c-sharp.
    ''' </summary>
    ''' <param name="image">The image to resize.</param>
    ''' <param name="width">The width to resize to.</param>
    ''' <param name="height">The height to resize to.</param>
    ''' <returns>The resized image.</returns>
    Private Function resizeImage(image As Drawing.Image, width As Int32, height As Int32) As Drawing.Bitmap
        Dim destRect As New Drawing.Rectangle(0, 0, width, height)
        Dim destImage As New Drawing.Bitmap(width, height)

        Call destImage.SetResolution(xDpi:=image.HorizontalResolution, yDpi:=image.VerticalResolution)

        Using graphics As Drawing.Graphics = Drawing.Graphics.FromImage(destImage)
            graphics.CompositingMode = Drawing.Drawing2D.CompositingMode.SourceCopy
            graphics.CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
            graphics.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
            graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality
            graphics.PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality

            Using WrapMode As New Drawing.Imaging.ImageAttributes
                Call WrapMode.SetWrapMode(mode:=Drawing.Drawing2D.WrapMode.TileFlipXY)
                Call graphics.DrawImage(image:=image, destRect:=destRect, srcX:=0, srcY:=0, srcWidth:=image.Width, srcHeight:=image.Height, srcUnit:=Drawing.GraphicsUnit.Pixel, imageAttr:=WrapMode)
            End Using
        End Using

        Return destImage
    End Function

    ''' <summary>
    ''' Converts a bitmap stream to a icon image stream.
    ''' </summary>
    ''' <param name="input">The input stream.</param>
    ''' <param name="output">The output stream.</param>
    ''' <returns>Whether or not the icon was succesfully generated.</returns>
    Public Function convertToIcon(input As IO.Stream, ByRef output As IO.Stream) As Boolean
        Dim inputBitmap As Drawing.Bitmap = Drawing.Bitmap.FromStream(stream:=input)
        Return Me.convertToIcon(inputBitmap:=inputBitmap, output:=output)
    End Function

    ''' <summary>
    ''' Converts a bitmap file to a icon file of type ico.
    ''' </summary>
    ''' <param name="inputPath">The input path.</param>
    ''' <param name="outputPath">The output path.</param>
    ''' <returns>Whether or not the icon was succesfully generated.</returns>
    Public Function convertToIcon(inputPath As String, ByRef outputPath As String) As Boolean
        Using inputStream As New IO.FileStream(path:=inputPath, mode:=IO.FileMode.Open)
            Using outputStream As New IO.FileStream(path:=outputPath, mode:=IO.FileMode.OpenOrCreate)
                Return Me.convertToIcon(input:=inputStream, output:=outputStream)
            End Using
        End Using
    End Function

    ''' <summary>
    ''' Converts an image to a icon image stream.
    ''' </summary>
    ''' <param name="inputImage">The input image.</param>
    ''' <param name="outputPath">The output path.</param>
    ''' <returns>Whether Or Not the icon was succesfully generated.</returns>
    Public Function convertToIcon(inputImage As Drawing.Image, outputPath As String)
        Using outputStream As New IO.FileStream(path:=outputPath, mode:=IO.FileMode.OpenOrCreate)
            Return Me.convertToIcon(inputBitmap:=New Drawing.Bitmap(original:=inputImage), output:=outputStream)
        End Using
    End Function

End Class

@Amerigoware1
Copy link

@EvertKuijpers Your VB.NET version works excellently; however (with Option Strick On), I had to fix three errors and several naming rule violations.
Added "As Boolean" to the last function.
Changed "offset += imageStreams(index:=i).Length" to "offset += CInt(imageStreams(index:=i).Length)".
Changed "Dim inputBitmap As Drawing.Bitmap = Drawing.Bitmap.FromStream(stream:=input)" to "Dim inputBitmap As Drawing.Bitmap = CType(Drawing.Bitmap.FromStream(stream:=input), Drawing.Bitmap)".

@EvertKuijpers
Copy link

@Amerigoware1 The missing "As Boolean" is an error for sure. Thank you for finding this. Greetz from Tilburg in The Netherlands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment