Download PhotoshopColorSwatchLoader.zip version 1.0.0.0, last updated 22/01/2014 (24.13 KB)

Download
  • md5: ea73aa398ec190b0794416866e79de48
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

// Reading PhotoShop Color Swatch (aco) files using C#
// http://cyotek.com/blog/reading-photoshop-color-swatch-aco-files-using-csharp

namespace PhotoShopColorSwatchLoader
{
  internal sealed partial class MainForm : Form
  {
    #region Enums

    private enum ColorSpace
    {
      Rgb = 0,

      Hsb = 1,

      Cmyk = 2,

      Lab = 7,

      Grayscale = 8
    }

    private enum FileVersion
    {
      Version1 = 1,

      Version2
    }

    #endregion

    #region Constants

    private const int DefaultX = 6;

    private const int DefaultY = 6;

    private const int Spacing = 6;

    private const int CellSize = 24;

    #endregion

    #region Instance Fields

    private List<Color> _loadedPalette;

    #endregion

    #region Public Constructors

    public MainForm()
    {
      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);

      this.AddFiles("aco");
    }

    #endregion

    #region Private Members

    private void AddFiles(string extension)
    {
      string path;

      path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
      foreach (string fileName in Directory.GetFiles(path, "*." + extension))
        filesListBox.Items.Add(new FileInfo(fileName));
    }

    /// <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-100
            brightness = value3 / 655.35; // 0-100

            throw new InvalidDataException(string.Format("Color space '{0}' not supported.", colorSpace));

          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;
    }

    #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 filesListBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      FileInfo selectedFile;

      selectedFile = filesListBox.SelectedItem as FileInfo;

      if (selectedFile != null)
      {
        _loadedPalette = this.ReadPhotoShopSwatchFile(selectedFile.FullPath);
        this.Text = string.Format("{0} - {1}", Path.GetFileName(selectedFile.FullPath), Application.ProductName);
      }
      else
      {
        _loadedPalette = null;
        this.Text = Application.ProductName;
      }

      palettePanel.Invalidate();
    }

    private void palettePanel_Paint(object sender, PaintEventArgs e)
    {
      if (_loadedPalette != null)
      {
        int x;
        int y;

        x = DefaultX;
        y = DefaultY;

        e.Graphics.Clear(palettePanel.BackColor);

        foreach (Color color in _loadedPalette)
        {
          Rectangle bounds;

          if (x > palettePanel.Width - (CellSize + DefaultX))
          {
            x = DefaultX;
            y += DefaultY + CellSize + Spacing;
          }

          bounds = new Rectangle(x, y, CellSize, CellSize);

          using (Brush brush = new SolidBrush(color))
            e.Graphics.FillRectangle(brush, bounds);

          e.Graphics.DrawRectangle(Pens.Black, bounds);

          x += (CellSize + Spacing);
        }
      }
    }

    #endregion
  }
}

Donate

Donate