/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = PRDLBOX.C
 *
 * DESCRIPTIVE NAME = Brief Description: Contains box drawing routines.
 *
 *
 * VERSION = V2.0
 *
 * DATE        04/08/88
 *
 * DESCRIPTION This module contains BoxBoundary, BoxInterior, BoxBoth and
 *             support routines.
 *
 * FUNCTIONS
 *
 *             prdl_BoxPath()      Create path corresponding to the engines box
 *             prdl_BoxBoundary()  Draws a boundary around a rounded corner box.
 *             prdl_BoxInterior    Fills the interior of a rounded corner box.
 *             prdl_BoxBoth()      Draws the boundary of a rounded corner box
 *             prdl_BoxCommon()    Draws the boundary of a rounded corner box
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_GPIPATHS
#include "inc\prdinclt.h"
#include "inc\prdlextf.h"
#include "inc\prdmath.h"
#include "inc\utl.h"
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#include <genplib.h>


/*
** percentage of x and y radii which define the locations of
** the two middle bezier control points which define the round
** corners of rounded boxes.  this magic percentage turns out
** to be .5523 or as a fixed point number, 0x8D64.
*/
#define  FIXED_BEZ_FRAC 0x8D64L

extern PDDC  EnterDriver(PDDC);
extern VOID  ExitDriver(PDDC);
LONG   prdl_BoxPath(PPOINTL,PDDC);
ULONG   prdl_BoxCommon(HDC,PPOINTL,LONG,PDDC,ULONG);

/***************************************************************************
 *
 * FUNCTION NAME = prdl_BoxPath()
 *
 * DESCRIPTION   = Create path corresponding to the engines box
 *                 primitives
 *
 *                 This routine creates a path corresponding to the
 *                 engines box primatives.  Rounded corner boxes are
 *                 implemented using Bezier curves and straight line
 *                 segments.  A geometric derivation of the Bezier
 *                 control points can be found in the PostScript notes.
 *
 * INPUT         = pptl = Pointer to an array of 2 points.
 *                        The first point specifies one corner of the
 *                        box rectangle (the other corner is at the
 *                        current pen location).  The second point
 *                        specifies the horizontal and vertical diameters
 *                        of the ellipse.
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

LONG prdl_BoxPath( PPOINTL pptl, PDDC pddc )
{
  LONG   lxRad;              /* The horizontal radius of the ellipse        */
  LONG   lyRad;              /* The vertical radius of the ellipse          */
  LONG   lxRadFrac;          /* fraction of radius to define bezier pt      */
  LONG   lyRadFrac;          /* fraction of radius to define bezier pt      */
  QUAD   qxRadFrac;
  RECTL  rcl;                /* Rectangle representing the box boundary     */
  POINTL aptl[4];            /* The four Bezier control points              */

  /*
  ** !!!CR THIS ROUTINE SHOULD ACCUMULATE BOUNDS
  **    let everyone know we have a path in progress.
  */
  pddc->pddcb->cgs.fPathExists = TRUE;

  /*
  ** the second point pointed to by pptl is really not a point
  ** at all.  it is the x and y radii of the corners of the box.
  ** if either of these is zero, then draw a simple rectangular
  ** box.
  */
  if (pptl[1].x == 0 || pptl[1].y == 0)
  {
    /*
    ** we will output a simple rectangular box
    */
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld box\n", pddc->pddcb->pen.ptlCur.x,
                  pddc->pddcb->pen.ptlCur.y, pptl->x, pptl->y );
  }
  else                       /* draw a complex box with rounded corners.    */
  {
    /*
    ** Get the horizontal coordinates of the box and sort them.
    */
    rcl.xRight = max( pptl->x, pddc->pddcb->pen.ptlCur.x );
    rcl.xLeft = min( pptl->x, pddc->pddcb->pen.ptlCur.x );

    /*
    ** Get the vertical coordinates of the box and sort them.
    */
    rcl.yTop = max( pptl->y, pddc->pddcb->pen.ptlCur.y );
    rcl.yBottom = min( pptl->y, pddc->pddcb->pen.ptlCur.y );

    /*
    ** Compute the horizontal radius of the ellipse and fractions
    ** of it.  A bounds check is done to make sure that the
    ** horizontal diameter does not exceed the width of the box.
    */
    lxRad = pptl[1].x;

    if (lxRad > rcl.xRight-rcl.xLeft)
    {
      lxRad = rcl.xRight-rcl.xLeft;
    }
    lxRad >>= 1;

    if (!LongMulFixed( lxRad, FIXED_BEZ_FRAC, &lxRadFrac) )
    {
      GplErrSetError( PMERR_COORDINATE_OVERFLOW );
      return( FAILURE );
    }

    /*
    ** Compute the vertical radius of the ellipse and fractions
    ** of it.  A bounds check is done to make sure that the
    ** vertical diameter of the ellipse does not exceed the
    ** height of the box.
    */
    lyRad = pptl[1].y;

    if (lyRad > rcl.yTop-rcl.yBottom)
    {
      lyRad = rcl.yTop-rcl.yBottom;
    }
    lyRad >>= 1;

    if (!LongMulFixed( lyRad, FIXED_BEZ_FRAC, &lyRadFrac) )
    {
      GplErrSetError( PMERR_COORDINATE_OVERFLOW );
      return( FAILURE );
    }

    /*
    ** -----------------------------------------------------
    ** we will calculate all of the control points at once
    ** to take advantage of simliar values.  the control
    ** points which define the rounded box are shown below.
    **
    **     2    3                              4    5
    **
    ** 1                                                6
    **
    ** 0                                                7
    **
    **
    **
    ** 15                                               8
    **
    ** 14                                               9
    **
    **     13   12                             11   10
    **
    **-----------------------------------------------------
    ** the following 4 points are simply used for their
    ** space to store repeat calculations.  their order
    ** has nothing to do with the order in which the values
    ** are used in the box.
    */
    aptl[0].y = rcl.yTop-lyRad;/* pts 0,7 have same y coord                 */
    aptl[1].y = aptl[0].y+lyRadFrac;/* pts 1,6 have same y coord            */
    aptl[2].y = rcl.yBottom+lyRad;/* pts 8,15 have same y coord             */
    aptl[3].y = aptl[2].y-lyRadFrac;/* pts 9,14 have same y coord           */
    aptl[0].x = rcl.xLeft+lxRad;/* pts 3,12 have same x coord               */
    aptl[1].x = aptl[0].x-lxRadFrac;/* pts 2,13 have same x coord           */
    aptl[2].x = rcl.xRight-lxRad;/* pts 4,11 have same x coord              */
    aptl[3].x = aptl[2].x+lxRadFrac;/* pts 5,10 have same x coord           */
    PrintChannel( pddc, (PSZ)"%ld %ld m\n", rcl.xLeft, aptl[0].y );
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld %ld %ld c\n", rcl.xLeft, aptl[1].
                  y, aptl[1].x, rcl.yTop, aptl[0].x, rcl.yTop );
    PrintChannel( pddc, (PSZ)"%ld %ld l\n", aptl[2].x, rcl.yTop );
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld %ld %ld c\n", aptl[3].x, rcl.yTop,
                  rcl.xRight, aptl[1].y, rcl.xRight, aptl[0].y );
    PrintChannel( pddc, (PSZ)"%ld %ld l\n", rcl.xRight, aptl[2].y );
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld %ld %ld c\n", rcl.xRight, aptl[3].y,
                  aptl[3].x, rcl.yBottom, aptl[2].x, rcl.yBottom );
    PrintChannel( pddc, (PSZ)"%ld %ld l\n", aptl[0].x, rcl.yBottom );
    PrintChannel( pddc, (PSZ)"%ld %ld %ld %ld %ld %ld c\n", aptl[1].x,
                  rcl.yBottom, rcl.xLeft, aptl[3].y, rcl.xLeft, aptl[2].y );
    PrintChannel( pddc, (PSZ)"cp\n" );

    /*
    ** indicate the current path contains curves.
    */
    pddc->pddcb->cgs.fCurvesInPath = TRUE;
  }
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_BoxBoundary()
 *
 * DESCRIPTION   = Draws a boundary around a rounded corner box.
 *
 * INPUT         = hdc, pptl, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS, ps_status(pddc)
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_BoxBoundary( HDC hdc, PPOINTL pptl, PDDC pddc, ULONG FunN )
{
  POINTL ptlCP;
  ULONG  tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_BoxBoundary(%08lx, %lp, %lp, %08lx\n", ((PB)&hdc)+sizeof(hdc));
  #endif                       /* DEBUG                                       */

  PrintLog( (PSZ)"ptlCur = {%ld, %ld}\n", pddc->pddcb->pen.ptlCur.x,
            pddc->pddcb->pen.ptlCur.y );
  PrintLog( (PSZ)"apt[0] = {%ld, %ld}\n", pptl[0].x, pptl[0].y );
  PrintLog( (PSZ)"apt[1] = {%ld, %ld}\n", pptl[1].x, pptl[1].y );

  /*
  ** check to see if we should do anything
  */
  if (!(FunN & COM_DRAW))
  {
    ExitDriver( pddc );
    return( SUCCESS );        /* do nothing if DRAW bit not set              */
  }
  pddc->pddcb->bounds.fAccumulate = (FunN) &COM_BOUND;

  /*
  ** If the box will be drawn immediately, then clear the old path.
  */
  if (!pddc->pddcb->path.fPathOrArea)
  {
    PrintChannel( pddc, (PSZ)"n\n" );
  }

  /*
  ** get the currentposition and save it.
  */
  if (!prdl_GetCurrentPosition( hdc, &ptlCP, pddc, FunN ))
  {
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** Add the box outline to the path.
  */
  if (!prdl_BoxPath(pptl, pddc))
  {
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** Stroke the box border immediatly if it isn't part of a larger
  ** path or area.
  */
  if (!pddc->pddcb->path.fPathOrArea)
  {
    ps_strokecurrentpath( pddc );
  }

  /*
  ** restore the current position.  We want to do a SetCurrentPosition
  ** here rather than an UpdateCurrentPosition, because this routine
  ** has to preserve the current position.  Therefore, we force the
  ** current position in the printer back to where it was.
  */
  if (!prdl_SetCurrentPosition( hdc, &ptlCP, pddc, FunN ))
  {
    ExitDriver( pddc );
    return( FAILURE );
  }
  tl = ps_status( pddc );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_BoxInterior
 *
 * DESCRIPTION   = Fills the interior of a rounded corner box.
 *
 * INPUT         = hdc, pptl, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = prdl_BoxCommon(hdc, pptl, BA_NOBOUNDARY, pddc, FunN)
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

ULONG prdl_BoxInterior( HDC hdc,PPOINTL pptl,PDDC pddc,ULONG FunN )
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_BoxInterior(%08lx, %lp, %lp, %08lx\n", ((PB)&hdc)+sizeof(hdc));
  #endif                       /* DEBUG                                       */

  PrintLog( (PSZ)"ptlCur = {%ld, %ld}\n", pddc->pddcb->pen.ptlCur.x,
            pddc->pddcb->pen.ptlCur.y );
  PrintLog( (PSZ)"apt[0] = {%ld, %ld}\n", pptl[0].x, pptl[0].y );
  PrintLog( (PSZ)"apt[1] = {%ld, %ld}\n", pptl[1].x, pptl[1].y );
  tl = prdl_BoxCommon( hdc, pptl, BA_NOBOUNDARY, pddc, FunN );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_BoxBoth()
 *
 * DESCRIPTION   = Draws the boundary of a rounded corner box and fills its
 *                 interior.
 *
 * INPUT         = hdc, pptl, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = prdl_BoxCommon(hdc, pptl, BA_BOUNDARY, pddc, FunN)
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

ULONG prdl_BoxBoth( HDC hdc, PPOINTL pptl, PDDC pddc, ULONG FunN )
{
  ULONG tl;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdl_BoxBoth(%08lx, %lp, %lp, %08lx\n", ((PB)&hdc)+sizeof(hdc) );
  #endif                       /* DEBUG                                       */

  PrintLog( (PSZ)"ptlCur = {%ld, %ld}\n", pddc->pddcb->pen.ptlCur.x,
            pddc->pddcb->pen.ptlCur.y );
  PrintLog( (PSZ)"apt[0] = {%ld, %ld}\n", pptl[0].x, pptl[0].y );
  PrintLog( (PSZ)"apt[1] = {%ld, %ld}\n", pptl[1].x, pptl[1].y );
  tl = prdl_BoxCommon( hdc, pptl, BA_BOUNDARY, pddc, FunN );
  ExitDriver( pddc );
  return( tl );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdl_BoxCommon()
 *
 * DESCRIPTION   = Draws the boundary of a rounded corner box and fills its
 *                 interior.
 *
 * INPUT         = hdc, pptl, lflag, pddc, FunN
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS,  ps_status(pddc)
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prdl_BoxCommon( HDC hdc,PPOINTL pptl,LONG lflag,PDDC pddc,
                      ULONG FunN )
{
  POINTL ptlCP;

  pddc->pddcb->bounds.fAccumulate = (FunN) &COM_BOUND;

  /*
  ** Its an error for a BoxBoth call to be part of a path or area.
  */
  if (pddc->pddcb->path.fPathOrArea)
  {
    GplErrSetError( PMERR_INV_NESTED_FIGURES );
    return( FAILURE );
  }

  /*
  ** check to see if we should do anything
  */
  if (!(FunN & COM_DRAW))
  {
    return( SUCCESS );        /* do nothing if DRAW bit not set              */
  }

  /*
  ** get the currentposition and save it.
  */
  if (!prdl_GetCurrentPosition(hdc, &ptlCP, pddc, FunN))
  {
    return( FAILURE );
  }

  /*
  ** begin area definition, with filling only
  */
  if (!prdl_BeginArea(hdc, lflag, pddc, FunN))
  {
    return( FAILURE );
  }

  /*
  ** Create a new path containing the box outline.
  */
  if (!prdl_BoxPath( pptl, pddc ))
  {
    return( FAILURE );
  }

  /*
  ** end the path definition and draw the interior of the box
  */
  if (!prdl_EndArea( hdc, (ULONG)EA_DRAW, pddc, FunN ))
  {
    return( FAILURE );
  }

  /*
  ** restore the current position.  We want to do a SetCurrentPosition
  ** here rather than an UpdateCurrentPosition, because this routine
  ** has to preserve the current position.  Therefore, we force the
  ** current position in the printer back to where it was.
  */
  if (!prdl_SetCurrentPosition( hdc, &ptlCP, pddc, FunN ))
  {
    return( FAILURE );
  }
  return( ps_status(pddc) );
}
