You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2976 lines
87 KiB
2976 lines
87 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: MfRec16.c
|
|
*
|
|
* Copyright (c) 1991-1999 Microsoft Corporation
|
|
*
|
|
* DESCRIPTIVE NAME: Metafile Recorder
|
|
*
|
|
* FUNCTION: Records GDI functions in memory and disk metafiles.
|
|
*
|
|
* PUBLIC ENTRY POINTS:
|
|
* CloseMetaFile
|
|
* CopyMetaFile
|
|
* CreateMetaFile
|
|
* GetMetaFileBitsEx
|
|
* SetMetaFileBitsEx
|
|
* PRIVATE ENTRY POINTS:
|
|
* RecordParms
|
|
* AttemptWrite
|
|
* MarkMetaFile
|
|
* RecordObject
|
|
* ProbeSize
|
|
* AddObjectToDCTable
|
|
*
|
|
* History:
|
|
* 02-Jul-1991 -by- John Colleran [johnc]
|
|
* Combined From Win 3.1 and WLO 1.0 sources
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "mf16.h"
|
|
|
|
|
|
|
|
UINT AddObjectToDCTable(HDC hdc, HANDLE hObject, PUINT piPosition, BOOL bRealAdd);
|
|
BOOL AddDCToObjectMetaList16(HDC hMeta16DC, HANDLE hObject);
|
|
BOOL AttemptWrite(PMFRECORDER16 pMFRec, DWORD dwBytes, LPBYTE lpData);
|
|
VOID MarkMetaFile(PMFRECORDER16 pMFRec);
|
|
BOOL MakeLogPalette(HDC hdc, HANDLE hPal, WORD magic);
|
|
HANDLE ProbeSize(PMFRECORDER16 pMF, DWORD dwLength);
|
|
BOOL RecordCommonBitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight,
|
|
HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight,
|
|
DWORD rop, WORD wFunc);
|
|
BOOL UnlistObjects(HDC hMetaDC);
|
|
BOOL MF16_DeleteRgn(HDC hdc, HANDLE hrgn);
|
|
|
|
|
|
// Metafile Logging stubs for 3.x Metafiles
|
|
|
|
/******************************Public*Routine******************************\
|
|
* XXX_RecordParms
|
|
*
|
|
* These routines package up the parameters of an NT GDI call and send
|
|
* them to a general purpose recording routine that validates the metafile
|
|
* DC and records the parameters.
|
|
*
|
|
* Returns
|
|
* TRUE iff successful
|
|
*
|
|
* Warnings
|
|
* Windows 3.x metafile behavior is that when a function is being metafiled
|
|
* the routine itself is not called; eg SetPixel does not call GreSetPixel
|
|
* but (GDI) SetPixel intercepts the calls and records the parameters and
|
|
* returns without taking further action
|
|
*
|
|
* History:
|
|
* 24-Nov-1991 -by- John Colleran [johnc]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL MF16_RecordParms1(HDC hdc, WORD Func)
|
|
{
|
|
return RecordParms(hdc, Func, 0, (LPWORD)NULL);
|
|
}
|
|
|
|
BOOL MF16_RecordParms2(HDC hdc, INT parm2, WORD Func)
|
|
{
|
|
return RecordParms(hdc, Func, 1, (LPWORD)&parm2);
|
|
}
|
|
|
|
BOOL MF16_RecordParms3(HDC hdc, INT parm2, INT parm3, WORD Func)
|
|
{
|
|
WORD aw[2];
|
|
|
|
aw[0] = (WORD)parm3;
|
|
aw[1] = (WORD)parm2;
|
|
return RecordParms(hdc, Func, 2, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParms5(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5, WORD Func)
|
|
{
|
|
WORD aw[4];
|
|
|
|
aw[0] = (WORD)parm5;
|
|
aw[1] = (WORD)parm4;
|
|
aw[2] = (WORD)parm3;
|
|
aw[3] = (WORD)parm2;
|
|
return RecordParms(hdc, Func, 4, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParms7(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5, INT parm6, INT parm7, WORD Func)
|
|
{
|
|
WORD aw[6];
|
|
|
|
aw[0] = (WORD)parm7;
|
|
aw[1] = (WORD)parm6;
|
|
aw[2] = (WORD)parm5;
|
|
aw[3] = (WORD)parm4;
|
|
aw[4] = (WORD)parm3;
|
|
aw[5] = (WORD)parm2;
|
|
return RecordParms(hdc, Func, 6, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParms9(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5,
|
|
INT parm6, INT parm7, INT parm8, INT parm9, WORD Func)
|
|
{
|
|
WORD aw[8];
|
|
|
|
aw[0] = (WORD)parm9;
|
|
aw[1] = (WORD)parm8;
|
|
aw[2] = (WORD)parm7;
|
|
aw[3] = (WORD)parm6;
|
|
aw[4] = (WORD)parm5;
|
|
aw[5] = (WORD)parm4;
|
|
aw[6] = (WORD)parm3;
|
|
aw[7] = (WORD)parm2;
|
|
return RecordParms(hdc, Func, 8, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParmsD(HDC hdc, DWORD d1, WORD Func)
|
|
{
|
|
return RecordParms(hdc, Func, 2, (LPWORD) &d1);
|
|
}
|
|
|
|
BOOL MF16_RecordParmsWWD(HDC hdc, WORD w1, WORD w2, DWORD d3, WORD Func)
|
|
{
|
|
WORD aw[4];
|
|
|
|
aw[0] = LOWORD(d3);
|
|
aw[1] = HIWORD(d3);
|
|
aw[2] = w2;
|
|
aw[3] = w1;
|
|
return RecordParms(hdc, Func, 4, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParmsWWDW(HDC hdc, WORD w1, WORD w2, DWORD d3, WORD w4, WORD Func)
|
|
{
|
|
WORD aw[5];
|
|
|
|
aw[0] = w4;
|
|
aw[1] = LOWORD(d3);
|
|
aw[2] = HIWORD(d3);
|
|
aw[3] = w2;
|
|
aw[4] = w1;
|
|
return RecordParms(hdc, Func, 5, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParmsWWWWD(HDC hdc, WORD w1, WORD w2, WORD w3, WORD w4, DWORD d5, WORD Func)
|
|
{
|
|
WORD aw[6];
|
|
|
|
aw[0] = LOWORD(d5);
|
|
aw[1] = HIWORD(d5);
|
|
aw[2] = w4;
|
|
aw[3] = w3;
|
|
aw[4] = w2;
|
|
aw[5] = w1;
|
|
return RecordParms(hdc, Func, 6, aw);
|
|
}
|
|
|
|
BOOL MF16_RecordParmsPoly(HDC hdc, LPPOINT lpPoint, INT nCount, WORD Func)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpW,lpT;
|
|
DWORD cw;
|
|
INT ii;
|
|
|
|
cw = (nCount*sizeof(POINTS)/sizeof(WORD))+1;
|
|
lpT = lpW = (LPWORD)LocalAlloc(LMEM_FIXED, cw*sizeof(WORD));
|
|
if (!lpW)
|
|
return(FALSE);
|
|
|
|
*lpW++ = (WORD)nCount;
|
|
|
|
for(ii=0; ii<nCount; ii++)
|
|
{
|
|
*lpW++ = (WORD)lpPoint[ii].x;
|
|
*lpW++ = (WORD)lpPoint[ii].y;
|
|
}
|
|
|
|
fRet = RecordParms(hdc, Func, cw, lpT);
|
|
|
|
if (LocalFree((HANDLE)lpT))
|
|
ASSERTGDI(FALSE, "MF16_RecordParmsPoly: LocalFree Failed\n");
|
|
|
|
return (fRet);
|
|
}
|
|
|
|
// SetDIBitsToDevice
|
|
// StretchDIBits
|
|
|
|
BOOL MF16_RecordDIBits
|
|
(
|
|
HDC hdcDst,
|
|
int xDst,
|
|
int yDst,
|
|
int cxDst,
|
|
int cyDst,
|
|
int xDib,
|
|
int yDib,
|
|
int cxDib,
|
|
int cyDib,
|
|
DWORD iStartScan,
|
|
DWORD cScans,
|
|
DWORD cbBitsDib,
|
|
CONST VOID * pBitsDib,
|
|
DWORD cbBitsInfoDib,
|
|
CONST BITMAPINFO *pBitsInfoDib,
|
|
DWORD iUsageDib,
|
|
DWORD rop,
|
|
DWORD mrType
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpW;
|
|
LPWORD lpWStart;
|
|
WORD cwParms;
|
|
PBMIH lpDIBInfoHeader;
|
|
|
|
PUTS("MF16_RecrodDIBits\n");
|
|
|
|
ASSERTGDI(mrType == META_SETDIBTODEV || mrType == META_STRETCHDIB,
|
|
"MF16_RecrodDIBits: Bad mrType");
|
|
|
|
// Get the number of parameters to save.
|
|
|
|
cwParms = (WORD) ((mrType == META_SETDIBTODEV) ? 9 : 11); // in words
|
|
|
|
// Allocate space for DIB plus parameters.
|
|
|
|
lpWStart = lpW = (LPWORD) LocalAlloc(LMEM_FIXED,
|
|
cbBitsInfoDib + (cbBitsDib + 1) / 2 * 2 + cwParms*sizeof(WORD));
|
|
if (!lpW)
|
|
{
|
|
ERROR_ASSERT(FALSE, "MF16_RecordDIBits: out of memory\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
// Copy the parameters.
|
|
|
|
if (mrType == META_SETDIBTODEV)
|
|
{
|
|
*lpW++ = (WORD) iUsageDib;
|
|
*lpW++ = (WORD) cScans;
|
|
*lpW++ = (WORD) iStartScan;
|
|
*lpW++ = (WORD) yDib;
|
|
*lpW++ = (WORD) xDib;
|
|
*lpW++ = (WORD) cyDib;
|
|
*lpW++ = (WORD) cxDib;
|
|
*lpW++ = (WORD) yDst;
|
|
*lpW++ = (WORD) xDst;
|
|
}
|
|
else
|
|
{
|
|
*lpW++ = (WORD) LOWORD(rop);
|
|
*lpW++ = (WORD) HIWORD(rop);
|
|
*lpW++ = (WORD) iUsageDib;
|
|
*lpW++ = (WORD) cyDib;
|
|
*lpW++ = (WORD) cxDib;
|
|
*lpW++ = (WORD) yDib;
|
|
*lpW++ = (WORD) xDib;
|
|
*lpW++ = (WORD) cyDst;
|
|
*lpW++ = (WORD) cxDst;
|
|
*lpW++ = (WORD) yDst;
|
|
*lpW++ = (WORD) xDst;
|
|
}
|
|
|
|
// Save the start of the bitmap info header field.
|
|
|
|
lpDIBInfoHeader = (LPBITMAPINFOHEADER) lpW;
|
|
|
|
// cbBitsInfoDib must be word sized
|
|
|
|
ASSERTGDI(cbBitsInfoDib % 2 == 0,
|
|
"MF16_RecordDIBits: cbBitsInfoDib is not word aligned");
|
|
|
|
// Copy dib info if given.
|
|
|
|
if (cbBitsInfoDib)
|
|
{
|
|
if (pBitsInfoDib->bmiHeader.biSize == sizeof(BMCH))
|
|
{
|
|
CopyCoreToInfoHeader
|
|
(
|
|
lpDIBInfoHeader,
|
|
(LPBITMAPCOREHEADER) pBitsInfoDib
|
|
);
|
|
|
|
if (iUsageDib == DIB_RGB_COLORS)
|
|
{
|
|
RGBQUAD *prgbq;
|
|
RGBTRIPLE *prgbt;
|
|
UINT ui;
|
|
|
|
prgbq = ((PBMI) lpDIBInfoHeader)->bmiColors;
|
|
prgbt = ((PBMC) pBitsInfoDib)->bmciColors;
|
|
|
|
ASSERTGDI(cbBitsInfoDib >= sizeof(BMIH),
|
|
"MF16_RecordDIBits: Bad cbBitsInfoDib size");
|
|
|
|
for
|
|
(
|
|
ui = (UINT) (cbBitsInfoDib - sizeof(BMIH))
|
|
/ sizeof(RGBQUAD);
|
|
ui;
|
|
ui--
|
|
)
|
|
{
|
|
prgbq->rgbBlue = prgbt->rgbtBlue;
|
|
prgbq->rgbGreen = prgbt->rgbtGreen;
|
|
prgbq->rgbRed = prgbt->rgbtRed;
|
|
prgbq->rgbReserved = 0;
|
|
prgbq++; prgbt++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory
|
|
(
|
|
(PBYTE) lpDIBInfoHeader + sizeof(BMIH),
|
|
(PBYTE) pBitsInfoDib + sizeof(BMCH),
|
|
cbBitsInfoDib - sizeof(BMIH)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory
|
|
(
|
|
(PBYTE) lpDIBInfoHeader,
|
|
(PBYTE) pBitsInfoDib,
|
|
cbBitsInfoDib
|
|
);
|
|
|
|
if (pBitsInfoDib->bmiHeader.biBitCount >= 16)
|
|
{
|
|
DWORD UNALIGNED *pClrUsed = (DWORD UNALIGNED *)&lpDIBInfoHeader->biClrUsed;
|
|
*pClrUsed = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Copy dib bits.
|
|
|
|
RtlCopyMemory((PBYTE) lpDIBInfoHeader + cbBitsInfoDib, pBitsDib, cbBitsDib);
|
|
|
|
// Finally record the parameters into the file.
|
|
|
|
fRet = RecordParms(hdcDst, mrType,
|
|
cwParms + (cbBitsInfoDib + cbBitsDib + 1) / sizeof(WORD),
|
|
lpWStart);
|
|
|
|
if (lpWStart)
|
|
if (LocalFree((HANDLE) lpWStart))
|
|
ASSERTGDI(FALSE, "MF16_RecordDIBits: LocalFree Failed\n");
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
BOOL MF16_BitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight,
|
|
HDC hdcSrc, INT xSrc, INT ySrc, DWORD rop)
|
|
{
|
|
WORD aw[9];
|
|
|
|
// This is how windows works but really it should look at the ROP
|
|
if (hdcDest == hdcSrc || hdcSrc == NULL)
|
|
{
|
|
aw[0] = (WORD)LOWORD(rop);
|
|
aw[1] = (WORD)HIWORD(rop);
|
|
aw[2] = (WORD)ySrc;
|
|
aw[3] = (WORD)xSrc;
|
|
aw[4] = (WORD)0; // No DC necessary
|
|
aw[5] = (WORD)nHeight;
|
|
aw[6] = (WORD)nWidth;
|
|
aw[7] = (WORD)y;
|
|
aw[8] = (WORD)x;
|
|
|
|
return(RecordParms(hdcDest, META_DIBBITBLT, 9, aw));
|
|
}
|
|
else
|
|
return(RecordCommonBitBlt(hdcDest,x,y,nWidth,nHeight,hdcSrc,
|
|
xSrc,ySrc,nWidth,nHeight,rop,META_DIBBITBLT));
|
|
}
|
|
|
|
BOOL MF16_StretchBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight,
|
|
HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight, DWORD rop)
|
|
{
|
|
WORD aw[11];
|
|
|
|
// This is how windows works but really it should look at the ROP
|
|
if (hdcDest == hdcSrc || hdcSrc == NULL)
|
|
{
|
|
aw[0] = (WORD)LOWORD(rop);
|
|
aw[1] = (WORD)HIWORD(rop);
|
|
aw[2] = (WORD)nSrcHeight;
|
|
aw[3] = (WORD)nSrcWidth;
|
|
aw[4] = (WORD)ySrc;
|
|
aw[5] = (WORD)xSrc;
|
|
aw[6] = (WORD)0; // No DC necessary
|
|
aw[7] = (WORD)nHeight;
|
|
aw[8] = (WORD)nWidth;
|
|
aw[9] = (WORD)y;
|
|
aw[10] = (WORD)x;
|
|
|
|
return(RecordParms(hdcDest, META_DIBSTRETCHBLT, 11, aw));
|
|
}
|
|
else
|
|
return(RecordCommonBitBlt(hdcDest,x,y,nWidth,nHeight,hdcSrc,
|
|
xSrc,ySrc,nSrcWidth,nSrcHeight,rop,META_DIBSTRETCHBLT));
|
|
}
|
|
|
|
BOOL RecordCommonBitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight,
|
|
HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight, DWORD rop,
|
|
WORD wFunc)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
HBITMAP hBitmap;
|
|
LPWORD lpW;
|
|
LPWORD lpWStart = (LPWORD) NULL;
|
|
WORD cwParms;
|
|
BMIH bmih;
|
|
DWORD cbBitsInfo;
|
|
DWORD cbBits;
|
|
PBMIH lpDIBInfoHeader;
|
|
|
|
// hdcSrc must be a memory DC.
|
|
|
|
if (GetObjectType((HANDLE)hdcSrc) != OBJ_MEMDC)
|
|
{
|
|
ERROR_ASSERT(FALSE, "RecordCommonBitblt hdcSrc must be MEMDC\n");
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Retrieve the source bitmap.
|
|
|
|
hBitmap = SelectObject(hdcSrc, GetStockObject(PRIV_STOCK_BITMAP));
|
|
ERROR_ASSERT(hBitmap, "RecordCommonBitblt: SelectObject1 failed\n");
|
|
|
|
// Get the bitmap info header and sizes.
|
|
|
|
if (!bMetaGetDIBInfo(hdcSrc, hBitmap, &bmih,
|
|
&cbBitsInfo, &cbBits, DIB_RGB_COLORS, 0, TRUE))
|
|
goto RecordCommonBitBlt_exit;
|
|
|
|
// Get the number of parameters to save.
|
|
|
|
cwParms = (WORD) ((wFunc == META_DIBSTRETCHBLT) ? 10 : 8); // in words
|
|
|
|
// Allocate space for DIB plus parameters.
|
|
|
|
lpWStart = lpW = (LPWORD) LocalAlloc(LMEM_FIXED,
|
|
cbBitsInfo + cbBits + cwParms*sizeof(WORD));
|
|
if (!lpW)
|
|
{
|
|
ERROR_ASSERT(FALSE, "RecordCommonBitblt: out of memory\n");
|
|
goto RecordCommonBitBlt_exit;
|
|
}
|
|
|
|
// Copy the parameters.
|
|
|
|
*lpW++ = (WORD)LOWORD(rop);
|
|
*lpW++ = (WORD)HIWORD(rop);
|
|
|
|
if (wFunc == META_DIBSTRETCHBLT)
|
|
{
|
|
*lpW++ = (WORD)nSrcHeight;
|
|
*lpW++ = (WORD)nSrcWidth;
|
|
}
|
|
|
|
*lpW++ = (WORD)ySrc;
|
|
*lpW++ = (WORD)xSrc;
|
|
*lpW++ = (WORD)nHeight;
|
|
*lpW++ = (WORD)nWidth;
|
|
*lpW++ = (WORD)y;
|
|
*lpW++ = (WORD)x;
|
|
|
|
// Save the start of the bitmap info header field.
|
|
|
|
lpDIBInfoHeader = (LPBITMAPINFOHEADER) lpW;
|
|
|
|
// Copy the bitmap info header.
|
|
|
|
*lpDIBInfoHeader = bmih;
|
|
|
|
// Get bitmap info and bits.
|
|
|
|
if (!GetDIBits(hdcSrc,
|
|
hBitmap,
|
|
0,
|
|
(UINT) bmih.biHeight,
|
|
(LPBYTE) ((PBYTE) lpW + cbBitsInfo),
|
|
(LPBITMAPINFO) lpDIBInfoHeader,
|
|
DIB_RGB_COLORS))
|
|
{
|
|
ERROR_ASSERT(FALSE, "RecordCommonBitBlt: GetDIBits2 failed\n");
|
|
goto RecordCommonBitBlt_exit;
|
|
}
|
|
|
|
// Finally record the parameters into the file.
|
|
|
|
fRet = RecordParms(hdcDest, wFunc,
|
|
cwParms + (cbBitsInfo + cbBits) / sizeof(WORD),
|
|
lpWStart);
|
|
|
|
RecordCommonBitBlt_exit:
|
|
|
|
if (lpWStart)
|
|
if (LocalFree((HANDLE)lpWStart))
|
|
ASSERTGDI(FALSE, "RecordCommonBitBlt: LocalFree Failed\n");
|
|
|
|
if (!SelectObject(hdcSrc, hBitmap))
|
|
ASSERTGDI(FALSE, "RecordCommonBitblt: SelectObject2 failed\n");
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
BOOL MF16_DeleteRgn(HDC hdc, HANDLE hrgn)
|
|
{
|
|
UINT pos;
|
|
|
|
if (AddObjectToDCTable(hdc, hrgn, &pos, FALSE) != 1)
|
|
ASSERTGDI(FALSE, "MF16_DeleteRgn: AddObjectToDCTable failed");
|
|
|
|
return(RecordParms(hdc, META_DELETEOBJECT, 1, (LPWORD)&pos));
|
|
}
|
|
|
|
BOOL MF16_DeleteObject(HANDLE hObject)
|
|
{
|
|
INT iCurDC;
|
|
UINT pos;
|
|
UINT iObjType;
|
|
PMFRECORDER16 pMFRec;
|
|
PMETALINK16 pml16;
|
|
|
|
pml16 = pmetalink16Get(hObject);
|
|
ASSERTGDI(pml16, "MF16_DeleteObject: Metalink is NULL\n");
|
|
|
|
iObjType = GetObjectType(hObject);
|
|
ASSERTGDI(iObjType != OBJ_REGION, "MF16_DeleteObject: region unexpected");
|
|
|
|
// Delete the object from each metafile DC which references it.
|
|
|
|
for(iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--)
|
|
{
|
|
// Send a DeleteObject record to each metafile
|
|
|
|
HDC hdc16 = pml16->ahMetaDC16[iCurDC];
|
|
|
|
if (!IS_METADC16_TYPE(hdc16))
|
|
{
|
|
RIP("MF16_DELETEOBJECT: invalid metaDC16\n");
|
|
continue;
|
|
}
|
|
|
|
// If the object is not selected then delete it, if it is then mark as predeleted
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc16);
|
|
|
|
if (pMFRec->recCurObjects[iObjType - MIN_OBJ_TYPE] != hObject)
|
|
{
|
|
if (AddObjectToDCTable(hdc16, hObject, &pos, FALSE) == 1)
|
|
RecordParms(hdc16, META_DELETEOBJECT, 1, (LPWORD)&pos);
|
|
else
|
|
RIP("MF16_DeleteObject Metalink16 and metadc table not in sync\n");
|
|
}
|
|
else
|
|
{
|
|
if (pMFRec->metaHeader.mtNoObjects)
|
|
{
|
|
UINT ii;
|
|
POBJECTTABLE pobjt;
|
|
|
|
pobjt = (POBJECTTABLE) pMFRec->hObjectTable;
|
|
|
|
for (ii=0; ii < (UINT) pMFRec->metaHeader.mtNoObjects; ii++)
|
|
{
|
|
if (pobjt[ii].CurHandle == hObject)
|
|
{
|
|
pobjt[ii].fPreDeleted = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This Object has been freed from all 3.x metafiles so free its MetaList16
|
|
// if the metalink field is in use resize METALINK16
|
|
|
|
if (pml16->metalink)
|
|
{
|
|
if (pml16->cMetaDC16 > 1)
|
|
pml16 = pmetalink16Resize(hObject,1);
|
|
|
|
if (pml16 == NULL)
|
|
{
|
|
ASSERTGDI(FALSE, "MF16_DeleteObject LocalReAlloc failed\n");
|
|
return (FALSE);
|
|
}
|
|
|
|
pml16->cMetaDC16 = 0;
|
|
pml16->ahMetaDC16[0] = (HDC) 0;
|
|
}
|
|
else
|
|
{
|
|
if (!bDeleteMetalink16(hObject))
|
|
ASSERTGDI(FALSE, "MF16_DeleteObject LocalFree failed\n");
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL MF16_RealizePalette(HDC hdc)
|
|
{
|
|
HPALETTE hpal;
|
|
PMFRECORDER16 pMFRec;
|
|
PMETALINK16 pml16;
|
|
|
|
ASSERTGDI(IS_METADC16_TYPE(hdc),"MF16_RealizePalette - invalid handle\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
hpal = pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE];
|
|
ASSERTGDI(hpal, "MF16_RealizePalette: bad hpal\n");
|
|
|
|
// emit the palette again only if the palette is dirty.
|
|
|
|
pml16 = pmetalink16Get(hpal);
|
|
|
|
ASSERTGDI(IS_STOCKOBJ(hpal) || pml16,"MF16_RealizePalette - pml16 == NULL\n");
|
|
|
|
if (pml16)
|
|
{
|
|
if (PtrToUlong(pml16->pv) != pMFRec->iPalVer)
|
|
if (!MakeLogPalette(hdc, hpal, META_SETPALENTRIES))
|
|
return(FALSE);
|
|
|
|
// record which version of the palette the metafile is synced to.
|
|
|
|
pMFRec->iPalVer = PtrToUlong(pml16->pv);
|
|
}
|
|
|
|
return(RecordParms(hdc, META_REALIZEPALETTE, 0, (LPWORD)NULL));
|
|
}
|
|
|
|
BOOL MF16_AnimatePalette
|
|
(
|
|
HPALETTE hpal,
|
|
UINT iStart,
|
|
UINT cEntries,
|
|
CONST PALETTEENTRY *pPalEntries
|
|
)
|
|
{
|
|
INT iCurDC;
|
|
PMETALINK16 pml16;
|
|
LPWORD lpW,lpT;
|
|
DWORD cw;
|
|
UINT ii;
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
if (!(pml16 = pmetalink16Get(hpal)))
|
|
return(FALSE);
|
|
|
|
cw = (cEntries * sizeof(PALETTEENTRY) / sizeof(WORD)) + 2;
|
|
lpT = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cw * sizeof(WORD));
|
|
|
|
if (!lpW)
|
|
return(FALSE);
|
|
|
|
*lpW++ = (WORD) iStart;
|
|
*lpW++ = (WORD) cEntries;
|
|
|
|
for (ii = 0; ii < cEntries; ii++)
|
|
((PPALETTEENTRY) lpW)[ii] = pPalEntries[ii];
|
|
|
|
// Send a AnimatePalette record to each associated metafile that has the
|
|
// palette selected.
|
|
|
|
for (iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--)
|
|
{
|
|
HDC hdc16 = pml16->ahMetaDC16[iCurDC];
|
|
|
|
if (!IS_METADC16_TYPE(hdc16))
|
|
{
|
|
ASSERTGDI(FALSE, "MF16_AnimatePalette: invalid metaDC16\n");
|
|
continue;
|
|
}
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc16);
|
|
if (pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] == hpal)
|
|
if (!RecordParms(pml16->ahMetaDC16[iCurDC], META_ANIMATEPALETTE, cw, lpT))
|
|
ASSERTGDI(FALSE, "MF16_AnimatePalette: RecordParms Failed\n");
|
|
}
|
|
|
|
if (LocalFree((HANDLE) lpT))
|
|
ASSERTGDI(FALSE, "MF16_AnimatePalette: LocalFree Failed\n");
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL MF16_ResizePalette(HPALETTE hpal, UINT nCount)
|
|
{
|
|
INT iCurDC;
|
|
PMETALINK16 pml16;
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
if (!(pml16 = pmetalink16Get(hpal)))
|
|
return(FALSE);
|
|
|
|
// Send a ResizePalette record to each associated metafile that has the
|
|
// palette selected.
|
|
|
|
for (iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--)
|
|
{
|
|
HDC hdc16 = pml16->ahMetaDC16[iCurDC];
|
|
|
|
if (!IS_METADC16_TYPE(hdc16))
|
|
{
|
|
ASSERTGDI(FALSE, "MF16_ResizePalette: invalid metaDC16\n");
|
|
continue;
|
|
}
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc16);
|
|
|
|
if (pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] == hpal)
|
|
if (!RecordParms(pml16->ahMetaDC16[iCurDC], META_RESIZEPALETTE, 1, (LPWORD) &nCount))
|
|
ASSERTGDI(FALSE, "MF16_ResizePalette: RecordParms Failed\n");
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL MF16_DrawRgn(HDC hdc, HRGN hrgn, HBRUSH hBrush, INT cx, INT cy, WORD Func)
|
|
{
|
|
WORD aw[4];
|
|
BOOL bRet;
|
|
|
|
// Each region function has at least a region to record
|
|
aw[0] = (WORD)RecordObject(hdc, hrgn);
|
|
|
|
switch(Func)
|
|
{
|
|
case META_PAINTREGION:
|
|
case META_INVERTREGION:
|
|
bRet = RecordParms(hdc, Func, 1, aw);
|
|
break;
|
|
|
|
case META_FILLREGION:
|
|
aw[1] = (WORD)RecordObject(hdc, hBrush);
|
|
bRet = RecordParms(hdc, Func, 2, aw);
|
|
break;
|
|
|
|
case META_FRAMEREGION:
|
|
aw[1] = (WORD)RecordObject(hdc, hBrush);
|
|
aw[2] = (WORD)cy;
|
|
aw[3] = (WORD)cx;
|
|
bRet = RecordParms(hdc, Func, 4, aw);
|
|
break;
|
|
|
|
default:
|
|
ASSERTGDI(FALSE, "MF16_DrawRgn: Bad Func\n");
|
|
bRet = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Delete the metafile region handle in the metafile after use!
|
|
// The reason is that a region can be modified (e.g. SetRectRgn)
|
|
// between each use and we have to re-record it each time it is used
|
|
// unless we use a dirty flag.
|
|
|
|
if (!MF16_DeleteRgn(hdc, hrgn))
|
|
ASSERTGDI(FALSE, "MF16_DrawRgn: MF16_DeleteRgn failed\n");
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
BOOL MF16_PolyPolygon(HDC hdc, CONST POINT *lpPoint, CONST INT *lpPolyCount, INT nCount)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpW,lpT;
|
|
DWORD cw;
|
|
INT cPt = 0;
|
|
INT ii;
|
|
|
|
for(ii=0; ii<nCount; ii++)
|
|
cPt += lpPolyCount[ii];
|
|
|
|
cw = 1+nCount+(cPt*sizeof(POINTS)/sizeof(WORD));
|
|
lpT = lpW = (LPWORD)LocalAlloc(LMEM_FIXED, cw*sizeof(WORD));
|
|
if (!lpW)
|
|
return(FALSE);
|
|
|
|
// first is the count
|
|
*lpW++ = (WORD)nCount;
|
|
|
|
// second is the list of poly counts
|
|
for(ii=0; ii<nCount; ii++)
|
|
*lpW++ = (WORD)lpPolyCount[ii];
|
|
|
|
// third is the list of points
|
|
for(ii=0; ii<cPt; ii++)
|
|
{
|
|
*lpW++ = (WORD)lpPoint[ii].x;
|
|
*lpW++ = (WORD)lpPoint[ii].y;
|
|
}
|
|
|
|
fRet = RecordParms(hdc, META_POLYPOLYGON, cw, lpT);
|
|
|
|
if(LocalFree((HANDLE)lpT))
|
|
ASSERTGDI(FALSE, "MF16_PolyPolygon: LocalFree Failed\n");
|
|
|
|
return (fRet);
|
|
}
|
|
|
|
BOOL MF16_SelectClipRgn(HDC hdc, HRGN hrgn, int iMode)
|
|
{
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
if (!IS_METADC16_TYPE(hdc))
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
PUTS("MF16_SelectClipRgn\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
if (iMode != RGN_COPY)
|
|
return(FALSE);
|
|
|
|
// We will emit SelectObject record for clip region just like Windows.
|
|
// However, a null region cannot be recorded in the SelectObject call since
|
|
// the handle does not identify the object type. This is a bug in Win 3.1!
|
|
//
|
|
// BUG 8419 winproj 4 has a bug where it counts on this bug. Chicago
|
|
// also has this bug so we will have this bug.
|
|
|
|
if (hrgn == (HRGN) 0)
|
|
{
|
|
#ifdef RECORD_SELECTCLIPRGN_NULL
|
|
BOOL fRet;
|
|
|
|
fRet = MF16_RecordParms2(hdc, 0, META_SELECTCLIPREGION);
|
|
|
|
// maintain the new selection in the CurObject table
|
|
|
|
pMFRec->recCurObjects[OBJ_REGION - MIN_OBJ_TYPE] = 0;
|
|
|
|
return(fRet);
|
|
#else
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
else
|
|
return(MF16_SelectObject(hdc, hrgn) ? TRUE : FALSE);
|
|
}
|
|
|
|
// SelectObject returns previous object! - new in win3.1
|
|
|
|
HANDLE MF16_SelectObject(HDC hdc, HANDLE h)
|
|
{
|
|
HANDLE hOldObject;
|
|
WORD position;
|
|
PMFRECORDER16 pMFRec;
|
|
UINT iType;
|
|
|
|
|
|
PUTS("MF16_SelectObject\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
if (!IS_METADC16_TYPE(hdc) || !pMFRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(0);
|
|
}
|
|
|
|
iType = GetObjectType(h);
|
|
|
|
if ((iType == 0) || !h || (position = (WORD) RecordObject(hdc, h)) == (WORD) -1)
|
|
return((HANDLE) 0);
|
|
else
|
|
{
|
|
if (!RecordParms(hdc, META_SELECTOBJECT, 1, &position))
|
|
return((HANDLE) 0);
|
|
|
|
// maintain the new selection in the CurObject table
|
|
|
|
ASSERTGDI(iType <= MAX_OBJ_TYPE && iType >= MIN_OBJ_TYPE,
|
|
"MF16_SelectObject type > max\n");
|
|
|
|
hOldObject = pMFRec->recCurObjects[iType - MIN_OBJ_TYPE];
|
|
pMFRec->recCurObjects[iType - MIN_OBJ_TYPE] = h;
|
|
|
|
// return the previously selected object or 1 if it is a region
|
|
// (for compatibility) - new in win3.1
|
|
|
|
if (iType == OBJ_REGION)
|
|
{
|
|
// We also delete the region handle here!
|
|
// The reason is that a region can be modified (e.g. SetRectRgn)
|
|
// between each use and we have to re-record it each time it is used
|
|
// unless we use a dirty flag. This is a bug in win3.1
|
|
|
|
return(MF16_DeleteRgn(hdc, h) ? (HANDLE) 1 : (HANDLE) 0);
|
|
}
|
|
else
|
|
{
|
|
return(hOldObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL MF16_SelectPalette(HDC hdc, HPALETTE hpal)
|
|
{
|
|
WORD position;
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
if (!IS_METADC16_TYPE(hdc) || !pMFRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(0);
|
|
}
|
|
|
|
if (!hpal || (position = (WORD) RecordObject(hdc, (HANDLE) hpal)) == (WORD) -1)
|
|
return(FALSE);
|
|
else
|
|
{
|
|
PMETALINK16 pml16;
|
|
|
|
if (!RecordParms(hdc, META_SELECTPALETTE, 1, &position))
|
|
return(FALSE);
|
|
|
|
// maintain the new selection in the CurObject table
|
|
pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] = hpal;
|
|
|
|
// Also record which version of the palette we are synced with
|
|
// so we know whether to emit a new palette when RealizePalette
|
|
// is called
|
|
|
|
pml16 = pmetalink16Get(hpal);
|
|
ASSERTGDI(IS_STOCKOBJ(hpal) || pml16,"MF16_RealizePalette - pml16 == NULL\n");
|
|
|
|
if (pml16)
|
|
pMFRec->iPalVer = PtrToUlong(pml16->pv);
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
BOOL MF16_TextOut(HDC hdc, INT x, INT y, LPCSTR lpString, INT nCount, BOOL bUnicode)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpw, lpT;
|
|
DWORD cw;
|
|
|
|
cw = (nCount + 1)/sizeof(WORD) + 3; // word-aligned character string
|
|
lpT = lpw = (LPWORD) LocalAlloc(LMEM_FIXED, cw*sizeof(WORD));
|
|
|
|
if (!lpw)
|
|
return(FALSE);
|
|
|
|
*lpw++ = (WORD)nCount;
|
|
|
|
// Copy the string
|
|
|
|
if (!bUnicode)
|
|
{
|
|
RtlCopyMemory(lpw, lpString, nCount);
|
|
}
|
|
else
|
|
{
|
|
(void) bToASCII_N((LPSTR) lpw, nCount, (LPWSTR) lpString, nCount);
|
|
}
|
|
|
|
lpw += (nCount+1)/sizeof(WORD); // keep word aligned
|
|
|
|
*lpw++ = (WORD)y;
|
|
*lpw++ = (WORD)x;
|
|
|
|
fRet = RecordParms(hdc, META_TEXTOUT, cw, lpT);
|
|
|
|
if(LocalFree((HANDLE)lpT))
|
|
ASSERTGDI(FALSE, "MF16_TextOut: LocalFree Failed\n");
|
|
return (fRet);
|
|
}
|
|
|
|
BOOL MF16_PolyTextOut(HDC hdc, CONST POLYTEXTA *ppta, int cpta, BOOL bUnicode)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < cpta; i++)
|
|
{
|
|
if (!MF16_ExtTextOut(hdc, ppta[i].x, ppta[i].y, ppta[i].uiFlags,
|
|
&ppta[i].rcl, (LPCSTR) ppta[i].lpstr, (INT) ppta[i].n,
|
|
(LPINT) ppta[i].pdx, bUnicode))
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL MF16_ExtTextOut(HDC hdc, INT x, INT y, UINT flOptions, CONST RECT *lpRect,
|
|
LPCSTR lpString, INT nCount, CONST INT *lpDX, BOOL bUnicode)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpw, lpT;
|
|
DWORD cw = 0;
|
|
INT nUnicodeCount = nCount;
|
|
char *pjAnsiString = NULL;
|
|
|
|
if(bUnicode)
|
|
{
|
|
// compute the real count of characters in the string
|
|
|
|
RtlUnicodeToMultiByteSize((PULONG) &nCount, (PWCH) lpString,
|
|
nCount * sizeof(WCHAR));
|
|
}
|
|
|
|
// Compute buffer space needed
|
|
// room for the char string
|
|
// room for the 4 words that are the fixed parms
|
|
// if there is a dx array, we need room for it
|
|
// if the rectangle is being used, we need room for it
|
|
// and we need extra byte for eventual word roundoff
|
|
//
|
|
|
|
if (flOptions & ETO_PDY)
|
|
return FALSE;
|
|
|
|
cw += (lpDX) ? nCount : 0; // DX array
|
|
cw += (flOptions & (ETO_OPAQUE | ETO_CLIPPED)) ? 4 : 0; // sizeof RECTS
|
|
cw += 4; // x,y,options and count
|
|
cw += (nCount + 1)/sizeof(WORD);
|
|
|
|
lpT = lpw = (LPWORD) LocalAlloc(LMEM_FIXED, cw*sizeof(WORD));
|
|
if (!lpw)
|
|
return(FALSE);
|
|
|
|
*lpw++ = (WORD)y;
|
|
*lpw++ = (WORD)x;
|
|
*lpw++ = (WORD)nCount;
|
|
*lpw++ = (WORD)flOptions;
|
|
|
|
// Copy the rect if present
|
|
if (flOptions & (ETO_OPAQUE | ETO_CLIPPED))
|
|
{
|
|
ERROR_ASSERT(lpRect, "MF16_ExtTextOut: expect valid lpRect\n");
|
|
*lpw++ = (WORD)lpRect->left;
|
|
*lpw++ = (WORD)lpRect->top;
|
|
*lpw++ = (WORD)lpRect->right;
|
|
*lpw++ = (WORD)lpRect->bottom;
|
|
}
|
|
|
|
// Copy the string
|
|
if (!bUnicode)
|
|
{
|
|
RtlCopyMemory(lpw, lpString, nCount);
|
|
}
|
|
else
|
|
{
|
|
(void) bToASCII_N((LPSTR) lpw, nCount, (LPWSTR) lpString, nUnicodeCount);
|
|
pjAnsiString = (char*) lpw;
|
|
}
|
|
|
|
lpw += (nCount+1)/sizeof(WORD); // keep word aligned
|
|
|
|
if (lpDX)
|
|
{
|
|
INT ii;
|
|
|
|
if(nCount != nUnicodeCount)
|
|
{
|
|
INT jj;
|
|
|
|
for(ii=0,jj=0; ii < nCount; ii++,jj++)
|
|
{
|
|
*lpw++ = (WORD)lpDX[jj];
|
|
|
|
if(IsDBCSLeadByte(pjAnsiString[ii]))
|
|
{
|
|
*lpw++ = 0;
|
|
ii++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(ii=0; ii<nCount; ii++)
|
|
*lpw++ = (WORD)lpDX[ii];
|
|
}
|
|
|
|
}
|
|
|
|
fRet = RecordParms(hdc, META_EXTTEXTOUT, cw, lpT);
|
|
|
|
if (LocalFree((HANDLE)lpT))
|
|
ASSERTGDI(FALSE, "MF16_ExtTextOut: LocalFree Failed\n");
|
|
|
|
return (fRet);
|
|
}
|
|
|
|
BOOL MF16_Escape(HDC hdc, int nEscape, int nCount, LPCSTR lpInData, LPVOID lpOutData)
|
|
{
|
|
BOOL fRet;
|
|
LPWORD lpW,lpT;
|
|
DWORD cw;
|
|
|
|
// If a metafile is retrieved from GetWinMetaFileBits, it may contain
|
|
// an embedded enhanced metafile. Do not include the enhanced metafile
|
|
// if we are playing the metafile to another metafile.
|
|
|
|
if (nEscape == MFCOMMENT
|
|
&& nCount > sizeof(META_ESCAPE_ENHANCED_METAFILE) - sizeof(DWORD) - 3 * sizeof(WORD)
|
|
&& ((DWORD UNALIGNED *) lpInData)[0] == MFCOMMENT_IDENTIFIER
|
|
&& ((DWORD UNALIGNED *) lpInData)[1] == MFCOMMENT_ENHANCED_METAFILE)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
// Some wow apps (e.g. amipro) use metafiles for printing. As a result,
|
|
// we need to record these printing escapes.
|
|
|
|
cw = 2 + ((nCount + 1) / sizeof(WORD));
|
|
lpT = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cw * sizeof(WORD));
|
|
if (!lpW)
|
|
return(FALSE);
|
|
|
|
*lpW++ = (WORD) nEscape; // escape number
|
|
*lpW++ = (WORD) nCount; // count of input data buffer
|
|
|
|
RtlCopyMemory(lpW, lpInData, nCount);
|
|
|
|
fRet = RecordParms(hdc, META_ESCAPE, cw, lpT);
|
|
|
|
if (LocalFree((HANDLE) lpT))
|
|
ASSERTGDI(FALSE, "MF16_Escape: LocalFree Failed\n");
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* *
|
|
* RecordParms *
|
|
* *
|
|
* Parameters: 1.hMF handle to a metafile header. *
|
|
* 2.The magic number of the function being recorded. *
|
|
* 3.The number of parmmeter of the function (size of lpParm *
|
|
* in words) *
|
|
* 4.A long pointer to parameters stored in reverse order *
|
|
* *
|
|
* Warning call only once per function because max record is updated. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
BOOL RecordParms(HDC hdc, DWORD magic, DWORD cw, CONST WORD *lpParm)
|
|
{
|
|
PMFRECORDER16 pMFRec;
|
|
METARECORD MFRecord;
|
|
|
|
PUTSX("RecordParms %lX\n", (ULONG)magic);
|
|
ASSERTGDI(HIWORD(magic) == 0, "RecordParms: bad magic\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
if (!IS_METADC16_TYPE(hdc) || !pMFRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Make sure the Metafile hasn't died before we continue
|
|
|
|
if (!(pMFRec->recFlags & METAFILEFAILURE))
|
|
{
|
|
MFRecord.rdSize = SIZEOF_METARECORDHEADER/sizeof(WORD) + cw;
|
|
MFRecord.rdFunction = (WORD)magic;
|
|
|
|
// Write the header
|
|
|
|
if (!AttemptWrite(pMFRec, SIZEOF_METARECORDHEADER, (LPBYTE)&MFRecord))
|
|
return(FALSE);
|
|
|
|
// Write the data
|
|
|
|
if (!AttemptWrite(pMFRec, cw*sizeof(WORD), (LPBYTE)lpParm))
|
|
return(FALSE);
|
|
|
|
// Update max record size
|
|
|
|
if (MFRecord.rdSize > pMFRec->metaHeader.mtMaxRecord)
|
|
pMFRec->metaHeader.mtMaxRecord = MFRecord.rdSize;
|
|
}
|
|
return (TRUE); // Win 3.1 returns true even if METAFAILEFAILURE is on!
|
|
}
|
|
|
|
/***************************** Internal Function ***************************\
|
|
* AttemptWrite
|
|
*
|
|
* Tries to write data to a metafile disk file
|
|
*
|
|
* dwBytes is the byte count of lpData.
|
|
*
|
|
* Returns TRUE iff the write was sucessful
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL AttemptWrite(PMFRECORDER16 pMFRec, DWORD dwBytes, LPBYTE lpData)
|
|
{
|
|
DWORD cbWritten;
|
|
BOOL fRet;
|
|
|
|
PUTS("AttemptWrite\n");
|
|
|
|
ASSERTGDI(dwBytes % 2 == 0, "AttemptWrite: bad dwBytes\n"); // must be even
|
|
ASSERTGDI(!(pMFRec->recFlags & METAFILEFAILURE),
|
|
"AttemptWrite: Bad recording\n");
|
|
|
|
// Handle disk file.
|
|
|
|
if (pMFRec->metaHeader.mtType == DISKMETAFILE)
|
|
{
|
|
// Flush the buffer if it's not large enough.
|
|
|
|
if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer)
|
|
{
|
|
fRet = WriteFile(pMFRec->hFile, (LPBYTE)pMFRec->hMem,
|
|
pMFRec->ibBuffer, &cbWritten, (LPOVERLAPPED)NULL);
|
|
if (!fRet || (cbWritten != pMFRec->ibBuffer))
|
|
{
|
|
ERROR_ASSERT(FALSE, "AttemptWrite: Write1 failed\n");
|
|
goto AttemptWrite_Error;
|
|
}
|
|
pMFRec->ibBuffer = 0; // reset buffer info
|
|
}
|
|
|
|
// If the data is still too large, write it out to disk directly.
|
|
|
|
if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer)
|
|
{
|
|
fRet = WriteFile(pMFRec->hFile, lpData,
|
|
dwBytes, &cbWritten, (LPOVERLAPPED)NULL);
|
|
if (!fRet || (cbWritten != dwBytes))
|
|
{
|
|
ERROR_ASSERT(FALSE, "AttemptWrite: Write2 failed\n");
|
|
goto AttemptWrite_Error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Store data in the buffer.
|
|
|
|
RtlCopyMemory((LPBYTE)pMFRec->hMem + pMFRec->ibBuffer, lpData, dwBytes);
|
|
pMFRec->ibBuffer += dwBytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Handle memory file.
|
|
|
|
// Grow the buffer if necessary.
|
|
|
|
if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer)
|
|
{
|
|
DWORD cbNewSize;
|
|
HANDLE hMem;
|
|
|
|
cbNewSize = pMFRec->cbBuffer + MF16_BUFSIZE_INC
|
|
+ dwBytes / MF16_BUFSIZE_INC * MF16_BUFSIZE_INC;
|
|
|
|
hMem = LocalReAlloc(pMFRec->hMem, cbNewSize, LMEM_MOVEABLE);
|
|
if (hMem == NULL)
|
|
{
|
|
ERROR_ASSERT(FALSE, "AttemptWrite: out of memory\n");
|
|
goto AttemptWrite_Error;
|
|
}
|
|
pMFRec->hMem = hMem;
|
|
pMFRec->cbBuffer = cbNewSize;
|
|
}
|
|
|
|
// Record the data.
|
|
|
|
RtlCopyMemory((LPBYTE)pMFRec->hMem + pMFRec->ibBuffer, lpData, dwBytes);
|
|
pMFRec->ibBuffer += dwBytes;
|
|
}
|
|
|
|
// Update the header size.
|
|
|
|
pMFRec->metaHeader.mtSize += dwBytes/sizeof(WORD);
|
|
|
|
return(TRUE);
|
|
|
|
AttemptWrite_Error:
|
|
|
|
MarkMetaFile(pMFRec);
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/***************************** Internal Function ***************************\
|
|
* VOID MarkMetaFile
|
|
*
|
|
* Marks a metafile as failed
|
|
*
|
|
* Effects:
|
|
* Frees the metafile resources
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID MarkMetaFile(PMFRECORDER16 pMFRec)
|
|
{
|
|
// Clean up is done in CloseMetaFile.
|
|
|
|
PUTS("MarkMetaFile\n");
|
|
|
|
pMFRec->recFlags |= METAFILEFAILURE;
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* MakeLogPalette
|
|
*
|
|
* Records either CreatePalette or SetPaletteEntries
|
|
*
|
|
* Returns TRUE iff sucessful
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL MakeLogPalette(HDC hdc, HANDLE hPal, WORD magic)
|
|
{
|
|
WORD cPalEntries;
|
|
BOOL fStatus = FALSE;
|
|
DWORD cbPalette;
|
|
LPLOGPALETTE lpPalette;
|
|
|
|
PUTS("MakeLogPalette\n");
|
|
|
|
if (!GetObject(hPal, sizeof(WORD), &cPalEntries))
|
|
{
|
|
ERROR_ASSERT(FALSE, "MakeLogPalette: GetObject Failed\n");
|
|
return(fStatus);
|
|
}
|
|
|
|
// alloc memory and get the palette entries
|
|
|
|
if (lpPalette = (LPLOGPALETTE)LocalAlloc(LMEM_FIXED,
|
|
cbPalette = sizeof(LOGPALETTE)-sizeof(PALETTEENTRY)+sizeof(PALETTEENTRY)*cPalEntries))
|
|
{
|
|
lpPalette->palNumEntries = cPalEntries;
|
|
|
|
GetPaletteEntries(hPal, 0, cPalEntries, lpPalette->palPalEntry);
|
|
|
|
if (magic == (META_CREATEPALETTE & 255))
|
|
{
|
|
lpPalette->palVersion = 0x300;
|
|
magic = META_CREATEPALETTE;
|
|
}
|
|
else if (magic == (META_SETPALENTRIES & 255))
|
|
{
|
|
lpPalette->palVersion = 0; /* really "starting index" */
|
|
magic = META_SETPALENTRIES;
|
|
}
|
|
|
|
fStatus = RecordParms(hdc, magic, (DWORD)cbPalette >> 1, (LPWORD)lpPalette);
|
|
|
|
if (LocalFree((HANDLE)lpPalette))
|
|
ASSERTGDI(FALSE, "MakeLogPalette: LocalFree Failed\n");
|
|
}
|
|
|
|
return(fStatus);
|
|
}
|
|
|
|
|
|
/***************************** Internal Function ***************************\
|
|
* RecordObject
|
|
*
|
|
* Records the use of an object by creating the object
|
|
*
|
|
* Returns: index of object in table
|
|
* -1 if error
|
|
*
|
|
\***************************************************************************/
|
|
|
|
WIN3REGION w3rgnEmpty =
|
|
{
|
|
0, // nextInChain
|
|
6, // ObjType
|
|
0x2F6, // ObjCount
|
|
sizeof(WIN3REGION) - sizeof(SCAN) + 2,
|
|
// cbRegion
|
|
0, // cScans
|
|
0, // maxScan
|
|
{0,0,0,0}, // rcBounding
|
|
{0,0,0,{0,0},0} // aScans[]
|
|
};
|
|
|
|
WORD RecordObject(HDC hdc, HANDLE hObject)
|
|
{
|
|
UINT status;
|
|
UINT iPosition;
|
|
HDC hdcScreen = (HDC) 0;
|
|
int iType;
|
|
UINT iUsage;
|
|
|
|
PUTS("RecordObject\n");
|
|
|
|
// Validate the object.
|
|
|
|
iType = LO_TYPE(hObject);
|
|
|
|
if (iType != LO_PEN_TYPE &&
|
|
iType != LO_BRUSH_TYPE &&
|
|
iType != LO_FONT_TYPE &&
|
|
iType != LO_REGION_TYPE &&
|
|
iType != LO_PALETTE_TYPE
|
|
)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return((WORD) -1);
|
|
}
|
|
|
|
// Add the object to the metafiles list.
|
|
|
|
status = AddObjectToDCTable(hdc, hObject, &iPosition, TRUE);
|
|
|
|
// An error occurred.
|
|
|
|
if (status == (UINT) -1)
|
|
return((WORD) -1);
|
|
|
|
// Object already exists.
|
|
|
|
if (status == 1)
|
|
return((WORD) iPosition);
|
|
|
|
ASSERTGDI(!status, "RecordObject: Bad return code from AddObjectToDCTable\n");
|
|
|
|
// Object does not exist, record it.
|
|
|
|
if (iType != LO_REGION_TYPE) // don't add regions to the metalist!
|
|
if (!AddDCToObjectMetaList16(hdc,hObject))
|
|
return((WORD) -1);
|
|
|
|
switch (iType)
|
|
{
|
|
case LO_PEN_TYPE:
|
|
{
|
|
LOGPEN16 logpen16;
|
|
|
|
GetObject16AndType(hObject, (LPVOID)&logpen16);
|
|
status = (UINT) RecordParms(hdc, (WORD)META_CREATEPENINDIRECT,
|
|
(DWORD)((sizeof(LOGPEN16) + 1) >> 1),
|
|
(LPWORD)&logpen16);
|
|
break;
|
|
}
|
|
|
|
case LO_FONT_TYPE:
|
|
{
|
|
LOGFONT16 logfont16;
|
|
|
|
GetObject16AndType(hObject, (LPVOID)&logfont16);
|
|
|
|
/* size of LOGFONT adjusted based on the length of the facename */
|
|
status = (UINT) RecordParms(hdc, META_CREATEFONTINDIRECT,
|
|
(DWORD)((sizeof(LOGFONT16) + 1) >> 1),
|
|
(LPWORD) &logfont16);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* in win2, METACREATEREGION records contained an entire region object,
|
|
* including the full header. this header changed in win3.
|
|
*
|
|
* to remain compatible, the region records will be saved with the
|
|
* win2 header. here we save our region with a win2 header.
|
|
*/
|
|
case LO_REGION_TYPE:
|
|
{
|
|
PWIN3REGION lpw3rgn;
|
|
DWORD cbNTRgnData;
|
|
DWORD curRectl;
|
|
WORD cScans;
|
|
WORD maxScanEntry;
|
|
WORD curScanEntry;
|
|
DWORD cbw3data;
|
|
PRGNDATA lprgn;
|
|
LPRECT lprc;
|
|
PSCAN lpScan;
|
|
|
|
ASSERTGDI(!status, "RecordObject: bad status\n");
|
|
|
|
// Get the NT Region Data
|
|
cbNTRgnData = GetRegionData(hObject, 0, NULL);
|
|
if (cbNTRgnData == 0)
|
|
break;
|
|
|
|
lprgn = (PRGNDATA) LocalAlloc(LMEM_FIXED, cbNTRgnData);
|
|
if (!lprgn)
|
|
break;
|
|
|
|
cbNTRgnData = GetRegionData(hObject, cbNTRgnData, lprgn);
|
|
if (cbNTRgnData == 0)
|
|
{
|
|
LocalFree((HANDLE) lprgn);
|
|
break;
|
|
}
|
|
|
|
// Handle the empty region.
|
|
|
|
if (!lprgn->rdh.nCount)
|
|
{
|
|
status = (UINT) RecordParms(hdc, META_CREATEREGION,
|
|
(sizeof(WIN3REGION) - sizeof(SCAN)) >> 1, // Convert to count of words
|
|
(LPWORD) &w3rgnEmpty);
|
|
|
|
LocalFree((HANDLE)lprgn);
|
|
break;
|
|
}
|
|
|
|
lprc = (LPRECT)lprgn->Buffer;
|
|
|
|
// Create the Windows 3.x equivalent
|
|
|
|
// worst case is one scan for each rect
|
|
cbw3data = 2*sizeof(WIN3REGION) + (WORD)lprgn->rdh.nCount*sizeof(SCAN);
|
|
|
|
lpw3rgn = (PWIN3REGION)LocalAlloc(LMEM_FIXED, cbw3data);
|
|
if (!lpw3rgn)
|
|
{
|
|
LocalFree((HANDLE) lprgn);
|
|
break;
|
|
}
|
|
|
|
// Grab the bounding rect.
|
|
lpw3rgn->rcBounding.left = (SHORT)lprgn->rdh.rcBound.left;
|
|
lpw3rgn->rcBounding.right = (SHORT)lprgn->rdh.rcBound.right;
|
|
lpw3rgn->rcBounding.top = (SHORT)lprgn->rdh.rcBound.top;
|
|
lpw3rgn->rcBounding.bottom = (SHORT)lprgn->rdh.rcBound.bottom;
|
|
|
|
cbw3data = sizeof(WIN3REGION) - sizeof(SCAN) + 2;
|
|
|
|
// visit all the rects
|
|
curRectl = 0;
|
|
cScans = 0;
|
|
maxScanEntry = 0;
|
|
lpScan = lpw3rgn->aScans;
|
|
|
|
while(curRectl < lprgn->rdh.nCount)
|
|
{
|
|
LPWORD lpXEntry;
|
|
DWORD cbScan;
|
|
|
|
curScanEntry = 0; // Current X pair in this scan
|
|
|
|
lpScan->scnPntTop = (WORD)lprc[curRectl].top;
|
|
lpScan->scnPntBottom = (WORD)lprc[curRectl].bottom;
|
|
|
|
lpXEntry = (LPWORD) lpScan->scnPntsX;
|
|
|
|
// handle rects on this scan
|
|
do
|
|
{
|
|
lpXEntry[curScanEntry + 0] = (WORD)lprc[curRectl].left;
|
|
lpXEntry[curScanEntry + 1] = (WORD)lprc[curRectl].right;
|
|
curScanEntry += 2;
|
|
curRectl++;
|
|
} while ((curRectl < lprgn->rdh.nCount)
|
|
&& (lprc[curRectl-1].top == lprc[curRectl].top)
|
|
&& (lprc[curRectl-1].bottom == lprc[curRectl].bottom)
|
|
);
|
|
|
|
lpScan->scnPntCnt = curScanEntry;
|
|
lpXEntry[curScanEntry] = curScanEntry; // Count also follows Xs
|
|
cScans++;
|
|
|
|
if (curScanEntry > maxScanEntry)
|
|
maxScanEntry = curScanEntry;
|
|
|
|
// account for each new scan + each X1 X2 Entry but the first
|
|
cbScan = sizeof(SCAN)-(sizeof(WORD)*2) + (curScanEntry*sizeof(WORD));
|
|
cbw3data += cbScan;
|
|
lpScan = (PSCAN)(((LPBYTE)lpScan) + cbScan);
|
|
}
|
|
|
|
// Initialize the header
|
|
lpw3rgn->nextInChain = 0;
|
|
lpw3rgn->ObjType = 6; // old Windows OBJ_RGN identifier
|
|
lpw3rgn->ObjCount= 0x2F6; // any non-zero number
|
|
lpw3rgn->cbRegion = (WORD)cbw3data; // don't count type and next
|
|
lpw3rgn->cScans = cScans;
|
|
lpw3rgn->maxScan = maxScanEntry;
|
|
|
|
status = (UINT) RecordParms(hdc, META_CREATEREGION,
|
|
(cbw3data-2) >> 1, // Convert to count of words
|
|
(LPWORD) lpw3rgn);
|
|
|
|
if (LocalFree((HANDLE)lprgn))
|
|
ASSERTGDI(FALSE, "RecordObject: LocalFree(lprgn) Failed\n");
|
|
if (LocalFree((HANDLE)lpw3rgn))
|
|
ASSERTGDI(FALSE, "RecordObject: LocalFree(lpw3rgn) Failed\n");
|
|
|
|
break;
|
|
}
|
|
|
|
case LO_BRUSH_TYPE:
|
|
{
|
|
LOGBRUSH lb;
|
|
|
|
if (!GetObjectA(hObject, sizeof(LOGBRUSH), &lb))
|
|
break;
|
|
|
|
switch (lb.lbStyle)
|
|
{
|
|
case BS_HATCHED:
|
|
case BS_HOLLOW:
|
|
case BS_SOLID:
|
|
{
|
|
LOGBRUSH16 lb16;
|
|
|
|
LOGBRUSH16FROMLOGBRUSH32(&lb16, &lb);
|
|
|
|
// non-pattern brush
|
|
status = (UINT) RecordParms(hdc, META_CREATEBRUSHINDIRECT,
|
|
(DWORD) ((sizeof(LOGBRUSH16) + 1) >> 1),
|
|
(LPWORD) &lb16);
|
|
break;
|
|
}
|
|
|
|
case BS_PATTERN:
|
|
case BS_DIBPATTERN:
|
|
case BS_DIBPATTERNPT:
|
|
{
|
|
HBITMAP hbmRemote;
|
|
BMIH bmih;
|
|
DWORD cbBitsInfo;
|
|
DWORD cbBits;
|
|
LPWORD lpWStart, lpW;
|
|
DWORD lbStyle = BS_DIBPATTERN;
|
|
PBMIH lpDIBInfoHeader;
|
|
|
|
if (!(hbmRemote = GetObjectBitmapHandle((HBRUSH) hObject, &iUsage)))
|
|
{
|
|
ASSERTGDI(FALSE, "RecordObject: GetObjectBitmapHandle failed");
|
|
break;
|
|
}
|
|
|
|
// For a pattern brush, if it is color, it is recorded as a
|
|
// DIB pattern brush with BS_DIBPATTERN style. If it is
|
|
// monochrome, it is recorded as a DIB pattern brush with
|
|
// BS_PATTERN style. The playback code has a special
|
|
// case to deal with monochrome brushes.
|
|
|
|
if (lb.lbStyle == BS_PATTERN)
|
|
{
|
|
iUsage = DIB_RGB_COLORS;
|
|
if (MonoBitmap(hbmRemote))
|
|
lbStyle = BS_PATTERN;
|
|
}
|
|
|
|
hdcScreen = CreateCompatibleDC((HDC) 0); // freed below
|
|
|
|
// Get the bitmap info header and sizes.
|
|
|
|
if (!bMetaGetDIBInfo(hdcScreen, hbmRemote, &bmih,
|
|
&cbBitsInfo, &cbBits, iUsage, 0, TRUE))
|
|
break;
|
|
|
|
// Make sure that cbBitsInfo is dword aligned
|
|
|
|
// If we have converted the bitmap format in bMetaGetDIBInfo,
|
|
// modify the iUsage to match the new format.
|
|
|
|
if (bmih.biBitCount == 24)
|
|
iUsage = DIB_RGB_COLORS;
|
|
|
|
// Allocate space for DIB plus parameters.
|
|
|
|
lpWStart = lpW = (LPWORD) LocalAlloc(LMEM_FIXED,
|
|
cbBitsInfo + cbBits + 2*sizeof(WORD));
|
|
if (!lpW)
|
|
{
|
|
ERROR_ASSERT(FALSE, "RecordObject: out of memory\n");
|
|
break;
|
|
}
|
|
|
|
*lpW++ = (WORD) lbStyle; // BS_PATTERN or BS_DIBPATTERN
|
|
*lpW++ = (WORD) iUsage; // usage word
|
|
|
|
// Save the start of the bitmap info header field.
|
|
|
|
lpDIBInfoHeader = (LPBITMAPINFOHEADER) lpW;
|
|
|
|
// Copy the bitmap info header.
|
|
|
|
*lpDIBInfoHeader = bmih;
|
|
|
|
// Get bitmap info and bits.
|
|
|
|
if (GetBrushBits(hdcScreen,
|
|
hbmRemote,
|
|
(UINT) iUsage,
|
|
cbBitsInfo,
|
|
(LPVOID) ((PBYTE) lpW + cbBitsInfo),
|
|
(LPBITMAPINFO) lpDIBInfoHeader))
|
|
{
|
|
// Finally record the parameters into the file.
|
|
|
|
status = (UINT) RecordParms(hdc, META_DIBCREATEPATTERNBRUSH,
|
|
2 + (cbBitsInfo + cbBits) / sizeof(WORD),
|
|
(LPWORD) lpWStart);
|
|
}
|
|
|
|
if (LocalFree((HANDLE) lpWStart))
|
|
ASSERTGDI(FALSE, "RecordObject: LocalFree Failed\n");
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERTGDI(FALSE, "RecordObject: Bad brush style");
|
|
break;
|
|
}
|
|
} // switch(lb.lbStyle)
|
|
break;
|
|
} // case LO_BRUSH
|
|
|
|
case LO_PALETTE_TYPE:
|
|
status = (UINT) MakeLogPalette(hdc, hObject, META_CREATEPALETTE);
|
|
break;
|
|
|
|
default:
|
|
ERROR_ASSERT(FALSE, "unknown case RecordObject");
|
|
break;
|
|
}
|
|
|
|
// Free the DC created in the brush case.
|
|
|
|
if (hdcScreen)
|
|
if (!DeleteDC(hdcScreen))
|
|
ASSERTGDI(FALSE, "RecordObject: DeleteDC Failed\n");
|
|
|
|
ASSERTGDI(status == TRUE, "RecordObject: Failing\n");
|
|
return ((WORD) (status == TRUE ? iPosition : -1));
|
|
} /* RecordObject */
|
|
|
|
|
|
BOOL AddDCToObjectMetaList16(HDC hMetaDC16, HANDLE hObject)
|
|
{
|
|
ULONG cMetaDC16New;
|
|
PMETALINK16 pml16;
|
|
|
|
ASSERTGDI(LO_TYPE(hObject) != LO_REGION_TYPE,
|
|
"AddDCToObjectMetaList16: unexpected region object");
|
|
|
|
// If the object is a stock object there is no work to do
|
|
|
|
if (IS_STOCKOBJ(hObject))
|
|
return(TRUE);
|
|
|
|
// If the Object's MetaList16 is NULL create allocate one
|
|
|
|
pml16 = pmetalink16Get(hObject);
|
|
|
|
if (!pml16)
|
|
{
|
|
ENTERCRITICALSECTION(&semLocal);
|
|
pml16 = pmetalink16Create(hObject);
|
|
LEAVECRITICALSECTION(&semLocal);
|
|
|
|
if (pml16)
|
|
{
|
|
pml16->metalink = 0;
|
|
pml16->cMetaDC16 = 1;
|
|
pml16->ahMetaDC16[0] = hMetaDC16;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(FALSE, "AddDCToObjectMetaList16: Out of Memory 1");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int cj;
|
|
|
|
cMetaDC16New = pml16->cMetaDC16 + 1;
|
|
|
|
if (pml16 = pmetalink16Resize(hObject,cMetaDC16New))
|
|
{
|
|
pml16->ahMetaDC16[pml16->cMetaDC16++] = hMetaDC16;
|
|
}
|
|
else
|
|
{
|
|
ASSERTGDI(FALSE, "AddDCToObjectMetaList16: Out of Memory 2");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/***************************** Internal Function ***************************\
|
|
* AddObjectToDCTable
|
|
*
|
|
* Add an object (brush, pen...) to a list of objects associated with the
|
|
* metafile.
|
|
*
|
|
*
|
|
*
|
|
* Returns: 1 if object is already in table
|
|
* 0 if object was just added to table
|
|
* -1 if failure
|
|
*
|
|
* Remarks
|
|
* bAdd is TRUE iff the object is being added otherwise it is being deleted
|
|
*
|
|
\***************************************************************************/
|
|
|
|
UINT AddObjectToDCTable(HDC hdc, HANDLE hObject, PUINT pPosition, BOOL bAdd)
|
|
{
|
|
UINT iEmptySpace = (UINT) -1;
|
|
UINT i;
|
|
UINT status = (UINT) -1;
|
|
POBJECTTABLE pHandleTable;
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
PUTS("AddObjectToDCTable\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hdc);
|
|
|
|
if (!IS_METADC16_TYPE(hdc) || !pMFRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return((UINT)-1);
|
|
}
|
|
|
|
// if the Object table already exists search it for the object
|
|
|
|
if (pHandleTable = (POBJECTTABLE)pMFRec->hObjectTable)
|
|
{
|
|
for (i=0; i < (UINT) pMFRec->metaHeader.mtNoObjects; ++i)
|
|
{
|
|
if (hObject == pHandleTable[i].CurHandle)
|
|
{
|
|
*pPosition = i;
|
|
status = 1; // the object exists in the table
|
|
|
|
// if we are doing a METADELETEOBJECT.
|
|
// delete object from table
|
|
|
|
if (!bAdd)
|
|
{
|
|
pHandleTable[i].fPreDeleted = FALSE;
|
|
pHandleTable[i].CurHandle = (HANDLE)NULL;
|
|
}
|
|
goto AddObjectToTable10;
|
|
}
|
|
else if ((pHandleTable[i].CurHandle == 0) && (iEmptySpace == (UINT) -1))
|
|
{
|
|
// if the entry has been deleted, we want to add a new object
|
|
// in its place. iEmptySpace will tell us where that place is.
|
|
|
|
iEmptySpace = i;
|
|
}
|
|
} // for
|
|
}
|
|
|
|
if (bAdd)
|
|
{
|
|
// If there is no object table for this MetaFile then Allocate one.
|
|
|
|
if (pHandleTable == (POBJECTTABLE)NULL)
|
|
{
|
|
pHandleTable = (POBJECTTABLE) LocalAlloc(LMEM_FIXED, sizeof(OBJECTTABLE));
|
|
pMFRec->hObjectTable = (HANDLE) pHandleTable;
|
|
}
|
|
else if (iEmptySpace == (UINT) -1)
|
|
{
|
|
pHandleTable = (POBJECTTABLE) LocalReAlloc(pMFRec->hObjectTable,
|
|
(pMFRec->metaHeader.mtNoObjects + 1) * sizeof(OBJECTTABLE),
|
|
LMEM_MOVEABLE);
|
|
if (pHandleTable)
|
|
pMFRec->hObjectTable = (HANDLE) pHandleTable;
|
|
}
|
|
|
|
if (pHandleTable)
|
|
{
|
|
if (iEmptySpace == (UINT) -1)
|
|
*pPosition = pMFRec->metaHeader.mtNoObjects++;
|
|
else
|
|
*pPosition = iEmptySpace;
|
|
|
|
pHandleTable[*pPosition].fPreDeleted = FALSE;
|
|
pHandleTable[*pPosition].CurHandle = hObject;
|
|
|
|
status = 0; // the object is added to the table
|
|
}
|
|
}
|
|
AddObjectToTable10:
|
|
|
|
ERROR_ASSERT(status != (UINT) -1, "AddObjectToTable: Failing\n");
|
|
return(status);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* HDC WINAPI CreateMetaFileW
|
|
*
|
|
* Creates a MetaFile DC
|
|
*
|
|
* The internal format for a MetaFileRecorder has two formats one
|
|
* for a memory MetaFile and one for a disk based MetaFile
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HDC WINAPI CreateMetaFileA(LPCSTR pszFileName)
|
|
{
|
|
UINT cch;
|
|
WCHAR awch[MAX_PATH];
|
|
|
|
if (pszFileName)
|
|
{
|
|
cch = strlen(pszFileName)+1;
|
|
|
|
if (cch > MAX_PATH)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CreateMetaFileA filename too long");
|
|
GdiSetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
return((HDC) 0);
|
|
}
|
|
vToUnicodeN(awch, MAX_PATH, pszFileName, cch);
|
|
|
|
return (CreateMetaFileW(awch));
|
|
}
|
|
else
|
|
return (CreateMetaFileW((LPWSTR)NULL));
|
|
}
|
|
|
|
|
|
HDC WINAPI CreateMetaFileW(LPCWSTR pwszFileName)
|
|
{
|
|
PMFRECORDER16 pMFRec;
|
|
HDC hdc;
|
|
|
|
PUTS("CreateMetaFileW\n");
|
|
|
|
if (!(pMFRec = (PMFRECORDER16) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof(MFRECORDER16))))
|
|
goto CreateMetaFileW_error;
|
|
|
|
// pMFRec->ident = ID_METADC16;
|
|
// pMFRec->hMem = 0;
|
|
pMFRec->hFile = INVALID_HANDLE_VALUE;
|
|
pMFRec->cbBuffer = MF16_BUFSIZE_INIT;
|
|
// pMFRec->ibBuffer = 0;
|
|
pMFRec->metaHeader.mtHeaderSize = sizeof(METAHEADER)/sizeof(WORD);
|
|
pMFRec->metaHeader.mtVersion = METAVERSION300;
|
|
// pMFRec->metaHeader.mtSize = 0;
|
|
// pMFRec->metaHeader.mtNoObjects = 0;
|
|
// pMFRec->metaHeader.mtMaxRecord = 0;
|
|
// pMFRec->metaHeader.mtNoParameters = 0;
|
|
// pMFRec->recFlags = 0;
|
|
// pMFRec->recCurObjects[] = 0;
|
|
pMFRec->recCurObjects[OBJ_PEN - MIN_OBJ_TYPE]
|
|
= GetStockObject(BLACK_PEN);
|
|
pMFRec->recCurObjects[OBJ_BRUSH - MIN_OBJ_TYPE]
|
|
= GetStockObject(WHITE_BRUSH);
|
|
pMFRec->recCurObjects[OBJ_FONT - MIN_OBJ_TYPE]
|
|
= GetStockObject(DEVICE_DEFAULT_FONT);
|
|
pMFRec->recCurObjects[OBJ_BITMAP - MIN_OBJ_TYPE]
|
|
= GetStockObject(PRIV_STOCK_BITMAP);
|
|
pMFRec->recCurObjects[OBJ_REGION - MIN_OBJ_TYPE]
|
|
= (HANDLE) NULL;
|
|
pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE]
|
|
= GetStockObject(DEFAULT_PALETTE);
|
|
// pMFRec->iPalVer = 0;
|
|
|
|
// Create a disk file if given. The filename is given in unicode.
|
|
|
|
if (pwszFileName)
|
|
{
|
|
LPWSTR pwszFilePart; // not used
|
|
DWORD cPathname;
|
|
|
|
// Convert the filename to a fully qualified pathname.
|
|
|
|
cPathname = GetFullPathNameW(pwszFileName,
|
|
MAX_PATH,
|
|
pMFRec->wszFullPathName,
|
|
&pwszFilePart);
|
|
|
|
if (!cPathname || cPathname > MAX_PATH)
|
|
{
|
|
ERROR_ASSERT(FALSE, "GetFullPathName failed");
|
|
if (cPathname > MAX_PATH)
|
|
GdiSetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
goto CreateMetaFileW_error;
|
|
}
|
|
pMFRec->wszFullPathName[cPathname] = 0;
|
|
|
|
// Create the file.
|
|
|
|
if ((pMFRec->hFile = CreateFileW(pMFRec->wszFullPathName,// Filename
|
|
GENERIC_WRITE, // Write access
|
|
0L, // Non-shared
|
|
(LPSECURITY_ATTRIBUTES) NULL, // No security
|
|
CREATE_ALWAYS, // Always create
|
|
FILE_ATTRIBUTE_NORMAL, // normal attributes
|
|
(HANDLE) 0)) // no template file
|
|
== INVALID_HANDLE_VALUE)
|
|
{
|
|
// Milestones, Etc. 3.1 creates the file for read/write access when
|
|
// it calls CreateMetaFile. This causes the above CreateFile to
|
|
// fail. However, we do not want to modify the above call since
|
|
// it provides serialization and access to the metafile. Instead,
|
|
// we add in this hack for Milestones. The only difference is
|
|
// that the metafile is shared for read/write access.
|
|
|
|
if ((pMFRec->hFile = CreateFileW(pMFRec->wszFullPathName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
(HANDLE) 0))
|
|
== INVALID_HANDLE_VALUE)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CreateFile failed");
|
|
goto CreateMetaFileW_error;
|
|
}
|
|
|
|
WARNING("CreateMetaFileW: Creating metafile with read/write share\n");
|
|
}
|
|
pMFRec->metaHeader.mtType = DISKMETAFILE;
|
|
}
|
|
else
|
|
{
|
|
pMFRec->metaHeader.mtType = MEMORYMETAFILE;
|
|
}
|
|
|
|
// Allocate memory for metafile.
|
|
// For disk metafile, it is used as a buffer.
|
|
// For memory metafile, it is the storage for the metafile.
|
|
|
|
if (!(pMFRec->hMem = LocalAlloc(LMEM_FIXED, MF16_BUFSIZE_INIT)))
|
|
goto CreateMetaFileW_error;
|
|
|
|
// Write the header.
|
|
|
|
if (!AttemptWrite(pMFRec, sizeof(METAHEADER), (LPBYTE)&pMFRec->metaHeader))
|
|
goto CreateMetaFileW_error;
|
|
|
|
// Finally, allocate a local handle for the metafile DC. It references
|
|
// the metafile recorder info.
|
|
|
|
hdc = hCreateClientObjLink(pMFRec,LO_METADC16_TYPE);
|
|
|
|
if (!hdc)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CreateMetaFileW: iAllocHandle failed\n");
|
|
goto CreateMetaFileW_error;
|
|
}
|
|
|
|
return(hdc);
|
|
|
|
CreateMetaFileW_error:
|
|
|
|
if (pMFRec)
|
|
{
|
|
if (pMFRec->hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!CloseHandle(pMFRec->hFile))
|
|
ASSERTGDI(FALSE, "CloseHandle failed\n");
|
|
|
|
if (!DeleteFileW(pMFRec->wszFullPathName))
|
|
WARNING("CreateMetaFileW: DeleteFile failed\n");
|
|
}
|
|
|
|
if (pMFRec->hMem)
|
|
if (LocalFree(pMFRec->hMem))
|
|
ASSERTGDI(FALSE, "LocalFree failed");
|
|
|
|
if (LocalFree((HANDLE) pMFRec))
|
|
ASSERTGDI(FALSE, "CreateMetaFileW: LocalFree failed\n");
|
|
}
|
|
|
|
ERROR_ASSERT(FALSE, "CreateMetaFileW failed\n");
|
|
return((HDC) 0);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* HMETAFILE WINAPI CloseMetaFile
|
|
*
|
|
* The CloseMetaFile function closes the metafile device context and creates a
|
|
* metafile handle that can be used to play the metafile by using the
|
|
* PlayMetaFile function.
|
|
*
|
|
* The internal format for a MetaFile has two formats one
|
|
* for a memory MetaFile and one for a disk based MetaFile
|
|
*
|
|
* Effects:
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HMETAFILE WINAPI CloseMetaFile(HDC hdc)
|
|
{
|
|
PMFRECORDER16 pmfRec;
|
|
HMETAFILE hmf = (HMETAFILE) 0;
|
|
|
|
PUTS("CloseMetaFile\n");
|
|
|
|
GET_PMFRECORDER16(pmfRec,hdc);
|
|
|
|
ASSERTGDI(pmfRec, "CloseMetaFile: pmfRec is NULL!");
|
|
|
|
if (!IS_METADC16_TYPE(hdc) || !pmfRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(hmf);
|
|
}
|
|
|
|
// If the metafile was aborted then free the MetaDC handle memory and go home.
|
|
|
|
if (pmfRec->recFlags & METAFILEFAILURE)
|
|
goto CLM_Cleanup;
|
|
|
|
// Write the terminate Record
|
|
|
|
if (!RecordParms(hdc, 0, 0, (LPWORD)NULL))
|
|
goto CLM_Cleanup;
|
|
|
|
// Flush the buffer and update the header record.
|
|
|
|
if (pmfRec->metaHeader.mtType == DISKMETAFILE)
|
|
{
|
|
BOOL fRet;
|
|
DWORD dwT;
|
|
|
|
ASSERTGDI(pmfRec->metaHeader.mtType == DISKMETAFILE,
|
|
"CloseMetaFile: unknown metafile type");
|
|
|
|
// Flush the memory buffer
|
|
|
|
fRet = WriteFile(pmfRec->hFile, (LPBYTE)pmfRec->hMem,
|
|
pmfRec->ibBuffer, &dwT, (LPOVERLAPPED)NULL);
|
|
if (!fRet || (dwT != pmfRec->ibBuffer))
|
|
{
|
|
ERROR_ASSERT(FALSE, "CloseMetaFile: Write1 failed\n");
|
|
goto CLM_Cleanup;
|
|
}
|
|
|
|
// Rewind the file and write the header out
|
|
|
|
if (SetFilePointer(pmfRec->hFile, 0, (LPLONG)NULL, FILE_BEGIN) != 0)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CloseMetaFile: SetFilePointer failed\n");
|
|
goto CLM_Cleanup;
|
|
}
|
|
|
|
// Fix up data written to disk as a memory metafile
|
|
|
|
pmfRec->metaHeader.mtType = MEMORYMETAFILE;
|
|
fRet = WriteFile(pmfRec->hFile, (LPBYTE)& pmfRec->metaHeader,
|
|
sizeof(METAHEADER), &dwT, (LPOVERLAPPED)NULL);
|
|
pmfRec->metaHeader.mtType = DISKMETAFILE; // restore it
|
|
|
|
if (!fRet || (dwT != sizeof(METAHEADER)))
|
|
{
|
|
ERROR_ASSERT(FALSE, "CloseMetaFile: Write2 failed\n");
|
|
goto CLM_Cleanup;
|
|
}
|
|
|
|
// Close the file.
|
|
|
|
if (!CloseHandle(pmfRec->hFile))
|
|
ASSERTGDI(FALSE, "CloseMetaFile: FileError\n");
|
|
|
|
pmfRec->hFile = INVALID_HANDLE_VALUE; // don't close it again below
|
|
}
|
|
else
|
|
{
|
|
HANDLE hMemNew;
|
|
|
|
// Flush the header record.
|
|
|
|
*(PMETAHEADER) pmfRec->hMem = pmfRec->metaHeader;
|
|
|
|
// Realloc memory metafile to exact size
|
|
|
|
hMemNew = LocalReAlloc(pmfRec->hMem, pmfRec->metaHeader.mtSize * sizeof(WORD), LMEM_MOVEABLE);
|
|
|
|
if (!hMemNew)
|
|
{
|
|
ASSERTGDI(FALSE, "LocalReAlloc failed");
|
|
goto CLM_Cleanup;
|
|
}
|
|
|
|
pmfRec->hMem = hMemNew;
|
|
}
|
|
|
|
// Allocate and initialize a metafile.
|
|
|
|
if (pmfRec->metaHeader.mtType == DISKMETAFILE)
|
|
{
|
|
hmf = GetMetaFileW(pmfRec->wszFullPathName);
|
|
}
|
|
else
|
|
{
|
|
hmf = SetMetaFileBitsAlt((HLOCAL) pmfRec->hMem);
|
|
if (hmf)
|
|
pmfRec->hMem = 0; // don't free it below because it has been transfered
|
|
}
|
|
|
|
CLM_Cleanup:
|
|
|
|
// Remove the MetaFile from the list of active metafiles
|
|
|
|
if (pmfRec->hObjectTable)
|
|
{
|
|
UnlistObjects(hdc);
|
|
if (LocalFree((HANDLE) pmfRec->hObjectTable))
|
|
ASSERTGDI( FALSE, "CloseMetaFile: LocalFree object table failed\n");
|
|
}
|
|
|
|
// If the file handle exists at this time, we have an error.
|
|
|
|
if (pmfRec->hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!CloseHandle(pmfRec->hFile))
|
|
ASSERTGDI(FALSE, "CloseHandle failed\n");
|
|
|
|
if (!DeleteFileW(pmfRec->wszFullPathName))
|
|
WARNING("CloseMetaFile: DeleteFile failed\n");
|
|
}
|
|
|
|
// Free the cache buffer.
|
|
|
|
if (pmfRec->hMem)
|
|
if (LocalFree(pmfRec->hMem))
|
|
ASSERTGDI(FALSE, "LocalFree failed");
|
|
|
|
// Free the memory for the metafile DC.
|
|
|
|
if (LocalFree((HANDLE) pmfRec))
|
|
ASSERTGDI(FALSE, "CloseMetaFile: LocalFree failed\n");
|
|
|
|
// Free the handle for the metafile DC.
|
|
|
|
if (!bDeleteClientObjLink(hdc))
|
|
RIP("CloseMetaFile - failed bDeleteClientObjLink\n");
|
|
|
|
ERROR_ASSERT(hmf != (HMETAFILE) 0, "CloseMetaFile failed\n");
|
|
return(hmf);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* CopyMetaFile(hSrcMF, lpFileName)
|
|
*
|
|
* Copies the metafile (hSrcMF) to a new metafile with name lpFileName. The
|
|
* function then returns a handle to this new metafile if the function was
|
|
* successful.
|
|
*
|
|
* Retuns a handle to a new metafile, 0 iff failure
|
|
*
|
|
* IMPLEMENTATION:
|
|
* The source and target metafiles are checked to see if they are both memory
|
|
* metafile and if so a piece of Local memory is allocated and the metafile
|
|
* is simply copied.
|
|
* If this is not the case CreateMetaFile is called with lpFileName and then
|
|
* records are pulled out of the source metafile (using GetEvent) and written
|
|
* into the destination metafile one at a time (using RecordParms).
|
|
*
|
|
* Lock the source
|
|
* if source is a memory metafile and the destination is a memory metafile
|
|
* alloc the same size Local memory as the source
|
|
* copy the bits directly
|
|
* else
|
|
* get a metafile handle by calling CreateMetaFile
|
|
* while GetEvent returns records form the source
|
|
* record the record in the new metafile
|
|
*
|
|
* close the metafile
|
|
*
|
|
* return the new metafile handle
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HMETAFILE WINAPI CopyMetaFileA(HMETAFILE hmf, LPCSTR psz)
|
|
{
|
|
UINT cch;
|
|
WCHAR awch[MAX_PATH];
|
|
|
|
if (psz != (LPSTR)NULL)
|
|
{
|
|
cch = strlen(psz)+1;
|
|
|
|
if (cch > MAX_PATH)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CopyMetaFileA filename too long");
|
|
GdiSetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
return((HMETAFILE)0);
|
|
}
|
|
|
|
vToUnicodeN(awch, MAX_PATH, psz, cch);
|
|
|
|
return (CopyMetaFileW(hmf, awch));
|
|
}
|
|
else
|
|
return (CopyMetaFileW(hmf, (LPWSTR)NULL));
|
|
}
|
|
|
|
HMETAFILE WINAPI CopyMetaFileW(HMETAFILE hSrcMF, LPCWSTR pwszFileName)
|
|
{
|
|
PMETAFILE16 pMFSource;
|
|
HMETAFILE hMFDest = (HMETAFILE) 0;
|
|
HDC hMDCDest;
|
|
UINT state;
|
|
|
|
PUTS("CopyMetaFile\n");
|
|
|
|
pMFSource = GET_PMF16(hSrcMF);
|
|
if (pMFSource == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(hMFDest);
|
|
}
|
|
|
|
state = (pMFSource->fl & MF16_DISKFILE) ? 2 : 0;
|
|
state |= (pwszFileName) ? 1 : 0;
|
|
|
|
switch (state)
|
|
{
|
|
case 0: /* memory -> memory */
|
|
hMFDest = SetMetaFileBitsEx
|
|
(
|
|
pMFSource->metaHeader.mtSize * sizeof(WORD),
|
|
(LPBYTE) pMFSource->hMem
|
|
);
|
|
break;
|
|
|
|
case 3: /* disk -> disk */
|
|
hMFDest = CopyFileW(pMFSource->wszFullPathName,
|
|
pwszFileName, FALSE)
|
|
? GetMetaFileW(pwszFileName) : 0;
|
|
|
|
ERROR_ASSERT(hMFDest, "CopyMetaFileW: GetMetaFile Failed\n");
|
|
break;
|
|
|
|
case 1:
|
|
case 2:
|
|
if (hMDCDest = CreateMetaFileW(pwszFileName))
|
|
{
|
|
PMFRECORDER16 pMFRecDest;
|
|
PMETARECORD lpMR = NULL;
|
|
|
|
GET_PMFRECORDER16(pMFRecDest,hMDCDest);
|
|
|
|
while (lpMR = GetEvent(pMFSource, lpMR))
|
|
if ((lpMR == (PMETARECORD) -1)
|
|
|| !RecordParms(hMDCDest, (DWORD)lpMR->rdFunction,
|
|
(DWORD)lpMR->rdSize - 3,
|
|
(LPWORD) lpMR->rdParm))
|
|
{
|
|
HMETAFILE hmfT;
|
|
|
|
MarkMetaFile(pMFRecDest);
|
|
|
|
if (hmfT = CloseMetaFile(hMDCDest))
|
|
DeleteMetaFile(hmfT);
|
|
return((HMETAFILE) 0);
|
|
}
|
|
|
|
// touch up the destination metafile header before we close
|
|
// the metafile!
|
|
|
|
pMFRecDest->metaHeader.mtNoObjects
|
|
= pMFSource->metaHeader.mtNoObjects;
|
|
ASSERTGDI(sizeof(METAHEADER) == 18, "CopyMetaFile: METAHEADER has changed!");
|
|
|
|
hMFDest = CloseMetaFile(hMDCDest);
|
|
}
|
|
break;
|
|
}
|
|
|
|
ERROR_ASSERT(hMFDest, "CopyMetaFileW Failing\n");
|
|
return(hMFDest);
|
|
}
|
|
|
|
/***************************** Internal Function ***************************\
|
|
* HANDLE WINAPI GetMetaFileBitsEx
|
|
*
|
|
* The GetMetaFileBits function returns a handle to a Windows metafile that
|
|
* contains the specified data describing the metafile.
|
|
*
|
|
* It does not invalidate the metafile handle like Windows!
|
|
*
|
|
* Effects:
|
|
*
|
|
\***************************************************************************/
|
|
|
|
UINT WINAPI GetMetaFileBitsEx(HMETAFILE hMF, UINT cbBuffer, LPVOID lpData)
|
|
{
|
|
DWORD cbHave;
|
|
PMETAFILE16 pmf16;
|
|
|
|
PUTS("GetMetaFileBitsEx\n");
|
|
|
|
pmf16 = GET_PMF16(hMF);
|
|
if (pmf16 == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return(0);
|
|
}
|
|
|
|
cbHave = pmf16->metaHeader.mtSize * sizeof(WORD);
|
|
|
|
// If lpData is NULL, return the size necessary to hold the data.
|
|
|
|
if (!lpData)
|
|
return(cbHave);
|
|
|
|
// Make sure the input buffer is large enough.
|
|
|
|
if (cbBuffer < cbHave)
|
|
return(0);
|
|
|
|
// Copy the bits. Win3.1 returns the bits for memory metafile only!
|
|
// We will do the right thing here.
|
|
|
|
RtlCopyMemory(lpData, (PBYTE) pmf16->hMem, cbHave);
|
|
|
|
// Return the number of bytes copied.
|
|
|
|
return(cbHave);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* HMETAFILE WINAPI SetMetaFileBitsEx
|
|
*
|
|
* Creates a memory based Windows 3.X metafile from the data provided
|
|
*
|
|
* Returns: HMETAFILE iff succesful.
|
|
*
|
|
* Effects:
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HMETAFILE WINAPI SetMetaFileBitsEx(UINT cbBuffer, CONST BYTE *lpData)
|
|
{
|
|
PMETAFILE16 pmf16;
|
|
HMETAFILE hmf;
|
|
|
|
PUTS("SetMetaFileBitsEx\n");
|
|
|
|
// Verify the input data.
|
|
|
|
if (cbBuffer < sizeof(METAHEADER)
|
|
|| !IsValidMetaHeader16((PMETAHEADER) lpData))
|
|
{
|
|
ERROR_ASSERT(FALSE, "SetMetaFileBitsEx: Bad input data\n");
|
|
GdiSetLastError(ERROR_INVALID_DATA);
|
|
return((HMETAFILE) 0);
|
|
}
|
|
|
|
ERROR_ASSERT(((PMETAHEADER) lpData)->mtType == MEMORYMETAFILE,
|
|
"SetMetaFileBitsEx: Bad mtType\n");
|
|
|
|
// Allocate and initialize a metafile.
|
|
// Some Windows metafiles contain bad values in mtSize. As a result,
|
|
// we have to verify and fix the mtSize if neccessary. This is fixed
|
|
// in the following call.
|
|
|
|
if (!(pmf16 = pmf16AllocMF16(0, (DWORD) cbBuffer, (PDWORD)lpData, (LPWSTR) NULL)))
|
|
return((HMETAFILE) 0);
|
|
|
|
ASSERTGDI(pmf16->metaHeader.mtType == MEMORYMETAFILE,
|
|
"SetMetaFileBitsEx: Bad mtType\n");
|
|
((PMETAHEADER) pmf16->hMem)->mtType = MEMORYMETAFILE; // just in case
|
|
|
|
// Allocate a local handle.
|
|
|
|
|
|
hmf = hmf16Create(pmf16);
|
|
if (hmf == NULL)
|
|
{
|
|
vFreeMF16(pmf16);
|
|
}
|
|
|
|
// Return the metafile handle.
|
|
|
|
return(hmf);
|
|
}
|
|
|
|
// Similar to Win3.x SetMetaFileBits.
|
|
// It is assumed that hMem is allocated with the LMEM_FIXED option.
|
|
// For internal use only.
|
|
|
|
HMETAFILE WINAPI SetMetaFileBitsAlt(HLOCAL hMem)
|
|
{
|
|
PMETAFILE16 pmf16;
|
|
HMETAFILE hmf;
|
|
|
|
PUTS("SetMetaFileBitsAlt\n");
|
|
|
|
// Allocate and initialize a metafile.
|
|
|
|
if (!(pmf16 = pmf16AllocMF16(ALLOCMF16_TRANSFER_BUFFER, 0, (PDWORD) hMem, (LPWSTR) NULL)))
|
|
return((HMETAFILE) 0);
|
|
|
|
ASSERTGDI(pmf16->metaHeader.mtType == MEMORYMETAFILE,
|
|
"SetMetaFileBitsAlt: Bad mtType\n");
|
|
((PMETAHEADER) pmf16->hMem)->mtType = MEMORYMETAFILE; // just in case
|
|
|
|
// Allocate a local handle.
|
|
|
|
hmf = hmf16Create(pmf16);
|
|
|
|
if (hmf == NULL)
|
|
{
|
|
pmf16->hMem = NULL; // let caller free the buffer!
|
|
vFreeMF16(pmf16);
|
|
}
|
|
|
|
// Return the metafile handle.
|
|
|
|
return(hmf);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* GetObject16AndType
|
|
*
|
|
* Returns the object type, eg OBJ_FONT, as well as a the LogObject
|
|
* in Windows 3.x Format
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#define MAXOBJECTSIZE sizeof(LOGFONT)
|
|
|
|
DWORD GetObject16AndType(HANDLE hObj, LPVOID lpObjBuf16)
|
|
{
|
|
BYTE objBuf32[MAXOBJECTSIZE];
|
|
int iObj;
|
|
|
|
PUTS("GetObjectAndType\n");
|
|
|
|
ASSERTGDI(MAXOBJECTSIZE >= sizeof(LOGPEN),
|
|
"GetObject16AndType MAXOBJECTSIZE wrong\n");
|
|
|
|
if (!GetObject(hObj, MAXOBJECTSIZE, &objBuf32))
|
|
{
|
|
ERROR_ASSERT(FALSE, "GetObject16AndType GetObject Failed\n");
|
|
return(0);
|
|
}
|
|
|
|
switch(iObj = GetObjectType(hObj))
|
|
{
|
|
case OBJ_PEN:
|
|
LOGPEN16FROMLOGPEN32((PLOGPEN16)lpObjBuf16,(LPLOGPEN)objBuf32);
|
|
break;
|
|
|
|
case OBJ_FONT:
|
|
LOGFONT16FROMLOGFONT32((PLOGFONT16)lpObjBuf16,(LPLOGFONT)objBuf32);
|
|
break;
|
|
|
|
default:
|
|
ASSERTGDI(FALSE, "GetObject16AndType unknown object type\n");
|
|
return(0);
|
|
break;
|
|
}
|
|
|
|
return((DWORD) iObj);
|
|
}
|
|
|
|
/***************************** Internal Function **************************\
|
|
* BOOL UnlistObjects(hMetaDC)
|
|
*
|
|
* Each object has a list of metafiles the object is associated with.
|
|
* UnlistObjects removes hMetaDC from all of its object's metafile lists
|
|
*
|
|
* Effects:
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL UnlistObjects(HDC hMetaDC)
|
|
{
|
|
PMETALINK16 pml16;
|
|
UINT iCurObject;
|
|
UINT iCurMetaListEntry;
|
|
POBJECTTABLE pht;
|
|
PMFRECORDER16 pMFRec;
|
|
|
|
PUTS("UnlistObjects\n");
|
|
|
|
GET_PMFRECORDER16(pMFRec,hMetaDC);
|
|
|
|
if (!IS_METADC16_TYPE(hMetaDC) || !pMFRec)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
return((UINT)-1);
|
|
}
|
|
|
|
if (pMFRec->metaHeader.mtNoObjects)
|
|
{
|
|
pht = (POBJECTTABLE) pMFRec->hObjectTable;
|
|
ASSERTGDI(pht, "UnlistObject: called even with no handle table");
|
|
|
|
// Loop through the objects and unlink this metafile
|
|
|
|
for (iCurObject=0; iCurObject < (UINT) pMFRec->metaHeader.mtNoObjects; iCurObject++)
|
|
{
|
|
HANDLE hObj;
|
|
|
|
if( (hObj = pht[iCurObject].CurHandle)
|
|
&& (!pht[iCurObject].fPreDeleted))
|
|
{
|
|
if (IS_STOCKOBJ(hObj))
|
|
continue;
|
|
|
|
ASSERTGDI(LO_TYPE(hObj) != LO_REGION_TYPE,
|
|
"UnlistObjects: unexpected region object");
|
|
|
|
pml16 = pmetalink16Get(hObj);
|
|
|
|
// It cannot be a empty list.
|
|
|
|
ASSERTGDI(pml16, "UnlistObject: pml16 is NULL");
|
|
|
|
if (!pml16 || pml16->cMetaDC16 == 0)
|
|
continue;
|
|
|
|
// Find the metafile in the objects MetaLink16 list
|
|
|
|
for (iCurMetaListEntry=0;
|
|
iCurMetaListEntry<pml16->cMetaDC16;
|
|
iCurMetaListEntry++)
|
|
{
|
|
if(pml16->ahMetaDC16[iCurMetaListEntry] == hMetaDC)
|
|
break;
|
|
}
|
|
|
|
ASSERTGDI(iCurMetaListEntry < pml16->cMetaDC16,
|
|
"UnlistObject: Metafile not found");
|
|
|
|
// Slide the rest of the metafiles in the list "up"
|
|
|
|
for(; iCurMetaListEntry < (pml16->cMetaDC16-1); iCurMetaListEntry++)
|
|
pml16->ahMetaDC16[iCurMetaListEntry] = pml16->ahMetaDC16[iCurMetaListEntry+1];
|
|
|
|
pml16->cMetaDC16--; // just got rid of one
|
|
|
|
if (pml16->cMetaDC16 == 0)
|
|
{
|
|
// We can only free the METALINK16 if the metalink field is not being used
|
|
|
|
if (pml16->metalink)
|
|
{
|
|
pml16->cMetaDC16 = 0;
|
|
pml16->ahMetaDC16[0] = (HDC)NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!bDeleteMetalink16(hObj))
|
|
ASSERTGDI(FALSE,"UnlistObjects: LocalFree Failed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pml16 = pmetalink16Resize(hObj,pml16->cMetaDC16);
|
|
|
|
if (pml16 == NULL)
|
|
{
|
|
ASSERTGDI(FALSE,"UnlistObjects: LocalReAlloc Failed\n");
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
} // for
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* pmf16AllocMF16(fl, cb, pb, pwszFilename)
|
|
*
|
|
* This routine allocates memory for an METAFILE16 and initializes it.
|
|
* Returns a pointer to the new METAFILE16. On error returns NULL.
|
|
* It accepts only windows metafiles.
|
|
*
|
|
* This routine is called by API level METAFILE16 allocation routines
|
|
* CloseMetaFile, GetMetaFile, SetMetaFileBitsEx.
|
|
*
|
|
* Arguments:
|
|
* fl - ALLOCMF16_TRANSFER_BUFFER is set if storage for memory
|
|
* metafile is to be set directly into METAFILE16. Otherwise,
|
|
* a copy of the memory metafile is duplicated.
|
|
* cb - Size of pb in bytes if given. This parameter is given
|
|
* by SetMetaFileBitsEx only. It is used to compare and
|
|
* fixup bad mtSize if necessary.
|
|
* pb - Pointer to a memory metafile if non-null.
|
|
* pwszFilename - Filename of a disk metafile if non-null.
|
|
*
|
|
* History:
|
|
* Fri May 15 14:11:22 1992 -by- Hock San Lee [hockl]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
PMETAFILE16 pmf16AllocMF16(DWORD fl, DWORD cb, CONST UNALIGNED DWORD *pb, LPCWSTR pwszFilename)
|
|
{
|
|
PMETAFILE16 pmf16, pmf16Rc = (PMETAFILE16) NULL;
|
|
|
|
PUTS("pmf16AllocMF16\n");
|
|
|
|
ASSERTGDI(!(fl & ~ALLOCMF16_TRANSFER_BUFFER), "pmf16AllocMF16: Invalid fl");
|
|
|
|
// Allocate a new METAFILE16.
|
|
|
|
if (!(pmf16 = (PMETAFILE16) LocalAlloc(LMEM_FIXED, sizeof(METAFILE16))))
|
|
goto pmf16AllocMF16_cleanup;
|
|
|
|
// Initialize it.
|
|
|
|
pmf16->ident = MF16_IDENTIFIER;
|
|
pmf16->hFile = INVALID_HANDLE_VALUE;
|
|
pmf16->hFileMap = (HANDLE) 0;
|
|
pmf16->hMem = (HANDLE) 0;
|
|
pmf16->iMem = 0;
|
|
pmf16->hMetaFileRecord = (HANDLE) 0;
|
|
pmf16->fl = 0L;
|
|
|
|
// Memory mapped the disk file if given.
|
|
|
|
if (pwszFilename)
|
|
{
|
|
LPWSTR pwszFilePart; // not used
|
|
DWORD cPathname;
|
|
|
|
pmf16->fl |= MF16_DISKFILE; // this must be first! See vFreeMF16.
|
|
|
|
// Convert the filename to a fully qualified pathname.
|
|
|
|
cPathname = GetFullPathNameW(pwszFilename,
|
|
MAX_PATH,
|
|
pmf16->wszFullPathName,
|
|
&pwszFilePart);
|
|
|
|
if (!cPathname || cPathname > MAX_PATH)
|
|
{
|
|
ERROR_ASSERT(FALSE, "GetFullPathName failed");
|
|
if (cPathname > MAX_PATH)
|
|
GdiSetLastError(ERROR_FILENAME_EXCED_RANGE);
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
pmf16->wszFullPathName[cPathname] = 0;
|
|
|
|
if ((pmf16->hFile = CreateFileW(pmf16->wszFullPathName, // Filename
|
|
GENERIC_READ, // Read access
|
|
FILE_SHARE_READ, // Share read
|
|
(LPSECURITY_ATTRIBUTES) 0L,// No security
|
|
OPEN_EXISTING, // Re-open
|
|
0, // file attributes ignored
|
|
(HANDLE) 0)) // no template file
|
|
== INVALID_HANDLE_VALUE)
|
|
{
|
|
// See the comment for Milestones in CreateMetaFileW.
|
|
|
|
if ((pmf16->hFile = CreateFileW(pmf16->wszFullPathName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
(LPSECURITY_ATTRIBUTES) 0L,
|
|
OPEN_EXISTING,
|
|
0,
|
|
(HANDLE) 0))
|
|
== INVALID_HANDLE_VALUE)
|
|
{
|
|
ERROR_ASSERT(FALSE, "CreateFile failed");
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
WARNING("pmf16AllocMF16: Opening metafile with read/write share\n");
|
|
}
|
|
|
|
if (!(pmf16->hFileMap = CreateFileMappingW(pmf16->hFile,
|
|
(LPSECURITY_ATTRIBUTES) 0L,
|
|
PAGE_READONLY,
|
|
0L,
|
|
0L,
|
|
(LPWSTR) NULL)))
|
|
{
|
|
ERROR_ASSERT(FALSE, "CreateFileMapping failed");
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
|
|
if (!(pmf16->hMem = MapViewOfFile(pmf16->hFileMap, FILE_MAP_READ, 0, 0, 0)))
|
|
{
|
|
ERROR_ASSERT(FALSE, "MapViewOfFile failed");
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
}
|
|
else if (fl & ALLOCMF16_TRANSFER_BUFFER)
|
|
{
|
|
// If this is our memory metafile from MDC, transfer it to METAFILE16.
|
|
|
|
pmf16->hMem = (BYTE *) pb;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, make a copy of memory metafile.
|
|
// We get here only if the caller is SetMetaFileBitsEx. Since some metafiles
|
|
// may contain a bad mtSize that is different than the actual file size, we
|
|
// need to fix it up if necessary!
|
|
|
|
DWORD mtSize = ((PMETAHEADER) pb)->mtSize;
|
|
|
|
if ((mtSize * 2 > cb) // mtSize greater than filesize
|
|
|| (((PWORD) pb)[mtSize - 3] != 3) // EOF record should be 3,0,0
|
|
|| (((PWORD) pb)[mtSize - 2] != 0)
|
|
|| (((PWORD) pb)[mtSize - 1] != 0))
|
|
{
|
|
// Compute the correct mtSize!
|
|
|
|
PMETARECORD pMR;
|
|
DWORD mtSizeMax;
|
|
|
|
PUTS("pmf16AllocMF16: verifying mtSize\n");
|
|
|
|
mtSize = ((PMETAHEADER) pb)->mtHeaderSize;
|
|
pMR = (PMETARECORD) ((PWORD) pb + ((PMETAHEADER) pb)->mtHeaderSize);
|
|
mtSizeMax = cb / 2 - 3; // max mtSize not counting EOF record
|
|
|
|
while (mtSize <= mtSizeMax && pMR->rdFunction != 0)
|
|
{
|
|
mtSize += pMR->rdSize;
|
|
pMR = (PMETARECORD) ((PWORD) pMR + pMR->rdSize);
|
|
}
|
|
|
|
if (mtSize > mtSizeMax)
|
|
{
|
|
ERROR_ASSERT(FALSE, "pmf16AllocMF16: incomplete metafile\n");
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
|
|
// Powerpnt uses 0,0,0 for the EOF record! We will change it to 3,0,0 below.
|
|
|
|
mtSize += 3; // include EOF record (pMR->rdSize may not be valid)
|
|
|
|
if (((PMETAHEADER) pb)->mtSize != mtSize)
|
|
{
|
|
ERROR_ASSERT(FALSE, "pmf16AllocMF16: fixing up bad mtSize\n");
|
|
}
|
|
}
|
|
|
|
if (!(pmf16->hMem = LocalAlloc(LMEM_FIXED, mtSize * sizeof(WORD))))
|
|
goto pmf16AllocMF16_cleanup;
|
|
RtlCopyMemory((PBYTE) pmf16->hMem, (PBYTE)pb, mtSize * sizeof(WORD));
|
|
((PMETAHEADER) pmf16->hMem)->mtSize = mtSize; // update mtSize
|
|
|
|
VERIFYGDI(((PWORD) pmf16->hMem)[mtSize - 3] == 3
|
|
&& ((PWORD) pmf16->hMem)[mtSize - 2] == 0
|
|
&& ((PWORD) pmf16->hMem)[mtSize - 1] == 0,
|
|
"pmf16AllocMF16: fixing up bad EOF metafile record\n");
|
|
|
|
((PWORD) pmf16->hMem)[mtSize - 3] = 3; // update EOF record
|
|
((PWORD) pmf16->hMem)[mtSize - 2] = 0;
|
|
((PWORD) pmf16->hMem)[mtSize - 1] = 0;
|
|
}
|
|
|
|
// Make a copy of the metafile header.
|
|
|
|
pmf16->metaHeader = *(PMETAHEADER) pmf16->hMem;
|
|
pmf16->metaHeader.mtType = (pmf16->fl & MF16_DISKFILE)
|
|
? DISKMETAFILE
|
|
: MEMORYMETAFILE;
|
|
|
|
// Verify metafile header
|
|
|
|
if (!IsValidMetaHeader16(&pmf16->metaHeader))
|
|
{
|
|
ERROR_ASSERT(FALSE,
|
|
"pmf16AllocMF16: Metafile has an invalid header; Failing\n");
|
|
goto pmf16AllocMF16_cleanup;
|
|
}
|
|
|
|
// Everything is golden.
|
|
|
|
pmf16Rc = pmf16;
|
|
|
|
// Cleanup and go home.
|
|
|
|
pmf16AllocMF16_cleanup:
|
|
|
|
if (!pmf16Rc)
|
|
if (pmf16)
|
|
{
|
|
if (fl & ALLOCMF16_TRANSFER_BUFFER)
|
|
pmf16->hMem = NULL; // let caller free the buffer!
|
|
vFreeMF16(pmf16);
|
|
}
|
|
|
|
ERROR_ASSERT(pmf16Rc, "pmf16AllocMF16 failed");
|
|
return(pmf16Rc);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vFreeMF16 (pmf16)
|
|
*
|
|
* This is a low level routine which frees the resouces in the METAFILE16.
|
|
*
|
|
* This function is intended to be called from the routines CloseMetaFile
|
|
* and DeleteMetaFile.
|
|
*
|
|
* Arguments:
|
|
* pmf16 - The METAFILE16 to be freed.
|
|
*
|
|
* History:
|
|
* Fri May 15 14:11:22 1992 -by- Hock San Lee [hockl]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vFreeMF16(PMETAFILE16 pmf16)
|
|
{
|
|
PUTS("vFreeMF16\n");
|
|
|
|
ASSERTGDI(pmf16->ident == MF16_IDENTIFIER, "Bad METAFILE16");
|
|
|
|
// Free the resources.
|
|
|
|
if (!(pmf16->fl & MF16_DISKFILE))
|
|
{
|
|
// Free memory metafile.
|
|
|
|
if (pmf16->hMem)
|
|
if (LocalFree(pmf16->hMem))
|
|
ASSERTGDI(FALSE, "LocalFree failed");
|
|
}
|
|
else
|
|
{
|
|
// Unmap disk file.
|
|
|
|
if (pmf16->hMem)
|
|
if (!UnmapViewOfFile((LPVOID) pmf16->hMem))
|
|
ASSERTGDI(FALSE, "UmmapViewOfFile failed");
|
|
|
|
if (pmf16->hFileMap)
|
|
if (!CloseHandle(pmf16->hFileMap))
|
|
ASSERTGDI(FALSE, "CloseHandle failed");
|
|
|
|
if (pmf16->hFile != INVALID_HANDLE_VALUE)
|
|
if (!CloseHandle(pmf16->hFile))
|
|
ASSERTGDI(FALSE, "CloseHandle failed");
|
|
}
|
|
|
|
// Smash the identifier.
|
|
|
|
pmf16->ident = 0;
|
|
|
|
// Free the memory.
|
|
|
|
if (LocalFree(pmf16))
|
|
ASSERTGDI(FALSE, "LocalFree failed");
|
|
}
|