//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       gen.cpp
//
//  Contents:   Implementation of the generic picture object (CGenObject)
//              and dib routines.
//
//  Classes:    CGenObject implementation
//
//  Functions:  DibDraw (internal)
//              DibMakeLogPalette (internal)
//              DibFillPaletteEntries (internal)
//
//  History:    dd-mmm-yy Author    Comment
//              01-Feb-95 t-ScottH  add Dump method and DumpCGenObject API
//              25-Jan-94 alexog    first pass at converting to Cairo-style
//                                  memory allocations.
//              11-Jan-94 alexgo    added VDATEHEAP macros to every function
//                                  and method
//              07-Dec-93 ChrisWe   make default params to StSetSize explicit
//              07-Dec-93 alexgo    merged 16bit RC9 changes
//              29-Nov-93 ChrisWe   make default arguments to UtDupGlobal,
//                                      UtConvertBitmapToDib explicit
//              23-Nov-93 alexgo    32bit port
//      srinik  06/04/93        Added the support for demand loading and
//                              discarding the caches.
//      SriniK  03/19/1993      Deleted dib.cpp and moved DIB drawing routines
//                              into this file.
//      SriniK  01/07/1993      Merged dib.cpp into gen.cpp
//
//--------------------------------------------------------------------------

/*
REVIEW32::: WARNING WARNING
There are many potentially bogus pointer to Palette, etc handle conversions
put in to make the code compile.  :(
(Gee, thanks for marking them as you went)
*/

#include <le2int.h>
#pragma SEG(gen)

#include "gen.h"

#ifdef _DEBUG
#include <dbgdump.h>
#endif // _DEBUG

ASSERTDATA

#define M_HPRES()               (m_hPres ? m_hPres : LoadHPRES())

//local functions
INTERNAL                DibDraw(HANDLE hDib, HDC hdc, LPCRECTL lprc);
INTERNAL_(HANDLE)       DibMakeLogPalette (BYTE FAR *lpColorData,
				WORD wDataSize,
				LPLOGPALETTE FAR* lplpLogPalette);
INTERNAL_(void)         DibFillPaletteEntries(BYTE FAR *lpColorData,
				WORD wDataSize, LPLOGPALETTE lpLogPalette);



/*
 *      IMPLEMENTATION of CGenObject
 *
 */

NAME_SEG(Gen)


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::CGenObject
//
//  Synopsis:   Constructor
//
//  Effects:
//
//  Arguments:  [pCacheNode]    -- cache for the object
//              [cfFormat]      -- clipboard format of the object
//              [dwAspect]      -- drawing aspect of the object
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:  just initializes member variables
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_ctor)
CGenObject::CGenObject(LPCACHENODE pCacheNode, CLIPFORMAT cfFormat,
	DWORD dwAspect)
{
	VDATEHEAP();

	m_ulRefs        = 1;
	m_dwSize        = NULL;
	m_lWidth        = NULL;
	m_lHeight       = NULL;
	m_hPres         = NULL;
	m_cfFormat      = cfFormat;
	m_dwAspect      = dwAspect;
	m_pCacheNode    = pCacheNode;
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::~CGenObject
//
//  Synopsis:   Destructor
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_dtor)
CGenObject::~CGenObject(void)
{
	VDATEHEAP();

	if (m_hPres)
	{
		LEVERIFY( NULL == GlobalFree (m_hPres));
	}
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::QueryInterface
//
//  Synopsis:   returns interfaces on the generic picture object
//
//  Effects:
//
//  Arguments:  [iid]           -- the requested interface ID
//              [ppvObj]        -- where to put the interface pointer
//
//  Requires:
//
//  Returns:    NOERROR, E_NOINTERFACE
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IUnkown
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_QueryInterface)
STDMETHODIMP CGenObject::QueryInterface (REFIID iid, void FAR* FAR* ppvObj)
{
	VDATEHEAP();

	if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IOlePresObj))
	{
		*ppvObj = this;
		AddRef();
		return NOERROR;
	}
	else
	{
		*ppvObj = NULL;
		return ResultFromScode(E_NOINTERFACE);
	}
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::AddRef
//
//  Synopsis:   increments the reference count
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:    ULONG -- the new reference count
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IUnknown
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_AddRef)
STDMETHODIMP_(ULONG) CGenObject::AddRef(void)
{
	VDATEHEAP();
	
	return ++m_ulRefs;
}
			
//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::Release
//
//  Synopsis:   Decrements the reference count
//
//  Effects:    may delete [this] object
//
//  Arguments:
//
//  Requires:
//
//  Returns:    ULONG -- the new reference count
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IUnknown
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_Release)
STDMETHODIMP_(ULONG) CGenObject::Release(void)
{
	VDATEHEAP();

	if (--m_ulRefs == 0)
	{
		delete this;
		return 0;
	}

	return m_ulRefs;
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetData
//
//  Synopsis:   retrieves data of the specified format
//
//  Effects:
//
//  Arguments:  [pformatetcIn]  -- the requested data format
//              [pmedium]       -- where to put the data
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObject
//
//  Algorithm:  If available, copies the presentation to pmedium
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------


#pragma SEG(CGenObject_GetData)
STDMETHODIMP CGenObject::GetData
	(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
	VDATEHEAP();

	SCODE sc;
	
	if (IsBlank())
	{
		sc = OLE_E_BLANK;
	}
	else if (pformatetcIn->cfFormat != m_cfFormat)
	{

		if (m_cfFormat == CF_DIB &&
			pformatetcIn->cfFormat == CF_BITMAP)
		{
			return GetBitmapData(pformatetcIn, pmedium);
		}
		else
		{
			sc = DV_E_CLIPFORMAT;
		}
	}
	else if (0 == (pformatetcIn->tymed & TYMED_HGLOBAL))
	{
		sc = DV_E_TYMED;
	}
	else
	{
		if (NULL == (pmedium->hGlobal = GetCopyOfHPRES()))
		{
			sc = E_OUTOFMEMORY;
			goto errRtn;
		}
		
		pmedium->tymed = TYMED_HGLOBAL;
		return NOERROR;
	}
	
errRtn:
	// null out in case of error
	pmedium->tymed = TYMED_NULL;
	pmedium->pUnkForRelease = NULL;
	return ResultFromScode(sc);
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetDataHere
//
//  Synopsis:   retrieves presentation data into the given pmedium
//
//  Effects:
//
//  Arguments:  [pformatetcIn]  -- the requested data format
//              [pmedium]       -- where to put the data
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:  copies presentation data into the given storage medium
//              after error checking on the arguments
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_GetDataHere)
STDMETHODIMP CGenObject::GetDataHere
	(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
	VDATEHEAP();

	SCODE scode = S_OK;

	if (pformatetcIn->cfFormat != m_cfFormat)
	{
		scode = DV_E_CLIPFORMAT;
	}
	else if (pmedium->tymed != TYMED_HGLOBAL
		&& pmedium->tymed != TYMED_ISTREAM)
	{
		scode = DV_E_TYMED;
	}
	else if (pmedium->hGlobal == NULL)
	{
		scode = E_INVALIDARG;
	}
	else if (IsBlank())
	{
		scode = OLE_E_BLANK;
	}
	else    // actually get the data now
	{
		if (pmedium->tymed == TYMED_HGLOBAL)
		{
			// check the size of the given pmedium and then
			// copy the data into it
			LPVOID  lpsrc = NULL;
			LPVOID  lpdst = NULL;
			DWORD   dwSizeDst;

			scode = E_OUTOFMEMORY;

			if (0 == (dwSizeDst = (DWORD) GlobalSize(pmedium->hGlobal)))
			{
				goto errRtn;
			}

			// not enough room to copy
			if (dwSizeDst  < m_dwSize)
			{
				goto errRtn;
			}
	
			if (NULL == (lpdst = (LPVOID) GlobalLock(pmedium->hGlobal)))
			{
				goto errRtn;
			}
	
			if (NULL == (lpsrc = (LPVOID) GlobalLock(M_HPRES())))
			{
				goto errMem;
			}
		
			_xmemcpy(lpdst, lpsrc, m_dwSize);
			scode = S_OK;
		
		errMem:
			if (lpdst)
			{
				GlobalUnlock(pmedium->hGlobal);
			}
			if (lpsrc)
			{
				GlobalUnlock(m_hPres);
			}
			
		}
		else
		{
			Assert(pmedium->tymed == TYMED_ISTREAM);
			if (m_cfFormat == CF_DIB)
			{
				return UtHDIBToDIBFileStm(M_HPRES(),
						m_dwSize,pmedium->pstm);
			}
			else
			{
				return UtHGLOBALtoStm(M_HPRES(),
						m_dwSize, pmedium->pstm);
			}
		}
	}
	
errRtn:
	return ResultFromScode(scode);
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::SetDataWDO
//
//  Synopsis:   Takes the given presentation data and stores it
//
//  Effects:
//
//  Arguments:  [pformatetc]    -- the format of the data
//              [pmedium]       -- the new presentation data
//              [fRelease]      -- if TRUE, then we keep the data, else
//                                 we keep a copy
//              [pDataObj]      -- pointer to the IDataObject, may be NULL
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

STDMETHODIMP CGenObject::SetDataWDO
	(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium, BOOL fRelease, IDataObject * pDataObj)
{
	VDATEHEAP();

	HRESULT         error;
	BOOL            fTakeData = FALSE;
	
	if (pformatetc->cfFormat != m_cfFormat)
	{
		if (m_cfFormat == CF_DIB && pformatetc->cfFormat == CF_BITMAP)
		{
			return SetBitmapData(pformatetc, pmedium, fRelease, pDataObj);
		}
		else
		{
			return ResultFromScode(DV_E_CLIPFORMAT);
		}
	}

	
	if (pmedium->tymed != TYMED_HGLOBAL)
	{
		return ResultFromScode(DV_E_TYMED);
	}

	if ((pmedium->pUnkForRelease == NULL) && fRelease)
	{
		// we can take the ownership of the data
		fTakeData = TRUE;
	}

	// ChangeData will keep the data if fRelease is TRUE, else it copies
	error = ChangeData (pmedium->hGlobal, fTakeData);

	if (fTakeData)
	{
		pmedium->tymed = TYMED_NULL;
	}
	else if (fRelease)
	{
		ReleaseStgMedium(pmedium);
	}

	return error;
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::ChangeData   (private)
//
//  Synopsis:   Replaces the stored presentation
//
//  Effects:
//
//  Arguments:  [hNewData]      -- the new presentation
//              [fDelete]       -- if TRUE, then free hNewData
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//  Notes:
//
// If the routine fails then the object will be left with it's old data.
// In case of failure if fDelete is TRUE, then hNewData will be freed.
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_ChangeData)
INTERNAL CGenObject::ChangeData (HANDLE hNewData, BOOL fDelete)
{
	VDATEHEAP();

	HRESULT         hresult = ResultFromScode(E_OUTOFMEMORY);
	
	if (!hNewData)
	{
		return ResultFromScode(OLE_E_BLANK);
	}

	if (!fDelete)
	{
		if (NULL == (hNewData = UtDupGlobal(hNewData, GMEM_MOVEABLE)))
		{
			return hresult;
		}
	}
	else
	{
		HANDLE          hTmp;
		
		// change the ownership to yourself

                hTmp = GlobalReAlloc (hNewData, 0L, GMEM_MODIFY|GMEM_SHARE);
		if (NULL == hTmp)
		{
			if (NULL == (hTmp = UtDupGlobal(hNewData, GMEM_MOVEABLE)))
			{
				goto errRtn;
			}
			
			// Realloc failed but copying succeeded. Since this is fDelete
			// case, free the source global handle.
			LEVERIFY( NULL == GlobalFree(hNewData));
		}
		
		hNewData = hTmp;
	}

#ifndef _MAC

	// CF_DIB format specific code.  Get the it's extents
	if (m_cfFormat == CF_DIB)
	{
		LPBITMAPINFOHEADER      lpBi;

		if (NULL == (lpBi = (LPBITMAPINFOHEADER) GlobalLock (hNewData)))
		{
			goto errRtn;
		}
	
		UtGetDibExtents (lpBi, &m_lWidth, &m_lHeight);
		GlobalUnlock (hNewData);
	}
	
#endif

	// free the old presentation
	if (m_hPres)
	{
		LEVERIFY( NULL == GlobalFree (m_hPres));
	}

	// store the new presentation in m_hPres
	m_dwSize  = (DWORD) GlobalSize (m_hPres = hNewData);
	
	return NOERROR;
	
errRtn:
	if (hNewData && fDelete)
	{
		LEVERIFY( NULL == GlobalFree (hNewData));
	}

	return hresult;
}



//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::Draw
//
//  Synopsis:   Calls DibDraw to draw the stored bitmap presentation
//
//  Effects:
//
//  Arguments:  [pvAspect]      -- drawing aspect
//              [hicTargetDev]  -- the target device
//              [hdcDraw]       -- the device context
//              [lprcBounds]    -- drawing boundary
//              [lprcWBounds]   -- boundary rectangle for metafiles
//              [pfnContinue]   -- callback function to periodically call
//                                 for long drawing operations
//              [dwContinue]    -- argument to be passed to pfnContinue
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_Draw)
STDMETHODIMP CGenObject::Draw(void *    /* UNUSED pvAspect      */,
			      HDC       /* UNUSED hicTargetDev  */,
			      HDC       hdcDraw,
			      LPCRECTL  lprcBounds,
			      LPCRECTL  /* UNUSED lprcWBounds   */,
			      BOOL (CALLBACK * /*UNUSED pfcCont*/)(ULONG_PTR),
			      ULONG_PTR     /* UNUSED dwContinue    */)
{
	VDATEHEAP();

#ifndef _MAC
	if (m_cfFormat == CF_DIB)
	{
		return DibDraw (M_HPRES(), hdcDraw,lprcBounds);
	}
#endif

	return ResultFromScode(E_NOTIMPL);
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::Load
//
//  Synopsis:   Loads a stored presentation object from the given stream
//
//  Effects:
//
//  Arguments:  [lpstream]              -- the stream from which to load
//              [fReadHeaderOnly]       -- if TRUE, only get header info
//                                         (such as size, width, height, etc)
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

STDMETHODIMP CGenObject::Load(LPSTREAM lpstream, BOOL fReadHeaderOnly)
{
	VDATEHEAP();

	DWORD           dwBuf[4];
	HRESULT         error;
	
	/* read dwCompression, width, height, size of data */
	error = StRead(lpstream, dwBuf, 4 * sizeof(DWORD));
	if (error)
	{
		return error;
	}

	// we don't allow for compression yet
	AssertSz (dwBuf[0] == 0, "Picture compression factor is non-zero");
	
	m_lWidth  = (LONG) dwBuf[1];
	m_lHeight = (LONG) dwBuf[2];
	m_dwSize  = dwBuf[3];


	if (!m_dwSize || fReadHeaderOnly)
	{
		return NOERROR;
	}

	return UtGetHGLOBALFromStm(lpstream, m_dwSize, &m_hPres);
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::Save
//
//  Synopsis:   Stores presentation data to the given stream
//
//  Effects:
//
//  Arguments:  [lpstream]      -- where to store the data
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

STDMETHODIMP CGenObject::Save(LPSTREAM lpstream)
{
	VDATEHEAP();

	HRESULT         error;
	DWORD           dwBuf[4];

	/* write dwCompression, width, height, size of data */

	dwBuf[0]  = 0L;
	dwBuf[1]  = (DWORD) m_lWidth;
	dwBuf[2]  = (DWORD) m_lHeight;
	dwBuf[3]  = m_dwSize;

        error = StWrite(lpstream, dwBuf, 4*sizeof(DWORD));
	if (error)
	{
		return error;
	}

	// if we're blank or don't have any presentation data, then
	// nothing to else to save.
	if (IsBlank() || m_hPres == NULL)
	{
		StSetSize(lpstream, 0, TRUE);
		return NOERROR;
	}

	return UtHGLOBALtoStm(m_hPres, m_dwSize, lpstream);
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetExtent
//
//  Synopsis:   retrieves the size (width/height) of the presentation bitmap
//
//  Effects:
//
//  Arguments:  [dwDrawAspect]  -- the drawing aspect the caller is
//                                 interested in
//              [lpsizel]       -- where to put the size extents
//
//  Requires:
//
//  Returns:    HRESULT  (NOERROR, DV_E_DVASPECT, OLE_E_BLANK)
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:  retrieves the stored dimensions
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_GetExtent)
STDMETHODIMP CGenObject::GetExtent(DWORD dwDrawAspect, LPSIZEL lpsizel)
{
	VDATEHEAP();

	// aspects must match
	if (!(dwDrawAspect & m_dwAspect))
	{
		return ResultFromScode(DV_E_DVASPECT);
	}

	if (IsBlank())
	{
		return ResultFromScode(OLE_E_BLANK);
	}
	
	lpsizel->cx = m_lWidth;
	lpsizel->cy = m_lHeight;
	
	if (lpsizel->cx || lpsizel->cy)
	{
		return NOERROR;
	}
	else
	{
		return ResultFromScode(OLE_E_BLANK);
	}
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetColorSet
//
//  Synopsis:   Retrieves the pallette associated with the bitmap
//
//  Effects:
//
//  Arguments:  [pvAspect]      -- the drawing aspect  (unused)
//              [hicTargetDev]  -- the target device (unused)
//              [ppColorSet]    -- where to put the new palette
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:  Allocates a new pallette and copies the bitmap
//              palette into it.
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port, fixed bad memory bugs
//
//  Notes:
//
//--------------------------------------------------------------------------

STDMETHODIMP CGenObject::GetColorSet(LPVOID         /* UNUSED pvAspect     */,
                                     HDC            /* UNUSED hicTargetDev */,
                                     LPLOGPALETTE * ppColorSet)
{
    VDATEHEAP();

    HRESULT hresult = ResultFromScode(S_FALSE);

    if (m_cfFormat == CF_DIB)
    {
	if (IsBlank())
	{
	    return ResultFromScode(OLE_E_BLANK);
	}

	LPBITMAPINFOHEADER      lpbmih;
	LPLOGPALETTE            lpLogpal;
	WORD                    wPalSize;
		
	if (NULL == (lpbmih = (LPBITMAPINFOHEADER) GlobalLock (M_HPRES())))
	{
	    return ResultFromScode(E_OUTOFMEMORY);
	}

	// A bitmap with more than 8 bpp cannot have a palette at all,
	// so we just return S_FALSE

	if (lpbmih->biBitCount > 8)
	{
	    goto errRtn;
	}

	// Note: the return from UtPaletteSize can overflow the WORD
	// wPalSize, but utPaletteSize asserts against this
                			
	if (0 == (wPalSize = (WORD) UtPaletteSize(lpbmih)))
	{
	    goto errRtn;
	}
	
	lpLogpal = (LPLOGPALETTE)PubMemAlloc(wPalSize +
				2*sizeof(WORD));
	if (lpLogpal == NULL)
	{
	    hresult = ResultFromScode(E_OUTOFMEMORY);
	    goto errRtn;
	}
	
	DibFillPaletteEntries((BYTE FAR *)++lpbmih, wPalSize, lpLogpal);
	*ppColorSet = lpLogpal;
	hresult = NOERROR;

    errRtn:
	GlobalUnlock(m_hPres);
	return hresult;
    }

    return hresult;
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::IsBlank
//
//  Synopsis:   returns TRUE if the presentation is blank
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    TRUE/FALSE
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObject
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(CGenObject_IsBlank)
STDMETHODIMP_(BOOL) CGenObject::IsBlank(void)
{
	VDATEHEAP();

    return (m_dwSize ? FALSE : TRUE);
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::LoadHPRES  (private)
//
//  Synopsis:   Loads the presentation from the internal cache's stream
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    HANDLE (to the presentation)
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

INTERNAL_(HANDLE) CGenObject::LoadHPRES()
{
	VDATEHEAP();

	LPSTREAM pstm;

        pstm = m_pCacheNode->GetStm(TRUE /*fSeekToPresBits*/, STGM_READ);
	if (pstm)
	{
		LEVERIFY( SUCCEEDED(Load(pstm)));
		pstm->Release();
	}
	
	return m_hPres;
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::DiscardHPRES
//
//  Synopsis:   Deletes the object's presentation
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Derivation: IOlePresObj
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

STDMETHODIMP_(void) CGenObject::DiscardHPRES(void)
{
	VDATEHEAP();

	if (m_hPres)
	{
		LEVERIFY( NULL == GlobalFree(m_hPres));
		m_hPres = NULL;
	}
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetCopyOfHPRES (private)
//
//  Synopsis:   Returns a handle to a copy of the presentation data
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    HANDLE
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:  makes a copy of m_hPres if not NULL, otherwise loads it
//              from the stream (without setting m_hPres)
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

INTERNAL_(HANDLE) CGenObject::GetCopyOfHPRES()
{
	VDATEHEAP();

	HANDLE  hPres;
	
	// Make a copy if the presentation data is already loaded
	if (m_hPres)
	{
		return(UtDupGlobal(m_hPres, GMEM_MOVEABLE));
	}

	// Load the presentation data now and return the same handle.
	// No need to copy the data. If the caller wants the m_hPres to be
	// set he would call LoadHPRES() directly.

	hPres = LoadHPRES();
	m_hPres = NULL;
	return hPres;
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::GetBitmapData (private)
//
//  Synopsis:   Gets bitmap data from a dib
//
//  Effects:
//
//  Arguments:  [pformatetcIn]  -- the requested format
//              [pmedium]       -- where to put the data
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:  checks the parameters, then calls UtConvertDibtoBitmap
//              to get raw bitmap data from the device-independent bitmap
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------
#ifndef _MAC

#pragma SEG(CGenObject_GetBitmapData)
INTERNAL CGenObject::GetBitmapData
	(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
	VDATEHEAP();

	SCODE sc = E_OUTOFMEMORY;
	
	if (0 == (pformatetcIn->tymed & TYMED_GDI))
	{
		sc = DV_E_TYMED;
	}

	pmedium->pUnkForRelease = NULL;

        pmedium->hGlobal = UtConvertDibToBitmap(M_HPRES());

        // if pmedium->hGlobal is not NULL, then UtConvertDibToBitmap succeeded
        // so the tymed needs to be set appropriately, and the return value
        // changed to S_OK.
        if (NULL != pmedium->hGlobal)
	{
		pmedium->tymed = TYMED_GDI;
		sc = S_OK;
	}
	else
	{
		pmedium->tymed = TYMED_NULL;
	}
	
	return ResultFromScode(sc);
}


//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::SetBitmapData (private)
//
//  Synopsis:   Converts bitmap data to a dib and stores it in [this]
//              presenatation object
//
//  Effects:
//
//  Arguments:  [pformatetc]    -- the format of the data
//              [pmedium]       -- the data
//              [fRelease]      -- if TRUE, then pmedium will be free'd
//
//  Returns:    HRESULT
//
//  Algorithm:  calls UtConvertBitmapToDib and stores the result
//
//  History:    dd-mmm-yy Author    Comment
//              07-Jul-94 DavePl    Added CF_PALETTE support
//
//  Notes:      if [fRelease] == TRUE, then [pmedium] is released, even
//              if a dib could not be built
//
//--------------------------------------------------------------------------

INTERNAL CGenObject::SetBitmapData(LPFORMATETC   pformatetc,
				   STGMEDIUM   * pmedium,
				   BOOL          fRelease,
				   IDataObject * pDataObject)
{
	VDATEHEAP();

	HGLOBAL         hDib;
		
	if (pmedium->tymed != TYMED_GDI)
	{
		return ResultFromScode(DV_E_TYMED);
	}

	// If we have a data object and if we can get the palette from it,
	// use that to do the bitmap -> dib conversion.  Otherwise, just
	// pass a NULL palette along and the default one will be used

	STGMEDIUM   stgmPalette;
	FORMATETC   fetcPalette = {
				    CF_PALETTE,
				    NULL,
				    pformatetc->dwAspect,
				    DEF_LINDEX,
				    TYMED_GDI
				  };
	

	if (pDataObject && SUCCEEDED(pDataObject->GetData(&fetcPalette, &stgmPalette)))
	{
	    hDib = UtConvertBitmapToDib((HBITMAP)pmedium->hGlobal,
					(HPALETTE) stgmPalette.hGlobal);
	    ReleaseStgMedium(&stgmPalette);
	}
	else
	{
	    hDib = UtConvertBitmapToDib((HBITMAP)pmedium->hGlobal, NULL);
	}

	if (fRelease)
	{
		ReleaseStgMedium(pmedium);
	}

	if (!hDib)
	{
		return ResultFromScode(E_OUTOFMEMORY);
	}
	
	FORMATETC foretcTmp = *pformatetc;
	STGMEDIUM pmedTmp = *pmedium;
	
	foretcTmp.cfFormat = CF_DIB;
	foretcTmp.tymed = TYMED_HGLOBAL;
	
	pmedTmp.pUnkForRelease = NULL;
	pmedTmp.tymed = TYMED_HGLOBAL;
	pmedTmp.hGlobal = hDib;

	// Now that we have converted the bitmap data to DIB,
	// SetData _back_ on ourselves again with the DIB info
		
	return SetDataWDO(&foretcTmp, &pmedTmp, TRUE, NULL);
}

//+-------------------------------------------------------------------------
//
//  Member:     CGenObject::Dump, public (_DEBUG only)
//
//  Synopsis:   return a string containing the contents of the data members
//
//  Effects:
//
//  Arguments:  [ppszDump]      - an out pointer to a null terminated character array
//              [ulFlag]        - flag determining prefix of all newlines of the
//                                out character array (default is 0 - no prefix)
//              [nIndentLevel]  - will add a indent prefix after the other prefix
//                                for ALL newlines (including those with no prefix)
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:   [ppszDump]  - argument
//
//  Derivation:
//
//  Algorithm:  use dbgstream to create a string containing information on the
//              content of data structures
//
//  History:    dd-mmm-yy Author    Comment
//              01-Feb-95 t-ScottH  author
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef _DEBUG

HRESULT CGenObject::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel)
{
    int i;
    char *pszPrefix;
    char *pszDVASPECT;
    char *pszCLIPFORMAT;
    dbgstream dstrPrefix;
    dbgstream dstrDump(500);

    // determine prefix of newlines
    if ( ulFlag & DEB_VERBOSE )
    {
        dstrPrefix << this << " _VB ";
    }

    // determine indentation prefix for all newlines
    for (i = 0; i < nIndentLevel; i++)
    {
        dstrPrefix << DUMPTAB;
    }

    pszPrefix = dstrPrefix.str();

    // put data members in stream
    dstrDump << pszPrefix << "No. of References     = " << m_ulRefs         << endl;

    pszDVASPECT = DumpDVASPECTFlags(m_dwAspect);
    dstrDump << pszPrefix << "Aspect flags          = " << pszDVASPECT      << endl;
    CoTaskMemFree(pszDVASPECT);

    dstrDump << pszPrefix << "Size                  = " << m_dwSize         << endl;

    dstrDump << pszPrefix << "Width                 = " << m_lWidth         << endl;

    dstrDump << pszPrefix << "Height                = " << m_lHeight        << endl;

    dstrDump << pszPrefix << "Presentation Handle   = " << m_hPres          << endl;

    pszCLIPFORMAT = DumpCLIPFORMAT(m_cfFormat);
    dstrDump << pszPrefix << "Clip Format           = " << pszCLIPFORMAT    << endl;
    CoTaskMemFree(pszCLIPFORMAT);

    dstrDump << pszPrefix << "pCacheNode            = " << m_pCacheNode     << endl;

    // cleanup and provide pointer to character array
    *ppszDump = dstrDump.str();

    if (*ppszDump == NULL)
    {
        *ppszDump = UtDupStringA(szDumpErrorMessage);
    }

    CoTaskMemFree(pszPrefix);

    return NOERROR;
}

#endif // _DEBUG

//+-------------------------------------------------------------------------
//
//  Function:   DumpCGenObject, public (_DEBUG only)
//
//  Synopsis:   calls the CGenObject::Dump method, takes care of errors and
//              returns the zero terminated string
//
//  Effects:
//
//  Arguments:  [pGO]           - pointer to CGenObject
//              [ulFlag]        - flag determining prefix of all newlines of the
//                                out character array (default is 0 - no prefix)
//              [nIndentLevel]  - will add a indent prefix after the other prefix
//                                for ALL newlines (including those with no prefix)
//
//  Requires:
//
//  Returns:    character array of structure dump or error (null terminated)
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Feb-95 t-ScottH  author
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef _DEBUG

char *DumpCGenObject(CGenObject *pGO, ULONG ulFlag, int nIndentLevel)
{
    HRESULT hresult;
    char *pszDump;

    if (pGO == NULL)
    {
        return UtDupStringA(szDumpBadPtr);
    }

    hresult = pGO->Dump(&pszDump, ulFlag, nIndentLevel);

    if (hresult != NOERROR)
    {
        CoTaskMemFree(pszDump);

        return DumpHRESULT(hresult);
    }

    return pszDump;
}

#endif // _DEBUG

//+-------------------------------------------------------------------------
//
//  Function:   DibDraw
//
//  Synopsis:   Draws a device independent bitmap
//
//  Effects:
//
//  Arguments:  [hDib]          -- the bitmap
//              [hdc]           -- the device context
//              [lprc]          -- the bounding rectangle
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:  Sets the palette to the palette in the dib, sizes and draws
//              the dib to the bounding rectangle.  The original palette
//              is then restored
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//              07-Dec-93 alexgo    merged RC9 16bit changes.  The
//                                  error-handling code used to reset the
//                                  old palette and then RealizePalette.
//                                  The call to RealizePalette was removed
//              11-May-94 davepl    Added support for BITMAPCOREINFO dibs
//              17-Jul-94 davepl    Added 12, 32 bpp support
//
//  Notes:
//
//--------------------------------------------------------------------------

INTERNAL DibDraw (HANDLE hDib, HDC hdc, LPCRECTL lprc)
{
	VDATEHEAP();

	HRESULT                 error       = ResultFromScode(E_DRAW);
	BYTE FAR *              lpDib;
	HANDLE                  hPalette    = NULL;
	HPALETTE                hLogPalette = NULL,
				hOldPalette = NULL;
	LPLOGPALETTE            lpLogPalette;
	WORD                    wPalSize;
	DWORD                   cbHeaderSize;
	BOOL                    fNeedPalette = FALSE;
	WORD                    iHeight;
	WORD                    iWidth;
	int                     iOffBits;
	BITMAPINFO *            pbi         = NULL;
	BOOL                    fWeAllocd   = FALSE;

	if (NULL == hDib)
	{
		return ResultFromScode(OLE_E_BLANK);
	}

	Assert(lprc);

	if (NULL == (lpDib = (BYTE FAR *) GlobalLock (hDib)))
	{
		return  ResultFromScode(E_OUTOFMEMORY);
	}

	// The bitmap header could be BITMAPINFOHEADER or
	// BITMAPCOREHEADER.  Set our cbHeaderSize flag
	// based on the header type.  We can then calculate the
	// palette size and the offset to the raw data bits.  If
	// we don't recognize either one of the structures here,
	// we bail; the data is likely corrupt.

	// Just a thought here: could be dangerous if the struct
	// is not LONG aligned and this is run on an Alpha.  As far
	// as I've been able to find out, they always are long
	// aligned

        cbHeaderSize = *((ULONG *) lpDib);
        LEWARN( cbHeaderSize > 500, "Struct size > 500, likely invalid!");
	
	if (cbHeaderSize == sizeof(BITMAPINFOHEADER))
	{
                // Note: this assignment can overflow the WORD wPalSize,
                // but the UtPaletteSize function asserts against this

		wPalSize = (WORD) UtPaletteSize((LPBITMAPINFOHEADER)lpDib);

		iWidth   = (WORD) ((LPBITMAPINFOHEADER)lpDib)->biWidth;
		iHeight  = (WORD) ((LPBITMAPINFOHEADER)lpDib)->biHeight;
		pbi      = (LPBITMAPINFO) lpDib;
		iOffBits = wPalSize + sizeof(BITMAPINFOHEADER);
	}
	else if (cbHeaderSize == sizeof(BITMAPCOREHEADER))
	{

// Since the clipboard itself does not support COREINFO
// bitmaps, we will not support them in the presentation
// cache.  When (if) windows adds complete support for
// these, the code is here and ready.

#ifndef CACHE_SUPPORT_COREINFO
		error = DV_E_TYMED;
		goto errRtn;
#else

		// Special case 32 bpp bitmaps

		// If we have a palette, we need to calculate its size and
		// allocate enough memory for the palette entries (remember
		// we get one entry for free with the BITMAPINFO struct, so
		// less one).  If we don't have a palette, we only need to
		// allocate enough for the BITMAPINFO struct itself.

		// Bitmaps with more than 64K colors lack a palette; they
		// use direct RGB entries in the pixels

		if ((((LPBITMAPCOREHEADER)lpDib)->bcBitCount) > 16)
		{
			wPalSize = 0;
			pbi = (BITMAPINFO *) PrivMemAlloc(sizeof(BITMAPINFO));
		}
		else
		{
			wPalSize = sizeof(RGBQUAD) *
				   (1 << (((LPBITMAPCOREHEADER)lpDib)->bcBitCount));
			pbi = (BITMAPINFO *) PrivMemAlloc(sizeof(BITMAPINFO)
				    + wPalSize - sizeof(RGBQUAD));
		}
		
		if (NULL == pbi)
		{
			return ResultFromScode(E_OUTOFMEMORY);
		}
		else
		{
			fWeAllocd = TRUE;
		}

			
		// Grab the width and height
		iWidth   = ((LPBITMAPCOREHEADER)lpDib)->bcWidth;
		iHeight  = ((LPBITMAPCOREHEADER)lpDib)->bcHeight;
		
		// Clear all the fields.  Don't worry about color table, as if
		// it exists we will set the entries explicitly.

		memset(pbi, 0, sizeof(BITMAPINFOHEADER));

		// Transfer what fields we do have from the COREINFO

		pbi->bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
		pbi->bmiHeader.biWidth    = iWidth;
		pbi->bmiHeader.biHeight   = iHeight;
		pbi->bmiHeader.biPlanes   = 1;
		pbi->bmiHeader.biBitCount = ((LPBITMAPCOREHEADER)lpDib)->bcBitCount;

		// Set up the color palette, if required.
		// Note that we must translate from RGBTRIPLE entries to
		// RGBQUAD.

		for (WORD c = 0; c < wPalSize / sizeof(RGBQUAD); c++)
		{
			pbi->bmiColors[c].rgbRed   = ((BITMAPCOREINFO *)lpDib)->bmciColors[c].rgbtRed;
			pbi->bmiColors[c].rgbBlue  = ((BITMAPCOREINFO *)lpDib)->bmciColors[c].rgbtBlue;
			pbi->bmiColors[c].rgbGreen = ((BITMAPCOREINFO *)lpDib)->bmciColors[c].rgbtGreen;
			pbi->bmiColors[c].rgbReserved = 0;
		}
	
		iOffBits = wPalSize + sizeof(BITMAPCOREHEADER);
#endif
	}
	else
	{
		error = E_FAIL;
		goto errRtn;
	}
		
	// if color info exists, create a palette from the data and select it
	// images with < 16 bpp do not have a palette from which we can create
	// a logical palette

	fNeedPalette = ((LPBITMAPINFOHEADER)lpDib)->biBitCount < 16;
	if (wPalSize && fNeedPalette)
	{
                hLogPalette = (HPALETTE)DibMakeLogPalette(lpDib + cbHeaderSize,
					                  wPalSize,
					                  &lpLogPalette);		
		if (NULL == hLogPalette)
		{
			error = ResultFromScode(E_OUTOFMEMORY);
			goto errRtn;
		}


		if (NULL == (hPalette = CreatePalette (lpLogPalette)))
		{
			goto errRtn;
		}

		// we're done with lpLogPalette now, so unlock it
		// (DibMakeLogPalette got the pointer via a GlobalLock)

		GlobalUnlock(hLogPalette);
		
		// select as a background palette
		hOldPalette = SelectPalette (hdc, (HPALETTE)hPalette, TRUE);
		if (NULL == hOldPalette)
		{
			goto errRtn;
		}

		LEVERIFY( 0 < RealizePalette(hdc) );
	}

	
	// size the dib to fit our drawing rectangle and draw it

	if (!StretchDIBits( hdc,                        // HDC
			    lprc->left,                 // XDest
			    lprc->top,                  // YDest
			    lprc->right - lprc->left,   // nDestWidth
			    lprc->bottom - lprc->top,   // nDestHeight
			    0,                          // XSrc
			    0,                          // YSrc
			    iWidth,                     // nSrcWidth
			    iHeight,                    // nSrcHeight
			    lpDib + iOffBits,           // lpBits
			    pbi,                        // lpBitsInfo
			    DIB_RGB_COLORS,             // iUsage
			    SRCCOPY                     // dwRop
			   )
	   )
	{
		error = ResultFromScode(E_DRAW);
	}
	else
	{
		error = NOERROR;
	}

errRtn:

	// We only want to free the header if it is was one which we allocated,
	// which can only happen when we were give a core header type in the
	// first place
		
	if (fWeAllocd)
	{
		PrivMemFree(pbi);
	}

	if (lpDib)
	{
		GlobalUnlock (hDib);
	}
	
	// if color palette exists do the following
	if (fNeedPalette)
	{
		hOldPalette = (HPALETTE)(OleIsDcMeta (hdc) ?
				GetStockObject(DEFAULT_PALETTE)
				: (HPALETTE)hOldPalette);
				
		if (hOldPalette)
		{
			LEVERIFY( SelectPalette (hdc, hOldPalette, TRUE) );
			// Do we need to realize the palette? [Probably not]
		}

		if (hPalette)
		{
			LEVERIFY( DeleteObject (hPalette) );
		}

		if (hLogPalette)
		{
			LEVERIFY( NULL == GlobalFree (hLogPalette) );
		}
	}

	return error;
}


//+-------------------------------------------------------------------------
//
//  Function:   DibMakeLogPalette
//
//  Synopsis:   Makes a logical palette from a byte array of color info
//
//  Effects:
//
//  Arguments:  [lpColorData]           -- the color data
//              [wDataSize]             -- size of the data
//              [lplpLogPalette]        -- where to put a pointer to the
//
//  Requires:
//
//  Returns:    HANDLE to the logical palette (must be global unlock'ed
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:      The caller MUST call GlobalUnlock on the returned handle
//              to avoid a memory leak  (*lplpLogPalette is the result
//              of a global lock on the handle)
//
//--------------------------------------------------------------------------

#pragma SEG(DibMakeLogPalette)
INTERNAL_(HANDLE) DibMakeLogPalette(
	BYTE FAR * lpColorData, WORD wDataSize,
	LPLOGPALETTE FAR *lplpLogPalette)
{
	VDATEHEAP();

	HANDLE          hLogPalette=NULL;
	LPLOGPALETTE    lpLogPalette;
	DWORD           dwLogPalSize = wDataSize +  2 * sizeof(WORD);

	if (NULL == (hLogPalette = GlobalAlloc(GMEM_MOVEABLE, dwLogPalSize)))
	{
		return NULL;
	}

	if (NULL == (lpLogPalette = (LPLOGPALETTE) GlobalLock (hLogPalette)))
	{
		LEVERIFY( NULL == GlobalFree (hLogPalette));
		return NULL;
	}

	*lplpLogPalette = lpLogPalette;
	DibFillPaletteEntries(lpColorData, wDataSize, lpLogPalette);
	return hLogPalette;
}

//+-------------------------------------------------------------------------
//
//  Function:   DibFillPaletteEntries
//
//  Synopsis:   Fills the logical palette with the color info in [lpColorData]
//
//  Effects:
//
//  Arguments:  [lpColorData]   -- the color info
//              [wDataSize]     -- the size of the color info
//              [lpLogPalette]  -- the logical palette
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              23-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

INTERNAL_(void) DibFillPaletteEntries(
	BYTE FAR * lpColorData, WORD wDataSize, LPLOGPALETTE lpLogPalette)
{
	VDATEHEAP();

	LPPALETTEENTRY  lpPE;
	RGBQUAD FAR *   lpQuad;

	lpLogPalette->palVersion = 0x300;
	lpLogPalette->palNumEntries = wDataSize / sizeof(PALETTEENTRY);

	/* now convert RGBQUAD to PALETTEENTRY as we copy color info */
	for (lpQuad = (RGBQUAD far *)lpColorData,
		lpPE   = (LPPALETTEENTRY)lpLogPalette->palPalEntry,
		wDataSize /= sizeof(RGBQUAD);
		wDataSize--;
		++lpQuad,++lpPE)
	{
		lpPE->peFlags           = NULL;
		lpPE->peRed             = lpQuad->rgbRed;
		lpPE->peBlue            = lpQuad->rgbBlue;
		lpPE->peGreen           = lpQuad->rgbGreen;
	}
}

#endif