Click here to Skip to main content
Click here to Skip to main content

A High-Quality IconBitmapEncoder for WPF

, 24 Nov 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
An IconBitmapEncoder that produces high-quality icons, written in VB.NET and C#, WPF

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Herbert Lausmann
Student
Brazil Brazil
No Biography provided

Comments and Discussions

 
QuestionNot an article... PinmvpDave Kreskowiak21-Dec-13 7:27 
AnswerRe: Not an article... PinmemberHerbert Lausmann21-Dec-13 8:00 
QuestionHow to use it in C#? PinmemberMurhaf Suz20-Dec-13 19:34 
AnswerSource Code available in C # PinmemberHerbert Lausmann21-Dec-13 9:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 24 Nov 2013
Article Copyright 2013 by Herbert Lausmann
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid