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()
Dim DataArray As Array
Dim OpenFile As New System.IO.StreamReader(FilePath)
Dim StringHolder As String = ""
Dim PeekHeaderLine As String = ""
PeekHeaderLine = OpenFile.ReadLine()
If PeekHeaderLine.StartsWith("JASC-PAL") Then
FileType = "JASC-PAL"
FileVersion = OpenFile.ReadLine
Colours = Val(OpenFile.ReadLine)
Else
OpenFile.Close()
OpenFile = New System.IO.StreamReader(FilePath)
End If
StringHolder = OpenFile.ReadToEnd
StringHolder = StringHolder.Replace(vbNewLine, " ")
OpenFile.Close()
DataArray = StringHolder.Split(" ")
For Each StringData As String In DataArray
If Not StringData.Length = 0 Then
GlobalDataArray.Add(StringData)
End If
Next
AddPaletteSwatches()
Catch MyException As Exception
Return False
Exit Function
End Try
Return True
End Function
The basic process of this function is as follows:
- Clear the global data, ready to open a new file.
- Check for a header and read in header information.
- Split the rest of the file into separate values.
- Remove the empty values.
- 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
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
SwatchLayoutPanel.Controls.Add(NewPanel)
Catch MyException As Exception
Return False
Exit Function
End Try
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:
- Create a new
panel
, assign to it a background image and make it the appropriate size (12x12)
- Add event handlers to it, allowing it to be highlighted when the mouse hovers over it and when it's clicked.
- 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
Dim MyStreamWriter As New System.IO.StreamWriter(FilePath)
If SaveAsJASCPAL Then
MyStreamWriter.WriteLine("JASC-PAL")
MyStreamWriter.WriteLine("0100")
MyStreamWriter.WriteLine(SwatchLayoutPanel.Controls.Count.ToString)
End If
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
MyStreamWriter.Flush()
MyStreamWriter.Close()
Catch MyException As Exception
Return False
Exit Function
End Try
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