

/****************************************************************************
                       Unit Cache; Implementation
*****************************************************************************

   Module Prefix: Ca

****************************************************************************/

#include "headers.c"
#pragma hdrstop

#include  "cache.h"        /* own interface */

/*********************** Exported Data **************************************/


/*********************** Private Data ***************************************/

/*--- Gdi cache ---*/

typedef struct
{
   HPEN            handle;
   LOGPEN          logPen;
   Boolean         stockObject;

} Pen, far * PenLPtr;


typedef struct
{
   HBRUSH          handle;
   LOGBRUSH        logBrush;
   Boolean         stockObject;

} Brush, far * BrushLPtr;


typedef struct
{
   Handle          metafile;           // metafile handle

   Rect            prevClipRect;       // last cliprect before SaveDC()
   Rect            curClipRect;        // last clipping rectangle
   Boolean         forceNewClipRect;   // always emit new clipping rectangle?

   HPEN            nulPen;             // frequently used pens
   HPEN            whitePen;
   HPEN            blackPen;

   HBRUSH          nulBrush;           // frequently used brushes
   HBRUSH          whiteBrush;
   HBRUSH          blackBrush;

   Boolean         stockFont;          // current font selection
   HFONT           curFont;
   LOGFONT         curLogFont;

   Brush           curBrush;           // current pen and brush selections
   Pen             curPen;
   Pen             nextPen;            // cached frame(pen)

   CaPrimitive     nextPrim;           // cached primitive
   Boolean         samePrim;
   Handle          polyHandle;
   Integer         numPoints;
   Integer         maxPoints;
   Point far *     pointList;

   Word            iniROP2;            // initial value for ROP2 mode
   Word            iniTextAlign;       // initial value for text alignment
   Word            iniBkMode;
   RGBColor        iniTxColor;
   RGBColor        iniBkColor;

   Word            curROP2;            // current ROP codes setting
   Word            curBkMode;          // current background mode
   RGBColor        curBkColor;         // current background color
   Word            curStretchMode;     // current stretchblt mode

   RGBColor        curTextColor;       // last text color
   Word            curTextAlign;       // last text alignment value
   short           curCharExtra;       // last char extra value
   Fixed           spExtra;            // last space extra value
   Point           txNumer;            // last text scaling
   Point           txDenom;            // factors

   Boolean         restorePen;         // do any attribs need to be re-issued
   Boolean         restoreBrush;       // after RestoreDC() call?
   Boolean         restoreFont;
   Boolean         restoreCharExtra;
   Boolean         restoreStretchMode;

} GdiCache;

private  GdiCache  gdiCache;

/*********************** Private Function Definitions ***********************/

#define /* void */ NewPolygon( /* void */ )                          \
/* start a new polygon definition */                                 \
gdiCache.numPoints = 0


private void AddPolyPt( Point pt );
/* Add a point to the polygon buffer */


private void SelectCachedPen( void );
/* select the currently cached Pen into the metafile */


/*********************** Function Implementation ****************************/


void CaInit( Handle metafile )
/*=========*/
/* initialize the gdi cache module */
{
   /* save off the metafile handle into global structure */
   gdiCache.metafile = metafile;

   /* make sure that text and background colors will be set */
   gdiCache.curTextColor =
   gdiCache.curBkColor = RGB( 12, 34, 56 );

   /* get handles to some stock pen objects */
   gdiCache.nulPen = GetStockObject( NULL_PEN );
   gdiCache.whitePen = CreatePen( PS_INSIDEFRAME, 1, RGB( 255, 255, 255 ) );
   gdiCache.blackPen = CreatePen( PS_INSIDEFRAME, 1, RGB( 0, 0, 0 ) );

   /* get handles to some stock brush objects */
   gdiCache.nulBrush = GetStockObject( NULL_BRUSH );
   gdiCache.whiteBrush = GetStockObject( WHITE_BRUSH );
   gdiCache.blackBrush = GetStockObject( BLACK_BRUSH );

   /* allocate space for the polygon buffer */
   gdiCache.numPoints = 0;
   gdiCache.maxPoints = 16;
   gdiCache.polyHandle = GlobalAlloc( GHND, gdiCache.maxPoints * sizeof( Point ) );
   if (gdiCache.polyHandle == NULL)
   {
      ErSetGlobalError( ErMemoryFull);
   }
   else
   {
      /* get a pointer address for the memory block */
      gdiCache.pointList = (Point far *)GlobalLock( gdiCache.polyHandle );
   }

   /* mark the primitive cache as empty */
   gdiCache.nextPrim.type = CaEmpty;

   /* the current primitive isn't being repeated */
   gdiCache.samePrim = FALSE;

   /* turn off forcing of a new clipping rectangle */
   gdiCache.forceNewClipRect = FALSE;

}  /* CaInit */



void CaFini( void )
/*=========*/
/* close down the cache module */
{
   /* delete the current font selection if non-NULL and non-stock */
   if ((gdiCache.curFont != NULL) && !gdiCache.stockFont)
   {
      /* free the font object */
      DeleteObject( gdiCache.curFont );
   }

   /* remove the current brush selection if non-NULL and not a stock brush */
   if ((gdiCache.curBrush.handle != NULL) && !gdiCache.curBrush.stockObject)
   {
      /* see if the current brush has a DIB - if so, delete it */
      if (gdiCache.curBrush.logBrush.lbStyle == BS_DIBPATTERN)
      {
         /* free the DIB memory used for brush */
         GlobalFree( (HANDLE) gdiCache.curBrush.logBrush.lbHatch );
      }

      /* delte the brush object */
      DeleteObject( gdiCache.curBrush.handle );
   }

   /* remove the current pen selection if non-NULL and not a stock pen */
   if ((gdiCache.curPen.handle != NULL) && !gdiCache.curPen.stockObject)
   {
      DeleteObject( gdiCache.curPen.handle );
   }

   /* Remove other pens created at initialization time */
   DeleteObject( gdiCache.whitePen );
   DeleteObject( gdiCache.blackPen );

   /* deallocate the polygon buffer */
   GlobalUnlock( gdiCache.polyHandle );
   GlobalFree( gdiCache.polyHandle );

}  /* CaFini */



void CaSetMetafileDefaults( void )
/*========================*/
/* Set up any defaults that will be used throughout the metafile context */
{
   /* set up some metafile defaults */
   gdiCache.iniTextAlign = TA_LEFT | TA_BASELINE | TA_NOUPDATECP;
   gdiCache.iniROP2 = R2_COPYPEN;
   gdiCache.iniBkMode = TRANSPARENT;
   gdiCache.iniTxColor = RGB( 0, 0, 0 );
   gdiCache.iniBkColor = RGB( 255, 255, 255 );

   /* Put the records into the metafile */
   CaSetROP2( gdiCache.iniROP2 );
   CaSetTextAlign( gdiCache.iniTextAlign );
   CaSetBkMode( gdiCache.iniBkMode );
   CaSetTextColor( gdiCache.iniTxColor );
   CaSetBkColor( gdiCache.iniBkColor );

}  /* CaSetMetafileDefaults */



void CaSamePrimitive( Boolean same )
/*==================*/
/* indicate whether next primitive is the same or new */
{
   gdiCache.samePrim = same;

}  /* CaSamePrimitive */



void CaMergePen( Word verb )
/*=============*/
/* indicate that next pen should be merged with previous logical pen */
{
   if (gdiCache.nextPen.handle != NULL)
   {
      /* check to see if this is a NULL pen - the merge can happen */
      if (gdiCache.samePrim && verb == GdiFrame &&
          gdiCache.nextPen.handle == gdiCache.nulPen)
      {
         /* remove the cached pen - don't delte the pen object */
         gdiCache.nextPen.handle = NULL;
      }
      else
     {
         /* if not removing a null pen, then flush the cache.  This will most
            often result in a line segment being flushed. */
         CaFlushCache();
      }
   }

}  /* CaMergePen */



Word CaGetCachedPrimitive( void )
/*=======================*/
/* return the current cached primitive type */
{
   return gdiCache.nextPrim.type;

}  /* CaGetCachedPrimitive */



void CaCachePrimitive( CaPrimitiveLPtr primLPtr )
/*===================*/
/* Cache the primitive passed down.  This includes the current pen and brush. */
{
   /* not another line segment and/or not continuous - flush cache */
   CaFlushCache();

   /* save off the new primitive */
   gdiCache.nextPrim = *primLPtr;

   /* check if we need to copy over the polygon list, also */
   if ((gdiCache.nextPrim.type == CaPolygon) ||
       (gdiCache.nextPrim.type == CaPolyLine))
   {
      /* create new polygon */
      NewPolygon();

      /* add the polygon to the polygon buffer */
      while (gdiCache.nextPrim.a.poly.numPoints--)
      {
         AddPolyPt( *gdiCache.nextPrim.a.poly.pointList++);
      }
   }

}  /* CaCachePrimitive */



void CaFlushCache( void )
/*===============*/
/* Flush the current primitive stored in the cache */
{
   /* if the cache is empty, then just return - nothing to do */
   if (gdiCache.nextPrim.type == CaEmpty)
   {
      return;
   }

   /* select all cached attributes */
   CaFlushAttributes();

   /* emit any cached primitive, if necessary */
   switch (gdiCache.nextPrim.type)
   {
      case CaLine:
      {
         Rect        clip;
         Point       delta;
         Point       offset;

         /* determine the length in both directions */
         delta.x = gdiCache.nextPrim.a.line.end.x - gdiCache.nextPrim.a.line.start.x;
         delta.y = gdiCache.nextPrim.a.line.end.y - gdiCache.nextPrim.a.line.start.y;

         /* set clipRect extents based upon current point position */
         clip.left   = min( gdiCache.nextPrim.a.line.start.x, gdiCache.nextPrim.a.line.end.x );
         clip.top    = min( gdiCache.nextPrim.a.line.start.y, gdiCache.nextPrim.a.line.end.y );
         clip.right  = max( gdiCache.nextPrim.a.line.start.x, gdiCache.nextPrim.a.line.end.x );
         clip.bottom = max( gdiCache.nextPrim.a.line.start.y, gdiCache.nextPrim.a.line.end.y );

         /* extend clip rectangle for down-right pen stylus hang */
         clip.right  += gdiCache.nextPrim.a.line.pnSize.x;
         clip.bottom += gdiCache.nextPrim.a.line.pnSize.y;

         /* determine the new starting and ending points */
         gdiCache.nextPrim.a.line.start.x -= delta.x;
         gdiCache.nextPrim.a.line.start.y -= delta.y;
         gdiCache.nextPrim.a.line.end.x   += delta.x;
         gdiCache.nextPrim.a.line.end.y   += delta.y;

         /* ajust the clipping rect for vertical line penSize roundoff? */
         if (delta.x == 0)
         {
            /* vertical line - expand clip in x dimension */
            clip.left--;
         }
         /* are we are adjusting pen by 1/2 metafile unit - roundoff error? */
         else if (gdiCache.nextPrim.a.line.pnSize.x & 0x01)
         {
            /* adjust clipping rectangle to clip the rounding error */
            clip.right--;
         }

         /* ajust the clipping rect for horizontal line penSize roundoff? */
         if (delta.y == 0)
         {
            /* horizontal line - extend clip in y dimension */
            clip.top--;
         }
         /* are we are adjusting pen by 1/2 metafile unit - roundoff error? */
         else if (gdiCache.nextPrim.a.line.pnSize.y & 0x01)
         {
            /* adjust clipping rectangle to clip the rounding error */
            clip.bottom--;
         }

         /* cut the size of the pen dimensions in half for offsets */
         offset.x = gdiCache.nextPrim.a.line.pnSize.x / 2;
         offset.y = gdiCache.nextPrim.a.line.pnSize.y / 2;

         /* set the new clipping rectangle */
         SaveDC( gdiCache.metafile );
         IntersectClipRect( gdiCache.metafile,
                            clip.left,  clip.top, clip.right, clip.bottom );

         /* move to the first point and draw to second (with padding) */

// MoveTo is replaced by MoveToEx in win32
#ifdef WIN32
         MoveToEx( gdiCache.metafile,
                   gdiCache.nextPrim.a.line.start.x + offset.x,
                   gdiCache.nextPrim.a.line.start.y + offset.y, NULL );
#else
         MoveTo( gdiCache.metafile,
                 gdiCache.nextPrim.a.line.start.x + offset.x,
                 gdiCache.nextPrim.a.line.start.y + offset.y );
#endif

         LineTo( gdiCache.metafile,
                 gdiCache.nextPrim.a.line.end.x + offset.x,
                 gdiCache.nextPrim.a.line.end.y + offset.y );

         /* restore the previous clipping rectangle */
         RestoreDC( gdiCache.metafile, -1 );
         break;
      }

      case CaRectangle:
      {
         if (gdiCache.curPen.handle == gdiCache.nulPen)
         {
            Point    poly[5];

            /* set up the bounding coodinates */
            poly[0].x = poly[3].x = gdiCache.nextPrim.a.rect.bbox.left;
            poly[0].y = poly[1].y = gdiCache.nextPrim.a.rect.bbox.top;
            poly[1].x = poly[2].x = gdiCache.nextPrim.a.rect.bbox.right;
            poly[2].y = poly[3].y = gdiCache.nextPrim.a.rect.bbox.bottom;
            poly[4]   = poly[0];

            /* perform call to render rectangle */
            Polygon( gdiCache.metafile, poly, 5 );
         }
         else
         {
            Rectangle( gdiCache.metafile,
                       gdiCache.nextPrim.a.rect.bbox.left,  gdiCache.nextPrim.a.rect.bbox.top,
                       gdiCache.nextPrim.a.rect.bbox.right, gdiCache.nextPrim.a.rect.bbox.bottom );
         }
         break;
      }

      case CaRoundRect:
      {
         RoundRect( gdiCache.metafile,
                    gdiCache.nextPrim.a.rect.bbox.left,  gdiCache.nextPrim.a.rect.bbox.top,
                    gdiCache.nextPrim.a.rect.bbox.right, gdiCache.nextPrim.a.rect.bbox.bottom,
                    gdiCache.nextPrim.a.rect.oval.x,     gdiCache.nextPrim.a.rect.oval.y );
         break;
      }

      case CaEllipse:
      {
         Ellipse( gdiCache.metafile,
                  gdiCache.nextPrim.a.rect.bbox.left,  gdiCache.nextPrim.a.rect.bbox.top,
                  gdiCache.nextPrim.a.rect.bbox.right, gdiCache.nextPrim.a.rect.bbox.bottom );
         break;
      }

      case CaArc:
      {
         Arc( gdiCache.metafile,
              gdiCache.nextPrim.a.arc.bbox.left,  gdiCache.nextPrim.a.arc.bbox.top,
              gdiCache.nextPrim.a.arc.bbox.right, gdiCache.nextPrim.a.arc.bbox.bottom,
              gdiCache.nextPrim.a.arc.start.x,    gdiCache.nextPrim.a.arc.start.y,
              gdiCache.nextPrim.a.arc.end.x,      gdiCache.nextPrim.a.arc.end.y );
         break;
      }

      case CaPie:
      {
         Pie( gdiCache.metafile,
              gdiCache.nextPrim.a.arc.bbox.left,  gdiCache.nextPrim.a.arc.bbox.top,
              gdiCache.nextPrim.a.arc.bbox.right, gdiCache.nextPrim.a.arc.bbox.bottom,
              gdiCache.nextPrim.a.arc.start.x,    gdiCache.nextPrim.a.arc.start.y,
              gdiCache.nextPrim.a.arc.end.x,      gdiCache.nextPrim.a.arc.end.y );
         break;
      }

      case CaPolygon:
      case CaPolyLine:
      {
         Point       offset;
         Integer     i;

         /* see if centering of the pen is required  */
         if (gdiCache.curPen.handle == gdiCache.nulPen)
         {
            /* no - just filling the object without frame */
            offset.x = offset.y = 0;
         }
         else
         {
            /* transform all points to correct for down-right pen
               rendering in QuickDraw and make for a GDI centered pen */
            offset.x = gdiCache.nextPrim.a.poly.pnSize.x / 2;
            offset.y = gdiCache.nextPrim.a.poly.pnSize.y / 2;
         }

         /* transform end point for all points in the polygon */
         for (i = 0; i < gdiCache.numPoints; i++)
         {
            /* increment each coordinate pair off half of the pen size */
            gdiCache.pointList[i].x += offset.x;
            gdiCache.pointList[i].y += offset.y;
         }

         /* call the appropriate GDI routine based upon the type */
         if (gdiCache.nextPrim.type == CaPolygon)
         {
            Polygon( gdiCache.metafile,
                     gdiCache.pointList,
                     gdiCache.numPoints );
         }
         else
         {
            Polyline( gdiCache.metafile,
                      gdiCache.pointList,
                      gdiCache.numPoints );
         }
         break;
      }
   }

   /* mark the primitive cache as empty */
   gdiCache.nextPrim.type = CaEmpty;

}  /* CaFlushCache */



void CaFlushAttributes( void )
/*====================*/
/* flush any pending attribute elements */
{
   /* select the cached pen - the routine will determine if one exits */
   SelectCachedPen();

}  /* CaFlushAttributes */



void CaCreatePenIndirect( LOGPEN far * newLogPen )
/*======================*/
/* create a new pen */
{
   PenLPtr     compare;
   Boolean     different;

   /* determine which pen to compare against */
   compare = (gdiCache.nextPen.handle != NULL) ? &gdiCache.nextPen :
                                                 &gdiCache.curPen;

   /* compare the two pens */
   different = ((newLogPen->lopnStyle   != compare->logPen.lopnStyle) ||
                (newLogPen->lopnColor   != compare->logPen.lopnColor) ||
                (newLogPen->lopnWidth.x != compare->logPen.lopnWidth.x));

   /* if the pens are different ... */
   if (different)
   {
      /* if there is a cached pen ... */
      if (gdiCache.nextPen.handle != NULL)
      {
         /* flush the cached primitive - there is a change of pens */
         CaFlushCache();

         /* check to see if the new pen is changed by next selection */
         different = ((newLogPen->lopnStyle   != gdiCache.curPen.logPen.lopnStyle) ||
                      (newLogPen->lopnColor   != gdiCache.curPen.logPen.lopnColor) ||
                      (newLogPen->lopnWidth.x != gdiCache.curPen.logPen.lopnWidth.x));
      }
   }

   /* if the pen has changed from the current setting, cache the next pen */
   if (different || gdiCache.curPen.handle == NULL)
   {
      /* if there is a pending line or polyline, the flush the cache */
      if (gdiCache.nextPrim.type == CaLine || gdiCache.nextPrim.type == CaPolyLine)
      {
         CaFlushCache();
      }

      /* assign the new pen attributes */
      gdiCache.nextPen.logPen = *newLogPen;

      /* currently not using a stock pen object */
      gdiCache.nextPen.stockObject = FALSE;

      /* check for any pre-defined pen objects */
      if (gdiCache.nextPen.logPen.lopnStyle == PS_NULL)
      {
         /* and use them if possible */
         gdiCache.nextPen.handle = gdiCache.nulPen;
         gdiCache.nextPen.stockObject = TRUE;
      }
      else if (gdiCache.nextPen.logPen.lopnWidth.x == 1)
      {
         if (newLogPen->lopnColor == RGB( 0, 0, 0 ))
         {
            gdiCache.nextPen.handle = gdiCache.blackPen;
            gdiCache.nextPen.stockObject = TRUE;
         }
         else if (gdiCache.nextPen.logPen.lopnColor == RGB( 255, 255, 255 ))
         {
            gdiCache.nextPen.handle = gdiCache.whitePen;
            gdiCache.nextPen.stockObject = TRUE;
         }
      }

      if (!gdiCache.nextPen.stockObject)
      {
         /* otherwise, create a new pen */
         gdiCache.nextPen.handle = CreatePenIndirect( &gdiCache.nextPen.logPen );
      }
   }
   else
   {
      /* copy the current setting back into the next pen setting */
      gdiCache.nextPen = gdiCache.curPen;
   }

   /* check if cache was invalidated */
   if (gdiCache.restorePen && (gdiCache.curPen.handle != NULL))
   {
      /* if pen was invalidated by RestoreDC(), reselect it */
      SelectObject( gdiCache.metafile, gdiCache.curPen.handle );
   }

   /* all is ok with cache now */
   gdiCache.restorePen = FALSE;

}  /* CaCreatePenIndirect */



void CaCreateBrushIndirect( LOGBRUSH far * newLogBrush )
/*========================*/
/* Create a new logical brush using structure passed in */
{
   /* assume that the DIB patterns are different */
   Boolean  differentDIB = TRUE;

   /* check if we are comparing two DIB patterned brushes */
   if ((newLogBrush->lbStyle == BS_DIBPATTERN) &&
       (gdiCache.curBrush.logBrush.lbStyle == BS_DIBPATTERN))
   {
      Word  nextSize = (Word)GlobalSize( (HANDLE) newLogBrush->lbHatch ) / 2;
      Word  currSize = (Word)GlobalSize( (HANDLE) gdiCache.curBrush.logBrush.lbHatch ) / 2;

      /* make sure that the sizes are the same */
      if (nextSize == currSize)
      {
         Word far *  nextDIBPattern = (Word far *)GlobalLock( (HANDLE) newLogBrush->lbHatch );
         Word far *  currDIBPattern = (Word far *)GlobalLock( (HANDLE) gdiCache.curBrush.logBrush.lbHatch );

         /* assume that the DIBs are the same so far */
         differentDIB = FALSE;

         /* compare all the bytes in the two brush patterns */
         while (currSize--)
         {
            /* are they the same ? */
            if (*nextDIBPattern++ != *currDIBPattern++)
            {
               /* if not, flag the difference and break from the loop */
               differentDIB = TRUE;
               break;
            }
         }

         /* Unlock the data blocks */
         GlobalUnlock( (HANDLE) newLogBrush->lbHatch );
         GlobalUnlock( (HANDLE) gdiCache.curBrush.logBrush.lbHatch );

         /* see if these did compare exactly */
         if (!differentDIB)
         {
            /* if so, free the new DIB brush - it's no longer needed */
            GlobalFree( (HANDLE) newLogBrush->lbHatch );
         }
      }
   }

   /* see if we are requesting a new brush */
   if (differentDIB &&
      (newLogBrush->lbStyle != gdiCache.curBrush.logBrush.lbStyle ||
       newLogBrush->lbColor != gdiCache.curBrush.logBrush.lbColor ||
       newLogBrush->lbHatch != gdiCache.curBrush.logBrush.lbHatch ||
       gdiCache.curBrush.handle == NULL))
   {
      HBRUSH   brushHandle;
      Boolean  stockBrush;

      /* flush the primitive cache if changing brush selection */
      CaFlushCache();

      /* if current brush has a DIB, make sure to free memory */
      if (gdiCache.curBrush.logBrush.lbStyle == BS_DIBPATTERN)
      {
         /* free the memory */
         GlobalFree( (HANDLE) gdiCache.curBrush.logBrush.lbHatch );
      }

      /* copy over the new structure */
      gdiCache.curBrush.logBrush = *newLogBrush;

      /* We currently aren't using a stock brush */
      stockBrush = FALSE;

      /* use stock objects if possible */
      if (gdiCache.curBrush.logBrush.lbStyle == BS_HOLLOW)
      {
         /* use null (hollow) brush */
         brushHandle = gdiCache.nulBrush;
         stockBrush = TRUE;
      }
      /* check for some standard solid colored brushes */
      else if (gdiCache.curBrush.logBrush.lbStyle == BS_SOLID)
      {
         if (gdiCache.curBrush.logBrush.lbColor == RGB( 0, 0, 0) )
         {
            /* use solid black brush */
            brushHandle = gdiCache.blackBrush;
            stockBrush = TRUE;
         }
         else if (gdiCache.curBrush.logBrush.lbColor == RGB( 255, 255, 255 ))
         {
            /* use solid white brush */
            brushHandle = gdiCache.whiteBrush;
            stockBrush = TRUE;
         }
      }

      /* if unable to find a stock brush, then create a new one */
      if (!stockBrush)
      {
         /* otherwise, create new brush using logbrush structure */
         brushHandle = CreateBrushIndirect( &gdiCache.curBrush.logBrush );
      }

      /* select the new brush */
      SelectObject( gdiCache.metafile, brushHandle );

      /* if this isn't the first brush selection and not a stock brush */
      if (gdiCache.curBrush.handle != NULL && !gdiCache.curBrush.stockObject)
      {
         /* delete the previous brush object */
         DeleteObject( gdiCache.curBrush.handle );
      }

      /* save brush handle in current cache variable */
      gdiCache.curBrush.handle = brushHandle;
      gdiCache.curBrush.stockObject = stockBrush;
   }
   else if (gdiCache.restoreBrush)
   {
      /* if brush was invalidated by RestoreDC(), reselect it */
      SelectObject( gdiCache.metafile, gdiCache.curBrush.handle );
   }

   /* all is ok with cache now */
   gdiCache.restoreBrush = FALSE;

}  /* CaCreateBrushIndirect */



void CaCreateFontIndirect( LOGFONT far * newLogFont )
/*=======================*/
/* create the logical font passed as paramter */
{
   /* make sure we are requesting a new font */
   if (newLogFont->lfHeight != gdiCache.curLogFont.lfHeight ||
       newLogFont->lfWeight != gdiCache.curLogFont.lfWeight ||
       newLogFont->lfEscapement  != gdiCache.curLogFont.lfEscapement ||
       newLogFont->lfOrientation != gdiCache.curLogFont.lfOrientation ||
       newLogFont->lfItalic != gdiCache.curLogFont.lfItalic ||
       newLogFont->lfUnderline != gdiCache.curLogFont.lfUnderline ||
       newLogFont->lfPitchAndFamily != gdiCache.curLogFont.lfPitchAndFamily ||
       lstrcmp( newLogFont->lfFaceName, gdiCache.curLogFont.lfFaceName ) != 0 ||
       gdiCache.curFont == NULL)
   {
      HFONT       fontHandle;
      Boolean     stockFont;

      /* flush the primitive cache if changing font attributes */
      CaFlushCache();

      /* assign the new pen attributes */
      gdiCache.curLogFont = *newLogFont;

      /* currently not using a stock font object */
      stockFont = FALSE;

      /* check for any pre-defined pen objects */
      if (newLogFont->lfFaceName == NULL)
      {
         fontHandle = GetStockObject( SYSTEM_FONT );
         stockFont = TRUE;
      }
      else
      {
         /* otherwise, create a new pen */
         fontHandle = CreateFontIndirect( &gdiCache.curLogFont );
      }

      /* select the new font */
      SelectObject( gdiCache.metafile, fontHandle );

      /* if this isn't the first font selection and not a stock font */
      if (gdiCache.curFont != NULL && !gdiCache.stockFont)
      {
         /* delete the previous font object */
         DeleteObject( gdiCache.curFont );
      }

      /* save font handle in current cache variable */
      gdiCache.curFont = fontHandle;
      gdiCache.stockFont = stockFont;
   }
   else if (gdiCache.restoreFont)
   {
      /* if pen was invalidated by RestoreDC(), reselect it */
      SelectObject( gdiCache.metafile, gdiCache.curFont );
   }

   /* all is ok with cache now */
   gdiCache.restoreFont = FALSE;

}  /* CaCreateFontIndirect */



void CaSetBkMode( Word mode )
/*==============*/
/* set the backgound transfer mode */
{
   if (gdiCache.curBkMode != mode)
   {
      /* flush the primitive cache if changing mode */
      CaFlushCache();

      /* set the background mode and save in global cache */
      SetBkMode( gdiCache.metafile, mode );
      gdiCache.curBkMode = mode;
   }

   /* no need to worry about restoring BkMode, since this is set
      before the initial SaveDC() is issued and is restored after each
      RestoreDC() call back to the metafile default. */

}  /* CaSetBkMode */



void CaSetROP2( Word ROP2Code )
/*============*/
/* set the transfer ROP mode according to ROP2Code */
{
   /* check for change in ROP code */
   if (gdiCache.curROP2 != ROP2Code)
   {
      /* flush the primitive cache if changing ROP mode */
      CaFlushCache();

      /* set the ROP code and save in global cache variable */
      SetROP2( gdiCache.metafile, ROP2Code );
      gdiCache.curROP2 = ROP2Code;
   }

   /* no need to worry about restoring ROP code, since this is set
      before the initial SaveDC() is issued and is restored after each
      RestoreDC() call back to the metafile default. */

}  /* CaSetROP2 */



void CaSetStretchBltMode( Word mode )
/*======================*/
/* stretch blt mode - how to preserve scanlines using StretchDIBits() */
{
   if (gdiCache.curStretchMode != mode)
   {
      /* flush the primitive cache if changing mode */
      CaFlushCache();

      /* set the stretch blt mode and save in global cache variable */
      SetStretchBltMode( gdiCache.metafile, mode );
      gdiCache.curStretchMode = mode;
   }
   else if (gdiCache.restoreStretchMode)
   {
      /* if stretch blt mode was invalidated by RestoreDC(), re-issue */
      SetStretchBltMode( gdiCache.metafile, gdiCache.curStretchMode );
   }

   /* all is ok with cache now */
   gdiCache.restoreStretchMode = FALSE;

}  /* CaSetStretchBltMode */



void CaSetTextAlign( Word txtAlign )
/*=================*/
/* set text alignment according to parameter */
{
   if (gdiCache.curTextAlign != txtAlign)
   {
      /* flush the primitive cache if changing text alignment */
      CaFlushCache();

      /* Set the text color and save in cache */
      SetTextAlign( gdiCache.metafile, txtAlign );
      gdiCache.curTextAlign = txtAlign;
   }

   /* no need to worry about restoring text align, since this is set
      before the initial SaveDC() is issued and is restored after each
      RestoreDC() call back to the metafile default. */

}  /* CaSetTextAlign */


void CaSetTextColor( RGBColor txtColor )
/*=================*/
/* set the text color if different from current setting */
{
   if (gdiCache.curTextColor != txtColor)
   {
      /* flush the primitive cache if changing text color */
      CaFlushCache();

      /* Set the text color and save in cache */
      SetTextColor( gdiCache.metafile, txtColor );
      gdiCache.curTextColor = txtColor;
   }

   /* no need to worry about restoring text color, since this is set
      before the initial SaveDC() is issued and is restored after each
      RestoreDC() call back to the metafile default. */

}  /* CaSetTextColor */


void CaSetTextCharacterExtra( Integer chExtra )
/*==========================*/
/* set the character extra spacing */
{
   if (gdiCache.curCharExtra != chExtra)
   {
      /* flush the primitive cache if changing text char extra */
      CaFlushCache();

      /* set the char extra and same state in the cache */
      SetTextCharacterExtra( gdiCache.metafile, chExtra );
      gdiCache.curCharExtra = chExtra;

   }
   else if (gdiCache.restoreCharExtra)
   {
      /* if text char extra was invalidated by RestoreDC(), re-issue */
      SetTextCharacterExtra( gdiCache.metafile, gdiCache.curCharExtra );
   }

   /* all is ok with cache now */
   gdiCache.restoreCharExtra = FALSE;

}  /* CaSetTextCharacterExtra */


void CaSetBkColor( RGBColor bkColor )
/*===============*/
/* set background color if different from current setting */
{
   if (gdiCache.curBkColor != bkColor)
   {
      /* flush the primitive cache if changing background color */
      CaFlushCache();

      /* Set the background color and save in cache */
      SetBkColor( gdiCache.metafile, bkColor );
      gdiCache.curBkColor = bkColor;
   }

   /* no need to worry about restoring background color, since this is set
      before the initial SaveDC() is issued and is restored after each
      RestoreDC() call back to the metafile default. */

}  /* CaSetBkColor */


Boolean CaIntersectClipRect( Rect rect )
/*=========================*/
/* Create new clipping rectangle - return FALSE if drawing is disabled */
{
   Rect     combinedRect;

   /* See if the clipping rectangle is empty, indicating that no drawing
      should occur into the metafile */
   if (Height( rect ) == 0 || Width( rect ) == 0)
   {
      /* indicate that drawing is disabled */
      return FALSE;
   }

   /* don't do anything if rectangle hasn't changed */
   if (!EqualRect( &rect, &gdiCache.curClipRect ) || gdiCache.forceNewClipRect)
   {
      /* flush the primitive cache if changing clip region */
      CaFlushCache();

      /* is new clip rect is completely enclosed by current cliprect? */
      IntersectRect( &combinedRect, &rect, &gdiCache.curClipRect );

      /* check for equality of intersection and new cliprect */
      if (!EqualRect( &combinedRect, &rect ) || gdiCache.forceNewClipRect)
      {
         /* must be called just to be able to change clipping rectangle */
         CaRestoreDC();
         CaSaveDC();
      }

      /* set the new clipping rectangle */
      IntersectClipRect( gdiCache.metafile,
                         rect.left, rect.top, rect.right, rect.bottom );

      /* save the current clip rectangle, since it has changed */
      gdiCache.curClipRect = rect;

      /* turn off forcing of the clipping rectangle */
      gdiCache.forceNewClipRect = FALSE;
   }

   /* return TRUE - drawing is enabled */
   return TRUE;

}  /* GdiIntersectClipRect */


void CaSetClipRect( Rect rect )
/*================*/
/* set the current cliprectangle to be equal to rect */
{
   gdiCache.curClipRect = rect;

}  /* CaSetClipRect */


Rect far * CaGetClipRect( void )
/*=====================*/
/* get the current cliprectangle */
{
   return &gdiCache.curClipRect;

}  /* CaGetClipRect */


void CaNonRectangularClip( void )
/*=======================*/
/* notify cache that a non-rectangular clipping region was set */
{
   gdiCache.forceNewClipRect = TRUE;

}  /* CaNonRectangularClip */


void CaSaveDC( void )
/*===========*/
/* save the current device context - used to set up clipping rects */
{
   /* the previous clipping rectangle is saved off */
   gdiCache.prevClipRect = gdiCache.curClipRect;

   /* issue call to GDI */
   SaveDC( gdiCache.metafile );
}


void CaRestoreDC( void )
/*==============*/
/* restore the device context and invalidate cached attributes */
{
   /* restore previous clipping rectangle */
   gdiCache.curClipRect = gdiCache.prevClipRect;

   /* invalidate all of the cached attributes and objects */
   gdiCache.restorePen =
   gdiCache.restoreBrush =
   gdiCache.restoreFont =
   gdiCache.restoreCharExtra = TRUE;

   /* reset metafile defaults */
   gdiCache.curROP2 = gdiCache.iniROP2;
   gdiCache.curTextAlign = gdiCache.iniTextAlign;
   gdiCache.curBkMode = gdiCache.iniBkMode;
   gdiCache.curTextColor = gdiCache.iniTxColor;
   gdiCache.curBkColor = gdiCache.iniBkColor;

   /* issue call to GDI */
   RestoreDC( gdiCache.metafile, -1 );
}

/******************************* Private Routines ***************************/


private void AddPolyPt( Point pt )
/*--------------------*/
/* Add a point to the polygon buffer */
{
   /* make sure that we haven't reached maximum size */
   if ((gdiCache.numPoints + 1) >= gdiCache.maxPoints)
   {
      /* expand the number of points that can be cached by 10 */
      gdiCache.maxPoints += 16;

      /* unlock to prepare for re-allocation */
      GlobalUnlock( gdiCache.polyHandle);

      /* re-allocate the memory handle by the given amount */
      gdiCache.polyHandle = GlobalReAlloc(
            gdiCache.polyHandle,
            gdiCache.maxPoints * sizeof( Point ),
            GMEM_MOVEABLE);

      /* make sure that the re-allocation succeeded */
      if (gdiCache.polyHandle == NULL)
      {
         /* if not, flag global error and exit from here */
         ErSetGlobalError( ErMemoryFull );
         return;
      }

      /* lock the memory handle to get a pointer address */
      gdiCache.pointList = (Point far *)GlobalLock( gdiCache.polyHandle );
   }

   /* insert the new point and increment the number of points in the buffer */
   gdiCache.pointList[gdiCache.numPoints++] = pt;

}  /* AddPolyPt */



private void SelectCachedPen( void )
/*--------------------------*/
/* select the currently cached Pen into the metafile */
{
   /* make sure that there is some new pen to select */
   if (gdiCache.nextPen.handle != NULL)
   {
      /* make sure that the pens are different */
      if (gdiCache.nextPen.handle != gdiCache.curPen.handle)
      {
        /* select the new pen */
         SelectObject( gdiCache.metafile, gdiCache.nextPen.handle);

         /* if this isn't the first pen selection and not a stock pen */
         if (gdiCache.curPen.handle != NULL && !gdiCache.curPen.stockObject)
         {
            /* delete the previous pen selection */
            DeleteObject( gdiCache.curPen.handle );
         }

         /* save pen handle in current cache variable */
         gdiCache.curPen = gdiCache.nextPen;
      }

      /* reset the cache pen to indicate no pre-existing cached pen */
      gdiCache.nextPen.handle = NULL;
   }

}  /* SelectCachedPen */

