This content has moved - please find it at https://devblog.cyotek.com.

Although these pages remain accessible, some content may not display correctly in future as the new blog evolves.

Visit https://devblog.cyotek.com.

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.

Example program that generates a random palette and saves it into a RIFF form

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 the GetBytes 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
  • sha256: 8781446c207bc5faf4021226bdbd3789c6acdd21efbaeaa101dbcca0a2749abf

Sample project for the writing Microsoft RIFF Palette (pal) files with C# blog post.

1.0.0.0 04/03/2017 Download

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

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?

Styling with Markdown is supported