/*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 =  CLIP.C
 *
 * DESCRIPTIVE NAME =  OS/2 Plotter Presentation Driver Clipping Routines
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION File contains OS/2 Plotter Presentation Driver Clipping Routines
 *
 *
 * FUNCTIONS   quad_sort()          Sorts the corner points of a quadrilateral
 *
 *
 *             choose_endpoints     choose_endpoints
 *
 *
 *             rectl_intersect()    Intersects the last two parameters
 *
 *
 *             merge_clip_rect()    merge the myriad of clip rectangles
 *
 *
 *             apply_clipping()     Determine the clipping rectangle
 *
 *
 *             set_clip_rectangle() sets the current soft clip limits at the
 *                                  plotter firmware
 *
 *
 *             init_quad_clip       Initialize the QCINFO structure
 *
 *
 *             get_bounds()          Returns the rectangular bounds
 *                                   (bounding box) of a point list
 *
 *
 *             get_quad_clip()      Returns a rectangle of an arbitrary height
 *
 *
 *             output_clip_rect()   sets the current soft clip limits at the
 *                                  plotter firmware,
 *
 *
 *             NotifyClipChange()   Called whenever an application requests
 *                                  to change any of
 *
 *
 *
 *
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*//*
** LISTOFF             code file post-processor directive
*/

#include "plotters.h"
#include "clip.h"
#include "dispatch.h"
#include "dosio.h"
#include "output.h"
#include "xforms.h"
#include "prdmath.h"
#include "utils.h"
#include "lockddc.h"

/*
** LISTON
*/
LONG round_divide(LONG, LONG);
/*
** þ Local Constants and Macros þþþþþ
*/

#define  MIN_LONG      0x80000000
#define  MAX_LONG      0x7FFFFFFF
#define  SLOPE(p0,p1)  frdiv( MAKEFIXED( (p1).y - (p0).y, 0 ), MAKEFIXED( (p1)\
.x - (p0).x, 0 ) )
#define  INTERCEPT(p,m) ( MAKEFIXED( (p).y, 0 ) - frmul( (m), MAKEFIXED( (p).x\
, 0 ) ) )
#define  INBOUNDS(x,min,max) ( ( (x) >= (min) ) && ( (x) <= (max) ) )

/*
** þ Local Function Prototypes þþþþþ
*/

LOCAL VOID quad_sort(PPOINTL);

LOCAL VOID choose_endpoints(PFIXED,PLONG,LONG,LONG);

/***************************************************************************
 *
 * FUNCTION NAME = quad_sort()
 *
 * DESCRIPTION   = Sorts the corner points of a quadrilateral
 *                 Sorts the corner points of a quadrilateral into
 *                 anti-clockwise order.  The result is returned in
 *                 the original array.
 *
 *
 * INPUT         = pptlPt
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LOCAL VOID quad_sort(PPOINTL pptlPt)
{
  INT i,j;
  POINTL ptlTemp;


  for (i = 0; i < 3; i++)
  {
    for (j = i+1; j < 4; j++)
    {
      if (pptlPt[i].y > pptlPt[j].y)
      {
        ptlTemp = pptlPt[i];
        pptlPt[i] = pptlPt[j];
        pptlPt[j] = ptlTemp;
      }
    }
  }

  for (i = 0; i < 3; i++)
  {
    for (j = i+1; j < 4; j++)
    {
      if (pptlPt[i].x > pptlPt[j].x)
      {
        ptlTemp   = pptlPt[i];
        pptlPt[i] = pptlPt[j];
        pptlPt[j] = ptlTemp;
      }
    }
  }
  ptlTemp   = pptlPt[2];
  pptlPt[2] = pptlPt[3];
  pptlPt[3] = ptlTemp;
}

/***************************************************************************
 *
 * FUNCTION NAME =  choose_endpoints
 *
 *
 * DESCRIPTION   =  choose_endpoints
 *                   Given the horizontal components (x) of four
 *                   co-linear points which intersect a
 *                   quadrilateral's extended boundary walls, returns
 *                   the values of the innermost two, ie:  those which
 *                   span the interior of the figure.  For example:
 *                   in the diagram below, the x value of points p1
 *                   and p2 would be returned.
 *
 *                            .   .
 *                     --------o------------         <- bounding box top
 *               .     |     /    \        |
 *                 .   |   /        \      |      .  <- boundary wall extension
 *                   . | /            \    |    .
 *                     o                \  |  .
 *                   . | \                \ .
 *                 .   |   \               o
 *             --p-----------p-----------p---p-----  <- current scan line
 *             . 0     |     1 \       / 2 | 3 .
 *                     |         \   /     |     .
 *                     ------------o--------         <- bounding box bottom
 *                          .   .
 *                             .       .
 *
 *
 * INPUT         =   PFIXED pfxX
 *                   PLONG plX;
 *                   LONG lMin;
 *                   LONG lMax;
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LOCAL VOID choose_endpoints(PFIXED pfxX,PLONG plX,LONG lMin,LONG
                            lMax)
{
  LONG i,j;
  LONG lTemp;
  LONG lX[4];


  for (i = 0; i < 4; i++)
  {
    lX[i] = (pfxX[i] >> 16);
  }
  for (i = 0; i < 3; i++)
  {
    for (j = i+1; j < 4; j++)
    {
      if (lX[i] > lX[j])
      {
        lTemp = lX[i];
        lX[i] = lX[j];
        lX[j] = lTemp;
      }
    }
  }

  if (INBOUNDS(lX[1], lMin, lMax))
  {
    plX[0] = lX[1];
    plX[1] = lX[2];
  }
  else
  {
    plX[0] = lX[2];
    plX[1] = lX[3];
  }

  if (plX[0] < lMin)
    plX[0] = lMin;

  if (plX[1] > lMax)
    plX[1] = lMax;
}

/*
** Global Functions
*/
/***************************************************************************
 *
 * FUNCTION NAME = rectl_intersect()
 *
 *
 * DESCRIPTION   = Intersects the last two parameters
 *               = Intersects the last two parameters, returning the
 *                 result in the first parameter.         straight
 *                 forward.
 *
 * INPUT         = PRECTL prResult;
 *                 PRECTL prIn0;
 *                 PRECTL prIn1;
 *
 *
 *
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID rectl_intersect(PRECTL prResult,PRECTL prIn0,PRECTL prIn1)
{
  prResult->xLeft = MAX(prIn0->xLeft, prIn1->xLeft);
  prResult->xRight = MIN(prIn0->xRight, prIn1->xRight);
  prResult->yBottom = MAX(prIn0->yBottom, prIn1->yBottom);
  prResult->yTop = MIN(prIn0->yTop, prIn1->yTop);

  if (prResult->xLeft > prResult->xRight ||
      prResult->yBottom > prResult->yTop )
  {
    prResult->xLeft = prResult->xRight = 0;
    prResult->yBottom = prResult->yTop = 0;
  }
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = merge_clip_rect()
 *
 *
 * DESCRIPTION   = merge the myriad of clip rectangles
 *                 Merge the myriad of clip rectangles to produce the
 *                 largest single rectangle covering the area.
 *
 *
 *
 * INPUT         = HDC hDC;
 *                 PRECTL prOut;
 *                 PRECTL prIn;
 *                 PUSHORT pusIndex;
 *                 PDDC pDDC;
 *
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

USHORT merge_clip_rect(HDC hDC,PRECTL prOut,PRECTL prIn,PUSHORT
                       pusIndex,PDDC pDDC)
{

  /*
  **    Operation is basically simple.  Loop calling GetClipRects() until
  **  we get rectangle that is no longer an extension of the one we are
  **  building,  OR until we run out of rectangles!
  */
  RGNRECT rrControl;
  RECTL rectlSum;
  BOOL bStart;
  USHORT usResult;

  bStart = TRUE;
  rrControl.ircStart = *pusIndex;
  rrControl.crc = 1;
  rrControl.ulDirection = RECTDIR_LFRT_TOPBOT;
  usResult = MCR_NONE;

  while (GreGetClipRects(hDC, prIn, &rrControl,
                         &rectlSum) && rrControl.crcReturned > 0)
  {

    if (bStart)
    {
      *prOut   = rectlSum;
      bStart   = FALSE;
      usResult = MCR_PART;
    }

    else
    {

      if (rectlSum.xLeft == prOut->xLeft &&
         rectlSum.xRight == prOut->xRight &&
         rectlSum.yTop == prOut->yBottom)
      {
        prOut->yBottom = rectlSum.yBottom;
      }
      else
      {
        break;
      }
    }
    rrControl.ircStart++;
  }

  if (!bStart)
    if (rrControl.crcReturned == 0)
      if (prIn && prOut)
        if ((prIn->xLeft == prOut->xLeft) &&
            (prIn->xRight == prOut->xRight) &&
            (prIn->yTop == prOut->yTop) &&
            (prIn->yBottom == prOut->yBottom) )
          usResult = MCR_WHOLE;

  *pusIndex = rrControl.ircStart;

  return  usResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = apply_clipping()
 *
 *
 * DESCRIPTION   = Determine the clipping rectangle
 *                 Determine the overall clipping rectangle, and
 *                 return to the caller.  This may involve including
 *                 the passed in rectangle prIn if this is not zero
 *                 (If it IS zero, it is ignored).  The basic clipping
 *                 information is obtained from the DDC.
 *
 *
 *
 *
 * INPUT         = HDC hDC;
 *                 PRECTL prResult;
 *                 PRECTL prIn;
 *                 PDDC pDDC;
 *
 *
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = AC_RECT0  - (Effectively) no clipping - prResult untouched.
 *                 AC_RECT1  - One clip rectangle, returned in prResult.
 *                 AC_RECT2  - Multiple clipping rectangles
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

SHORT apply_clipping(HDC hDC,PRECTL prResult,PRECTL prIn,PDDC pDDC)
{

  /*
  **     Use the ClipComplexity field to determine if there is any clipping
  **  already in effect.  If not,  then clipping is determined by prIn:
  **  if zero,  no clipping,  otherwise that is the clip rectangle.
  **     If ClipComplexity is 1,  then a single rectangle is the current
  **  clip limit,  so the output is either that OR the intersection of
  **  that AND prIn.
  **     Otherwise,  we have a multiple clip rectangle situation,  and so
  **  need to take the really complex route.
  */

  SHORT sResult;


  if (pDDC->DCState.ClipComplexity == 0)
  {
    if (prIn)
    {
      *prResult = *prIn;
      sResult = AC_RECT1;
    }
    else
      sResult = AC_RECT0;
  }
  else
  {
    if (pDDC->DCState.ClipComplexity == 1)
    {
      if (prIn)
      {
        rectl_intersect(prResult, prIn, &pDDC->DCState.ClipRect);
      }
      else
      {
        *prResult = pDDC->DCState.ClipRect;
      }
      sResult = AC_RECT1;
    }
    else
    {
      sResult = AC_RECT2;

      if (prIn)
      {
        USHORT usBase;

        usBase = 1;

        if (merge_clip_rect(hDC, prResult, prIn, &usBase,
                            pDDC) == MCR_WHOLE)
          sResult = AC_RECT1;
      }
    }
  }
  if (sResult == AC_RECT1)
  {
    output_clip_rect(pDDC, 1L, prResult);
  }

  return  sResult;
}

/***************************************************************************
 *
 * FUNCTION NAME =  set_clip_rectangle()
 *
 *
 * DESCRIPTION   =  sets the current soft clip limits at the plotter firmware
 *
 *
 * INPUT         =  pDDC
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID set_clip_rectangle(PDDC pDDC)
{
  output_clip_rect(pDDC, pDDC->DCState.ClipComplexity,
                   &pDDC->DCState.ClipRect);
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = init_quad_clip
 *
 *
 * DESCRIPTION   = Initialize the QCINFO structure
 *                 Initializes the QCINFO structure supplied by the
 *                 caller based on AC_RECT0 - (Effectively) no clipping
 *                 - prResult untouched.  the supplied corner points of
 *                 a given quadrilateral.  To expedite AC_RECT1 - One
 *                 clip rectangle, returned in prResult.  later
 *                 processes we arrange the quad's corners in an
 *                 anti-clockwise AC_RECT2 - Multiple clipping
 *                 rectangles order, determine the quad's bounding box,
 *                 the slope and y axis intercept for each line.  All of
 *                 this is based on the equation of a line:  y=mx+b,
 *                 where m is the slope and b is the y axis intercept;
 *                 basically everything we need to know about the line.
 *                 We deal with the lines as directed segments from left
 *                 to right.  Consider the diagram below:
 *
 *
 *                        p            m0 = SLOPE ( p0 -> p1 )
 *                  m1  / 3 \  m2
 *                    /       \        m1 = SLOPE ( p0 -> p3 )
 *                  p           p
 *                  0 \       / 2      m2 = SLOPE ( p3 -> p2 )
 *                      \   /
 *                  m0    p   m3       m3 = SLOPE ( p1 -> p2 )
 *                        1
 *
 *                 See also diagram in choose_endpoints().
 *
 *
 *
 *
 *
 *
 *
 * INPUT         = PQCINFO pQC;
 *                 PPOINTL pptlPt;
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID init_quad_clip(PQCINFO pQC,PPOINTL pptlPt)
{
  INT i;
  POINTL ptlPt[4];


  for (i = 0; i < 4; i++)
  {
    ptlPt[i] = pptlPt[i];
    pQC->fxM[i] = 0L;
  }

  /*
  **  Arrange points anti-clockwise
  */
  quad_sort(ptlPt);

  /*
  ** Get minimum and maximum, x and y values (bounding box)
  */
  get_bounds(4, ptlPt, &pQC->rctlBox);

  /*
  ** Calculate slopes of the lines
  */
  if (ptlPt[1].x-ptlPt[0].x)
    pQC->fxM[0] = SLOPE(ptlPt[0], ptlPt[1]);

  if (ptlPt[3].x-ptlPt[0].x)
    pQC->fxM[1] = SLOPE(ptlPt[0], ptlPt[3]);

  if (ptlPt[2].x-ptlPt[3].x)
    pQC->fxM[2] = SLOPE(ptlPt[3], ptlPt[2]);

  if (ptlPt[2].x-ptlPt[1].x)
    pQC->fxM[3] = SLOPE(ptlPt[1], ptlPt[2]);

  /*
  ** Calculate slope Y intercepts
  */
  pQC->fxB[0] = INTERCEPT(ptlPt[0], pQC->fxM[0]);
  pQC->fxB[1] = INTERCEPT(ptlPt[0], pQC->fxM[1]);
  pQC->fxB[2] = INTERCEPT(ptlPt[3], pQC->fxM[2]);
  pQC->fxB[3] = INTERCEPT(ptlPt[1], pQC->fxM[3]);

  /*
  ** Set incremental size used in rectangle generation
  */
  pQC->lSize = QC_RECTYSIZE;

  /*
  ** Set first scan line close to boundary box bottom
  */
  pQC->lD = pQC->rctlBox.yBottom+(pQC->lSize >> 1);

  /*
  ** Indicate that quad clipping is in effect
  */
  pQC->bEnabled = TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = get_bounds()
 *
 *
 * DESCRIPTION   = Returns the rectangular bounds (bounding box) of a point list
 *
 *
 * INPUT         = INT iPts;
 *                 PPOINTL pptlPt;
 *                 PRECTL prctlBox
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID get_bounds(INT iPts,PPOINTL pptlPt,PRECTL prctlBox)
{
  PPOINTL pptlEnd;

  prctlBox->xLeft   = MAX_LONG;
  prctlBox->xRight  = MIN_LONG;
  prctlBox->yBottom = MAX_LONG;
  prctlBox->yTop    = MIN_LONG;
  pptlEnd           = pptlPt+iPts;

  while (pptlPt < pptlEnd)
  {
    if (pptlPt->y > prctlBox->yTop)
      prctlBox->yTop = pptlPt->y;

    if (pptlPt->x < prctlBox->xLeft)
      prctlBox->xLeft = pptlPt->x;

    if (pptlPt->y < prctlBox->yBottom)
      prctlBox->yBottom = pptlPt->y;

    if (pptlPt->x > prctlBox->xRight)
      prctlBox->xRight = pptlPt->x;
    pptlPt++;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = get_quad_clip()
 *
 *
 * DESCRIPTION   = Returns a rectangle of an arbitrary height
 *                 Returns a rectangle of an arbitrary height extending
 *                 above a given scan line.  When bStart is true the first
 *                 scan line is processed.  When bStart is false, the next
 *                 scan line is processed.
 *
 *
 * INPUT         = PQCINFO pQC;
 *                 PRECTL prctlBox;
 *                 BOOL bStart;
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = TRUE
 *
 *
 *
 * RETURN-ERROR  = FALSE
 *
 *
 *
 **************************************************************************/

BOOL get_quad_clip(PQCINFO pQC,PRECTL prctlBox,BOOL bStart)
{
  FIXED fxD;
  FIXED fxX[4];
  LONG lX[4];
  INT i;


  if (bStart)
    /*
    ** start slightly above the current scan line
    */
    pQC->lD = pQC->rctlBox.yBottom+(pQC->lSize >> 1);

  if ((pQC->lD+pQC->lSize) >= pQC->rctlBox.yTop)
    /*
    ** out of bounds, we are done...
    */
    return  FALSE;

  /*
  ** Convert current scan line to fixed point
  */
  fxD = MAKEFIXED(pQC->lD, 0);

  /*
  **  Calculate the x value of each line:
  **      x =  (y - b) / m
  */
  for (i = 0; i < 4; i++)
  {
    fxX[i] = frdiv(fxD-pQC->fxB[i], pQC->fxM[i]);
  }

  /*
  ** Choose the interior x values for the current scan line
  */
  choose_endpoints(fxX, lX, pQC->rctlBox.xLeft,
                   pQC->rctlBox.xRight);
  /*
  ** Convert next scan line to fixed
  */

  fxD = MAKEFIXED(pQC->lD+pQC->lSize, 0);
  /*
  **  Calculate the x value of each line:
  **      x =  (y - b) / m
  */
  for (i = 0; i < 4; i++)
  {
    fxX[i] = frdiv(fxD-pQC->fxB[i], pQC->fxM[i]);
  }

  /*
  ** Choose the interior x values for the next scan line
  */
  choose_endpoints(fxX, &lX[2], pQC->rctlBox.xLeft,
                   pQC->rctlBox.xRight);
  /*
  **  Choose the shorter line as the horizontal limit for the
  **  rectangle were generating (don't want to stick outside figure).
  */
  if (ABS(lX[1]-lX[0]) > ABS(lX[3]-lX[2]))
  {
    lX[0] = lX[2];
    lX[1] = lX[3];
  }

  /*
  ** Return the rectangle to the caller.
  */
  prctlBox->xLeft = lX[0];
  prctlBox->xRight = lX[1];
  prctlBox->yBottom = pQC->lD;
  prctlBox->yTop = pQC->lD+pQC->lSize;

  /*
  ** Increment to the next scan line
  */
  pQC->lD += pQC->lSize;

  /*
  ** Let caller know we returned a rectangle.
  */
  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = output_clip_rect()
 *
 *
 * DESCRIPTION   = sets the current soft clip limits at the plotter firmware,
 *                 with some intelligence.
 *
 *                 notes:
 *                    1. This routine is called from multiple locations within
 *                       the driver, and it is important not to modify the
 *                       the pClipRect data fields within the pPDevice portion
 *                       of the driver instance data.
 *                    2. If ClipComplexity == 0 and the pPDevice->ClipComplexity
 *                       is one, this signifies a null rectangle.
 *
 *
 *
 *
 *
 * INPUT         = PDDC pDDC;
 *                 LONG ClipComplexity;
 *                 PRECTL pClipRect;
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID output_clip_rect(PDDC pDDC,LONG ClipComplexity,PRECTL
                      pClipRect)
{
  /*
  **   Output the specified clipping limits to the plotter.  Returns no value.
  **  If ClipComplexity is 0 and a previous clipping limit was in effect, then
  **  output is initiated such that all drawing output is clipped.
  **  If ClipComplexity is 1,  set the limit to that contained in pClipRect.
  **  pClipRect is in pels.
  **  If ClipComplexity is any other value, then disable clipping.
  **
  **    NOTE:  Updates the PDevice values to reflect new values.  DOES NOT
  **  change the DDC values, since then this function can be called with
  **  a temporary clip limit change (e.g. by CharStringPos() ) which
  **  is sent to the plotter.  This temporary change is then undone by
  **  calling set_clip_rectangle,  which will then output the values
  **  remaining in the DDC.
  */

  PPDEVICE pPDevice = pDDC->pPDevice;
  POINTL   Origin[2];
  BOOL     bDifferent;

  /*
  ** Only output the clip rectangle if the page is started
  ** Start_page will send a header with the current clip rectangle
  */
  if (!pPDevice->bPageStarted)
  {
    return;
  }
  else
  {
    /*
    ** if Null rectl
    */
    if (ClipComplexity == 0L)
    {
      /*
      ** if device is not in null rectl mode
      */
      if (pPDevice->ClipComplexity != 0L)
      {
        /*
        ** Null rectl
        */
        check_string_construction(pDDC);

        /*
        ** HPGL2 devices did not clip with IW0,0,-1,-1
        ** Setting 0,0 may produce a 1 pell dot.
        */
        if (pPDevice->usHPGLType & HPGL2)
          output_bytes(pDDC, "IW0,0,0,0");
        else
          output_bytes(pDDC, "IW0,0,-1,-1");

        pPDevice->ClipRect = *pClipRect;
        pPDevice->bLineInConstruction = FALSE;
      }
    }
    else
    {
      /*
      ** if Complex area- Engine will do the clipping
      */
      if ( ClipComplexity > 1L ||
           ClipComplexity == -1L)
      {
        /*
        ** if the device is not in complex clip mode
        */
        if (pPDevice->ClipComplexity == 0L ||
            pPDevice->ClipComplexity == 1L)
        {
          /*
          ** Complex area- Engine will do the clipping
          ** so set to hard clip area
          ** NOTE: -1 is a special flag we use from ClipChar
          **       to turn off clipping.
          */
          RECTL Rectl;
          Rectl = pDDC->pPDevice->DefaultP1P2;
          Rectl.xRight = Rectl.xRight - Rectl.xLeft;
          Rectl.xLeft = 0;
          Rectl.yTop = Rectl.yTop - Rectl.yBottom;
          Rectl.yBottom = 0;

          /*
          ** TRUE if new clip rectangle is different from
          ** current the soft-clip limits
          */
          bDifferent = pPDevice->ClipRect.xLeft != Rectl.xLeft ||
                       pPDevice->ClipRect.xRight != Rectl.xRight ||
                       pPDevice->ClipRect.yTop != Rectl.yTop ||
                       pPDevice->ClipRect.yBottom != Rectl.yBottom;
          if (bDifferent)
          {
            check_string_construction(pDDC);
            output_bytes(pDDC, "IW");
            pPDevice->ClipRect = Rectl;
            pPDevice->bLineInConstruction = FALSE;
          }
        }
      }
      /*
      ** ClipCompexity == 1 SIMPLE CLIP CASE
      */
      else
      {
        /*
        ** TRUE if new clip rectangle is different from
        ** current the soft-clip limits
        */
        bDifferent = pPDevice->ClipRect.xLeft != pClipRect->xLeft ||
                     pPDevice->ClipRect.xRight != pClipRect->xRight ||
                     pPDevice->ClipRect.yTop != pClipRect->yTop ||
                     pPDevice->ClipRect.yBottom != pClipRect->yBottom;

        if (bDifferent)
        {
//#define DEBUGCLIP 1
// note: Debugging clip rectanges may cause some text not to come
//       out because the current position is not correctly restored
//       when clipping the same character many times.
#ifdef  DEBUGCLIP
          /*
          ** draw the clip rectangle
          */
          {
             POINTL ptlSavePos;
             ptlSavePos = pPDevice->PhysPosition;

             pPDevice->bLineInConstruction = FALSE;

             if (bNewValue && bDifferent)
             {
               check_string_construction(pDDC);
               /*
               **  Subtract 1 from top right because the plotter is
               **  inclusive and the engine is exclusive on the top
               **  right.
               **  MV
               */
               output_bytes(pDDC, "IW;");

               Origin[0].x = pClipRect->xLeft;
               Origin[0].y = pClipRect->yBottom;
               Origin[1].x = pClipRect->xRight - 1;
               Origin[1].y = pClipRect->yTop - 1;

               if (pPDevice->fsTransform & DXF_PORTRAIT)
                 SWAPLONG(Origin[0].y, Origin[1].y);
               move_pen( pDDC, &Origin[0], FALSE );
               output_bytes( pDDC, "EA" );
               construct_point( pDDC, &Origin[1], FALSE );

               pPDevice->bLineInConstruction = FALSE;
               move_pen(pDDC, &ptlSavePos, TRUE );

             }
          }
#endif

          {
            pPDevice->bLineInConstruction = FALSE;
            check_string_construction(pDDC);
            /*
            ** IW- Defines a rectangular area, or window,
            ** that establishes soft-clip limits.
            */
            output_bytes(pDDC, "IW");

            /*
            **  subtract 1 to the top Right because the plotter is
            **  inclusive on all sides and the on the engine is exclusive
            **  on the top right.
            **  MarkV
            */
            pPDevice->ClipRect = *pClipRect;

            Origin[0].x = pClipRect->xLeft;
            Origin[0].y = pClipRect->yBottom;
            Origin[1].x = pClipRect->xRight - 1;
            Origin[1].y = pClipRect->yTop - 1;

            /*
            ** Code added to fix a     in the HP DRAFTPRO DXL
            ** Setting a 1 pelwide clip rectangle produced no clipping. MV
            ** 6182 had the same problem with 1 pel clip rectangles.
            ** So I make the check for non HPGL2 devices. MV
            ** if (pPDevice->Plotter == CLASS_HP7570A)
            */
            if (!(pPDevice->usHPGLType & HPGL2))
            {
              /*
              ** min clip rectangle must be 2 pels
              */
              if (Origin[0].x == Origin[1].x)
              {
                Origin[1].x++;
              }
              if (Origin[0].y == Origin[1].y)
              {
                Origin[1].y++;
              }
              /*
              ** try to fix prtdd-41 "g" clipping 1 line
              */
              if (pPDevice->Plotter == CLASS_HP7585B ||
                  pPDevice->Plotter == CLASS_HP7470A)
              {
                LONG lDiff;
                /*
                ** min clip rectangle must be 6 pels
                */
                lDiff = (Origin[1].x - Origin[0].x);
                if (lDiff < 4)
                {
                  Origin[1].x += lDiff;
                }
                lDiff = (Origin[1].y -  Origin[0].y);
                if (lDiff < 4)
                {
                  Origin[1].y += lDiff;
                }
              }
            }

            convert_point(pDDC, &Origin[0], FALSE);
            convert_point(pDDC, &Origin[1], FALSE);

            /*
            ** if we must scale the IW values to plotter units
            */
            if (!(PlotterClass[pPDevice->Plotter].fsOdd & OD_USERIW))
            {
              /*
              ** Warning: Scaling may be off if the IP P1P2 command
              ** was larger than the printable area the plotter
              ** will adjust the scaling factor and we will be
              ** using the unadjusted factor.  Solution: Change
              ** the PaperOff entry to be accurate.
              */
              LONG lScale = pPDevice->lIWScale;
              LONG lMultiplier = pPDevice->lIWScaleMultiplier;
              ///*
              //** It looks like HPGL1 devices that are not OD_USERIW
              //** are using only two decimal places for scaling.
              //** So we adjust the IW scale multiplier to only 2
              //** decimal places
              //*/
              //if (!(pPDevice->usHPGLType & HPGL2))
              //{
              //  while (lMultiplier > 100)
              //  {
              //     lMultiplier /= 10L;
              //     lScale /= 10L;
              //  } /* endwhile */
              //}

              /* Was round_divide  */
              //Origin[0].x = round_divide((Origin[0].x * lScale), lMultiplier);
              //Origin[0].y = round_divide((Origin[0].y * lScale), lMultiplier);
              //Origin[1].x = round_divide((Origin[1].x * lScale), lMultiplier);
              //Origin[1].y = round_divide((Origin[1].y * lScale), lMultiplier);
              Origin[0].x = ((Origin[0].x * lScale)/ lMultiplier);
              Origin[0].y = ((Origin[0].y * lScale)/ lMultiplier);
              Origin[1].x = ((Origin[1].x * lScale)/ lMultiplier);
              Origin[1].y = ((Origin[1].y * lScale)/ lMultiplier);

            }

            if (pPDevice->fsTransform & DXF_PORTRAIT)
              SWAPLONG(Origin[0].y, Origin[1].y);

            output_point(pDDC, &Origin[0]);
            output_comma(pDDC);
            output_point(pDDC, &Origin[1]);
          }
        }
      }
    }
  }
  pPDevice->ClipComplexity = ClipComplexity;

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_MemClipChange
 *
 * DESCRIPTION   = Set the clipping region  of the shadow memory DC.
 *                It copies the DC region  of the DC and
 *                set the same in the shadow memory DC.
 *                Hence both will be in sync.
 *
 * INPUT                = pddc  The DC handle
 *
 * OUTPUT            = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL prdl_MemClipChange( PDDC pddc, ULONG ulComFlags)
{
  HRGN  hrgnOld;
  RECTL rclBounds;
  ULONG ulRet = TRUE;

  /*
  ** Release the current clip region of the shadow memory.
  */
  if (InnerGreSelectClipRegion( pddc->hdcMemory, (HRGN) NULL, &hrgnOld,
                                ulComFlags | NGreSelectClipRegion) == RGN_ERROR)
      ulRet = FALSE;
  /*
  ** Copy the DC region
  */
  else if (InnerGreCopyClipRegion( pddc->hdc, pddc->hrgnMemory, &rclBounds,
                                   COPYCRGN_ALLINTERSECT,
                                   ulComFlags | NGreCopyClipRegion ) == RGN_ERROR)
      ulRet = FALSE;
  else if (InnerGreSelectClipRegion( pddc->hdcMemory, pddc->hrgnMemory,
                                     &hrgnOld,
                                     ulComFlags | NGreSelectClipRegion ) == RGN_ERROR)
    ulRet = FALSE;
    return( ulRet );
}

/***************************************************************************
 *
 * FUNCTION NAME = NotifyClipChange()
 *
 *
 * DESCRIPTION   =    Called whenever an application requests to change any of
 *                    the clipping bounds for the current ps/dc pair
 *
 *
 *
 * INPUT         = HDC hDC;
 *                 PRECTL pClipRect;
 *                 ULONG Complexity;
 *                 ULONG Flag;
 *                 PDDC pDDC;
 *                 ULONG FunN;
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = TRUE, FALSE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/
 /*
 ** Graphics Engine Functions
 */

BOOL NotifyClipChange(HDC hDC,PRECTL pClipRect,ULONG Complexity,
                      ULONG Flag,PDDC pDDC,ULONG FunN)
{
  PPDEVICE pPDevice;
  ULONG ulRet;

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

  /**************************************************************************
  **          In case of MEMORYDC Dispatch to Graphics Engine              **
  **                            Kran                                       **
  **************************************************************************/
  if (pDDC->usDCType == OD_MEMORY)
  {
    if (ulRet = prdl_MemClipChange (pDDC, FunN & 0xFFFF0000))
    {
      ulRet = (*daNotifyClipChange)( hDC, pClipRect, Complexity, Flag, pDDC, FunN );
    }
    LeaveDriver( pDDC );
    return( ulRet );
  }
  pPDevice = pDDC->pPDevice;
  pDDC->DCState.ClipComplexity = Complexity;

  /*
  ** if null rectl
  */
  if (pDDC->DCState.ClipComplexity == 0L)
  {

    /*
    **   you get nothing
    */
    pDDC->DCState.ClipRect.xLeft = 0L;
    pDDC->DCState.ClipRect.yBottom = 0L;
    pDDC->DCState.ClipRect.xRight = -1L;
    pDDC->DCState.ClipRect.yTop = -1L;
  }
  else
  {
    /*
    **    Store the bounds
    **
    **    if simple clip ( pDDC->DCState.ClipComplexity == 1L )
    **       Plotters only know how to clip very simple
    **       rectangles, using firmware.
    **
    **    if complex clip ( pDDC->DCState.ClipComplexity > 1L )
    **       store the bound, but we will let the engine handle
    **       the clipping
    */
    pDDC->DCState.ClipRect = pClipRect[0];
  }

  /*
  **                                                                  B730303
  */
  if (pPDevice->bDocStarted && !pPDevice->bBanding)
  {
    output_clip_rect(pDDC, pDDC->DCState.ClipComplexity,
                     &pDDC->DCState.ClipRect);
  }                                    /* endif                             */

  LeaveDriver(pDDC);
  return (BOOL)(*daNotifyClipChange)(hDC, pClipRect, Complexity,
                                     Flag, pDDC, FunN);
}

