/******************************Module*Header*******************************\ * Module Name: rgngdi.cxx * * GDI Region calls * * Created: 30-Aug-1990 10:21:11 * Author: Donald Sidoroff [donalds] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/ #include "precomp.hxx" extern PBRUSH gpbrNull; #if DBG extern ULONG dbgrgn; #endif // Tracing defines for traces that should ignore class #define GDITE_GreCreatePolyPolygonRgnInternal_return \ (GDITE_GreCreatePolyPolygonRgnInternal|GDITF_IGNORE_CLASS) #define GDITE_GreCreateRectRgn_return \ (GDITE_GreCreateRectRgn|GDITF_IGNORE_CLASS) #define GDITE_GreCreateRectRgnIndirect_return \ (GDITE_GreCreateRectRgnIndirect|GDITF_IGNORE_CLASS) #define GDITE_GreExtCreateRegion_return \ (GDITE_GreExtCreateRegion|GDITF_IGNORE_CLASS) #define GDITE_NtGdiCreateEllipticRgn_return \ (GDITE_NtGdiCreateEllipticRgn|GDITF_IGNORE_CLASS) #define GDITE_NtGdiCreateRectRgn_return \ (GDITE_NtGdiCreateRectRgn|GDITF_IGNORE_CLASS) #define GDITE_NtGdiCreateRoundRectRgn_return \ (GDITE_NtGdiCreateRoundRectRgn|GDITF_IGNORE_CLASS) /******************************Public*Routine******************************\ * BOOL bDeleteRegion(HRGN) * * Delete the specified region * * History: * 17-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ BOOL bDeleteRegion(HRGN hrgn) { RGNLOG rl(hrgn,NULL,"bDeleteRegion",0,0,0); RGNOBJAPI ro(hrgn,FALSE); return(ro.bValid() && (ro.cGet_cRefs() == 0) && ro.bDeleteRGNOBJAPI()); } /***************************Exported*Routine****************************\ * BOOL GreSetRegionOwner(hrgn,lPid) * * Assigns a new owner to the given region. This function should be as * fast as possible so that the USER component can call it often without * concern for performance! * \***********************************************************************/ BOOL GreSetRegionOwner( HRGN hrgn, W32PID lPid) { RGNLOG rl(hrgn,NULL,"GreSetRegionOwner",W32GetCurrentPID(),0,0); // // Setting a region to public, the region must not have // a user mode component // #if DBG RGNOBJAPI ro(hrgn,TRUE); if (ro.bValid()) { if (PENTRY_FROM_POBJ(ro.prgn)->pUser != NULL) { RIP("Error: setting region public that has user component"); } } #endif // // Get the current PID. // if (lPid == OBJECT_OWNER_CURRENT) { lPid = W32GetCurrentPID(); } return HmgSetOwner((HOBJ)hrgn, lPid, RGN_TYPE); } /******************************Public*Routine******************************\ * CLIPOBJ *EngCreateClip() * * Create a long live clipping object for a driver * * History: * 22-Sep-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ CLIPOBJ *EngCreateClip() { // // Note that we intentionally zero this memory on allocation. Even though // we're going to set some of these fields to non-zero values right away, // this is not a performance-critical function (a driver typically calls // this only once), and we save a lot of instruction bytes by not having to // zero a number of fields explicitly. // VOID *pv = EngAllocMem(FL_ZERO_MEMORY, sizeof(ECLIPOBJ) + SINGLE_REGION_SIZE, 'vrdG'); if (pv != NULL) { // // Make this a CLIPOBJ that doesn't clip anything. // ((ECLIPOBJ *) pv)->iDComplexity = DC_TRIVIAL; ((ECLIPOBJ *) pv)->iFComplexity = FC_RECT; ((ECLIPOBJ *) pv)->iMode = TC_RECTANGLES; REGION *prgn = (REGION*)((PBYTE)pv + sizeof(ECLIPOBJ)); ((ECLIPOBJ *) pv)->prgn = prgn; RGNOBJ ro(prgn); RECTL r; r.left = r.top = MIN_REGION_COORD; r.right = r.bottom = MAX_REGION_COORD; ro.vSet(&r); } return((CLIPOBJ *)pv); } /******************************Public*Routine******************************\ * VOID EngDeleteClip() * * Delete a long live clipping object for a driver * * History: * 22-Sep-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/ VOID EngDeleteClip(CLIPOBJ *pco) { if (pco == NULL) { WARNING("Driver calling to free NULL clipobj"); } else { ASSERTGDI(pco->iUniq == 0, "Non-zero iUniq\n"); } // // We call EngFreeMem since some drivers like to free non-existant // Clip Objects. // EngFreeMem((PVOID)pco); } /******************************Public*Routine******************************\ * ASSERTDEVLOCK() * * This validates that the thread using the DC has the devlock as well. * * History: * 24-Jan-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ #if DBG VOID ASSERTDEVLOCK(PDC pdc) { return; if (pdc->fs() & DC_SYNCHRONIZEACCESS) { ASSERTGDI(GreIsSemaphoreOwnedByCurrentThread(pdc->hsemDcDevLock_), "ASSERTDEVLOCK: wrong id\n"); } } #endif /******************************Public*Routine******************************\ * LONG GreCombineRgn(hrgnTrg,hrgnSrc1,hrgnSrc2,iMode) * * Combine the two source regions by the given mode. The result is placed * in the target. Note that either (or both sources) may be the same as * the target. * \**************************************************************************/ int APIENTRY GreCombineRgn( HRGN hrgnTrg, HRGN hrgnSrc1, HRGN hrgnSrc2, int iMode) { GDITraceHandle3(GreCombineRgn, "(%X, %X, %X, %d)\n", (va_list)&hrgnTrg, hrgnTrg, hrgnSrc1, hrgnSrc2); RGNLOG rl(hrgnTrg,NULL,"GreCombineRgn",(ULONG_PTR)hrgnSrc1,(ULONG_PTR)hrgnSrc2,iMode); LONG Status; if ((iMode < RGN_MIN) || (iMode > RGN_MAX)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return ERROR; } // // Check if a simple copy is to be performed. // if (iMode == RGN_COPY) { RGNOBJAPI roTrg(hrgnTrg,FALSE); RGNOBJAPI roSrc1(hrgnSrc1,TRUE); // // if either of these regions have a client rectangle, then set the // km region // if (!roTrg.bValid() || !roSrc1.bValid() || !roTrg.bCopy(roSrc1)) { if (!roSrc1.bValid() || !roTrg.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } Status = ERROR; } else { Status = roTrg.iComplexity(); } } else if (SAMEHANDLE(hrgnTrg, hrgnSrc1) || SAMEHANDLE(hrgnTrg, hrgnSrc2)) { // Two of the handles are the same. Check to determine if all three // handles are the same. if (SAMEHANDLE(hrgnSrc1, hrgnSrc2)) { RGNOBJAPI roTrg(hrgnTrg,FALSE); if (!roTrg.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); Status = ERROR; } else { if ((iMode == RGN_DIFF) || (iMode == RGN_XOR)) { roTrg.vSet(); } Status = roTrg.iComplexity(); } } else { // // All three handles are not the same. // // Also, Src1 or Src2 could be the actual // destination so don't use TRUE on the // RGNOBJAPI contructor // RGNMEMOBJTMP rmo((BOOL)FALSE); RGNOBJAPI roSrc1(hrgnSrc1,FALSE); RGNOBJAPI roSrc2(hrgnSrc2,FALSE); if (!rmo.bValid() || !roSrc1.bValid() || !roSrc2.bValid() || (rmo.iCombine(roSrc1, roSrc2, iMode) == ERROR)) { if (!roSrc1.bValid() || !roSrc2.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } Status = ERROR; } else if (SAMEHANDLE(hrgnTrg, hrgnSrc1)) { if (!roSrc1.bSwap(&rmo)) { Status = ERROR; } else { Status = roSrc1.iComplexity(); } } else { if (!roSrc2.bSwap(&rmo)) { Status = ERROR; } else { Status = roSrc2.iComplexity(); } } } } else { // Handle the general case. RGNOBJAPI roSrc1(hrgnSrc1,TRUE); RGNOBJAPI roSrc2(hrgnSrc2,TRUE); RGNOBJAPI roTrg(hrgnTrg,FALSE); if (!roSrc1.bValid() || !roSrc2.bValid() || !roTrg.bValid() || (roTrg.iCombine(roSrc1, roSrc2, iMode) == ERROR)) { if (!roSrc1.bValid() || !roSrc2.bValid() || !roTrg.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } Status = ERROR; } else { Status = roTrg.iComplexity(); } } return (int)Status; } /******************************Public*Routine******************************\ * HRGN NtGdiCreateEllipticRgn(xLeft,yTop,xRight,yBottom) * * Create an elliptical region. * \**************************************************************************/ HRGN APIENTRY NtGdiCreateEllipticRgn( int xLeft, int yTop, int xRight, int yBottom) { GDITrace(NtGdiCreateEllipticRgn, "(%d, %d, %d, %d)\n", (va_list)&xLeft); HRGN hrgn; PATHMEMOBJ pmo; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } ERECTL ercl(xLeft, yTop, xRight, yBottom); if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return((HRGN) 0); } // Handle the PS_INSIDEFRAME pen attribute and lower-right exclusion // by adjusting the box now. And set the flag that this will be an // ellipse, to fill it nice: EBOX ebox(ercl, TRUE); if (ebox.bEmpty()) { RGNMEMOBJ rmoEmpty; if (rmoEmpty.bValid()) { hrgn = rmoEmpty.hrgnAssociate(); if (hrgn == (HRGN)0) { rmoEmpty.bDeleteRGNOBJ(); } } else { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN) 0; } } else if (!bEllipse(pmo, ebox) || !pmo.bFlatten()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN)0; } else { RGNMEMOBJ rmo(pmo); // convert path to region (ALTERNATE) if (rmo.bValid()) { rmo.vTighten(); hrgn = rmo.hrgnAssociate(); if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); } } else { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN) 0; } } GDITrace(NtGdiCreateEllipticRgn_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * HRGN GreCreatePolyPolygonRgn(aptl,acptl,cPoly,iFill) * * Create a polygonal region with multiple, disjoint polygons. * \**************************************************************************/ HRGN APIENTRY GreCreatePolyPolygonRgnInternal( CONST POINT *aptl, CONST INT *acptl, int cPoly, int iFill, UINT cMaxPoints) { GDITrace(GreCreatePolyPolygonRgnInternal, "(%p, %p, %d, %d, %u)\n", (va_list)&aptl); HRGN hrgn = NULL; if ((iFill == ALTERNATE) || (iFill == WINDING)) { PATHMEMOBJ pmo; if (pmo.bValid()) { EXFORMOBJ exfo(IDENTITY); ASSERTGDI(exfo.bValid(), "Can\'t make IDENTITY matrix!\n"); if (bPolyPolygon(pmo, exfo, (PPOINTL) aptl, (PLONG) acptl, cPoly, cMaxPoints)) { RGNMEMOBJ rmo(pmo, iFill); // convert path to region if (rmo.bValid()) { hrgn = rmo.hrgnAssociate(); if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); } } } } } GDITrace(GreCreatePolyPolygonRgnInternal_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * HRGN GreCreateRectRgn(xLeft,yTop,xRight,yBottom) * * Create a rectangular region. * * Called only from user * \**************************************************************************/ HRGN APIENTRY GreCreateRectRgn( int xLeft, int yTop, int xRight, int yBottom) { GDITrace(GreCreateRectRgn, "(%d, %d, %d, %d)\n", (va_list)&xLeft); RGNLOG rl((PREGION)NULL,"GreCreateRectRgn"); ERECTL ercl(xLeft, yTop, xRight, yBottom); if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return((HRGN) 0); } RGNMEMOBJ rmo((BOOL)FALSE); HRGN hrgn; if (!rmo.bValid()) { hrgn = (HRGN) 0; SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); } else { #if NOREORDER_RGN // // reduce region if coordinates are not well ordered // if ((xLeft > xRigth) || (yTop > yBottom)) { WARNING("GreCreateRectRgn: region not well ordered"); xLeft = 0; yTop = 0; xRight = 0; yBottom = 0; } #else // // Make the rectangle well ordered. // ercl.vOrder(); #endif rmo.vSet((RECTL *) &ercl); hrgn = (HRGN)HmgInsertObject(rmo.prgnGet(),HMGR_MAKE_PUBLIC,RGN_TYPE); if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); } } rl.vRet((ULONG_PTR)hrgn); GDITrace(GreCreateRectRgn_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * * NtGdiCreateRectRgn is the same as GreCreateRectRgn except an additional * argument is passed in, a shared RECTREGION pointer. This pointer must be * put into the shared pointer filed of the handle table for the RGN * created. This allows fast user-mode access to RECT regions. * * Arguments: * * xLeft - left edge of region * yTop - top edge of region * xRight - right edge of region * yBottom - bottom edge of region * pRectRegion - pointer to user-mode data * * Return Value: * * new HRGN or NULL * * History: * * 20-Jun-1995 -by- Mark Enstrom [marke] * \**************************************************************************/ HRGN APIENTRY NtGdiCreateRectRgn( int xLeft, int yTop, int xRight, int yBottom ) { GDITrace(NtGdiCreateRectRgn, "(%d, %d, %d, %d)\n", (va_list)&xLeft); RGNLOG rl((PREGION)NULL,"GreCreateRectRgn"); ERECTL ercl(xLeft, yTop, xRight, yBottom); if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return((HRGN) 0); } PVOID pRgnattr = (PRGNATTR)HmgAllocateObjectAttr(); HRGN hrgn; if (pRgnattr == NULL) { // // memory alloc error // hrgn = (HRGN) 0; SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); } else { RGNMEMOBJ rmo((BOOL)FALSE); if (!rmo.bValid()) { hrgn = (HRGN) 0; SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); } else { // // Make the rectangle well ordered. // ercl.vOrder(); rmo.vSet((RECTL *) &ercl); // // allocate an object for this region, set // the shared pointer if needed // #if DBG RGNLOG rl(rmo.prgn,"RGNOBJ::hrgnAssociate"); hrgn = (HRGN)HmgInsertObject(rmo.prgn,HMGR_ALLOC_LOCK,RGN_TYPE); rl.vRet((ULONG_PTR)hrgn); #else hrgn = (HRGN)HmgInsertObject(rmo.prgn,HMGR_ALLOC_LOCK,RGN_TYPE); #endif if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); HmgFreeObjectAttr((POBJECTATTR)pRgnattr); } else { // // set shared rect region pointer and unlock // PENTRY_FROM_POBJ(rmo.prgn)->pUser = (PDC_ATTR)pRgnattr; DEC_EXCLUSIVE_REF_CNT(rmo.prgn); } } } rl.vRet((ULONG_PTR)hrgn); GDITrace(NtGdiCreateRectRgn_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * HRGN GreCreateRectRgnIndirect(prcl) * * Create a rectangular region. * \**************************************************************************/ HRGN APIENTRY GreCreateRectRgnIndirect(LPRECT prcl) { GDITrace(GreCreateRectRgnIndirect, "(%p)\n", (va_list)&prcl); RGNLOG rl((PREGION)NULL,"GreCreateRectRgnIndirect",prcl->left,prcl->top,prcl->right); if ((prcl == (LPRECT) NULL) || !VALID_SCRPRC(prcl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return((HRGN) 0); } RGNMEMOBJ rmo((BOOL)FALSE); HRGN hrgn; if (!rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN) 0; } else { ((ERECTL *) prcl)->vOrder(); // Make the rectangle well ordered. rmo.vSet((RECTL *) prcl); hrgn = rmo.hrgnAssociate(); if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); } } rl.vRet((ULONG_PTR)hrgn); GDITrace(GreCreateRectRgnIndirect_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } HRGN APIENTRY NtGdiCreateRoundRectRgn( int xLeft, int yTop, int xRight, int yBottom, int xWidth, int yHeight ) { GDITrace(NtGdiCreateRoundRectRgn, "(%d, %d, %d, %d, %d, %d)\n", (va_list)&xLeft); PATHMEMOBJ pmo; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } ERECTL ercl(xLeft, yTop, xRight, yBottom); if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return((HRGN) 0); } // Handle the PS_INSIDEFRAME pen attribute and lower-right exclusion // by adjusting the box now. And set the flag that this will be an // ellipse, to fill it nice: EBOX ebox(ercl, TRUE); HRGN hrgn; if (ebox.bEmpty()) { RGNMEMOBJ rmoEmpty; if (rmoEmpty.bValid()) { hrgn = rmoEmpty.hrgnAssociate(); if (hrgn == (HRGN)0) { rmoEmpty.bDeleteRGNOBJ(); } } else { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN)0; } } else if (!bRoundRect(pmo, ebox, xWidth, yHeight) || !pmo.bFlatten()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN)0; } else { RGNMEMOBJ rmo(pmo); // convert path to region (ALTERNATE) if (rmo.bValid()) { rmo.vTighten(); hrgn = rmo.hrgnAssociate(); if (hrgn == (HRGN)0) { rmo.bDeleteRGNOBJ(); } } else { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); hrgn = (HRGN)0; } } GDITrace(NtGdiCreateRoundRectRgn_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * NtGdiEqualRgn() * * Check if the two regions are equal. * \**************************************************************************/ BOOL APIENTRY NtGdiEqualRgn( HRGN hrgn1, HRGN hrgn2 ) { GDITraceHandle2(NtGdiEqualRgn, "(%X, %X)\n", (va_list)&hrgn1, hrgn1, hrgn2); BOOL bRet = ERROR; RGNOBJAPI roSrc1(hrgn1,TRUE); RGNOBJAPI roSrc2(hrgn2,TRUE); if (roSrc1.bValid() && roSrc2.bValid()) { bRet = roSrc1.bEqual(roSrc2); } return (bRet); } /******************************Public*Routine******************************\ * BOOL GreFillRgn (hdc,hrgn,hbrush,pac) * * Paint the region with the specified brush. * \**************************************************************************/ BOOL NtGdiFillRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush ) { GDITraceHandle3(NtGdiFillRgn, "(%X, %X, %X)\n", (va_list)&hdc, hdc, hrgn, hbrush); BOOL bRet = FALSE; DCOBJ dco(hdc); BOOL bXform; PREGION prgnOrg; if (dco.bValid()) { EXFORMOBJ exo(dco, WORLD_TO_DEVICE); // We may have to scale/rotate the incoming region. bXform = !dco.pdc->bWorldToDeviceIdentity(); RGNOBJAPI ro(hrgn,FALSE); if (ro.bValid()) { if (bXform) { PATHMEMOBJ pmo; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } if (!exo.bValid() || !ro.bCreate(pmo, &exo)) return(FALSE); ASSERTGDI(pmo.bValid(),"GreFillRgn - pmo not valid\n"); RGNMEMOBJ rmo(pmo); if (!rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } // this replaces the prgn in ro with the new prgn. The ro destructor will // unlock the handle for hrgn. We must first delete the prgn though. prgnOrg = ro.prgnGet(); ro.vSetRgn(rmo.prgnGet()); } // If region is null, return TRUE if (ro.iComplexity() != NULLREGION) { // Accumulate bounds. We can do this before knowing if the operation is // successful because bounds can be loose. ERECTL ercl(0, 0, 0, 0); ro.vGet_rcl((RECTL *) &ercl); if (dco.fjAccum()) dco.vAccumulate(ercl); if (dco.bHasSurface()) { dco.pdc->prgnAPI(ro.prgnGet()); // Dirties rgnRao DEVLOCKOBJ dlo(dco); SURFACE *pSurf = dco.pSurface(); if (!dlo.bValid()) { bRet = dco.bFullScreen(); } else { ercl += dco.eptlOrigin(); // So we know where to draw // Compute the clipping complexity and maybe reduce the exclusion rectangle. ECLIPOBJ eco(dco.prgnEffRao(), ercl); if (eco.erclExclude().bEmpty()) { bRet = TRUE; } else { XEPALOBJ epal(pSurf->ppal()); XEPALOBJ epalDC(dco.ppal()); PDEVOBJ pdo(pSurf->hdev()); EBRUSHOBJ ebo; PBRUSH pbrush = (BRUSH *)HmgShareCheckLock((HOBJ)hbrush, BRUSH_TYPE); bRet = FALSE; // assume we won't succeed // // Substitute the NULL brush if this brush handle // couldn't be locked. // if (pbrush != NULL) { // // in case the brush is cached and the color has changed // bSyncBrushObj(pbrush); ebo.vInitBrush(dco.pdc, pbrush, epalDC, epal, pSurf); ebo.pColorAdjustment(dco.pColorAdjustment()); if (!pbrush->bIsNull()) { // Exclude the pointer. DEVEXCLUDEOBJ dxo(dco,&eco.erclExclude(),&eco); // Get and compute the correct mix mode. MIX mix = ebo.mixBest(dco.pdc->jROP2(), dco.pdc->jBkMode()); // Inc the target surface uniqueness INC_SURF_UNIQ(pSurf); // Issue a call to Paint. EngPaint( pSurf->pSurfobj(), &eco, &ebo, &dco.pdc->ptlFillOrigin(), mix); bRet = TRUE; } DEC_SHARE_REF_CNT_LAZY0(pbrush); } } } dco.pdc->prgnAPI((PREGION) NULL); // Dirties rgnRao } else { bRet = TRUE; } } else { bRet = TRUE; } if (bXform) { // need to delete the temporary one and put the old one back in so // the handle gets unlocked ro.prgnGet()->vDeleteREGION(); ro.vSetRgn(prgnOrg); } } } return(bRet); } /******************************Public*Routine******************************\ * BOOL GreFrameRgn (hdc,hrgn,hbrush,xWidth,yHeight,pac) * * Frame the region and fill with the specified brush. * \**************************************************************************/ BOOL APIENTRY NtGdiFrameRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush, int xWidth, int yHeight ) { GDITraceHandle3(NtGdiFrameRgn, "(%X, %X, %X, %d, %d)\n", (va_list)&hdc, hdc, hrgn, hbrush); DCOBJ dco(hdc); RGNOBJAPI ro(hrgn,TRUE); BOOL bRet = FALSE; // // Take the absolute value just like Win3 does: // xWidth = ABS(xWidth); yHeight = ABS(yHeight); // // do some validation // if (dco.bValid() && ro.bValid() && (xWidth > 0) && (yHeight > 0)) { if (ro.iComplexity() == NULLREGION) { bRet = TRUE; } else { // // Convert the region to a path, scaling/rotating it as we do so. // PATHMEMOBJ pmoSpine; PATHMEMOBJ pmoWide; EXFORMOBJ exo(dco, WORLD_TO_DEVICE); ASSERTGDI(exo.bValid(), "Non valid xform"); if (pmoSpine.bValid() && pmoWide.bValid()) { if (ro.bCreate(pmoSpine, &exo)) { EXFORMOBJ exoWiden; LINEATTRS la; MATRIX mx; exoWiden.vInit(&mx, DONT_COMPUTE_FLAGS); // // Initialize line attributes and xform from DC's xform: // pmoSpine.vWidenSetupForFrameRgn(dco, xWidth, yHeight, &exoWiden, &la); // // Make sure we won't expand out of device space before we // widen: // if (pmoWide.bComputeWidenedBounds(pmoSpine, (XFORMOBJ*) &exoWiden, &la) && pmoWide.bWiden(pmoSpine, (XFORMOBJ*) &exoWiden, &la)) { // // Now convert the widened result back into a region: // RGNMEMOBJTMP rmoFill(pmoWide, WINDING); RGNMEMOBJTMP rmoFrame; if (rmoFill.bValid() && rmoFrame.bValid()) { if (dco.pdc->bWorldToDeviceIdentity()) { // // We AND the original region and the widened region to get the // frame region: // bRet = rmoFrame.bMerge(rmoFill, ro, gafjRgnOp[RGN_AND]); } else { // // Ugh, we have to transform the original region according to the // world transform before we merge it: // RGNMEMOBJTMP rmo(pmoSpine); bRet = rmo.bValid() && rmoFrame.bMerge(rmoFill, rmo, gafjRgnOp[RGN_AND]); } if (bRet) { // // Accumulate bounds. We can do this before knowing if the operation is // successful because bounds can be loose. // // NOTE - the default return value is now TRUE ERECTL ercl(0, 0, 0, 0); rmoFrame.vGet_rcl((RECTL *) &ercl); if (dco.fjAccum()) { dco.vAccumulate(ercl); } // in FULLSCREEN mode, exit with success. if (!dco.bFullScreen() && dco.bHasSurface()) { dco.pdc->prgnAPI(rmoFrame.prgnGet()); // Dirties rgnRao DEVLOCKOBJ dlo(dco); SURFACE *pSurf = dco.pSurface(); if (!dlo.bValid()) { dco.pdc->prgnAPI(NULL); // Dirties rgnRao bRet = dco.bFullScreen(); } else { ercl += dco.eptlOrigin(); // // Compute the clipping complexity and maybe reduce the exclusion rectangle. // ECLIPOBJ eco(dco.prgnEffRao(), ercl); if (eco.erclExclude().bEmpty()) { dco.pdc->prgnAPI(NULL); // Dirties rgnRao } else { XEPALOBJ epal(pSurf->ppal()); XEPALOBJ epalDC(dco.ppal()); PDEVOBJ pdo(pSurf->hdev()); EBRUSHOBJ ebo; // // NOTE - the default return value // is now FALSE; PBRUSH pbrush = (BRUSH *)HmgShareCheckLock((HOBJ)hbrush, BRUSH_TYPE); bRet = FALSE; if (pbrush == NULL) { dco.pdc->prgnAPI(NULL); // Dirties rgnRao } else { // // in case the brush is cached and the color has changed // bSyncBrushObj (pbrush); ebo.vInitBrush(dco.pdc, pbrush, epalDC, epal, pSurf); ebo.pColorAdjustment(dco.pColorAdjustment()); if (pbrush->bIsNull()) { dco.pdc->prgnAPI(NULL); // Dirties rgnRao } else { // // Exclude the pointer. // DEVEXCLUDEOBJ dxo(dco,&eco.erclExclude(),&eco); // // Get and compute the correct mix mode. // MIX mix = ebo.mixBest(dco.pdc->jROP2(), dco.pdc->jBkMode()); // // Inc the target surface uniqueness // INC_SURF_UNIQ(pSurf); // // Issue a call to Paint. // EngPaint( pSurf->pSurfobj(), // Destination surface. &eco, // Clip object. &ebo, // Realized brush. &dco.pdc->ptlFillOrigin(), // Brush origin. mix); // Mix mode. dco.pdc->prgnAPI(NULL); // Dirties rgnRao bRet = TRUE; } DEC_SHARE_REF_CNT_LAZY0(pbrush); } } } } } } } } } } } return(bRet); } /******************************Public*Routine******************************\ * LONG GreGetRgnBox(hrgn,prcl) * * Get the bounding box of the region. * \**************************************************************************/ int APIENTRY GreGetRgnBox( HRGN hrgn, LPRECT prcl) { GDITraceHandle(GreGetRgnBox, "(%X, %p)\n", (va_list)&hrgn, hrgn); int iret = ERROR; RGNOBJAPI ro(hrgn,TRUE); if ((prcl != NULL) && (ro.bValid())) { ro.vGet_rcl((RECTL *) prcl); iret = (int)ro.iComplexity(); if (iret == NULLREGION) { // // Be compatible with Win 3.1 [donalds] 02-Jun-1993 // prcl->left = 0; prcl->top = 0; prcl->right = 0; prcl->bottom = 0; } } return(iret); } /******************************Public*Routine******************************\ * BOOL GreInvertRgn(hdc,hrgn) * * Invert the colors in the given region. * \**************************************************************************/ BOOL NtGdiInvertRgn( HDC hdc, HRGN hrgn) { GDITraceHandle2(NtGdiInvertRgn, "(%X, %X)\n", (va_list)&hdc, hdc, hrgn); DCOBJ dco(hdc); BOOL bXform; PREGION prgnOrg; BOOL bRet = FALSE; if (dco.bValid()) { EXFORMOBJ exo(dco, WORLD_TO_DEVICE); // // We may have to scale/rotate the incoming region. // bXform = !dco.pdc->bWorldToDeviceIdentity(); RGNOBJAPI ro(hrgn,TRUE); if (ro.bValid()) { if (bXform) { PATHMEMOBJ pmo; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } if (!exo.bValid() || !ro.bCreate(pmo, &exo)) return(FALSE); RGNMEMOBJ rmo(pmo); if (!rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } prgnOrg = ro.prgnGet(); ro.vSetRgn(rmo.prgnGet()); } // // If region is null, return TRUE // if (ro.iComplexity() != NULLREGION) { // Accumulate bounds. We can do this before knowing if the operation is // successful because bounds can be loose. ERECTL ercl; ro.vGet_rcl((RECTL *) &ercl); if (dco.fjAccum()) dco.vAccumulate(ercl); if (dco.bHasSurface()) { dco.pdc->prgnAPI(ro.prgnGet()); // Dirties rgnRao DEVLOCKOBJ dlo(dco); SURFACE *pSurf = dco.pSurface(); if (!dlo.bValid()) { bRet = dco.bFullScreen(); } else { ercl += dco.eptlOrigin(); // Compute the clipping complexity and maybe reduce the exclusion rectangle. ECLIPOBJ eco(dco.prgnEffRao(), ercl); if (!eco.erclExclude().bEmpty()) { PDEVOBJ pdo(pSurf->hdev()); // Exclude the pointer. DEVEXCLUDEOBJ dxo(dco,&eco.erclExclude(),&eco); // Inc the target surface uniqueness INC_SURF_UNIQ(pSurf); // Issue a call to Paint. EngPaint( pSurf->pSurfobj(), // Destination surface. &eco, // Clip object. (BRUSHOBJ *) NULL, // Realized brush. (POINTL *) NULL, // Brush origin. 0x00000606); // R2_NOT } bRet = TRUE; } dco.pdc->prgnAPI((PREGION)NULL); // Dirties rgnRao } else { bRet = TRUE; } } else { bRet = TRUE; } if (bXform) { // need to delete the temporary one and put the old one back in so // the handle gets unlocked ro.prgnGet()->vDeleteREGION(); ro.vSetRgn(prgnOrg); } } } return(bRet); } /******************************Public*Routine******************************\ * LONG GreOffsetRgn(hrgn,x,y) * * Offset the given region. * \**************************************************************************/ int APIENTRY GreOffsetRgn( HRGN hrgn, int x, int y) { GDITraceHandle(GreOffsetRgn, "(%X, %d, %d)\n", (va_list)&hrgn, hrgn); RGNOBJAPI ro(hrgn,FALSE); POINTL ptl; int iRet = ERROR; ptl.x = x; ptl.y = y; if (ro.bValid()) { if (ro.bOffset(&ptl)) { iRet = ro.iComplexity(); } } return iRet; } /******************************Public*Routine******************************\ * BOOL GrePtInRegion(hrgn,x,y) * * Is the point in the region? * \**************************************************************************/ BOOL APIENTRY GrePtInRegion( HRGN hrgn, int x, int y) { GDITraceHandle(GrePtInRegion, "(%X, %d, %d)\n", (va_list)&hrgn, hrgn); RGNOBJAPI ro(hrgn,TRUE); if (!ro.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return((BOOL) ERROR); } POINTL ptl; ptl.x = x; ptl.y = y; return(ro.bInside(&ptl) == REGION_POINT_INSIDE); } /******************************Public*Routine******************************\ * BOOL GreRectInRegion(hrgn,prcl) * * Is any part of the rectangle in the region? * \**************************************************************************/ BOOL APIENTRY GreRectInRegion( HRGN hrgn, LPRECT prcl) { GDITraceHandle(GreRectInRegion, "(%X, %p)\n", (va_list)&hrgn, hrgn); BOOL bRet = ERROR; RGNOBJAPI ro(hrgn,TRUE); if (prcl && (ro.bValid())) { bRet = (ro.bInside((RECTL *) prcl) == REGION_RECT_INTERSECT); } return (bRet); } /******************************Public*Routine******************************\ * VOID GreSetRectRgn(hrgn,xLeft,yTop,xRight,yBottom) * * Set the region to be the specified rectangle * \**************************************************************************/ BOOL APIENTRY GreSetRectRgn( HRGN hrgn, int xLeft, int yTop, int xRight, int yBottom) { GDITraceHandle(GreSetRectRgn, "(%X, %d, %d, %d, %d)\n", (va_list)&hrgn, hrgn); RGNOBJAPI ro(hrgn,FALSE); BOOL bRet = ERROR; if (ro.bValid()) { ERECTL ercl(xLeft, yTop, xRight, yBottom); if (VALID_SCRPRC((RECTL *) &ercl)) { ercl.vOrder(); // Make the rectangle well ordered. ro.vSet((RECTL *) &ercl); bRet = TRUE; } } return bRet; } /******************************Public*Routine******************************\ * LONG GreExcludeClipRect(hdc,xLeft,yTop,xRight,yBottom) * * Subtract the rectangle from the current clip region * \**************************************************************************/ int APIENTRY GreExcludeClipRect( HDC hdc, int xLeft, int yTop, int xRight, int yBottom) { GDITraceHandle(GreExcludeClipRect, "(%X, %d, %d, %d, %d)\n", (va_list)&hdc, hdc); int iRet; DCOBJ dco(hdc); if (dco.bValid()) { // For speed, test for rotation upfront. EXFORMOBJ exo(dco, WORLD_TO_DEVICE); ERECTL ercl(xLeft, yTop, xRight, yBottom); if (!exo.bRotation()) { exo.vOrder(*(RECTL *)&ercl); exo.bXform(ercl); iRet = (int)dco.pdc->iCombine((RECTL *) &ercl,RGN_DIFF); } else if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); iRet = ERROR; } else { iRet = (int) dco.pdc->iCombine(&exo, (RECTL *) &ercl,RGN_DIFF); } if (iRet > NULLREGION) { iRet = COMPLEXREGION; } } else { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); iRet = ERROR; } return (iRet); } /******************************Public*Routine******************************\ * LONG GreGetAppClipBox(hdc,prcl) * * Get the bounding box of the clip region * \**************************************************************************/ int APIENTRY GreGetAppClipBox( HDC hdc, LPRECT prcl) { GDITraceHandle(GreGetAppClipBox, "(%X, %p)\n", (va_list)&hdc, hdc); DCOBJ dor(hdc); int iRet; int iSaveLeft; if (dor.bValid()) { DEVLOCKOBJ dlo(dor); if (!dlo.bValid()) { if (dor.bFullScreen()) { prcl->left = 0; // Make it a 'simple' empty rectangle prcl->right = 0; prcl->top = 0; prcl->bottom = 0; return(COMPLEXREGION); } else { return(ERROR); } } RGNOBJ ro(dor.prgnEffRao()); ro.vGet_rcl((RECTL *) prcl); // // return to logical coordinates // if ((prcl->left >= prcl->right) || (prcl->top >= prcl->bottom)) { prcl->left = 0; // Make it a 'simple' empty rectangle prcl->right = 0; prcl->top = 0; prcl->bottom = 0; iRet = NULLREGION; } else { EXFORMOBJ xfoDtoW(dor, DEVICE_TO_WORLD); if (xfoDtoW.bValid()) { *(ERECTL *)prcl -= dor.eptlOrigin(); if (!xfoDtoW.bRotation()) { if (xfoDtoW.bXform(*(ERECTL *)prcl)) { iRet = ro.iComplexity(); // // Transforms with negative scale can give // a prcl that is not in vOrder. // // // This is the correct fix, but some apps require the // previous bad behaviour. // //((ERECTL *)prcl)->vOrder(); } else { iRet = ERROR; } } else { POINTL aptl[4]; aptl[0].x = prcl->left; aptl[0].y = prcl->top; aptl[1].x = prcl->right; aptl[1].y = prcl->top; aptl[2].x = prcl->left; aptl[2].y = prcl->bottom; aptl[3].x = prcl->right; aptl[3].y = prcl->bottom; xfoDtoW.bXform(aptl, 4); prcl->left = MIN4(aptl[0].x, aptl[1].x, aptl[2].x, aptl[3].x); prcl->top = MIN4(aptl[0].y, aptl[1].y, aptl[2].y, aptl[3].y); prcl->right = MAX4(aptl[0].x, aptl[1].x, aptl[2].x, aptl[3].x); prcl->bottom = MAX4(aptl[0].y, aptl[1].y, aptl[2].y, aptl[3].y); iRet = COMPLEXREGION; } } else { iRet = ERROR; } } if ((iRet != ERROR) && MIRRORED_DC(dor.pdc) && (prcl->left > prcl->right)) { iSaveLeft = prcl->left; prcl->left = prcl->right; prcl->right = iSaveLeft; } } else { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); iRet = ERROR; } return(iRet); } /******************************Public*Routine******************************\ * int GreGetRandomRgn(hdc,hrgn,iNum) * * Copy the specified region into the handle provided * \**************************************************************************/ int GreGetRandomRgn( HDC hdc, HRGN hrgn, int iNum) { GDITraceHandle2(GreGetRandomRgn, "(%X, %X, %d)\n", (va_list)&hdc, hdc, hrgn); DCOBJ dor(hdc); PREGION prgnSrc1, prgnSrc2; int iMode = RGN_COPY; int iRet = -1; if (!dor.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } else { DEVLOCKOBJ dlo(dor); switch(iNum) { case 1: prgnSrc1 = dor.pdc->prgnClip(); break; case 2: prgnSrc1 = dor.pdc->prgnMeta(); break; case 3: prgnSrc1 = dor.pdc->prgnClip(); prgnSrc2 = dor.pdc->prgnMeta(); if (prgnSrc1 == NULL) // prgnSrc1 == 0, prgnSrc2 != 0 { prgnSrc1 = prgnSrc2; } else if (prgnSrc2 != NULL) // prgnSrc1 != 0, prgnSrc2 != 0 { iMode = RGN_AND; } break; case 4: ASSERTDEVLOCK(dor.pdc); prgnSrc1 = dor.pdc->prgnVis(); break; default: prgnSrc1 = NULL; } if (prgnSrc1 == NULL) { iRet = 0; } else { RGNOBJAPI ro(hrgn,FALSE); if (ro.bValid()) { RGNOBJ ro1(prgnSrc1); if (iMode == RGN_COPY) { if (ro.bCopy(ro1)) { // For a redirection DC, the surface originates at the // redirected window origin. For compatibility reasons, // we must return the visrgn in screen coordinates. if (iNum == 4 && dor.pdc->bRedirection()) { POINTL ptl; if (UserGetRedirectedWindowOrigin(hdc, (LPPOINT)&ptl) && ro.bOffset(&ptl)) { iRet = 1; } } else { iRet = 1; } } } else { RGNOBJ ro2(prgnSrc2); if (ro.iCombine(ro1,ro2,iMode) != RGN_ERROR) iRet = 1; } } } } return(iRet); } /******************************Public*Routine******************************\ * LONG GreIntersectClipRect(hdc,xLeft,yTop,xRight,yBottom) * * AND the rectangle with the current clip region * \**************************************************************************/ int APIENTRY GreIntersectClipRect( HDC hdc, int xLeft, int yTop, int xRight, int yBottom) { GDITraceHandle(GreIntersectClipRect, "(%X, %d, %d, %d, %d)\n", (va_list)&hdc, hdc); DCOBJ dco(hdc); if (!dco.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return(ERROR); } EXFORMOBJ exo(dco, WORLD_TO_DEVICE); ERECTL ercl(xLeft, yTop, xRight, yBottom); // For speed, test for rotation up front. int iRet; if (!exo.bRotation()) { exo.vOrder(*(RECTL *)&ercl); exo.bXform(ercl); iRet = (int)dco.pdc->iCombine((RECTL *) &ercl,RGN_AND); } else if (!VALID_SCRPRC((RECTL *) &ercl)) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); iRet = ERROR; } else { iRet = (int)dco.pdc->iCombine(&exo, (RECTL *) &ercl,RGN_AND); } if (iRet > NULLREGION) iRet = COMPLEXREGION; return(iRet); } /******************************Public*Routine******************************\ * INT NtGdiOffsetClipRgn(hdc,x,y) * * Offset the current clip region * \**************************************************************************/ int APIENTRY NtGdiOffsetClipRgn( HDC hdc, int x, int y) { GDITraceHandle(NtGdiOffsetClipRgn, "(%X, %d, %d)\n", (va_list)&hdc, hdc); DCOBJ dor(hdc); if (!dor.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return(ERROR); } PREGION prgn = dor.pdc->prgnClip(); if (prgn == NULL) return(SIMPLEREGION); // if this region has multiple references (saved levels) we need to duplicate // it and modify the copy. if (prgn->cRefs > 1) { RGNOBJ ro(prgn); RGNMEMOBJ rmo(ro.sizeRgn()); if (!rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return(ERROR); } rmo.vCopy(ro); prgn = rmo.prgnGet(); rmo.vSelect(hdc); ro.vUnselect(); dor.pdc->prgnClip(prgn); } RGNOBJ ro(prgn); EPOINTL eptl(x, y); // Transform the point from Logical to Device EXFORMOBJ xfo(dor, WORLD_TO_DEVICE); if (!xfo.bXform(*((EVECTORL *) &eptl)) || !ro.bOffset((PPOINTL)&eptl)) { SAVE_ERROR_CODE(ERROR_CAN_NOT_COMPLETE); return(ERROR); } dor.pdc->vReleaseRao(); dor.pdc->vUpdate_VisRect(dor.pdc->prgnVis()); return(ro.iComplexity()); } /******************************Public*Routine******************************\ * BOOL GrePtVisible(hdc,x,y) * * Is the point in the current clip region? * \**************************************************************************/ BOOL APIENTRY NtGdiPtVisible( HDC hdc, int x, int y) { DCOBJ dor(hdc); if (!dor.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return(ERROR_BOOL); } DEVLOCKOBJ dlo(dor); if (!dlo.bValid()) return(REGION_POINT_OUTSIDE); RGNOBJ ro(dor.prgnEffRao()); EPOINTL eptl(x, y); // Transform the point from Logical to Screen EXFORMOBJ xfo(dor, WORLD_TO_DEVICE); xfo.bXform(eptl); eptl += dor.eptlOrigin(); return(ro.bInside((PPOINTL)&eptl) == REGION_POINT_INSIDE); } /******************************Public*Routine******************************\ * BOOL GreRectVisible(hdc,prcl) * * Is the rectangle in the current clip region? * \**************************************************************************/ BOOL APIENTRY GreRectVisible( HDC hdc, LPRECT prcl) { DCOBJ dor(hdc); if (!dor.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return(ERROR_BOOL); } DEVLOCKOBJ dlo(dor); if (!dlo.bValid()) return(REGION_RECT_OUTSIDE); RGNOBJ ro(dor.prgnEffRao()); ERECTL ercl = *((ERECTL *) prcl); // Transform the rectangle from Logical to Screen EXFORMOBJ xfo(dor, WORLD_TO_DEVICE); // If there is no rotation in the transform, just call bInside(). if (!xfo.bRotation()) { xfo.vOrder(*(RECTL *)&ercl); xfo.bXform(ercl); ercl += dor.eptlOrigin(); BOOL bIn = ro.bInside((RECTL *) &ercl); return(bIn == REGION_RECT_INTERSECT); } // Convert the rectangle to a parallelogram and merge it with the Rao. // If there is anything left, the call succeeded. POINTL aptl[4]; aptl[0].x = prcl->left; aptl[0].y = prcl->top; aptl[1].x = prcl->right; aptl[1].y = prcl->top; aptl[2].x = prcl->right; aptl[2].y = prcl->bottom; aptl[3].x = prcl->left; aptl[3].y = prcl->bottom; // Create a path, and draw the parallelogram. PATHMEMOBJ pmo; BOOL bRes; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); bRes = ERROR_BOOL; } else if (!pmo.bMoveTo(&xfo, &aptl[0]) || !pmo.bPolyLineTo(&xfo, &aptl[1], 3) || !pmo.bCloseFigure()) { bRes = ERROR_BOOL; } else { // Now, convert it back into a region. RGNMEMOBJTMP rmoPlg(pmo, ALTERNATE); RGNMEMOBJTMP rmo; if (!rmoPlg.bValid() || !rmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); bRes = ERROR_BOOL; } else { if (!rmo.bMerge(ro, rmoPlg, gafjRgnOp[RGN_AND]) || (rmo.iComplexity() == NULLREGION)) { bRes = (BOOL)REGION_RECT_OUTSIDE; } else { bRes = (BOOL)REGION_RECT_INTERSECT; } } } return(bRes); } /******************************Public*Routine******************************\ * int GreExtSelectClipRgn(hdc,hrgn,iMode) * * Merge the region into current clip region * \**************************************************************************/ int GreExtSelectClipRgn( HDC hdc, HRGN hrgn, int iMode) { GDITraceHandle2(GreExtSelectClipRgn, "(%X, %X, %d)\n", (va_list)&hdc, hdc, hrgn); int iRet = RGN_ERROR; BOOL bSame = FALSE; if (((iMode < RGN_MIN) || (iMode > RGN_MAX))) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); } else { DCOBJ dco(hdc); if (!dco.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } else { if (hrgn != (HRGN)0) { RGNOBJAPI ro(hrgn,TRUE); if (ro.bValid()) { iRet = dco.pdc->iSelect(ro.prgnGet(),iMode); if (iRet != RGN_ERROR) { DEVLOCKOBJ dlo(dco); RGNOBJ ro(dco.prgnEffRao()); iRet = ro.iComplexity(); } } } else { if (iMode == RGN_COPY) { iRet = dco.pdc->iSelect((PREGION)NULL,iMode); if (iRet != RGN_ERROR) { DEVLOCKOBJ dlo(dco); RGNOBJ roVis(dco.pdc->prgnVis()); iRet = roVis.iComplexity(); } } } } } return(iRet); } /******************************Public*Routine******************************\ * SelectClip from bathcing * * Arguments: * * * * Return Value: * * * * History: * * 26-Oct-1995 -by- Mark Enstrom [marke] * \**************************************************************************/ GreExtSelectClipRgnLocked( XDCOBJ &dco, PRECTL prcl, int iMode) { int iRet = RGN_ERROR; BOOL bNullHrgn = iMode & REGION_NULL_HRGN; iMode &= ~REGION_NULL_HRGN; if (((iMode < RGN_MIN) || (iMode > RGN_MAX))) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); } else { if (!dco.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } else { // // iFlag specifies a null hrgn // if (!bNullHrgn) { // // check if current region is the same as new // hrgn // BOOL bSame = FALSE; RGNOBJ roClipOld(dco.pdc->prgnClip()); if (roClipOld.bValid()) { if (roClipOld.bRectl()) { RECTL rclOld; roClipOld.vGet_rcl(&rclOld); if ( (prcl->left == rclOld.left) && (prcl->top == rclOld.top) && (prcl->right == rclOld.right) && (prcl->bottom == rclOld.bottom) ) { RGNOBJ ro(dco.prgnEffRao()); iRet = ro.iComplexity(); bSame = TRUE; } } } // // regions don't match, must select new region into DC // if (!bSame) { RGNMEMOBJTMP ro(FALSE); if (ro.bValid()) { ro.vSet(prcl); iRet = dco.pdc->iSelect(ro.prgnGet(),iMode); // // need to update RAO // if (dco.pdc->bDirtyRao()) { if (!dco.pdc->bCompute()) { WARNING("bCompute fails in GreExtSelectClipRgnLocked"); } } if (iRet != RGN_ERROR) { RGNOBJ ro(dco.prgnEffRao()); iRet = ro.iComplexity(); } } } } else { if (iMode == RGN_COPY) { iRet = dco.pdc->iSelect((PREGION)NULL,iMode); // // need to update RAO // if (dco.pdc->bDirtyRao()) { if (!dco.pdc->bCompute()) { WARNING("bCompute fails in GreExtSelectClipRgnLocked"); } } if (iRet != RGN_ERROR) { RGNOBJ roVis(dco.pdc->prgnVis()); iRet = roVis.iComplexity(); } } } } } return(iRet); } /******************************Public*Routine******************************\ * int GreStMetaRgn(hdc,hrgn,iMode) * * Merge the region into current meta region * \**************************************************************************/ int GreSetMetaRgn( HDC hdc) { DCOBJ dco(hdc); if (!dco.bValid()) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return(ERROR); } return(dco.pdc->iSetMetaRgn()); } /******************************Public*Routine******************************\ * GreGetRegionData * * Retreives the region data * * History: * 5-Dec-1997 -by- Samer Arafeh [samera] * Wrote it. \**************************************************************************/ DWORD GreGetRegionData( HRGN hrgn, DWORD nCount, LPRGNDATA lpRgnData) { GDITraceHandle(GreGetRegionData, "(%X, %u, %p)\n", (va_list)&hrgn, hrgn); DWORD nSize; DWORD nRectangles; DWORD nRgnSize; // size of buffer of rectangles RGNOBJAPI ro(hrgn,TRUE); if (ro.bValid()) { // // just return size if buffer is NULL // nRgnSize = ro.sizeSave(); nSize = nRgnSize + sizeof(RGNDATAHEADER); if (lpRgnData != (LPRGNDATA) NULL) { if (nSize > nCount) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); nSize = ERROR; } else { nRectangles = (nSize - sizeof(RGNDATAHEADER)) / sizeof(RECTL); lpRgnData->rdh.dwSize = sizeof(RGNDATAHEADER); lpRgnData->rdh.iType = RDH_RECTANGLES; lpRgnData->rdh.nCount = nRectangles; lpRgnData->rdh.nRgnSize = nRgnSize; if (nRectangles != 0) { ro.vGet_rcl((RECTL *) &lpRgnData->rdh.rcBound); } else { lpRgnData->rdh.rcBound.left = 0; lpRgnData->rdh.rcBound.top = 0; lpRgnData->rdh.rcBound.right = 0; lpRgnData->rdh.rcBound.bottom = 0; } ro.vDownload((PVOID) &lpRgnData->Buffer); } } } else { SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); nSize = ERROR; } return(nSize); } /******************************Public*Routine******************************\ * DWORD NtGdiGetRegionData(hrgn, nCount, lpRgnData) * * Compute size of buffer/copy region data to buffer * \**************************************************************************/ DWORD NtGdiGetRegionData( HRGN hrgn, DWORD nCount, LPRGNDATA lpRgnData) { GDITraceHandle(NtGdiGetRegionData, "(%X, %u, %p)\n", (va_list)&hrgn, hrgn); DWORD nSize=!ERROR; ULONG ulTemp[QUANTUM_REGION_SIZE/2]; LPRGNDATA prgnTemp=NULL; // // If it is valid user pointer, let's copy into // into kernel memory (we don't know what the user might do with this memory) // if (lpRgnData) { if (nCount <= sizeof(ulTemp)) prgnTemp = (PRGNDATA) ulTemp; else prgnTemp = (PRGNDATA)AllocFreeTmpBuffer(nCount); if (!prgnTemp) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); nSize = ERROR; } } // // Check if allocation (if happened) is ok ? // if (nSize != ERROR) { nSize = GreGetRegionData( hrgn , nCount , prgnTemp ); // // Copy retreived data to user-buffer // if (lpRgnData && (nSize != ERROR)) { __try { ProbeForWrite(lpRgnData,nSize, sizeof(DWORD)); RtlCopyMemory( lpRgnData , prgnTemp , nSize ); } __except(EXCEPTION_EXECUTE_HANDLER) { SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); nSize = ERROR; } } } // // And free the kernel memory, if allocated // if (prgnTemp && (prgnTemp != (PRGNDATA)&ulTemp[0])) { FreeTmpBuffer(prgnTemp); } return(nSize); } /******************************Public*Routine******************************\ * HRGN GreExtCreateRegion(lpXform, nCount, lpRgnData) * * Create a region from a region data buffer * \**************************************************************************/ HRGN GreExtCreateRegion( XFORML *lpXform, DWORD nCount, LPRGNDATA lpRgnData) { GDITrace(GreExtCreateRegion, "(%p, %u, %p)\n", (va_list)&lpXform); ASSERTGDI(nCount >= sizeof(RGNDATAHEADER), "GreExtCreateRegion: passed invalid count"); DWORD nSize = lpRgnData->rdh.dwSize; ULONG cRect = lpRgnData->rdh.nCount; if (nSize != sizeof(RGNDATAHEADER)) { WARNING("GreExtCreateRegion: bad nSize"); return((HRGN) 0); } if (cRect > ((MAXULONG - sizeof(RGNDATAHEADER)) / sizeof (RECTL))) { // cRect is too large, which will cause the computation for nSize below to overflow. return ((HRGN) 0); } nSize += (cRect * sizeof(RECTL)); if (nSize > nCount) return((HRGN) 0); // At this point we have what looks like a valid header, and a buffer that // is at least big enough to contain all the data for a region. Create a // region to contain it and then attempt to upload the data into the region. RGNMEMOBJ rmo; if(!rmo.bValid()) { rmo.bDeleteRGNOBJ(); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } RECTL *prcl = (RECTL *)lpRgnData->Buffer; if(!rmo.bSet(cRect, prcl)) { rmo.bDeleteRGNOBJ(); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } HRGN hrgn; if ((lpXform == NULL) || (rmo.iComplexity() == NULLREGION)) { // // Create the proper bounding box and make it long lived // rmo.vTighten(); hrgn = rmo.hrgnAssociate(); if (hrgn == NULL) { rmo.bDeleteRGNOBJ(); } GDITrace(GreExtCreateRegion_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } // // Convert the XFORM to a MATRIX // MATRIX mx; vConvertXformToMatrix(lpXform, &mx); // // Scale it to FIXED notation. // mx.efM11.vTimes16(); mx.efM12.vTimes16(); mx.efM21.vTimes16(); mx.efM22.vTimes16(); mx.efDx.vTimes16(); mx.efDy.vTimes16(); mx.fxDx *= 16; mx.fxDy *= 16; EXFORMOBJ exo(&mx, XFORM_FORMAT_LTOFX | COMPUTE_FLAGS); if (!exo.bValid()) { rmo.bDeleteRGNOBJ(); return((HRGN) 0); } // // If the xform is the identity, we don't have to do anything. // if (exo.bIdentity()) { // // Create the proper bounding box and make it long lived // rmo.vTighten(); hrgn = rmo.hrgnAssociate(); if (hrgn == NULL) { rmo.bDeleteRGNOBJ(); } GDITrace(GreExtCreateRegion_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } // // Create a path from the region // PATHMEMOBJ pmo; if (!pmo.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); rmo.bDeleteRGNOBJ(); return((HRGN) 0); } BOOL bSuccess = rmo.bCreate(pmo, &exo); // // done with the region, delete it now. // rmo.bDeleteRGNOBJ(); if (!bSuccess) { return((HRGN) 0); } // // Create a region from the path // RGNMEMOBJTMP rmoPath(pmo); if (!rmoPath.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } RGNMEMOBJ rmoFinal; if (!rmoFinal.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return((HRGN) 0); } // // coelece the region // rmoFinal.iReduce(rmoPath); // // Create the proper bounding box and make it long lived // rmoFinal.vTighten(); hrgn = rmoFinal.hrgnAssociate(); if (hrgn == NULL) { rmoFinal.bDeleteRGNOBJ(); } GDITrace(GreExtCreateRegion_return, "(%X)\n", (va_list)&hrgn); return(hrgn); } /******************************Public*Routine******************************\ * BOOL GreIntersectVisRect(hdc, xLeft, yTop, xRight, yBottom) * * Intersect (AND) the rectangle with the vis region * * Warnings: * This is a PRIVATE USER API. * \**************************************************************************/ BOOL GreIntersectVisRect( HDC hdc, int xLeft, int yTop, int xRight, int yBottom) { BOOL bRes = FALSE; // // fail bad ordered rectangles // if ((xLeft>=xRight) || (yTop>=yBottom)) { return(FALSE); } // // fail bad coordinates // if ((xLeftMAX_REGION_COORD) || (yTopMAX_REGION_COORD)) { return(FALSE); } DCOBJA dov(hdc); // Use ALTLOCK if (dov.bValid()) // don't trust them the DC to be valid { // We invoke the 'dlo(po)' devlock form instead of 'dlo(dco)' // to avoid the bCompute that the latter does: PDEVOBJ po(dov.hdev()); DEVLOCKOBJ dlo(po); ASSERTDEVLOCK(dov.pdc); if (dlo.bValid()) { RGNOBJ ro(dov.pdc->prgnVis()); ERECTL ercl(xLeft, yTop, xRight, yBottom); RGNMEMOBJTMP rmo; RGNMEMOBJTMP rmo2(ro.sizeRgn()); if (!rmo.bValid() || !rmo2.bValid()) { SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); } else { rmo.vSet((RECTL *) &ercl); rmo2.vCopy(ro); if (ro.iCombine(rmo, rmo2, RGN_AND) != ERROR) { dov.pdc->prgnVis(ro.prgnGet()); ro.prgnGet()->vStamp(); dov.pdc->vReleaseRao(); bRes = TRUE; } } } RGNLOG rl((HRGN) dov.pdc->prgnVis(),0,"GreIntersectVisRect",(ULONG_PTR)hdc); rl.vRet((ULONG_PTR)bRes); } #if DBG else { // // USER may send a NULL hdc in low memory situations. // Just print a warning for those situations, but rip // for other bad handles (i.e., USER should figure out // why they passed us a bad handle). // if (hdc) { RIP("GDISRV!GreSelectVisRgn: Bad hdc\n"); } else { WARNING("GDISRV!GreSelectVisRgn: hdc NULL\n"); } } #endif return(bRes); } #if DBG || defined(PRERELEASE) /******************************Public*Routine******************************\ * GreValidateVisrgn() * * History: * 10-Dec-1998 -by- Vadim Gorokhovsky [vadimg] * Wrote it. \**************************************************************************/ VOID GreValidateVisrgn( HDC hdc, BOOL bValidateVisrgn ) { DCOBJA dco(hdc); if (dco.bValid()) { dco.pdc->vValidateVisrgn(bValidateVisrgn); if (bValidateVisrgn) { PDEVOBJ pdo(dco.hdev()); SURFACE *pSurf = dco.pSurface(); REGION *prgn = dco.pdc->prgnVis(); if (pdo.bValid() && !pdo.bMetaDriver() && pSurf && prgn) { BOOL bIsOK = ((pSurf->sizl().cx >= prgn->rcl.right) && (pSurf->sizl().cy >= prgn->rcl.bottom)&& (prgn->rcl.left >= 0) && (prgn->rcl.top >= 0)); FREASSERTGDI(bIsOK, "Rgn size is bigger than surface size"); } } } } #endif /******************************Public*Routine******************************\ * GreIsValidRegion() * * History: * 08-Dec-2001 -by- Mohamed Sadek [msadek] * Wrote it. \**************************************************************************/ BOOL GreIsValidRegion( HRGN hrgn) { RGNOBJAPI ro(hrgn, TRUE); return ro.bValid(); } /******************************Public*Routine******************************\ * HRGN GreSelectVisRgn(hdc,hrgn,fl) * * Select the region as the new vis region * * flags - only one of these may be passed in * * SVR_COPYNEW - make a copy of region passed in, deletes the old one * SVR_DELETEOLD - use the select rgn, delete the old one * SVR_SWAP - swaps the contents of the hrgn and the visrgn * * Warnings: * This is a PRIVATE USER API. * \**************************************************************************/ BOOL GreSelectVisRgn( HDC hdc, HRGN hrgn, VIS_REGION_SELECT fl) { RGNLOG rl(hrgn,NULL,"GreSelectVisRgn",(ULONG_PTR)hdc,(ULONG_PTR)fl); ASSERTGDI((fl == SVR_COPYNEW ) || (fl == SVR_DELETEOLD) || (fl == SVR_SWAP ), "GreSelectVisRgn - invalid fl\n"); BOOL bRet; // // Share Lock DC // DCOBJA dco(hdc); PREGION prgnOld; PREGION prgn; ASSERTDEVLOCK(dco.pdc); // // Always validate input hdc // if (!dco.bValid()) { #if DBG // // USER may send a NULL hdc in low memory situations. // Just print a warning for those situations, but rip // for other bad handles (i.e., USER should figure out // why they passed us a bad handle). // if (hdc) { RIP("GDISRV!GreSelectVisRgn: Bad hdc\n"); } else { WARNING("GDISRV!GreSelectVisRgn: hdc NULL\n"); } #endif bRet = FALSE; } else { bRet = TRUE; // // Always nuke the Rao // dco.pdc->vReleaseRao(); BOOL bDeleteOld = TRUE; if (hrgn != (HRGN) NULL) { // // The incoming region may be some random thing, make it lockable // GreSetRegionOwner(hrgn, OBJECT_OWNER_PUBLIC); RGNOBJAPI ro(hrgn,FALSE); if (ro.bValid()) { #if DBG || defined(PRERELEASE) // // Make Sure USER is not going to give us a RGN bigger than the surface // Note: USER may select a bogus rgn in during ReleaseDC time, we don't want // to assert there. // // To make things easy, we only check for single monitors // PDEVOBJ pdo(dco.hdev()); if (pdo.bValid() && !pdo.bMetaDriver()) { UINT uiIndex = (UINT) HmgIfromH((HOBJ)hdc); PENTRY pentTmp = &gpentHmgr[uiIndex]; SURFACE *pSurf = dco.pSurface(); if (pSurf && dco.pdc->bValidateVisrgn() && (OBJECTOWNER_PID(pentTmp->ObjectOwner) != OBJECT_OWNER_NONE)) { BOOL bIsOK = ((pSurf->sizl().cx >= ro.prgn->rcl.right) && (pSurf->sizl().cy >= ro.prgn->rcl.bottom)&& (ro.prgn->rcl.left >= 0) && (ro.prgn->rcl.top >= 0)); FREASSERTGDI(bIsOK, "Rgn size is bigger than surface size"); } } #endif switch (fl) { case SVR_COPYNEW: { // // We need to make a copy of the new one and delete the old one // RGNMEMOBJ rmo(ro.sizeRgn()); if (!rmo.bValid()) { prgn = prgnDefault; } else { rmo.vCopy(ro); prgn = rmo.prgnGet(); } } break; case SVR_SWAP: { // // we need to just swap handles. No deletion. // prgn = dco.pdc->prgnVis(); if (prgn == NULL) { prgn = prgnDefault; } // // don't swap out prgnDefault // if (prgn != prgnDefault) { RGNOBJ roVis(prgn); ro.bSwap(&roVis); // // roVis now contains the new vis rgn and the old visrgn // is associated with hrgn. // prgn = roVis.prgnGet(); bDeleteOld = FALSE; } else { bRet = FALSE; } } break; case SVR_DELETEOLD: // // delete the old handle but keep the region // prgn = ro.prgnGet(); if (ro.bDeleteHandle()) ro.vSetRgn(NULL); break; } } else { RIP("Bad hrgn"); prgn = prgnDefault; } // see if we need to delete the old one if (bDeleteOld) { dco.pdc->vReleaseVis(); } // set the new one in. dco.pdc->prgnVis(prgn); prgn->vStamp(); } else { // // User called GreSelectVisRgn after CreateRectRgn without // checking return value, so may have NULL hrgn here. // #if DBG if (fl != SVR_DELETEOLD) { WARNING("GreSelectVisRgn - fl != SVR_DELETEOLD"); } #endif dco.pdc->vReleaseVis(); dco.pdc->bSetDefaultRegion(); } } rl.vRet((ULONG_PTR)bRet); return(bRet); } /******************************Public*Routine******************************\ * GreCopyVisVisRgn() * * History: * 11-Jan-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ int GreCopyVisRgn( HDC hdc, HRGN hrgn) { RGNLOG rl(hrgn,NULL,"GreCopyVisRgn",(ULONG_PTR)hdc,0); int iRet = ERROR; DCOBJA dco(hdc); // Use ALT_LOCK on DC RGNOBJAPI ro(hrgn,FALSE); ASSERTDEVLOCK(dco.pdc); if (dco.bValid() && ro.bValid()) { RGNOBJ roVis(dco.pdc->prgnVis()); if (roVis.bValid() && ro.bCopy(roVis)) iRet = ro.iComplexity(); } return(iRet); } /******************************Public*Routine******************************\ * LONG GreGetClipBox(hdc,prcl,fXForm) * * Get the bounding box of the clip region * \**************************************************************************/ int APIENTRY GreGetClipBox( HDC hdc, LPRECT prcl, BOOL fXForm) { GDITraceHandle(GreGetClipBox, "(%X, %p, %X)\n", (va_list)&hdc, hdc); int iRet = ERROR; int iSaveLeft; DCOBJ dor(hdc); if (dor.bValid()) { DEVLOCKOBJ dlo(dor); if (!dlo.bValid()) { prcl->left = 0; // Make it a 'simple' empty rectangle prcl->right = 0; prcl->top = 0; prcl->bottom = 0; if (dor.bFullScreen()) iRet = NULLREGION; } else { RGNOBJ ro(dor.prgnEffRao()); ro.vGet_rcl((RECTL *) prcl); // First convert from screen to device coordinates if ((prcl->left >= prcl->right) || (prcl->top >= prcl->bottom)) { prcl->left = 0; // Make it a 'simple' empty rectangle prcl->right = 0; prcl->top = 0; prcl->bottom = 0; } else { *(ERECTL *)prcl -= dor.eptlOrigin(); // If requested, convert from device to logical coordinates. if (fXForm) { EXFORMOBJ xfoDtoW(dor, DEVICE_TO_WORLD); if (xfoDtoW.bValid()) { xfoDtoW.bXform(*(ERECTL *)prcl); } } if (MIRRORED_DC(dor.pdc) && (prcl->left > prcl->right)) { iSaveLeft = prcl->left; prcl->left = prcl->right; prcl->right = iSaveLeft; } } iRet = ro.iComplexity(); } GDITrace(GreGetClipBox, " returns (%d, %d) - (%d %d)\n", (va_list)prcl); } return(iRet); } /******************************Public*Routine******************************\ * int GreSubtractRgnRectList(hrgn, prcl, arcl, crcl) * * Quickly subtract the list of rectangles from the first rectangle to * produce a region. * \**************************************************************************/ int GreSubtractRgnRectList( HRGN hrgn, LPRECT prcl, LPRECT arcl, int crcl) { GDITraceHandle(GreSubtractRgnRectList, "(%X, %p, %p, %d)\n", (va_list)&hrgn, hrgn); RGNLOG rl(hrgn,NULL,"GreSubtractRgnRectList",crcl); RGNOBJAPI ro(hrgn,FALSE); int iRet; if (!ro.bValid() || !ro.bSubtract((RECTL *) prcl, (RECTL *) arcl, crcl)) { // If bSubtract fails, clean up the target region for USER. if (ro.bValid()) ro.vSet(); iRet = ERROR; } else { iRet = ro.iComplexity(); } rl.vRet(iRet); return(iRet); }