/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft 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.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = OUTPLINE.C
 *
 * DESCRIPTIVE NAME = Output Line Functions
 *
 *
 * VERSION = V2.0
 *
 * DATE      12/18/88
 *
 * DESCRIPTION This file contains all the primitive line drawing
 *             functions used by the Plotter.
 *
 * FUNCTIONS
 *             DrawLinesInPath
 *             GetCurrentPosition
 *             GetLineOrigin
 *             PolyLine
 *             PolyShortLine
 *             PolyScanline()           PolyScanline
 *             SetCurrentPosition
 *             SetLineOrigin
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "plotters.h"
#include "bounds.h"
#include "clip.h"
#include "color.h"
#include "dispatch.h"
#include "error.h"
#include "outpline.h"
#include "output.h"
#include "utils.h"
#include "xforms.h"
#include "dosio.h"
#include "lockddc.h"

/*
** Exported Routines
*/
/***************************************************************************
 *
 * FUNCTION NAME = DrawLinesInPath
 *
 * DESCRIPTION   = This required function will loop through the given
 *                 path and draw all lines it finds in that path.
 *
 * INPUT         = hDC    - handle to Device Context
 *                 pPath  - pointer to path
 *                 pLine  - line attributes
 *                 nPaths - number paths
 *                 pDDC   - pointer to device driver cookie
 *                 ulFunN   - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 1
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  DrawLinesInPath(HDC hDC,PRECTL pPath,PLINE pLine,LONG nPaths,
                                 PDDC pDDC,ULONG ulFunN)
{

  BOOL   bDrawIt,
         bFirstTime;
  POINTL Pts[2];                       /* markv moved to the top for
                                          compiler                          */
  ULONG ulRet;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreDrawLinesInPath (pDDC->hdcMemory, pPath, pLine,
                                      nPaths, ulFunN);
    LeaveDriver( pDDC );
    return( ulRet );
  }

  bDrawIt    = FALSE;
  bFirstTime = TRUE;

  /*
  ** Note: we check the background color here because of the polygon recording
  */
  if (pDDC->DCState.bFilling)
    bDrawIt = (check_area_color(pDDC) || check_area_background_color(pDDC)) &&
              (ulFunN & COM_DRAW);
  else
    bDrawIt = check_line_color(pDDC) && (ulFunN & COM_DRAW);

  /*
  ** if we are filling a polygon and the first point has been
  ** set we must end the previous subpolygon before starting a new one.
  */

  if (pDDC->pPDevice->bFillingPolygon && pDDC->pPDevice->bFirstPolygonPtSet)
  {
    /*
    ** Close the current polygon and remain in polygon mode
    ** The point specified after PM1 becomes the first point of
    ** the next subpolygon
    */
    output_bytes(pDDC, "PM1");

    /*
    ** We must turn of bLineInConstruction because
    ** continuing comma separated points directly
    ** after Ending a polygon will not work.
    */
    pDDC->pPDevice->bLineInConstruction = FALSE;
  }

  while (nPaths--)
  {
    if (pLine->bType == LINE_IDENTIFIER)
    {
      Pts[0].x = (SHORT)(pLine->ptfxA.x >> 16);
      Pts[0].y = (SHORT)(pLine->ptfxA.y >> 16);
      Pts[1].x = (SHORT)(pLine->ptfxC.x >> 16);
      Pts[1].y = (SHORT)(pLine->ptfxC.y >> 16);

      if (ulFunN & COM_BOUND)
        accumulate_line_bounds(hDC, pDDC, &Pts[0], &Pts[1]);

      if (bDrawIt)
      {
        if (bFirstTime)
        {
          bFirstTime = FALSE;

          if (pDDC->DCState.bDrawingText)
           select_text_pen(pDDC, pDDC->DCState.abnd.lColor);
          else
            if (pDDC->DCState.bFilling)
              select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
            else
              select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);

          set_line_type(pDDC, pDDC->DCState.lbnd.usType);

          /*
          ** Force move to the first point. We must guarantee
          ** we start at the first point for polygon
          ** mode. MV.
          **
          ** PM0 and PM1 must have the first point set.
          ** PM0 - first point is set before PM0
          ** PM1 - first point is set after PM1
          */
          move_pen(pDDC, &Pts[0], TRUE);

          if (pDDC->pPDevice->bFillingPolygon &&
             !pDDC->pPDevice->bFirstPolygonPtSet)
          {
            pDDC->pPDevice->bFirstPolygonPtSet = TRUE;
            output_bytes(pDDC, "PM0");

            /*
            ** We must turn of bLineInConstruction because
            ** continuing comma separated points directly
            ** after Starting a polygon will not work.
            */
            pDDC->pPDevice->bLineInConstruction = FALSE;
            pDDC->pPDevice->usNumPtsInPolygon = 1;
          }
        }

        if (get_closest_point(&pDDC->pPDevice->PhysPosition, &Pts[0], &Pts[1]))
        {
          move_pen(pDDC, &Pts[0], FALSE);
          plot_line(pDDC, &Pts[1]);
        }
        else
        {
          move_pen(pDDC, &Pts[1], FALSE);
          plot_line(pDDC, &Pts[0]);
        }

        if (pDDC->pPDevice->bFillingPolygon)
          pDDC->pPDevice->usNumPtsInPolygon++;
      }
    }
    pLine = (PLINE)pLine->pcvNext;
  }
  LeaveDriver(pDDC);
  return (1L);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetCurrentPosition
 *
 * DESCRIPTION   = This function returns the logical pen position to
 *                 the (X,Y) pointed to by pPts.  Note that physical
 *                 pen position (the actual location of the pen) may
 *                 be different.
 *
 * INPUT         = hDC    - handle to Device Context
 *                 pDDC   - pointer to device driver cookie
 *                 ulFunN   - function number
 *
 * OUTPUT        = pPts   - current pen position
 *
 * RETURN-NORMAL = 1
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  GetCurrentPosition(HDC hDC,PPOINTL pPts,PDDC pDDC,ULONG ulFunN)
{
  ULONG ulRet;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreGetCurrentPosition (pDDC->hdcMemory, pPts, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }
  else
  {
    if (ulFunN & COM_TRANSFORM)
       *pPts = pDDC->DCState.WorldPosition;
    else
       *pPts = pDDC->DCState.LogPosition;
    LeaveDriver(pDDC);
    return (1L);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = GetLineOrigin
 *
 * DESCRIPTION   = This function acts just like GetCurrentPosition.
 *
 * INPUT         = hDC     - handle to Device Context
 *                 pDDC    - pointer to device driver cookie
 *                 ulFunN    - function number
 *
 * OUTPUT        = pOrigin - current pen position
 *
 * RETURN-NORMAL = 1
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG  GetLineOrigin(HDC hDC,PPOINTL pOrigin,PDDC pDDC,ULONG ulFunN)
{
  ULONG ulRet;

  if(!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreGetLineOrigin( pDDC->hdcMemory, pOrigin, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }

  GetCurrentPosition(hDC, pOrigin, pDDC, ulFunN);

  /*
  ** make it look like a display driver driver for call backs to the
  ** engine when in an area bracket
  */
  pDDC->DCState.ulLineStyle |= DDC_FIRST_PEL;
  ulRet = pDDC->DCState.ulLineStyle;
  LeaveDriver(pDDC);
  return (ulRet);
}

/***************************************************************************
 *
 * FUNCTION NAME = DisJointLines
 *
 * DESCRIPTION   = Optional function draws a sequence of disjoint straight
 *                 lines.
 *
 * INPUT         = hDC
 *                 pWrldPts
 *                 nPts
 *                 pDDC
 *                 ulFunN
 *
 * OUTPUT        = pOrigin - current pen position
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/

#define  ARRAY_SIZE    30
LONG  DisjointLines(HDC hDC,PPOINTL pWrldPts,LONG nPts,PDDC pDDC,
                               ULONG ulFunN)
{
  LONG Index;
  LONG Result = GPI_OK;
  BOOL bFreeMem = FALSE;
  POINTL aPtl[ARRAY_SIZE];


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    Result = InnerGreDisjointLines (pDDC->hdcMemory, pWrldPts, nPts, ulFunN);
    LeaveDriver( pDDC );
    return( Result );
  }

  if (!(ulFunN & COM_DRAW) || (ulFunN & (COM_AREA | COM_PATH |
     COM_BOUND | COM_ALT_BOUND)) || pDDC->DCState.ClipComplexity > 1)
  {

    /*
    **  Engine can do this one
    **  - if in a path the engine calls  DrawLinesInPath
    **    with clipped lines
    */
    Result = (*daDisjointLines)(hDC, pWrldPts, nPts, pDDC, ulFunN);
  }
  else
    if (nPts)
    {
      PPOINTL pPts = pWrldPts;

      /*
      **  If the data is supplied in world coordinates,  transform
      **  to device.  Copy data to local buffer,  then transform the
      **  copy.
      */
      if (ulFunN & COM_TRANSFORM)
      {

        /*
        ** if the points will fit in the stack array, use it.
        */
        if (nPts < ARRAY_SIZE)
          pPts = &aPtl[0];

        /*
        ** else alloc the mem
        */
        else
          if (pPts = (PPOINTL)GplMemoryAlloc(pDDC->pPDevice->hmcbHeap,
                                              sizeof(POINTL) * nPts))
            bFreeMem = TRUE;

        /*
        ** if we have some memory to copy the points to, copy and
        ** convert them
        */
        if (pPts)
        {
          SHORT Loop;


          for (Loop = 0; Loop < (SHORT)nPts; Loop++)
            pPts[Loop] = pWrldPts[Loop];
          convert_world_to_device(hDC, pPts, nPts, pDDC);
        }
        else
          Result = GPI_ERROR;          /* Failure                           */
      }

      /*
      ** Have some data - either user supplied or locally transformed
      */
      if (pPts)
      {
        if (ulFunN & COM_BOUND)
        {
          for (Index = 0; Index < nPts; Index += 2)
          {
            accumulate_line_bounds(hDC, pDDC, &pPts[Index], &pPts[Index+1]);
          }
        }

        /*
        ** If we are Drawing,  and the desired pen color is available
        ** select the correct pen, line style, and draw the lines
        */
        if ((pDDC->DCState.bFilling?check_area_color(pDDC):check_line_color
           (pDDC)) && (ulFunN & COM_DRAW))
        {
          if (pDDC->DCState.bFilling)
            select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
          else
            select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);

          set_line_type(pDDC, pDDC->DCState.lbnd.usType);

          for (Index = 0; Index < nPts; Index += 2)
          {
            draw_line(pDDC, &pPts[Index], &pPts[Index+1]);
          }
        }

        /*
        ** If we allocated memory,  free it now
        */
        if (bFreeMem)
          GplMemoryFree ((PVOID)pPts);

        SetCurrentPosition(hDC, &pWrldPts[nPts-1], pDDC,
                           MAKEULONG(NGreSetCurrentPosition, HIUSHORT(ulFunN)));
      }
    }

  LeaveDriver(pDDC);
  return  Result;
}
#undef   ARRAY_SIZE

/***************************************************************************
 *
 * FUNCTION NAME = PolyLine
 *
 * DESCRIPTION   = Draws a series of connected lines starting at the
 *                 current position.  Input is in world coordinates.
 *                 Final position is at last point.
 *
 * INPUT         = hDC      - handle to Device Context
 *                 pWrldPts - points to draw lines through (in world)
 *                 nPts     - number of points
 *                 pDDC     - pointer to device driver cookie
 *                 ulFunN   - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/
#define STK_ARRAY_SZ  30
LONG  PolyLine(HDC hDC,PPOINTL pWrldPts,LONG nPts,PDDC pDDC,ULONG
                          ulFunN)
{
  LONG   Index;
  LONG   Result = GPI_OK;
  BOOL   bMemAlloc = FALSE;
  POINTL pointls[STK_ARRAY_SZ];


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    if (ulFunN & (COM_PATH | COM_AREA))
    {
       Result = (ULONG) (*daPolyLine)( pDDC->hdc, pWrldPts, nPts, pDDC, ulFunN );
    }
    else
    {
       Result = InnerGrePolyLine( pDDC->hdcMemory, pWrldPts, nPts, ulFunN );
    }
    LeaveDriver( pDDC );
    return( Result );
  }

  if ( (ulFunN & (COM_AREA | COM_PATH | COM_ALT_BOUND)) ||
        pDDC->DCState.ClipComplexity > 1)
  {

    /*
    **  Engine can do this one
    **  - if in a path the engine calls  DrawLinesInPath
    **    with clipped lines
    */
    Result = (*daPolyLine)(hDC, pWrldPts, nPts, pDDC, ulFunN);
  }
  else
    if (nPts)
    {
      PPOINTL pPts = pWrldPts;


      if (ulFunN & COM_TRANSFORM)
      {
        /*
        ** if the the points will fit on our stack array
        ** use the stack
        */
        if (nPts < STK_ARRAY_SZ)
        {
          pPts = &pointls[0];
        }
        /*
        ** else alloc the mem
        */
        else
        {
        if (pPts = (PPOINTL)GplMemoryAlloc(pDDC->pPDevice->hmcbHeap,
                                            sizeof(POINTL) * nPts))
          bMemAlloc = TRUE;
        else
          Result = GPI_ERROR;          /* Failure                           */
        }

        if (pPts)
        {
          /*
          **    The data is supplied in world coordinates,  so transform
          **  to device.  Copy data to local buffer,  then transform the
          **  copy.
          */
          SHORT Loop;


          for (Loop = 0; Loop < (SHORT)nPts; Loop++)
            pPts[Loop] = pWrldPts[Loop];

          convert_world_to_device(hDC, pPts, nPts, pDDC);
        }
      }

      if (pPts)
      {
        /*
        **  Have some data - either user supplied or locally transformed
        */
        if (ulFunN & COM_BOUND)
        {
          POINTL TempPts;

          TempPts = pDDC->DCState.LogPosition;

          for (Index = 0; Index < nPts; Index++)
          {
            accumulate_line_bounds(hDC, pDDC, &TempPts, &pPts[Index]);
            TempPts = pPts[Index];
          }
        }

        if ( (ulFunN & COM_DRAW) &&
             (pDDC->DCState.bFilling ? check_area_color(pDDC) :
                                       check_line_color(pDDC)) )
        {
          /*
          **  Drawing,  and the desired pen color is available
          */
          if (pDDC->DCState.bFilling)
            select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
          else
            select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);

          /*
          ** Since notify_clip_change is called when the clip rectl
          ** changes, I do not think setting the clip rectl for every
          ** line is necessary. MarkV 08/13/92
          **
          **
          **      set_clip_rectangle( pDDC );
          */

          set_line_type(pDDC, pDDC->DCState.lbnd.usType);

          /*
          **      If current pen position is closer is to the end
          **  point than it is to the start point,  draw the lines
          **  in reverse order - this cuts down pen travel and so
          **  reduces plotting time.
          */

          if ((pDDC->DCState.lbnd.usType == LINETYPE_SOLID) &&
             get_closest_point(&pDDC->pPDevice->PhysPosition, &pPts[nPts-1],
                               &pDDC->DCState.LogPosition))
          {

            /*
            ** draw lines in reverse order
            */
            move_pen(pDDC, &pPts[nPts-1], FALSE);

            for (Index = nPts-2; Index >= 0; Index--)
              plot_line(pDDC, &pPts[Index]);

            plot_line(pDDC, &pDDC->DCState.LogPosition);
          }
          else
          {
            /*
            ** draw lines in order received
            */
            move_pen(pDDC, &pDDC->DCState.LogPosition, FALSE);

            for (Index = 0; Index < nPts; Index++)
              plot_line(pDDC, &pPts[Index]);
          }
        }

        /*
        **  If we allocated memory,  free it now
        */
        if (bMemAlloc)
        {
          GplMemoryFree ((PVOID)pPts);
        }
        SetCurrentPosition(hDC, &pWrldPts[nPts-1], pDDC,
                           MAKEULONG(NGreSetCurrentPosition, HIUSHORT(ulFunN)));
      }
    }

  LeaveDriver(pDDC);
  return  Result;
}
#undef STK_ARRAY_SZ

/***************************************************************************
 *
 * FUNCTION NAME = PolyShortLine
 *
 * DESCRIPTION   = This function processes polyshortlines.  A pointer
 *                 to the shortline header is passed.  Within the
 *                 header, start points and stop points are specified.
 *                 Following the header is an array of X's (currently
 *                 16 bits).  The curve starts at x0,y0 and ends at
 *                 x1,y1.  Then number of x values is determined by
 *                 the absolute value of y1-y1+1.  If y1 is greater
 *                 than y0, y0 is incremented until it equals y1.  If
 *                 y1 is less than y0, y0 is decremented until it
 *                 equals y1.  The current position is not affected by
 *                 this call.
 *
 * INPUT         = hDC      - handle to Device Context
 *                 pSL      - points to shortline
 *                 pDDC     - pointer to device driver cookie
 *                 ulFunN     - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/

LONG  PolyShortLine(HDC hDC,PSHORTLINE pSL,PDDC pDDC,ULONG ulFunN)
{
  LONG lIndex,lNumPts;
  BOOL   bDrawIt;
  ULONG  ulRet;
  BOOL   bIncrementY = FALSE, bFirstTime = TRUE;
  POINTL Pts[2];

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGrePolyShortLine (pDDC->hdcMemory, pSL, ulFunN);
    LeaveDriver( pDDC );
    return( ulRet );
  }

  /*
  ** MarkV- PolyShortLine can be called on an area fill, so check area
  */

  bDrawIt = (pDDC->DCState.bFilling ? check_area_color(pDDC) :
                                      check_line_color (pDDC));
  Pts[0].x = (LONG)pSL->slh.ptlStart.x;
  Pts[0].y = (LONG)pSL->slh.ptlStart.y;

  do
  {
    Pts[1].y = (LONG)pSL->slh.ptlStart.y;
    lNumPts = ABS(pSL->slh.ptlStop.y-pSL->slh.ptlStart.y)+1;

    if (pSL->slh.ptlStop.y > pSL->slh.ptlStart.y)
      bIncrementY = TRUE;

    for (lIndex = 1; lIndex < lNumPts; lIndex++)
    {
      Pts[1].x = (LONG)pSL->ax[lIndex];

      if (bIncrementY)
        ++Pts[1].y;
      else
        --Pts[1].y;

      if (ulFunN & COM_BOUND)
        accumulate_line_bounds(hDC, pDDC, &Pts[0], &Pts[1]);

      if (bDrawIt && (ulFunN & COM_DRAW))
      {
        if (bFirstTime)
        {
          bFirstTime = FALSE;

          /*
          ** select area color if filling
          */
          if (pDDC->DCState.bFilling)
            select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
          else                         /* else select line color            */
            select_line_pen(pDDC, pDDC->DCState.lbnd.lColor);

          set_line_type(pDDC, pDDC->DCState.lbnd.usType);
        }
        draw_line(pDDC, &Pts[0], &Pts[1]);
      }
      Pts[0] = Pts[1];
    }

    pSL = (PSHORTLINE)pSL->slh.pslhNext;

  } while (pSL);
  LeaveDriver(pDDC);
  return (1L);
}
/***************************************************************************
 *
 * FUNCTION NAME =  PolyScanline()
 *
 *
 * DESCRIPTION   =  PolyScanline
 *                  PolyScanline (HDC hDC, PSCANDATA pPSD, PDDC
 *                  pDDC,ULONG ulFunN) This function fills an area lying
 *                  between two polyshortlines using the current
 *                  pattern attributes.  This function receives a
 *                  pointer to a header structure which contains
 *                  pointers to linked left and right shortlines (see
 *                  PMDDI.H).  According to DCR#23504, the following is
 *                  noted:  The number of pairs in the buffer is never
 *                  odd.  Each pair will be the same height, and will
 *                  never intersect.  The ordering of the edges is from
 *                  bottom to top and left to right.  The plotters
 *                  routine however, does not assume that this is true.
 *                  If start Y > stop Y, then Y is decremented.  If
 *                  start Y < stop Y, then Y is incremented.  This is
 *                  checked for both left and right sides.  Thus, for
 *                  example, the left side could be increasing (bottom
 *                  to top) while the right side is decreasing (top to
 *                  bottom).  This is a REQUIRED function.  returns:
 *                  0L error 1L ok
 *
 *
 *
 *
 *
 * INPUT         =  HDC hDC;
 *                  PSCANDATA pPSD;
 *                  PDDC pDDC;
 *                  ULONG ulFunN;
 *
 *
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = lResult
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/
#ifndef  COM_PRECLIP                   /* defined in 2.0 headers eng ver
                                          202 or greater                    */
  #define  COM_PRECLIP   0x0400        /* Lines are preclipped              */
#endif

LONG  PolyScanline(HDC hDC,PSCANDATA pPSD,PDDC pDDC,ULONG ulFunN)
{
  LONG lResult = 1L;
  POINTL Pts[2];

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    lResult = InnerGrePolyScanline (pDDC->hdcMemory, pPSD, ulFunN);
    LeaveDriver( pDDC );
    return( lResult );
  }


  if (ulFunN & COM_BOUND)
     AccumulateBounds(hDC, &pPSD->rclBound, pDDC, ulFunN);

  if ((ulFunN & COM_DRAW) && check_area_color(pDDC))
  {
    SHORT Index,NumPts,YIncrement;
    LONG CurLeftY,CurRightY;
    PSHORTLINE pslCurLeft = pPSD->pslFirstLeft;
    PSHORTLINE pslCurRight = pPSD->pslFirstRight;

    if (pDDC->DCState.bDrawingText)
      select_text_pen(pDDC, pDDC->DCState.abnd.lColor);
    else
      select_fill_pen(pDDC, pDDC->DCState.abnd.lColor);
    set_line_type(pDDC, LINETYPE_SOLID);

    ///*
    //**  Set flag to identify the coordinates as device.  MarkV
    //*/
    //pDDC->DCState.bDeviceSetCurrentPos = TRUE;

    /*
    ** make sure polyline(if it's called) will use the fill pen
    */
    pDDC->DCState.bFilling = TRUE;

    for (pslCurLeft = pPSD->pslFirstLeft,
         pslCurRight = pPSD->pslFirstRight; pslCurLeft != NULL|pslCurRight !=
            NULL; pslCurLeft = (PSHORTLINE)pslCurLeft->slh.pslhNext,
         pslCurRight = (PSHORTLINE)pslCurRight->slh.pslhNext)
    {

      if ((NumPts = pslCurRight->slh.ptlStop.y-pslCurRight->slh.ptlStart.y) > 0)
        YIncrement = 1;
      else
      {
        YIncrement = -1;
        NumPts     = -NumPts;
      }
      CurLeftY  = (LONG)pslCurLeft->slh.ptlStart.y;
      CurRightY = (LONG)pslCurRight->slh.ptlStart.y;

      /*
      ** if lines are already clipped,
      ** just plot them.
      */
      if (ulFunN & COM_PRECLIP)
      {
        for (Index = 0; Index < NumPts; Index++)
        {
          Pts[0].x = (LONG)pslCurLeft->ax[Index];
          Pts[0].y = CurLeftY;
          Pts[1].x = (LONG)pslCurRight->ax[Index];
          Pts[1].y = CurRightY;

          if (get_closest_point(&pDDC->pPDevice->PhysPosition, &Pts[1], &Pts[0]))
          {
            move_pen(pDDC, &Pts[1], FALSE);
            plot_line(pDDC, &Pts[0]);
          }
          else
          {
            move_pen(pDDC, &Pts[0], FALSE);
            plot_line(pDDC, &Pts[1]);
          }

          CurLeftY  += YIncrement;
          CurRightY += YIncrement;
        }
      }
      /*
      ** else lines may need clipping
      */
      else
      {
        /*
        ** make sure polyline will use the fill pen
        */
        pDDC->DCState.bFilling = TRUE;

        for (Index = 0; Index < NumPts; Index++)
        {
          Pts[0].x = (LONG)pslCurLeft->ax[Index];
          Pts[0].y = CurLeftY;
          Pts[1].x = (LONG)pslCurRight->ax[Index];
          Pts[1].y = CurRightY;

          /*
          ** Check if some portion of the line
          ** is withen the current clip bounds. MarkV
          */
          if (Pts[0].x <= pDDC->DCState.ClipRect.xRight && Pts[1].x >=
             pDDC->DCState.ClipRect.xLeft && Pts[0].y <=
             pDDC->DCState.ClipRect.yTop && Pts[0].y >=
             pDDC->DCState.ClipRect.yBottom)
          {

            /*
            ** This is done in notify clip change.  Taken out by MarkV
            ** set_clip_rectangle (pDDC);
            */
            /*
            ** if clipping is simple send the lines to the device.
            ** The device has been set to do the clipping in
            ** NotifyClipChange.
            */
            if (pDDC->DCState.ClipComplexity < 2)
            {
              if (get_closest_point(&pDDC->pPDevice->PhysPosition,
                                    &Pts[1], &Pts[0]))
              {
                move_pen(pDDC, &Pts[1], FALSE);
                plot_line(pDDC, &Pts[0]);
              }
              else
              {
                move_pen(pDDC, &Pts[0], FALSE);
                plot_line(pDDC, &Pts[1]);
              }
            }                          /* end simple clipping               */

            /*
            ** else Complex clipping, call the engine with each line
            ** to be clipped
            */
            else
            {
              SetCurrentPosition(hDC, (PPOINTL)&Pts[0], pDDC,
                                 MAKEULONG(NGreSetCurrentPosition, 0));
              PolyLine(hDC, (PPOINTL)&Pts[1], 1L, pDDC,
                             MAKEULONG(NGrePolyLine, COM_DRAW));
            }                          /* end complex clipping              */
          }                            /* end if in clip bounds             */

          CurLeftY  += YIncrement;
          CurRightY += YIncrement;
        }                              /* end for                           */
      }                                /* end else may need clipping        */
    }

    pDDC->DCState.bFilling = FALSE;
    //pDDC->DCState.bDeviceSetCurrentPos = FALSE;
  }
  LeaveDriver(pDDC);
  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = SetCurrentPosition
 *
 * DESCRIPTION   = Sets the current position to that supplied.  This
 *                 value is recorded in both world and device
 *                 coordinates, so, if required, the values are
 *                 transformed into device coordinates.
 *
 * INPUT         = hDC      - handle to Device Context
 *                 pPts     - new position (in World)
 *                 pDDC     - pointer to device driver cookie
 *                 ulFunN   - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/

LONG  SetCurrentPosition(HDC hDC,PPOINTL pPts,PDDC pDDC,ULONG ulFunN)
{
  LONG Result = GPI_OK;

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    if ((Result = InnerGreSetCurrentPosition (pDDC->hdcMemory, pPts, ulFunN)) &&
       (ulFunN & (COM_PATH | COM_AREA)))
    {
      /*
      ** Call back to engine.
      */
      Result = (ULONG) (*daSetCurrentPosition) (pDDC->hdc, pPts, pDDC, ulFunN );
    }
    LeaveDriver( pDDC );
    return( Result );
  }

  pDDC->DCState.WorldPosition = *pPts;
  pDDC->DCState.LogPosition = *pPts;

   /*
   ** Transform the device coordinates if required
   */

  if (ulFunN & COM_TRANSFORM)
  {
    convert_world_to_device(hDC, &pDDC->DCState.LogPosition, 1L, pDDC);
  }
  else
  {
    ///*
    //** if called with device coordinates  #44143
    //*/
    //if (pDDC->DCState.bDeviceSetCurrentPos)
      convert_device_to_world(hDC, &pDDC->DCState.WorldPosition, 1L, pDDC);
  }

  /*
  **  Spec says to call engine when in area or path
  */
  if (ulFunN & (COM_AREA | COM_PATH))
  {
    Result = (*daSetCurrentPosition)(hDC, pPts, pDDC, ulFunN);

    /*
    **  Make it look like a display driver for call backs to the engine
    */
    Result |= DDC_FIRST_PEL;
  }

  LeaveDriver(pDDC);
  return  Result;
}

/***************************************************************************
 *
 * FUNCTION NAME = SetLineOrigin
 *
 * DESCRIPTION   = This function acts just like SetCurrentPosition.
 *
 * INPUT         = hDC      - handle to Device Context
 *                 pOrigin  - new position (in World)
 *                 ulStyle  - Line style
 *                 pDDC     - pointer to device driver cookie
 *                 ulFunN     - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 **************************************************************************/

LONG  SetLineOrigin(HDC hDC,PPOINTL pOrigin,ULONG ulStyle,PDDC pDDC,
                               ULONG ulFunN)
{
  ULONG ulRet;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreSetLineOrigin( pDDC->hdcMemory, pOrigin, ulStyle, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }

  pDDC->DCState.ulLineStyle = ulStyle;
  ulFunN &= MAKEULONG(0xffff, ~(HIUSHORT(COM_AREA + COM_PATH)));
  ulRet = SetCurrentPosition(hDC, pOrigin, pDDC, ulFunN);
  LeaveDriver(pDDC);
  return(ulRet);
}

