Archive Browser
Download RiffPaletteWriter.zip version 1.0.0.0, last updated 04/03/2017 (14.94 KB)
Download- md5: 2864394d3c1cc948887ced13c5d5bedf
- sha1: dc44b4f4d8d6318030d6460a5f88f43aa345ce2f
- sha256: 8781446c207bc5faf4021226bdbd3789c6acdd21efbaeaa101dbcca0a2749abf
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
// Loading Microsoft RIFF Palette (pal) files with C#
// http://cyotek.com/blog/loading-microsoft-riff-palette-pal-files-with-csharp
// Writing Microsoft RIFF Palette (pal) files with C#
// http://cyotek.com/blog/writing-microsoft-riff-palette-pal-files-with-csharp
namespace Cyotek.Demonstrations.RiffPaletteWriter
{
internal sealed class RiffPalette : IEnumerable<Color>
{
#region Fields
private Color[] _palette;
#endregion
#region Constructors
public RiffPalette()
{
_palette = new Color[0];
}
public RiffPalette(Color[] colors)
{
_palette = (Color[])colors.Clone();
}
#endregion
#region Methods
public void Load(string fileName)
{
using (Stream stream = File.OpenRead(fileName))
{
this.Load(stream);
}
}
public void Load(Stream stream)
{
byte[] buffer;
bool eof;
buffer = new byte[1032]; // create a buffer big enough to hold a default 256 color palette
// first read and validate the form header
// this is comprised of
// * RIFF header
// * document size
// * FORM header
stream.Read(buffer, 0, 12);
if (buffer[0] != 'R' || buffer[1] != 'I' || buffer[2] != 'F' || buffer[3] != 'F')
{
throw new InvalidDataException("Source stream is not a RIFF document.");
}
// TODO: Validate the document size if required
if (buffer[8] != 'P' || buffer[9] != 'A' || buffer[10] != 'L' || buffer[11] != ' ')
{
throw new InvalidDataException("Source stream is not a palette.");
}
eof = false;
while (!eof)
{
// a RIFF document can have one or more chunks.
// for our purposes, each chunk has a 4 letter
// identifier and a size of the data part of the
// chunk. We can use these two values to either
// process the palette data, or skip data we
// don't recognise
// TODO: This procedure only supports simple, not extended, palettes
if (stream.Read(buffer, 0, 8) == 8)
{
int chunkSize;
chunkSize = buffer.GetInt32(4);
// see if we have the palette data
if (buffer[0] == 'd' && buffer[1] == 'a' && buffer[2] == 't' && buffer[3] == 'a')
{
if (chunkSize > buffer.Length)
{
buffer = new byte[chunkSize];
}
if (stream.Read(buffer, 0, chunkSize) != chunkSize)
{
throw new InvalidDataException("Failed to read enough data to match chunk size.");
}
_palette = this.ReadPalette(buffer);
// only one palette in a file, so break out of the chunk scan
eof = true;
}
else
{
// not the palette data? advance the stream to the next chunk
// riff chunks are word-aligned. if the size is odd
// then a padding byte is included after the chunk
// data. This is junk data, so we just skip it
if (chunkSize % 2 != 0)
{
chunkSize++;
}
stream.Position += chunkSize;
}
}
else
{
// nothing to read, abort
eof = true;
}
}
;
}
public void Save(string fileName)
{
using (Stream stream = File.Create(fileName))
{
this.Save(stream);
}
}
public void Save(Stream stream)
{
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];
// 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);
// the form type
buffer[8] = (byte)'P';
buffer[9] = (byte)'A';
buffer[10] = (byte)'L';
buffer[11] = (byte)' ';
// data chunk header
buffer[12] = (byte)'d';
buffer[13] = (byte)'a';
buffer[14] = (byte)'t';
buffer[15] = (byte)'a';
WordHelpers.PutInt32(chunkSize, buffer, 16);
// logpalette
buffer[20] = 0;
buffer[21] = 3;
WordHelpers.PutInt16(count, buffer, 22);
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;
}
stream.Write(buffer, 0, length);
}
private Color[] ReadPalette(byte[] buffer)
{
Color[] palette;
ushort count;
// The buffer should hold a LOGPALETTE structure containing
// OS version (2 bytes, always 03)
// Color count (2 bytes)
// Color data (4 bytes * color count)
count = buffer.GetInt16(2);
palette = new Color[count];
for (int i = 0; i < count; i++)
{
byte r;
byte g;
byte b;
int offset;
offset = i * 4 + 4;
r = buffer[offset];
g = buffer[offset + 1];
b = buffer[offset + 2];
// TODO: The fourth byte are flags, which we have no use for here
palette[i] = Color.FromArgb(r, g, b);
}
return palette;
}
#endregion
#region IEnumerable<Color> Interface
public IEnumerator<Color> GetEnumerator()
{
// ReSharper disable once ForCanBeConvertedToForeach
// ReSharper disable once LoopCanBeConvertedToQuery
for (int i = 0; i < _palette.Length; i++)
{
yield return _palette[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
}
Donate
This software may be used free of charge, but as with all free software there are costs involved to develop and maintain.
If this site or its services have saved you time, please consider a donation to help with running costs and timely updates.
Donate