Archive Browser
Download PhotoshopColorSwatchWriter.zip version 1.0.0.0, last updated 28/01/2014 (16.86 KB)
Download- md5: c5498aad2487ba4d5b87707f27e9eb30
// Writing PhotoShop Color Swatch (aco) files using C#
// http://cyotek.com/blog/writing-photoshop-color-swatch-aco-files-using-csharp
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Cyotek.Windows.Forms;
namespace PhotoshopColorSwatchWriter
{
internal sealed partial class GeneratorWindow : Form
{
#region Enums
private enum ColorSpace
{
Rgb = 0,
Hsb = 1,
Cmyk = 2,
Lab = 7,
Grayscale = 8
}
private enum FileVersion
{
Version1 = 1,
Version2
}
#endregion
#region Instance Fields
private List<Color> _comparePalette;
private List<Color> _originalPalette;
private Random _random;
private string _tempFileName;
#endregion
#region Public Constructors
public GeneratorWindow()
{
InitializeComponent();
}
#endregion
#region Overridden Methods
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Form.Load"/> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data. </param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_random = new Random();
_tempFileName = Path.GetTempFileName();
this.GenerateAndSavePalette();
}
#endregion
#region Private Members
private void GenerateAndSavePalette()
{
// generate our palette
_originalPalette = this.GeneratePalette();
// show a preview in the first pane
originalPalettePanel.Colors = _originalPalette;
// save and update the compare preview
this.SaveAndComparePalette();
}
private List<Color> GeneratePalette()
{
List<Color> palette;
palette = new List<Color>();
for (int i = 0; i < 255; i++)
{
int r;
int g;
int b;
r = _random.Next(0, 255);
g = _random.Next(0, 255);
b = _random.Next(0, 255);
palette.Add(Color.FromArgb(r, g, b));
}
return palette;
}
/// <summary>
/// Reads a 16bit unsigned integer in big-endian format.
/// </summary>
/// <param name="stream">The stream to read the data from.</param>
/// <returns>The unsigned 16bit integer cast to an <c>Int32</c>.</returns>
private int ReadInt16(Stream stream)
{
return (stream.ReadByte() << 8) | (stream.ReadByte() << 0);
}
/// <summary>
/// Reads a 32bit unsigned integer in big-endian format.
/// </summary>
/// <param name="stream">The stream to read the data from.</param>
/// <returns>The unsigned 32bit integer cast to an <c>Int32</c>.</returns>
private int ReadInt32(Stream stream)
{
return ((byte)stream.ReadByte() << 24) | ((byte)stream.ReadByte() << 16) | ((byte)stream.ReadByte() << 8) | ((byte)stream.ReadByte() << 0);
}
private List<Color> ReadPhotoshopSwatchFile(string fileName)
{
List<Color> colorPalette;
using (Stream stream = File.OpenRead(fileName))
{
FileVersion version;
// read the version, which occupies two bytes
version = (FileVersion)this.ReadInt16(stream);
if (version != FileVersion.Version1 && version != FileVersion.Version2)
throw new InvalidDataException("Invalid version information.");
// the specification states that a version2 palette follows a version1
// the only difference between version1 and version2 is the inclusion
// of a name property. Perhaps there's addtional color spaces as well
// but we can't support them all anyway
// I noticed some files no longer include a version 1 palette
colorPalette = this.ReadSwatches(stream, version);
if (version == FileVersion.Version1)
{
version = (FileVersion)this.ReadInt16(stream);
if (version == FileVersion.Version2)
colorPalette = this.ReadSwatches(stream, version);
}
}
return colorPalette;
}
/// <summary>
/// Reads a unicode string of the specified length.
/// </summary>
/// <param name="stream">The stream to read the data from.</param>
/// <param name="length">The number of characters in the string.</param>
/// <returns>The string read from the stream.</returns>
private string ReadString(Stream stream, int length)
{
byte[] buffer;
buffer = new byte[length * 2];
stream.Read(buffer, 0, buffer.Length);
return Encoding.BigEndianUnicode.GetString(buffer);
}
private List<Color> ReadSwatches(Stream stream, FileVersion version)
{
int colorCount;
List<Color> results;
results = new List<Color>();
// read the number of colors, which also occupies two bytes
colorCount = this.ReadInt16(stream);
for (int i = 0; i < colorCount; i++)
{
ColorSpace colorSpace;
int value1;
int value2;
int value3;
int value4;
// again, two bytes for the color space
colorSpace = (ColorSpace)(this.ReadInt16(stream));
value1 = this.ReadInt16(stream);
value2 = this.ReadInt16(stream);
value3 = this.ReadInt16(stream);
value4 = this.ReadInt16(stream);
if (version == FileVersion.Version2)
{
int length;
// need to read the name even though currently our colour collection doesn't support names
length = ReadInt32(stream);
this.ReadString(stream, length);
}
switch (colorSpace)
{
case ColorSpace.Rgb:
int red;
int green;
int blue;
// RGB.
// The first three values in the color data are red , green , and blue . They are full unsigned
// 16-bit values as in Apple's RGBColor data structure. Pure red = 65535, 0, 0.
red = value1 / 256; // 0-255
green = value2 / 256; // 0-255
blue = value3 / 256; // 0-255
results.Add(Color.FromArgb(red, green, blue));
break;
case ColorSpace.Hsb:
double hue;
double saturation;
double brightness;
// HSB.
// The first three values in the color data are hue , saturation , and brightness . They are full
// unsigned 16-bit values as in Apple's HSVColor data structure. Pure red = 0,65535, 65535.
hue = value1 / 182.04; // 0-359
saturation = value2 / 655.35; // 0-1
brightness = value3 / 655.35; // 0-1
results.Add(new HslColor(hue, saturation, brightness).ToRgbColor());
break;
case ColorSpace.Grayscale:
int gray;
// Grayscale.
// The first value in the color data is the gray value, from 0...10000.
gray = (int)(value1 / 39.0625); // 0-255
results.Add(Color.FromArgb(gray, gray, gray));
break;
default:
throw new InvalidDataException(string.Format("Color space '{0}' not supported.", colorSpace));
}
}
return results;
}
private void SaveAndComparePalette()
{
// write the palette to a swatch file
this.WriteSwatches(_originalPalette, _tempFileName);
// load back the swatch file and display this in the second pane
_comparePalette = this.ReadPhotoshopSwatchFile(_tempFileName);
comparePalettePanel.Colors = _comparePalette;
}
/// <summary>
/// Writes a 16bit unsigned integer in big-endian format.
/// </summary>
/// <param name="stream">The stream to write the data to.</param>
/// <param name="value">The value to write</param>
private void WriteInt16(Stream stream, short value)
{
stream.WriteByte((byte)(value >> 8));
stream.WriteByte((byte)(value >> 0));
}
/// <summary>
/// Writes a 32bit unsigned integer in big-endian format.
/// </summary>
/// <param name="stream">The stream to write the data to.</param>
/// <param name="value">The value to write</param>
private void WriteInt32(Stream stream, int value)
{
stream.WriteByte((byte)((value & 0xFF000000) >> 24));
stream.WriteByte((byte)((value & 0x00FF0000) >> 16));
stream.WriteByte((byte)((value & 0x0000FF00) >> 8));
stream.WriteByte((byte)((value & 0x000000FF) >> 0));
}
private void WritePalette(Stream stream, ICollection<Color> palette, FileVersion version, ColorSpace colorSpace)
{
int swatchIndex;
this.WriteInt16(stream, (short)version);
this.WriteInt16(stream, (short)palette.Count);
swatchIndex = 0;
foreach (Color color in palette)
{
short value1;
short value2;
short value3;
short value4;
swatchIndex++;
switch (colorSpace)
{
case ColorSpace.Rgb:
value1 = (short)(color.R * 256);
value2 = (short)(color.G * 256);
value3 = (short)(color.B * 256);
value4 = 0;
break;
case ColorSpace.Hsb:
value1 = (short)(color.GetHue() * 182.04);
value2 = (short)(color.GetSaturation() * 655.35);
value3 = (short)(color.GetBrightness() * 655.35);
value4 = 0;
break;
case ColorSpace.Grayscale:
if (color.R == color.G && color.R == color.B)
{
// already grayscale
value1 = (short)(color.R * 39.0625);
}
else
{
// color is not grayscale, convert
value1 = (short)(((color.R + color.G + color.B) / 3.0) * 39.0625);
}
value2 = 0;
value3 = 0;
value4 = 0;
break;
default:
throw new InvalidOperationException("Color space not supported.");
}
this.WriteInt16(stream, (short)colorSpace);
this.WriteInt16(stream, value1);
this.WriteInt16(stream, value2);
this.WriteInt16(stream, value3);
this.WriteInt16(stream, value4);
if (version == FileVersion.Version2)
{
string name;
name = color.IsNamedColor ? color.Name : string.Format("Swatch {0}", swatchIndex);
this.WriteInt32(stream, name.Length);
this.WriteString(stream, name);
}
}
}
/// <summary>
/// Writes a unicode string to the specified stream in big-endian format
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
private void WriteString(Stream stream, string value)
{
stream.Write(Encoding.BigEndianUnicode.GetBytes(value), 0, value.Length * 2);
}
private void WriteSwatches(List<Color> palette, string fileName)
{
ColorSpace space;
if (grayscaleRadioButton.Checked)
space = ColorSpace.Grayscale;
else if (hslRadioButton.Checked)
space = ColorSpace.Hsb;
else
space = ColorSpace.Rgb;
using (Stream stream = File.Create(fileName))
{
this.WritePalette(stream, palette, FileVersion.Version1, space);
this.WritePalette(stream, palette, FileVersion.Version2, space);
}
}
#endregion
#region Event Handlers
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
using (Form dialog = new AboutDialog())
dialog.ShowDialog(this);
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void refreshButton_Click(object sender, EventArgs e)
{
this.GenerateAndSavePalette();
}
private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
{
this.GenerateAndSavePalette();
}
private void rgbRadioButton_CheckedChanged(object sender, EventArgs e)
{
this.SaveAndComparePalette();
}
#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