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

Palettes - You've Gotta Love Them!

0.00/5 (No votes)
27 Sep 2009 1  
Creating our own palette viewer and picker UserControl

Introduction

Palettes are just what they say they are. They define which colours you can safely use in an image without infringing on its colour depth. For instance, a 256-colour palette for a 256-colour image would be appropriate, or a greyscale palette for a greyscale image. Nothing, as far as I'm concerned, indicates a truly excellent image editor program better than the capability to read palette files. And what better to use for clarity and robustness than a user control?

Background

First, how do palette files work? Well, first of all, there are so many variants of this image format that the .pal extension has become a generic extension meaning that any file that is intended as use as a palette file will probably have this extension. For this article, we'll be focusing on the “JASC-PAL” format, originally used by Jasc Software in Paint Shop Pro.

Here is a typical JASC-PAL palette file:

JASC-PAL
0100
16
255 0 0
0 255 0
0 0 255
255 255 0
0 255 255
255 0 255
100 0 0
0 100 0
0 0 100
100 100 0
0 100 100
100 0 100
200 200 0
0 200 200
200 0 200
255 255 255

Let’s break it down to see what it does.

Line 1 - File Format Header

This is the part of the file that identifies the file as a Paint Shop Pro palette file.

Line 2 - File Format Version

The version of the file format used in this palette file.

Line 3 - Number Of Colours In The Palette

Old rules state that a JASC-PAL palette file can only contain either 256 or 16 colours. We don't play by those rules.

Line 4 and Onwards - Palette Data

Each line defines a standard RGB value for a single palette swatch.

Using the Code

That was simple! So with that out of our way, let’s get on with the actual control. We will be creating a control capable of reading a palette file. Populating itself with the swatches therein, and offering selection of each of these colours, it will be an extremely robust control, and in the interest of that, we will be using a FlowLayoutPanel and adding a panel for every swatch that we read in from the palette file in question. But first, we need to define some global variables:

  • Private FileType As String 

    Holds the file type - Defined in the header

  • Private FileVersion As String 

    Holds the Image version

  • Private Colours As Integer 

    Holds the number of colours in the palette

  • Private GlobalDataArray As New ArrayList 

    Holds the actual palette data

  • Private SelectedPanel As Panel 

    Holds the currently selected swatch panel

  • Private ShowSelection As Boolean 

    Show the white selection square?

The global names in themselves are fairly self-explanatory, and the source code in the sample download is fully commented. But let's go through the general purpose behind the functions in the PaletteReader control:

  • AddColourSwatch - Adds a swatch of the specified colour to the control
  • OpenPaletteFile - Opens a palette file, parses the header and stores the palette data
  • SavePaletteFile - Saves the palette currently displayed to a file
  • RemoveSwatch - Removes the swatch at the specified index number
  • AddPaletteSwatches - Adds the swatches contained in the data passed by OpenPaletteFile control to the control
  • PurgeGlobalData - Resets all global variables
  • HighlightSender - Highlights the swatch that raises this event
  • RemoveHighlightSender - Removes the highlight from the swatch that raises this event
  • SelectSender - Selects the swatch that raises this event.

All these functions are very simple, apart from perhaps the OpenPaletteFile and SavePalletFile functions. OpenPalettefile is the most complex:

Public Function OpenPaletteFile(ByVal FilePath As String) As Boolean

Try

PurgeGlobalData()

'Create Some Variables To Hold our Data [
Dim DataArray As Array
Dim OpenFile As New System.IO.StreamReader(FilePath)
Dim StringHolder As String = ""
']

'Look At The Header - Is It A Header? Or Have We Started The Data Already? [
Dim PeekHeaderLine As String = ""

PeekHeaderLine = OpenFile.ReadLine()

If PeekHeaderLine.StartsWith("JASC-PAL") Then

'Assign All The Data In The Header To A Variable [
FileType = "JASC-PAL"
FileVersion = OpenFile.ReadLine
Colours = Val(OpenFile.ReadLine)
']

Else

'If There Is No Header, Start Over [
OpenFile.Close()
OpenFile = New System.IO.StreamReader(FilePath)
']

End If

']

'Read In Our Data And Flatten It To A Single Line [
StringHolder = OpenFile.ReadToEnd
StringHolder = StringHolder.Replace(vbNewLine, " ")
']

'Close Our StreamReader [
OpenFile.Close()
']

'Split Our Data Into Sections [
DataArray = StringHolder.Split(" ")
']

'Remove Empty Items From Those Sections [
For Each StringData As String In DataArray

If Not StringData.Length = 0 Then
GlobalDataArray.Add(StringData)
End If

Next
']

'Start To Add The Swatches To The Control [
AddPaletteSwatches()
']

Catch MyException As Exception

'Return A Failure And Exit [
Return False
Exit Function
']

End Try

'Return A Success [
Return True
']

End Function

The basic process of this function is as follows:

  1. Clear the global data, ready to open a new file.
  2. Check for a header and read in header information.
  3. Split the rest of the file into separate values.
  4. Remove the empty values.
  5. Start to add the swatches.

Easy as pie! So where do we go from there. Well, many image editor applications can not only read and display palettes, they can also create and save custom palettes, This is where our AddColourSwatch and SavePaletteFile functions come in. Let's take a look at AddColourSwatch first:

Public Function AddColorSwatch(ByVal PanelColour As Color) As Boolean

Try

'Create A New Palette Colour Panel [
Dim NewPanel As New Panel

NewPanel.BackColor = PanelColour
NewPanel.BackgroundImage = My.Resources.Palette_Seperator
NewPanel.Margin = New Padding(0)
NewPanel.Size = New Size(12, 12)
']

AddHandler NewPanel.MouseEnter, AddressOf HighlightSender
AddHandler NewPanel.MouseLeave, AddressOf RemoveHighlightSender
AddHandler NewPanel.Click, AddressOf SelectSender

'Add It To Our Palette Holder [
SwatchLayoutPanel.Controls.Add(NewPanel)
']

Catch MyException As Exception

'Return A Failure And Exit [
Return False
Exit Function
']

End Try

'Return A Success [
Return True
']

End Function

This function is public, allowing it to be accessed from the control's parent form, perfect for adding swatches. But how does this work? Let's have a look:

  1. Create a new panel, assign to it a background image and make it the appropriate size (12x12)
  2. Add event handlers to it, allowing it to be highlighted when the mouse hovers over it and when it's clicked.
  3. Add it to the FlowLayoutPanel.

So how about saving? The capability to save customised palettes and swatches is common to many image editing applications, including Photoshop CS3 and CS4 and Paint Shop Pro.

Public Function SavePaletteFile(ByVal SaveAsJASCPAL As Boolean, _
ByVal FilePath As String) As Boolean

Try

'Open A Stream [
Dim MyStreamWriter As New System.IO.StreamWriter(FilePath)
']

'If We're Supposed To Be Saving As A JASC-PAL file, Write the Header [
If SaveAsJASCPAL Then
MyStreamWriter.WriteLine("JASC-PAL")
MyStreamWriter.WriteLine("0100")
MyStreamWriter.WriteLine(SwatchLayoutPanel.Controls.Count.ToString)
End If

']

'Write The RGB Value For Each Swatch To The File [
For Each CurrentPanel As Panel In SwatchLayoutPanel.Controls
MyStreamWriter.Write(CurrentPanel.BackColor.R & " ")
MyStreamWriter.Write(CurrentPanel.BackColor.G & " ")
MyStreamWriter.Write(CurrentPanel.BackColor.B & vbNewLine)
Next
']

'Close Everything Off [
MyStreamWriter.Flush()
MyStreamWriter.Close()
']

Catch MyException As Exception

'Return A Failure And Exit [
Return False
Exit Function
']

End Try

'Return A Success [
Return True
']

End Function

It really is as simple as that. Loop through every panel in the SwatchLayoutPanel and write its RGB value to the destination file.

To Conclude...

So there you have it! A highly extensible palette viewer and picker to use in your applications! I sincerely hope that you have as much fun using it and modifying it as I have had creating it!

Credits

  • A huge thank you to Mark James for his Silk Icon Pack (used in the demo app). Visit his site here.

History

  • 22/12/08 - Article uploaded at 19:00

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