/**************************************************************************** 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 = (WORD) 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 */