/*************************************************************************\ * Module Name: mfdc.cxx * * This file contains the member functions for metafile DC class defined * in mfdc.hxx. * * Created: 12-June-1991 13:46:00 * Author: Hock San Lee [hockl] * * Copyright (c) 1991-1999 Microsoft Corporation \*************************************************************************/ #define NO_STRICT extern "C" { #if defined(_GDIPLUS_) #include #endif #include #include #include #include #include // GDI function declarations. #include #include "firewall.h" #define __CPLUSPLUS #include #include #include "ntgdistr.h" #include "winddi.h" #include "hmgshare.h" #include "icm.h" #include "local.h" // Local object support. #include "gdiicm.h" #include "metadef.h" // Metafile record type constants. } #include "rectl.hxx" #include "mfdc.hxx" // Metafile DC class declarations. extern RECTL rclNull; // METAFILE.CXX #define MF_CHECK_RECORDMEMORY_LIMIT 0x00010000 VOID METALINK::vInit(ULONG metalink) { imhe = ((METALINK *) &metalink)->imhe; ihdc = ((METALINK *) &metalink)->ihdc; } /******************************Public*Routine******************************\ * void * MDC::pvNewRecord(nSize) * * Allocate a metafile record from memory buffer. * Also set the size field in the metafile record. If a fatal error * has occurred, do not allocate new record. * * History: * Thu Jul 18 11:19:20 1991 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ void * MDC::pvNewRecord(DWORD nSize) { #if DBG static DWORD cRcd = 0; PUTSX("MDC::pvNewRecord %ld \n", cRcd++); #endif // If a fatal error has occurred, do not allocate any more. if (fl & MDC_FATALERROR) return((void *) 0); // Before we allocate a new record, commit the previous bounds record // if necessary. if (fl & MDC_DELAYCOMMIT) { // Clear the flag. fl &= ~MDC_DELAYCOMMIT; PENHMETABOUNDRECORD pmrb = (PENHMETABOUNDRECORD) GetNextRecordPtr(sizeof(ENHMETABOUNDRECORD)); if(pmrb) { // Get and reset bounds. // See also MDC::vFlushBounds. if (GetBoundsRectAlt(hdcRef, (LPRECT) &pmrb->rclBounds, (UINT) (DCB_RESET | DCB_WINDOWMGR)) == DCB_SET) { // Need to intersect bounds with current clipping region first *((PERECTL) &pmrb->rclBounds) *= *perclMetaBoundsGet(); *((PERECTL) &pmrb->rclBounds) *= *perclClipBoundsGet(); // Make it inclusive-inclusive. pmrb->rclBounds.right--; pmrb->rclBounds.bottom--; // Accumulate bounds to the metafile header. if (!((PERECTL) &pmrb->rclBounds)->bEmpty()) *((PERECTL) &mrmf.rclBounds) += pmrb->rclBounds; else pmrb->rclBounds = rclNull; } else { pmrb->rclBounds = rclNull; } vCommit(*(PENHMETARECORD) pmrb); ASSERTGDI(!(fl & MDC_FATALERROR), "MDC::pvNewRecord: Fatal error has occurred"); ReleasePtr(pmrb); } else { WARNING("MDC::pvNewRecord() failed to get new record\n"); return(NULL); } } // If there is enough free buffer space, use it. if (iMem + nSize > nMem) { // Not enough free buffer space. Flush the filled buffer if it is // a disk metafile. if (bIsDiskFile()) if (!bFlush()) return((void *) 0); // Realloc memory buffer if the free buffer is still too small. if (iMem + nSize > nMem) { ULONG nMemNew, sizeNeeded, sizeExtra; sizeNeeded = (nSize + MF_BUFSIZE_INC - 1) / MF_BUFSIZE_INC * MF_BUFSIZE_INC; if (!bIsEMFSpool()) { // // When not EMF spooling, use the following heuristics: // If current size <= 64K, enlarge the buffer by extra 16K // Else, enlarge the buffer by extra 25% // sizeExtra = (nMem > 0x10000) ? (nMem >> 2) : MF_BUFSIZE_INC; nMemNew = nMem + sizeExtra + sizeNeeded; } else { // // When EMF spooling, use the more aggressive heuristics: // If current size <= 1MB, double the buffer // Else, enlarge the buffer by 50% with a cap of 4MB if (nMem > 0x100000) { sizeExtra = nMem >> 1; if (sizeExtra > 0x400000) sizeExtra = 0x400000; } else sizeExtra = nMem; nMemNew = nMem + max(sizeNeeded, sizeExtra); } if (!ReallocMem(nMemNew)) { ERROR_ASSERT(FALSE, "ReallocMem failed\n"); return NULL; } } } // Zero the last dword. If the record does not use up all bytes in the // last dword, the unused bytes will be zeros. PVOID pvRecord = GetNextRecordPtr(nSize); if(pvRecord) { ((PDWORD) pvRecord)[nSize / 4 - 1] = 0; // Set the size field and return the pointer to the new record. ((PENHMETARECORD) pvRecord)->nSize = nSize; // WINBUG 365051 4-10-2001 pravins Need to modify usage of pvNewRecord() to release ptr // We should not be doing the release here. We should have users of pvNewRecord() // call ReleasePtr when they are done. This requires a bunch of edits in metarec.cxx // and mestsup.cxx. ReleasePtr(pvRecord); } else { WARNING("MDC::pvNewRecord() failed to get next record ptr\n"); } return pvRecord; } BOOL MDC::ReallocMem( ULONG newsize ) /*++ Routine Description: Resize the memory buffer used to hold EMF data Arguments: newsize - new size of EMF memory buffer Return Value: TRUE if successful, FALSE if there is an error --*/ { if (bIsEMFSpool()) { ENHMETAHEADER *pNewHeader; if(!((EMFSpoolData *) hData)->ResizeEMFData(newsize)) return FALSE; } else { HANDLE hMemNew; if ((hMemNew = LocalReAlloc(hData, newsize, LMEM_MOVEABLE)) == NULL) { ERROR_ASSERT(FALSE, "LocalReAlloc failed"); return FALSE; } hData = hMemNew; } nMem = newsize; return TRUE; } /******************************Public*Routine******************************\ * BOOL MDC::bFlush() * * Flush the filled memory buffer to disk. * * History: * Thu Jul 18 11:19:20 1991 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ BOOL MDC::bFlush() { ULONG nWritten ; PUTS("MDC::bFlush\n"); PUTSX("\tnFlushSize = %ld\n", (ULONG)iMem); PUTSX("\tnBufferSize = %ld\n", (ULONG)nMem); ASSERTGDI(bIsDiskFile(), "MDC::bFlush: Not a disk metafile"); ASSERTGDI(!(fl & MDC_FATALERROR), "MDC::bFlush: Fatal error has occurred"); // WriteFile handles a null write correctly. if (!WriteFile(hFile, hData, iMem, &nWritten, (LPOVERLAPPED) NULL) || nWritten != iMem) { // The write error here is fatal since we are doing record buffering and // have no way of recovering to a previous state. ERROR_ASSERT(FALSE, "MDC::bFlush failed"); fl |= MDC_FATALERROR; return(FALSE); } iMem = 0L; return(TRUE); } /******************************Public*Routine******************************\ * VOID MDC::vAddToMetaFilePalette(cEntries, pPalEntriesNew) * * Add new palette entries to the metafile palette. * * When new palette entries are added to a metafile in CreatePalette or * SetPaletteEntries, they are also accumulated to the metafile palette. * The palette is later returned in GetEnhMetaFilePaletteEntries when an * application queries it. It assumes that the peFlags of the palette entries * are zeroes. * * A palette color is added to the metafile palette only if it is not a * duplicate. It uses a simple linear search algorithm to determine if * a duplicate palette exists. * * History: * Mon Sep 23 14:27:25 1991 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ VOID MDC::vAddToMetaFilePalette(UINT cEntries, PPALETTEENTRY pPalEntriesNew) { UINT ii; PUTS("vAddToMetaFilePalette\n"); while (cEntries--) { ASSERTGDI(pPalEntriesNew->peFlags == 0, "MDC::vAddToMetaFilePalette: peFlags not zero"); // Look for duplicate. for (ii = 0; ii < iPalEntries; ii++) { ASSERTGDI(sizeof(PALETTEENTRY) == sizeof(DWORD), "MDC::vAddToMetaFilePalette: Bad size"); if (*(PDWORD) &pPalEntries[ii] == *(PDWORD) pPalEntriesNew) break; } // Add to the metafile palette if not a duplicate. if (ii >= iPalEntries) { pPalEntries[iPalEntries] = *pPalEntriesNew; iPalEntries++; // Advance iPalEntries for next loop! } pPalEntriesNew++; } } /******************************Public*Routine******************************\ * VOID METALINK::vNext() * * Update *this to the next metalink. * * History: * Wed Aug 07 09:28:54 1991 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ VOID METALINK::vNext() { PUTS("METALINK::vNext\n"); ASSERTGDI(bValid(), "METALINK::vNext: Invalid metalink"); PMDC pmdc = pmdcGetFromIhdc(ihdc); ASSERTGDI(pmdc,"METALINK::vNext - invalid pmdc\n"); if (pmdc == NULL) ZeroMemory(this,sizeof(*this)); // Make it invalid so bValid will fail else *this = pmdc->pmhe[imhe].metalink; } /******************************Public*Routine******************************\ * METALINK * METALINK::pmetalinkNext() * * Return the pointer to the next metalink. * * History: * Wed Aug 07 09:28:54 1991 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ METALINK * METALINK::pmetalinkNext() { PUTS("METALINK::pmetalinkNext\n"); ASSERTGDI(bValid(), "METALINK::pmetalinkNext: Invalid metalink"); PMDC pmdc = pmdcGetFromIhdc(ihdc); ASSERTGDI(pmdc,"METALINK::vNext - invalid pmdc\n"); return(&pmdc->pmhe[imhe].metalink); } VOID EMFContainer::Init(ENHMETAHEADER * inHdr, UINT32 dataSize) { dwRefCount = 0; pemfhdr = inHdr; dwHdrSize = dataSize; pvWindow = NULL; dwWindowOffset = 0; dwWindowSize = 0; hFile = NULL; } BOOL EMFContainer::Init(HANDLE inFile, UINT64 inHdrOffset, UINT64 inFileSize) { BOOL bResult = FALSE; dwRefCount = 0; pemfhdr = NULL; dwHdrSize = 0; pvWindow = NULL; dwWindowOffset = 0; dwWindowSize = 0; hFile = inFile; hFileMapping = NULL; pvHdr = NULL; qwHdrOffset = inHdrOffset; qwFileSize = inFileSize; if(qwFileSize != 0 || GetFileSizeEx(hFile, (LARGE_INTEGER *) &qwFileSize)) { hFileMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, HIDWORD(qwFileSize), LODWORD(qwFileSize), NULL); if(hFileMapping != NULL) { UINT64 qwOffset = qwHdrOffset; UINT32 dwSize = sizeof(ENHMETAHEADER); pvHdr = pvMapView(&qwOffset, &dwSize); if(pvHdr) { UINT32 dwViewOffset = (UINT32) (qwHdrOffset - qwOffset); pemfhdr = (PENHMETAHEADER) ((PBYTE) pvHdr + dwViewOffset); dwHdrSize = dwSize - dwViewOffset; bResult = TRUE; } } } if(!bResult) Term(); return bResult; } VOID EMFContainer::Term() { // Free any locally allocated resources if(hFile) { // We were using mapped views of the file if(hFileMapping) { if(!CloseHandle(hFileMapping)) { WARNING("EMFContainer::Term() failed to close file mapping\n"); } hFileMapping = NULL; } if(pvHdr != NULL) { if(!UnmapViewOfFile(pvHdr)) { WARNING("EMFContainer::Term() Failed to unmap header view\n"); } pvHdr = NULL; } if(pvWindow != NULL) { if(!UnmapViewOfFile(pvWindow)) { WARNING("EMFContainer::Term() Failed to unmap window view\n"); } pvWindow = NULL; } hFile = NULL; } pemfhdr = NULL; dwHdrSize = 0; } BOOL bProbeAndPageInAddressRange(BYTE *pb, UINT inSize) { BOOL bRet = TRUE; if (inSize == 0) return bRet; __try { DWORD dwPagesize = GetSystemPageSize( ); BYTE bData; BYTE *pbLoc = pb; BYTE *pbEnd = pbLoc + ( (inSize < MF_CHECK_RECORDMEMORY_LIMIT) ? inSize : MF_CHECK_RECORDMEMORY_LIMIT ); while (pbLoc < pbEnd) { bData = *pbLoc; pbLoc += dwPagesize; } pbEnd--; bData = *pbEnd; } __except( GetExceptionCode() == STATUS_IN_PAGE_ERROR || GetExceptionCode() == STATUS_ACCESS_VIOLATION) { EMFVALFAIL(("bProbeAndPageInAddressRange: (%p) [%08x] Failed\n", pb, inSize)); bRet = FALSE; } return bRet; } PVOID EMFContainer::ObtainPtr(UINT inOffset, UINT inSize) { if(dwRefCount > 1) { WARNING("Obtaining record with non-zero ref count\n"); return NULL; } dwRefCount++; if(inOffset < dwHdrSize && inSize <= (dwHdrSize - inOffset)) { if (bProbeAndPageInAddressRange((PBYTE) pemfhdr + inOffset, inSize)) return (PVOID) ((PBYTE) pemfhdr + inOffset); else { --dwRefCount; return NULL; } } if(!hFile) { WARNING("EMFContainer::ObtainPtr() Attempt to obtain ptr past end of memory EMF\n"); dwRefCount--; return NULL; } if(inOffset < dwWindowOffset || (inOffset + inSize) > (dwWindowOffset + dwWindowSize)) { if(pvWindow && !UnmapViewOfFile(pvWindow)) { WARNING("EMFContainer::ObtainPtr() failed to unmap window view\n"); } UINT64 qwOffset = qwHdrOffset + inOffset; dwWindowSize = inSize; pvWindow = pvMapView(&qwOffset, &dwWindowSize); if(!pvWindow) { WARNING("EMFContainer::ObtainPtr() failed to map window view\n"); dwRefCount--; return NULL; } if(qwOffset < qwHdrOffset) { dwWindowUnusable = (UINT32) (qwHdrOffset - qwOffset); ASSERTGDI(dwWindowUnusable < dwWindowSize, "EMFContainer::ObtainPtr() Unexpected dwUnusable value\n"); dwWindowSize -= dwWindowUnusable; dwWindowOffset = 0; } else { dwWindowUnusable = 0; dwWindowOffset = (UINT32) (qwOffset - qwHdrOffset); } if(inOffset < dwWindowOffset || (inOffset + inSize) > (dwWindowOffset + dwWindowSize)) { WARNING("EMFContainer::ObtainPtr() something went really wrong\n"); dwRefCount--; return NULL; } } BYTE *pbRet = (BYTE *)pvWindow + inOffset - dwWindowOffset + dwWindowUnusable; if (bProbeAndPageInAddressRange(pbRet, inSize)) return (PVOID)pbRet; else return NULL; } PENHMETARECORD EMFContainer::ObtainRecordPtr(UINT inOffset) { ENHMETARECORD *pemr = NULL; ENHMETARECORD *pemrTemp = (ENHMETARECORD *) ObtainPtr(inOffset, sizeof(ENHMETARECORD)); if (pemrTemp != NULL) { UINT size = pemrTemp->nSize; ReleasePtr(pemrTemp); pemrTemp = (ENHMETARECORD *) ObtainPtr(inOffset, size); if (pemrTemp != NULL) { pemr = pemrTemp; } } return pemr; } PEMREOF EMFContainer::ObtainEOFRecordPtr() { PEMREOF pmreof = NULL; ENHMETAHEADER * pmrmf = GetEMFHeader(); if(pmrmf) { PDWORD pdw = (PDWORD) ObtainPtr(pmrmf->nBytes - sizeof(DWORD), sizeof(DWORD)); if(pdw) { DWORD dwOffset = pmrmf->nBytes - *pdw; ReleasePtr(pdw); pmreof = (PEMREOF) ObtainRecordPtr(dwOffset); } } return pmreof; } PVOID EMFContainer::pvMapView(UINT64 * ioOffset, UINT32 * ioSize) { UINT64 qwMappingAlignment = GetFileMappingAlignment(); UINT64 qwMappingMask = ~(qwMappingAlignment - 1); UINT64 qwRecordStart = *ioOffset; UINT64 qwRecordEnd = qwRecordStart + *ioSize; UINT64 qwViewStart; UINT64 qwViewEnd; PVOID pvView; qwViewStart = qwRecordStart & qwMappingMask; qwViewEnd = qwViewStart + qwMappingAlignment; if(qwViewEnd < qwRecordEnd) { qwViewEnd = qwRecordEnd; } qwViewEnd = (qwViewEnd + (qwMappingAlignment - 1)) & qwMappingMask; if(qwViewEnd > qwFileSize) { qwViewEnd = qwFileSize; } DWORD dwViewSize = (DWORD) (qwViewEnd - qwViewStart); pvView = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, HIDWORD(qwViewStart), LODWORD(qwViewStart), dwViewSize); if(pvView) { *ioOffset = qwViewStart; *ioSize = dwViewSize; } else { ioOffset = 0; ioSize = 0; } return pvView; } BOOL EMFContainer::bBounded(BYTE *p, DWORD dwSize) { BYTE *pBh = 0, *pEh = 0; BYTE *pBw = 0, *pEw = 0; BYTE *pBt = 0, *pEt = 0; if (pvWindow) { pBw = (PBYTE)pvWindow; pEw = pBw + dwWindowSize; } if (pemfhdr) { pBh = (PBYTE)pemfhdr; pEh = pBh + dwHdrSize; } pBt = p; pEt = p + dwSize - 1; // The pointer/extent is bounded if the address range they represent are // bounded between the address ranges represented by the header range or // the window range. if (((pBt >= pBw && pBt < pEw) && (pEt >= pBw && pEt < pEw)) || ((pBt >= pBh && pBt < pEh) && (pEt >= pBh && pEt < pEh))) return TRUE; return FALSE; }