/* libsswf_blendmode.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::BlendMode 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;



/////////////////////////////////////////// State


/** \class sswf::BlendMode
 *
 * \brief The mode used to render the object on the screen
 *
 * This class was introduced in version 1.8.0.
 *
 * This class is used to declare a blending mode which can be
 * shared by different classes.
 *
 * \sa sswf::State
 * \sa sswf::TagPlace
 * \sa <a href="../SWFalexref.html#tag_placeobject3">SWF Alexis' Reference&mdash;Place Object 3</a>
 */


/** \enum sswf::BlendMode::blend_mode_t
 *
 * \brief List all the available blend modes
 *
 * The following lists the blend modes available when placing an object
 * on your screen.
 *
 * In the following, I included formulas which use the following variables:
 *
 *	\li R - the resulting color
 *	\li C - the source color (i.e. the object in the TagPlace or State)
 *	\li B - the background (i.e. what has been drawn before this object)
 *
 * In some cases, a specific component is affected. This is marked by
 * a subscript with the component letter (one of RGBA as in: R<sub>a</sub>.)
 */


/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_UNDEFINED
 *
 * \brief According to Macromedia this is the same as sswf::BlendMode::BLEND_MODE_NORMAL
 *
 * This value should probably not be used, yet according to the
 * Macromedia documentation, it is equivalent to sswf::BlendMode::BLEND_MODE_NORMAL
 *
 * This is a <i>do nothing</i> blending mode.
 *
 * \code
 * R = C
 * \endcode
 */


/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_NORMAL
 *
 * \brief A <i>do nothing</i> blending mode
 *
 * This blending mode has no effect on the object being placed.
 *
 * \note
 * This may be a place holder in case you want to apply some blending mode
 * with an ActionScript. Without it you may not be able to apply any blending
 * mode from a script (TBD).
 *
 * \code
 * R = C
 * \endcode
 */


/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_LAYER
 *
 * \brief Use multiple objects
 *
 * This blending mode uses multiple objects to render the final output. At
 * this time, it isn't clear to me what this does...
 */


/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_MULTIPLY
 *
 * \brief Multiply the background and the object colors (light effect)
 *
 * This blending mode can be used to multiply the background colors with
 * the object being placed. When the object being placed is a grey scale,
 * this is a way to apply brightness to the background. Thus you can
 * generate the effect of a light (or a reflection of a light) on the
 * screen.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R = B &times; C / 255
</pre></div>
\endhtmlonly
 */




/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_SCREEN
 *
 * \brief Multiply the inverse of the background and the object colors
 *
 * There is probably no good reason to use the inverse color of the
 * object since the object could be rendered properly in the first
 * place (i.e. if you need 255, use white and if you need 0 use black).
 * Yet, this filter multiplies the inverse of the background and the
 * object colors together.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R = (255 - B) &times; (255 - C) / 255
</pre></div>
\endhtmlonly
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_LIGHTEN
 *
 * \brief Use the largest of the two colors
 *
 * Select which of the background or the object being placed has the
 * largest component.
 *
 * Use black in your object to not affect the screen.
 *
 * \code
 * R = max(B, C)
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_DARKEN
 *
 * \brief Use the largest of the two colors
 *
 * Select which of the background or the object being placed has the
 * smallest component.
 *
 * Use white in your object to not affect the screen.
 *
 * \code
 * R = min(B, C)
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_ADD
 *
 * \brief Add the object components to the background
 *
 * This filter can be used for a strong brightness effect. Watchout, do not
 * create too bright an object for this blend mode. It would otherwise make
 * your screen look bad.
 *
 * \code
 * R = max(B + C, 255)
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_SUBTRACT
 *
 * \brief Subject the object components from the background
 *
 * This filter can be used for a strong darkning effect. Watchout, do not
 * create too bright an object for this blend mode. It would otherwise make
 * your screen look bad.
 *
 * \code
 * R = min(B - C, 0)
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_DIFFERENCE
 *
 * \brief Take the absolute value of the object color minus the background color
 *
 * At this time, I'm not too sure what this is for. The colors will loop around
 * with this object and in general this make the image look <i>weird</i>. May
 * be a good idea to create some sort of negative effect.
 *
 * \code
 * R = | B - C |
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_INVERT
 *
 * \brief Inverse the background colors
 *
 * This filter does not use the source image for anything more than know where
 * to apply the filter. Otherwise it inverts the color creating a negative
 * (the same as when you take pictures with your camera, the resulting film
 * is a negative -- the colors are invered.)
 *
 * \code
 * R = 255 - B
 * \endcode
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_ALPHA
 *
 * \brief Copy the object alpha in the background
 *
 * This filter copies the alpha channel as is from the object to the background.
 * Used in a Sprite, I suppose this can be used to switch between different
 * alpha channels for the same object.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R<sub>a</sub> = C<sub>a</sub>
</pre></div>
\endhtmlonly
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_ERASE
 *
 * \brief Copy the inverse of the object alpha in the background
 *
 * This filter copies the inverts of the alpha channel from the object to
 * the background. This as the effect of making the object itself
 * disappear... and what's around appear, I would think.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R<sub>a</sub> = 255 - C<sub>a</sub>
</pre></div>
\endhtmlonly
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_OVERLAY
 *
 * \brief Applies a MULTIPLY or SCREEN effect depending on the current background colors
 *
 * This filter copies the alpha channel as is from the object to the background.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R = (B &lt; 128 ? B &times; C : (255 - B) &times; (255 - C)) / 255
</pre></div>
\endhtmlonly
 */



/** \var sswf::BlendMode::blend_mode_t sswf::BlendMode::BLEND_MODE_HARDLIGHT
 *
 * \brief Applies a MULTIPLY or SCREEN effect depending on the current background colors
 *
 * This filter copies the alpha channel as is from the object to the background.
 *
\htmlonly
<div class="fragment"><pre class="fragment">
 R = (C &lt; 128 ? B &times; C : (255 - B) &times; (255 - C)) / 255
</pre></div>
\endhtmlonly
 */



/** \brief Initialize the blend mode to \e undefined
 *
 * The constructor initializes the blend mode to the default of:
 * BLEND_MODE_UNDEFINED.
 *
 * \sa sswf::BlendMode::HasBlendMode(void) const
 */
BlendMode::BlendMode(void)
{
	f_blend_mode = BLEND_MODE_UNDEFINED;
}


/** \brief Test whether a blending filter was defined
 *
 * This function determines whether a filter was defined in this BlendMode
 * object.
 *
 * This is the same as testing wheter the BlendMode::HasBlendMode(void) const
 * function returns BLEND_MODE_UNDEFINED.
 *
 * \return \e true when the blend mode is not BLEND_MODE_UNDEFINED; false otherwise
 *
 * \sa sswf::BlendMode::SetBlendMode(blend_mode_t blend_mode)
 * \sa sswf::BlendMode::SetBlendModeByName(const char *blend_mode_name)
 */
bool BlendMode::HasBlendMode(void) const
{
	return f_blend_mode != BLEND_MODE_UNDEFINED;
}


/** \brief Get the current blending mode
 *
 * This function returns the current blending mode as set by SetBlendMode()
 * or SetBlendModeByName().
 *
 * \return The current blend mode
 *
 * \sa sswf::BlendMode::SetBlendMode(blend_mode_t blend_mode)
 * \sa sswf::BlendMode::SetBlendModeByName(const char *blend_mode_name)
 */
BlendMode::blend_mode_t BlendMode::GetBlendMode(void) const
{
	return f_blend_mode;
}


/** \brief Defines a filter by value
 *
 * This function defines the filter to use with a TagPlace or State object.
 *
 * \note
 * Trying to set a mode which does not exist forces the mode to
 * BLEND_MODE_UNDEFINED.
 *
 * \param blend_mode The new blend mode (BLEND_MODE_...)
 *
 * \sa sswf::BlendMode::SetBlendModeName(const char *blend_mode_name)
 */
void BlendMode::SetBlendMode(blend_mode_t blend_mode)
{
	switch(blend_mode) {
	default: // case BLEND_MODE_UNDEFINED:
		f_blend_mode = BLEND_MODE_UNDEFINED;
		break;

	case BLEND_MODE_NORMAL:
	case BLEND_MODE_LAYER:
	case BLEND_MODE_MULTIPLY:
	case BLEND_MODE_SCREEN:
	case BLEND_MODE_LIGHTEN:
	case BLEND_MODE_DARKEN:
	case BLEND_MODE_DIFFERENCE:
	case BLEND_MODE_ADD:
	case BLEND_MODE_SUBTRACT:
	case BLEND_MODE_INVERT:
	case BLEND_MODE_ALPHA:
	case BLEND_MODE_ERASE:
	case BLEND_MODE_OVERLAY:
	case BLEND_MODE_HARDLIGHT:
		f_blend_mode = blend_mode;
		break;

	}

	return;
}


/** \brief Defines the filter by name
 *
 * This function is similar to the sswf::BlendMode::SetBlendMode(blend_mode_t blend_mode)
 * except that it takes a name instead of a number to define the blending mode
 * to use.
 *
 * The supported names are the same as those found in the
 * sswf::BlendMode::blend_mode_t enumeration without the "BLEND_MODE_"
 * part and excluding UNDEFINED (you can reset the blend mode to Undefined
 * by calling sswf::BlendMode::SetBlendMode(sswf::BlendMode::BLEND_MODE_UNDEFINED) instead.)
 *
 * \param blend_mode_name The ASCII name of the blend mode to use
 *
 * \return The new blend mode value or BLEND_MODE_UNDEFINED when the input
 * name was not a valid blend mode
 *
 * \sa sswf::TagBase::SetBlendMode(blend_mode_t blend_mode)
 */
bool BlendMode::SetBlendModeByName(const char *blend_mode_name)
{
	f_blend_mode = BLEND_MODE_UNDEFINED;

	if(blend_mode_name != 0) switch(blend_mode_name[0]) {
	case 'A':
		if(strcasecmp(blend_mode_name, "ADD") == 0) {
			f_blend_mode = BLEND_MODE_ADD;
		}
		else if(strcasecmp(blend_mode_name, "ALPHA") == 0) {
			f_blend_mode = BLEND_MODE_ALPHA;
		}
		break;

	case 'D':
		if(strcasecmp(blend_mode_name, "DARKEN") == 0) {
			f_blend_mode = BLEND_MODE_DARKEN;
		}
		else if(strcasecmp(blend_mode_name, "DIFFERENCE") == 0) {
			f_blend_mode = BLEND_MODE_DIFFERENCE;
		}
		break;

	case 'E':
		if(strcasecmp(blend_mode_name, "ERASE") == 0) {
			f_blend_mode = BLEND_MODE_ERASE;
		}
		break;

	case 'H':
		if(strcasecmp(blend_mode_name, "HARDLIGHT") == 0) {
			f_blend_mode = BLEND_MODE_HARDLIGHT;
		}
		break;

	case 'I':
		if(strcasecmp(blend_mode_name, "INVERT") == 0) {
			f_blend_mode = BLEND_MODE_INVERT;
		}
		break;

	case 'L':
		if(strcasecmp(blend_mode_name, "LAYER") == 0) {
			f_blend_mode = BLEND_MODE_LAYER;
		}
		else if(strcasecmp(blend_mode_name, "LIGHTEN") == 0) {
			f_blend_mode = BLEND_MODE_LIGHTEN;
		}
		break;

	case 'M':
		if(strcasecmp(blend_mode_name, "MULTIPLY") == 0) {
			f_blend_mode = BLEND_MODE_MULTIPLY;
		}
		break;

	case 'N':
		if(strcasecmp(blend_mode_name, "NORMAL") == 0) {
			f_blend_mode = BLEND_MODE_NORMAL;
		}
		break;

	case 'O':
		if(strcasecmp(blend_mode_name, "OVERLAY") == 0) {
			f_blend_mode = BLEND_MODE_OVERLAY;
		}
		break;

	case 'S':
		if(strcasecmp(blend_mode_name, "SCREEN") == 0) {
			f_blend_mode = BLEND_MODE_SCREEN;
		}
		else if(strcasecmp(blend_mode_name, "SUBTRACT") == 0) {
			f_blend_mode = BLEND_MODE_SUBTRACT;
		}
		break;

	}

	return f_blend_mode != BLEND_MODE_UNDEFINED;
}



/** \brief Save the filter in the specified Data buffer
 *
 * This function saves the specified blend mode in the specified Data buffer.
 *
 * \param data The Data buffer where the blend mode is to be saved
 */
void BlendMode::Save(Data& data)
{
	data.PutByte(f_blend_mode);
}


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