Download AdobeSwatchExchangeLoader-v2.zip version 2.0.0.0, last updated 21/10/2015 (72.79 KB)

Download
  • md5: 3591debc1f6749c23ad9bbd113dd760a
  • sha1: 9b8b0e53179b0b8d04c14324125ae5452df58305
  • sha256: 152c5051abec694120031b4b0ed923608627b0081994f99cf6d043cc1663ed20
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// Writing Adobe Swatch Exchange (ase) files using C#
// http://www.cyotek.com/blog/writing-adobe-swatch-exchange-ase-files-using-csharp

namespace AdobeSwatchExchangeLoader
{
  [DefaultEvent("TopRowChanged")]
  internal sealed class AseHexViewer : Control
  {
    #region Constants

    private const TextFormatFlags _flags =
      TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.NoPrefix | TextFormatFlags.NoPadding;

    private readonly List<ByteGroup> _groups;

    private readonly VScrollBar _scrollBar;

    #endregion

    #region Fields

    private int _cellOffset;

    private Size _cellSize;

    private int _columns;

    private string _fileName;

    private int _topRow;

    private int _totalBytes;

    #endregion

    #region Constructors

    public AseHexViewer()
    {
      this.DoubleBuffered = true;
      this.SetStyle(ControlStyles.Selectable | ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, false);
      _groups = new List<ByteGroup>();

      _scrollBar = new VScrollBar();
      _scrollBar.Scroll += this.ScrollBarScrollHandler;
      _scrollBar.ValueChanged += this.ScrollBarScrollHandler;
      this.Controls.Add(_scrollBar);

      this.BackColor = SystemColors.Window;
      this.Font = new Font("Courier New", 9.75F);
      this.Padding = this.DefaultPadding;
      this.DefineCellSize();
    }

    #endregion

    #region Events

    /// <summary>
    /// Occurs when the TopRow property value changes
    /// </summary>
    [Category("Property Changed")]
    public event EventHandler TopRowChanged;

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets the background color for the control.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Drawing.Color"/> that represents the background color of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultBackColor"/> property.
    /// </returns>
    /// <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/></PermissionSet>
    [DefaultValue(typeof(Color), "Window")]
    public override Color BackColor
    {
      get { return base.BackColor; }
      set { base.BackColor = value; }
    }

    [Browsable(false)]
    public string FileName
    {
      get { return _fileName; }
      set
      {
        _fileName = value;

        this.Load();
      }
    }

    /// <summary>
    /// Gets or sets the font of the text displayed by the control.
    /// </summary>
    /// <returns>
    /// The <see cref="T:System.Drawing.Font"/> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont"/> property.
    /// </returns>
    /// <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/></PermissionSet>
    [DefaultValue(typeof(Font), "Courier New, 9.75pt")]
    public override Font Font
    {
      get { return base.Font; }
      set { base.Font = value; }
    }

    [DefaultValue(typeof(Padding), "6, 6, 6, 6")]
    public new Padding Padding
    {
      get { return base.Padding; }
      set { base.Padding = value; }
    }

    [Browsable(false)]
    public int Rows { get; private set; }

    [Browsable(false)]
    public int TopRow
    {
      get { return _topRow; }
      set
      {
        if (this.TopRow != value)
        {
          _topRow = value;

          this.OnTopRowChanged(EventArgs.Empty);
        }
      }
    }

    /// <summary>
    /// Gets the internal spacing, in pixels, of the contents of a control.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Windows.Forms.Padding"/> that represents the internal spacing of the contents of a control.
    /// </returns>
    protected override Padding DefaultPadding
    {
      get { return new Padding(6); }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control"/> and its child controls and optionally releases the managed resources.
    /// </summary>
    /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.Controls.Remove(_scrollBar);
        _scrollBar.Scroll -= this.ScrollBarScrollHandler;
        _scrollBar.ValueChanged -= this.ScrollBarScrollHandler;
        _scrollBar.Dispose();
      }

      base.Dispose(disposing);
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.FontChanged"/> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data. </param>
    protected override void OnFontChanged(EventArgs e)
    {
      base.OnFontChanged(e);

      this.DefineCellSize();
      this.Invalidate();
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.MouseWheel"/> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data. </param>
    protected override void OnMouseWheel(MouseEventArgs e)
    {
      int value;

      base.OnMouseWheel(e);

      value = _scrollBar.Value + -e.Delta;

      if (value < 0)
      {
        value = 0;
      }

      if (value > _scrollBar.Maximum)
      {
        value = _scrollBar.Maximum;
      }

      _scrollBar.Value = value;
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.PaddingChanged"/> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
    protected override void OnPaddingChanged(EventArgs e)
    {
      base.OnPaddingChanged(e);

      this.Invalidate();
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data. </param>
    protected override void OnPaint(PaintEventArgs e)
    {
      Point location;

      base.OnPaint(e);

      e.Graphics.Clear(this.BackColor);

      location = new Point(this.Padding.Left, this.Padding.Top + -(_cellSize.Height * this.TopRow));

      foreach (ByteGroup group in _groups)
      {
        location = this.PaintGroup(e.Graphics, group, location);

        if (location.Y > this.ClientSize.Height)
        {
          // simple control, no scrolling so just abort if painting off-screen
          break;
        }
      }

      e.Graphics.DrawRectangle(SystemPens.ControlDark, 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Resize"/> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data. </param>
    protected override void OnResize(EventArgs e)
    {
      base.OnResize(e);

      if (_scrollBar != null)
      {
        int w;
        Size size;

        w = SystemInformation.VerticalScrollBarWidth;
        size = this.ClientSize;

        _scrollBar.SetBounds(size.Width - (w + 1), 1, w, size.Height - 2);
      }

      this.DefineRowsAndColumns();
      this.Invalidate();
    }

    private void DefineCellSize()
    {
      using (Graphics g = this.CreateGraphics())
      {
        _cellSize = TextRenderer.MeasureText(g, "9", this.Font, Size.Empty, _flags);
        _cellOffset = TextRenderer.MeasureText(g, " ", this.Font, Size.Empty, _flags).
                                   Width;
      }
    }

    private void DefineRowsAndColumns()
    {
      Size size;
      int visibleRows;

      size = this.ClientSize;

      visibleRows = (size.Height - this.Padding.Vertical) / _cellSize.Height;

      if (_totalBytes > 0)
      {
        _columns = (size.Width - (SystemInformation.VerticalScrollBarWidth + this.Padding.Horizontal)) /
                   (_cellSize.Width + (_cellOffset * 2));
        this.Rows = _totalBytes / _columns;

        if (this.Rows > visibleRows)
        {
          _scrollBar.Maximum = this.Rows - visibleRows;
        }
      }
      else
      {
        this.Rows = 0;
      }

      _scrollBar.LargeChange = visibleRows;
      _scrollBar.Enabled = this.Rows > visibleRows;
    }

    private int GetUInt16(IList<byte> buffer)
    {
      return (buffer[0] << 8) | buffer[1];
    }

    private int GetUInt32(IList<byte> buffer)
    {
      return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
    }

    private void Load()
    {
      _groups.Clear();

      if (!string.IsNullOrEmpty(_fileName))
      {
        using (Stream stream = File.OpenRead(_fileName))
        {
          byte[] signature;
          byte[] majorVersion;
          byte[] minorVersion;

          signature = this.Read(stream, 4);
          majorVersion = this.Read(stream, 2);
          minorVersion = this.Read(stream, 2);

          if (signature[0] == 'A' && signature[1] == 'S' && signature[2] == 'E' && signature[3] == 'F' &&
              this.GetUInt16(majorVersion) == 1 && this.GetUInt16(minorVersion) == 0)
          {
            byte[] blockCount;
            int trailingDataLength;

            // ase file
            _groups.Add(new ByteGroup(Color.Black, Color.White, signature));
            _groups.Add(new ByteGroup(Color.Navy, Color.White, majorVersion));
            _groups.Add(new ByteGroup(Color.Navy, Color.White, minorVersion));

            blockCount = this.Read(stream, 4);

            _groups.Add(new ByteGroup(Color.Orange, Color.White, blockCount));

            for (int i = 0; i < this.GetUInt32(blockCount); i++)
            {
              BlockType actualBlockType;
              byte[] blockType;
              byte[] blockLength;
              int offset;
              int otherLength;
              int actualBlockLength;

              blockType = this.Read(stream, 2);
              blockLength = this.Read(stream, 4);
              actualBlockType = (BlockType)this.GetUInt16(blockType);
              actualBlockLength = this.GetUInt32(blockLength);
              offset = 0;

              _groups.Add(new ByteGroup(Color.LightGray, Color.Black, blockType));
              _groups.Add(new ByteGroup(Color.Gold, Color.Black, blockLength));

              if (actualBlockType == BlockType.Color || actualBlockType == BlockType.GroupStart)
              {
                byte[] nameLength;
                byte[] name;

                nameLength = this.Read(stream, 2);
                name = this.Read(stream, this.GetUInt16(nameLength) * 2);
                _groups.Add(new ByteGroup(Color.Yellow, Color.Black, nameLength));
                _groups.Add(new ByteGroup(Color.YellowGreen, Color.Black, name));

                offset += (name.Length + nameLength.Length);
              }

              if (actualBlockType == BlockType.Color)
              {
                byte[] colorModel;
                byte[] colorType;
                string actualColorModel;
                int valueCount;

                colorModel = this.Read(stream, 4);
                actualColorModel = Encoding.ASCII.GetString(colorModel);

                _groups.Add(new ByteGroup(Color.Tomato, Color.Black, colorModel));

                switch (actualColorModel)
                {
                  case "RGB ":
                  case "LAB ":
                    valueCount = 3;
                    break;
                  case "Gray":
                    valueCount = 1;
                    break;
                  case "CMYK":
                    valueCount = 1;
                    break;
                  default:
                    valueCount = 0;
                    break;
                }

                for (int j = 0; j < valueCount; j++)
                {
                  byte[] value;

                  value = this.Read(stream, 4);
                  offset += value.Length;

                  _groups.Add(new ByteGroup(Color.LightSalmon, Color.Black, value));
                }

                colorType = this.Read(stream, 2);

                _groups.Add(new ByteGroup(Color.PaleVioletRed, Color.Black, colorType));

                offset += (colorModel.Length + colorType.Length);
              }

              otherLength = actualBlockLength - offset;
              if (otherLength > 0)
              {
                byte[] other;

                other = this.Read(stream, otherLength);
                _groups.Add(new ByteGroup(Color.White, Color.LightGray, other));
              }
            }

            // stuff at end of file?
            trailingDataLength = (int)(stream.Length - stream.Position);
            if (trailingDataLength != 0)
            {
              byte[] trailingData;

              trailingData = this.Read(stream, trailingDataLength);
              _groups.Add(new ByteGroup(Color.White, Color.LightGray, trailingData));
            }
          }
          else
          {
            // some other file
            _groups.Add(new ByteGroup(Color.White, Color.LightGray, File.ReadAllBytes(_fileName)));
          }
        }
      }

      _totalBytes = _groups.Sum(group => group.Data.Length);

      this.DefineRowsAndColumns();
      this.Invalidate();
    }

    /// <summary>
    /// Raises the <see cref="TopRowChanged" /> event.
    /// </summary>
    /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
    private void OnTopRowChanged(EventArgs e)
    {
      EventHandler handler;

      _scrollBar.Value = this.TopRow;
      this.Invalidate();

      handler = this.TopRowChanged;

      handler?.Invoke(this, e);
    }

    private Point PaintGroup(Graphics graphics, ByteGroup group, Point location)
    {
      int x;
      int y;
      int w;
      int h;
      int offset;
      Font font;
      Size size;

      size = this.ClientSize;
      x = location.X;
      y = location.Y;
      offset = _cellSize.Width + _cellOffset;
      w = size.Width - this.Padding.Horizontal;
      h = size.Height;
      font = this.Font;

      using (Brush brush = new SolidBrush(group.BackColor))
      {
        for (int i = 0; i < group.Data.Length; i++)
        {
          Rectangle bounds;

          bounds = new Rectangle(x, y, offset, _cellSize.Height);

          if (bounds.Bottom > 0)
          {
            graphics.FillRectangle(brush, bounds);

            TextRenderer.DrawText(graphics, group.Data[i].ToString("X2"), font, bounds, group.ForeColor, group.BackColor,
                                  _flags);

            if (i < group.Data.Length - 1 && x + offset + _cellOffset + _cellSize.Width < w)
            {
              graphics.FillRectangle(brush, x + offset, y, _cellOffset, _cellSize.Height);
            }
          }

          x += offset + _cellOffset;
          if (x + _cellSize.Width >= w)
          {
            y += _cellSize.Height;
            x = this.Padding.Left;

            if (y > h)
            {
              break;
            }
          }
        }
      }

      return new Point(x, y);
    }

    private byte[] Read(Stream stream, int length)
    {
      byte[] buffer;

      buffer = new byte[length];

      stream.Read(buffer, 0, length);

      return buffer;
    }

    private void ScrollBarScrollHandler(object sender, EventArgs e)
    {
      this.TopRow = _scrollBar.Value;
    }

    #endregion

    #region Nested type: ByteGroup

    private sealed class ByteGroup
    {
      #region Constructors

      public ByteGroup(Color backColor, Color foreColor, byte[] data)
      {
        this.BackColor = backColor;
        this.ForeColor = foreColor;
        this.Data = data;
      }

      #endregion

      #region Properties

      public Color BackColor { get; set; }

      public byte[] Data { get; set; }

      public Color ForeColor { get; set; }

      #endregion
    }

    #endregion
  }
}

Donate

Donate