CHADecoder

CHADecoder is a small class that can read and decode Adobe's CHA files used in Photoshop's Channel Mixer.

The format

The CHA format is actually quite simple. All files are exactly 44 bytes long. All values are in a WORD (or SHORT, 16 bit signed integer).

All values are sequentially ordered inside the file at a specific offset.

Offset Length (in bytes) Description
0x00 4 Header
Red
0x04 2 Red
0x06 2 Green
0x08 2 Blue
0x10 2 Always 0
0x0C 2 Constant
Green
0x0E 2 Red
0x10 2 Green
0x12 2 Blue
0x14 2 Always 0
0x16 2 Constant
Blue
0x18 2 Red
0x1A 2 Green
0x1C 2 Blue
0x1E 2 Always 0
0x20 2 Constant
 
0x22 10 Footer

The code

Below is a implementation of a CHA reader class in C#. Feel free to port this to other programming languages.

Download file: CHADecoder.cs

/*
 * Created 2018 by Jonas Kohl
 * http://jonaskohl.de/
 */

using System;
using System.IO;

namespace JonasKohl.Data
{
    /// <summary>
    /// A class for decoding Adobe's Photoshop CHA files (used in the "Channel Mixer")
    /// </summary>
    class CHADecoder
    {
        /// <summary>
        /// The offset in the file for the values. All values are short words (2 Byte/16 Bit, signed).
        /// </summary>
        public enum Offset
        {
            Red_R   = 0x04,
            Red_G   = 0x06,
            Red_B   = 0x08,
            Red_C   = 0x0C,
            Green_R = 0x0E,
            Green_G = 0x10,
            Green_B = 0x12,
            Green_C = 0x16,
            Blue_R  = 0x18,
            Blue_G  = 0x1A,
            Blue_B  = 0x1C,
            Blue_C  = 0x20
        }

        /// <summary>
        /// The data of the file currently loaded.
        /// </summary>
        public byte[] Data { get; private set; }

        /// <summary>
        /// Creates a new CHA decoder.
        /// </summary>
        /// <param name="data">The data of the file to decode</param>
        public CHADecoder(byte[] data)
        {
            Data = data;
        }

        /// <summary>
        /// Creates a new CHA decoder.
        /// </summary>
        /// <param name="fileName">The name of the file to decode</param>
        public CHADecoder(string fileName)
        {
            Data = File.ReadAllBytes(fileName);
        }

        /// <summary>
        /// Get data at a specific offset in the file
        /// </summary>
        /// <param name="offset">The offset of the data</param>
        /// <returns>A signed short word which represents the value for the given offset.</returns>
        public short GetData(Offset offset)
        {
            // Convert the enum value to an integer
            int _offset = (int)offset;

            // Get the bytes
            byte low  = Data[_offset];
            byte high = Data[_offset + 1];

            // Convert the bytes to a short word
            if (BitConverter.IsLittleEndian)
                return BitConverter.ToInt16(new byte[2] { high, low }, 0);
            else
                return BitConverter.ToInt16(new byte[2] { low, high }, 0);
        }
    }
}

Below is an implementation of a CHA reader in Python 3.x.

Download file: CHADecoder.py

#!/usr/bin/python

from struct import unpack
import sys

Red_R   = 0x04
Red_G   = 0x06
Red_B   = 0x08
Red_C   = 0x0C
Green_R = 0x0E
Green_G = 0x10
Green_B = 0x12
Green_C = 0x16
Blue_R  = 0x18
Blue_G  = 0x1A
Blue_B  = 0x1C
Blue_C  = 0x20

def get_bytes_from_file(filename):
    return open(filename, "rb").read()

def get_short(low, high):
    barray = bytes([low, high])
    if sys.byteorder == 'little':
        return unpack('>'+'h'*(len(barray)//2),barray)
    else:
        return unpack('<'+'h'*(len(barray)//2),barray)

def get_short_at(data, offset):
    low = data[offset]
    high = data[offset+1]
    return get_short(low, high)

file_name = "TEST.cha"

data = get_bytes_from_file(file_name)

print("Red:")
print("  R: %i" % get_short_at(data, Red_R))
print("  G: %i" % get_short_at(data, Red_G))
print("  B: %i" % get_short_at(data, Red_B))
print("  C: %i" % get_short_at(data, Red_C))
print("Green:")
print("  R: %i" % get_short_at(data, Green_R))
print("  G: %i" % get_short_at(data, Green_G))
print("  B: %i" % get_short_at(data, Green_B))
print("  C: %i" % get_short_at(data, Green_C))
print("Blue:")
print("  R: %i" % get_short_at(data, Blue_R))
print("  G: %i" % get_short_at(data, Blue_G))
print("  B: %i" % get_short_at(data, Blue_B))
print("  C: %i" % get_short_at(data, Blue_C))

This page was created by Jonas Kohl