/*
 * Functionality for 2D rendering
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

/****************************************************************************
 *                                                                          *
 *  Module  :   brush.c                                                     *
 *                                                                          *
 *  Purpose :   fill state                                              *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include <rwcore.h>
#include <rpdbgerr.h>

#include "brush.h"
#include "gstate.h"
#include "rt2d.h"

static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: brush.c,v 1.46 2001/07/19 13:15:46 johns Exp $";

/****************************************************************************
 Local Types
 */

/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */
#define MyRGBARealFromRGBA(o,i)                 \
MACRO_START                                     \
{                                               \
    (o)->red = (RwReal)(i)->red;                \
    (o)->green = (RwReal)(i)->green;            \
    (o)->blue = (RwReal)(i)->blue;              \
    (o)->alpha = (RwReal)(i)->alpha;            \
}                                               \
MACRO_STOP

/****************************************************************************
 Globals (across program)
 */

/****************************************************************************
 Local (static) Globals
 */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************/

/****************************************************************************/

void
_rt2dBrushClose(void)
{
    RWFUNCTION(RWSTRING("_rt2dBrushClose"));

    RwFreeListDestroy(Rt2dGlobals.brushFreeList);

    Rt2dGlobals.brushFreeList = (RwFreeList *)NULL;

    RWRETURNVOID();
}

/****************************************************************************/

RwBool
_rt2dBrushOpen(void)
{
    RWFUNCTION(RWSTRING("_rt2dBrushOpen"));

    Rt2dGlobals.brushFreeList =
        RwFreeListCreate(sizeof(Rt2dBrush), 20, 16);

    RWRETURN(TRUE);
}

/****************************************************************************/

/**
 * \ingroup rt2d
 * \ref Rt2dBrushCreate
 * is used to create a new brush for painting and filling
 * paths. Note that the primitive resulting from a painted path is
 * topologically equivalent to a rectangle.
 *
 * A newly created brush has a number of default properties but these can
 * be defined by an application using the available API functions.
 *
 * Relevant properties are:
 *
 * \par Brush width
 * Used only when painting a path; width is measured perpendicular to
 * path and the path lies at the center of the painted primitive.
 * Default: 1.0.
 *
 * \par Brush colors
 * Four colors are required: For painting they define the colors at the
 * corners of the resulting primitive; for filling they define the colors
 * at the corners of the path's bounding-box. In both cases, the corner
 * colors are ordered anticlockwise and interior colors are determined by
 * bilinear interpolation.
 * Default: All colors are opaque white.
 *
 * \par Brush texture coordinates
 * Four (u, v) pairs are required: For painting they define the texture
 * coordinates at the corners of the resulting primitive; for filling
 * they define the texture coordinates at the corners of the path's
 * bounding-box. In both cases, corner texture coordinates are ordered
 * anticlockwise and interior coordinates are determined by bilinear
 * interpolation.
 * Default: All texture coordinates are zero.
 *
 * \par Brush texture image
 * Image used for texturing; texture coordinates must also be properly
 * defined.
 * Default:NULL.
 *
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \return a pointer to the new brush if successful or NULL if there is
 * an error.
 * \see Rt2dBrushDestroy
 * \see Rt2dBrushSetWidth
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetTexture
 */
Rt2dBrush          *
Rt2dBrushCreate(void)
{
    Rt2dBrush          *brush;

    RWAPIFUNCTION(RWSTRING("Rt2dBrushCreate"));

    brush = (Rt2dBrush *)RwFreeListAlloc(Rt2dGlobals.brushFreeList);

    if (brush)
    {
        RwRGBA              colour;
        RwV2d               uv;

        memset(brush, 0, sizeof(Rt2dBrush));

        colour.red = 255;
        colour.green = 255;
        colour.blue = 255;
        colour.alpha = 255;
        Rt2dBrushSetRGBA(brush, &colour, &colour, &colour, &colour);

        uv.x = (RwReal) (0.0);
        uv.y = (RwReal) (0.0);
        Rt2dBrushSetUV(brush, &uv, &uv, &uv, &uv);
        Rt2dBrushSetTexture(brush, (RwTexture *)NULL);

        Rt2dBrushSetWidth(brush, (RwReal) (1.0));

        RWRETURN(brush);
    }

    RWRETURN((Rt2dBrush *)NULL);
}

/****************************************************************************/

/**
 * \ingroup rt2d
 * \ref Rt2dBrushDestroy
 * is used to destroy the specified brush. All brushes
 * created by an application must be destroyed before the application
 * closes down.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param brush  Pointer to the brush.
 * \return TRUE if successful or FALSE if there is an error.
 * \see Rt2dBrushCreate
 * \see Rt2dOpen
 * \see Rt2dClose
 */
RwBool
Rt2dBrushDestroy(Rt2dBrush * brush)
{
    RWAPIFUNCTION(RWSTRING("Rt2dBrushDestroy"));

    if (brush)
    {
        RwFreeListFree(Rt2dGlobals.brushFreeList, brush);

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

/**
 * \ingroup rt2d
 * \ref Rt2dBrushSetRGBA
 * is used to define the color of the specified brush.
 * Four colors are required: For painting they define the colors at the
 * corners of the resulting primitive; for filling they define the colors
 * at the corners of the path's bounding-box. In both cases, corner
 * colors are ordered anticlockwise and interior colors are determined by
 * bilinear interpolation.
 * By default all colors are opaque white.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 *
 * \param brush  Pointer to the brush.
 * \param col0  Pointer to the first color.
 * \param col1  Pointer to the second color.
 * \param col2  Pointer to the third color.
 * \param col3  Pointer to the fourth color.
 *
 * \return pointer to the brush if successful or NULL if there is an
 * error.
 *
 * \see Rt2dBrushSetWidth
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetTexture
 * \see Rt2dBrushCreate
 * \see Rt2dPathStroke
 * \see Rt2dPathFill
 */
Rt2dBrush          *
Rt2dBrushSetRGBA(Rt2dBrush * brush, RwRGBA * col0, RwRGBA * col1,
                 RwRGBA * col2, RwRGBA * col3)
{
#ifndef SKY
    RwInt32             i;
#endif /* SKY */

    RWAPIFUNCTION(RWSTRING("Rt2dBrushSetRGBA"));

    RWASSERT(brush);

    MyRGBARealFromRGBA(&brush->top.col, col3);
    MyRGBARealFromRGBA(&brush->dtop.col, col2);
    RwRGBARealSub(&brush->dtop.col, &brush->dtop.col, &brush->top.col);

    MyRGBARealFromRGBA(&brush->bottom.col, col0);
    MyRGBARealFromRGBA(&brush->dbottom.col, col1);
    RwRGBARealSub(&brush->dbottom.col, &brush->dbottom.col,
                  &brush->bottom.col);

    /* setup vertex colours if possible */
    brush->calcFields |= FIELDRGBA;
    if ((brush->dtop.col.red == ((RwReal) 0)) &&
        (brush->dtop.col.green == ((RwReal) 0)) &&
        (brush->dtop.col.blue == ((RwReal) 0)) &&
        (brush->dtop.col.alpha == ((RwReal) 0)) &&
        (brush->dbottom.col.red == ((RwReal) 0)) &&
        (brush->dbottom.col.green == ((RwReal) 0)) &&
        (brush->dbottom.col.blue == ((RwReal) 0)) &&
        (brush->dbottom.col.alpha == ((RwReal) 0)))
    {

#ifndef SKY

        /* Only need to copy the RGBA into the vertex array if
         * not on PSX2.
         */

        RWIM3DVERTEX       *vptr = brush->vertex;
        RWIM3DVERTEX        inside, outside;

        RWIM3DVERTEXSetRGBA(&inside,
                            col2->red, col2->green,
                            col2->blue, col2->alpha);
        RWIM3DVERTEXSetRGBA(&outside,
                            col0->red, col0->green,
                            col0->blue, col0->alpha);

        for (i = 0; i < (256 / 2); i++)
        {
            /* use fast copy if available */
#ifdef RWIM3DVERTEXCopyRGBA
            RWIM3DVERTEXCopyRGBA(vptr, &inside);
            vptr++;
            RWIM3DVERTEXCopyRGBA(vptr, &outside);
            vptr++;
#else /* RWIM3DVERTEXCopyRGBA */
            RWIM3DVERTEXSetRGBA(vptr,
                                col2->red, col2->green,
                                col2->blue, col2->alpha);
            vptr++;
            RWIM3DVERTEXSetRGBA(vptr,
                                col0->red, col0->green,
                                col0->blue, col0->alpha);
            vptr++;
#endif /* RWIM3DVERTEXCopyRGBA */

        }

#endif /* SKY2 */

        brush->calcFields &= ~FIELDRGBA;
    }

    RWRETURN(brush);
}

/****************************************************************************/

/**
 * \ingroup rt2d
 * \ref Rt2dBrushSetUV
 * is used to define the texture coordinates for the
 * specified brush.
 * Four (u, v) pairs are required: For painting they define the texture
 * coordinates at the corners of the resulting primitive; for filling
 * they define the texture coordinates at the corners of the path's
 * bounding-box. In both cases, corner texture coordinates are ordered
 * anticlockwise and interior coordinates are determined by bilinear
 * interpolation.
 * By default all texture coordinates are zero.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param brush  Pointer to the brush.
 * \param uv0  Pointer to the first texture coordinates.
 * \param uv1  Pointer to the second texture coordinates.
 * \param uv2  Pointer to the third texture coordinates.
 * \param uv3  Pointer to the fourth texture coordinates.
 * \return pointer to the brush if successful or NULL if there is an
 * error.
 * \see Rt2dBrushSetTexture
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetWidth
 * \see Rt2dBrushCreate
 */
Rt2dBrush          *
Rt2dBrushSetUV(Rt2dBrush * brush, RwV2d * uv0, RwV2d * uv1, RwV2d * uv2,
               RwV2d * uv3)
{
    RwInt32             i;

    RWAPIFUNCTION(RWSTRING("Rt2dBrushSetUV"));

    RWASSERT(brush);

    brush->top.uv = *uv3;
    RwV2dSub(&brush->dtop.uv, uv2, uv3);

    brush->bottom.uv = *uv0;
    RwV2dSub(&brush->dbottom.uv, uv1, uv0);

    /* setup vertex UV if possible */
    brush->calcFields |= FIELDUV;
    if ((brush->dtop.uv.x == 0.0f) &&
        (brush->dtop.uv.y == 0.0f) &&
        (brush->dbottom.uv.x == 0.0f) && (brush->dbottom.uv.y == 0.0f))
    {
        for (i = 0; i < 256; i += 2)
        {
            RWIM3DVERTEXSetU(&brush->vertex[i], uv2->x);
            RWIM3DVERTEXSetV(&brush->vertex[i], uv2->y);
            RWIM3DVERTEXSetU(&brush->vertex[i + 1], uv0->x);
            RWIM3DVERTEXSetV(&brush->vertex[i + 1], uv0->y);
        }
        brush->calcFields &= ~FIELDUV;
    }

    RWRETURN(brush);
}

/****************************************************************************/

/**
 * \ingroup rt2d
 * \ref Rt2dBrushSetTexture
 * is used to define a texture for the specified
 * brush using the given image. For texturing to work properly,
 * appropriate texture coordinates must also be defined.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param brush  Pointer to the brush.
 * \param texture  Pointer to the texture.
 * \return pointer to the brush if successful or NULL if there is an
 * error.
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetWidth
 * \see Rt2dBrushCreate
 */
Rt2dBrush          *
Rt2dBrushSetTexture(Rt2dBrush * brush, RwTexture * texture)
{
    RWAPIFUNCTION(RWSTRING("Rt2dBrushSetTexture"));

    RWASSERT(brush);

    brush->texture = texture;
    if (texture)
    {
        brush->calcFields |= FIELDUV;
    }
    else
    {
        brush->calcFields &= ~FIELDUV;
    }

    RWRETURN(brush);
}

/****************************************************************************/

 /* All of the Following functions are for Debug version only
  * They each have a macro counterpart which is used in the
  * Release builds.
  */

#if ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )

/**
 * \ingroup rt2d
 * \ref Rt2dBrushSetWidth
 * is used to define the width of the specified brush.
 * The width is used only when painting a path; it is measured
 * perpendicular to path and the path lies at the center of the painted
 * primitive.
 * A newly created brush has a unit width.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 *
 * \note This function is used for debug purposes only and, for
 * efficiency, is available as a macro for final release versions of an
 * application.
 *
 * \param brush  Pointer to the brush.
 * \param width  A RwReal value equal to the width of the brush.
 * \return pointer to the brush if successful or NULL if there is an
 * error.
 * \see Rt2dBrushGetWidth
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetTexture
 * \see Rt2dBrushCreate
 */
Rt2dBrush          *
Rt2dBrushSetWidth(Rt2dBrush * brush, RwReal width)
{
    RWAPIFUNCTION(RWSTRING("Rt2dBrushSetWidth"));

    RWASSERT(brush);

    Rt2dBrushSetWidthMacro(brush, width);

    RWRETURN(brush);
}

/****************************************************************************/

/**
 * \ingroup rt2d
 * \ref Rt2dBrushGetWidth
 * is used to retrieve the width of the specified
 * brush.
 * The width is used only when painting a path; it is measured
 * perpendicular to path and the path lies at the center of the painted
 * primitive.
 * A newly created brush has a unit width.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 *
 * Note that this function is used for debug purposes only and, for
 * efficiency, is available as a macro for final release versions of an
 * application.
 *
 * \param brush  Pointer to the brush.
 * \return a RwReal value equal to the width of the brush if successful
 * or zero if there is an error.
 * \see Rt2dBrushSetWidth
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetTexture
 * \see Rt2dBrushCreate
 */
RwReal
Rt2dBrushGetWidth(Rt2dBrush * brush)
{
    RWAPIFUNCTION(RWSTRING("Rt2dBrushGetWidth"));

    RWASSERT(brush);

    RWRETURN(Rt2dBrushGetWidthMacro(brush));
}

#endif /* ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) ) */
