/* libsswf_style.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2008 */

/*

Copyright (c) 2002-2008 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

/** \file
 *
 * \brief The implementation of the sswf::Style class
 *
 * This file declares the body of the functions which are not
 * inline. It is part of the SSWF library.
 */

#include	"sswf/libsswf.h"

using namespace sswf;



/////////////////////////////////////////// Style


/** \class sswf::Style
 *
 * \brief Defines the style of a shape.
 *
 * When drawing a shape, one can change the style. This indicates
 * the line width and color, as well as the fill of the inside
 * of the shape.
 *
 * The Style object includes support for morphing shapes.
 *
 * \sa sswf::TagShape
 * \sa <a href="../SWFalexref.html#swf_fill_style">SWF Alexis' Reference&mdash;swf_fill_style</a>
 * \sa <a href="../SWFalexref.html#swf_fill_style_array">SWF Alexis' Reference&mdash;swf_fill_style_array</a>
 * \sa <a href="../SWFalexref.html#swf_gradient">SWF Alexis' Reference&mdash;swf_gradient</a>
 * \sa <a href="../SWFalexref.html#swf_gradient_record">SWF Alexis' Reference&mdash;swf_gradient_record</a>
 * \sa <a href="../SWFalexref.html#swf_line_style">SWF Alexis' Reference&mdash;swf_line_style</a>
 * \sa <a href="../SWFalexref.html#swf_line_style_array">SWF Alexis' Reference&mdash;swf_line_style_array</a>
 * \sa <a href="../SWFalexref.html#swf_styles">SWF Alexis' Reference&mdash;swf_styles</a>
 */




/** \enum sswf::Style::cap_t
 *
 * \brief How the line start and end.
 *
 * This enumeration lists the different possible ways the start
 * and end of a line are drawn.
 */

/** \var sswf::Style::STYLE_LINE_CAP_SAME
 *
 * \brief Use the other side cap definition. 
 *
 * When the start or end line cap is set to this value (but not
 * both) the SetLineCaps() sets both caps to the other value.
 */

/** \var sswf::Style::STYLE_LINE_CAP_ROUND
 *
 * \brief Use a round cap (half a circle.)
 *
 * This is the default and the same as the old style for the tip
 * of a line: half a circle.
 */

/** \var sswf::Style::STYLE_LINE_CAP_NONE
 *
 * \brief Do not draw any cap.
 *
 * This mode means that no cap shall be added. It looks similar
 * to the Style::STYLE_LINE_CAP_SQUARE but the line stops right
 * at the last point or edge.
 *
 * \sa sswf::Style::STYLE_LINE_CAP_SQUARE
 */

/** \var sswf::Style::STYLE_LINE_CAP_SQUARE
 *
 * \brief Draw a square (rectangle) cap.
 *
 * This mode means that the cap shall be flat (a rectangle). It
 * looks like a flat edge. Before, this could be achive with the
 * use of a rectangle to draw the line. Yet, it is easier to draw
 * a line since it connects with multiple segments and curves.
 *
 * \sa sswf::Style::STYLE_LINE_CAP_NONE
 */




/** \enum sswf::Style::join_t
 *
 * \brief How the lines segments join together.
 *
 * This enumeration lists the different possible ways two line
 * segment will be joined.
 */

/** \var sswf::Style::STYLE_LINE_JOIN_UNKNOWN
 *
 * \brief Unknown join for your own purpose.
 *
 * This mode can be used to mark that a join was not defined in
 * a variable. (It is used in sswf.)
 */

/** \var sswf::Style::STYLE_LINE_JOIN_ROUND
 *
 * \brief Draw a circle where two segments meet.
 *
 * This mode is the default. It draws rounded joins wherever two
 * line segments meet.
 */

/** \var sswf::Style::STYLE_LINE_JOIN_BEVEL
 *
 * \brief Draw a square where two segments meet.
 *
 * This mode can be used to draw a triangle to close the join.
 */

/** \var sswf::Style::STYLE_LINE_JOIN_MITER
 *
 * \brief Continue the edges until they meet.
 *
 * This mode is similar to the bevel join. It continues the edges
 * until they meet unless the specified miter limit is reached.
 */



/** \enum sswf::Style::spread_t
 *
 * \brief How the gradient is spread on the edges
 *
 * This enumeration lists the different possible ways a gradient
 * can be repeated on the edges.
 */

/** \var sswf::Style::STYLE_GRADIENT_SPREAD_PAD
 *
 * \brief Draw the edge color on the sides.
 *
 * This mode is the default mode which what was used in the
 * previous gradients.
 */

/** \var sswf::Style::STYLE_GRADIENT_SPREAD_REFLECT
 *
 * \brief Draw the gradient and mirror.
 *
 * In this mode, the edges are rendered going backward into
 * the gradient, ping-ponging as many times as necessary.
 */

/** \var sswf::Style::STYLE_GRADIENT_SPREAD_REPEAT
 *
 * \brief Draw the gradient multiple times.
 *
 * In this mode, the edges are rendered restarting on the other
 * side of the gradient as many times as necessary.
 */



/** \enum sswf::Style::interpolation_t
 *
 * \brief How the gradient colors are computed
 *
 * This enumeration lists the different possible ways a gradient
 * colors can be computed.
 */

/** \var sswf::Style::STYLE_GRADIENT_INTERPOLATION_NORMAL
 *
 * \brief Draw in normal RGB mode, taking the luminance in account.
 *
 * The normal interpolation mode is what was used before for gradients.
 * It means that the luminance is taken in account whenever computing
 * the intermediate colors.
 */

/** \var sswf::Style::STYLE_GRADIENT_INTERPOLATION_LINEAR
 *
 * \brief Draw in linear RGB mode.
 *
 * The linear interpolation mode is very fast and computes the different
 * colors with a very simple line equation (x = ay + b).
 */




/** \enum sswf::Style::style_t
 *
 * \brief The enumeration of all the supported types
 *
 * This is the list of styles supported by the SSWF library.
 */

/** \var sswf::Style::STYLE_TYPE_UNKNOWN
 *
 * \brief Until set, the type of a style is unknown.
 *
 * By default, this type is the type of a style.
 */

/** \var sswf::Style::STYLE_TYPE_NO_LINE
 *
 * \brief Used to remove a previous line style.
 *
 * This special type is used to create a style used to remove a previous
 * line style. When creating a shape, you can have a line, then a fill
 * without line. In that case, you need to remove the line, This is done
 * with a style of this type.
 *
 * \sa sswf::Style::STYLE_TYPE_NO_FILL
 */

/** \var sswf::Style::STYLE_TYPE_NO_FILL
 *
 * \brief Used to remove a previous fill style.
 *
 * This special type is used to create a style used to remove a previous
 * fill style. When creating a shape, you can have a fill, then a line
 * without fill. In that case, you need to remove the fill, This is done
 * with a style of this type.
 *
 * \sa sswf::Style::STYLE_TYPE_NO_LINE
 */

/** \var sswf::Style::STYLE_TYPE_LINE
 *
 * \brief Draw a simple solid color line.
 *
 * This style defines a line thickness and color. It can be used for a morphing shape.
 *
 * \sa sswf::Style::STYLE_TYPE_ENHANCED_LINE
 */

/** \var sswf::Style::STYLE_TYPE_ENHANCED_LINE
 *
 * \brief Draw a line.
 *
 * This style defines a line thickness, color, caps, joins, scaling reaction,
 * hinting, closing behavior and fill style.
 *
 * Support for this type of line appeared in SWF version 8.
 *
 * \sa sswf::Style::STYLE_TYPE_LINE
 */

/** \var sswf::Style::STYLE_TYPE_SOLID
 *
 * \brief Fill with a solid color.
 *
 * This style is called solid because it uses just one color.
 * Note that it can still make use of a color with transparency.
 */

/** \var sswf::Style::STYLE_TYPE_GRADIENT_LINEAR
 *
 * \brief Fill with a linear gradient.
 *
 * Fill with a gradient drawn linearly (left to right).
 */

/** \var sswf::Style::STYLE_TYPE_GRADIENT_RADIAL
 *
 * \brief Fill with a radial gradient.
 *
 * Fill with a gradient drawn in a circle (center to edge).
 *
 * \sa sswf::Style::STYLE_TYPE_GRADIENT_FOCAL
 */

/** \var sswf::Style::STYLE_TYPE_GRADIENT_FOCAL
 *
 * \brief Fill with a radial gradient using a focal point to start.
 *
 * Fill with a gradient drawn in a circle (focal position to edge).
 *
 * \sa sswf::Style::STYLE_TYPE_GRADIENT_RADIAL
 */

/** \var sswf::Style::STYLE_TYPE_BITMAP_TILLED
 *
 * \brief Fill with a bitmap, repeat as required.
 *
 * This style type is used to fill a background with a repeated
 * bitmap.
 *
 * \sa sswf::Style::STYLE_TYPE_BITMAP_HARDEDGE_TILLED
 */

/** \var sswf::Style::STYLE_TYPE_BITMAP_CLIPPED
 *
 * \brief Fill with a bitmap, draw nothing on the edges.
 *
 * This style type is used to draw an image.
 *
 * \sa sswf::Style::STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED
 */

/** \var sswf::Style::STYLE_TYPE_BITMAP_HARDEDGE_TILLED
 *
 * \brief Fill with a bitmap, repeat as required.
 *
 * This style type is used to fill a background with a repeated
 * bitmap.
 *
 * \sa sswf::Style::STYLE_TYPE_BITMAP_TILLED
 */

/** \var sswf::Style::STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED
 *
 * \brief Fill with a bitmap, draw nothing on the edges.
 *
 * This style type is used to draw an image.
 *
 * \sa sswf::Style::STYLE_TYPE_BITMAP_CLIPPED
 */

/** \var sswf::Style::STYLE_TYPE_MATRIX
 *
 * \brief Used internally to mark that a matrix was defined.
 *
 * This type is used to mark a style which type was not
 * yet defined as a style using a matrix.
 *
 * This enables tests to ensure that the style is a bitmap
 * or a gradient style (only ones that accept a matrix.)
 */

/** \var sswf::Style::STYLE_TYPE_max
 *
 * \brief Used to know the maximum number of available styles.
 *
 * This is not a style type. It only defines the maximum number
 * of styles. Any valid style will have a positive or null number
 * smaller than this value.
 */



























/** \brief Initializes a style.
 *
 * The constructor initializes a style to all defaults.
 *
 * This means no lines and no fill.
 */
Style::Style(ErrorManager& error_manager)
	: f_error_manager(error_manager),
	  f_fill_style(0)
{
	Reset();
}


/** \brief Clean up the style.
 *
 * Since version 1.8.1 a style can include another style when
 * a line uses a fill style to get rendered. This sub-style
 * needs to be deleted.
 */
Style::~Style()
{
	delete f_fill_style;
}


/** \brief Resets a style to all defaults.
 *
 * This function resets the style to its defaults.
 *
 * This means no lines and no fill. It also resets the colors
 * to solid black and bitmap matrices to the identity.
 */
void Style::Reset(void)
{
	int		idx;

	f_style = STYLE_TYPE_UNKNOWN;

	f_morph = false;
	f_use_alpha = false;

	f_line_width[0] = 0;
	f_line_width[1] = 0;
	f_start_cap_style = STYLE_LINE_CAP_ROUND;
	f_end_cap_style = STYLE_LINE_CAP_ROUND;
	f_join_style = STYLE_LINE_JOIN_ROUND;
	f_miter_limit_factor = 0.0f;
	f_no_hscale = false;
	f_no_vscale = false;
	f_pixel_hinting = false;
	f_no_close = false;
	delete f_fill_style;
	f_fill_style = 0;

	f_color[0].Reset();
	f_color[1].Reset();

	f_bitmap_ref = SSWF_ID_NONE;

	f_matrix[0].Reset();
	f_matrix[1].Reset();

	f_gradient = -1;	// nothing defined yet
	idx = MAX_GRADIENTS * 2;
	do {
		idx--;
		f_gradient_pos[idx] = 0;
		f_gradient_color[idx].Reset();
	} while(idx > 0);
	f_gradient_spread = STYLE_GRADIENT_SPREAD_PAD;
	f_gradient_interpolation = STYLE_GRADIENT_INTERPOLATION_NORMAL;
	f_gradient_focal = 0;
}


/** \fn sswf::Style::Type(void) const
 *
 * \brief Retrieve the current type of the style.
 *
 * This function returns the current type of the style. It
 * can be one of:
 *
 * \li Style::STYLE_TYPE_UNKNOWN
 * \li Style::STYLE_TYPE_NO_LINE
 * \li Style::STYLE_TYPE_NO_FILL
 * \li Style::STYLE_TYPE_LINE
 * \li Style::STYLE_TYPE_ENHANCED_LINE
 * \li Style::STYLE_TYPE_SOLID
 * \li Style::STYLE_TYPE_GRADIENT_LINEAR
 * \li Style::STYLE_TYPE_GRADIENT_RADIAL
 * \li Style::STYLE_TYPE_GRADIENT_FOCAL
 * \li Style::STYLE_TYPE_BITMAP_TILLED
 * \li Style::STYLE_TYPE_BITMAP_CLIPPED
 * \li Style::STYLE_TYPE_BITMAP_HARDEDGE_TILLED
 * \li Style::STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED
 * \li Style::STYLE_TYPE_MATRIX
 *
 * On creation and after a call to Reset(), the style type
 * is set to UNKNOWN.
 *
 * The MATRIX type is an intermediate style of force you to
 * create a style with a matrix (i.e. if you set the matrix
 * before the image or gradient.)
 *
 * The NO_LINE and NO_FILL are special types used to create
 * empty styles to be able to turn off the current line or
 * fill style (which at times is necessary.)
 *
 * Many functions will change the type on you forcing you
 * to create a style of that type (or group of style.)
 *
 * \return one of the STYLE_TYPE_...
 *
 * \sa sswf::Style::SetType(style_t style)
 */



/** \fn signed char sswf::Style::Gradients(void) const
 *
 * \brief Return the number of gradients in a gradient style.
 *
 * This function can be used to determine how many gradients are
 * defined in a gradient style.
 *
 * Since version 8 of SWF, it is possible to use up to 15 gradients.
 * The older versions accepted only up to 8.
 *
 * This function can be used to determine the version necessary
 * to handle this style. When 9 or more gradients are used, it
 * is necessary to use a DefineShape4.
 *
 * \return The number of gradients currently defined.
 */



/** \fn sswf::Style::HasAlpha(void) const
 *
 * \brief Use to check the style for an alpha channel.
 *
 * This function can be used to check whether a style has one color or
 * more which is not solid. This is important to determine the version
 * of shape to use.
 *
 * \return true if at least one color of the style is not 100% solid.
 */




/** \fn sswf::Style::HasHardEdges(void) const
 *
 * \brief Check whether the style uses hard edges.
 *
 * This function checks whether the type of the style implies that it
 * uses hard edges.
 *
 * The two types which implies hard edges are:
 *
 * \li Style::STYLE_TYPE_BITMAP_HARDEDGE_TILLED
 * \li Style::STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED
 *
 * This is important since these types only appeared in SWF version 7.
 *
 * \return true if at the style type is one of the hard edge bitmap styles
 */






/** \fn sswf::Style::HasMorph(void) const
 *
 * \brief Check whether the style is defined as a morphing style.
 *
 * This function checks whether any of the setup function was called
 * with a morphing index (1 for most functions, Style::MAX_GRADIENT to
 * Style::MAX_GRADIENT * 2 - 1 for the gradient function.)
 *
 * \return true if at least one function was called with a morph index
 */



/** \brief Sets the type of the style.
 *
 * This function can be used to change between style flavors.
 *
 * If the style type is not yet set, then any type can be
 * selected with this function.
 *
 * Groups of styles are defined as follow:
 *
 * Style::STYLE_TYPE_NO_LINE, Style::STYLE_TYPE_NO_FILL
 *
 * Style::STYLE_TYPE_LINE
 * Style::STYLE_TYPE_ENHANCED_LINE
 *
 * Style::STYLE_TYPE_SOLID
 *
 * Style::STYLE_TYPE_GRADIENT_LINEAR, Style::STYLE_TYPE_GRADIENT_RADIAL,
 * Style::STYLE_TYPE_GRADIENT_FOCAL
 *
 * Style::STYLE_TYPE_BITMAP_TILLED, Style::STYLE_TYPE_BITMAP_CLIPPED,
 * Style::STYLE_TYPE_BITMAP_HARDEDGE_TILLED, Style::STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED
 *
 * One can change between the different types in a group at will.
 *
 * The special type Style::STYLE_TYPE_MATRIX is used internally to know that
 * you are creating a style including a bitmap or a gradient. It helps
 * enforcing that you do specify a bitmap or gradient type later.
 *
 * If the type is currently set to Style::STYLE_TYPE_UNKNOWN any style will
 * be accepted. The type is set to Unknown after a call to Reset()
 * and on construction of a style object. Trying to set the type to
 * unknown results in an assert in debug and an error in release.
 *
 * Different functions will automatically force the type of a
 * style so you usually do not need to call this very function
 * unless the default is not what you want.
 *
 * \return true when the type was changed as requested;
 * false when the type cannot be changed
 *
 * \sa sswf::Style::Reset()
 */
bool Style::SetType(style_t style)
{
	switch(f_style) {
	case STYLE_TYPE_NO_LINE:
	case STYLE_TYPE_NO_FILL:
		if(style != STYLE_TYPE_NO_LINE && style != STYLE_TYPE_NO_FILL) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change a no line or no fill style to anything else");
			return false;
		}
		break;

	case STYLE_TYPE_LINE:
	case STYLE_TYPE_ENHANCED_LINE:
		if(style != STYLE_TYPE_LINE && style != STYLE_TYPE_ENHANCED_LINE) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change a line or enhanced line style to another");
			return false;
		}
		break;

	case STYLE_TYPE_SOLID:
		if(style != STYLE_TYPE_SOLID) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change a solid fill style");
			return false;
		}
		break;

	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
		if(style != STYLE_TYPE_GRADIENT_LINEAR
		&& style != STYLE_TYPE_GRADIENT_RADIAL
		&& style != STYLE_TYPE_GRADIENT_FOCAL) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change from a gradient fill style to another style");
			return false;
		}
		break;

	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		if(style != STYLE_TYPE_BITMAP_TILLED
		&& style != STYLE_TYPE_BITMAP_CLIPPED
		&& style != STYLE_TYPE_BITMAP_HARDEDGE_TILLED
		&& style != STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change from a bitmap fill style to another style");
			return false;
		}
		break;

	case STYLE_TYPE_MATRIX:
		if(style != STYLE_TYPE_GRADIENT_LINEAR
		&& style != STYLE_TYPE_GRADIENT_RADIAL
		&& style != STYLE_TYPE_GRADIENT_FOCAL
		&& style != STYLE_TYPE_BITMAP_TILLED
		&& style != STYLE_TYPE_BITMAP_CLIPPED
		&& style != STYLE_TYPE_BITMAP_HARDEDGE_TILLED
		&& style != STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED) {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_CANNOT_CHANGE_STYLE, "cannot change from a style with a bitmap or a gradient (except focal) to one without");
			return false;
		}
		break;

	case STYLE_TYPE_UNKNOWN:
		break;

	default:
		assert(0, "invalid style");
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_STYLE, "cannot change from a style with a bitmap or a gradient to one without");
		return false;
		/*NOTREACHED*/

	}

	f_style = style;

	return true;
}


/** \brief Define this style as a line.
 *
 * This function defines the current style as a line. The style
 * must have been reset, never set or set to a line or the function
 * will fail.
 *
 * On success, the type of the style is set to Style::STYLE_TYPE_LINE.
 *
 * \note
 * This is the only Line style available. All the other styles are
 * considered fill styles.
 *
 * \param[in] index The morph index (0 or 1)
 * \param[in] width The width (thickness) of the line
 * \param[in] color The color of the line
 *
 * \return true when the style is set to the specified line;
 * false if a parameter is invalid or the type of the style
 * cannot be modified
 */
bool Style::SetLine(int index, unsigned short width, const Color &color)
{
	if(index != 0 && index != 1) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index of %d for Style::SetLine().", index);
		return false;
	}

	if(!SetType(STYLE_TYPE_LINE)) {
		return false;
	}

	f_line_width[index] = width;
	f_color[index] = color;

	if(color.Alpha() != 255) {
		f_use_alpha = true;
	}

	if(index == 1) {
		f_morph = true;
	}

	return true;
}


/** \brief Define the caps of a line.
 *
 * This function defines the start and end caps.
 *
 * Caps can be:
 *
 * \code
 *	Style::STYLE_LINE_CAP_ROUND
 *	Style::STYLE_LINE_CAP_NONE
 *	Style::STYLE_LINE_CAP_SQUARE
 * \endcode
 *
 * By default, the start and end caps are set to Style::STYLE_LINE_CAP_ROUND.
 *
 * \note
 * The function returns an error if the style cannot be set to
 * the enhanced line mode.
 *
 * \param[in] start One of STYLE_LINE_CAP_...
 * \param[in] end One of STYLE_LINE_CAP_...
 *
 * \return true when the line style can be set; false otherwise
 */
bool Style::SetLineCaps(cap_t start, cap_t end)
{
	if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
		return false;
	}

	switch(start) {
	case STYLE_LINE_CAP_ROUND:
	case STYLE_LINE_CAP_NONE:
	case STYLE_LINE_CAP_SQUARE:
		break;

	case STYLE_LINE_CAP_SAME:
		if(end != STYLE_LINE_CAP_SAME) {
			start = end;
		}
		else {
			f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_LINE_INFO, "start and end cap styles cannot both be set to STYLE_LINE_CAP_SAME in Style::SetLineCaps().");
			return false;
		}
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_LINE_INFO, "invalid start cap style of %d for Style::SetLineCaps().", start);
		return false;

	}

	switch(end) {
	case STYLE_LINE_CAP_ROUND:
	case STYLE_LINE_CAP_NONE:
	case STYLE_LINE_CAP_SQUARE:
		break;

	case STYLE_LINE_CAP_SAME:
		end = start;
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_LINE_INFO, "invalid end cap style of %d for Style::SetLineCaps().", end);
		return false;

	}

	f_start_cap_style = start;
	f_end_cap_style = end;

	return true;
}


/** \brief Define how to join line segments.
 *
 * This function defines the join and when set to a miter
 * join, the limit factor of the miter join.
 *
 * The maximum length of the miter is the limit factor times
 * the width of the line. Over that limit, the line is cutoff.
 * The miter factor must be positive or null.
 *
 * The available joins are:
 *
 * \code
 *	Style::STYLE_LINE_JOIN_ROUND
 *	Style::STYLE_LINE_JOIN_BEVEL
 *	Style::STYLE_LINE_JOIN_MITER
 * \endcode
 *
 * By default, the join is set to Style::STYLE_LINE_JOIN_ROUND
 * and the miter limit is set to 0.0f.
 *
 * \note
 * The function returns an error if the style cannot be set to
 * the enhanced line mode.
 *
 * \bug
 * The miter join limit factor is always saved. The default is
 * 0.0f which will have no effects.
 *
 * \param[in] join One of STYLE_LINE_JOIN_...
 * \param[in] miter_limit_factor The limit of the miter join
 *
 * \return true when the line style can be set; false otherwise
 */
bool Style::SetLineJoin(join_t join, float miter_limit_factor)
{
	if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
		return false;
	}

	if(miter_limit_factor < 0.0f) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_NEGATIVE_MITER, "invalid miter join limit of %f for Style::SetLineJoin(); it must be positive.", miter_limit_factor);
		return false;
	}

	switch(join) {
	case STYLE_LINE_JOIN_ROUND:
	case STYLE_LINE_JOIN_BEVEL:
	case STYLE_LINE_JOIN_MITER:
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_LINE_INFO, "invalid join style of %d for Style::SetLineJoin().", join);
		return false;

	}

	f_join_style = join;
	f_miter_limit_factor = miter_limit_factor;

	return true;
}


/** \brief Define the scaling effects on this line.
 *
 * This function defines whether a scale factor affects a line.
 * The factors can be cancelled horizontally, vertically or
 * in both direction.
 *
 * It is not clear what happens when the object is rotated.
 *
 * By default, these flags are set to false which means that
 * the line will be resized in both directions.
 *
 * \param[in] horiz Whether to scale the line horizontally
 * \param[in] vert Whether to scale the line vertically
 *
 * \return true when the line style can be set; false otherwise
 */
bool Style::SetLineScale(bool horiz, bool vert)
{
	if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
		return false;
	}

	f_no_hscale = horiz;
	f_no_vscale = vert;

	return true;
}


/** \brief Drawn pixels alignment with physical pixels.
 *
 * This function defines whether pixels should be drawn at a pixel
 * boundary on the screen.
 *
 * This is good to avoid anti-aliasing, but bad if you want to move
 * your objects slowly.
 *
 * By default, this flag is set to false meaning that lines are drawn
 * anywhere.
 *
 * \param[in] pixel_hinting True to force pixels to screen boundaries
 *
 * \return true when the line style can be set; false otherwise
 */
bool Style::SetLinePixelHinting(bool pixel_hinting)
{
	if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
		return false;
	}

	f_pixel_hinting = pixel_hinting;

	return true;
}


/** \brief Equal start and end points and closure.
 *
 * This function is used to tell Flash whether a start and end point
 * which are equal represent an enclosed shape and thus to draw a
 * join (false) or whether it is a line with ends anyway (true).
 *
 * By default, this value is set to false meaning that the line
 * forms an enclosed shape if the start and end points are equal.
 *
 * \param[in] no_close If true, this is an open line
 *
 * \return true when the line style can be set; false otherwise
 */
bool Style::SetLineNoClose(bool no_close)
{
	if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
		return false;
	}

	f_no_close = no_close;

	return true;
}


/** \brief Set a fill for the line.
 *
 * This function defines a fill style to use to render the
 * enhanced line.
 *
 * With regular lines, only a color can be used.
 *
 * By default, solid colors are used.
 *
 * \note
 * If the fill_style is a solid color, then the line becomes
 * a regular line and the fill style colors are copied. If
 * you want to keep an enhanced line, use the SetColor()
 * function instead.
 *
 * \param[in] fill_style The solid, bitmap or gradient fill
 *
 * \return true when the line style can be set; false otherwise
 *
 * \sa sswf::Style::SetColor(int index, const Color& color)
 */
bool Style::SetLineFillStyle(const Style& fill_style)
{
	switch(fill_style.f_style) {
	case STYLE_TYPE_SOLID:	// would work as a sub-style? (not useful anyway!)
		if(!SetType(STYLE_TYPE_LINE)) {
			return false;
		}
		// we copy both colors, but we do not mess with the f_morph flag
		f_color[0] = fill_style.f_color[0];
		f_color[1] = fill_style.f_color[1];
		break;

	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		if(!SetType(STYLE_TYPE_ENHANCED_LINE)) {
			return false;
		}
		f_fill_style = new Style(fill_style);
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_LINE_INFO, "invalid fill style of type %d for Style::SetLineFillStyle(); expected a gradient or a bitmap style.", fill_style.f_style);
		return false;

	}

	return true;
}


/** \brief Defines the color of a style
 *
 * This function defines a style as a solid color.
 *
 * On success, the type of the style is set to Style::STYLE_TYPE_SOLID.
 *
 * \return true when the style is set to the specified color;
 * false if a parameter is invalid or the type of the style
 * cannot be modified
 */
bool Style::SetColor(int index, const Color& color)
{
	if(index != 0 && index != 1) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index of %d for Style::SetColor().", index);
		return false;
	}

	if(!SetType(STYLE_TYPE_SOLID)) {
		return false;
	}

	f_color[index] = color;

	if(color.Alpha() != 255) {
		f_use_alpha = true;
	}

	if(index == 1) {
		f_morph = true;
	}

	return true;
}


/** \brief Defines a gradient entry.
 *
 * This function defines the style as a gradient and sets the
 * specified gradient entry to the specified color.
 *
 * It is expected that one will only use small indices. If you
 * need a gradient with three colors, then you should only use
 * indices 0, 1 and 2.
 *
 * To create a morphing gradient, add Style::MAX_GRADIENTS to the index
 * to change the color of the second image.
 *
 * Style::MAX_GRADIENTS is currently 15. It was 8 in previous versions.
 *
 * The position parameter must be set to 0 for the very first
 * point and 255 for the very last. Any intermediate values
 * are acceptable.
 *
 * \note
 * This function sets the type to Style::STYLE_TYPE_GRADIENT_LINEAR if
 * the current style is not one of the available gradient types.
 *
 * \param[in] index The gradient color to change (0 to Style::MAX_GRADIENTS)
 * \param[in] pos The position of the color in the gradient definition
 * \param[in] color The gradient color at that position
 *
 * \return true when the style is set to the specified color;
 * false if a parameter is invalid or the type of the style
 * cannot be modified
 */
bool Style::SetGradient(int index, int pos, const Color& color)
{
	if(index < 0 || index >= MAX_GRADIENTS * 2) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index of %d for Style::SetGradient().", index);
		return false;
	}
	if(pos < 0 || pos >= 256) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_POSITION, "invalid pos of %d for Style::SetGradient().", index);
		return false;
	}

	switch(f_style) {
	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
		break;

	default:
		/* assumes a default */
		if(!SetType(STYLE_TYPE_GRADIENT_LINEAR)) {
			return false;
		}
		break;

	}

	f_gradient_pos[index] = pos;
	f_gradient_color[index] = color;

	if(color.Alpha() != 255) {
		f_use_alpha = true;
	}

	if(index >= MAX_GRADIENTS) {
		f_morph = true;
		index -= MAX_GRADIENTS;
	}

	if(index > f_gradient) {
		f_gradient = index;
	}

	return true;
}




/** \brief Defines the modes of a gradient.
 *
 * This function defines a gradient style modes.
 *
 * Spread Modes:
 *
 * \li Style::STYLE_GRADIENT_SPREAD_PAD &mdash; fill with the edge color
 * \li Style::STYLE_GRADIENT_SPREAD_REFLECT &mdash; repeat backward (mirror like effect)
 * \li Style::STYLE_GRADIENT_SPREAD_REPEAT &mdash; loop back to the start
 *
 * Interpolation Modes:
 *
 * \li Style::STYLE_GRADIENT_INTERPOLATION_NORMAL
 * \li Style::STYLE_GRADIENT_INTERPOLATION_LINEAR
 *
 * \note
 * This function sets the type to Style::STYLE_TYPE_GRADIENT_LINEAR if
 * the current style is not one of the available gradient types.
 *
 * \param[in] spread One of STYLE_GRADIENT_SPREAD_...
 * \param[in] interpolation One of STYLE_GRADIENT_INTERPOLATION_...
 *
 * \return true when the style is set to the specified color;
 * false if a parameter is invalid or the type of the style
 * cannot be modified
 */
bool Style::SetGradientModes(spread_t spread, interpolation_t interpolation)
{
	switch(spread) {
	case STYLE_GRADIENT_SPREAD_PAD:
	case STYLE_GRADIENT_SPREAD_REFLECT:
	case STYLE_GRADIENT_SPREAD_REPEAT:
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_SPREAD_MODE, "invalid spread (%d) for Style::SetGradientModes().", spread);
		return false;

	}

	switch(interpolation) {
	case STYLE_GRADIENT_INTERPOLATION_NORMAL:
	case STYLE_GRADIENT_INTERPOLATION_LINEAR:
		break;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_INTERPOLATION_MODE, "invalid interpolation (%d) for Style::SetGradientModes().", interpolation);
		return false;

	}

	switch(f_style) {
	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
		break;

	default:
		/* assumes a default */
		if(!SetType(STYLE_TYPE_GRADIENT_LINEAR)) {
			return false;
		}
		break;

	}

	f_gradient_spread = spread;
	f_gradient_interpolation = interpolation;

	return true;
}




/** \brief Defines the focal point of a gradient.
 *
 * This function defines the focal point of a gradient.
 *
 * The value goes from -1 to +1, where -1 represents
 * the left side, 0 the center and +1 the right side.
 *
 * Using the matrix and rotation it is possible to
 * place the focal point anywhere.
 *
 * The style must be a gradient style or still undefined
 * for this function to work.
 *
 * The focal value must be defined between -1 and +1
 * or the function will return false.
 *
 * \param[in] focal The gradient focal point
 *
 * \return true when the gradient focal could be saved;
 * false otherwise
 */
bool Style::SetGradientFocal(float focal)
{
	if(focal < -1.0 || focal > 1.0) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_FOCAL, "focal is limited to the -1 to +1 range (%f is invalid) for Style::SetGradientModes().", focal);
		return false;
	}

	if(!SetType(STYLE_TYPE_GRADIENT_FOCAL)) {
		return false;
	}

	f_gradient_focal = static_cast<signed short>(focal * 256.0f);

	return true;
}




/** \brief Defines the fill matrix.
 *
 * The gradient and bitmap fills support a matrix. The matrix is used
 * to define the scale, position and rotation to use with the accompagnying
 * image.
 *
 * The style must have been Created, Reset() or be of one of the gradient
 * or bitmap fills for this function to work.
 *
 * \param[in] index The morph index (0 or 1)
 * \param[in] matrix The matrix to set at the specified index position
 *
 * \return true when the matrix could be saved; false otherwise
 */
bool Style::SetMatrix(int index, const Matrix& matrix)
{
	if(index != 0 && index != 1) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index of %d for Style::SetMatrix().", index);
		return false;
	}

	switch(f_style) {
	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		break;

	default:
		/* set an intermediate value so we can check that people do not change to an incompatible type */
		if(!SetType(STYLE_TYPE_MATRIX)) {
			return false;
		}
		break;

	}

	f_matrix[index] = matrix;

	return true;
}


/** \brief Set the identifier of the bitmap to used by this fill.
 *
 * This function sets the identifier of the bitmap to use in
 * case the style is a bitmap fill.
 *
 * By default, this function sets the type of the style to
 * Style::STYLE_TYPE_BITMAP_TILLED.
 *
 * \param[in] id_ref The identifier of the bitmap object
 *
 * \return true when the identifier is used; false if the
 * identifier is invalid or the style type cannot be
 * changed
 */
bool Style::SetBitmap(sswf_id_t id_ref)
{
	if(id_ref == 0 || id_ref == 65535) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid identifier for Style::SetBitmap() - 0 and 65535 are always refused");
		return false;
	}

	switch(f_style) {
	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		break;

	default:
		/* assumes a default */
		if(!SetType(STYLE_TYPE_BITMAP_TILLED)) {
			return false;
		}
		break;

	}

	f_bitmap_ref = id_ref;

	return true;
}


/** \brief Mark style as clipping style.
 *
 * This function marks the bitmap style as clipping.
 *
 * \return true when the clipping is set; false if the type
 * cannot be changed as necessary.
 */
bool Style::SetClipping(void)
{
	switch(f_style) {
	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		break;

	default:
		/* assumes a default */
		if(!SetType(STYLE_TYPE_BITMAP_TILLED)) {
			return false;
		}
		break;

	}

	f_bitmap_ref = SSWF_ID_NONE;

	return true;
}



/** \brief Save a style in the specified Data buffer.
 *
 * This function saves the style in a Data buffer.
 *
 * The caller must know whether the style is using alpha
 * or is for a morphing shape. If it does not correspond,
 * then errors are generated.
 *
 * This function is used by the regular shapes and the
 * font glyphs. When used by font glyphs, the save_morph
 * parameter is always set to false.
 *
 * When a morphing shape is saved, the save_morph is set
 * to true in which case the save_alpha is expected to be
 * true too.
 *
 * \param[in] data The Data buffer where the style is saved
 * \param[in] save_alpha Whether the alpha should be saved
 * \param[in] save_morph Whether the morph data should be saved
 *
 * \return an error code or ERROR_CODE_NONE when no error occurs
 */
ErrorManager::error_code_t Style::Save(Data& data, bool save_alpha, bool save_morph)
{
	int		idx;

	// morph has restrictions
	if(save_morph) {
		if(!save_alpha) {
			return f_error_manager.OnError(ErrorManager::ERROR_CODE_MORPH_REQUIRES_ALPHA, "cannot save a morph style without alpha");
		}
		// To be tested
		if(f_style == STYLE_TYPE_GRADIENT_FOCAL) {
			return f_error_manager.OnError(ErrorManager::ERROR_CODE_NO_FOCAL_WITH_MORPH, "cannot save a morph style with a focal gradient");
		}
		// To be tested
		if(f_gradient > 8) {	// old versions only supported 8 colors
			return f_error_manager.OnError(ErrorManager::ERROR_CODE_MORPH_GRADIENT_LIMIT, "cannot save a morph style with more than 8 color gradients");
		}
	}

	if(f_morph ^ save_morph) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_MORPH_MISMATCH, "a style is used with(out) morph information (not) for a DefineMorphShape");
	}
	if(f_use_alpha && !save_alpha) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_ALPHA_MISMATCH, "a style using transparency is saved using solid colors");
	}

	switch(f_style) {
	case STYLE_TYPE_LINE:
		data.PutShort(f_line_width[0]);
		if(save_morph) {
			data.PutShort(f_line_width[1]);
			f_color[0].Save(data, true);
			f_color[1].Save(data, true);
		}
		else if(save_alpha) {
			f_color[0].Save(data, true);
		}
		else {
			f_color[0].Save(data, false);
		}
		break;

	case STYLE_TYPE_ENHANCED_LINE:
		if(!save_alpha) {
			return f_error_manager.OnError(ErrorManager::ERROR_CODE_ENHANCED_LINE_REQUIRES_ALPHA, "cannot save an enhanced line style without alpha");
		}

		data.PutShort(f_line_width[0]);
		if(save_morph) {
			data.PutShort(f_line_width[1]);
		}
		// byte 1
		data.WriteBits(f_start_cap_style, 2);
		data.WriteBits(f_join_style, 2);
		data.WriteBits(f_fill_style != 0, 1);
		data.WriteBits(f_no_hscale, 1);
		data.WriteBits(f_no_vscale, 1);
		data.WriteBits(f_pixel_hinting, 1);

		data.WriteBits(0, 5);
		data.WriteBits(f_no_close, 1);
		data.WriteBits(f_end_cap_style, 2);
		if(f_join_style == STYLE_LINE_JOIN_MITER) {
			long miter = static_cast<long>(rint(f_miter_limit_factor * 256));
			if(miter >= 32768) {
				miter = 32768;
			}
			else if(miter < -32768) {
				miter = -32768;
			}
			data.PutShort(miter);
		}
		if(f_fill_style == 0) {
			f_color[0].Save(data, true);
			if(save_morph) {
				f_color[1].Save(data, true);
			}
		}
		else {
			f_fill_style->Save(data, save_alpha, save_morph);
		}
		break;

	case STYLE_TYPE_SOLID:
		data.PutByte(0);		// type SOLID
		if(save_morph) {
			f_color[0].Save(data, true);
			f_color[1].Save(data, true);
		}
		else {
			f_color[0].Save(data, save_alpha);
		}
		break;

	case STYLE_TYPE_GRADIENT_LINEAR:
		data.PutByte(0x10);		// type LINEAR GRADIENT
		goto save_gradient;

	case STYLE_TYPE_GRADIENT_RADIAL:
		data.PutByte(0x12);		// type RADIAL GRADIENT
		goto save_gradient;

	case STYLE_TYPE_GRADIENT_FOCAL:
		data.PutByte(0x13);		// type RADIAL FOCAL
save_gradient:
		f_matrix[0].Save(data);
		if(save_morph) {
			f_matrix[1].Save(data);
		}
		data.Align();
		data.WriteBits(f_gradient_spread, 2);
		data.WriteBits(f_gradient_interpolation, 2);
		data.WriteBits(f_gradient + 1, 4);
		if(save_morph) {
			for(idx = 0; idx <= f_gradient; idx++) {
				data.PutByte(f_gradient_pos[idx]);
				f_gradient_color[idx].Save(data, true);
				data.PutByte(f_gradient_pos[idx + MAX_GRADIENTS]);
				f_gradient_color[idx + MAX_GRADIENTS].Save(data, true);
			}
		}
		else {
			for(idx = 0; idx <= f_gradient; idx++) {
				data.PutByte(f_gradient_pos[idx]);
				f_gradient_color[idx].Save(data, save_alpha);
			}
		}
		if(f_style == STYLE_TYPE_GRADIENT_FOCAL) {
			// the actual focal position
			data.PutShort(f_gradient_focal);
		}
		break;

	case STYLE_TYPE_BITMAP_TILLED:
		data.PutByte(0x40);
		goto save_bitmap;

	case STYLE_TYPE_BITMAP_CLIPPED:
		data.PutByte(0x41);
		goto save_bitmap;

	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
		data.PutByte(0x42);
		goto save_bitmap;

	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		data.PutByte(0x43);
save_bitmap:
		data.PutShort(f_bitmap_ref);
		f_matrix[0].Save(data);
		if(save_morph) {
			f_matrix[1].Save(data);
		}
		break;

	default:
		assert(0, "trying to save a style with an undefined/invalid type");
		return f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_STYLE, "trying to save a style with an undefined/invalid type");
		/*NOTREACHED*/

	}

	return ErrorManager::ERROR_CODE_NONE;
}



/** \brief Compare two styles between each other.
 *
 * This comparison operator can be used to compare two styles. This
 * is used in many places to know whether a certain style already
 * exists in a list of styles. This let's us optimize as much as
 * possible.
 *
 * Note that if both types have different types, they are always
 * considered different.
 *
 * When types have the same types, then the corresponding data is
 * compared between both objects. Thus, a bitmap style requires
 * the bitmap identifier and matrix comparisons.
 *
 * \bug
 * Try to compare a style of type Style::STYLE_TYPE_UNKNOWN generates
 * an error and returns false.
 *
 * \param[in] style The style to compare against
 *
 * \return true if the two styles are indeed equal
 */
bool Style::operator == (const Style& style) const
{
	int		idx;

	if(f_style == STYLE_TYPE_UNKNOWN
	|| style.f_style == STYLE_TYPE_UNKNOWN) {
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_STYLE, "trying to compare a style with an undefined type");
		return false;
	}

	// to simplify the following switch we test this first
	if(f_style != style.f_style || f_morph != style.f_morph) {
		return false;
	}

	// don't test if we compare a style with itself because
	// that's not equivalent to the following!
	switch(f_style) {
	case STYLE_TYPE_LINE:
		return f_line_width[0] == style.f_line_width[0] && f_line_width[1] == style.f_line_width[1]
			&& f_color[0] == style.f_color[0] && f_color[1] == style.f_color[1];

	case STYLE_TYPE_ENHANCED_LINE:
		if(f_line_width[0] != style.f_line_width[0] || f_line_width[1] != style.f_line_width[1]
				|| f_color[0] != style.f_color[0] || f_color[1] != style.f_color[1]
				|| f_start_cap_style != style.f_start_cap_style
				|| f_end_cap_style != style.f_end_cap_style
				|| f_join_style != style.f_join_style
				|| f_miter_limit_factor != style.f_miter_limit_factor
				|| f_no_hscale != style.f_no_hscale || f_no_vscale != style.f_no_vscale
				|| f_pixel_hinting != style.f_pixel_hinting
				|| f_no_close != style.f_no_close) {
			return false;
		}
		if(f_fill_style == 0 && style.f_fill_style == 0) {
			return true;
		}
		if(f_fill_style == 0 || style.f_fill_style == 0) {
			return false;
		}
		return f_fill_style->operator == (*style.f_fill_style);

	case STYLE_TYPE_SOLID:
		return f_color[0] == style.f_color[0] && f_color[1] == style.f_color[1];

	case STYLE_TYPE_GRADIENT_LINEAR:
	case STYLE_TYPE_GRADIENT_RADIAL:
	case STYLE_TYPE_GRADIENT_FOCAL:
		if(f_matrix[0] != style.f_matrix[0] || f_matrix[1] != style.f_matrix[1]) {
			return false;
		}
		if(f_gradient != style.f_gradient) {
			return false;
		}
		for(idx = 0; idx < f_gradient; idx++) {
			if(f_gradient_pos[idx] != style.f_gradient_pos[idx]
			|| f_gradient_pos[idx + MAX_GRADIENTS] != style.f_gradient_pos[idx + MAX_GRADIENTS]
			|| f_gradient_color[idx] != style.f_gradient_color[idx]
			|| f_gradient_color[idx + MAX_GRADIENTS] != style.f_gradient_color[idx + MAX_GRADIENTS]) {
				return false;
			}
		}
		return true;

	case STYLE_TYPE_BITMAP_TILLED:
	case STYLE_TYPE_BITMAP_CLIPPED:
	case STYLE_TYPE_BITMAP_HARDEDGE_TILLED:
	case STYLE_TYPE_BITMAP_HARDEDGE_CLIPPED:
		return f_matrix[0] == style.f_matrix[0] && f_matrix[1] == style.f_matrix[1]
				&& f_bitmap_ref == style.f_bitmap_ref;

	default:
		f_error_manager.OnError(ErrorManager::ERROR_CODE_INVALID_STYLE, "trying to compare a style with an undefined type");
		return false;
		/*NOTREACHED*/

	}

	return true;	// avoid warnings
}







/* The following options fold the documentation; use 'zi' to turn on and off
 *
 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
 */
