DRAWICON.C

/* 
* DRAWICON.C
*
* 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-1996 Microsoft Corporation, All Right Reserved
*/

#define STRICT 1
#include "olestd.h"
#include "common.h"
#include "utility.h"

/*
* Strings for metafile comments. KEEP THESE IN SYNC WITH THE
* STRINGS IN GETICON.C.
*/

static 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
* OleUIMetafilePictFromIconAndLabel
*
* Return Value:
* None
*/

STDAPI_(void) OleUIMetafilePictIconFree(HGLOBAL hMetaPict)
{
LPMETAFILEPICT pMF;

if (NULL==hMetaPict)
return;

pMF=(LPMETAFILEPICT)GlobalLock(hMetaPict);

if (NULL!=pMF)
{
if (NULL!=pMF->hMF)
DeleteMetaFile(pMF->hMF);
}

GlobalUnlock(hMetaPict);
GlobalFree(hMetaPict);
return;
}








/*
* OleUIMetafilePictIconDraw
*
* Purpose:
* Draws the metafile from OleUIMetafilePictFromIconAndLabel, 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
* OleUIMetafilePictFromIconAndLabel
* 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, LPRECT pRect, HGLOBAL hMetaPict
, BOOL fIconOnly)
{
LPMETAFILEPICT pMF;
DRAWINFO di;
int cx, cy;
SIZE size;
POINT point;

if (NULL==hMetaPict)
return FALSE;

pMF=GlobalLock(hMetaPict);

if (NULL==pMF)
return FALSE;

di.Rect = *pRect;
di.fIconOnly = fIconOnly;

//Transform to back to pixels
cx=XformWidthInHimetricToPixels(hDC, pMF->xExt);
cy=XformHeightInHimetricToPixels(hDC, pMF->yExt);

SaveDC(hDC);

SetMapMode(hDC, pMF->mm);
SetViewportOrgEx(hDC, (pRect->right - cx) / 2, 0, &point);

SetViewportExtEx(hDC, min ((pRect->right - cx) / 2 + cx, cx), cy, &size);

if (fIconOnly)
{
// Since we've used the __export keyword on the
// EnumMetafileIconDraw proc, we do not need to use
// MakeProcInstance
EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileIconDraw
, (LPARAM)(LPDRAWINFO)&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 EXPORT 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==lstrcmpi(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, LPOLESTR lpszLabel
, UINT cchLabel, LPDWORD lpWrapIndex)
{
LPMETAFILEPICT pMF;
LABELEXTRACT le;
HDC hDC;

/*
* 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.
*/

if (NULL==hMetaPict || NULL==lpszLabel || 0==cchLabel)
return FALSE;

pMF=GlobalLock(hMetaPict);

if (NULL==pMF)
return FALSE;

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=GetDC(NULL);

// Since we've used the EXPORT keyword on the
// EnumMetafileExtractLabel proc, we do not need to use
// MakeProcInstance

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 EXPORT 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==lstrcmpi(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)
{
UINT cchMax;
LPOLESTR lpszTemp;

/*
* 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.
*
*/

cchMax=min(pLE->u.cch - pLE->Index, (UINT)pMFR->rdParm[2]);
lpszTemp = /*(LPOLESTR)*/((/*(LPSTR)*/pLE->lpsz) + pLE->Index);

A2W ((LPSTR)&(pMFR->rdParm[8]), lpszTemp, cchMax);
lpszTemp[cchMax]='\0';

pLE->u.cch = OLESTRLEN (pLE->lpsz);
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)
{
LPMETAFILEPICT pMF;
HDC hDC;
ICONEXTRACT ie;

/*
* 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.
*/

if (NULL==hMetaPict)
return NULL;

pMF=GlobalLock(hMetaPict);

if (NULL==pMF)
return FALSE;

//Use a screen DC so we have something valid to pass in.
hDC=GetDC(NULL);
ie.fAND=TRUE;

// We get information back in the ICONEXTRACT structure.
// (Since we've used the EXPORT keyword on the
// EnumMetafileExtractLabel proc, we do not need to use
// MakeProcInstance)
EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIcon, (LONG)(LPICONEXTRACT)&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 EXPORT EnumMetafileExtractIcon(HDC hDC, HANDLETABLE FAR *phTable
, METARECORD FAR *pMFR, int cObj, LPICONEXTRACT pIE)
{
LPBITMAPINFO lpBI;
LPBITMAPINFOHEADER lpBH;
LPBYTE lpbSrc;
LPBYTE lpbDst;
UINT uWidth, uHeight;
DWORD cb;
HGLOBAL hMem;
BITMAP bm;
HBITMAP hBmp;
int cxIcon, cyIcon;


//Continue enumeration if we don't see the records we want.
if (META_DIBBITBLT!=pMFR->rdFunction && META_DIBSTRETCHBLT!=pMFR->rdFunction)
return 1;

/*
* 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]);
}

lpBH=(LPBITMAPINFOHEADER)&(lpBI->bmiHeader);

//Pointer to the bits which follows the BITMAPINFO structure.
lpbSrc=(LPBYTE)lpBI+lpBH->biSize;

//Add the length of the color table.
if (0!=lpBH->biClrUsed)
lpbSrc+=(DWORD)(lpBH->biClrUsed*sizeof(RGBQUAD));
else
{
/*
* 1 << bc gives 2, 16, 256 for 1, 4, or 8 bits. 24-bit
* bitmaps have no color table, so there's no need to
* change lpbSrc.
*/
if (lpBH->biBitCount <= 8)
lpbSrc+=(DWORD)((1 << (lpBH->biBitCount))*sizeof(RGBQUAD));
}


/*
* 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.
*/

//Use CreateBitmap for ANY monochrome bitmaps
if (pIE->fAND || 1==lpBH->biBitCount || lpBH->biBitCount > 8)
hBmp=CreateBitmap((UINT)lpBH->biWidth, (UINT)lpBH->biHeight, 1, 1, NULL);
else if (lpBH->biBitCount <= 8)
hBmp=CreateCompatibleBitmap(hDC, (UINT)lpBH->biWidth, (UINT)lpBH->biHeight);

if (!hBmp || !SetDIBits(hDC, hBmp, 0, (UINT)lpBH->biHeight, (LPVOID)lpbSrc, lpBI, DIB_RGB_COLORS))
{
if (!pIE->fAND)
GlobalFree(pIE->hMemAND);

DeleteObject(hBmp);
return 0;
}

//Allocate memory and get the DDBits into it.
GetObject(hBmp, sizeof(bm), &bm);

cb=bm.bmHeight*bm.bmWidthBytes * bm.bmPlanes;

// if (cb % 4 != 0) // dword align
// cb += 4 - (cb % 4);

hMem=GlobalAlloc(GHND, cb);

if (NULL==hMem)
{
if (NULL!=pIE->hMemAND)
GlobalFree(pIE->hMemAND);

DeleteObject(hBmp);
return 0;
}

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.
*/
cxIcon = GetSystemMetrics(SM_CXICON);
cyIcon = GetSystemMetrics(SM_CYICON);

pIE->hIcon=CreateIcon(ghInst,
uWidth,
uHeight,
(BYTE)bm.bmPlanes,
(BYTE)bm.bmBitsPixel,
(LPVOID)lpbSrc,
(LPVOID)lpbDst);

GlobalUnlock(pIE->hMemAND);
GlobalFree(pIE->hMemAND);
GlobalFree(hMem);

//We're done so we can stop.
return 0;
}
}





/*
* OleUIMetafilePictExtractIconSource
*
* Purpose:
* Retrieves the filename and index of the icon source from a metafile
* created with OleUIMetafilePictFromIconAndLabel.
*
* Parameters:
* hMetaPict HGLOBAL to the METAFILEPICT containing the metafile.
* lpszSource LPSTR in which to store the source filename. This
* buffer should be OLEUI_CCHPATHMAX 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
, LPOLESTR lpszSource, UINT FAR *piIcon)
{
LPMETAFILEPICT pMF;
LABELEXTRACT le;
HDC hDC;

/*
* 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.
*/

if (NULL==hMetaPict || NULL==lpszSource || NULL==piIcon)
return FALSE;

pMF=GlobalLock(hMetaPict);

if (NULL==pMF)
return FALSE;

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=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 EXPORT EnumMetafileExtractIconSource(HDC hDC, HANDLETABLE FAR *phTable
, METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE)
{
LPSTR psz;

/*
* 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==lstrcmpi(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])
{
A2W ((LPSTR)&pMFR->rdParm[2], pLE->lpsz, OLEUI_CCHPATHMAX);
pLE->lpsz[OLEUI_CCHPATHMAX-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.
*/
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;
}