Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A High-Quality IconBitmapEncoder for WPF

0.00/5 (No votes)
11 Nov 2017 1  
An IconBitmapEncoder that produces high-quality icons, written in VB.NET and C#, WPF

Update 2 (11/11/2017)

Contradicting what I've said before, you don't need to include the 4bpp and 8bpp frames within your icons.

Seems like, on modern icons (Windows Vista and newer), you need to include only 32bpp frames.

I've opened lots of icons from internet, and from Microsoft Windows (10) itself to get to this ultimate conclusion.

I'm sorry for that. Well, live and learn.

Plus, I'd like to mention that I've made some changes to the code and moved it to GITHUB. There you will get the most recent code, releases and etc. I've made many changes since the last update on Codeplex.

GitHub - HerbertLausmann/HL.IconPro: A .NET C# Library for working with Windows Icons and Cursors

Update

Now, after some months of development, I've finished the first release of a more complete tool for icon editing featuring a more powerful IconBitmapEnconder and a complete new IconBitmapDecoder. This tool is called Icon Pro. The source code and the first release are available on CodePlex on this link:

Introduction

Windows Presentation Foundation was really a great revolution in application development for Windows, allowing us to create applications with high-end UIs.

However, not everything is perfect. Writing code for WPF, I realized that was missing in API an Encoder for icon files (Because WIC don't have a icon encoder, and WPF Image Encoders (and Decoders too) are wrappers to native WIC API), which would be the IconBitmapEncoder.

Not satisfied with this, I decided to try to create an encoder for icons to cover for the deficiency. After several days of work, I managed to develop an IconBitmapEncoder that generates icon files with high quality.

By high-quality, I mean that this encoder supports 32 Bit icons, and also the size 256x256 in png format.

Background

Basically, an icon file consists of the following parts:

However, that is not all!

An icon file must contain multiple sizes of the same image (Example: 16x16, 32x32 ...) and each image should have three different pixel formats: Indexed 4 (16 colors [4bits]), Indexed 8 (256 colors [8bits]), and RGB (A – Alpha Channel is optional) (True Colors [32bits]). Look at a visual representation of an icon:

All images in an icon file must be in Bitmap (BMP) format without the file header (First 14 Bytes), except that 256x256 image should be stored in PNG format (RGBA 32 bits).

BMP images must include a doubled height because they do not have an AND mask. For more information about AND Mask, see BMP file format (Wikipedia).

Don't ask me why, but to work right, the frames in icon file should be ordered from smallest to largest. For this reason, I did the implementation of the SortAscending method in class IconBitmapFramesCollection. Another thing I should emphasize is that the images must be square and must have at least 16x16 pixels and no more than 256x256 pixels.

To develop this encoder, the following articles were of great importance. Give them a read for more background information:

Using the Code

Below is a sample code for creating a high-quality icon from a single 256x256 PNG image, using my IconBitmapEncoder class, this code is also available in the solution for download (In C# version too):

Public Sub SaveIcon(Source As BitmapSource, _
    Stream As IO.Stream, Quality As Integer)
        Dim encoder As New IconBitmapEncoder

        Dim bmp16 As BitmapSource = IconBitmapEncoder.GetResized(Source, 16)

        Dim bmp24 As BitmapSource = IconBitmapEncoder.GetResized(Source, 24)

        Dim bmp32 As BitmapSource = IconBitmapEncoder.GetResized(Source, 32)

        Dim bmp48 As BitmapSource = IconBitmapEncoder.GetResized(Source, 48)

        Dim bmp64 As BitmapSource = IconBitmapEncoder.GetResized(Source, 64)

        Dim bmp72 As BitmapSource = IconBitmapEncoder.GetResized(Source, 72)

        Dim bmp96 As BitmapSource = IconBitmapEncoder.GetResized(Source, 96)

        Dim bmp128 As BitmapSource = IconBitmapEncoder.GetResized(Source, 128)

        Dim bmp256 As BitmapSource = IconBitmapEncoder.GetResized(Source, 256)

        If Quality >= 90 Then
            encoder.Frames.Add(BitmapFrame.Create(bmp256))
        End If

        If Quality >= 80 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp128)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp128)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp128)))
        End If

        If Quality >= 70 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp96)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp96)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp96)))
        End If

        If Quality >= 60 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp72)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp72)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp72)))
        End If

        If Quality >= 50 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp64)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp64)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp64)))
        End If

        If Quality >= 40 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp48)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp48)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp48)))
        End If

        If Quality >= 30 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp32)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp32)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp32)))
        End If

        If Quality >= 20 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp24)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp24)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp24)))
        End If

        If Quality >= 0 Then
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get24plus8BitImage(bmp16)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get4BitImage(bmp16)))
            encoder.Frames.Add(BitmapFrame.Create_
            (IconBitmapEncoder.Get8BitImage(bmp16)))
        End If
        encoder.Save(Stream)
        Stream.Close()
End Sub  

Source is the 256x256 PNG 32bit Image; Stream is the out stream, and Quality is a value in 0-100 range.

So, we need to add one BitmapFrame for each image size, and for each image pixel format, to the encoder Frames collection.

Points of Interest

  • BitmapImage pixel conversion from 32 bits to 8bits and 4bits
  • BitmapImage scaling
  • Use of BinaryWriter to handle the little-endian byte order writing
  • List items sorting, ascending and descending

Notes

  • The Encoder meets all requirements for creating icons supported on Windows XP and newer. However, I only had the chance to test it on Windows 7 SP1. So if you have any problems with the icons generated on other Windows versions, please let me know and I will try to fix it.
  • The demo and the API were written in Visual Studio 2012 SP2, .NET Framework 3.5 SP1.

History

  • Version 1.0.0.0 – 25/11/2013
  • Version 1.1.0.0 – 21/12/2013
    • Solution with the source code (and a fully functional demo) uploaded
    • Now the code is available both as VB.NET and C#
  • Version 1.1.0.1 – 22/12/2013
    • Added some more information
    • Removed IconBitmapEncoder class source code from article page

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here