/*
 * FSColorTransform.java
 * Transform
 * 
 * Copyright (c) 2001-2008 Flagstone Software Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *  * Neither the name of Flagstone Software Ltd. nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software 
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.flagstone.transform;

/**
 * The FSColorTransform is used to change the colour of a shape or button
 * without changing the values in the original definition of the object.
 * 
 * <p>
 * Two types of transformation are supported: <b>Add</b> and <b>Multiply</b>
 * </p>
 * 
 * <p>
 * In Add transformations a value is added to each colour channel:
 * </p>
 * 
 * <pre>
 *  newRed = red + addRedTerm
 *  newGreen = green + addGreenTerm
 *  newBlue = blue + addBlueTerm
 *  newAlpha = alpha + addAlphaTerm
 * </pre>
 * 
 * <p>
 * In Multiply transformations each colour channel is multiplied by a given
 * value:
 * </p>
 * 
 * <pre>
 *  newRed = red * multiplyRedTerm
 *  newGreen = green * multiplyGreenTerm
 *  newBlue = blue * multiplyBlueTerm
 *  newAlpha = alpha * multiplyAlphaTerm
 * </pre>
 * 
 * <p>
 * Add and Multiply transforms may be combined in which case the multiply terms
 * are applied to the colour channel before the add terms.
 * </p>
 * 
 * <pre>
 *  newRed = (red * multiplyRedTerm) + addRedTerm
 *  newGreen = (green * multiplyGreenTerm) + addGreenTerm
 *  newBlue = (blue * multiplyBlueTerm) + addBlueTerm
 *  newAlpha = (alpha * multiplyAlphaTerm) + addAlphaTerm
 * </pre>
 * 
 * <p>
 * For each type of transform the result of the calculation is limited to the
 * range 0..255. If the result is less than 0 or greater than 255 then it is
 * clamped at 0 and 255 respectively.
 * </p>
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>multiplyRed</td>
 * <td>The multiply term for the red colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>multiplyGreen</td>
 * <td>The multiply term for the green colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>multiplyBlue</td>
 * <td>The multiply term for the blue colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>multiplyAlpha</td>
 * <td>The optional multiply term for the alpha colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>addRed</td>
 * <td>The add term for the red colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>addGreen</td>
 * <td>The add term for the green colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>addBlue</td>
 * <td>The add term for the blue colour channel.</td>
 * </tr>
 * 
 * <tr>
 * <td>addAlpha</td>
 * <td>The optional add term for the alpha colour channel.</td>
 * </tr>
 * 
 * </table>
 * 
 * <p>
 * Not all objects containing a colour transform use the add or multiply terms
 * defined for the alpha channel. The colour objects defined in an
 * FSDefineButton, FSButtonColorTransform or FSPlaceObject object do not use the
 * alpha channel. Transparent colours was introduced in Flash 3. The "parent"
 * object that contains the colour transform controls whether the alpha channel
 * information is encoded or not. Simplifying the alpha channel is not
 * sufficient.
 * </p>
 * 
 * <h1 class="datasheet">Examples</h1>
 * 
 * <p>
 * The class provides a range of constructors to support the different colour
 * transformations. To create a colour transform object specify the values for
 * colour channels in the following order: red, green, blue and optionally
 * alpha:
 * </p>
 * 
 * <pre>
 * float multRed = 1.5f;
 * 
 * float multGreen = 1.5f;
 * 
 * float multBlue = 1.5f;
 * 
 * float multAlpha = 1.5f;
 * 
 * FSColorTransform multiply = new FSColorTransform(multRed, multGreen, multBlue);
 * 
 * FSColorTransform multiplyWithAlpha = new FSColorTransform(multRed, multGreen,
 * 				multBlue, multAlpha);
 * </pre>
 * 
 * <pre>
 * int addRed = 128;
 * 
 * int addGreen = 128;
 * 
 * int addBlue = 128;
 * 
 * int addAlpha = 128;
 * 
 * FSColorTransform add = new FSColorTransform(addRed, addGreen, addBlue);
 * 
 * FSColorTransform addWithAlpha = new FSColorTransform(addRed, addGreen, addBlue,
 * 				addAlpha);
 * </pre>
 * 
 * <p>
 * For transforms combining both multiply and add terms:
 * </p>
 * 
 * <pre>
 * int addRed = 128;
 * 
 * int addGreen = 128;
 * 
 * int addBlue = 128;
 * 
 * int addAlpha = 128;
 * 
 * float multRed = 1.5f;
 * 
 * float multGreen = 1.5f;
 * 
 * float multBlue = 1.5f;
 * 
 * float multAlpha = 1.5f;
 * 
 * FSColorTransform combined = new FSColorTransform(multRed, multGreen, multBlue,
 * 				addRed, addGreen, addBlue);
 * 
 * FSColorTransform combinedWithAlpha = new FSColorTransform(multRed, multGreen,
 * 				multBlue, multAlpha, addRed, addGreen, addBlue, addAlpha);
 * </pre>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * The FSCoordTransform class represents the CXForm and CXFormWithAlpha data
 * structures from the Macromedia Flash (SWF) File Format Specification. They
 * were introduced in Flash 1 and Flash 3 respectively.
 * </P>
 */
public class FSColorTransform extends FSTransformObject
{
	private float multiplyRed = 1.0f;
	private float multiplyGreen = 1.0f;
	private float multiplyBlue = 1.0f;
	private float multiplyAlpha = 1.0f;
	private int addRed = 0;
	private int addGreen = 0;
	private int addBlue = 0;
	private int addAlpha = 0;

	/**
	 * Construct an FSColorTransform object an initialise with the values
	 * decoded from an encoded colour transform
	 * 
	 * @param coder
	 *            an FSCoder object containing the encoded colour transform.
	 */
	public FSColorTransform(FSCoder coder)
	{
		decode(coder);
	}

	/**
	 * Constructs an FSColorTransform object defining a unity transform. If the
	 * transform is applied to a shape its colour will not change.
	 */
	public FSColorTransform()
	{
	}

	/**
	 * Constructs an FSColorTransform object with the specified add terms.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = red + addRedTerm
	 * 	    newGreen = green + addGreenTerm
	 * 	    newBlue = blue + addBlueTerm
	 * 	
	 * </pre>
	 * 
	 * The add term value for the alpha channel defaults to 0 so if the
	 * transform is added to an object that uses alpha channel information then
	 * the alpha channel will remain unchanged.
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param redTerm
	 *            value to add to the red colour channel.
	 * @param greenTerm
	 *            value to add to the green colour channel.
	 * @param blueTerm
	 *            value to add to the blue colour channel.
	 */
	public FSColorTransform(int redTerm, int greenTerm, int blueTerm)
	{
		setAddRed(redTerm);
		setAddGreen(greenTerm);
		setAddBlue(blueTerm);
	}

	/**
	 * Constructs an FSColorTransform object with the specified add terms,
	 * including the alpha channel.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = red + addRedTerm
	 * 	    newGreen = green + addGreenTerm
	 * 	    newBlue = blue + addBlueTerm
	 * 	    newAlpha = alpha + addAlphaTerm
	 * 	
	 * </pre>
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param redTerm
	 *            value to add to the red colour channel.
	 * @param greenTerm
	 *            value to add to the green colour channel.
	 * @param blueTerm
	 *            value to add to the blue colour channel.
	 * @param alphaTerm
	 *            value to add to the alpha colour channel.
	 */
	public FSColorTransform(int redTerm, int greenTerm, int blueTerm,
							int alphaTerm)
	{
		setAddRed(redTerm);
		setAddGreen(greenTerm);
		setAddBlue(blueTerm);
		setAddAlpha(alphaTerm);
	}

	/**
	 * Constructs a FSColorTransform object initialised with the specified
	 * multiply terms.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = red * multiplyRedTerm
	 * 	    newGreen = green * multiplyGreenTerm
	 * 	    newBlue = blue * multiplyBlueTerm
	 * 	
	 * </pre>
	 * 
	 * The multiply term value for the alpha channel defaults to 1.0 so if the
	 * transform is added to an object that uses alpha channel information then
	 * the alpha channel will remain unchanged.
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param redTerm
	 *            value to multiply the red colour channel by.
	 * @param greenTerm
	 *            value to multiply the green colour channel by.
	 * @param blueTerm
	 *            value to multiply the blue colour channel by.
	 */
	public FSColorTransform(float redTerm, float greenTerm, float blueTerm)
	{
		setMultiplyRed(redTerm);
		setMultiplyGreen(greenTerm);
		setMultiplyBlue(blueTerm);
	}

	/**
	 * Constructs a FSColorTransform object initialised with the specified
	 * multiply terms, including the alpha channel.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = red * multiplyRedTerm
	 * 	    newGreen = green * multiplyGreenTerm
	 * 	    newBlue = blue * multiplyBlueTerm
	 * 	    newAlpha = alpha * multiplyAlphaTerm
	 * 	
	 * </pre>
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param redTerm
	 *            value to multiply the red colour channel by.
	 * @param greenTerm
	 *            value to multiply the green colour channel by.
	 * @param blueTerm
	 *            value to multiply the blue colour channel by.
	 * @param alphaTerm
	 *            value to multiply the alpha colour channel by.
	 */
	public FSColorTransform(float redTerm, float greenTerm, float blueTerm,
							float alphaTerm)
	{
		setMultiplyRed(redTerm);
		setMultiplyGreen(greenTerm);
		setMultiplyBlue(blueTerm);
		setMultiplyAlpha(alphaTerm);
	}

	/**
	 * Constructs a FSColorTransform object initialised with the specified
	 * addition and multiplication terms.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = (red * multiplyRedTerm) + addRedTerm
	 * 	    newGreen = (green * multiplyGreenTerm) + addGreenTerm
	 * 	    newBlue = (blue * multiplyBlueTerm) + addBlueTerm
	 * 	
	 * </pre>
	 * 
	 * The multiply and add terms for the alpha channel default to 1.0 and 0
	 * respectively so for objects that use the alpha channel information then
	 * the alpha channel will remain unchanged.
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param multRedTerm
	 *            value to multiply the red colour channel by.
	 * @param multGreenTerm
	 *            value to multiply the green colour channel by.
	 * @param multBlueTerm
	 *            value to multiply the blue colour channel by.
	 * @param addRedTerm
	 *            value to add to the red colour channel.
	 * @param addGreenTerm
	 *            value to add to the green colour channel.
	 * @param addBlueTerm
	 *            value to add to the blue colour channel.
	 */
	public FSColorTransform(float multRedTerm, float multGreenTerm,
							float multBlueTerm, int addRedTerm,
							int addGreenTerm, int addBlueTerm)
	{
		setMultiplyRed(multRedTerm);
		setMultiplyGreen(multGreenTerm);
		setMultiplyBlue(multBlueTerm);
		setAddRed(addRedTerm);
		setAddGreen(addGreenTerm);
		setAddBlue(addBlueTerm);
	}

	/**
	 * Constructs a FSColorTransform object initialised with the specified
	 * addition and multiplication terms, including alpha channel values.
	 * 
	 * Each colour channel is transformed by the following calculation:
	 * 
	 * <pre>
	 * 	    newRed = (red * multiplyRedTerm) + addRedTerm
	 * 	    newGreen = (green * multiplyGreenTerm) + addGreenTerm
	 * 	    newBlue = (blue * multiplyBlueTerm) + addBlueTerm
	 * 	    newAlpha = (alpha * multiplyAlphaTerm) + addAlphaTerm
	 * 	
	 * </pre>
	 * 
	 * In the Flash Player the value assigned to the respective colour channel
	 * is clamped to the range 0..255.
	 * 
	 * @param multRedTerm
	 *            value to multiply the red colour channel by.
	 * @param multGreenTerm
	 *            value to multiply the green colour channel by.
	 * @param multBlueTerm
	 *            value to multiply the blue colour channel by.
	 * @param multAlphaTerm
	 *            value to multiply the alpha colour channel by.
	 * @param addRedTerm
	 *            value to add to the red colour channel.
	 * @param addGreenTerm
	 *            value to add to the green colour channel.
	 * @param addBlueTerm
	 *            value to add to the blue colour channel.
	 * @param addAlphaTerm
	 *            value to add to the alpha colour channel.
	 */
	public FSColorTransform(float multRedTerm, float multGreenTerm,
							float multBlueTerm, float multAlphaTerm,
							int addRedTerm, int addGreenTerm, int addBlueTerm,
							int addAlphaTerm)
	{
		setMultiplyRed(multRedTerm);
		setMultiplyGreen(multGreenTerm);
		setMultiplyBlue(multBlueTerm);
		setMultiplyAlpha(multAlphaTerm);
		setAddRed(addRedTerm);
		setAddGreen(addGreenTerm);
		setAddBlue(addBlueTerm);
		setAddAlpha(addAlphaTerm);
	}

	/**
	 * Construct an FSColorTansform object and initialize it using a copy of the
	 * values from another colour transform.
	 * 
	 * @param obj
	 *            a colour transform.
	 */
	public FSColorTransform(FSColorTransform obj)
	{
		multiplyRed = obj.multiplyRed;
		multiplyGreen = obj.multiplyGreen;
		multiplyBlue = obj.multiplyBlue;
		multiplyAlpha = obj.multiplyAlpha;
		addRed = obj.addRed;
		addGreen = obj.addGreen;
		addBlue = obj.addBlue;
		addAlpha = obj.addAlpha;
	}

	/**
	 * Gets the value of the multiply term for the red channel.
	 * 
	 * @return the value that will be multiplied with the red colour channel's
	 *         value.
	 */
	public float getMultiplyRed()
	{
		return multiplyRed;
	}

	/**
	 * Gets the value of the multiply term for the green channel.
	 * 
	 * @return the value that will be multiplied with the green colour channel's
	 *         value.
	 */
	public float getMultiplyGreen()
	{
		return multiplyGreen;
	}

	/**
	 * Gets the value of the multiply term for the blue channel.
	 * 
	 * @return the value that will be multiplied with the blue colour channel's
	 *         value.
	 */
	public float getMultiplyBlue()
	{
		return multiplyBlue;
	}

	/**
	 * Gets the value of the multiply term for the alpha channel.
	 * 
	 * @return the value that will be multiplied with the alpha colour channel's
	 *         value.
	 */
	public float getMultiplyAlpha()
	{
		return multiplyAlpha;
	}

	/**
	 * Gets the value of the add term for the red channel.
	 * 
	 * @return the value that will be added to the red colour channel's value.
	 */
	public int getAddRed()
	{
		return addRed;
	}

	/**
	 * Gets the value of the add term for the green channel.
	 * 
	 * @return the value that will be added to the green colour channel's value.
	 */
	public int getAddGreen()
	{
		return addGreen;
	}

	/**
	 * Gets the value of the add term for the blue channel.
	 * 
	 * @return the value that will be added to the blue colour channel's value.
	 */
	public int getAddBlue()
	{
		return addBlue;
	}

	/**
	 * Gets the value of the add term for the alpha channel.
	 * 
	 * @return the value that will be added to the alpha colour channel's value.
	 */
	public int getAddAlpha()
	{
		return addAlpha;
	}

	/**
	 * Sets the value for the multiplyTerm which will be applied to the red
	 * colour channel.
	 * 
	 * @param aNumber
	 *            the value to be multiplied with the red colour channel's
	 *            value.
	 */
	public void setMultiplyRed(float aNumber)
	{
		multiplyRed = aNumber;
	}

	/**
	 * Sets the value for the multiplyTerm which will be applied to the green
	 * colour channel.
	 * 
	 * @param aNumber
	 *            the value to be multiplied with the green colour channel's
	 *            value.
	 */
	public void setMultiplyGreen(float aNumber)
	{
		multiplyGreen = aNumber;
	}

	/**
	 * Sets the value for the multiplyTerm which will be applied to the blue
	 * colour channel.
	 * 
	 * @param aNumber
	 *            the value to be multiplied with the blue colour channel's
	 *            value.
	 */
	public void setMultiplyBlue(float aNumber)
	{
		multiplyBlue = aNumber;
	}

	/**
	 * Sets the value for the multiplyTerm which will be applied to the alpha
	 * colour channel.
	 * 
	 * @param aNumber
	 *            the value to be multiplied with the alpha colour channel's
	 *            value.
	 */
	public void setMultiplyAlpha(float aNumber)
	{
		multiplyAlpha = aNumber;
	}

	/**
	 * Sets the values for the multiply terms for the red, green and blue colour
	 * channels
	 * 
	 * @param redTerm
	 *            value to multiply the red colour channel by.
	 * @param greenTerm
	 *            value to multiply the green colour channel by.
	 * @param blueTerm
	 *            value to multiply the blue colour channel by.
	 */
	public void setMultiplyTerms(float redTerm, float greenTerm, float blueTerm)
	{
		setMultiplyRed(redTerm);
		setMultiplyGreen(greenTerm);
		setMultiplyBlue(blueTerm);
	}

	/**
	 * Sets the values for the multiply terms for each of the colour channels
	 * 
	 * @param redTerm
	 *            value to multiply the red colour channel by.
	 * @param greenTerm
	 *            value to multiply the green colour channel by.
	 * @param blueTerm
	 *            value to multiply the blue colour channel by.
	 * @param alphaTerm
	 *            value to multiply the alpha colour channel by.
	 */
	public void setMultiplyTerms(float redTerm, float greenTerm,
									float blueTerm, float alphaTerm)
	{
		setMultiplyRed(redTerm);
		setMultiplyGreen(greenTerm);
		setMultiplyBlue(blueTerm);
		setMultiplyAlpha(alphaTerm);
	}

	/**
	 * Sets the value for the addTerm which will be applied to the red colour
	 * channel.
	 * 
	 * @param aNumber
	 *            the value to be added to the red colour channel's value.
	 */
	public void setAddRed(int aNumber)
	{
		addRed = aNumber;
	}

	/**
	 * Sets the value for the addTerm which will be applied to the green colour
	 * channel.
	 * 
	 * @param aNumber
	 *            the value to be added to the green colour channel's value.
	 */
	public void setAddGreen(int aNumber)
	{
		addGreen = aNumber;
	}

	/**
	 * Sets the value for the addTerm which will be applied to the blue colour
	 * channel.
	 * 
	 * @param aNumber
	 *            the value to be added to the blue colour channel's value.
	 */
	public void setAddBlue(int aNumber)
	{
		addBlue = aNumber;
	}

	/**
	 * Sets the value for the addTerm which will be applied to the alpha colour
	 * channel.
	 * 
	 * @param aNumber
	 *            the value to be added to the alpha colour channel's value.
	 */
	public void setAddAlpha(int aNumber)
	{
		addAlpha = aNumber;
	}

	/**
	 * Sets the values for the add terms for each of the colour channels.
	 * 
	 * @param redTerm
	 *            value to add to the red colour channel.
	 * @param greenTerm
	 *            value to add to the green colour channel.
	 * @param blueTerm
	 *            value to add to the blue colour channel.
	 * @param alphaTerm
	 *            value to add to the alpha colour channel.
	 */
	public void setAddTerms(int redTerm, int greenTerm, int blueTerm,
							int alphaTerm)
	{
		setAddRed(redTerm);
		setAddGreen(greenTerm);
		setAddBlue(blueTerm);
		setAddAlpha(alphaTerm);
	}

	/**
	 * Sets the values for the add terms for the red, green and blue colour
	 * channels.
	 * 
	 * @param redTerm
	 *            value to add to the red colour channel.
	 * @param greenTerm
	 *            value to add to the green colour channel.
	 * @param blueTerm
	 *            value to add to the blue colour channel.
	 */
	public void setAddTerms(int redTerm, int greenTerm, int blueTerm)
	{
		setAddRed(redTerm);
		setAddGreen(greenTerm);
		setAddBlue(blueTerm);
	}

	/**
	 * Returns true if anObject is equal to this one. Objects are considered
	 * equal if they would generate identical binary data when they are encoded
	 * to a Flash file.
	 * 
	 * @return true if this object would be identical to anObject when encoded.
	 */
	public boolean equals(Object anObject)
	{
		boolean result = false;

		if (super.equals(anObject))
		{
			FSColorTransform typedObject = (FSColorTransform) anObject;

			result = multiplyRed == typedObject.multiplyRed;
			result = result && multiplyGreen == typedObject.multiplyGreen;
			result = result && multiplyBlue == typedObject.multiplyBlue;
			result = result && multiplyAlpha == typedObject.multiplyAlpha;
			result = result && addRed == typedObject.addRed;
			result = result && addGreen == typedObject.addGreen;
			result = result && addBlue == typedObject.addBlue;
			result = result && addAlpha == typedObject.addAlpha;
		}
		return result;
	}

	public void appendDescription(StringBuffer buffer, int depth)
	{
		buffer.append(name());

		if (depth > 0)
		{
			buffer.append(": { ");
			buffer.append("multiply = [" + multiplyRed + ", " + multiplyGreen
							+ ", " + multiplyBlue + ", " + multiplyAlpha
							+ "]; ");
			buffer.append("add = [" + addRed + ", " + addGreen + ", " + addBlue
							+ ", " + addAlpha + "]; ");
			buffer.append("}");
		}
	}

	public int length(FSCoder coder)
	{
		int numberOfBits = 6;
		int fieldSize = fieldSize(coder);
		boolean containsMultiplyTerms = containsMultiplyTerms(coder);
		boolean containsAddTerms = containsAddTerms(coder);

		if (containsMultiplyTerms)
			numberOfBits += fieldSize
							* ((coder.context[FSCoder.TransparentColors] != 0) ? 4
											: 3);

		if (containsAddTerms)
			numberOfBits += fieldSize
							* ((coder.context[FSCoder.TransparentColors] != 0) ? 4
											: 3);

		numberOfBits += (numberOfBits % 8 > 0) ? 8 - (numberOfBits % 8) : 0;

		return numberOfBits >> 3;
	}

	public void encode(FSCoder coder)
	{
		int numberOfBits = fieldSize(coder);
		boolean containsMultiplyTerms = containsMultiplyTerms(coder);
		boolean containsAddTerms = containsAddTerms(coder);

		coder.alignToByte();

		coder.writeBits(containsAddTerms ? 1 : 0, 1);
		coder.writeBits(containsMultiplyTerms ? 1 : 0, 1);
		coder.writeBits(numberOfBits, 4);

		if (containsMultiplyTerms)
		{
			coder.writeFixedBits(multiplyRed, numberOfBits, 8);
			coder.writeFixedBits(multiplyGreen, numberOfBits, 8);
			coder.writeFixedBits(multiplyBlue, numberOfBits, 8);

			if (coder.context[FSCoder.TransparentColors] != 0)
				coder.writeFixedBits(multiplyAlpha, numberOfBits, 8);
		}
		if (containsAddTerms)
		{
			coder.writeBits(addRed, numberOfBits);
			coder.writeBits(addGreen, numberOfBits);
			coder.writeBits(addBlue, numberOfBits);

			if (coder.context[FSCoder.TransparentColors] != 0)
				coder.writeBits(addAlpha, numberOfBits);
		}
		coder.alignToByte();
	}

	public void decode(FSCoder coder)
	{
		boolean containsAddTerms = false;
		boolean containsMultiplyTerms = false;
		int fieldSize = 0;

		coder.alignToByte();

		containsAddTerms = coder.readBits(1, false) != 0 ? true : false;
		containsMultiplyTerms = coder.readBits(1, false) != 0 ? true : false;
		fieldSize = coder.readBits(4, false);

		if (containsMultiplyTerms)
		{
			multiplyRed = coder.readFixedBits(fieldSize, 8);
			multiplyGreen = coder.readFixedBits(fieldSize, 8);
			multiplyBlue = coder.readFixedBits(fieldSize, 8);

			if (coder.context[FSCoder.TransparentColors] != 0)
				multiplyAlpha = coder.readFixedBits(fieldSize, 8);
		}

		if (containsAddTerms)
		{
			addRed = coder.readBits(fieldSize, true);
			addGreen = coder.readBits(fieldSize, true);
			addBlue = coder.readBits(fieldSize, true);

			if (coder.context[FSCoder.TransparentColors] != 0)
				addAlpha = coder.readBits(fieldSize, true);
		}
		coder.alignToByte();
	}

	/**
	 * Returns true if the values for the add and multiply terms represent a
	 * unity transform - one which will not change the colour of a shape.
	 * Whether the alpha channel is included is determined by the context
	 * associated with the FSCoder object.
	 * 
	 * @param coder
	 *            the FSCoder used to encoded the transform.
	 * @return true if the object represents a unity transform, false otherwise.
	 */
	public boolean isUnityTransform(FSCoder coder)
	{
		return !(containsAddTerms(coder) || containsMultiplyTerms(coder));
	}

	private boolean containsAddTerms(FSCoder coder)
	{
		boolean containsTerms = addRed != 0 || addGreen != 0 || addBlue != 0;

		if (coder.context[FSCoder.TransparentColors] != 0)
			containsTerms = containsTerms || addAlpha != 0;

		return containsTerms;
	}

	private boolean containsMultiplyTerms(FSCoder coder)
	{
		boolean containsTerms = multiplyRed != 1.0 || multiplyGreen != 1.0
						|| multiplyBlue != 1.0;

		if (coder.context[FSCoder.TransparentColors] != 0)
			containsTerms = containsTerms || multiplyAlpha != 1.0;

		return containsTerms;
	}

	private int addFieldSize(FSCoder coder)
	{
		int[] values;
		int size = 0;

		if (coder.context[FSCoder.TransparentColors] != 0)
			values = new int[]
			{
				addRed, addGreen, addBlue, addAlpha };
		else
			values = new int[]
			{
				addRed, addGreen, addBlue };

		size = FSCoder.size(values, true);

		return size;
	}

	private int multiplyFieldSize(FSCoder coder)
	{
		float[] values;
		int size = 0;

		if (coder.context[FSCoder.TransparentColors] != 0)
			values = new float[]
			{
				multiplyRed, multiplyGreen, multiplyBlue, multiplyAlpha };
		else
			values = new float[]
			{
				multiplyRed, multiplyGreen, multiplyBlue };

		size = FSCoder.fixedShortSize(values);

		return size;
	}

	private int fieldSize(FSCoder coder)
	{
		boolean containsMultiplyTerms = containsMultiplyTerms(coder);
		boolean containsAddTerms = containsAddTerms(coder);
		int numberOfBits = 0;

		if (containsAddTerms && !containsMultiplyTerms)
			numberOfBits = addFieldSize(coder);
		else if (!containsAddTerms && containsMultiplyTerms)
			numberOfBits = multiplyFieldSize(coder);
		else if (containsAddTerms && containsMultiplyTerms)
			numberOfBits = Math.max(addFieldSize(coder),
							multiplyFieldSize(coder));
		else
			numberOfBits = 1;

		return numberOfBits;
	}
}
