Writing Microsoft RIFF Palette (pal) files with C#
A short follow up and sample program which demonstrates how to write a RIFF palette with ease.
About RIFF Palettes
I covered the basics of the RIFF specification and how to read palettes in my previous article.
Performance Considerations
When I first started this journey and wrote how to read and write palette files in different formats, the code I provided generally read and wrote bytes one at a time. At the start of January (2016, time has a habit of getting away from me!) I wrote an article which described how to read and write farbfeld images.
While updating the source for this project, I created a series of benchmarks testing the serialisation code and proved the obvious fact that reading and writing a byte a time was really inefficient.
As a result of this, I'm now a little more careful when reading and writing files. The previous article on reading RIFF palettes tried to be efficient both in terms of IO (reading blocks of information at a time) and in terms of allocations (by using the same buffer object as much as possible), so hopefully that code is quite efficient.
Similarly, when writing the file as per the code below, I create a buffer large enough to hold the entire RIFF form - palettes generally aren't huge objects so this is fine. I then populate the buffer with the form and write it all at once.
There aren't any guards around this code though to ensure that buffers are reasonably sized and so if this code was being adapted (for example to read WAVE audio or AVI videos) then additional precautions would be required.
Writing int
and ushort
values into byte arrays
As we're going to construct the entire RIFF form in a byte
array, we can't use classes such as StreamWriter
to write
values. I'm going to use a pair of helper methods that will
break down an int
into four bytes or a ushort
into a pair of
bytes which I will then place into the array at appropriate
offsets.
Remember that RIFF uses little-endian ordering
public static void PutInt32(int value, byte[] buffer, int offset)
{
buffer[offset + 3] = (byte)((value & 0xFF000000) >> 24);
buffer[offset + 2] = (byte)((value & 0x00FF0000) >> 16);
buffer[offset + 1] = (byte)((value & 0x0000FF00) >> 8);
buffer[offset] = (byte)((value & 0x000000FF) >> 0);
}
public static void PutInt16(ushort value, byte[] buffer, int offset)
{
buffer[offset + 1] = (byte)((value & 0x0000FF00) >> 8);
buffer[offset] = (byte)((value & 0x000000FF) >> 0);
}
You could use the
BitConverter
class to break down the values, but that means extra allocations for the byte array returned by theGetBytes
method.
Writing a RIFF palette
First we need to calculate the size of our data
chunk for the
palette, which is 4 + number_of_colors * 4
. Each colour is
comprised of 4
bytes, which accounts for the bulk of the
chunk, but there's also 4
bytes for the palVersion
and
palNumEntries
fields of the LOGPALETTE
structure.
Once we have that size, we calculate the size of the complete RIFF form and create a byte array that will hold the entire form.
byte[] buffer;
int length;
ushort count;
ushort chunkSize;
count = (ushort)_palette.Length;
chunkSize = (ushort)(4 + count * 4);
// 4 bytes for RIFF
// 4 bytes for document size
// 4 bytes for PAL
// 4 bytes for data
// 4 bytes for chunk size
// 2 bytes for the version
// 2 bytes for the count
// (4*n) for the colors
length = 4 + 4 + 4 + 4 + 4 + 2 + 2 + count * 4;
buffer = new byte[length];
Next, we write the RIFF header. Remember that the document size is the size of the entire form minus 8 bytes representing the RIFF header.
// the riff header
buffer[0] = (byte)'R';
buffer[1] = (byte)'I';
buffer[2] = (byte)'F';
buffer[3] = (byte)'F';
WordHelpers.PutInt32(length - 8, buffer, 4); // document size
We then follow this with the form type
// the form type
buffer[8] = (byte)'P';
buffer[9] = (byte)'A';
buffer[10] = (byte)'L';
buffer[11] = (byte)' ';
So far so good. We won't be writing any meta data, only the
data
chunk with our basic RGB palette. First we'll write the
chunk header, and then we'll write the first two fields
describing the palette.
// data chunk header
buffer[12] = (byte)'d';
buffer[13] = (byte)'a';
buffer[14] = (byte)'t';
buffer[15] = (byte)'a';
WordHelpers.PutInt32(chunkSize, buffer, 16); // chunk size
// logpalette
buffer[20] = 0;
buffer[21] = 3; // os version (always 03)
WordHelpers.PutInt16(count, buffer, 22); // colour count
Now it's just a case of filling in the colour information
for (int i = 0; i < count; i++)
{
Color color;
int offset;
color = _palette[i];
offset = 24 + i * 4;
buffer[offset] = color.R;
buffer[offset + 1] = color.G;
buffer[offset + 2] = color.B;
// TODO: use buffer[offset + 3] for flags
}
And finally, we can write our buffer to the destination stream. Easy!
stream.Write(buffer, 0, length);
Update History
- 2017-03-04 - First published
- 2020-11-22 - Updated formatting
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
RiffPaletteWriter.zip
|
Sample project for the writing Microsoft RIFF Palette (pal) files with C# blog post. |
1.0.0.0 | 04/03/2017 | Download |
Leave a Comment
While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?