/******************************Module*Header*******************************\ * Module Name: brushobj.cxx * * Support for brmemobj.hxx and brushobj.hxx. * * Created: 06-Dec-1990 12:02:24 * Author: Walt Moore [waltm] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/ #include "precomp.hxx" extern "C" BOOL bInitBRUSHOBJ(); extern "C" BOOL bInitBrush(int iBrush, COLORREF cr, DWORD dwHS, PULONG_PTR pdw, BOOL bEnableDither); #pragma alloc_text(INIT, bInitBRUSHOBJ) #pragma alloc_text(INIT, bInitBrush) // Global pointer to the last RBRUSH freed, if any (for one-deep caching). PRBRUSH gpCachedDbrush = NULL; PRBRUSH gpCachedEngbrush = NULL; #define MAX_STOCKBRUSHES 4*1024 LONG gStockBrushFree = MAX_STOCKBRUSHES; //#define DBG_STOCKBRUSHES 1 #if DBG_STOCKBRUSHES #define STOCKWARNING DbgPrint #define STOCKINFO DbgPrint #else #define STOCKWARNING #define STOCKINFO #endif extern "C" HFASTMUTEX ghfmMemory; #if DBG LONG bo_inits, bo_realize, bo_notdirty, bo_cachehit; LONG bo_missnotcached, bo_missfg, bo_missbg, bo_misspaltime, bo_misssurftime; #endif /****************************Global*Public*Data******************************\ * * These are the 5 global brushes and 3 global pens maintained by GDI. * These are retrieved through GetStockObject. * * History: * 20-May-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ HBRUSH ghbrText; HBRUSH ghbrBackground; HBRUSH ghbrGrayPattern; PBRUSH gpbrText; PBRUSH gpbrNull; PBRUSH gpbrBackground; PPEN gpPenNull; HBRUSH ghbrDCBrush; PBRUSH gpbrDCBrush; HBRUSH ghbrDCPen; PBRUSH gpbrDCPen; // Uniqueness so a logical handle can be reused without having it look like // the same brush as before. We don't really care where this starts. ULONG BRUSH::_ulGlobalBrushUnique = 0; ULONG gCacheHandleEntries[GDI_CACHED_HADNLE_TYPES] = { CACHE_BRUSH_ENTRIES , CACHE_PEN_ENTRIES , CACHE_REGION_ENTRIES, CACHE_LFONT_ENTRIES }; ULONG gCacheHandleOffsets[GDI_CACHED_HADNLE_TYPES] = { 0, CACHE_BRUSH_ENTRIES, ( CACHE_BRUSH_ENTRIES + CACHE_PEN_ENTRIES ), ( CACHE_BRUSH_ENTRIES + CACHE_PEN_ENTRIES + CACHE_PEN_ENTRIES ) }; /******************************Public*Routine******************************\ * bPEBCacheHandle * * Try to place the object(handle) in a free list on the PEB. The objects * are removed from this list in user mode. * * Arguments: * * Handle - handle to cache * HandleType - type of handle to attempt cache * pBrushattr - pointer to user-mode object * * Return Value: * * TRUE if handle is cached, FALSE otherwise * * History: * * 30-Jan-1996 -by- Mark Enstrom [marke] * \**************************************************************************/ BOOL bPEBCacheHandle( HANDLE Handle, HANDLECACHETYPE HandleType, POBJECTATTR pObjectattr, PENTRY pentry ) { BOOL bRet = FALSE; PBRUSHATTR pBrushattr = (PBRUSHATTR)pObjectattr; PW32PROCESS pw32Process = W32GetCurrentProcess(); PPEB Peb; #if !defined(_GDIPLUS_) ASSERTGDI(((HandleType == BrushHandle) || (HandleType == PenHandle) || (HandleType == RegionHandle) ||(HandleType == LFontHandle) ),"hGetPEBHandle: illegal handle type"); Peb = PsGetProcessPeb(pw32Process->Process); if (Peb != NULL) { PGDIHANDLECACHE pCache = (PGDIHANDLECACHE)(&Peb->GdiHandleBuffer[0]); BOOL bStatus; // // Lock Handle cache on PEB // LOCK_HANDLE_CACHE(pCache,PsGetCurrentThread(),bStatus); if (bStatus) { // // are any free slots still availablle // if (pCache->ulNumHandles[HandleType] < gCacheHandleEntries[HandleType]) { ULONG Index = gCacheHandleOffsets[HandleType]; PHANDLE pHandle,pMaxHandle; // // calculate handle offset in PEB array // pHandle = &(pCache->Handle[Index]); pMaxHandle = pHandle + gCacheHandleEntries[HandleType]; // // search array for a free entry // while (pHandle != pMaxHandle) { if (*pHandle == NULL) { // // for increased robust behavior, increment handle unique // pentry->FullUnique += UNIQUE_INCREMENT; Handle = (HOBJ)MAKE_HMGR_HANDLE((ULONG)(ULONG_PTR)Handle & INDEX_MASK, pentry->FullUnique); pentry->einfo.pobj->hHmgr = Handle; // // store handle in cache and inc stored count // *pHandle = Handle; pCache->ulNumHandles[HandleType]++; bRet = TRUE; // // clear to be deleted and select flags, // set cached flag // pBrushattr->AttrFlags &= ~(ATTR_TO_BE_DELETED | ATTR_CANT_SELECT); pBrushattr->AttrFlags |= ATTR_CACHED; break; } pHandle++; } ASSERTGDI(bRet,"bPEBCacheHandle: count indicates free handle, but none free\n"); } UNLOCK_HANDLE_CACHE(pCache); } } #endif return(bRet); } /******************************Public*Routine******************************\ * BRUSHMEMOBJ::pbrAllocBrush(bPen) * * Base constructor for brush memory object. This constructor is to be * called by the various public brush constructors only. * * History: * 29-Oct-1992 -by- Michael Abrash [mikeab] * changed to allocate but not get a handle or lock (so the brush can be fully * set up before the handle exists, exposing the data to the outside world). * * Wed 19-Jun-1991 -by- Patrick Haluptzok [patrickh] * 0 out the brush. * * Thu 06-Dec-1990 12:02:41 -by- Walt Moore [waltm] * Wrote it. \**************************************************************************/ PBRUSH BRUSHMEMOBJ::pbrAllocBrush(BOOL bPen) { PBRUSH pbrush; bKeep = FALSE; // Allocate a new brush or pen // // Note: if anyone decides to try turning off zeroinit for performance, // make sure to initialize the pen's psytle and cstyle to zero. Of // course, other dependencies may creep in, so do this very very very // carefully (if you even dare!). if ((pbrush = (PBRUSH)ALLOCOBJ(bPen ? sizeof(PEN) : sizeof(BRUSH), BRUSH_TYPE, TRUE)) != NULL) { pbrush->pBrushattr(&pbrush->_Brushattr); pbrush->pIcmDIBList(NULL); // no ICM translated DIBs pbrush->iUsage(0); // Set up as initially not caching any realization pbrush->vSetNotCached(); // no one's trying to cache a realization // in this logical brush yet pbrush->crFore((COLORREF)BO_NOTCACHED); // no cached realization yet (no need to // worry about crFore not being set when // someone tries to check for caching, // because we don't have a handle yet, and // we'll lock when we do get the handle, // forcing writes to flush) pbrush->ulBrushUnique(pbrush->ulGlobalBrushUnique()); // set the uniqueness so the are-you- // really-dirty check in vInitBrush will // know this is not the brush in the DC } return(pbrush); } /******************************Public*Routine******************************\ * BRUSHMEMOBJ::BRUSHMEMOBJ * * Create a pattern brush or a DIB brush. * * History: * 29-Oct-1992 -by- Michael Abrash [mikeab] * changed to get handle only after fully initialized * * 14-May-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ BRUSHMEMOBJ::BRUSHMEMOBJ(HBITMAP hbmClone, HBITMAP hbmClient,BOOL bMono, FLONG flDIB, FLONG flType, BOOL bPen) { if (flDIB == DIB_PAL_COLORS) { flType |= BR_IS_DIBPALCOLORS; } else if (flDIB == DIB_PAL_INDICES) { flType |= BR_IS_DIBPALINDICES; } PBRUSH pbrush; if ((pbp.pbr = pbrush = pbrAllocBrush(bPen)) != NULL) { pbrush->crColor(0); pbrush->ulStyle(HS_PAT); pbrush->hbmPattern(hbmClone); pbrush->hbmClient(hbmClient); pbrush->AttrFlags(0); pbrush->flAttrs(flType); if (bMono) { pbrush->flAttrs(pbrush->flAttrs() | (BR_NEED_BK_CLR | BR_NEED_FG_CLR | BR_IS_MONOCHROME)); } // Now that everything is set up, create the handle and expose this logical // brush if (HmgInsertObject(pbrush, HMGR_ALLOC_ALT_LOCK, BRUSH_TYPE) == 0) { FREEOBJ(pbrush, BRUSH_TYPE); pbp.pbr = NULL; } } else { WARNING1("Brush allocation failed\n"); } } /******************************Public*Routine******************************\ * GreSetSolidBrush * * Chicago API to change the color of a solid color brush. * * History: * 19-Apr-1994 -by- Patrick Haluptzok patrickh * Made it a function that User can call too. * * 03-Dec-1993 -by- Eric Kutter [erick] * Wrote it - bReset. \**************************************************************************/ BOOL GreSetSolidBrush(HBRUSH hbr, COLORREF clr) { return(GreSetSolidBrushInternal(hbr, clr, FALSE, TRUE)); } BOOL GreSetSolidBrushInternal( HBRUSH hbr, COLORREF clr, BOOL bPen, BOOL bUserCalled ) { BOOL bReturn = FALSE; BRUSHSELOBJ ebo(hbr); PBRUSH pbrush = ebo.pbrush(); if (pbrush != NULL) { if ((pbrush->flAttrs() & BR_IS_SOLID) && ((!pbrush->bIsGlobal()) || bUserCalled) && ((!!pbrush->bIsPen()) == bPen)) { #if DBG if (bPen) { ASSERTGDI(((PPEN) pbrush)->pstyle() == NULL || (pbrush->flAttrs() & BR_IS_DEFAULTSTYLE), "GreSetSolidBrush - bad attrs\n"); } #endif ASSERTGDI(pbrush->hbmPattern() == NULL, "ERROR how can solid have pat"); PRBRUSH prbrush = (PRBRUSH) NULL; RBTYPE rbType; { // // Can't do the delete of the RBRUSH under MLOCK, takes too // long and it may try and grab it again. // MLOCKFAST mlo; // // User may call when the brush is selected in a DC, but // the client side should only ever call on a brush that's // not in use. // if ((pbrush->cShareLockGet() == 1) || bUserCalled) { bReturn = TRUE; pbrush->crColor(clr); HANDLELOCK HandleLock(PENTRY_FROM_POBJ(pbrush), FALSE); if (HandleLock.bValid()) { if (pbrush->cShareLockGet() == 1) { // // Nobody is using it and we have the handle lock // so noone can select it in till we are done. So // clean out the old realization now. // if ((pbrush->crFore() != BO_NOTCACHED) && !pbrush->bCachedIsSolid()) { prbrush = (PRBRUSH) pbrush->ulRealization(); rbType = pbrush->bIsEngine() ? RB_ENGINE : RB_DRIVER; } // Set up as initially not caching any realization pbrush->vSetNotCached(); // no one's trying to cache a realization // in this logical brush yet pbrush->crFore((COLORREF)BO_NOTCACHED); // no cached realization yet (no need to // worry about crFore not being set when // someone tries to check for caching, // because we don't have a handle yet, and // we'll lock when we do get the handle, // forcing writes to flush) if (!bUserCalled) { // // If it's not User calling we are resetting the // attributes / type. // pbrush->ulStyle(HS_DITHEREDCLR); pbrush->flAttrs(BR_IS_SOLID | BR_DITHER_OK); } else { pbrush->vClearSolidRealization(); } } else { //ASSERTGDI(bUserCalled, // "Client side is hosed, shouldn't " // "call this with it still selected"); ASSERTGDI(pbrush->flAttrs() & BR_IS_SOLID, "ERROR not solid"); ASSERTGDI(pbrush->ulStyle() == HS_DITHEREDCLR, "ERROR not HS_DI"); // // Mark this brushes realization as dirty by setting // it's cache id's to invalid states. Note that if a // realization hasn't been cached yet this will cause // no problem either. // pbrush->crBack(0xFFFFFFFF); pbrush->ulPalTime(0xFFFFFFFF); pbrush->ulSurfTime(0xFFFFFFFF); // // This brush is being used other places, check for // any DC's that have this brush selected in and mark // their realizations dirty. // // Note there is the theoretical possibility that // somebody is realizing the brush while we are // marking them dirty and they won't pick up the new // color. We set the color first and set the // uniqueness last so that it is extremely unlikely // (maybe impossible) that someone gets a realization // that incorrectly thinks it has the proper // realization. This is fixable by protecting access // to the realization and cache fields but we aren't // going to do it for Daytona. // // Mark every DC in the system that has this brush // selected as a dirty brush. // HOBJ hobj = (HOBJ) 0; DC *pdc; while ((pdc = (DC *) HmgSafeNextObjt(hobj, DC_TYPE)) != NULL) { if (pdc->peboFill()->pbrush() == pbrush) { pdc->flbrushAdd(DIRTY_FILL); } hobj = (HOBJ) pdc->hGet(); } } HandleLock.vUnlock(); } // // Set the uniqueness so the are-you- // really-dirty check in vInitBrush will // not think an old realization is still valid. // pbrush->ulBrushUnique(pbrush->ulGlobalBrushUnique()); } else { WARNING1("Error, SetSolidBrush with cShare != 1"); } } if (prbrush) { prbrush->vRemoveRef(rbType); } } #if DBG else { if (bPen) { WARNING1("bPen True\n"); } if (pbrush->bIsPen()) { WARNING1("bIsPen True\n"); } if (bUserCalled) { WARNING1("bUserCalled\n"); } if (pbrush->bIsGlobal()) { WARNING1("bIsGlobal\n"); } if (pbrush->flAttrs() & BR_IS_SOLID) { WARNING1("BR_IS_SOLID is set\n"); } WARNING1("GreSetSolidBrush not passed a solid color brush\n"); } #endif } #if DBG else { WARNING1("GreSetSolidBrush failed to lock down brush\n"); } #endif return(bReturn); } /******************************Public*Routine******************************\ * GreSetSolidBrushLight: * * Private version of GreSetSolidBrush, user can't call * * Arguments: * * pbrush - pointer to log brush * clr - new color * bPen - Brush is a pen * * Return Value: * * Status * * History: * * 2-Nov-1995 -by- Mark Enstrom [marke] * \**************************************************************************/ BOOL GreSetSolidBrushLight( PBRUSH pbrush, COLORREF clr, BOOL bPen ) { BOOL bReturn = FALSE; if (pbrush != NULL) { if ( (pbrush->flAttrs() & BR_IS_SOLID) && (!pbrush->bIsGlobal()) ) { // // make sure bPen flag matches brush type // if ((bPen != 0) == (pbrush->bIsPen() != 0)) { #if DBG if (bPen) { ASSERTGDI(((PPEN) pbrush)->pstyle() == NULL || (pbrush->flAttrs() & BR_IS_DEFAULTSTYLE), "GreSetSolidBrushLight - illegal PEN attrs\n"); } #endif ASSERTGDI(pbrush->hbmPattern() == NULL, "ERROR how can solid have pat"); PRBRUSH prbrush = (PRBRUSH) NULL; RBTYPE rbType; { // // Grab the handle lock to stabize the lock counts. // Do not attempt to free the realized brush under // this lock; it may take to long. // ASSERTGDI(pbrush->hGet(), "ERROR brush obj has no handle\n"); HANDLELOCK HandleLock(PENTRY_FROM_POBJ(pbrush),FALSE); if (HandleLock.bValid()) { if (pbrush->cShareLockGet() == 1) { bReturn = TRUE; pbrush->crColor(clr); // // Nobody is using it and we have the HANDLELOCK // so noone can select it in till we are done. So // clean out the old realization now. // if ((pbrush->crFore() != BO_NOTCACHED) && !pbrush->bCachedIsSolid()) { prbrush = (PRBRUSH) pbrush->ulRealization(); rbType = pbrush->bIsEngine() ? RB_ENGINE : RB_DRIVER; } // // Set up as initially not caching any realization // pbrush->vSetNotCached(); // no one's trying to cache a realization // in this logical brush yet pbrush->crFore((COLORREF)BO_NOTCACHED); // no cached realization yet (no need to // worry about crFore not being set when // someone tries to check for caching, // because we don't have a handle yet, and // we'll lock when we do get the handle, // forcing writes to flush) // // we are resetting the attributes / type. // if (bPen) { pbrush->ulStyle(HS_DITHEREDCLR); FLONG flOldAttrs = pbrush->flAttrs() & (BR_IS_PEN | BR_IS_OLDSTYLEPEN); pbrush->flAttrs(BR_IS_SOLID | flOldAttrs); } else { pbrush->ulStyle(HS_DITHEREDCLR); pbrush->flAttrs(BR_IS_SOLID | BR_DITHER_OK); } // // Set the uniqueness so the are-you- // really-dirty check in vInitBrush will // not think an old realization is still valid. // pbrush->ulBrushUnique(pbrush->ulGlobalBrushUnique()); } else { WARNING1("Error, SetSolidBrush with cShare != 1"); } HandleLock.vUnlock(); } } if (prbrush) { prbrush->vRemoveRef(rbType); } } } #if DBG else { if (pbrush->bIsGlobal()) { WARNING1("bIsGlobal\n"); } if (pbrush->flAttrs() & BR_IS_SOLID) { WARNING1("BR_IS_SOLID is set\n"); } WARNING("GreSetSolidBrush not passed a solid color brush\n"); } #endif } #if DBG else { WARNING1("GreSetSolidBrush failed to lock down brush\n"); } #endif return(bReturn); } /******************************Public*Routine******************************\ * GreGetBrushColor * * Call for User to retrieve the color from any brush owned by any process * so User can repaint the background correctly in full drag. To make sure * we don't hose an app we need to hold the mult-lock while we do this so * any operation by the app (such as a Delete) will wait and not fail * because we're temporarily locking the brush down to peek inside of it. * * History: * 14-Jun-1994 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ COLORREF GreGetBrushColor(HBRUSH hbr) { COLORREF clrRet = 0xFFFFFFFF; // // Grab the multi-lock so everyone waits while do our quick hack // to return the brush color. // MLOCKFAST mlo; // // Lock it down but don't check ownership because we want to succeed // no matter what. // // // using try except to make sure we will not crash // when a bad handle passed in. // __try { PENTRY pentry = &gpentHmgr[HmgIfromH(hbr)]; PBRUSH pbrush = (PBRUSH)(pentry->einfo.pobj); if (pbrush) { if ((pbrush->ulStyle() == HS_SOLIDCLR) || (pbrush->ulStyle() == HS_DITHEREDCLR)) { clrRet = pbrush->crColor(); } } } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING1("GreGetBrushColor - bad handle passed in\n"); } return(clrRet); } /******************************Public*Routine******************************\ * BRUSHMEMOBJ::BRUSHMEMOBJ * * Creates hatched brushes and solid color brushes. * * History: * 29-Oct-1992 -by- Michael Abrash [mikeab] * changed to get handle only after fully initialized * * Wed 26-Feb-1992 -by- Patrick Haluptzok [patrickh] * rewrote to subsume other constructors, add new hatch styles. * * Sun 19-May-1991 -by- Patrick Haluptzok [patrickh] * Wrote it. \**************************************************************************/ BRUSHMEMOBJ::BRUSHMEMOBJ(COLORREF cr, ULONG ulStyle_, BOOL bPen, BOOL bSharedMem) { if (ulStyle_ > HS_NULL) { WARNING1("Invalid style type\n"); pbp.pbr = NULL; return; } PBRUSH pbrush; if ((pbp.pbr = pbrush = pbrAllocBrush(bPen)) == NULL) { WARNING1("Brush allocation failed\n"); return; } pbrush->crColor(cr); pbrush->ulStyle(ulStyle_); pbrush->hbmPattern(0); pbrush->AttrFlags(0); if (ulStyle_ < HS_DDI_MAX) { // The old hatch brushes have been extended to include all the default // patterns passed back by the driver. There are 19 default pattens. pbrush->flAttrs(BR_IS_HATCH | BR_NEED_BK_CLR | BR_IS_MASKING); goto CreateHandle; } // Handle the other brush types switch(ulStyle_) { case HS_SOLIDCLR: pbrush->flAttrs(BR_IS_SOLID); break; case HS_DITHEREDCLR: pbrush->flAttrs(BR_IS_SOLID | BR_DITHER_OK); break; case HS_SOLIDTEXTCLR: pbrush->flAttrs(BR_IS_SOLID | BR_NEED_FG_CLR); break; case HS_DITHEREDTEXTCLR: pbrush->flAttrs(BR_IS_SOLID | BR_NEED_FG_CLR | BR_DITHER_OK); break; case HS_SOLIDBKCLR: pbrush->flAttrs(BR_IS_SOLID | BR_NEED_BK_CLR); break; case HS_DITHEREDBKCLR: pbrush->flAttrs(BR_IS_SOLID | BR_NEED_BK_CLR | BR_DITHER_OK); break; case HS_NULL: pbrush->flAttrs(BR_IS_NULL); break; default: RIP("ERROR BRUSHMEMOBJ hatches invalid type"); } // Now that everything is set up, create the handle and expose this logical // brush CreateHandle: if (HmgInsertObject(pbrush, HMGR_ALLOC_ALT_LOCK, BRUSH_TYPE) == 0) { FREEOBJ(pbrush, BRUSH_TYPE); pbp.pbr = NULL; } else { if (bSharedMem) { // // Setup the user mode BRUSHATTR // PBRUSHATTR pUser = (PBRUSHATTR)HmgAllocateObjectAttr(); if (pUser) { HANDLELOCK BrushLock; BrushLock.bLockHobj((HOBJ)pbrush->hHmgr,BRUSH_TYPE); if (BrushLock.bValid()) { PENTRY pent = BrushLock.pentry(); // // fill up the brushattr // *pUser = pbrush->_Brushattr; pent->pUser = (PVOID)pUser; pbrush->pBrushattr(pUser); BrushLock.vUnlock(); } } } } } /******************************Public*Routine******************************\ * EBRUSHOBJ::vInitBrush * * Initializes the brush user object. If the color can be represented * without dithering, we set iSolidColor. * * History: * Tue 08-Dec-1992 -by- Michael Abrash [mikeab] * Rewrote for speed. * * Sun 23-Jun-1991 -by- Patrick Haluptzok [patrickh] * Wrote it. \**************************************************************************/ VOID EBRUSHOBJ::vInitBrush( PDC pdc, // current dc or a fake dc with only back/foreground clr info PBRUSH pbrushIn, // Current logical brush XEPALOBJ palDC, // Target's DC palette XEPALOBJ palSurf, // Target's surface palette SURFACE *pSurface, // Target surface BOOL bCanDither // If FALSE then never dither ) { // Note: If more members of pdc are accessed in the future, then code must be // added in drvsup.cxx to initialize the members in each place a fake DC // object is initialized. There's also a fake DC in bDynamicModeChange in // opendc.cxx which must be updated. // If the palSurf isn't valid, then the target is a bitmap for a palette // managed device; therefore the palette means nothing until we actually blt, // and only the DC palette is relevant. Likewise, if the target is a palette // managed surface, the brush is realized as indices into the logical palette, // and unless the logical palette is changed, the brush doesn't need to be // rerealized. This causes us effectively to check the logical palette time // twice, but that's cheaper than checking whether we need to check the surface // palette time and then possibly checking it. ULONG ulSurfTime = (palSurf.bValid() && !palSurf.bIsPalManaged()) ? palSurf.ulTime() : 1; #if DBG bo_inits++; #endif // If the brush really is dirty, we have to set this anyway; if it's not dirty, // this takes care of the case where the surface has changed out from under us, // and then a realization is required and we would otherwise fault trying to // access the target surface structure in the process of realization. psoTarg1 = pSurface; // surface for which brush is realized // The journaling code depends on this // being set correctly. This has the PDEV // for the device it's selected into. COLORREF crTextDC = pdc->crTextClr(); COLORREF crBackDC = pdc->crBackClr(); LONG lIcmModeDC = pdc->lIcmMode(); HANDLE hcmXformDC = pdc->hcmXform(); // See if the brush really isn't dirty and doesn't need to be rerealized if ( ( pbrushIn->ulBrushUnique() == _ulUnique ) && (!bCareAboutFg() || (crCurrentText() == crTextDC) ) && (!bCareAboutBg() || (crCurrentBack() == crBackDC) ) && (palDC.ulTime() == ulDCPalTime()) && (ulSurfTime == ulSurfPalTime()) && (pbrushIn != gpbrDCBrush)&& (pbrushIn != gpbrDCPen) && (lIcmMode() == lIcmModeDC) && (hcmXform() == hcmXformDC) && (bCanDither == _bCanDither)) { #if DBG bo_notdirty++; #endif return; } // Get Cached Values flAttrs = pbrushIn->flAttrs(); // Remember the characteristics of the brush _pbrush = pbrushIn; _ulUnique = pbrushIn->ulBrushUnique(); // brush uniqueness crCurrentText1 = crTextDC; // text color at realization time crCurrentBack1 = crBackDC; // background color at realization time _ulDCPalTime = palDC.ulTime(); // DC palette set time at realization time _ulSurfPalTime = ulSurfTime; // surface palette set time at realization time _bCanDither = bCanDither; // dither enabled? // Initialize ICM stuffs BOOL bCMYKColorSolid = FALSE; flColorType = 0; // Initialized with zero. // Set icm modes if (IS_ICM_ON(lIcmModeDC)) { BOOL bIcmBrush = FALSE; // color translation should happen, check we have nessesary data for ICM. if (flAttrs & (BR_IS_SOLID|BR_IS_HATCH|BR_IS_MONOCHROME)) { if (IS_ICM_HOST(lIcmModeDC)) { // DC attributes should have ICM-ed color. // // (or with null-ColorTransform, no color translation happen) if (flAttrs & (BR_IS_SOLID|BR_IS_MONOCHROME)) { if ((flAttrs & (BR_NEED_FG_CLR|BR_NEED_BK_CLR)) || (pbrushIn == gpbrDCBrush) || (pbrushIn == gpbrDCPen)) { bIcmBrush = TRUE; } } if (bIcmBrush == FALSE) { if (pbrushIn->bIsPen()) { if ((hcmXformDC == NULL) || pdc->bValidIcmPenColor()) { bIcmBrush = TRUE; } else { ICMMSG(("vInitBrush():ERROR: No ICMed pen color for this brush\n")); } } else { if ((hcmXformDC == NULL) || pdc->bValidIcmBrushColor()) { bIcmBrush = TRUE; } else { ICMMSG(("vInitBrush():ERROR: No ICMed brush color for this brush\n")); } } } } else // other ICM modes (Device and Apps) { bIcmBrush = TRUE; } } else if (flAttrs & BR_IS_DIB) { if (IS_ICM_HOST(lIcmModeDC)) { // Brush should have ICM-ed DIB // // (or with null-ColorTransform, no color translation happen) if ((hcmXformDC == NULL) || pbrushIn->hFindIcmDIB(hcmXformDC)) { bIcmBrush = TRUE; } else { ICMMSG(("vInitBrush():ERROR: No ICMed DIB for this brush\n")); } } else // other ICM modes (Device and Apps) { bIcmBrush = TRUE; } } else { // Other stlyes, no ICM } if (bIcmBrush) { lIcmMode(lIcmModeDC); // ICM mode hcmXform(hcmXformDC); // ICM molor Transform handle // setup colortype flag. if (bIsAppsICM() || bIsHostICM()) { flColorType |= BR_HOST_ICM; } else if (bIsDeviceICM()) { flColorType |= BR_DEVICE_ICM; } // If the brush is solid, iSolidColor will have CMKY color. Otherwise, // iSolidColor will be 0xFFFFFFFF and flColorType does not have BR_CMYKCOLOR. bCMYKColorSolid = (bIsCMYKColor() && (flAttrs & BR_IS_SOLID)); if (bCMYKColorSolid) { flColorType |= BR_CMYKCOLOR; // color type is CMYK color } } else { ICMMSG(("vInitBrush():This brush is not ICMed\n")); lIcmMode(DC_ICM_OFF); // ICM mode hcmXform(NULL); // ICM molor Transform handle } } else { lIcmMode(DC_ICM_OFF); // ICM mode hcmXform(NULL); // ICM molor Transform handle } // Get the target PDEV PDEVOBJ po(pSurface->hdev()); ASSERTGDI(po.bValid(), "ERROR BRUSHOBJ PDEVOBJ"); // Set palettes palDC1.ppalSet(palDC.ppalGet()); palSurf1.ppalSet(palSurf.ppalGet()); palMeta1.ppalSet(po.ppalSurfNotDynamic()); _iMetaFormat = po.iDitherFormatNotDynamic(); ASSERTGDI(pSurface != NULL, "ERROR BRUSHOBJ::bInit0"); ASSERTGDI(palDC.bValid(), "ERROR BRUSHOBJ::bInit4"); // Clean up what was already here // If this brush object had an engine brush realization, get rid of it if (pengbrush1 != (PENGBRUSH) NULL) { PRBRUSH prbrush = pengbrush1; // point to engine brush realization prbrush->vRemoveRef(RB_ENGINE); // decrement the reference count on the // realization and free the brush if // this is the last reference pengbrush1 = NULL; // mark that there's no realization } // If this brush object had a device brush realization, get rid of it if (pvRbrush != (PVOID) NULL) { PRBRUSH prbrush = (PDBRUSH)DBRUSHSTART(pvRbrush); // point to DBRUSH (pvRbrush points to // realization, which is at the end of DBRUSH) prbrush->vRemoveRef(RB_DRIVER); // decrement the reference count on the // realization and free the brush if // this is the last reference pvRbrush = NULL; // mark that there's no realization } // Remember the color so we do the realization code correctly later // if it's a dithered brush. We may need this even if we have // a hit in the cache since we have driver/engine distinction. if (flAttrs & BR_IS_SOLID) { if (flAttrs & BR_NEED_FG_CLR) { crRealize = crCurrentText(); // use text brush if (bIsHostICM()) crRealizeOrignal = pdc->ulTextClr(); } else if (flAttrs & BR_NEED_BK_CLR) { crRealize = crCurrentBack(); // use back brush if (bIsHostICM()) crRealizeOrignal = pdc->ulBackClr(); } else if (pbrushIn == gpbrDCBrush) { crRealize = pdc->crDCBrushClr(); // use DC brush if (bIsHostICM()) crRealizeOrignal = pdc->ulDCBrushClr(); } else if (pbrushIn == gpbrDCPen) { crRealize = pdc->crDCPenClr(); // use DC pen if (bIsHostICM()) crRealizeOrignal = pdc->ulDCPenClr(); } else { crRealize = pbrushIn->crColor(); if (bIsHostICM()) { crRealizeOrignal = crRealize; if (pbrushIn->bIsPen()) { if (pdc->bValidIcmPenColor()) { crRealize = pdc->crIcmPenColor(); // use ICM translated pen } } else { if (pdc->bValidIcmBrushColor()) { crRealize = pdc->crIcmBrushColor(); // use ICM translated brush } } } } } else if (flAttrs & BR_IS_HATCH) { crRealize = pbrushIn->crColor(); if (bIsHostICM()) { crRealizeOrignal = crRealize; if (pbrushIn->bIsPen()) { if (pdc->bValidIcmPenColor()) { crRealize = pdc->crIcmPenColor(); } } else { if (pdc->bValidIcmBrushColor()) { crRealize = pdc->crIcmBrushColor(); } } } } // See if there's a cached realization that we can use // Note that the check for crFore MUST come first, because if and only if // that field is not BO_NOTCACHED is there a valid cached realization. #if DBG bo_realize++; if ( (pbrushIn->crFore() == BO_NOTCACHED) ) { bo_missnotcached++; } else if ( pbrushIn->bCareAboutFg() && (pbrushIn->crFore() != crTextDC) ) { bo_missfg++; } else if (pbrushIn->bCareAboutBg() && (pbrushIn->crBack() != crBackDC) ) { bo_missbg++; } else if ( pbrushIn->ulPalTime() != ulDCPalTime() ) { bo_misspaltime++; } else if ( pbrushIn->ulSurfTime() != ulSurfPalTime() ) { bo_misssurftime++; } else { bo_cachehit++; } #endif if ( (pbrushIn->crFore() != BO_NOTCACHED) && ( (!pbrushIn->bCareAboutFg()) || (pbrushIn->crFore() == crTextDC) ) && ( (!pbrushIn->bCareAboutBg()) || (pbrushIn->crBack() == crBackDC) ) && (pbrushIn->ulPalTime() == ulDCPalTime()) && (pbrushIn->ulSurfTime() == ulSurfPalTime()) && (pbrushIn->hdevRealization() == po.hdev()) && (pbrushIn != gpbrDCBrush) && (pbrushIn != gpbrDCPen) ) { // Uncache the realization according to the realization type (solid, // driver realization, or engine realization) if (pbrushIn->bCachedIsSolid()) { // Retrieve the cached solid color and done iSolidColor = (ULONG)pbrushIn->ulRealization(); crPaletteColor = pbrushIn->crPalColor(); } else { // See whether this is an engine or driver realization PRBRUSH prbrush = (PRBRUSH)pbrushIn->ulRealization(); if (pbrushIn->bIsEngine()) { pengbrush1 = (PENGBRUSH)prbrush; } else { // Skip over the RBRUSH at the start of the DBRUSH, so that the // driver doesn't see that pvRbrush = (PVOID)(((PDBRUSH)prbrush)->aj); } // Whether this was an engine or driver realization, now we've got // it selected into another DC, so increment the reference count // so it won't get deleted until it's no longer selected into any // DC and the logical brush no longer exists prbrush->vAddRef(); // Indicate that this is a pattern brush iSolidColor = 0xffffffff; crPaletteColor = pbrushIn->crPalColor(); } // Nothing more to do once we've found that the realization is cached; // this tells us all we hoped to find out in this call, either the // solid color for the realization or else that the realization isn't // solid (in which case we probably found the realization too, although // if the cached realization is driver and this time the engine will do // the drawing, or vice-versa, the cached realization won't help us) return; } // If brush isn't based on color (if it is a bitmap or hatch), we're done // here, because all we want to do is set iSolidColor if possible if (!(flAttrs & BR_IS_SOLID)) { iSolidColor = crPaletteColor = 0xffffffff; return; } // See if we can find exactly the color we want if (bCMYKColorSolid) { // crRealize is CMKY color just set it to iSolidColor iSolidColor = crPaletteColor = crRealize; } else if (po.bCapsForceDither() && bCanDither) { // printer drivers may set the FORCEDITHER flag. In this case, we always // want to dither brushes, even if they map to a color in the drivers palette. iSolidColor = 0xffffffff; crPaletteColor = crRealize; } else { iSolidColor = ulGetMatchingIndexFromColorref( palSurf, palDC, crRealize ); crPaletteColor = rgbFromColorref(palSurf, palDC, crRealize ); } // Under CMYK color context, there is no dither. if ((iSolidColor == 0xFFFFFFFF) && (!bCMYKColorSolid)) { // Not an exact match. If we can dither, then we're done for now; we'll // realize the brush when the driver wants it, so if all conditions are // met for dithering this brush, then we're done // we dither the brush if the caller says we can and if either the brush // says it is ditherable or the driver has requested dithering. if (((flAttrs & BR_DITHER_OK) || (po.bCapsForceDither())) && bCanDither) { // ...and the PDEV allows color dithering and either the bitmap is // for a palette managed device, or if the surface and device // palettes are the same palette, or if the destination surface is // monochrome and the pdev has hooked mono dithering. // // Note: There is a dynamic mode change synchronization hole here // between the time we check GCAPS_COLOR_DITHER/MONO_DITHER // and the time that we go to actually realize the brush -- // the driver's capabilities may have changed in the mean // time. Note that this will happen only when drawing to // DIB based compatible bitmaps. Since it will be rare, and // since we won't fall over, I'm letting it through... if ( ( ( (!palSurf.bValid()) || (palSurf.ppalGet() == po.ppalSurfNotDynamic()) ) && (po.flGraphicsCapsNotDynamic() & GCAPS_COLOR_DITHER) ) || (palSurf.bIsMonochrome() && (po.flGraphicsCapsNotDynamic() & GCAPS_MONO_DITHER) ) ) { // ...then we can dither this brush, so we can't set iSolidColor // and we're done. Dithering will be done when the driver // requests realization // crPaletteColor = crRealize; return; } } // We can't dither and there's no exact match, so find the nearest // color and that'll have to do if (pSurface->iFormat() == BMF_1BPP) { // For monochrome surface, we'll have background mapped to // background and everything else mapped to foreground. iSolidColor = ulGetNearestIndexFromColorref( palSurf, palDC, crBackDC, SE_DONT_SEARCH_EXACT_FIRST ); crPaletteColor = rgbFromColorref(palSurf, palDC, crBackDC); if (crBackDC != crRealize) { iSolidColor = 1 - iSolidColor; // Obtain corresponding color from index. PAL_ULONG ulPalTemp; ulPalTemp.pal = palSurf.palentryGet(iSolidColor); crPaletteColor = ulPalTemp.ul; } } else { iSolidColor = ulGetNearestIndexFromColorref( palSurf, palDC, crRealize, SE_DONT_SEARCH_EXACT_FIRST ); crPaletteColor = rgbFromColorref(palSurf, palDC, crRealize); } } // See if we can cache this brush color in the logical brush; we can't if // another realization has already been cached in the logical brush // See vTryToCacheRealization, in BRUSHDDI.CXX, for a detailed explanation // of caching in the logical brush if ( !pbrushIn->bCacheGrabbed() ) { // Try to grab the "can cache" flag; if we don't get it, someone just // sneaked in and got it ahead of us, so we're out of luck and can't // cache if ( pbrushIn->bGrabCache() ) { // We got the "can cache" flag, so now we can cache this realization // in the logical brush // These cache ID fields must be set before crFore, because crFore // is the key that indicates when the cached realization is valid. // If crFore is -1 when the logical brush is being realized, we // just go realize the brush; if it's not -1, we check the cache ID // fields to see if we can use the cached fields. // InterlockedExchange() is used below to set crFore to make sure // the cache ID fields are set before crFore pbrushIn->crBack(crCurrentBack1); pbrushIn->ulPalTime(ulDCPalTime()); pbrushIn->ulSurfTime(ulSurfPalTime()); pbrushIn->ulRealization(iSolidColor); pbrushIn->crPalColor(crPaletteColor); pbrushIn->SetSolidRealization(); // This must be set last, because once it's set, other selections // of this logical brush will attempt to use the cached brush. The // use of InterlockedExchange in this method enforces this pbrushIn->crForeLocked(crCurrentText1); // The realization is now cached in the logical brush } } return; } /******************************Public*Routine******************************\ * EBRUSHOBJ::vNuke() * * Clean up framed EBRUSHOBJ * * History: * 20-Mar-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ VOID EBRUSHOBJ::vNuke() { if (pengbrush1 != (PENGBRUSH) NULL) { PRBRUSH prbrush = pengbrush1; // point to engine brush realization prbrush->vRemoveRef(RB_ENGINE); // decrement the reference count on the // realization and free the brush if // this is the last reference } if (pvRbrush != (PVOID) NULL) { PRBRUSH prbrush = (PDBRUSH)DBRUSHSTART(pvRbrush); // point to DBRUSH (pvRbrush points to // realization, which is at the end of DBRUSH) prbrush->vRemoveRef(RB_DRIVER); // decrement the reference count on the // realization and free the brush if // this is the last reference } } // This is the brusheng.cxx section /******************************Public*Routine******************************\ * bInitBRUSHOBJ * * Initializes the default brushes and and the dclevel default values for * brushes and pens. * * Explanation of the NULL brush (alias Hollow Brush) * The Null brush is special. Only 1 is ever created * (at initialization time in hbrNull). The only API's for * getting a Null brush are CreateBrushIndirect and GetStockObject which * both return "the 1 and only 1" Null brush. A Null brush is never * realized by a driver or the engine. No output call should ever occur * that requires a brush if the brush is NULL, the engine should stop * these before they get to the driver. * * History: * 20-May-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ extern "C" BOOL bInitBrush( int iBrush, COLORREF cr, DWORD dwHS, PULONG_PTR pdw, BOOL bEnableDither ) { BOOL bSuccess = FALSE; BRUSHMEMOBJ brmo(cr,dwHS,FALSE,FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); if (bEnableDither) brmo.vEnableDither(); if (pdw) *pdw = (ULONG_PTR)brmo.pbrush(); bSetStockObject(brmo.hbrush(),iBrush); // init DcAttrDefault brush if (iBrush == WHITE_BRUSH) { DcAttrDefault.hbrush = brmo.hbrush(); } bSuccess = TRUE; } else { #if DBG DbgPrint("couldn't create default brush %lx, %lx\n",cr,iBrush); #endif return(FALSE); } return(bSuccess); } BOOL bInitBRUSHOBJ() { if (!bInitBrush(WHITE_BRUSH,(COLORREF)RGB(0xFF,0xFF,0xFF), HS_DITHEREDCLR,(PULONG_PTR)&dclevelDefault.pbrFill,FALSE) || !bInitBrush(BLACK_BRUSH, (COLORREF)RGB(0x0, 0x0, 0x0), HS_DITHEREDCLR,NULL,FALSE) || !bInitBrush(GRAY_BRUSH, (COLORREF)RGB(0x80,0x80,0x80),HS_DITHEREDCLR,NULL,TRUE) || !bInitBrush(DKGRAY_BRUSH,(COLORREF)RGB(0x40,0x40,0x40),HS_DITHEREDCLR,NULL,TRUE) || !bInitBrush(LTGRAY_BRUSH,(COLORREF)RGB(0xc0,0xc0,0xc0),HS_DITHEREDCLR,NULL,TRUE) || !bInitBrush(NULL_BRUSH, (COLORREF)0,HS_NULL,(PULONG_PTR)&gpbrNull,FALSE)) { return(FALSE); } // Init default Null Pen { BRUSHMEMOBJ brmo((COLORREF) 0, HS_NULL, TRUE, FALSE); // TRUE signifies a pen if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); brmo.vSetOldStylePen(); brmo.flStylePen(PS_NULL); brmo.lWidthPen(1); HmgModifyHandleType((HOBJ)MODIFY_HMGR_TYPE(brmo.hbrush(),LO_PEN_TYPE)); bSetStockObject(brmo.hbrush(),NULL_PEN); gpPenNull = (PPEN)brmo.pbrush(); } else { WARNING("Failed Null Pen"); return(FALSE); } } // Init default Black Pen { BRUSHMEMOBJ brmo((COLORREF) (RGB(0,0,0)), HS_DITHEREDCLR, TRUE, FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); brmo.vSetOldStylePen(); brmo.flStylePen(PS_SOLID); brmo.lWidthPen(0); brmo.l_eWidthPen(IEEE_0_0F); brmo.iJoin(JOIN_ROUND); brmo.iEndCap(ENDCAP_ROUND); brmo.pstyle((PFLOAT_LONG) NULL); HmgModifyHandleType((HOBJ)MODIFY_HMGR_TYPE(brmo.hbrush(),LO_PEN_TYPE)); bSetStockObject(brmo.hbrush(),BLACK_PEN); DcAttrDefault.hpen = (HPEN)brmo.hbrush(); dclevelDefault.pbrLine = brmo.pbrush(); } else { WARNING("failed black pen"); return(FALSE); } } // Init default White Pen { BRUSHMEMOBJ brmo((COLORREF) (RGB(0xFF,0xFF,0xFF)), HS_DITHEREDCLR, TRUE, FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); brmo.vSetOldStylePen(); brmo.flStylePen(PS_SOLID); brmo.lWidthPen(0); brmo.l_eWidthPen(IEEE_0_0F); brmo.iJoin(JOIN_ROUND); brmo.iEndCap(ENDCAP_ROUND); brmo.pstyle((PFLOAT_LONG) NULL); HmgModifyHandleType((HOBJ)MODIFY_HMGR_TYPE(brmo.hbrush(),LO_PEN_TYPE)); bSetStockObject(brmo.hbrush(),WHITE_PEN); } else { WARNING("Failed white pen"); return(FALSE); } } // Init the stock DC Pen { BRUSHMEMOBJ brmo((COLORREF) (RGB(0,0,0)), HS_DITHEREDCLR, TRUE, FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); brmo.vSetOldStylePen(); brmo.flStylePen(PS_SOLID); brmo.lWidthPen(0); brmo.l_eWidthPen(IEEE_0_0F); brmo.iJoin(JOIN_ROUND); brmo.iEndCap(ENDCAP_ROUND); brmo.pstyle((PFLOAT_LONG) NULL); HmgModifyHandleType((HOBJ)MODIFY_HMGR_TYPE(brmo.hbrush(),LO_PEN_TYPE)); bSetStockObject(brmo.hbrush(),DC_PEN); ghbrDCPen = brmo.hbrush(); gpbrDCPen = brmo.pbrush(); } else { WARNING("Failed DC pen"); return(FALSE); } } // init the text brush { BRUSHMEMOBJ brmo((COLORREF) (RGB(0,0,0)),HS_DITHEREDTEXTCLR,FALSE,FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); ghbrText = brmo.hbrush(); gpbrText = brmo.pbrush(); } else { WARNING("Could not create default text brush"); return(FALSE); } } // init the background brush { BRUSHMEMOBJ brmo((COLORREF) (RGB(0xff,0xff,0xff)),HS_DITHEREDBKCLR,FALSE,FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); ghbrBackground = brmo.hbrush(); gpbrBackground = brmo.pbrush(); } else { WARNING("Could not create default background brush"); return(FALSE); } } // init the global pattern gray brush { static WORD patGray[8] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }; HBITMAP hbmGray; hbmGray = GreCreateBitmap(8, 8, 1, 1, (LPBYTE)patGray); if (hbmGray == (HBITMAP) 0) { WARNING1("bInitBRUSHOBJ failed GreCreateBitmap\n"); return(FALSE); } ghbrGrayPattern = GreCreatePatternBrush(hbmGray); if (ghbrGrayPattern == (HBRUSH) 0) { WARNING1("bInitBRUSHOBJ failed GreCreatePatternBrush\n"); return(FALSE); } GreDeleteObject(hbmGray); GreSetBrushOwnerPublic((HBRUSH)ghbrGrayPattern); } // init the stock DC brush { BRUSHMEMOBJ brmo((COLORREF) (RGB(0xff,0xff,0xff)),HS_DITHEREDCLR,FALSE,FALSE); if (brmo.bValid()) { brmo.vKeepIt(); brmo.vGlobal(); bSetStockObject(brmo.hbrush(), DC_BRUSH); ghbrDCBrush = brmo.hbrush(); gpbrDCBrush = brmo.pbrush(); } else { WARNING("Could not create direct dc brush"); return(FALSE); } } return(TRUE); } /******************************Public*Routine******************************\ * GreSelectBrush * * Selects the given brush into the given DC. Fast SelectObject * * History: * Thu 21-Oct-1993 -by- Patrick Haluptzok [patrickh] * wrote it. \**************************************************************************/ HBRUSH GreSelectBrush(HDC hdc, HBRUSH hbrush) { HBRUSH hbrReturn = (HBRUSH) 0; // // Try to lock the DC. If we fail, we just return failure. // XDCOBJ dco(hdc); if (dco.bValid()) { // // call DC locked version // hbrReturn = GreDCSelectBrush(dco.pdc,hbrush); dco.vUnlockFast(); } return(hbrReturn); } /******************************Public*Routine******************************\ * GreDCSelectBrush * * Select brush with dc already locked * * Arguments: * * pdc - locked DC pointer * hbrush - brush to select * * Return Value: * * Old hbrush or NULL * * History: * * 19-May-1995 : copied from GreSelectBrush * \**************************************************************************/ HBRUSH GreDCSelectBrush( PDC pdc, HBRUSH hbrush ) { HBRUSH hbrReturn = (HBRUSH) 0; PBRUSH pbrush = NULL; XDCOBJ dco; dco.pdc = pdc; if (dco.bValid()) { HBRUSH hbrOld; // // The DC is locked. Set the return value to the old brush in the DC. // hbrOld = (HBRUSH) (dco.pdc->pbrushFill())->hGet(); // // the return value should be the one cached in the dc // hbrReturn = dco.pdc->hbrush(); // // If the new brush is the same as the old brush, nothing to do. // if (DIFFHANDLE(hbrush,hbrOld)) { // // Try to lock down the logical brush so we can get the pointer out. // pbrush = (BRUSH *)HmgShareCheckLock((HOBJ)hbrush, BRUSH_TYPE); if (pbrush) { // // Undo the lock from when the brush was selected. // DEC_SHARE_REF_CNT_LAZY0(dco.pdc->pbrushFill()); // // Changing pbrushfill, set flag to force re-realization // dco.ulDirtyAdd(DIRTY_FILL); // // Save the pointer to the logical brush in the DC. We don't // unlock the logical brush, because the alt lock count in the // logical brush is the reference count for DCs in which the brush // is currently selected; this protects us from having the actual // logical brush deleted while it's selected into a DC, and allows // us to reference the brush with a pointer rather than having to // lock down the logical brush every time. // dco.pdc->pbrushFill(pbrush); } else { WARNING1("SelectBrush got invalid brush handle\n"); hbrReturn = NULL; } } else { pbrush = dco.pdc->pbrushFill(); } if (pbrush != NULL) { if (hbrReturn != NULL) { // // must still check brush for new color // PBRUSHATTR pUser = pbrush->_pBrushattr; // // if the brush handle is a cached solid brush, // call GreSetSolidBrushInternal to change the color // if (pUser != &pbrush->_Brushattr) { if (pUser->AttrFlags & ATTR_NEW_COLOR) { // // force re-realization in case handle was same // dco.ulDirtyAdd(DIRTY_FILL); // // set the new color for the cached brush. // Note: since pbrush is pulled straight // from the DC, it's reference count will // be 1, which is needed by SetSolidBrush // if (!GreSetSolidBrushLight(pbrush,pUser->lbColor,FALSE)) { WARNING1("GreSyncbrush failed to setsolidbrushiternal\n"); } pUser->AttrFlags &= ~ATTR_NEW_COLOR; } } } dco.pdc->hbrush(hbrush); dco.pdc->ulDirtySub(DC_BRUSH_DIRTY); } } return(hbrReturn); } /******************************Public*Routine******************************\ * GreDCSelectPen * Selects a hpen into the given pdc * * Arguments: * * pdc - locked dc * hpen - hpen to select * * Return Value: * * old hpen * * History: * * 29-Jan-1996 -by- Mark Enstrom [marke] * \**************************************************************************/ HPEN GreDCSelectPen( PDC pdc, HPEN hpen ) { HPEN hpReturn = (HPEN) NULL; PPEN ppen = NULL; // // Try to lock the DC. If we fail, we just return failure. // XDCOBJ dco; dco.pdc = pdc; if (dco.bValid()) { HPEN hpOld; BOOL bRealize = FALSE; // // hpOld is the pen (LINE BRUSH) currently realized in the DC // hpOld = (HPEN) (dco.pdc->pbrushLine())->hGet(); // // Set the return value to the old pen in the DCATTR. // This is the last hpen the user selected // hpReturn = (HPEN)dco.pdc->hpen(); // // If the new pen is the same as the old pen, nothing to do. // if (DIFFHANDLE(hpen, hpOld)) { // // Try to lock down the logical brush so we can get the pointer out. // ppen = (PEN *)HmgShareCheckLock((HOBJ)hpen, BRUSH_TYPE); if (ppen && ppen->bIsPen()) { // // Undo the lock from when the pen was selected. // DEC_SHARE_REF_CNT_LAZY0(dco.pdc->pbrushLine()); // // Mark line relization is invalid. // dco.ulDirtyAdd(DIRTY_LINE); // // Save the pointer to the logical brush in the DC. We don't // unlock the logical brush, because the alt lock count in the // logical brush is the reference count for DCs in which the brush // is currently selected; this protects us from having the actual // logical brush deleted while it's selected into a DC, and allows // us to reference the brush with a pointer rather than having to // lock down the logical brush every time. // dco.pdc->pbrushLine(ppen); // // The pen changed, so realize the new LINEATTRS, based on // the current world transform. // bRealize = TRUE; } else { WARNING1("SelectPen got invalid pen handle\n"); // // If this is not pen, can't select it as pen. // if (ppen) { DEC_SHARE_REF_CNT(ppen); ppen = NULL; } hpReturn = NULL; } } else { ppen = (PPEN)dco.pdc->pbrushLine(); } // // In case the handle stays the same, but the pen is new // due to re-use of a user hpen, so re-realize it. // if (ppen != (PPEN) NULL) { if (hpReturn != NULL) { PBRUSHATTR pUser = ppen->_pBrushattr; // // if the brush handle is a cached solid brush, // call GreSetSolidBrushInternal to change the color // if (pUser != &ppen->_Brushattr) { if (pUser->AttrFlags & ATTR_NEW_COLOR) { // // set the new color for the cached brush. // Note: since pbrush is pulled straight // from the DC, it's reference count will // be 1, which is needed by SetSolidBrush // if (!GreSetSolidBrushLight(ppen,pUser->lbColor,TRUE)) { WARNING ("GreDCSelectPen failed to setsolidbrushiternal\n"); } dco.ulDirtyAdd(DIRTY_LINE); pUser->AttrFlags &= ~ATTR_NEW_COLOR; bRealize = TRUE; } } if (bRealize) { // // The pen changed, so realize the new LINEATTRS, based on // the current world transform. // EXFORMOBJ exo(dco, WORLD_TO_DEVICE); dco.pdc->vRealizeLineAttrs(exo); LINEATTRS *pla = dco.plaRealized(); } } dco.pdc->hpen(hpen); dco.pdc->ulDirtySub(DC_PEN_DIRTY); } } return(hpReturn); } /******************************Public*Routine******************************\ * GreSelectPen * * Selects the given brush into the given DC. Fast SelectObject * * History: * Thu 21-Oct-1993 -by- Patrick Haluptzok [patrickh] * wrote it. \**************************************************************************/ HPEN GreSelectPen(HDC hdc, HPEN hpen) { HPEN hpenReturn = (HPEN) 0; // // Try to lock the DC. If we fail, we just return failure. // XDCOBJ dco(hdc); if (dco.bValid()) { // // call DC locked version // hpenReturn = GreDCSelectPen(dco.pdc,hpen); dco.vUnlockFast(); } return(hpenReturn); } /******************************Public*Routine******************************\ * bDeleteBrush * * This will delete the brush. The brush can only be deleted if it's not * global and not being used by anyone else. * * History: * * 7-Feb-1996 -by- Mark Enstrom [marke] * Add PEB caching for brush and pen objects * 23-May-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ BOOL bDeleteBrush(HBRUSH hbrush, BOOL bCleanup) { PBRUSHPEN pbp; BOOL bReturn = TRUE; HBRUSH hbrushNew; BOOL bDelete = TRUE; PBRUSHATTR pUser = NULL; PENTRY pentTmp; BOOL bMakeNonStock = FALSE; // // check handle for user-mode brush // if (!bCleanup) { HANDLELOCK BrushLock; BrushLock.bLockHobj((HOBJ)hbrush,BRUSH_TYPE); if (BrushLock.bValid()) { POBJ pobj = BrushLock.pObj(); pUser = (PBRUSHATTR)BrushLock.pUser(); ASSERTGDI(pobj->cExclusiveLock == 0, "deletebrush - exclusive lock not 0\n"); // // If Brush is still in use, mark for lazy deletion and return true // if (BrushLock.ShareCount() > 0) { ((PBRUSH)pobj)->AttrFlags(ATTR_TO_BE_DELETED); bDelete = FALSE; } else if (pUser != (PBRUSHATTR)NULL) { if (!(pUser->AttrFlags & ATTR_CACHED)) { BOOL bPen = ((PPEN)pobj)->bIsPen(); INT iType = LO_TYPE(hbrush); #if DBG // // make sure handle type agrees with bPen // if (bPen) { ASSERTGDI(((iType == LO_PEN_TYPE) || (iType == LO_EXTPEN_TYPE)), "bDeleteBrush Error: PEN TYPE NOT LO_PEN OR LO_EXTPEN"); } else { ASSERTGDI((iType == LO_BRUSH_TYPE), "bDeleteBrush error: BRUSH type not LO_BRUSH_TYPE"); } #endif // // try to cache the solid brush // don't cache LO_EXTPEN_TYPE // if ((((PBRUSH)pobj)->flAttrs() & BR_IS_SOLID) && (!bPen || iType != LO_EXTPEN_TYPE)) { BOOL bStatus; bStatus = bPEBCacheHandle(hbrush,bPen?PenHandle:BrushHandle,(POBJECTATTR)pUser,(PENTRY)BrushLock.pentry()); if (bStatus) { bDelete = FALSE; } } } else { // // brush is already cached // WARNING1("Trying to delete brush marked as cached\n"); bDelete = FALSE; } } if (bDelete) { if (bMakeNonStock = ((PBRUSH)pobj)->bIsMakeNonStock()) { ((PBRUSH)pobj)->vClearMakeNonStock(); } } BrushLock.vUnlock(); } } if (bDelete) { if (bMakeNonStock) { STOCKINFO("Brush(%p) is marked MakeNonStock. Doing it\n", hbrush); if(!GreMakeBrushNonStock(hbrush)) STOCKWARNING("GreMakeBrushNonStock (%p) failed\n", hbrush); } // // Try and remove handle from Hmgr. This will fail if the brush // is locked down on any threads or if it has been marked global // or undeletable. // pbp.pbr = (PBRUSH) HmgRemoveObject((HOBJ)hbrush, 0, 0, FALSE, BRUSH_TYPE); if (pbp.pbr!= NULL) { // // Free the style array memory if there is some and it's // not pointing to our stock default styles: // if (pbp.pbr->bIsPen()) { if ((pbp.ppen->pstyle() != (PFLOAT_LONG) NULL) && !pbp.ppen->bIsDefaultStyle()) { // // We don't set the field to NULL since this brush // is on it's way out. // VFREEMEM(pbp.ppen->pstyle()); } } // // Free the bitmap pattern in the brush. // if (pbp.pbr->hbmPattern()) { // // We don't set the field to NULL since this brush // is on it's way out. // BOOL bTemp = bDeleteSurface((HSURF)pbp.pbr->hbmPattern()); ASSERTGDI(bTemp, "ERROR How could pattern brush failed deletion?"); } // // Un-reference count the realization cached in the logical brush, // if any. We don't have to worry about anyone else being in the // middle of trying to cache a realization for this brush because // we have removed it from Hmgr and noone else has it locked down. // We only have to do this if there is a cached realization and the // brush is non-solid. // if ((pbp.pbr->crFore() != BO_NOTCACHED) && !pbp.pbr->bCachedIsSolid()) { ASSERTGDI(pbp.pbr->ulRealization() != NULL, "ERROR ulRealization() is NULL"); ((PRBRUSH)pbp.pbr->ulRealization())->vRemoveRef( pbp.pbr->bIsEngine() ? RB_ENGINE : RB_DRIVER); } // // Brush is DIB pattern ? if so we may need to free ICM DIBs // if (pbp.pbr->flAttrs() & BR_IS_DIB) { // // Free cached color translated ICM DIBs. // pbp.pbr->vDeleteIcmDIBs(); } FREEOBJ(pbp.pbr, BRUSH_TYPE); // // free pUser // if (!bCleanup && (pUser != (PBRUSHATTR)NULL)) { HmgFreeObjectAttr((POBJECTATTR)pUser); } } else { // // Under Win31 deleting stock objects returns True. // BRUSHSELOBJ bo(hbrush); if (!bo.bValid() || !bo.bIsGlobal()) bReturn = FALSE; } } return(bReturn); } /******************************Public*Routine******************************\ * GreSetBrushGlobal * * Sets the brush to be a global one. * \**************************************************************************/ void GreSetBrushGlobal(HBRUSH hbr) { BRUSHSELOBJ ebo(hbr); if (ebo.bValid()) { ebo.vGlobal(); } } /******************************Public*Routine******************************\ * GreMakeBrushStock * * Make the brush a stock brush. * \**************************************************************************/ HBRUSH GreMakeBrushStock(HBRUSH hbr) { BRUSHSELOBJAPI ebo(hbr); HANDLE bRet = 0; BOOL bHandleModified = TRUE; // Can make the brush a stock brush only when: // (1) It is valid // (2) Its not global already (i.e already stock) // (3) Its not a DIBSection based brush. // (4) Its not selected into any DC already. if (!ebo.bValid() || ebo.bIsGlobal() || ebo.pbrush()->cShareLockGet() > 0) { STOCKWARNING("GreMakeBrushStock (%p) invalid/global/selected\n",hbr); return (HBRUSH)0; } bRet = (HANDLE)((ULONG_PTR)hbr | GDISTOCKOBJ); if (InterlockedDecrement(&gStockBrushFree) >= 0 && GreSetBrushOwner((HBRUSH)hbr,OBJECT_OWNER_PUBLIC) && (bHandleModified = HmgLockAndModifyHandleType((HOBJ)bRet))) { ebo.pbrush()->flAttrs(ebo.pbrush()->flAttrs() | BR_IS_GLOBAL); return (HBRUSH)bRet; } else { if (!bHandleModified) GreSetBrushOwner((HBRUSH)hbr,OBJECT_OWNER_CURRENT); STOCKWARNING("GreMakeBrushStock (%p) Count/GreSetBrushOwner/ModifyH failed\n",bRet); InterlockedIncrement(&gStockBrushFree); bRet = 0; } return (HBRUSH)bRet; } /******************************Public*Routine******************************\ * GreMakeBrushNonStock * * Makes the brush a non stock brush * \**************************************************************************/ HBRUSH GreMakeBrushNonStock(HBRUSH hbr) { HANDLE bRet = 0; BRUSHSELOBJAPI ebo(hbr); // Can make a stock brush non stock only when // (1) It is valid // (2) It is a global brush // (3) It is not a Fixed stock brush if (ebo.bValid() && ebo.bIsGlobal() && !ebo.pbrush()->bIsFixedStock()) { bRet = (HANDLE)((ULONG_PTR)hbr & ~GDISTOCKOBJ); if (ebo.pbrush()->cShareLockGet() > 0) { // The brush has more than one share lock. This means it will need // to be lazy deleted. ebo.pbrush()->vSetMakeNonStock(); STOCKINFO("GreMakeBrushNonStock (%p) is selected. Delay it\n", hbr); } else if(HmgLockAndModifyHandleType((HOBJ)bRet)) { ebo.pbrush()->flAttrs(ebo.pbrush()->flAttrs() & ~BR_IS_GLOBAL); if(!GreSetBrushOwner((HBRUSH)bRet,OBJECT_OWNER_CURRENT)) { bRet = 0; STOCKWARNING("GreMakeBrushNonStock (%p) GreSetBrushOwner failed\n", bRet); } else { InterlockedIncrement(&gStockBrushFree); } } else { bRet = 0; } } return (HBRUSH)bRet; } /******************************Public*Routine******************************\ * GreSetBrushOwner * * Sets the brush owner. * \**************************************************************************/ BOOL GreSetBrushOwner( HBRUSH hbr, W32PID lPid ) { // If it is a global brush which by design are public we dont need to // do anything. { BRUSHSELOBJ ebo(hbr); if (ebo.bValid()) { if (ebo.bIsGlobal()) return TRUE; } } BOOL bStatus = FALSE; PBRUSHATTR pBrushattr = NULL; PENTRY pentry; UINT uiIndex = (UINT) HmgIfromH(hbr); if (uiIndex < gcMaxHmgr) { pentry = &gpentHmgr[uiIndex]; if (lPid == OBJECT_OWNER_CURRENT) { pBrushattr = (PBRUSHATTR)HmgAllocateObjectAttr(); } // // Accquire handle lock. Don't check PID here because owner could // be NONE, not PUBLIC // HANDLELOCK HandleLock(pentry, FALSE); if (HandleLock.bValid()) { POBJ pobj = pentry->einfo.pobj; if ((pentry->Objt == BRUSH_TYPE) && (pentry->FullUnique== HmgUfromH(hbr))) { PBRUSH pBrush = (PBRUSH)pobj; if ((pobj->cExclusiveLock == 0) || (pobj->Tid == (PW32THREAD)PsGetCurrentThread())) { // // Handle is locked. It is illegal to accquire the hmgr // resource when a handle is locked. // if ((lPid == OBJECT_OWNER_NONE) || (lPid == OBJECT_OWNER_PUBLIC)) { // // free PBRUSHATTR if PID matches current process // transfer brushattributes to kernel mode. if (HandleLock.Pid() == W32GetCurrentPID()) { // // If user mode BRUSHATTR is allocated for this // brush // if (pBrush->pBrushattr() != &pBrush->_Brushattr) { // Copy pBrushattr() to _BrushAttr pBrush->_Brushattr = *(pBrush->pBrushattr()); // Free BRUSHATTR at bottom of function pBrushattr = pBrush->pBrushattr(); // Set pBrushAttr to point to internal pBrush->pBrushattr(&pBrush->_Brushattr); // Clear entry pentry->pUser = NULL; } // Set Brush owner to NONE or PUBLIC HandleLock.Pid(lPid); // dec process handle count HmgDecProcessHandleCount(W32GetCurrentPID()); bStatus = TRUE; } else if (HandleLock.Pid() == OBJECT_OWNER_NONE) { // Allow to set from NONE to PUBLIC or NONE HandleLock.Pid(lPid); bStatus = TRUE; } // // Move bitmap owner if needed. No need to do this if // we are doing OBJECT_OWNER_NONE. // if (bStatus && lPid == OBJECT_OWNER_PUBLIC) { if (pBrush->hbmPattern() != (HBITMAP)NULL) { GreSetBitmapOwner(pBrush->hbmPattern(), OBJECT_OWNER_PUBLIC); } } } else if (lPid == OBJECT_OWNER_CURRENT) { // // can only set to OBJECT_OWNER_CURRENT if Brush is // not owned, or already owned by current pid. // lPid = W32GetCurrentPID(); if (HandleLock.Pid() == lPid || HandleLock.Pid() == OBJECT_OWNER_NONE || HandleLock.Pid() == OBJECT_OWNER_PUBLIC) { BOOL bIncHandleCount = FALSE; bStatus = TRUE; // only inc handle count if assigning new pid if (HandleLock.Pid() != lPid) { // dont check quota for Brushes. ??? if (HmgIncProcessHandleCount(lPid,BRUSH_TYPE)) bIncHandleCount = TRUE; } // // Check if user object already allocated for this // handle // if (pentry->pUser == NULL) { if (pBrushattr != NULL) { // Set BrushAttr pointer pBrush->pBrushattr(pBrushattr); // Set pUser in ENTRY pentry->pUser = pBrushattr; // copy clean brush attrs *pBrushattr = pBrush->_Brushattr; // Set pBrushattr to NULL so it is not freed pBrushattr = NULL; } else { WARNING("GreSetBrushOwner failed - No BRUSHATTR available"); bStatus = FALSE; // reduce handle quota count if (bIncHandleCount) HmgDecProcessHandleCount(lPid); } } if (bStatus) { // Set new owner HandleLock.Pid(lPid); // // Move bitmap owner if needed. // if (pBrush->hbmPattern() != (HBITMAP)NULL) { GreSetBitmapOwner(pBrush->hbmPattern(), OBJECT_OWNER_CURRENT); } } } else { WARNING("GreSetBrushOwner failed, trying to set directly from one PID to another"); } } else { WARNING("GreSetBrushOwner failed, bad lPid"); } } else { WARNING("GreSetBrushOwner failed - Handle is exclusivly locked"); } } else { WARNING("GreSetBrushOwner failed - bad unique or object type"); } HandleLock.vUnlock(); } } else { WARNING("GtreSetBrushOwner failed - invalid handle index\n"); } // free pBrushattr if needed if (pBrushattr) { HmgFreeObjectAttr((POBJECTATTR)pBrushattr); } return(bStatus); } /******************************Public*Routine******************************\ * vFreeOrCacheRbrush * * Either frees the current RBRUSH (the one pointed to by the this pointer) or * puts it in the 1-deep RBRUSH cache, if the cache is empty. * * History: * * 30-Sep-1996 -by- Tom Zakrajsek [tomzak] * Fixed it for multi brushes (DDML). * * 14-Dec-1993 -by- Michael Abrash [mikeab] * Wrote it. \**************************************************************************/ VOID MulDestroyBrushInternal(VOID*); VOID RBRUSH::vFreeOrCacheRBrush(RBTYPE rbtype) { // // If RBRUSH is for UMPD, just free it // if (!IS_SYSTEM_ADDRESS(this)) { ASSERTGDI(bUMPDRBrush(),"RBRUSH::vFreeOrCacheRBrush UserMode brush does not have bUMPDBrush() bit on\n"); EngFreeUserMem(this); return; } PRBRUSH *pprbrush; // The bMultiBrush check is only valid for DRIVER realizations. // Otherwise, it is assumed false. BOOL bMulti = FALSE; if (rbtype == RB_DRIVER) { pprbrush = &gpCachedDbrush; bMulti = bMultiBrush(); if (bMulti) { PVOID pvRbrush = (PVOID)(((PDBRUSH)this)->aj); MulDestroyBrushInternal(pvRbrush); } } else { pprbrush = &gpCachedEngbrush; } // If there's already a cached RBRUSH, or this is a DDML brush // just free this. if ((*pprbrush != NULL) || (bMulti == TRUE)) { VFREEMEM(this); } else { PRBRUSH pOldRbrush; // There's no cached RBRUSH, and it's not a DDML brush, // so cache this one. if ((pOldRbrush = (PRBRUSH) InterlockedExchangePointer((PVOID *)pprbrush, this)) != NULL) { // Before we could cache this one, someone else cached another one, // which we just acquired responsibility for, so free it. VFREEMEM(pOldRbrush); } } } /******************************Public*Routine******************************\ * BRUSH::hFindIcmDIB * * Search ICM DIBs associated with brush until a match is found * * Arguments: * * Return Value: * * History: * * 9/25/1996 Mark Enstrom [marke] * \**************************************************************************/ HBITMAP BRUSH::hFindIcmDIB(HANDLE hcmXform) { ICMMSG(("hFindIcmDIB: FIND ICM DIB \n")); if (hcmXform == NULL) { return _hbmPattern; } else { GreAcquireFastMutex(ghfmMemory); PICM_DIB_LIST pDIBList = pIcmDIBList(); while (pDIBList != NULL) { if (pDIBList->hcmXform == hcmXform) { GreReleaseFastMutex(ghfmMemory); return(pDIBList->hDIB); } pDIBList = pDIBList->pNext; } GreReleaseFastMutex(ghfmMemory); return(NULL); } } BOOL BRUSH::bAddIcmDIB(HANDLE hcmXform,HBITMAP hDIB) { ICMMSG(("bAddIcmDIB: ADD ICM DIB \n")); BOOL bRet = FALSE; // // Check current hcmform is not on the list. // if (hFindIcmDIB(hcmXform)) { ICMMSG(("bAddIcmDIB(): The DIB for hcmXform is exist\n")); // // hcmXform is exist, // // Do we need to do delete existing one and insert new one ?? // return (FALSE); } SURFREF SurfDIB((HSURF) hDIB); if (SurfDIB.bValid()) { PICM_DIB_LIST pDIBList = (PICM_DIB_LIST)PALLOCNOZ(sizeof(ICM_DIB_LIST),'ldbG'); if (pDIBList) { // // Inc. ref. count // { SURFACE *ps = SurfDIB.ps; ps->vInc_cRef(); } // // Fill DIBList cell. // pDIBList->hcmXform = hcmXform; pDIBList->hDIB = hDIB; pDIBList->pNext = pIcmDIBList(); // // Updates list. // GreAcquireFastMutex(ghfmMemory); pIcmDIBList(pDIBList); GreReleaseFastMutex(ghfmMemory); bRet = TRUE; } else { bRet = FALSE; } } return(bRet); } VOID BRUSH::vDeleteIcmDIBs(VOID) { ICMMSG(("vDeleteIcmDIBs: Free ICM DIB \n")); PICM_DIB_LIST pDIBList = pIcmDIBList(); GreAcquireFastMutex(ghfmMemory); while (pDIBList != NULL) { PICM_DIB_LIST pNext = pDIBList->pNext; HSURF hSurf = (HSURF) pDIBList->hDIB; BOOL bValidSurface = FALSE; // // Dec. ref count, before deleting // { SURFREF SurfDIB(hSurf); if (SurfDIB.bValid()) { SURFACE *ps = SurfDIB.ps; ps->vDec_cRef(); bValidSurface = TRUE; } } if (bValidSurface) { // // Delete ICM-ed DIB surface. // if (!bDeleteSurface((HSURF)pDIBList->hDIB)) { ICMMSG(("vDeleteICMDIBs(): bDeleteSurface is failed\n")); } } else { ICMMSG(("vDeleteICMDIBs(): Invalid surface\n")); } // // free the cell. // VFREEMEM(pDIBList); pDIBList = pNext; } GreReleaseFastMutex(ghfmMemory); }