/* libsswf_tags.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 different helper classes
 *
 * 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;




// since the main attraction in an SWF animation are the tags,
// I place the mainpage of the SSWF library here...


/** \mainpage SSWF -- A complete library to generate Flash animations
 *
 * \par Summary
 *
 * \li \ref cpp_library
 * \li \ref c_library
 * \li \ref by_the_same_author
 *
 *
 *
 * \section cpp_library SSWF, a C++ library
 *
 * The SSWF library is freesoftware created to generate Flash animations
 * without having to worry about the format specification of SWF which
 * can be quite cumbersome!
 *
 * (To download, check out the \ref links pages)
 *
 * Especially, it hides from you the need to know whether you should
 * use a DefineFont or DefineFont2. Depending on the information that
 * you put in a TagFont object, the library will know which of DefineFont
 * or DefineFont2 will be required for your movie.
 *
 * The library also takes care of selecting different tags depending on
 * the version of the output movie. For instance, the ProtectDebug tag
 * can only be used with version 5. Before version 5, you need a Protect
 * tag and after, you need a ProtectDebug2. Similarly, since version 8
 * you need a FileAttributes tag at the very beginning of the movie.
 *
 * Because the available version of the player may be constrained, you
 * can also specify what minimum and maximum version of a movie you want.
 *
 * At this time SSWF supports all of version 7, except the Video and the
 * streaming sound tags. It can also create a proper version 8 by adding
 * the FileAttributes and the Import2 instead of Import as required by
 * the specification. Other version 8 tags will come soon.
 *
 * To get started, look inside the samples directory. It includes samples of
 * SSWF and C++ code. The SSWF compiler can be looked at (especially the
 * sswf_save.c++ file) as a good example on how to use the SSWF library.
 *
 * SSWF is a C++ library ready to be used on your Unix system. It also
 * includes a set of pre-compiled libraries which run under Microsoft
 * Windows. Though, I do not offer support for Visual Studio, many have
 * successfully compiled the library with it. So it is possible. Once in
 * a while I do check to see whether cl likes my code. In general, it does.
 * Note that all the code works perfectly with g++ and should work with
 * any compatible compiler.
 *
 * The packages I offer work on the following platforms:
 *
 *	\li Linux RedHat
 *	\li Linux Debian
 *	\li Linux on Power (IBM) [support till 1.7.4]
 *	\li Linux slackware
 *	\li IRIX 6.5
 *	\li Mac OS/X (10.3 with fink or not)
 *	\li MS-Windows
 *
 * For the systems I support, you need absolutly no patches.
 *
 * Some people out there made the library work for:
 *
 *	\li FreeBSD
 *	\li Gentoo
 *
 * \section c_library SSWF is also a C library
 *
 * Since version 1.7.5, C programmers can also use the library. It includes
 * a sub-library which allows C programs to link against the SSWF library.
 * It is called libsswf_c.{a,so,dylib}.
 *
 * You may have a look at the C samples in:
 *
 *	samples/c-samples/...
 *
 * The C programmers are invited to look inside the header file:
 *
 *	include/sswf/libsswf_c.h
 *
 * This lists all the available enumerations, structures and functions
 * one needs to create an SSWF movie. The use is very object oriented. The
 * main thing is that all the objects need to be created on the heap and
 * destroyed once done with them. It may be a bit tedious, but that's why
 * you should maybe consider using C++ one day...
 *
 * The following shows you how to create a TagHeader, set the minimum version
 * and add a Sprite to it before saving the program.
 *
 * \code
 * #include	<sswf/libsswf_c.h>
 *
 * ...
 *
 * struct SSWF_TagHeader	*header;
 * struct SSWF_TagSprite	*sprite;
 * struct SSWF_Data	*data;
 * void			*buffer;
 * size_t			size;
 *
 * header = sswf_tag_header_create();
 * sswf_tag_header_set_minimum_version(header, 5);
 *
 * sprite = sswf_tag_sprite_create(sswf_tag_header_cast_tag_base(header));
 * ... -- initialize the sprite
 *
 * ... -- add other objects
 *
 * data = sswf_data_create();
 * sswf_tag_header_save(header, data);
 * sswf_data_read(data, &buffer, &size);
 * f = fopen("a.swf", "w");
 * fwrite(buffer, 1, size, f);
 * fclose(f);
 *
 * sswf_tag_header_destroy(header);
 * \endcode
 *
 * As you can see, this is pretty straight forward, just somewhat more text
 * to type (feel free to copy & paste!)
 *
 * The naming convention is as follow:
 *
 * 	\li Objects
 *
 * 	Objects get <b>SSWF_</b> prepended. These are all viewed as opaque
 * 	structures. You only can use pointers to objects.
 *
 * 	\li Enumerations
 *
 * 	The enumerations get <b>sswf_</b> prepended. In C, they are not in attached
 * 	to any specific object so you can use them directly. Since their name
 * 	include the object and/or what they are supposed to be used for, there
 * 	is no name clash.
 *
 * 	\li Structures
 *
 * 	Like enumerations, structures get <b>sswf_</b> prepended.
 * 	These structures are used to pass more complex data to some objects.
 * 	Like the enumerations, structures are not attached to any specific
 * 	object.
 *
 * 	All of these structures can be put on the stack. You are responsible
 * 	to initialize them properly (just like their counter part in C++).
 *
 * 	\li Functions
 *
 * 	Just as expected, all the functions cannot be attached to a specific
 * 	object in C. Thus, the name of the function includes the name of the
 * 	object in lower case after the prepended 'sswf_'. Then follows the
 * 	name of the function as found in the corresponding object.
 *
 * 	The cpp-to-c generator creates several special functions to (1)
 * 	create, (2) destroy and (3) cast the object pointers. The cast is
 * 	very important since C casting can result in the wrong C++ pointer.
 *
 * 	The parameters to each function is either kept the same (standard
 * 	C type such as int, char, etc.), tweaked to work in C (i.e. the C++
 * 	library makes use of the type bool which is transformed to an int in
 * 	the C library.) or, for objects, enumerations and structures, are
 * 	renamed with their C name.
 *
 * 	The result looks like this:
 *
 * \code
 * <return type> sswf_<class name>_<function>(<parameters>);
 * \endcode
 *
 * 	I suggest you look at a few object in the C and the C++ headers to
 * 	compare. It is rather easy to see what happens to the names.
 *
 * 	The C++ constructor is transformed into:
 *
 * \code
 * void sswf_<class name>_create(<parameters>);
 * \endcode
 *
 *	The C++ destructor is transformed into:
 *
 * \code
 * void sswf_<class name>_destroy(ClassName *ojbect);
 * \endcode
 *
 *	The casts (which are only static casts in C++, thought I have to use
 *	reinterpret from the C pointer to the C++ pointer and vice versa!):
 *
 * \code
 * <inherited class name>
 *              sswf_<class name>_cast_<inherited class name>(<class name>);
 * \endcode
 *
 *	Once you understand how this works, you can read the C++ documentation
 *	even when you are working with the C library. In the first code
 *	sample, I use sswf_tag_sprite_create() which is the Constructor of the
 *	sswf::TagSprite object.
 *
 * \par IMPORTANT NOTE:
 *
 *	The tags will delete all the memory that they allocate in a totally
 *	automated way. However, in C you will need to explicitly delete objects
 *	(with their sswf_..._destroy() corresponding function.)
 *
 * \section by_the_same_author Other software
 *
 *	I also wrote and offer a few other software as defined here on
 *	the \ref other_links page.
 */


/** \page links Links to libraries and tools necessary to build SSWF
 *
 * The SSWF project can be found at Made to Order Software Corp. and
 * on SourceForge.net. Following the links:
 *
 *	\li http://sswf.m2osw.com
 *
 *	\li http://www.sourceforge.net/projects/sswf
 *
 * I want to thanks all the people helping GNU and the Free Software Foundation
 * for all the free software available to all of us.
 *
 *	\li http://www.gnu.org
 *
 *	\li http://www.fsf.org
 *
 * In order to compile the SSWF library and tools, you will need the following
 * free tools and libraries:
 *
 *	\li <a href="http://gcc.gnu.org">gcc/g++</a>
 *
 *	\li <a href="http://www.zlib.net/">zlib</a>
 *
 *	\li <a href="http://www.ijg.org/">JPEG library version 6b</a>
 *
 *	\li <a href="http://amake.m2osw.com">amake</a> or <a href="http://directory.fsf.org/GNU/make.html">GNU make</a>
 *
 *	\li <a href="http://directory.fsf.org/GNU/bison.html">bison</a>
 *
 *	\li <a href="http://freetype.sourceforge.net/">the FreeType Project</a> <i>(For ft2sswf only at this time)</i>
 *
 *	\li <a href="http://www.gnu.org/software/libiconv/">libiconv</a>
 *
 *	\li <a href="http://www.doxygen.org">Doxygen</a> <i>(To re-generate the dox only&mdash;optional)</i>
 *
 * Older versions of SSWF also required the following tool:
 *
 *	\li <a href="http://directory.fsf.org/GNU/flex.html">flex</a>
 *
 * Newer versions of SSWF have a lexical.c++ file which removed the
 * dependency on flex and added more flexibility for internationalization.
 * Then a bit later a better flex with full internationalization support
 * was coming out. 8-)
 */


/** \page other_links Other software by the same author
 *
 * \li <a href="http://aftp.sourceforge.net">Alexis'&nbsp;FTP and TELNET</a>
 *
 * \li <a href="http://apple-emulator.sourceforge.net">Apple //c emulator</a>
 *
 * \li <a href="http://boost-extras.sourceforge.net">Boost-Extras</a>
 *
 * \li <a href="http://libx3d.sourceforge.net">libx3d</a>
 *
 * \li <a href="http://amake.sourceforge.net">Alexis' make tool (i.e. amake)</a>
 *
 * \li <a href="http://edll.sourceforge.net">EDLL</a> <i>(Enhanced Dynamic Library Loading under MinGW for MS-Windows)</i>
 *
 * \li <a href="http://solitaire.sourceforge.net">Solitaire</a>
 *
 * \li <a href="http://unigw.sourceforge.net">unigw & wpkg</a>
 */


/** \page action_script_v3 ActionScript version 3
 *
 * Macromedia and now Adobe have chosen to continue to use a free
 * implementation of their ActionScript interpreter. For this they
 * used the NS4 Javascript implementation up to version 7. Now they
 * use the Tamarin project. The format of the compiled action script
 * is much different since Flash version 9. It includes the
 * ActionScript version 3 which is currently described in what is
 * called the <a href="../abcFormat.html">abcFormat</a> document.
 *
 * I have a copy available in my project. I hope to find the time
 * to duplicate that information in my own reference later.
 */




/////////////////////////////////////////// SRectangle



/** \class sswf::SRectangle
 *
 * \brief The SWF rectangle
 *
 * The SWF format allows for compressed rectangles. These are
 * saved with 5 bits representing the number of bits used for
 * each coordinate followed by the coordinates.
 *
 * The coordinates represent points. In other words, the rectangle
 * is defined with two points (xmin, ymin) and (xmax, ymax).
 *
 * If the rectangle is considered empty, the width and height are
 * considered to be 0.
 *
 * To compute the width, do XMax() - XMin().
 *
 * To compute the height, do YMax() - YMin().
 *
 * \sa sswf::SRectangle::Reset(void)
 * \sa sswf::SRectangle::Set(long xmin, long xmax, long ymin, long ymax)
 * \sa sswf::SRectangle::SetReorder(long xmin, long xmax, long ymin, long ymax)
 * \sa <a href="../SWFalexref.html#swf_rect">SWF Alexis' Reference&mdash;swf_rect</a>
 */


/** \brief Initialize the rectangle to all zeroes.
 *
 * By default a rectangle is an empty rectangle with all of its
 * coordinates set to zero.
 *
 * \sa sswf::SRectangle::Reset(void)
 */
SRectangle::SRectangle(void)
{
	Reset();
}


/** \brief Resets the rectangle to all zeroes.
 *
 * This function resets the rectangle to all zeroes. In effect,
 * the rectangle becomes the empty rectangle.
 *
 * \sa sswf::SRectangle::SRectangle(void)
 */
void SRectangle::Reset(void)
{
	f_xmin = 0;
	f_xmax = 0;
	f_ymin = 0;
	f_ymax = 0;
}


/** \brief Sets the coordinates of the rectangle.
 *
 * Sets the coordinates of the rectangle to the specified coordinates.
 * If xmin >= xmax or ymin >= ymax then the rectangle is considered
 * empty. You can also call the sswf::SRectangle::Reset() function to
 * make the rectangle empty.
 *
 * \sa sswf::SRectangle::Reset(void)
 * \sa sswf::SRectangle::SetReorder(long xmin, long xmax, long ymin, long ymax)
 */
void SRectangle::Set(long xmin, long xmax, long ymin, long ymax)
{
	f_xmin = xmin;
	f_xmax = xmax;
	f_ymin = ymin;
	f_ymax = ymax;
}


/** \brief Set the coordinates of the rectangle ordering them as required.
 *
 * This function sets the coordinates of the rectangle. It will
 * reorder the min/max is the right order. So if xmin > xmax, the
 * horizontal coordinates are swapped. And if ymin > ymax, the
 * vertical coodinates are swapped.
 *
 * To create an empty rectangle, you need to set xmin equal to xmax
 * or ymin equal to ymax or call the sswf::SRectangle::Reset() function.
 *
 * \sa sswf::SRectangle::Reset(void)
 * \sa sswf::SRectangle::Set(long xmin, long xmax, long ymin, long ymax)
 */
void SRectangle::SetReorder(long xmin, long xmax, long ymin, long ymax)
{
	if(xmin < xmax) {
		f_xmin = xmin;
		f_xmax = xmax;
	}
	else {
		f_xmax = xmin;
		f_xmin = xmax;
	}
	if(ymin < ymax) {
		f_ymin = ymin;
		f_ymax = ymax;
	}
	else {
		f_ymax = ymin;
		f_ymin = ymax;
	}
}


/** \brief Read the left side X coordinate
 *
 * This function returns the minimum X coordinate.
 *
 * \return The xmin coordinate
 *
 * \sa sswf::SRectangle::XMax(void) const
 */
long SRectangle::XMin(void) const
{
	return f_xmin;
}


/** \brief Read the right side X coordinate
 *
 * This function returns the maximum X coordinate.
 *
 * \return The xmax coordinate
 *
 * \sa sswf::SRectangle::XMin(void) const
 */
long SRectangle::XMax(void) const
{
	return f_xmax;
}


/** \brief Read the top side Y coordinate
 *
 * This function returns the minimum Y coordinate.
 *
 * \return The ymin coordinate
 *
 * \sa sswf::SRectangle::YMax(void) const
 */
long SRectangle::YMin(void) const
{
	return f_ymin;
}


/** \brief Read the bottom side Y coordinate
 *
 * This function returns the maximum Y coordinate.
 *
 * \return The ymax coordinate
 *
 * \sa sswf::SRectangle::YMin(void) const
 */
long SRectangle::YMax(void) const
{
	return f_ymax;
}


/** \brief Check whether a rectangle is empty.
 *
 * This function returns true if the rectangle is considered empty.
 *
 * Some rectangles in SWF cannot be empty (especially the display
 * screen!)
 *
 * A rectangle is considered empty when xmin >= xmax or
 * ymin >= ymax.
 *
 * \return true if the rectangle is empty
 *
 * \sa sswf::SRectangle::Reset(void)
 */
bool SRectangle::IsEmpty(void) const
{
	return f_xmin >= f_xmax || f_ymin >= f_ymax;
}


/** \brief Save a rectangle in a data buffer
 *
 * This function saves a rectangle in a data buffer as compressed
 * as possible.
 *
 * The function first determines the necessary number of bits to
 * save all the coordinates, and then it saves them in the
 * specified data buffer.
 *
 * An empty rectangle is saved on 2 bytes.
 *
 * \param[in] data The data buffer where the rectangle is saved.
 */
void SRectangle::Save(Data& data) const
{
	int		sz, bits;

	sz = TagBase::SIBitSize(f_xmin);
	bits = TagBase::SIBitSize(f_ymin);
	if(bits > sz) {
		sz = bits;
	}
	bits = TagBase::SIBitSize(f_xmax);
	if(bits > sz) {
		sz = bits;
	}
	bits = TagBase::SIBitSize(f_ymax);
	if(bits > sz) {
		sz = bits;
	}

	data.WriteBits(sz, 5);
	data.WriteBits(f_xmin, sz);
	data.WriteBits(f_xmax, sz);
	data.WriteBits(f_ymin, sz);
	data.WriteBits(f_ymax, sz);
}



/////////////////////////////////////////// Color

/** \class sswf::Color
 *
 * \brief A small object to hold an SWF color.
 *
 * The Color class is used to specify an RGB and RGBA colors of
 * objects.
 *
 * When only an RGB color is necessary, you should pass a Color object
 * with the alpha component set to 255 (solid).
 *
 * \sa <a href="../SWFalexref.html#swf_rgb">SWF Alexis' Reference&mdash;swf_rgb</a>
 * \sa <a href="../SWFalexref.html#swf_xrgb">SWF Alexis' Reference&mdash;swf_xrgb</a>
 * \sa <a href="../SWFalexref.html#swf_rgba">SWF Alexis' Reference&mdash;swf_rgba</a>
 * \sa <a href="../SWFalexref.html#swf_argb">SWF Alexis' Reference&mdash;swf_argb</a>
 */

/** \fn sswf::Color::Color(void)
 *
 * \brief Construct a color object.
 *
 * The Color() constructor initialize the color with a call to Reset().
 *
 * This is supposed to have the effect of setting the color to solid white.
 */

/** \fn sswf::Color::Red(void) const
 *
 * \brief Returns the red component.
 *
 * The Red() function is used to read the red component of a
 * Color object.
 *
 * \return The red component
 */

/** \fn sswf::Color::Green(void) const
 *
 * \brief Returns the green component.
 *
 * The Green() function is used to read the green component of a
 * Color object.
 *
 * \return The green component
 */

/** \fn sswf::Color::Blue(void) const
 *
 * \brief Returns the blue component.
 *
 * The Blue() function is used to read the blue component of a
 * Color object.
 *
 * \return The blue component
 */

/** \fn sswf::Color::Alpha(void) const
 *
 * \brief Returns the alpha (or transparency) component.
 *
 * The Alpha() function is used to read the alpha component of a
 * Color object.
 *
 * \return The alpha component
 */

/** \fn sswf::Color::IsSolid(void) const
 *
 * \brief Checks whether this color represents a solid color.
 *
 * The IsSolid() function returns true when the alpha is 255.
 *
 * \return 'false' when alpha is 255
 */

/** \fn sswf::Color::IsInvisible(void) const
 *
 * \brief Checks whether this color is invisible.
 *
 * The IsInvisible() function returns true when the alpha is 0.
 *
 * \return 'true' when alpha is 0
 */



/** \brief Reset the color to its default values.
 *
 * The Reset() function sets the color to solid white which is the default animation
 * background color.
 */
void Color::Reset(void)
{
	f_red   = 255;
	f_green = 255;
	f_blue  = 255;
	f_alpha = 255;
}


/** \brief Set the color to the specified values
 *
 * The Set() function sets the color to the specified red, green, blue and alpha
 * values.
 *
 * 0 represent none and 255 represents full color.
 *
 * For the alpha, 0 represent 100% transparent and 255 represents 100% solid.
 * Notice that by default the alpha will be set to 255 which means a solid color.
 *
 * \param[in] red The value for the red component
 * \param[in] green The value for the green component
 * \param[in] blue The value for the blue component
 * \param[in] alpha The value for the alpha component
 */
void Color::Set(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
{
	f_red =   red;
	f_green = green;
	f_blue =  blue;
	f_alpha = alpha;
}


/** \brief Save the color to a Data buffer
 *
 * The Save() function saves the color to a Data buffer.
 *
 * The 'save_alpha' flag can be used to determine whether the
 * alpha should also be saved.
 *
 * This function saves the color as RGB or RGBA. To save
 * the color as XRGB or ARGB you need to write your own
 * code.
 *
 * \param[in] data The output buffer where the result is saved.
 * \param[in] save_alpha A flag used to specify whether the alpha is saved too
 */
void Color::Save(Data& data, bool save_alpha)
{
	data.PutByte(f_red);
	data.PutByte(f_green);
	data.PutByte(f_blue);
	if(save_alpha) {
		data.PutByte(f_alpha);
	}
}


/** \brief operator == compares two colors for equality
 *
 * The == operator compares two colors together and returns
 * true when all the components, including the alpha channel,
 * match exactly.
 *
 * \param[in] color The other color to compare 'this' with
 *
 * \return 'true' when both colors are equal
 */
bool Color::operator == (const Color& color) const
{
	return f_red == color.f_red && f_green == color.f_green && f_blue == color.f_blue && f_alpha == color.f_alpha;
}


/** \brief operator != compares two colors for inequality
 *
 * The != operator compares two colors together and returns
 * true when any of the components, including the alpha channel,
 * does not match exactly.
 *
 * \param[in] color The other color to compare 'this' with
 *
 * \return 'true' when both colors are inequal
 */
bool Color::operator != (const Color& color) const
{
	return f_red != color.f_red || f_green != color.f_green || f_blue != color.f_blue || f_alpha != color.f_alpha;
}




/////////////////////////////////////////// ColorTransform


/** \class sswf::ColorTransform
 *
 * \brief The simple color transform of a TagPlace tag.
 *
 * Up to version 7, only a ColorTransform can be used to change
 * the colors of the objects. This color transformation uses a
 * simplified matrix like this:
 *
 * \code
 *  | s 0 0 0 0 |
 *  | 0 s 0 0 0 |
 *  | 0 0 s 0 0 |
 *  | 0 0 0 s 0 |
 *  | o o o o 1 |
 * \endcode
 *
 * The 's' represents the scaling and it is set with
 * sswf::ColorTranform::SetMult(double red, double green, double blue, double alpha).
 * or one of the specific color functions.
 * By default the scalings are set to 1.0f.
 *
 * The 'o' represents the offset and it is set with
 * sswf::ColorTranform::SetAdd(double red, double green, double blue, double alpha)
 * or one of the specific color functions.
 * By default the offsets are set to 0.0f.
 *
 * There is no way to apply a rotation to your colors with this limited color
 * transformation.
 *
 * \sa sswf::ColorTranform::Reset(void)
 * \sa sswf::ColorTranform::SetAdd(double red, double green, double blue, double alpha)
 * \sa sswf::ColorTranform::SetMult(double red, double green, double blue, double alpha)
 * \sa <a href="../SWFalexref.html#swf_color_transform">SWF Alexis' Reference&mdash;swf_color_transform</a>
 */


/** \brief Initializes a color tranformation to "no transformation"
 *
 * This function initializes the color transformation to no offset
 * and no scaling of the colors.
 *
 * \sa sswf::ColorTransform::Reset(void)
 */
ColorTransform::ColorTransform(void)
{
	Reset();
}


/** \fn sswf::ColorTransform::AddRed(void) const
 *
 * \brief Get the current red offset.
 *
 * This function returns the current red offset.
 *
 * \return The red offset.
 *
 * \sa sswf::ColorTransform::AddGreen(void) const
 * \sa sswf::ColorTransform::AddBlue(void) const
 * \sa sswf::ColorTransform::AddAlpha(void) const
 */


/** \fn sswf::ColorTransform::AddGreen(void) const
 *
 * \brief Get the current green offset.
 *
 * This function returns the current green offset.
 *
 * \return The green offset.
 *
 * \sa sswf::ColorTransform::AddRed(void) const
 * \sa sswf::ColorTransform::AddBlue(void) const
 * \sa sswf::ColorTransform::AddAlpha(void) const
 */


/** \fn sswf::ColorTransform::AddBlue(void) const
 *
 * \brief Get the current blue offset.
 *
 * This function returns the current blue offset.
 *
 * \return The blue offset.
 *
 * \sa sswf::ColorTransform::AddRed(void) const
 * \sa sswf::ColorTransform::AddGreen(void) const
 * \sa sswf::ColorTransform::AddAlpha(void) const
 */


/** \fn sswf::ColorTransform::AddAlpha(void) const
 *
 * \brief Get the current alpha offset.
 *
 * This function returns the current alpha offset.
 *
 * \return The alpha offset.
 *
 * \sa sswf::ColorTransform::AddRed(void) const
 * \sa sswf::ColorTransform::AddGreen(void) const
 * \sa sswf::ColorTransform::AddBlue(void) const
 */


/** \fn sswf::ColorTransform::MultRed(void) const
 *
 * \brief Get the current red scaling.
 *
 * This function returns the current red scaling.
 *
 * \return The red scaling.
 *
 * \sa sswf::ColorTransform::MultGreen(void) const
 * \sa sswf::ColorTransform::MultBlue(void) const
 * \sa sswf::ColorTransform::MultAlpha(void) const
 */


/** \fn sswf::ColorTransform::MultGreen(void) const
 *
 * \brief Get the current green scaling.
 *
 * This function returns the current green scaling.
 *
 * \return The green scaling.
 *
 * \sa sswf::ColorTransform::MultRed(void) const
 * \sa sswf::ColorTransform::MultBlue(void) const
 * \sa sswf::ColorTransform::MultAlpha(void) const
 */


/** \fn sswf::ColorTransform::MultBlue(void) const
 *
 * \brief Get the current blue scaling.
 *
 * This function returns the current blue scaling.
 *
 * \return The blue scaling.
 *
 * \sa sswf::ColorTransform::MultRed(void) const
 * \sa sswf::ColorTransform::MultGreen(void) const
 * \sa sswf::ColorTransform::MultAlpha(void) const
 */


/** \fn sswf::ColorTransform::MultAlpha(void) const
 *
 * \brief Get the current alpha scaling.
 *
 * This function returns the current alpha scaling.
 *
 * \return The alpha scaling.
 *
 * \sa sswf::ColorTransform::MultRed(void) const
 * \sa sswf::ColorTransform::MultGreen(void) const
 * \sa sswf::ColorTransform::MultBlue(void) const
 */


/** \brief Resets the transformation to the identity matrix.
 *
 * This function removes the effect of any previous transformation.
 * The offsets are set to all zeroes (0.0f) and the multiplicators
 * are all set to 1.0f.
 */
void ColorTransform::Reset(void)
{
	f_add_red   = 0.0;
	f_add_green = 0.0;
	f_add_blue  = 0.0;
	f_add_alpha = 0.0;
	f_mult_red   = 1.0;
	f_mult_green = 1.0;
	f_mult_blue  = 1.0;
	f_mult_alpha = 1.0;
}


/** \brief Set the color offsets.
 *
 * This function lets you define the color offsets.
 *
 * By default the offsets are all zeroes (0.0f).
 *
 * \param[in] red The red offset
 * \param[in] green The green offset
 * \param[in] blue The blue offset
 * \param[in] alpha The alpha offset
 *
 * \sa ColorTransform::SetMult(double red, double green, double blue, double alpha)
 */
void ColorTransform::SetAdd(double red, double green, double blue, double alpha)
{
	f_add_red   = red;
	f_add_green = green;
	f_add_blue  = blue;
	f_add_alpha = alpha;
}


/** \brief Set the color scalings.
 *
 * This function lets you define the color scalings.
 *
 * By default the scalings are all ones (1.0f).
 *
 * \param[in] red The red scaling
 * \param[in] green The green scaling
 * \param[in] blue The blue scaling
 * \param[in] alpha The alpha scaling
 *
 * \sa ColorTransform::SetAdd(double red, double green, double blue, double alpha)
 */
void ColorTransform::SetMult(double red, double green, double blue, double alpha)
{
	f_mult_red   = red;
	f_mult_green = green;
	f_mult_blue  = blue;
	f_mult_alpha = alpha;
}


/** \brief Check whether this transformation has an effect.
 *
 * This function returns true if all the offsets are zeroes (0.0f)
 * and the scalings are all ones (1.0f).
 *
 * The test is performed with the internal values transformed to
 * 16 bits signed 8.8 fixed values. This means rounding may be applied
 * before the test is applied.
 *
 * This function is used to know whether a color transform needs
 * to be saved in the output. A null color transform is not saved
 * if not really necessary.
 *
 * \param[in] with_alpha Whether the alpha channel is tested
 *
 * \return true if the color transform matrix will have no effect
 * on the user colors.
 *
 * \sa sswf::ColorTransform::IsSolidCompatible(void) const
 * \sa TagBase::Double2Signed16(double value)
 */
bool ColorTransform::IsNull(bool with_alpha) const
{
	// if this is true, then this transformation is the Null transformation
	long	ar, ag, ab, aa, mr, mg, mb, ma;

	ar = TagBase::Double2Signed16(f_add_red  );
	ag = TagBase::Double2Signed16(f_add_green);
	ab = TagBase::Double2Signed16(f_add_blue );
	aa = TagBase::Double2Signed16(f_add_alpha);

	mr = TagBase::Double2Signed16(f_mult_red  );
	mg = TagBase::Double2Signed16(f_mult_green);
	mb = TagBase::Double2Signed16(f_mult_blue );
	ma = TagBase::Double2Signed16(f_mult_alpha);

	if(with_alpha) {
		return ar == 0 && ag == 0 && ab == 0 && aa == 0
			&& mr == 256 && mg == 256 && mb == 256 && ma == 256;
	}

	return ar == 0 && ag == 0 && ab == 0
			&& mr == 256 && mg == 256 && mb == 256;
}


/** \brief Check whether this color transform has no alpha.
 *
 * This function checks whether the color transform has an alpha
 * channel offset set to zero (0.0f) and a scaling of one (1.0f).
 *
 * The test is performed with the internal values transformed to
 * 16 bits signed 8.8 fixed values. This means rounding may be applied
 * before the test is applied.
 *
 * \return true if the color transform is solid
 *
 * \sa sswf::ColorTransform::IsNull(bool with_alpha) const
 * \sa TagBase::Double2Signed16(double value)
 */
bool ColorTransform::IsSolidCompatible(void) const
{
	// if this is true, we can consider the color transformation
	// has solid (opposed to a transformation generating a semi
	// transparent or completely transparent shape.)
	long	aa, ma;

	aa = TagBase::Double2Signed16(f_add_alpha);
	ma = TagBase::Double2Signed16(f_mult_alpha);

	return aa == 0 && ma == 256;
}


/** \brief Save the color transformation in a buffer
 *
 * This function saves the color transformation matrix in the
 * specified data buffer.
 *
 * The function ensures that the result is saved as compressed as
 * possible. If the scalings are all ones (1.0f), then it is not
 * saved. If the offsets are all zeroes (0.0f), then it is not
 * saved.
 *
 * The number of bits used to save the scaling and offset values
 * is computed so the smallest possible number of bits is used.
 *
 * \param[in] data The buffer where the color transform is saved
 * \param[in] save_alpha Whether the alpha channel needs to be saved
 */
void ColorTransform::Save(Data& data, bool save_alpha)
{
	bool	has_add, has_mult;
	int	sz, bits;
	long	ar, ag, ab, aa, mr, mg, mb, ma;

	ar = TagBase::Double2Signed16(f_add_red  );
	ag = TagBase::Double2Signed16(f_add_green);
	ab = TagBase::Double2Signed16(f_add_blue );
	aa = TagBase::Double2Signed16(f_add_alpha);

	mr = TagBase::Double2Signed16(f_mult_red  );
	mg = TagBase::Double2Signed16(f_mult_green);
	mb = TagBase::Double2Signed16(f_mult_blue );
	ma = TagBase::Double2Signed16(f_mult_alpha);

	if(save_alpha) {
		has_add = ar != 0 || ag != 0 || ab != 0 || aa != 0;
		has_mult = mr != 256 || mg != 256 || mb != 256 || ma != 256;
	}
	else {
		// don't take the alpha in account
		has_add = ar != 0 || ag != 0 || ab != 0;
		has_mult = mr != 256 || mg != 256 || mb != 256;
	}

	sz = 0;

	if(has_add) {
		bits = TagBase::SIBitSize(ar);
		if(bits > sz) {
			sz = bits;
		}
		bits = TagBase::SIBitSize(ag);
		if(bits > sz) {
			sz = bits;
		}
		bits = TagBase::SIBitSize(ab);
		if(bits > sz) {
			sz = bits;
		}
		if(save_alpha) {
			bits = TagBase::SIBitSize(aa);
			if(bits > sz) {
				sz = bits;
			}
		}
	}
	if(has_mult) {
		bits = TagBase::SIBitSize(mr);
		if(bits > sz) {
			sz = bits;
		}
		bits = TagBase::SIBitSize(mg);
		if(bits > sz) {
			sz = bits;
		}
		bits = TagBase::SIBitSize(mb);
		if(bits > sz) {
			sz = bits;
		}
		if(save_alpha) {
			bits = TagBase::SIBitSize(ma);
			if(bits > sz) {
				sz = bits;
			}
		}
	}

	data.Align();
	data.WriteBits(has_add, 1);
	data.WriteBits(has_mult, 1);
	data.WriteBits(sz, 4);

	if(has_mult) {
		data.WriteBits(mr, sz);
		data.WriteBits(mg, sz);
		data.WriteBits(mb, sz);
		if(save_alpha) {
			data.WriteBits(ma, sz);
		}
	}
	if(has_add) {
		data.WriteBits(ar, sz);
		data.WriteBits(ag, sz);
		data.WriteBits(ab, sz);
		if(save_alpha) {
			data.WriteBits(aa, sz);
		}
	}
}





/////////////////////////////////////////// Matrix


/** \class sswf::Matrix
 *
 * \brief The position matrix.
 *
 * This class is used to position shapes. The matrix
 * supports rotations, skews, offsets and scaling.
 *
 * The matrix is a 2D matrix and thus is only a 3x3.
 *
 * The default is the identity matrix:
 *
 * \code
 *  | 1 0 0 |
 *  | 0 1 0 |
 *  | 0 0 1 |
 * \endcode
 *
 * However, the matrix is not managed as a matrix.
 * Instead, you define the scaling, skewing, rotation
 * and translation parameters. The library takes care
 * of generating the matrix when it is saved in the
 * SWF movie.
 *
 * Notice that the translation is defined in twips
 * and not floating point values.
 *
 * \sa <a href="../SWFalexref.html#swf_matrix">SWF Alexis' Reference&mdash;swf_matrix</a>
 */


/** \brief Initialize a matrix to a "no effect" matrix.
 *
 * The constructor creates and initializes a matrix with
 * no effect. (i.e. the identity matrix.)
 *
 * \sa sswf::Matrix::Reset(void)
 */
Matrix::Matrix(void)
{
	Reset();
}


/** \brief Reset a matrix to a "no effect" matrix.
 *
 * The Reset() function sets all the parameters to
 * no effect. (i.e. the identity matrix.)
 */
void Matrix::Reset(void)
{
	f_scale_x = 1.0;
	f_scale_y = 1.0;
	f_rotate = 0.0;
	f_translate_x = 0;
	f_translate_y = 0;
	f_skew_0 = 0.0;
	f_skew_1 = 0.0;
}


/** \fn sswf::Matrix::SetScale(double x, double y)
 *
 * \brief Define the horizontal and vertical scaling factors.
 *
 * This function changes the matrix scaling. This function can be used
 * to change the horizontal and vertical scaling with different values.
 * This let's you scale objects exactly the way you want.
 *
 * A call to this function is not cumulative. Each call sets the
 * new scaling values to the specified (x, y) scaling by overwriting the
 * previous values.
 *
 * \param[in] x Defines the new horizontal scaling
 * \param[in] y Defines the new vertical scaling
 *
 * \sa sswf::Matrix::SetScale(double scale)
 * \sa sswf::Matrix::SetRotate(double rotate)
 * \sa sswf::Matrix::SetSkew(double skew0, double skew1)
 * \sa sswf::Matrix::SetTranslate(long x, long y)
 */


/** \fn sswf::Matrix::SetScale(double scale)
 *
 * \brief Define the scale of the object.
 *
 * This function changes the matrix scaling. This function can be used
 * to change the scaling uniformly in both directions. This is often
 * used when you need to enlarge or reduce an object without losing
 * its aspect ratio.
 *
 * A call to this function is not cumulative. Each call sets the
 * new scale to the specified value by overwriting the previous value.
 *
 * It is equivalent to call SetScale() with the same value in x and y:
 *
 * \code
 *  // both calls are equivalent
 *  SetScale(scale);
 *  SetScale(scale, scale);
 * \endcode
 *
 * \param[in] scale Defines the new horizontal and vertical scaling
 *
 * \sa sswf::Matrix::SetScale(double x, double y)
 * \sa sswf::Matrix::SetRotate(double rotate)
 * \sa sswf::Matrix::SetSkew(double skew0, double skew1)
 * \sa sswf::Matrix::SetTranslate(long x, long y)
 */


/** \brief Change the rotation.
 *
 * \brief Set the rotation in radian.
 *
 * The SetRotate() function takes a radian angle and saves
 * it as the current rotation angle. This function is not
 * cumulative. Instead the new angle overwrites any other
 * angle previously defined with SetRotate().
 *
 * \param[in] rotate The rotation angle
 *
 * \sa sswf::Matrix::SetScale(double x, double y)
 * \sa sswf::Matrix::SetScale(double scale)
 * \sa sswf::Matrix::SetSkew(double skew0, double skew1)
 * \sa sswf::Matrix::SetTranslate(long x, long y)
 */
void Matrix::SetRotate(double rotate)
{
	// so the comparisons work for similar yet
	// possibly not equal angles
	f_rotate = fmod(rotate, M_PI * 2.0);
}


/** \fn sswf::Matrix::SetSkew(double skew0, double skew1)
 *
 * \brief Set a skew (sliding off).
 *
 * This function is used to change the skew parameters. The skew
 * makes the top and bottom (or left and right) edges move horizontally
 * (vertically) away from each others.
 *
 * The skew defined by this function is not cumulative. Instead it
 * replaces the current value with the new values.
 *
 * The unit of the skew is the twips times the scaling factors.
 *
 * \param[in] skew0 The horizontal skew
 * \param[in] skew1 The vertical skew
 *
 * \sa sswf::Matrix::SetScale(double x, double y)
 * \sa sswf::Matrix::SetScale(double scale)
 * \sa sswf::Matrix::SetRotate(double rotate)
 * \sa sswf::Matrix::SetTranslate(long x, long y)
 */


/** \fn sswf::Matrix::SetTranslate(long x, long y)
 *
 * \brief Define the matrix translation.
 *
 * Set the translation to (x, y). Previous translations are replaced
 * by each new call to this function.
 *
 * The translation is defined in twips and taken as long values.
 * (i.e. 20 represents 1 pixel on the screen.)
 *
 * \param[in] x The horizontal translation
 * \param[in] y The vertical translation
 *
 * \sa sswf::Matrix::SetScale(double x, double y)
 * \sa sswf::Matrix::SetScale(double scale)
 * \sa sswf::Matrix::SetRotate(double rotate)
 * \sa sswf::Matrix::SetSkew(double skew0, double skew1)
 */


/** \brief Internal function used to compute the matrix.
 *
 * This function computes the matrix of signed values.
 * The matrix is saved as 4x 16.16 fixed point values.
 * This function computes the 4 fixed point values
 * using the current definition of the matrix (i.e.
 * rotation, skew and scaling.) The translation is
 * not added in this matrix since this matrix is a
 * 2x2 matrix.
 *
 * \param[out] m When the computed matrix is saved
 *
 * \sa sswf::Matrix::IsNull(void) const
 * \sa Matrix::Save(Data& data)
 * \sa Matrix::operator == (const Matrix& matrix) const
 * \sa Matrix::operator != (const Matrix& matrix) const
 */
void Matrix::ComputeMatrix(signed_matrix_t& m) const
{
	double		m00, m01, m10, m11, s, c;

	c = cos(f_rotate);
	s = sin(f_rotate);
	m00 =  f_scale_x * c;
	m01 =  f_scale_y * s + f_skew_0;
	m10 = -f_scale_x * s + f_skew_1;
	m11 =  f_scale_y * c;

	// Note that the matrix is saved as:
	//	m[0], m[3]
	//	m[2], m[1]
	// (i.e. in diagonals)
	m.m[0] = TagBase::Double2Signed(m00);
	m.m[1] = TagBase::Double2Signed(m01);
	m.m[2] = TagBase::Double2Signed(m10);
	m.m[3] = TagBase::Double2Signed(m11);
}


/** \brief Check whether the matrix has an effect.
 *
 * This function checks whether the different parameters define
 * a null matrix. If so, then the function returns true.
 *
 * The test is performed with the floating point values transformed
 * in 16.16 fixed points as when they are saved in the final matrix.
 * To ensures that the normal rounding is applied and thus it catches
 * more matrices as null matrices.
 *
 * \return true if the matrix is the identity matrix.
 *
 * \sa sswf::TagBase::Double2Signed(double value)
 */
bool Matrix::IsNull(void) const
{
	signed_matrix_t	m;

	if(f_translate_x != 0 || f_translate_y != 0) {
		return false;
	}

	ComputeMatrix(m);

	return m.m[0] == 65536 && m.m[3] == 65536
		&& m.m[2] == 0 && m.m[1] == 0;
}


/** \brief Save a matrix to a data buffer.
 *
 * The Save() function computes the matrix and then saves it in
 * the specified data buffer.
 *
 * It is valid to save an identity matrix.
 *
 * \param[in] data The buffer where the matrix is to be saved
 *
 * \sa sswf::Matrix::IsNull(void) const
 */
void Matrix::Save(Data& data)
{
	signed_matrix_t	m;
	long		sz, bits;

	ComputeMatrix(m);

	data.Align();

	// save scaling diagonal
	if(m.m[0] != 65536 || m.m[3] != 65536) {
		data.WriteBits(1, 1);
		sz = TagBase::SIBitSize(m.m[0]);
		bits = TagBase::SIBitSize(m.m[3]);
		if(bits > sz) {
			sz = bits;
		}
		data.WriteBits(sz, 5);
		data.WriteBits(m.m[0], sz);
		data.WriteBits(m.m[3], sz);
	}
	else {
		data.WriteBits(0, 1);
	}

	// save the other diagonal
	if(m.m[2] != 0.0 || m.m[1] != 0.0) {
		data.WriteBits(1, 1);
		sz = TagBase::SIBitSize(m.m[2]);
		bits = TagBase::SIBitSize(m.m[1]);
		if(bits > sz) {
			sz = bits;
		}
		data.WriteBits(sz, 5);
		data.WriteBits(m.m[2], sz);
		data.WriteBits(m.m[1], sz);
	}
	else {
		data.WriteBits(0, 1);
	}

	sz = TagBase::SIBitSize(f_translate_x);
	bits = TagBase::SIBitSize(f_translate_y);
	if(bits > sz) {
		sz = bits;
	}
	if(sz >= 32) {
		fprintf(stderr, "ERROR: invalid matrix - translation too large.\n");
	}
	data.WriteBits(sz, 5);
	if(sz > 0) {
		data.WriteBits(f_translate_x, sz);
		data.WriteBits(f_translate_y, sz);
	}
}




/** \brief Compare two matrices for equality.
 *
 * This operator compares two matrices to know whether
 * they are equal or not.
 *
 * Note that the doubles are first transformed into
 * 16.16 fixed point values and these are compared
 * for equality. This means more matrices may look
 * equal than what their exact definitions with doubles
 * would imply.
 *
 * \sa sswf::Matrix::operator != (const Matrix& matrix) const
 */
bool Matrix::operator == (const Matrix& matrix) const
{
	signed_matrix_t	lhs, rhs;

	if(f_translate_x != matrix.f_translate_x
	|| f_translate_y != matrix.f_translate_y) {
		return false;
	}

	ComputeMatrix(lhs);
	matrix.ComputeMatrix(rhs);

	return lhs.m[0] == rhs.m[0]
		&& lhs.m[1] == rhs.m[1]
		&& lhs.m[2] == rhs.m[2]
		&& lhs.m[3] == rhs.m[3];
}


/** \brief Compare two matrices for inequality.
 *
 * This operator compares two matrices to know whether
 * they are inequal.
 *
 * Note that the doubles are first transformed into
 * 16.16 fixed point values and these are compared
 * for inequality. This means less matrices may look
 * inequal than what their exact definitions with doubles
 * would imply.
 *
 * \sa sswf::Matrix::operator != (const Matrix& matrix) const
 */
bool Matrix::operator != (const Matrix& matrix) const
{
	return !operator == (matrix);
}




/* 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
 */
