/*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.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "@(#)box.c      13.3 91/07/23";*/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = box.c
 *
 * DESCRIPTIVE NAME = PLOTTER DRIVER SOURCE
 *
 *
 * VERSION = V2.0
 *
 * DATE      09/14/89
 *
 * DESCRIPTION         File Contains functions associated with drawing boxes
 *
 *
 * FUNCTIONS   draw_box           Generate the plotter command to draw a box
 *             calc_bounds        Determine the bounding rectangle
 *             BoxBoth()          Draw the edges of a box and fill the interior
 *             BoxBoundary        Draw the boundary lines of a box.
 *             BoxInterior        Draw the interior of a box.
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

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

/*
**  Local function definitions
*/

LOCAL VOID calc_bounds( PRECTL, PPOINTL, INT );


/***************************************************************************
 *
 * FUNCTION NAME = draw_box
 *
 *
 * DESCRIPTION   = Generate the plotter command to draw a box
 *
 *                 Generate the plotter command to draw a box,
 *                 from the current posn to the End point given
 *                 to us.  Box may be filled or boundary.
 *
 *
 * INPUT         =   pDDC
 *                   pEnd      Far corner; near corner is current position
 *                   Color     The colour to use
 *                   bFill     TRUE for a filled box,  else box outline only
 *                   usPatSym  if filling use this pattern
 * OUTPUT        =
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LOCAL VOID draw_box( PDDC pDDC, PPOINTL pEnd, LONG lColor,
                                 BOOL bFill , USHORT usPatSym)

{
    PPDEVICE pPDevice = pDDC->pPDevice;


    /*
    ** Only output a box command if both dimensions are non-zero
    */

    if ( ((pDDC->DCState.LogPosition.x - pEnd->x) != 0) &&
         ((pDDC->DCState.LogPosition.y - pEnd->y) != 0))
    {
       move_pen( pDDC, &pDDC->DCState.LogPosition,FALSE );

       if( bFill )
       {
           /* begin @MJH2
           ** We are assuming that the box that is given to us is inclusive exclusive.
           ** That is, the bottom left point is included while the top right point
           ** is not.  There are differences in how plotters draw boxes.  Some
           ** will include the points given to them while others will exclude
           ** the points.  Therefore, we must make adjustments here.
           */
           if (PlotterClass[pPDevice->Plotter].fsOdd & OD_FILLRECT_INCLUSIVE)
           {
              /* INCLUSIVE INCLUSIVE
              ** Shrink the rectangle on all corners.
              */
              if (pDDC->DCState.LogPosition.x <= pEnd->x)
              {
                 pDDC->DCState.LogPosition.x++;
                 pEnd->x--;
              }
              else
              {
                 pDDC->DCState.LogPosition.x--;
                 pEnd->x++;
              }
              if (pDDC->DCState.LogPosition.y <= pEnd->y)
              {
                 pDDC->DCState.LogPosition.y++;
                 pEnd->y--;
              }
              else
              {
                 pDDC->DCState.LogPosition.y--;
                 pEnd->y++;
              }
           }
           // end @MJH2

           /*
           ** handle special case- setting area to white
           */
           if (usPatSym == PATSYM_NOSHADE || usPatSym == PATSYM_BLANK)
           {
             if (!(PlotterClass[pDDC->pPDevice->Plotter].usHPGLCaps & HPGL2))
             {
               /*
               ** Not an HPGL2 device
               ** Can't erase, area so just return.
               */
               return;
             }
             else
             {
               /*
               ** Force color to White
               */
               if (pDDC->DCState.usColorFormat != LCOLF_RGB)
                 lColor = CLR_WHITE;
               else
                 lColor = COLOR_WHITE;
             }
           }
           select_fill_pen( pDDC, lColor );
           /*
           ** Note: must select fill type after selecting fill pen
           */
           select_fill_type( pDDC, usPatSym, &pDDC->DCState.LogPosition, pEnd );
           set_line_type( pDDC, LINETYPE_SOLID );
       }
       else
       {
           select_line_pen( pDDC, lColor );
           set_line_type( pDDC, pDDC->DCState.lbnd.usType );
       }

       /*
       ** fill or edge the rectangle.
       ** Relative operations yield smaller data streams
       ** @MV
       */
       {
           POINTL DistPts;
           output_bytes( pDDC, (bFill ? "RR" : "ER" ));
           DistPts.x = pEnd->x - pPDevice->PhysPosition.x;
           DistPts.y = pEnd->y - pPDevice->PhysPosition.y;
           construct_point( pDDC, &DistPts, TRUE );
       }

       /*
       ** We must turn of bLineInConstruction because continuing with
       ** points after a box command will not work.
       */
       pDDC->pPDevice->bLineInConstruction = FALSE;

    }
    else
    {
       /*
       ** This "box" should be approximated to a line...
       ** Draw a line from the closest end of the box
       */
       if ( get_closest_point( &pDDC->pPDevice->PhysPosition,
            &pDDC->DCState.LogPosition, pEnd))
       {
          move_pen( pDDC, &pDDC->DCState.LogPosition, FALSE );
          plot_line( pDDC, pEnd );
       }
       else
       {
          move_pen( pDDC, pEnd, FALSE );
          plot_line( pDDC, &pDDC->DCState.LogPosition );
       } /* endif */
    } /* endif */

    return;
}

/***************************************************************************
 *
 * FUNCTION NAME = calc_bounds
 *
 *
 * DESCRIPTION   = Determine the bounding rectangle
 *                 for the array of POINTLs given.
 *
 *
 *
 * INPUT         = PRECTL    prBounds  Where the result goes
 *                 PPOINTL   ppIn      Input Point array
 *                 INT       iCount    Number of above
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LOCAL VOID calc_bounds( PRECTL prBounds, PPOINTL ppIn, INT iCount )
{
    /*
    **    Easy to do - scan the array for the smallest and largest x and y.
    */
    INT    i;

    prBounds->xLeft = prBounds->xRight = ppIn[ 0 ].x;
    prBounds->yBottom = prBounds->yTop = ppIn[ 0 ].y;

    for( i = 1; i < iCount; i++ )
    {
        if( ppIn[ i ].x < prBounds->xLeft )
            prBounds->xLeft = ppIn[ i ].x;

        if( ppIn[ i ].x > prBounds->xRight )
            prBounds->xRight = ppIn[ i ].x;

        if( ppIn[ i ].y < prBounds->yBottom )
            prBounds->yBottom = ppIn[ i ].y;

        if( ppIn[ i ].y > prBounds->yTop )
            prBounds->yTop = ppIn[ i ].y;
    }

    return;
}

/***************************************************************************
*
* FUNCTION NAME = BoxBoth()
*
*
* DESCRIPTION   =
*
*     Draw the edges of a box and fill the interior.  One corner is the
*     current position,  other corner is supplied.  May have rounded
*     corners.
*
*
* INPUT         = hDC
*                 pWrldPts   Far corner & corner rounding information
*                 pDDC
*                 FunN
*
* OUTPUT        = NONE
*
*
*
*
* RETURN-NORMAL = GPI_OK    - Function completed correctly
*
*
*
* RETURN-ERROR  = GPI_ERROR - Troubles
*
*
****************************************************************************/

LONG BoxBoth( HDC hDC, PPOINTL pWrldPts, PDDC pDDC, ULONG FunN )
{

    LONG      Result = GPI_OK;
    PPDEVICE  pPDevice = pDDC->pPDevice;

    if(!(EnterDriver(pDDC)))
      return(GPI_ERROR);
    if( !(FunN & COM_DRAW) ||

        (FunN & (COM_AREA | COM_PATH)) ||

        pDDC->DCState.ClipComplexity > 1L ||

        (pWrldPts[ 1 ].x && pWrldPts[ 1 ].y) ||

        ((FunN & COM_TRANSFORM) &&
          !(pDDC->DCState.usXfData & XF_SCALE_ONLY)) ||

        !(pPDevice->usHPGLType & HPGL_FILLRECT) ||

        !(pPDevice->usHPGLType & HPGL_EDGERECT) )
    {
        /*
        **      Call the engine when any of the following is true:
        **   we do not have to draw (COM_DRAW if off)
        **   AREA or PATH is active, or bounds are being accumulated
        **   Clip Complexity > 1 (plotter cannot handle clipping)
        **   Corners are rounded (plotter cannot do this)
        **   Correct pen is not available
        **   Transform is being applied,  and it is not simple scaling
        **   Plotter does not implement rectangle instruction
        */
        Result = (*daBoxBoth)( hDC, pWrldPts, pDDC, FunN );
    }
    else
    {
        POINTL DevPts;

        DevPts = *pWrldPts;

        if( FunN & COM_TRANSFORM )
            convert_world_to_device( hDC, &DevPts, 1L, pDDC );

        if( FunN & COM_BOUND )
        {
            RECTL Bounds;

            Bounds.xLeft = pDDC->DCState.LogPosition.x;
            Bounds.yBottom = pDDC->DCState.LogPosition.y;
            Bounds.xRight = DevPts.x;
            Bounds.yTop = DevPts.y;

            AccumulateBounds( hDC, &Bounds, pDDC, FunN );
        }

        if( FunN & COM_DRAW )
        {
            /*   Caller says we HAVE to do it!  */

            /*
            **    Make Three calls here - the reason is that the outline
            **  may be a different colour to the interior of the box and
            **  the interier of the box may have a background color
            */
            /*
            ** OK. But let us check here first if we have to fill the box with
            ** user defined pattern. If so then we call draw_box only twice.
            ** The reason is the background and the foreground of the interior
            ** is defined by the pattern bitmap. That is one call. Call again
            ** to draw the outline.      Kran
            */

            if (pDDC->DCState.abnd.usSet)  // user defined pattern
            {
               if(!setup_raster_fill_index(hDC,pDDC,NULL,NULL))
                  Result = GPI_ERROR;
               else if(pDDC->DCState.abnd.usMixMode == FM_LEAVEALONE)
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lBackColor, TRUE,
                            PATSYM_SOLID);
               else
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lColor, TRUE,
                            PATSYM_USERDEFINED);
            }
            else    // standard pattern
            {
              if( check_area_background_color(pDDC) )
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lBackColor, TRUE,
                            PATSYM_SOLID);

              if( check_area_color( pDDC ) )
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lColor, TRUE,
                            pDDC->DCState.abnd.usSymbol);
            }
            if( check_line_color( pDDC ) )
                draw_box( pDDC, &DevPts, pDDC->DCState.lbnd.lColor, FALSE,
                          pDDC->DCState.abnd.usSymbol);
        }
    }

    LeaveDriver(pDDC);
    return  Result;
}

/***************************************************************************
 *
 * FUNCTION NAME = BoxBoundary
 *
 *
 * DESCRIPTION   = Draw the boundary lines of a box.
 *
 *                 Box may have rounded corners
 *
 * INPUT         =  hDC
 *                  pWrldPts  Far corner & corner rounding information
 *                  pDDC
 *                  FunN
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = GPI_OK    - Box has been drawn
 *
 *
 * RETURN-ERROR  = GPI_ERROR - Nothing has happened - mostly from engine
 *
 *
 *
 **************************************************************************/

 LONG BoxBoundary (HDC hDC, PPOINTL pWrldPts,
                              PDDC pDDC, ULONG FunN)
 {

     LONG      Result = GPI_OK;
     PPDEVICE  pPDevice = pDDC->pPDevice;

     if(!(EnterDriver(pDDC)))
       return(GPI_ERROR);
     if( !(FunN & COM_DRAW) ||

         (FunN & (COM_AREA | COM_PATH)) ||

         pDDC->DCState.ClipComplexity > 1L ||

         (pWrldPts[ 1 ].x && pWrldPts[ 1 ].y) ||

         ((FunN & COM_TRANSFORM) &&
           !(pDDC->DCState.usXfData & XF_SCALE_ONLY)) ||

         !(pPDevice->usHPGLType & HPGL_EDGERECT) )
     {

         /*
         **      The above results in calling the engine when:
         **   COM_DRAW is off (we do not have to do it)
         **   AREA, PATH are active,
         **   Clip Complexity > 1 (meaning plotter clip is useless)
         **   There is a transform involving shear/rotation
         **   the box's corners are rounded
         **   the plotter is incapable of drawing rectabgles.
         */
         Result = (*daBoxBoundary)( hDC, pWrldPts, pDDC, FunN );

     }
     else
     {
         /*
         **   Simple case - go do it.
         */
         POINTL DevPts;


         DevPts = *pWrldPts;

         if( FunN & COM_TRANSFORM )
             convert_world_to_device( hDC, &DevPts, 1L, pDDC );

         if( FunN & COM_BOUND )
         {
             /*   Bounds are being accumulated too!  */
             RECTL rBounds;

             rBounds.xLeft = pDDC->DCState.LogPosition.x;
             rBounds.yBottom = pDDC->DCState.LogPosition.y;
             rBounds.xRight = DevPts.x;
             rBounds.yTop = DevPts.y;

             AccumulateBounds( hDC, &rBounds, pDDC, FunN );
         }

         if( check_line_color( pDDC ) && (FunN & COM_DRAW) )
             draw_box( pDDC, &DevPts, pDDC->DCState.lbnd.lColor, FALSE,
                       pDDC->DCState.abnd.usSymbol);
     }

     LeaveDriver(pDDC);
     return  Result;
 }


/***************************************************************************
 *
 * FUNCTION NAME = BoxInterior
 *
 *
 * DESCRIPTION   = Draw the interior of a box.
 *
 *                 Draw the interior of a box.  Drawn
 *                 from current position to position
 *                 supplied to us, and possibly with
 *                 rounded corners.
 *
 * INPUT         = (hDC,pWrldPts,pDDC,FunN)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = GPI_OK    - Box drawn correctly
 *
 *
 *
 * RETURN-ERROR  = GPI_ERROR - Box not drawn - only returned by engineNONE
 *
 *
 *
 **************************************************************************/

LONG BoxInterior (HDC     hDC,
                  PPOINTL pWrldPts,/* Far corner and corner rounding operations */
                  PDDC    pDDC,
                  ULONG   FunN)

{
    LONG      Result      = GPI_OK;
    PPDEVICE  pPDevice    = pDDC->pPDevice;
    BOOL      bCallEngine = FALSE;
    LONG      lRectVisRC  = RVIS_VISIBLE;

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

    if( !(FunN & COM_DRAW) ||
        (FunN & (COM_AREA | COM_PATH)) ||
        (pWrldPts[ 1 ].x && pWrldPts[ 1 ].y) ||
        !(pPDevice->usHPGLType & HPGL_FILLRECT) ||
        ((FunN & COM_TRANSFORM) &&
          !(pDDC->DCState.usXfData & XF_SCALE_ONLY)) )
    {
        /*
        **      Call the engine if any of the following is true:
        **  DRAW bit is off (we don't have to do it)
        **  AREA or PATH is active,  or BOUNDS are being accumulated
        **  Clip Complexity > 1 (plotter's clip is unable to handle it)
        **  Corners are rounded (plotter cannot do this)
        **  Required pen is not available
        **  A transform with rotation/shear is operating (plotter cannot do it)
        **  This plotter has no rectangle instruction.
        */
        bCallEngine = TRUE;
    }

    /*
    ** if clipping is complex check if all box is
    ** visible.
    ** -If the box is only partialy visible
    ** we must let the engine handle it.
    ** -If the box is completely  visible we
    ** will draw the box even if clipping is complex.
    ** -If the box is invisible we will accumulate the
    ** bounds but we will avoid drawing the box.
    */
    if (pDDC->DCState.ClipComplexity > 1L)
    {
        RECTL rectlBox;

        rectlBox.xLeft   = pDDC->DCState.WorldPosition.x;
        rectlBox.yBottom = pDDC->DCState.WorldPosition.y;
        rectlBox.xRight  = pWrldPts[0].x;
        rectlBox.yTop    = pWrldPts[0].y;

        lRectVisRC = GreRectVisible(hDC, &rectlBox);
        if ( lRectVisRC == RVIS_PARTIAL)
        {
            bCallEngine = TRUE;
        }
    }

    if (bCallEngine)
    {
        Result = (*daBoxInterior)( hDC, pWrldPts, pDDC, FunN );
    }
    else
    {
        /*
        **  This one we can handle - so on with it
        */
        POINTL DevPts;

        DevPts = *pWrldPts;

        if( FunN & COM_TRANSFORM )
            convert_world_to_device( hDC, &DevPts, 1L, pDDC );

        if( FunN & COM_BOUND )
        {
            RECTL Bounds;

            Bounds.xLeft = pDDC->DCState.LogPosition.x;
            Bounds.yBottom = pDDC->DCState.LogPosition.y;
            Bounds.xRight = DevPts.x;
            Bounds.yTop = DevPts.y;

            AccumulateBounds( hDC, &Bounds, pDDC, FunN );
        }

        if( (FunN & COM_DRAW) && (lRectVisRC != RVIS_INVISIBLE) )
        {

            /*   Caller says we HAVE to do it!  */
            if (pDDC->DCState.abnd.usSet)  // user defined pattern.  Kran
            {
               if(!setup_raster_fill_index(hDC,pDDC,NULL,NULL))
                  Result = GPI_ERROR;
               else
                 if(pDDC->DCState.abnd.usMixMode == FM_LEAVEALONE)
                    draw_box(pDDC,&DevPts, pDDC->DCState.abnd.lBackColor,TRUE,
                             PATSYM_SOLID);
                 else
                    draw_box(pDDC, &DevPts, pDDC->DCState.abnd.lColor, TRUE,
                             PATSYM_USERDEFINED);
            }
            else
            {
              if( check_area_background_color(pDDC) )
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lBackColor, TRUE,
                            PATSYM_SOLID);

              if( check_area_color( pDDC ) )
                  draw_box( pDDC, &DevPts, pDDC->DCState.abnd.lColor, TRUE,
                            pDDC->DCState.abnd.usSymbol);
            }

        }
    }

    LeaveDriver(pDDC);
    return  Result;
}
