/*
** Copyright 1991, 1992, 1993, Silicon Graphics, Inc.
** All Rights Reserved.
**
** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
** the contents of this file may not be disclosed to third parties, copied or
** duplicated in any form, in whole or in part, without the prior written
** permission of Silicon Graphics, Inc.
**
** RESTRICTED RIGHTS LEGEND:
** Use, duplication or disclosure by the Government is subject to restrictions
** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
** and Computer Software clause at DFARS 252.227-7013, and/or in similar or
** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
** rights reserved under the Copyright Laws of the United States.
*/
#include "precomp.h"
#pragma hdrstop

#include <fixed.h>

/* This routine sets gc->polygon.shader.cfb to gc->drawBuffer */

static void FASTCALL FillSubTriangle(__GLcontext *gc, GLint iyBottom, GLint iyTop)
{
    GLint ixLeft, ixRight;
    GLint ixLeftFrac, ixRightFrac;
    GLint dxLeftFrac, dxRightFrac;
    GLint dxLeftLittle, dxRightLittle;
    GLint dxLeftBig, dxRightBig;
    GLint spanWidth, clipY0, clipY1;
    GLuint modeFlags;
#ifdef NT
    __GLstippleWord stackWords[__GL_MAX_STACK_STIPPLE_WORDS];
    __GLstippleWord *words;
    __GLcolor colors[__GL_MAX_STACKED_COLORS>>1];
    __GLcolor fbcolors[__GL_MAX_STACKED_COLORS>>1];
    __GLcolor *vColors, *vFbcolors;
    int iMaxWidth, iDy, dxLeft, dxRight;

    ixLeft = gc->polygon.shader.ixLeft;
    dxLeft = (gc->polygon.shader.dxLeftBig < gc->polygon.shader.dxLeftLittle) ?
        gc->polygon.shader.dxLeftBig : gc->polygon.shader.dxLeftLittle;
    ixRight = gc->polygon.shader.ixRight;
    dxRight = (gc->polygon.shader.dxRightBig > gc->polygon.shader.dxRightLittle) ?
        gc->polygon.shader.dxRightBig : gc->polygon.shader.dxRightLittle;
    iMaxWidth = ixRight - ixLeft;
    iDy = iyTop - iyBottom - 1;
    ixRight += dxRight * iDy;
    ixLeft += dxLeft * iDy;
    iMaxWidth = (iMaxWidth < (ixRight - ixLeft)) ? ixRight - ixLeft :
        iMaxWidth;
    
    if (iMaxWidth > __GL_MAX_STACK_STIPPLE_BITS)
    {
        words = gcTempAlloc(gc, (iMaxWidth+__GL_STIPPLE_BITS-1)/8);
        if (words == NULL)
        {
            return;
        }
    }
    else
    {
        words = stackWords;
    }
    
    if (iMaxWidth > (__GL_MAX_STACKED_COLORS>>1))
    {
        vColors = (__GLcolor *) gcTempAlloc(gc, iMaxWidth * sizeof(__GLcolor));
        if (NULL == vColors)
        {
            if (iMaxWidth > __GL_MAX_STACK_STIPPLE_BITS)
            {
                gcTempFree(gc, words);
            }
            return;
        }

        vFbcolors = (__GLcolor *) gcTempAlloc(gc, iMaxWidth * sizeof(__GLcolor));
        if (NULL == vFbcolors) {
            if (iMaxWidth > __GL_MAX_STACK_STIPPLE_BITS)
            {
                gcTempFree(gc, words);
            }
            gcTempFree(gc, vColors);
            return;
        }
    }
    else
    {
        vColors = colors;
        vFbcolors = fbcolors;
    }
#else
    __GLstippleWord words[__GL_MAX_STIPPLE_WORDS];
    __GLcolor vColors[__GL_MAX_MAX_VIEWPORT];/*XXX oink */
    __GLcolor vFbcolors[__GL_MAX_MAX_VIEWPORT];/*XXX oink */
#endif

    ixLeft = gc->polygon.shader.ixLeft;
    ixLeftFrac = gc->polygon.shader.ixLeftFrac;
    ixRight = gc->polygon.shader.ixRight;
    ixRightFrac = gc->polygon.shader.ixRightFrac;
    clipY0 = gc->transform.clipY0;
    clipY1 = gc->transform.clipY1;
    dxLeftFrac = gc->polygon.shader.dxLeftFrac;
    dxLeftBig = gc->polygon.shader.dxLeftBig;
    dxLeftLittle = gc->polygon.shader.dxLeftLittle;
    dxRightFrac = gc->polygon.shader.dxRightFrac;
    dxRightBig = gc->polygon.shader.dxRightBig;
    dxRightLittle = gc->polygon.shader.dxRightLittle;
    modeFlags = gc->polygon.shader.modeFlags;

    gc->polygon.shader.colors = vColors;
    gc->polygon.shader.fbcolors = vFbcolors;
    gc->polygon.shader.stipplePat = words;
    if (modeFlags & __GL_SHADE_STENCIL_TEST) {
        gc->polygon.shader.sbuf =
            __GL_STENCIL_ADDR(&gc->stencilBuffer, (__GLstencilCell*),
                              ixLeft, iyBottom);
    }
    if (modeFlags & __GL_SHADE_DEPTH_TEST) {
#ifdef NT
        if( gc->modes.depthBits == 32 )
            gc->polygon.shader.zbuf = (__GLzValue *)
                __GL_DEPTH_ADDR(&gc->depthBuffer, (__GLzValue*),
                                ixLeft, iyBottom);
        else
            gc->polygon.shader.zbuf = (__GLzValue *)
                __GL_DEPTH_ADDR(&gc->depthBuffer, (__GLz16Value*),
                                ixLeft, iyBottom);
#else
        gc->polygon.shader.zbuf =
            __GL_DEPTH_ADDR(&gc->depthBuffer, (__GLzValue*),
                            ixLeft, iyBottom);
#endif
    }
    gc->polygon.shader.cfb = gc->drawBuffer;
    while (iyBottom < iyTop) {
        spanWidth = ixRight - ixLeft;
        /*
        ** Only render spans that have non-zero width and which are
        ** not scissored out vertically.
        */
        if ((spanWidth > 0) && (iyBottom >= clipY0) && (iyBottom < clipY1)) {
            gc->polygon.shader.frag.x = ixLeft;
            gc->polygon.shader.frag.y = iyBottom;
            gc->polygon.shader.length = spanWidth;
            (*gc->procs.span.processSpan)(gc);
        }

        /* Advance right edge fixed point, adjusting for carry */
        ixRightFrac += dxRightFrac;
        if (ixRightFrac < 0) {
            /* Carry/Borrow'd. Use large step */
            ixRight += dxRightBig;
            ixRightFrac &= ~0x80000000;
        } else {
            ixRight += dxRightLittle;
        }

        iyBottom++;
        ixLeftFrac += dxLeftFrac;
        if (ixLeftFrac < 0) {
            /* Carry/Borrow'd.  Use large step */
            ixLeft += dxLeftBig;
            ixLeftFrac &= ~0x80000000;

            if (modeFlags & __GL_SHADE_RGB) {
                if (modeFlags & __GL_SHADE_SMOOTH) {
                    gc->polygon.shader.frag.color.r += gc->polygon.shader.rBig;
                    gc->polygon.shader.frag.color.g += gc->polygon.shader.gBig;
                    gc->polygon.shader.frag.color.b += gc->polygon.shader.bBig;
                    gc->polygon.shader.frag.color.a += gc->polygon.shader.aBig;
                }
                if (modeFlags & __GL_SHADE_TEXTURE) {
                    gc->polygon.shader.frag.s += gc->polygon.shader.sBig;
                    gc->polygon.shader.frag.t += gc->polygon.shader.tBig;
                    gc->polygon.shader.frag.qw += gc->polygon.shader.qwBig;
                }
            } else {
                if (modeFlags & __GL_SHADE_SMOOTH) {
                    gc->polygon.shader.frag.color.r += gc->polygon.shader.rBig;
                }
            }
            if (modeFlags & __GL_SHADE_STENCIL_TEST) {
                /* The implicit multiply is taken out of the loop */
                gc->polygon.shader.sbuf = (__GLstencilCell*)
                    ((GLubyte*) gc->polygon.shader.sbuf
                     + gc->polygon.shader.sbufBig);
            }
            if (modeFlags & __GL_SHADE_DEPTH_ITER) {
                gc->polygon.shader.frag.z += gc->polygon.shader.zBig;
            }

            if (modeFlags & __GL_SHADE_DEPTH_TEST) {
                /* The implicit multiply is taken out of the loop */
                gc->polygon.shader.zbuf = (__GLzValue*)
                    ((GLubyte*) gc->polygon.shader.zbuf
                     + gc->polygon.shader.zbufBig);
            }

            if (modeFlags & (__GL_SHADE_COMPUTE_FOG | __GL_SHADE_INTERP_FOG))
            {
                gc->polygon.shader.frag.f += gc->polygon.shader.fBig;
            }
        } else {
            /* Use small step */
            ixLeft += dxLeftLittle;
            if (modeFlags & __GL_SHADE_RGB) {
                if (modeFlags & __GL_SHADE_SMOOTH) {
                    gc->polygon.shader.frag.color.r += gc->polygon.shader.rLittle;
                    gc->polygon.shader.frag.color.g += gc->polygon.shader.gLittle;
                    gc->polygon.shader.frag.color.b += gc->polygon.shader.bLittle;
                    gc->polygon.shader.frag.color.a += gc->polygon.shader.aLittle;
                }
                if (modeFlags & __GL_SHADE_TEXTURE) {
                    gc->polygon.shader.frag.s += gc->polygon.shader.sLittle;
                    gc->polygon.shader.frag.t += gc->polygon.shader.tLittle;
                    gc->polygon.shader.frag.qw += gc->polygon.shader.qwLittle;
                }
            } else {
                if (modeFlags & __GL_SHADE_SMOOTH) {
                    gc->polygon.shader.frag.color.r += gc->polygon.shader.rLittle;
                }
            }
            if (modeFlags & __GL_SHADE_STENCIL_TEST) {
                /* The implicit multiply is taken out of the loop */
                gc->polygon.shader.sbuf = (__GLstencilCell*)
                    ((GLubyte*) gc->polygon.shader.sbuf
                     + gc->polygon.shader.sbufLittle);
            }
            if (modeFlags & __GL_SHADE_DEPTH_ITER) {
                gc->polygon.shader.frag.z += gc->polygon.shader.zLittle;
            }
            if (modeFlags & __GL_SHADE_DEPTH_TEST) {
                /* The implicit multiply is taken out of the loop */
                gc->polygon.shader.zbuf = (__GLzValue*)
                    ((GLubyte*) gc->polygon.shader.zbuf
                     + gc->polygon.shader.zbufLittle);
            }

            if (modeFlags & (__GL_SHADE_COMPUTE_FOG | __GL_SHADE_INTERP_FOG))
            {
                gc->polygon.shader.frag.f += gc->polygon.shader.fLittle;
            }
        }
    }
    gc->polygon.shader.ixLeft = ixLeft;
    gc->polygon.shader.ixLeftFrac = ixLeftFrac;
    gc->polygon.shader.ixRight = ixRight;
    gc->polygon.shader.ixRightFrac = ixRightFrac;
#ifdef NT
    if (iMaxWidth > __GL_MAX_STACK_STIPPLE_BITS)
    {
        gcTempFree(gc, words);
    }
    if (iMaxWidth > (__GL_MAX_STACKED_COLORS>>1))
    {
        gcTempFree(gc, vColors);
        gcTempFree(gc, vFbcolors);
    }
#endif
}

static void SnapXLeft(__GLcontext *gc, __GLfloat xLeft, __GLfloat dxdyLeft)
{
    GLint ixLeft, ixLeftFrac, lineBytes, elementSize, iLittle, iBig;

    ASSERT_CHOP_ROUND();
    
    ixLeft = __GL_VERTEX_FLOAT_TO_INT(xLeft);
    /* Pre-add .5 to allow truncation in spanWidth calculation */
    ixLeftFrac = __GL_VERTEX_PROMOTED_FRACTION(xLeft) + 0x40000000;

    gc->polygon.shader.ixLeft = ixLeft + (((GLuint) ixLeftFrac) >> 31);
    gc->polygon.shader.ixLeftFrac = ixLeftFrac & ~0x80000000;

    /* Compute big and little steps */
    iLittle = FTOL(dxdyLeft);
    gc->polygon.shader.dxLeftFrac = FLT_FRACTION(dxdyLeft - iLittle);
    if (dxdyLeft < 0) {
	iBig = iLittle - 1;
    } else {
	iBig = iLittle + 1;
    }
    if (gc->polygon.shader.modeFlags & __GL_SHADE_STENCIL_TEST) {
	/*
	** Compute the big and little stencil buffer steps.  We walk the
	** memory pointers for the stencil buffer along the edge of the
	** triangle as we walk the edge.  This way we don't have to
	** recompute the buffer address as we go.
	*/
	elementSize = gc->stencilBuffer.buf.elementSize;
	lineBytes = elementSize * gc->stencilBuffer.buf.outerWidth;
	gc->polygon.shader.sbufLittle = lineBytes + iLittle * elementSize;
	gc->polygon.shader.sbufBig = lineBytes + iBig * elementSize;
    }
    if (gc->polygon.shader.modeFlags & __GL_SHADE_DEPTH_TEST) {
	/*
	** Compute the big and little depth buffer steps.  We walk the
	** memory pointers for the depth buffer along the edge of the
	** triangle as we walk the edge.  This way we don't have to
	** recompute the buffer address as we go.
	*/
	elementSize = gc->depthBuffer.buf.elementSize;
	lineBytes = elementSize * gc->depthBuffer.buf.outerWidth;
	gc->polygon.shader.zbufLittle = lineBytes + iLittle * elementSize;
	gc->polygon.shader.zbufBig = lineBytes + iBig * elementSize;
    }
    gc->polygon.shader.dxLeftLittle = iLittle;
    gc->polygon.shader.dxLeftBig = iBig;
}

static void SnapXRight(__GLshade *sh, __GLfloat xRight, __GLfloat dxdyRight)
{
    GLint ixRight, ixRightFrac, iLittle, iBig;

    ASSERT_CHOP_ROUND();
    
    ixRight = __GL_VERTEX_FLOAT_TO_INT(xRight);
    /* Pre-add .5 to allow truncation in spanWidth calculation */
    ixRightFrac = __GL_VERTEX_PROMOTED_FRACTION(xRight) + 0x40000000;

    sh->ixRight = ixRight + (((GLuint) ixRightFrac) >> 31);
    sh->ixRightFrac = ixRightFrac & ~0x80000000;

    /* Compute big and little steps */
    iLittle = FTOL(dxdyRight);
    sh->dxRightFrac = FLT_FRACTION(dxdyRight - iLittle);
    if (dxdyRight < 0) {
	iBig = iLittle - 1;
    } else {
	iBig = iLittle + 1;
    }
    sh->dxRightLittle = iLittle;
    sh->dxRightBig = iBig;
}


__GLfloat __glPolygonOffsetZ(__GLcontext *gc )
{

    __GLshade *sh = &gc->polygon.shader;
    __GLfloat factor;
    __GLfloat maxdZ;
    __GLfloat bias;
    __GLfloat offsetZ;

    /*
    ** Calculate factor and bias
    */
    factor = gc->state.polygon.factor;

    // For 16-bit z, bias = units,
    // For 32-bit z, we only have 24 bits of resolution from the floating
    // point z value.  Therefore, the minimum resolvable difference in z
    // values is 8-bits, and we multiply units by 2**8, or 256.

    if( gc->modes.depthBits == 16 )
        bias = gc->state.polygon.units;
    else
        bias = gc->state.polygon.units * __glVal256;

    /*
    ** find the maximum Z slope with respect to X and Y
    */
    // Note: all z values have already been scaled up from [0,1]
    if(__GL_ABSF(sh->dzdxf) > __GL_ABSF(sh->dzdyf))
        maxdZ = __GL_ABSF(sh->dzdxf);
    else
        maxdZ = __GL_ABSF(sh->dzdyf);

    offsetZ = factor * maxdZ + bias;

    // XXX! This value should really be clamped, but supposedly we don't
    // bother in other parts of the code, so leave it for now.  Clamping
    // should also only be applied AFTER the addition of offsetZ to any
    // base value.
    return( offsetZ );
}

#ifdef NT
static void SetInitialParameters(__GLcontext *gc, const __GLvertex *a,
				 const __GLcolor *ac, __GLfloat aFog,
				 __GLfloat dx, __GLfloat dy)
{
    __GLshade *sh = &gc->polygon.shader;
#else
static void SetInitialParameters(__GLshade *sh, const __GLvertex *a,
				 const __GLcolor *ac, __GLfloat aFog,
				 __GLfloat dx, __GLfloat dy)
{
#endif
    __GLfloat little = sh->dxLeftLittle;
    __GLfloat big = sh->dxLeftBig;
    GLuint modeFlags = sh->modeFlags;
#define bPolygonOffset \
        (gc->state.enables.general & __GL_POLYGON_OFFSET_FILL_ENABLE )


    if (big > little) {
        if (modeFlags & __GL_SHADE_RGB) {
            if (modeFlags & __GL_SHADE_SMOOTH) {
                sh->frag.color.r = ac->r + dx*sh->drdx + dy*sh->drdy;
                sh->rLittle = sh->drdy + little * sh->drdx;
                sh->rBig = sh->rLittle + sh->drdx;
                
                sh->frag.color.g = ac->g + dx*sh->dgdx + dy*sh->dgdy;
                sh->gLittle = sh->dgdy + little * sh->dgdx;
                sh->gBig = sh->gLittle + sh->dgdx;
                
                sh->frag.color.b = ac->b + dx*sh->dbdx + dy*sh->dbdy;
                sh->bLittle = sh->dbdy + little * sh->dbdx;
                sh->bBig = sh->bLittle + sh->dbdx;

                sh->frag.color.a = ac->a + dx*sh->dadx + dy*sh->dady;
                sh->aLittle = sh->dady + little * sh->dadx;
                sh->aBig =sh->aLittle + sh->dadx;
            }
            if (modeFlags & __GL_SHADE_TEXTURE) {
                __GLfloat oneOverW = a->window.w;
                sh->frag.s = a->texture.x * oneOverW + dx*sh->dsdx
                    + dy*sh->dsdy;
                sh->sLittle = sh->dsdy + little * sh->dsdx;
                sh->sBig = sh->sLittle + sh->dsdx;
                
                sh->frag.t = a->texture.y * oneOverW + dx*sh->dtdx
                    + dy*sh->dtdy;
                sh->tLittle = sh->dtdy + little * sh->dtdx;
                sh->tBig = sh->tLittle + sh->dtdx;
                
                sh->frag.qw = a->texture.w * oneOverW + dx*sh->dqwdx
                    + dy*sh->dqwdy;
                sh->qwLittle = sh->dqwdy + little * sh->dqwdx;
                sh->qwBig = sh->qwLittle + sh->dqwdx;
                }
        } else {
            if (modeFlags & __GL_SHADE_SMOOTH) {
                sh->frag.color.r = ac->r + dx*sh->drdx + dy*sh->drdy;
                sh->rLittle = sh->drdy + little * sh->drdx;
                sh->rBig = sh->rLittle + sh->drdx;
            }
        }
        if (modeFlags & __GL_SHADE_DEPTH_ITER) {
            __GLfloat zLittle, zOffset;
            
            zOffset = bPolygonOffset ? __glPolygonOffsetZ(gc) : 0.0f;
            if (gc->modes.depthBits == 16) {
                sh->frag.z = (__GLzValue)
                    FLT_TO_Z16_SCALE(a->window.z + dx*sh->dzdxf + dy*sh->dzdyf
                                     + zOffset );
                zLittle = sh->dzdyf + little * sh->dzdxf;
                sh->zLittle = FLT_TO_Z16_SCALE(zLittle);
                sh->zBig = FLT_TO_Z16_SCALE(zLittle + sh->dzdxf);
            }
            else {
                sh->frag.z = (__GLzValue)
                    FTOL(a->window.z + dx*sh->dzdxf + dy*sh->dzdyf +
                         zOffset );
                zLittle = sh->dzdyf + little * sh->dzdxf;
                sh->zLittle = FTOL(zLittle);
                sh->zBig = FTOL(zLittle + sh->dzdxf);
            }
        }
        
        if (modeFlags & (__GL_SHADE_COMPUTE_FOG | __GL_SHADE_INTERP_FOG))
        {
            sh->frag.f = aFog + dx*sh->dfdx + dy*sh->dfdy;
            sh->fLittle = sh->dfdy + little * sh->dfdx;
            sh->fBig = sh->fLittle + sh->dfdx;
        }
    } else {	
        if (modeFlags & __GL_SHADE_RGB) {
            if (modeFlags & __GL_SHADE_SMOOTH) {
                sh->frag.color.r = ac->r + dx*sh->drdx + dy*sh->drdy;
                sh->rLittle = sh->drdy + little * sh->drdx;
                sh->rBig = sh->rLittle - sh->drdx;
                sh->frag.color.g = ac->g + dx*sh->dgdx + dy*sh->dgdy;
                sh->gLittle = sh->dgdy + little * sh->dgdx;
                sh->gBig = sh->gLittle - sh->dgdx;
                
                sh->frag.color.b = ac->b + dx*sh->dbdx + dy*sh->dbdy;
                sh->bLittle = sh->dbdy + little * sh->dbdx;
                sh->bBig = sh->bLittle - sh->dbdx;
                
                sh->frag.color.a = ac->a + dx*sh->dadx + dy*sh->dady;
                sh->aLittle = sh->dady + little * sh->dadx;
                sh->aBig =sh->aLittle - sh->dadx;
            }
            if (modeFlags & __GL_SHADE_TEXTURE) {
                __GLfloat oneOverW = a->window.w;
                sh->frag.s = a->texture.x * oneOverW + dx*sh->dsdx
                    + dy*sh->dsdy;
                sh->sLittle = sh->dsdy + little * sh->dsdx;
                sh->sBig = sh->sLittle - sh->dsdx;
                
                sh->frag.t = a->texture.y * oneOverW + dx*sh->dtdx
                    + dy*sh->dtdy;
                sh->tLittle = sh->dtdy + little * sh->dtdx;
                sh->tBig = sh->tLittle - sh->dtdx;
                
                sh->frag.qw = a->texture.w * oneOverW + dx*sh->dqwdx
                    + dy*sh->dqwdy;
                sh->qwLittle = sh->dqwdy + little * sh->dqwdx;
                sh->qwBig = sh->qwLittle - sh->dqwdx;
            }
        } else {
            if (modeFlags & __GL_SHADE_SMOOTH) {
                sh->frag.color.r = ac->r + dx*sh->drdx + dy*sh->drdy;
                sh->rLittle = sh->drdy + little * sh->drdx;
                sh->rBig = sh->rLittle - sh->drdx;
            }
        }
        if (modeFlags & __GL_SHADE_DEPTH_ITER) {
            __GLfloat zLittle, zOffset;
            
            zOffset = bPolygonOffset ? __glPolygonOffsetZ(gc) : 0.0f;
            if(( gc->modes.depthBits == 16 ) &&
               ( gc->depthBuffer.scale <= (GLuint)0xffff )) {
                sh->frag.z = (__GLzValue)
                    FLT_TO_Z16_SCALE(a->window.z + dx*sh->dzdxf + dy*sh->dzdyf
                                     + zOffset );
                zLittle = sh->dzdyf + little * sh->dzdxf;
                sh->zLittle = FLT_TO_Z16_SCALE(zLittle);
                sh->zBig = FLT_TO_Z16_SCALE(zLittle - sh->dzdxf);
            }
            else {
                sh->frag.z = (__GLzValue)
                    FTOL( a->window.z + dx*sh->dzdxf + dy*sh->dzdyf+ zOffset );
                zLittle = sh->dzdyf + little * sh->dzdxf;
                sh->zLittle = FTOL(zLittle);
                sh->zBig = FTOL(zLittle - sh->dzdxf);
            }
        }
        
        if (modeFlags & (__GL_SHADE_COMPUTE_FOG | __GL_SHADE_INTERP_FOG))
        {
            sh->frag.f = aFog + dx*sh->dfdx + dy*sh->dfdy;
            sh->fLittle = sh->dfdy + little * sh->dfdx;
            sh->fBig = sh->fLittle - sh->dfdx;
        }
    }
}


void FASTCALL __glFillTriangle(__GLcontext *gc, __GLvertex *a, __GLvertex *b,
		      __GLvertex *c, GLboolean ccw)

{
    __GLfloat oneOverArea, t1, t2, t3, t4;
    __GLfloat dxAC, dxBC, dyAC, dyBC;
    __GLfloat aFog, bFog;
    __GLfloat dxAB, dyAB;
    __GLfloat dx, dy, dxdyLeft, dxdyRight;
    __GLcolor *ac, *bc;
    GLint aIY, bIY, cIY;
    GLuint modeFlags;
    __GLfloat dxdyAC;

    CHOP_ROUND_ON();
    
    /* Pre-compute one over polygon area */

    __GL_FLOAT_BEGIN_DIVIDE(__glOne, gc->polygon.shader.area, &oneOverArea);

    /* Fetch some stuff we are going to reuse */
    modeFlags = gc->polygon.shader.modeFlags;
    dxAC = gc->polygon.shader.dxAC;
    dxBC = gc->polygon.shader.dxBC;
    dyAC = gc->polygon.shader.dyAC;
    dyBC = gc->polygon.shader.dyBC;
    ac = a->color;
    bc = b->color;

    /*
    ** Compute delta values for unit changes in x or y for each
    ** parameter.
    */

    __GL_FLOAT_SIMPLE_END_DIVIDE(oneOverArea);
    t1 = dyAC * oneOverArea;
    t2 = dyBC * oneOverArea;
    t3 = dxAC * oneOverArea;
    t4 = dxBC * oneOverArea;
    
    if (modeFlags & __GL_SHADE_RGB) {
	if (modeFlags & __GL_SHADE_SMOOTH) {
	    __GLfloat drAC, dgAC, dbAC, daAC;
	    __GLfloat drBC, dgBC, dbBC, daBC;
	    __GLcolor *cc;

	    cc = c->color;
	    drAC = ac->r - cc->r;
	    drBC = bc->r - cc->r;
	    dgAC = ac->g - cc->g;
	    dgBC = bc->g - cc->g;
	    dbAC = ac->b - cc->b;
	    dbBC = bc->b - cc->b;
	    daAC = ac->a - cc->a;
	    daBC = bc->a - cc->a;

	    gc->polygon.shader.drdx = drAC * t2 - drBC * t1;
	    gc->polygon.shader.drdy = drBC * t3 - drAC * t4;
	    gc->polygon.shader.dgdx = dgAC * t2 - dgBC * t1;
	    gc->polygon.shader.dgdy = dgBC * t3 - dgAC * t4;
	    gc->polygon.shader.dbdx = dbAC * t2 - dbBC * t1;
	    gc->polygon.shader.dbdy = dbBC * t3 - dbAC * t4;
	    gc->polygon.shader.dadx = daAC * t2 - daBC * t1;
	    gc->polygon.shader.dady = daBC * t3 - daAC * t4;
	} else {
	    __GLcolor *flatColor = gc->vertex.provoking->color;
	    gc->polygon.shader.frag.color = *flatColor;
	}
	if (modeFlags & __GL_SHADE_TEXTURE) {
	    __GLfloat awinv, bwinv, cwinv, scwinv, tcwinv, qwcwinv;
	    __GLfloat dsAC, dsBC, dtAC, dtBC, dqwAC, dqwBC;

	    awinv = a->window.w;
	    bwinv = b->window.w;
	    cwinv = c->window.w;
	    scwinv = c->texture.x * cwinv;
	    tcwinv = c->texture.y * cwinv;
	    qwcwinv = c->texture.w * cwinv;

	    dsAC = a->texture.x * awinv - scwinv;
	    dsBC = b->texture.x * bwinv - scwinv;
	    dtAC = a->texture.y * awinv - tcwinv;
	    dtBC = b->texture.y * bwinv - tcwinv;
	    dqwAC = a->texture.w * awinv - qwcwinv;
	    dqwBC = b->texture.w * bwinv - qwcwinv;

	    gc->polygon.shader.dsdx = dsAC * t2 - dsBC * t1;
	    gc->polygon.shader.dsdy = dsBC * t3 - dsAC * t4;
	    gc->polygon.shader.dtdx = dtAC * t2 - dtBC * t1;
	    gc->polygon.shader.dtdy = dtBC * t3 - dtAC * t4;
	    gc->polygon.shader.dqwdx = dqwAC * t2 - dqwBC * t1;
	    gc->polygon.shader.dqwdy = dqwBC * t3 - dqwAC * t4;

	}
    } else {
	if (modeFlags & __GL_SHADE_SMOOTH) {
	    __GLfloat drAC;
	    __GLfloat drBC;
	    __GLcolor *cc;

	    cc = c->color;
	    drAC = ac->r - cc->r;
	    drBC = bc->r - cc->r;

	    gc->polygon.shader.drdx = drAC * t2 - drBC * t1;
	    gc->polygon.shader.drdy = drBC * t3 - drAC * t4;
	} else {
	    __GLcolor *flatColor = gc->vertex.provoking->color;
	    gc->polygon.shader.frag.color.r = flatColor->r;
	}
    }
    if (modeFlags & __GL_SHADE_DEPTH_ITER) {
	__GLfloat dzAC, dzBC;

	dzAC = a->window.z - c->window.z;
	dzBC = b->window.z - c->window.z;

	gc->polygon.shader.dzdxf = dzAC * t2 - dzBC * t1;
	gc->polygon.shader.dzdyf = dzBC * t3 - dzAC * t4;
#ifdef NT
	if(( gc->modes.depthBits == 16 ) &&
           ( gc->depthBuffer.scale <= (GLuint)0xffff )) {
	    gc->polygon.shader.dzdx = 
                FLT_TO_Z16_SCALE(gc->polygon.shader.dzdxf);
	}
	else {
	    gc->polygon.shader.dzdx = FTOL(gc->polygon.shader.dzdxf);
	}
#else
	gc->polygon.shader.dzdx = (GLint) gc->polygon.shader.dzdxf;
#endif
    }

    if (modeFlags & __GL_SHADE_COMPUTE_FOG)
    {
        __GLfloat dfAC, dfBC, cFog;

        aFog = a->eyeZ;
        bFog = b->eyeZ;
        cFog = c->eyeZ;

        dfAC = aFog - cFog;
        dfBC = bFog - cFog;
        
        gc->polygon.shader.dfdx = dfAC * t2 - dfBC * t1;
        gc->polygon.shader.dfdy = dfBC * t3 - dfAC * t4;
    }
    else if (modeFlags & __GL_SHADE_INTERP_FOG)
    {
        __GLfloat dfAC, dfBC, cFog;

        aFog = a->fog;
        bFog = b->fog;
        cFog = c->fog;

        dfAC = aFog - cFog;
        dfBC = bFog - cFog;
        
        gc->polygon.shader.dfdx = dfAC * t2 - dfBC * t1;
        gc->polygon.shader.dfdy = dfBC * t3 - dfAC * t4;
    }
    

    __GL_FLOAT_SIMPLE_BEGIN_DIVIDE(dxAC, dyAC, dxdyAC);
    
    /* Snap each y coordinate to its pixel center */
    aIY = __GL_VERTEX_FIXED_TO_INT(__GL_VERTEX_FLOAT_TO_FIXED(a->window.y)+
                                   __GL_VERTEX_FRAC_HALF);
    bIY = __GL_VERTEX_FIXED_TO_INT(__GL_VERTEX_FLOAT_TO_FIXED(b->window.y)+
                                   __GL_VERTEX_FRAC_HALF);
    cIY = __GL_VERTEX_FIXED_TO_INT(__GL_VERTEX_FLOAT_TO_FIXED(c->window.y)+
                                   __GL_VERTEX_FRAC_HALF);

    /*
    ** This algorithim always fills from bottom to top, left to right.
    ** Because of this, ccw triangles are inherently faster because
    ** the parameter values need not be recomputed.
    */
    dxAB = a->window.x - b->window.x;
    dyAB = a->window.y - b->window.y;
    if (ccw) {
	dy = (aIY + __glHalf) - a->window.y;
        __GL_FLOAT_SIMPLE_END_DIVIDE(dxdyAC);
	SnapXLeft(gc, a->window.x + dy*dxdyAC, dxdyAC);
	dx = (gc->polygon.shader.ixLeft + __glHalf) - a->window.x;
#ifdef NT
	SetInitialParameters(gc, a, ac, aFog, dx, dy);
#else
	SetInitialParameters(&gc->polygon.shader, a, ac, aFog, dx, dy);
#endif
	if (aIY != bIY) {
	    dxdyRight = dxAB / dyAB;
	    SnapXRight(&gc->polygon.shader, a->window.x + dy*dxdyRight,
		       dxdyRight);
	    FillSubTriangle(gc, aIY, bIY);
	}

	if (bIY != cIY) {
	    dxdyRight = dxBC / dyBC;
	    dy = (bIY + __glHalf) - b->window.y;
	    SnapXRight(&gc->polygon.shader, b->window.x + dy*dxdyRight,
		       dxdyRight);
	    FillSubTriangle(gc, bIY, cIY);
	}
    } else {
	dy = (aIY + __glHalf) - a->window.y;
        __GL_FLOAT_SIMPLE_END_DIVIDE(dxdyAC);
	SnapXRight(&gc->polygon.shader, a->window.x + dy*dxdyAC, dxdyAC);
	if (aIY != bIY) {
	    dxdyLeft = dxAB / dyAB;
	    SnapXLeft(gc, a->window.x + dy*dxdyLeft, dxdyLeft);
	    dx = (gc->polygon.shader.ixLeft + __glHalf) - a->window.x;
#ifdef NT
	    SetInitialParameters(gc, a, ac, aFog, dx, dy);
#else
	    SetInitialParameters(&gc->polygon.shader, a, ac, aFog, dx, dy);
#endif
	    FillSubTriangle(gc, aIY, bIY);
	}

	if (bIY != cIY) {
	    dxdyLeft = dxBC / dyBC;
	    dy = (bIY + __glHalf) - b->window.y;
	    SnapXLeft(gc, b->window.x + dy*dxdyLeft, dxdyLeft);
	    dx = (gc->polygon.shader.ixLeft + __glHalf) - b->window.x;
#ifdef NT
	    SetInitialParameters(gc, b, bc, bFog, dx, dy);
#else
	    SetInitialParameters(&gc->polygon.shader, b, bc, bFog, dx, dy);
#endif
	    FillSubTriangle(gc, bIY, cIY);
	}
    }

    CHOP_ROUND_OFF();

}

void FASTCALL __glFillFlatFogTriangle(__GLcontext *gc, __GLvertex *a, __GLvertex *b,
                             __GLvertex *c, GLboolean ccw)
{
    __GLcolor acol, bcol, ccol;
    __GLcolor *aocp, *bocp, *cocp;
    __GLvertex *pv;

    pv = gc->vertex.provoking;
    (*gc->procs.fogColor)(gc, &acol, pv->color, a->fog);
    (*gc->procs.fogColor)(gc, &bcol, pv->color, b->fog);
    (*gc->procs.fogColor)(gc, &ccol, pv->color, c->fog);
    aocp = a->color;
    bocp = b->color;
    cocp = c->color;
    a->color = &acol;
    b->color = &bcol;
    c->color = &ccol;

    (*gc->procs.fillTriangle2)(gc, a, b, c, ccw);

    a->color = aocp;
    b->color = bocp;
    c->color = cocp;
}

#ifdef GL_WIN_specular_fog
void FASTCALL __glFillFlatSpecFogTriangle(__GLcontext *gc, __GLvertex *a, 
                                          __GLvertex *b, __GLvertex *c, 
                                          GLboolean ccw)
{
    __GLfloat af, bf, cf;
    __GLvertex *pv;

    pv = gc->vertex.provoking;
    
    af = a->fog;
    bf = b->fog;
    cf = c->fog;
    
    a->fog = b->fog = c->fog = pv->fog;
    
    (*gc->procs.fillTriangle2)(gc, a, b, c, ccw);

    a->fog = af;
    b->fog = bf;
    c->fog = cf;
}
#endif //GL_WIN_specular_fog