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

/****************************************************************************
 *                                                                          *
 *  Module  :   fill.c                                                      *
 *                                                                          *
 *  Purpose :   fill paths                                                  *
 *                                                                          *
 ****************************************************************************/

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

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

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

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

#if (defined(SKY2_DRVMODEL_H))
#include "ps2fill.h"
#endif /* (defined(SKY2_DRVMODEL_H)) */

static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: fill.c,v 1.44 2001/08/30 11:35:34 johns Exp $";

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline off
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */

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

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

/****************************************************************************
 Local Defines
 */
#define FILLMAXVERT 32

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

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

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

   Functions

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

#ifdef AMB_SPECIFIC
#ifdef RWDEBUG
static void
WireRender2d(RWIM3DVERTEX * vertex, int vcount,
             RWIMVERTEXINDEX * topo, int icount)
{
    RWIM3DVERTEX        local[256];
    int                 i;

    RWFUNCTION(RWSTRING("WireRender2d"));

    for (i = 0; i < vcount; i++)
    {
        local[i] = vertex[i];
        RWIM3DVERTEXSetRGBA(&(local[i]), 0, 0, 0, 255);
    }

    if (RwIm3DTransform(local, vcount, _rt2dCTMGet(), 0))
    {
        RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, topo,
                                     icount - 1);
        RwIm3DEnd();
    }

    RWRETURNVOID();
}
#endif /* RWDEBUG */
#endif /* AMB_SPECIFIC */

/****************************************************************************/
static Rt2dPath    *
DefaultPathClosedFill2d(Rt2dPath * path, Rt2dBrush * brush)
{
    Rt2dPath           *flat;
    rt2dPathNode       *pnode, *pleft, *pright;
    RwInt32             vcount, i, vindex;
    Rt2dBBox            bbox;
    RwV2d               posnormalize, avgnormal;
    RWIM3DVERTEX        vertex[256], *vdst;
    RwReal              layerDepth, inset;
    RwV2d               delta;
    RwV3d               pos;
    rt2dShadeParameters top, bottom, mid;
    RwMatrix           *ctm = _rt2dCTMGet();

    RWFUNCTION(RWSTRING("DefaultPathClosedFill2d"));

    RWASSERT(path);
    RWASSERT(path->closed);

    layerDepth = Rt2dGlobals.layerDepth;
    inset = path->inset;

    /* we'll be using the flattened path */
    flat = path;
    if (!path->flat)
    {
        flat = _rt2dScratchPath();
        _rt2dSubPathFlatten(flat, path);
    }

    pnode = (rt2dPathNode *) rwSListGetArray(flat->segments);
    vcount = rwSListGetNumEntries(flat->segments);

    /* we need the bbox to generate vertex shading info */
    bbox.x = bbox.w = pnode[0].pos.x;
    bbox.y = bbox.h = pnode[0].pos.y;

    /* get left/right NOT pos/width */
    _rt2dFlatSubPathExtendBBox(flat, &bbox);
    posnormalize.x = ((RwReal) 1.0) / (bbox.w - bbox.x);
    posnormalize.y = ((RwReal) 1.0) / (bbox.h - bbox.y);

    if (brush->texture)
    {
        const RwTextureFilterMode filterMode =
            RwTextureGetFilterMode(brush->texture);
        const RwTextureAddressMode addressingMode =
            RwTextureGetAddressing(brush->texture);

        RwRenderStateSet(rwRENDERSTATETEXTURERASTER,
                         (void *) RwTextureGetRaster(brush->texture));
        RwRenderStateSet(rwRENDERSTATETEXTUREFILTER,
                         (void *) filterMode);
        RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS,
                         (void *) addressingMode);
    }
    else
    {
        RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
    }

    vdst = vertex;
    pright = pnode;
    pleft = pnode + vcount - 1;
    vindex = 256;              /* force new strip */
    for (i = 1; i < vcount; i++)
    {
        RwInt32             red;
        RwInt32             green;
        RwInt32             blue;
        RwInt32             alpha;

        /* need to flush? */
        if (vindex >= 256 - 2)
        {
            /* render */
            if (vdst - vertex)
            {
                if (RwIm3DTransform(vertex,
                                    vdst - vertex,
                                    ctm,
                                    (brush->texture ? rwIM3D_VERTEXUV : 0) |
                                    Rt2dGlobals.TransformFlags))
                {
                    RwIm3DRenderIndexedPrimitive
                        (rwPRIMTYPETRISTRIP, Rt2dGlobals.topo, vindex);
                    RwIm3DEnd();

#ifdef AMB_SPECIFIC
#ifdef RWDEBUG
                    WireRender2d(vertex, vdst - vertex,
                                 Rt2dGlobals.topo, vindex);
#endif
#endif
                }
            }

            vdst = vertex;

            /* start new strip */

            /* corner normal */
            RwV2dScale(&avgnormal, &pleft->normal, inset);
            RwV2dSub((RwV2d *) & pos, &pleft->pos, &avgnormal);

            delta.x = pos.x - bbox.x;
            delta.y = pos.y - bbox.y;
            delta.x *= posnormalize.x;
            delta.y *= posnormalize.y;

            /* interpolate across bottom edge */
            RwRGBARealScale(&bottom.col, &brush->dbottom.col, delta.x);
            RwV2dScale(&bottom.uv, &brush->dbottom.uv, delta.x);
            RwRGBARealAdd(&bottom.col, &brush->bottom.col, &bottom.col);
            RwV2dAdd(&bottom.uv, &brush->bottom.uv, &bottom.uv);

            /* interpolate across top edge */
            RwRGBARealScale(&top.col, &brush->dtop.col, delta.x);

            RwV2dScale(&top.uv, &brush->dtop.uv, delta.x);
            RwRGBARealAdd(&top.col, &brush->top.col, &top.col);
            RwV2dAdd(&top.uv, &brush->top.uv, &top.uv);

            /* interpolate top to bottom */
            RwRGBARealSub(&mid.col, &top.col, &bottom.col);
            RwV2dSub(&mid.uv, &top.uv, &bottom.uv);
            RwRGBARealScale(&mid.col, &mid.col, delta.y);
            RwV2dScale(&mid.uv, &mid.uv, delta.y);
            RwRGBARealAdd(&mid.col, &bottom.col, &mid.col);
            RwV2dAdd(&mid.uv, &bottom.uv, &mid.uv);

            RWIM3DVERTEXSetPos(vdst, pos.x, pos.y, layerDepth);

            red = (RwInt32) mid.col.red;
            green = (RwInt32) mid.col.green;
            blue = (RwInt32) mid.col.blue;
            alpha = (RwInt32) mid.col.alpha;

            RWIM3DVERTEXSetRGBA(vdst,
                                (RwUInt8) red,
                                (RwUInt8) green,
                                (RwUInt8) blue, (RwUInt8) alpha);
            RWIM3DVERTEXSetU(vdst, mid.uv.x);
            RWIM3DVERTEXSetV(vdst, mid.uv.y);
            vdst++;
            pleft--;

            RwV2dScale(&avgnormal, &pright->normal, inset);
            RwV2dSub((RwV2d *) & pos, &pright->pos, &avgnormal);

            delta.x = pos.x - bbox.x;
            delta.y = pos.y - bbox.y;
            delta.x *= posnormalize.x;
            delta.y *= posnormalize.y;

            /* interpolate across bottom edge */
            RwRGBARealScale(&bottom.col, &brush->dbottom.col, delta.x);
            RwV2dScale(&bottom.uv, &brush->dbottom.uv, delta.x);
            RwRGBARealAdd(&bottom.col, &brush->bottom.col, &bottom.col);
            RwV2dAdd(&bottom.uv, &brush->bottom.uv, &bottom.uv);

            /* interpolate across top edge */
            RwRGBARealScale(&top.col, &brush->dtop.col, delta.x);
            RwV2dScale(&top.uv, &brush->dtop.uv, delta.x);
            RwRGBARealAdd(&top.col, &brush->top.col, &top.col);
            RwV2dAdd(&top.uv, &brush->top.uv, &top.uv);

            /* interpolate top to bottom */
            RwRGBARealSub(&mid.col, &top.col, &bottom.col);
            RwV2dSub(&mid.uv, &top.uv, &bottom.uv);
            RwRGBARealScale(&mid.col, &mid.col, delta.y);
            RwV2dScale(&mid.uv, &mid.uv, delta.y);
            RwRGBARealAdd(&mid.col, &bottom.col, &mid.col);
            RwV2dAdd(&mid.uv, &bottom.uv, &mid.uv);

            RWIM3DVERTEXSetPos(vdst, pos.x, pos.y, layerDepth);

            red = (RwInt32) mid.col.red;
            green = (RwInt32) mid.col.green;
            blue = (RwInt32) mid.col.blue;
            alpha = (RwInt32) mid.col.alpha;

            RWIM3DVERTEXSetRGBA(vdst,
                                (RwUInt8) red,
                                (RwUInt8) green,
                                (RwUInt8) blue, (RwUInt8) alpha);

            RWIM3DVERTEXSetU(vdst, mid.uv.x);
            RWIM3DVERTEXSetV(vdst, mid.uv.y);
            vdst++;
            pright++;

            vindex = 2;
        }

        RwV2dScale(&avgnormal, &pleft->normal, inset);
        RwV2dSub((RwV2d *) & pos, &pleft->pos, &avgnormal);

        delta.x = pos.x - bbox.x;
        delta.y = pos.y - bbox.y;
        delta.x *= posnormalize.x;
        delta.y *= posnormalize.y;

        /* interpolate across bottom edge */
        RwRGBARealScale(&bottom.col, &brush->dbottom.col, delta.x);
        RwV2dScale(&bottom.uv, &brush->dbottom.uv, delta.x);
        RwRGBARealAdd(&bottom.col, &brush->bottom.col, &bottom.col);
        RwV2dAdd(&bottom.uv, &brush->bottom.uv, &bottom.uv);

        /* interpolate across top edge */
        RwRGBARealScale(&top.col, &brush->dtop.col, delta.x);
        RwV2dScale(&top.uv, &brush->dtop.uv, delta.x);
        RwRGBARealAdd(&top.col, &brush->top.col, &top.col);
        RwV2dAdd(&top.uv, &brush->top.uv, &top.uv);

        /* interpolate top to bottom */
        RwRGBARealSub(&mid.col, &top.col, &bottom.col);
        RwV2dSub(&mid.uv, &top.uv, &bottom.uv);
        RwRGBARealScale(&mid.col, &mid.col, delta.y);
        RwV2dScale(&mid.uv, &mid.uv, delta.y);
        RwRGBARealAdd(&mid.col, &bottom.col, &mid.col);
        RwV2dAdd(&mid.uv, &bottom.uv, &mid.uv);

        RWIM3DVERTEXSetPos(vdst, pos.x, pos.y, layerDepth);

        red = (RwInt32) mid.col.red;
        green = (RwInt32) mid.col.green;
        blue = (RwInt32) mid.col.blue;
        alpha = (RwInt32) mid.col.alpha;

        RWIM3DVERTEXSetRGBA(vdst,
                            (RwUInt8) red,
                            (RwUInt8) green,
                            (RwUInt8) blue, (RwUInt8) alpha);

        RWIM3DVERTEXSetU(vdst, mid.uv.x);
        RWIM3DVERTEXSetV(vdst, mid.uv.y);
        vdst++;
        pleft--;
        vindex++;

        if (pleft < pright)
        {
            break;
        }

        RwV2dScale(&avgnormal, &pright->normal, inset);
        RwV2dSub((RwV2d *) & pos, &pright->pos, &avgnormal);

        delta.x = pos.x - bbox.x;
        delta.y = pos.y - bbox.y;
        delta.x *= posnormalize.x;
        delta.y *= posnormalize.y;

        /* interpolate across bottom edge */
        RwRGBARealScale(&bottom.col, &brush->dbottom.col, delta.x);
        RwV2dScale(&bottom.uv, &brush->dbottom.uv, delta.x);
        RwRGBARealAdd(&bottom.col, &brush->bottom.col, &bottom.col);
        RwV2dAdd(&bottom.uv, &brush->bottom.uv, &bottom.uv);

        /* interpolate across top edge */
        RwRGBARealScale(&top.col, &brush->dtop.col, delta.x);
        RwV2dScale(&top.uv, &brush->dtop.uv, delta.x);
        RwRGBARealAdd(&top.col, &brush->top.col, &top.col);
        RwV2dAdd(&top.uv, &brush->top.uv, &top.uv);

        /* interpolate top to bottom */
        RwRGBARealSub(&mid.col, &top.col, &bottom.col);
        RwV2dSub(&mid.uv, &top.uv, &bottom.uv);
        RwRGBARealScale(&mid.col, &mid.col, delta.y);
        RwV2dScale(&mid.uv, &mid.uv, delta.y);
        RwRGBARealAdd(&mid.col, &bottom.col, &mid.col);
        RwV2dAdd(&mid.uv, &bottom.uv, &mid.uv);

        RWIM3DVERTEXSetPos(vdst, pos.x, pos.y, layerDepth);

        red = (RwInt32) mid.col.red;
        green = (RwInt32) mid.col.green;
        blue = (RwInt32) mid.col.blue;
        alpha = (RwInt32) mid.col.alpha;

        RWIM3DVERTEXSetRGBA(vdst,
                            (RwUInt8) red,
                            (RwUInt8) green,
                            (RwUInt8) blue, (RwUInt8) alpha);

        RWIM3DVERTEXSetU(vdst, mid.uv.x);
        RWIM3DVERTEXSetV(vdst, mid.uv.y);
        vdst++;
        pright++;
        vindex++;

        if (pleft < pright)
        {
            break;
        }
    }

    /* render scrag end */
    if (vdst - vertex)
    {
        if (RwIm3DTransform(vertex,
                            vdst - vertex, ctm,
                            (brush->texture ? rwIM3D_VERTEXUV : 0) |
                            Rt2dGlobals.TransformFlags))
        {
            RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRISTRIP,
                                         Rt2dGlobals.topo, vindex);
            RwIm3DEnd();

#ifdef AMB_SPECIFIC
#ifdef RWDEBUG
            WireRender2d(vertex, vdst - vertex,
                         Rt2dGlobals.topo, vindex);
#endif
#endif
        }
    }

    RWRETURN(path);
}

/****************************************************************************/
static Rt2dPath    *
DefaultPathFill2d(Rt2dPath * path, Rt2dBrush * brush)
{
    RWFUNCTION(RWSTRING("DefaultPathFill2d"));

    if (path)
    {
        if (path->closed)
        {
            DefaultPathClosedFill2d(path, brush);
        }

        DefaultPathFill2d(path->next, brush);
    }

    RWRETURN(path);
}

/**
 * \ingroup rt2d
 * \ref Rt2dPathFill
 * is used to fill the specified path using the colors and
 * texture coordinates of the given brush. The path must be closed for
 * this function to work properly.
 * The fill color for each point within the path is determined by
 * bilinear interpolation of the colors of the brush assuming they
 * represent the colors of the four corners of the path's
 * bounding-box. Hence, the fill color depends on the relative distance
 * of each interior point from the corner points of the path's
 * bounding-box.
 * If the brush also specifies texture coordinates and a texture image,
 * the path is filled with the image assuming that the bounding-box
 * corners have the texture coordinates of the brush.
 * The include file rt2d.h and the library file rt2d.lib are required to
 * use this function.
 * \param path  Pointer to the path.
 * \param brush  Pointer to the brush.
 * \return a pointer to the path if successful or NULL if there is an
 * error.
 * \see Rt2dBrushSetRGBA
 * \see Rt2dBrushSetUV
 * \see Rt2dBrushSetTexture
 * \see Rt2dPathStroke
 */

Rt2dPath           *
Rt2dPathFill(Rt2dPath * path, Rt2dBrush * brush)
{
    RWAPIFUNCTION(RWSTRING("Rt2dPathFill"));

#ifdef SKY2_DRVMODEL_H

    _rt2dPS2PathFill(path, brush);

#else /* SKY2_DRVMODEL_H */

    DefaultPathFill2d(path, brush);

#endif /* SKY2_DRVMODEL_H */

    RWRETURN(path);
}

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline on
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */
