/* * DRAWICON.CPP * * Functions to handle creation of metafiles with icons and labels * as well as functions to draw such metafiles with or without the label. * * The metafile is created with a comment that marks the records containing * the label code. Drawing the metafile enumerates the records, draws * all records up to that point, then decides to either skip the label * or draw it. * * Copyright (c)1992 Microsoft Corporation, All Right Reserved */ #include "precomp.h" #include "utility.h" #include "malloc.h" // Private implementation //Structure for label and source extraction from a metafile typedef struct tagLABELEXTRACT { LPTSTR lpsz; UINT Index; // index in lpsz (so we can retrieve 2+ lines) DWORD PrevIndex; // index of last line (so we can mimic word wrap) union { UINT cch; //Length of label for label extraction UINT iIcon; //Index of icon in source extraction. } u; //For internal use in enum procs BOOL fFoundIconOnly; BOOL fFoundSource; BOOL fFoundIndex; } LABELEXTRACT, FAR * LPLABELEXTRACT; //Structure for extracting icons from a metafile (CreateIcon parameters) typedef struct tagICONEXTRACT { HICON hIcon; //Icon created in the enumeration proc. /* * Since we want to handle multitasking well we have the caller * of the enumeration proc instantiate these variables instead of * using statics in the enum proc (which would be bad). */ BOOL fAND; HGLOBAL hMemAND; //Enumeration proc allocates and copies } ICONEXTRACT, FAR * LPICONEXTRACT; //Structure to use to pass info to EnumMetafileDraw typedef struct tagDRAWINFO { RECT Rect; BOOL fIconOnly; } DRAWINFO, FAR * LPDRAWINFO; int CALLBACK EnumMetafileIconDraw(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPARAM); int CALLBACK EnumMetafileExtractLabel(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT); int CALLBACK EnumMetafileExtractIcon(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPICONEXTRACT); int CALLBACK EnumMetafileExtractIconSource(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT); /* * Strings for metafile comments. KEEP THESE IN SYNC WITH THE * STRINGS IN GETICON.CPP */ static const char szIconOnly[] = "IconOnly"; // Where to stop to exclude label. /* * OleUIMetafilePictIconFree * * Purpose: * Deletes the metafile contained in a METAFILEPICT structure and * frees the memory for the structure itself. * * Parameters: * hMetaPict HGLOBAL metafilepict structure created in * OleMetafilePictFromIconAndLabel * * Return Value: * None */ STDAPI_(void) OleUIMetafilePictIconFree(HGLOBAL hMetaPict) { if (NULL != hMetaPict) { STGMEDIUM stgMedium; stgMedium.tymed = TYMED_MFPICT; stgMedium.hMetaFilePict = hMetaPict; stgMedium.pUnkForRelease = NULL; ReleaseStgMedium(&stgMedium); } } /* * OleUIMetafilePictIconDraw * * Purpose: * Draws the metafile from OleMetafilePictFromIconAndLabel, either with * the label or without. * * Parameters: * hDC HDC on which to draw. * pRect LPRECT in which to draw the metafile. * hMetaPict HGLOBAL to the METAFILEPICT from * OleMetafilePictFromIconAndLabel * fIconOnly BOOL specifying to draw the label or not. * * Return Value: * BOOL TRUE if the function is successful, FALSE if the * given metafilepict is invalid. */ STDAPI_(BOOL) OleUIMetafilePictIconDraw(HDC hDC, LPCRECT pRect, HGLOBAL hMetaPict, BOOL fIconOnly) { if (NULL == hMetaPict) return FALSE; LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict); if (NULL == pMF) return FALSE; DRAWINFO di; di.Rect = *pRect; di.fIconOnly = fIconOnly; //Transform to back to pixels int cx = XformWidthInHimetricToPixels(hDC, pMF->xExt); int cy = XformHeightInHimetricToPixels(hDC, pMF->yExt); SaveDC(hDC); SetMapMode(hDC, pMF->mm); SetViewportOrgEx(hDC, (pRect->right - cx) / 2, 0, NULL); SetViewportExtEx(hDC, min ((pRect->right - cx) / 2 + cx, cx), cy, NULL); if (fIconOnly) EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileIconDraw, (LPARAM)&di); else PlayMetaFile(hDC, pMF->hMF); RestoreDC(hDC, -1); GlobalUnlock(hMetaPict); return TRUE; } /* * EnumMetafileIconDraw * * Purpose: * EnumMetaFile callback function that draws either the icon only or * the icon and label depending on given flags. * * Parameters: * hDC HDC into which the metafile should be played. * phTable HANDLETABLE FAR * providing handles selected into the DC. * pMFR METARECORD FAR * giving the enumerated record. * lParam LPARAM flags passed in EnumMetaFile. * * Return Value: * int 0 to stop enumeration, 1 to continue. */ int CALLBACK EnumMetafileIconDraw(HDC hDC, HANDLETABLE FAR *phTable, METARECORD FAR *pMFR, int cObj, LPARAM lParam) { LPDRAWINFO lpdi = (LPDRAWINFO)lParam; /* * We play everything blindly except for DIBBITBLT (or DIBSTRETCHBLT) * and ESCAPE with MFCOMMENT. For the BitBlts we change the x,y to * draw at (0,0) instead of wherever it was written to draw. The * comment tells us there to stop if we don't want to draw the label. */ //If we're playing icon only, stop enumeration at the comment. if (lpdi->fIconOnly) { if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) { if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2])) return 0; } /* * Check for the records in which we want to munge the coordinates. * destX is offset 6 for BitBlt, offset 9 for StretchBlt, either of * which may appear in the metafile. */ if (META_DIBBITBLT == pMFR->rdFunction) pMFR->rdParm[6]=0; if (META_DIBSTRETCHBLT == pMFR->rdFunction) pMFR->rdParm[9] = 0; } PlayMetaFileRecord(hDC, phTable, pMFR, cObj); return 1; } /* * OleUIMetafilePictExtractLabel * * Purpose: * Retrieves the label string from metafile representation of an icon. * * Parameters: * hMetaPict HGLOBAL to the METAFILEPICT containing the metafile. * lpszLabel LPSTR in which to store the label. * cchLabel UINT length of lpszLabel. * lpWrapIndex DWORD index of first character in last line. Can be NULL * if calling function doesn't care about word wrap. * * Return Value: * UINT Number of characters copied. */ STDAPI_(UINT) OleUIMetafilePictExtractLabel(HGLOBAL hMetaPict, LPTSTR lpszLabel, UINT cchLabel, LPDWORD lpWrapIndex) { if (NULL == hMetaPict || NULL == lpszLabel || 0 == cchLabel) return FALSE; /* * We extract the label by getting a screen DC and walking the metafile * records until we see the ExtTextOut record we put there. That * record will have the string embedded in it which we then copy out. */ LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict); if (NULL == pMF) return FALSE; LABELEXTRACT le; le.lpsz=lpszLabel; le.u.cch=cchLabel; le.Index=0; le.fFoundIconOnly=FALSE; le.fFoundSource=FALSE; //Unused for this function. le.fFoundIndex=FALSE; //Unused for this function. le.PrevIndex = 0; //Use a screen DC so we have something valid to pass in. HDC hDC = GetDC(NULL); EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractLabel, (LONG)(LPLABELEXTRACT)&le); ReleaseDC(NULL, hDC); GlobalUnlock(hMetaPict); //Tell where we wrapped (if calling function cares) if (NULL != lpWrapIndex) *lpWrapIndex = le.PrevIndex; //Return amount of text copied return le.u.cch; } /* * EnumMetafileExtractLabel * * Purpose: * EnumMetaFile callback function that walks a metafile looking for * ExtTextOut, then concatenates the text from each one into a buffer * in lParam. * * Parameters: * hDC HDC into which the metafile should be played. * phTable HANDLETABLE FAR * providing handles selected into the DC. * pMFR METARECORD FAR * giving the enumerated record. * pLE LPLABELEXTRACT providing the destination buffer and length. * * Return Value: * int 0 to stop enumeration, 1 to continue. */ int CALLBACK EnumMetafileExtractLabel(HDC hDC, HANDLETABLE FAR *phTable, METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE) { /* * We don't allow anything to happen until we see "IconOnly" * in an MFCOMMENT that is used to enable everything else. */ if (!pLE->fFoundIconOnly) { if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0]) { if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2])) pLE->fFoundIconOnly=TRUE; } return 1; } //Enumerate all records looking for META_EXTTEXTOUT - there can be more //than one. if (META_EXTTEXTOUT == pMFR->rdFunction) { /* * If ExtTextOut has NULL fuOptions, then the rectangle is omitted * from the record, and the string starts at rdParm[4]. If * fuOptions is non-NULL, then the string starts at rdParm[8] * (since the rectange takes up four WORDs in the array). In * both cases, the string continues for (rdParm[2]+1) >> 1 * words. We just cast a pointer to rdParm[8] to an LPSTR and * lstrcpyn into the buffer we were given. * * Note that we use element 8 in rdParm instead of 4 because we * passed ETO_CLIPPED in for the options on ExtTextOut--docs say * [4] which is rect doesn't exist if we passed zero there. * */ UINT cchMax = min(pLE->u.cch - pLE->Index, (UINT)pMFR->rdParm[2]); LPTSTR lpszTemp = pLE->lpsz + pLE->Index; #ifdef _UNICODE MultiByteToWideChar(CP_ACP, 0, (LPSTR)&pMFR->rdParm[8], cchMax, lpszTemp, cchMax+1); #else lstrcpyn(lpszTemp, (LPSTR)&pMFR->rdParm[8], cchMax+1); #endif lpszTemp[cchMax+1] = 0; pLE->PrevIndex = pLE->Index; pLE->Index += cchMax; } return 1; } /* * OleUIMetafilePictExtractIcon * * Purpose: * Retrieves the icon from metafile into which DrawIcon was done before. * * Parameters: * hMetaPict HGLOBAL to the METAFILEPICT containing the metafile. * * Return Value: * HICON Icon recreated from the data in the metafile. */ STDAPI_(HICON) OleUIMetafilePictExtractIcon(HGLOBAL hMetaPict) { if (NULL == hMetaPict) return NULL; /* * We extract the label by getting a screen DC and walking the metafile * records until we see the ExtTextOut record we put there. That * record will have the string embedded in it which we then copy out. */ LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict); if (NULL == pMF) return FALSE; ICONEXTRACT ie; ie.fAND = TRUE; //Use a screen DC so we have something valid to pass in. HDC hDC=GetDC(NULL); EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIcon, (LONG)&ie); ReleaseDC(NULL, hDC); GlobalUnlock(hMetaPict); return ie.hIcon; } /* * EnumMetafileExtractIcon * * Purpose: * EnumMetaFile callback function that walks a metafile looking for * StretchBlt (3.1) and BitBlt (3.0) records. We expect to see two * of them, the first being the AND mask and the second being the XOR * data. We * ExtTextOut, then copies the text into a buffer in lParam. * * Parameters: * hDC HDC into which the metafile should be played. * phTable HANDLETABLE FAR * providing handles selected into the DC. * pMFR METARECORD FAR * giving the enumerated record. * pIE LPICONEXTRACT providing the destination buffer and length. * * Return Value: * int 0 to stop enumeration, 1 to continue. */ int CALLBACK EnumMetafileExtractIcon(HDC hDC, HANDLETABLE FAR *phTable, METARECORD FAR *pMFR, int cObj, LPICONEXTRACT pIE) { //Continue enumeration if we don't see the records we want. if (META_DIBBITBLT != pMFR->rdFunction && META_DIBSTRETCHBLT != pMFR->rdFunction) return 1; UNALIGNED BITMAPINFO* lpBI; UINT uWidth, uHeight; /* * Windows 3.0 DrawIcon uses META_DIBBITBLT in whereas 3.1 uses * META_DIBSTRETCHBLT so we have to handle each case separately. */ if (META_DIBBITBLT==pMFR->rdFunction) //Win3.0 { //Get dimensions and the BITMAPINFO struct. uHeight = pMFR->rdParm[1]; uWidth = pMFR->rdParm[2]; lpBI = (LPBITMAPINFO)&(pMFR->rdParm[8]); } if (META_DIBSTRETCHBLT == pMFR->rdFunction) //Win3.1 { //Get dimensions and the BITMAPINFO struct. uHeight = pMFR->rdParm[2]; uWidth = pMFR->rdParm[3]; lpBI = (LPBITMAPINFO)&(pMFR->rdParm[10]); } UNALIGNED BITMAPINFOHEADER* lpBH=(LPBITMAPINFOHEADER)&(lpBI->bmiHeader); //Pointer to the bits which follows the BITMAPINFO structure. LPBYTE lpbSrc=(LPBYTE)lpBI+sizeof(BITMAPINFOHEADER); //Add the length of the color table (if one exists) if (0 != lpBH->biClrUsed) { // If we have an explicit count of colors used, we // can find the offset to the data directly lpbSrc += (lpBH->biClrUsed*sizeof(RGBQUAD)); } else if (lpBH->biCompression == BI_BITFIELDS) { // 16 or 32 bpp, indicated by BI_BITFIELDS in the compression // field, have 3 DWORD masks for adjusting subsequent // direct-color values, and no palette lpbSrc += 3 * sizeof(DWORD); } else { // In other cases, there is an array of RGBQUAD entries // equal to 2^(biBitCount) where biBitCount is the number // of bits per pixel. The exception is 24 bpp bitmaps, // which have no color table and just use direct RGB values. lpbSrc += (lpBH->biBitCount == 24) ? 0 : (1 << (lpBH->biBitCount)) * sizeof(RGBQUAD); } // copy into aligned stack space (since SetDIBits needs aligned data) size_t nSize = lpbSrc - (LPBYTE)lpBI; LPBITMAPINFO lpTemp = (LPBITMAPINFO)_alloca(nSize); memcpy(lpTemp, lpBI, nSize); /* * All the bits we have in lpbSrc are device-independent, so we * need to change them over to be device-dependent using SetDIBits. * Once we have a bitmap with the device-dependent bits, we can * GetBitmapBits to have buffers with the real data. * * For each pass we have to allocate memory for the bits. We save * the memory for the mask between passes. */ HBITMAP hBmp; //Use CreateBitmap for ANY monochrome bitmaps if (pIE->fAND || 1==lpBH->biBitCount) hBmp=CreateBitmap((UINT)lpBH->biWidth, (UINT)lpBH->biHeight, 1, 1, NULL); else hBmp=CreateCompatibleBitmap(hDC, (UINT)lpBH->biWidth, (UINT)lpBH->biHeight); if (!hBmp || !SetDIBits(hDC, hBmp, 0, (UINT)lpBH->biHeight, (LPVOID)lpbSrc, lpTemp, DIB_RGB_COLORS)) { if (!pIE->fAND) GlobalFree(pIE->hMemAND); DeleteObject(hBmp); return 0; } //Allocate memory and get the DDBits into it. BITMAP bm; GetObject(hBmp, sizeof(bm), &bm); DWORD cb = bm.bmHeight*bm.bmWidthBytes * bm.bmPlanes; HGLOBAL hMem = GlobalAlloc(GHND, cb); if (NULL==hMem) { if (NULL != pIE->hMemAND) GlobalFree(pIE->hMemAND); DeleteObject(hBmp); return 0; } LPBYTE lpbDst = (LPBYTE)GlobalLock(hMem); GetBitmapBits(hBmp, cb, (LPVOID)lpbDst); DeleteObject(hBmp); GlobalUnlock(hMem); /* * If this is the first pass (pIE->fAND==TRUE) then save the memory * of the AND bits for the next pass. */ if (pIE->fAND) { pIE->fAND = FALSE; pIE->hMemAND = hMem; //Continue enumeration looking for the next blt record. return 1; } else { //Get the AND pointer again. lpbSrc=(LPBYTE)GlobalLock(pIE->hMemAND); /* * Create the icon now that we have all the data. lpbDst already * points to the XOR bits. */ int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); pIE->hIcon = CreateIcon(_g_hOleStdInst, uWidth, uHeight, (BYTE)bm.bmPlanes, (BYTE)bm.bmBitsPixel, lpbSrc, lpbDst); GlobalUnlock(pIE->hMemAND); GlobalFree(pIE->hMemAND); GlobalFree(hMem); return 0; } } /* * OleUIMetafilePictExtractIconSource * * Purpose: * Retrieves the filename and index of the icon source from a metafile * created with OleMetafilePictFromIconAndLabel. * * Parameters: * hMetaPict HGLOBAL to the METAFILEPICT containing the metafile. * lpszSource LPTSTR in which to store the source filename. This * buffer should be MAX_PATH characters. * piIcon UINT FAR * in which to store the icon's index * within lpszSource * * Return Value: * BOOL TRUE if the records were found, FALSE otherwise. */ STDAPI_(BOOL) OleUIMetafilePictExtractIconSource(HGLOBAL hMetaPict, LPTSTR lpszSource, UINT FAR *piIcon) { if (NULL == hMetaPict || NULL == lpszSource || NULL == piIcon) return FALSE; /* * We will walk the metafile looking for the two comment records * following the IconOnly comment. The flags fFoundIconOnly and * fFoundSource indicate if we have found IconOnly and if we have * found the source comment already. */ LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict); if (NULL == pMF) return FALSE; LABELEXTRACT le; le.lpsz = lpszSource; le.fFoundIconOnly = FALSE; le.fFoundSource = FALSE; le.fFoundIndex = FALSE; //Use a screen DC so we have something valid to pass in. HDC hDC = GetDC(NULL); EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIconSource, (LONG)(LPLABELEXTRACT)&le); ReleaseDC(NULL, hDC); GlobalUnlock(hMetaPict); //Copy the icon index to the caller's variable. *piIcon=le.u.iIcon; //Check that we found everything. return (le.fFoundIconOnly && le.fFoundSource && le.fFoundIndex); } /* * EnumMetafileExtractIconSource * * Purpose: * EnumMetaFile callback function that walks a metafile skipping the first * comment record, extracting the source filename from the second, and * the index of the icon in the third. * * Parameters: * hDC HDC into which the metafile should be played. * phTable HANDLETABLE FAR * providing handles selected into the DC. * pMFR METARECORD FAR * giving the enumerated record. * pLE LPLABELEXTRACT providing the destination buffer and * area to store the icon index. * * Return Value: * int 0 to stop enumeration, 1 to continue. */ int CALLBACK EnumMetafileExtractIconSource(HDC hDC, HANDLETABLE FAR *phTable, METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE) { /* * We don't allow anything to happen until we see "IconOnly" * in an MFCOMMENT that is used to enable everything else. */ if (!pLE->fFoundIconOnly) { if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0]) { if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2])) pLE->fFoundIconOnly=TRUE; } return 1; } //Now see if we find the source string. if (!pLE->fFoundSource) { if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0]) { #ifdef _UNICODE MultiByteToWideChar(CP_ACP, 0, (LPSTR)&pMFR->rdParm[2], -1, pLE->lpsz, MAX_PATH); #else lstrcpyn(pLE->lpsz, (LPSTR)&pMFR->rdParm[2], MAX_PATH); #endif pLE->lpsz[MAX_PATH-1] = '\0'; pLE->fFoundSource=TRUE; } return 1; } //Next comment will be the icon index. if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0]) { /* * This string contains the icon index in string form, * so we need to convert back to a UINT. After we see this * we can stop the enumeration. The comment will have * a null terminator because we made sure to save it. */ LPSTR psz = (LPSTR)&pMFR->rdParm[2]; pLE->u.iIcon = 0; //Do Ye Olde atoi while (*psz) pLE->u.iIcon = (10*pLE->u.iIcon)+((*psz++)-'0'); pLE->fFoundIndex=TRUE; return 0; } return 1; }