/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Lexmark Corporation, 1989                                   */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = PRDLPOLY
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   prdl_UpdateBoundsRectangle
 *             prdl_Polyline
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_32                   /* CON3201 */
#define INCL_DOSPROCESS           /* CON3201 */
#define INCL_DOSSEMAPHORES
#define INCL_GREALL
#define INCL_GPIPRIMITIVES
#define INCL_WINHEAP
#define INCL_GPIBITMAPS
#define INCL_GPIERRORS
#include <os2.h>
#undef INCL_DOSPROCESS            /* CON3201 */
#undef INCL_DOSSEMAPHORES
#undef INCL_GREALL
#undef INCL_GPIPRIMITIVES
#undef INCL_WINHEAP
#undef INCL_GPIBITMAPS
#undef INCL_GPIERRORS

#define INCL_DDIBUNDLES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIDEFS
#define INCL_DDIMISC
#define INCL_DDICOMFLAGS
#include <pmddi.h>
#undef INCL_DDIBUNDLES
#undef INCL_DDIFONTSTRUCS
#undef INCL_DDIDEFS
#undef INCL_DDIMISC
#undef INCL_DDICOMFLAGS

#define INCL_WINP_SELECTIVE
#define INCL_WINP_SEI
#include <pmwinx.h>
#undef INCL_WINP_SELECTIVE
#undef INCL_WINP_SEI
#undef INCL_32                    /* CON3201 */

#define NO_CONSTANT_INCL
#include <prdinclt.h>
#undef NO_CONSTANT_INCL

#include <prdltypt.h>
#include <prdgextf.h>
#include <prdiextf.h>
#include <prdlextf.h>
#include <prdnextf.h>
#include <prdncone.h>

/******************************************************************************/
/*  Set up access to the engine function for simulations                      */
/******************************************************************************/
extern PFNL            da_PolyLine;

/******************************************************************************/
/*  FUNCTION: prdl_UpdateBoundsRectangle                                      */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  DevPoint  *  EndPoint;  The endpoint of a line                            */
/*  DevPoint  *  Boundary;  The current bounding rectangle                    */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  This is used to keep a record of the bounding rectangle of the current set*/
/*  of lines being drawn by prdl_Polyline.  It works by simply checking on the*/
/*  end point of each line drawn and extending the bounding rectangle to      */
/*  include it if required.                                                   */
/******************************************************************************/
VOID prdl_UpdateBoundsRectangle(DevPoint  *EndPoint,
                                DevPoint  *Boundary)

{
#define TFUNC "prdl_UpdateBoundsRectangle"
    TRACE4(TFUNC, "Endpoint.X, Y", EndPoint, 1);
    TRACE4(TFUNC, "xmin,ymin,xmax,ymax", Boundary, 2);

    /**************************************************************************/
    /* IF the endpoint X coordinate is greater than the boundary xmax         */
    /*     boundary xmax becomes the endpoint X coordinate                    */
    /* ELSE IF endpoint X coordinate is less than the boundary xmin           */
    /*     boundary xmin becomes the endpoint X coordinate                    */
    /**************************************************************************/
    if (EndPoint->X > (Boundary+1)->X)
        (Boundary+1)->X = EndPoint->X;
    else if (EndPoint->X < Boundary->X)
        Boundary->X = EndPoint->X;

    /**************************************************************************/
    /* IF the endpoint Y coordinate is greater than the boundary ymax         */
    /*     endpoint Y coordinate becomes the boundary ymax                    */
    /* ELSE IF endpoint Y coordinate is less than the boundary ymin           */
    /*     endpoint Y coordinate becomes the boundary ymin                    */
    /**************************************************************************/
    if (EndPoint->Y > (Boundary+1)->Y)
        (Boundary+1)->Y = EndPoint->Y;
    else if (EndPoint->Y < Boundary->Y)
        Boundary->Y = EndPoint->Y;
    TRACE4(TFUNC, "Exit coords", Boundary, 2);
}
#undef TFUNC

/******************************************************************************/
/*  FUNCTION: prdl_Polyline                                                   */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  See "OS/2 Technical Reference: I/O Subsytems and Device Drivers"          */
/*                                                                            */
/*  hanDC   DcH;                                                              */
/*  PULONG  ArgXYArray;                                                       */
/*  ULONG   ArgNumber;                                                        */
/*  lpDCI   IData;                                                            */
/*  ULONG   FunN;                                                             */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Draws a sequence of one or more lines from the current position.  On      */
/*  completion the end point of the last line is the current position.  If the*/
/*  sequence of lines describes a closed shape then the last point of the last*/
/*  line is not drawn.                                                        */
/******************************************************************************/
ULONG EXPENTRY prdl_PolyLine(hanDC   DcH,
                             PULONG  ArgXYArray,
                             ULONG   ArgNumber,
                             lpDCI   DCIData,
                             ULONG    FunN)

{
#define TFUNC "prdl_PolyLine"

    /**************************************************************************/
    /*  Local variables                                                       */
    /**************************************************************************/
    ULONG                Command;      /* Command bits                        */
    ULONG                i;            /* Loop counter                        */
    USHORT               j;            /* Loop counter                        */
    PULONG               ArrayPtr;     /* Pointer to be used for stepping     */
                                       /* through the XY array                */
    DevPoint             Start;        /* Start coord for line                */
    DevPoint             ClipStart;    /* Clipped start coord                 */
    DevPoint             Next;         /* End coord for line                  */
    DevPoint             ClipNext;     /* Clipped end coord                   */
    DevPoint             DevNext;      /* Start coord for next line           */
    BitmapListEntry     *ListEntry;    /* Pointer to bitmap list entry        */
    DevRect              lpClipReg;    /* Clip region                         */
    BOOL                 BoundsReqd;   /* Flag saying whether bounds          */
                                       /* determination required              */
    DevRect              BoundsRect;   /* Bounding rectangle                  */
    BYTE                 LineColor;    /* Foreground line color               */
    USHORT               LineMix;      /* Foreground line mix                 */
    USHORT               LineType;     /* Line type to use                    */
    USHORT               Result;
    PUSHORT              LineMask;     /* Pointer to line mask (in DCIData)   */
    USHORT               LastStep;     /* Flag determines whether the line    */
                                       /* drawing algorithm draws the last pel*/
    RECTL                BoundsRectL;  /* Bounds for clipping processing      */
    DevRect              BRect;

    /**************************************************************************/
    /*  Trace the parameters to this call                                     */
    /**************************************************************************/
    TRACE8(TFUNC, "FunN", &FunN, 1);
    TRACE8(TFUNC, "DcH", &DcH, 1);
    TRACE8(TFUNC, "ArgNumber", &ArgNumber, 1);
    TRACE8(TFUNC, "DCIData", &DCIData, 1);
    TRACE8(TFUNC, "Command", &Command, 1);

    /**************************************************************************/
    /*  Get the driver and DCI Data semaphore                                 */
    /**************************************************************************/
#ifdef PRD_TIMING
    DEKHOOK0(A,8,11)
#endif
    prdm_EnterDriver(DCIData);

    /**************************************************************************/
    /*  if COM_PATH or COM_AREA set then pass call back to engine for         */
    /*  processing as we are not required to draw the lines at this point.    */
    /*  Also call back to engine if the number of clip rectangles is greater  */
    /*  than 1.  The call will then be simulated through DrawLinesInPath.     */
    /*  This is done to get around the problem with Bresenham's algorithm for */
    /*  multiple joining clip rctangles.                                      */
    /**************************************************************************/
    Command = (FunN & DCIData->CommandMask);
    if ((Command & (COM_PATH | COM_AREA)) || (DCIData->DCIClipNum > 1))
    {
        TRACE8(TFUNC, "Returning to engine", &Command, 1);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        prdm_LeaveDriver(DCIData);
        return(da_PolyLine(DcH, ArgXYArray, ArgNumber, DCIData, FunN));
    }

    /**************************************************************************/
    /*  Do the new framework drawing function - sets DRAWN_INTO flag if       */
    /*  COM_DRAW is set.  Returns error if not in a suitable state.           */
    /**************************************************************************/
    if ((Result = prdn_CheckDrawingState((PULONG)&Command, DCIData)) != OK)
    {
        prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        return((Result==ERR_PURGE) ? OK : ERROR_ZERO); /* CON3203 */
    }

    /**************************************************************************/
    /*  If there are no points then we can return.                            */
    /**************************************************************************/
    if (ArgNumber == 0)
    {
        prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        return (OK);
    }
    TRACE8(TFUNC, "ArgXYArray", ArgXYArray, ArgNumber*2);

    /**************************************************************************/
    /*  Reset bit 0 of style high word to indicate a figure is not starting   */
    /*  (1st pel not drawn)                                                   */
    /**************************************************************************/
    DCIData->StyleNumber &= 0xfffeffff;

    /**************************************************************************/
    /*  Set the device text only flag to FALSE since we're about to draw into */
    /*  the band.                                                             */
    /**************************************************************************/
    DCIData->TextOnlyDC = FALSE;

    /**************************************************************************/
    /*  Check if current line attributes supported.  If not the result is set */
    /*  up to effect a call to a simulation.  A value of anything other than  */
    /*  x10000 for the current line width indicates a "thick" line.  All      */
    /*  "thick" lines are passed on to the default Graphics Engine handler for*/
    /*  simulations.  If COM_DEVICE assume thin line.                         */
    /**************************************************************************/
    if (DCIData->DCICurLinAts->lbnd.fxWidth != 0x00010000L &&
        (Command & COM_DEVICE) == 0)
    {
        prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        return(da_PolyLine(DcH, ArgXYArray, ArgNumber, DCIData, FunN));
    }

    /**************************************************************************/
    /*  Get the bitmap listentry, color, mix, line type and a pointer to the  */
    /*  line mask.                                                            */
    /**************************************************************************/
    if (prdl_LineSetUp(&ListEntry, &LineColor, &LineMix, &LineType,
                       (PUSHORT *)&LineMask, DCIData) != OK)
    {
        TRACE4(TFUNC, "Set up failed", FNULL, 0);
        prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        return(ERROR_ZERO);
    }

    /**************************************************************************/
    /*  Start from current position in device coordinates.                    */
    /**************************************************************************/
    DevNext = DCIData->DCICurrPosDev;
    TRACE4(TFUNC, "Current position", &DevNext, 1);

    /**************************************************************************/
    /*  Initialize bounding rectangle to be the current position              */
    /**************************************************************************/
    if ((BoundsReqd = (Command & COM_BOUND)) != 0)
    {
        BoundsRect[0].X = DCIData->DCICurrPosDev.X;
        BoundsRect[1].X = DCIData->DCICurrPosDev.X;
        BoundsRect[0].Y = DCIData->DCICurrPosDev.Y;
        BoundsRect[1].Y = DCIData->DCICurrPosDev.Y;
    }
    BRect[0].X = DCIData->DCICurrPosDev.X;
    BRect[1].X = DCIData->DCICurrPosDev.X;
    BRect[0].Y = DCIData->DCICurrPosDev.Y;
    BRect[1].Y = DCIData->DCICurrPosDev.Y;

    /**************************************************************************/
    /*  Set pointer to start of XY array                                      */
    /**************************************************************************/
    ArrayPtr = (ArgXYArray);

    /**************************************************************************/
    /*  Processs each line in turn (each point has two coordinates in world   */
    /*  coordinates hence +=2 for ArrayPtr which is a pointer to a Dword.     */
    /**************************************************************************/
    for (; ArgNumber-- > 0; ArrayPtr += 2)
    {
        /**********************************************************************/
        /*  Set up position to draw line from (ie the last endpoint or the    */
        /*  current position if first line).                                  */
        /**********************************************************************/
        Start = DevNext;

        /**********************************************************************/
        /*  Set up position to draw line to                                   */
        /**********************************************************************/
        if (prdg_FastConvert ((WcsPoint *)ArrayPtr,
                             (DevPoint  *)&Next, DcH, Command) != OK)
        {
            TRACE4(TFUNC, "Convert failed", FNULL, 0);
            prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
            return(ERROR_ZERO);
        }
        DevNext = Next;
        TRACE4(TFUNC, "NextXY", &Next, 1);

        /**********************************************************************/
        /*  If bounds determination is required then update the bounds        */
        /*  recatngle for this polyline with a call to UpdateBounds-          */
        /*  Rectangle.                                                        */
        /**********************************************************************/
        if (BoundsReqd)
        {
            (VOID)prdl_UpdateBoundsRectangle((DevPoint  *) &Next,
                                        (DevPoint  *) BoundsRect);
        }
        /**********************************************************************/
        /*  Fetch each clip rectangle which is currently valid (ie all those  */
        /*  that prdi_GetClipRect will return and clip the line to this       */
        /*  rectangle then draw the line.  This loop should only be executed  */
        /*  if the Draw Bit is on.                                            */
        /**********************************************************************/
        if ((Command & COM_DRAW) != 0)
        {
            (VOID)prdl_UpdateBoundsRectangle((DevPoint  *)&Next,
                                             (DevPoint  *)BRect);
            BoundsRectL.xLeft   = (ULONG)BRect[0].X;
            BoundsRectL.yBottom = (ULONG)BRect[0].Y;
            BoundsRectL.xRight  = (ULONG)BRect[1].X + 2; /* PD00308...        */
            BoundsRectL.yTop    = (ULONG)BRect[1].Y + 2; /* PD00308...        */
            for (j = 1; prdi_GetNextClip(DCIData,j++,(DevRect *)lpClipReg,
                                         &BoundsRectL) == OK; )
            {
                /**************************************************************/
                /*  See if any part of the current line lies within the       */
                /*  current clip rectangle.  Start and Next are the first and */
                /*  last coordinates of the unclipped line and ClipStart and  */
                /*  ClipNext are set to these before clipping the line for    */
                /*  each clip rectangle.  ClipStart and ClipNext will hold the*/
                /*  endpoints of the clipped line.                            */
                /**************************************************************/
                TRACE4(TFUNC, "before clip start", &Start, 1);
                TRACE4(TFUNC, "before clip next", &Next, 1);
                TRACE4(TFUNC, "clip region", lpClipReg, 2);
                ClipStart = Start;
                ClipNext = Next;
                if (Start.X < lpClipReg[0].X || Start.X >= lpClipReg[1].X ||
                    Start.Y < lpClipReg[0].Y || Start.Y >= lpClipReg[1].Y ||
                    Next.X  < lpClipReg[0].X || Next.X  >= lpClipReg[1].X ||
                    Next.Y  < lpClipReg[0].Y || Next.Y  >= lpClipReg[1].Y)
                {
                    /**********************************************************/
                    /*  The if statement above has evaluated true so the line */
                    /*  must be clipped.  We may not need to draw it at all if*/
                    /*  there is no intersection with the clip rectangle we   */
                    /*  are clipping to.                                      */
                    /**********************************************************/
                    TRACE4(TFUNC, "clip line", NULL,0);
                    if (prdi_ClipLine ((DevRect  *) lpClipReg,
                                      (DevPoint  *) &ClipStart,
                                      (DevPoint  *) &ClipNext) != OK)
                    {
                        TRACE4(TFUNC, "line not in clip rect", NULL,0);
                        continue;
                    }
                }

                /**************************************************************/
                /*  Adjust end points for the DC origin                       */
                /**************************************************************/
                ClipStart.X += ListEntry->XOrigin;
                ClipNext.X  += ListEntry->XOrigin;
                ClipStart.Y += ListEntry->YOrigin;
                ClipNext.Y  += ListEntry->YOrigin;
                TRACE4(TFUNC, "after clip start", &ClipStart, 1);
                TRACE4(TFUNC, "after clip next", &ClipNext, 1);

                /**************************************************************/
                /*  We must draw the line specified by ClipStart and ClipNext.*/
                /*  If we are drawing the last line of a closed figure (ie the*/
                /*  end point of this line will be the start point of the     */
                /*  entire polyline which will be the current position in the */
                /*  DC Instance data) then the last pel on the line is not    */
                /*  drawn.  Otherwise it is drawn.                            */
                /**************************************************************/
                if ((ArgNumber == 0) &&
                    ((ClipNext.X == DCIData->DCICurrPosDev.X) &&
                     (ClipNext.Y == DCIData->DCICurrPosDev.Y)))
                    LastStep = 1;
                else
                    LastStep = 0;
                TRACE4(TFUNC, "LastStep", &LastStep, 1);

                /**************************************************************/
                /*  Part of line lies in clip rectangle and ClipStart and     */
                /*  ClipNext describe this segment so draw the clipped line.  */
                /**************************************************************/
                (VOID)Bresenham(ListEntry, ClipStart, ClipNext, LineColor,
                                LineMix, LineType, LineMask, LastStep);
            }

            /******************************************************************/
            /*  Note: DevNext holds the start position for the next line.     */
            /******************************************************************/
        }
    }

    /**************************************************************************/
    /*  Update the current position.  Move ArrayPtr back to the last entry and*/
    /*  call SetCurrentPosition                                               */
    /**************************************************************************/
    ArrayPtr -= 2;
    if (prdl_SetCurrentPosition((HDC)DcH, (PPOINTL)ArrayPtr, DCIData, FunN) !=
        OK)
    {
        prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
        return(ERROR_ZERO);
    }
    TRACE4(TFUNC, "CP DEV", &DCIData->DCICurrPosDev, 2);
    TRACE4(TFUNC, "CP WCS", &DCIData->DCICurrPosWcs, 2);

    /**************************************************************************/
    /*  If bounds accumulation is required then call prdg_AddBounds to add the*/
    /*  accumulated bounds data for this polyline to the overall bounds data. */
    /**************************************************************************/
    if (BoundsReqd)
        (VOID)prdg_AddBounds((DevRect *)BoundsRect, DCIData);
    TRACE4(TFUNC, "Returned OK", NULL, 0);
    prdm_LeaveDriver(DCIData);
#ifdef PRD_TIMING
    DEKHOOK0(A,8,91)
#endif
    return(OK);
}
#undef TFUNC
