WindowsXP-SP1/shell/comctl32/v6/imagestream.cpp

706 lines
20 KiB
C++

#include "ctlspriv.h"
#include "image.h"
HRESULT Stream_WriteBitmap(LPSTREAM pstm, HBITMAP hbm, int cBitsPerPixel);
HRESULT Stream_ReadBitmap(LPSTREAM pstm, int iExpectedBitdepth, HBITMAP* hbmp, PVOID* pvBits);
HRESULT CImageList::_Read(ILFILEHEADER *pilfh, HBITMAP hbmImageI, PVOID pvBits, HBITMAP hbmMaskI)
{
int i;
HRESULT hr = Initialize(pilfh->cx, pilfh->cy, pilfh->flags, 1, pilfh->cGrow);
if (SUCCEEDED(hr))
{
// select into DC's before deleting existing bitmaps
// patch in the bitmaps we loaded
SelectObject(_hdcImage, hbmImageI);
DeleteObject(_hbmImage);
_hbmImage = hbmImageI;
_pargbImage = (RGBQUAD*)pvBits;
_clrBlend = CLR_NONE;
// Same for the mask (if necessary)
if (_hdcMask)
{
SelectObject(_hdcMask, hbmMaskI);
DeleteObject(_hbmMask);
_hbmMask = hbmMaskI;
}
_cAlloc = pilfh->cAlloc;
//
// Call ImageList_SetBkColor with 0 in piml->_cImage to avoid
// calling expensive ImageList__ResetBkColor
//
_cImage = 0;
_SetBkColor(pilfh->clrBk);
_cImage = pilfh->cImage;
for (i=0; i<NUM_OVERLAY_IMAGES; i++)
_SetOverlayImage(pilfh->aOverlayIndexes[i], i+1);
}
else
{
DeleteObject(hbmImageI);
DeleteObject(hbmMaskI);
}
return hr;
}
// Object: Create a 1 strip bitmap from a 4 strip bitmap.
BOOL CreateUplevelBitmap(int cx, int cy, int cCount, BOOL f32bpp, BOOL fMono, HBITMAP* phbmpDest, PVOID* ppvBits)
{
BOOL fRet = FALSE;
// src contains a 4 x cx bitmap
HDC hdcSrc = CreateCompatibleDC(NULL);
if (hdcSrc)
{
HBITMAP hbmpOldSrc = (HBITMAP)SelectObject(hdcSrc, *phbmpDest);
HDC hdcDest = CreateCompatibleDC(NULL);
if (hdcDest)
{
HBITMAP hbmpDest;
if (fMono)
{
hbmpDest = CreateMonoBitmap(cx, cy * cCount);
}
else
{
if (f32bpp)
{
hbmpDest = CreateDIB(hdcSrc, cx, cy * cCount, (RGBQUAD**)ppvBits);
}
else
{
hbmpDest = CreateCompatibleBitmap(hdcSrc, cx, cy * cCount);
if (hbmpDest)
{
BITMAP bm;
GetObject(hbmpDest, sizeof(bm), &bm);
*ppvBits = bm.bmBits;
}
}
}
if (hbmpDest)
{
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hbmpDest);
for (int i = 0; i < cCount; i++)
{
int xSrc = cx * (i % 4);
int ySrc = cy * (i / 4);
int yDst = cy * i;
BitBlt(hdcDest, 0, yDst, cx, cy, hdcSrc, xSrc, ySrc, SRCCOPY);
}
fRet = TRUE;
SelectObject(hdcDest, hbmpOld);
DeleteObject(*phbmpDest);
*phbmpDest = hbmpDest;
}
DeleteDC(hdcDest);
}
SelectObject(hdcSrc, hbmpOldSrc);
DeleteDC(hdcSrc);
}
return fRet;
}
STDMETHODIMP CImageList::LoadEx(DWORD dwFlags, IStream* pstm)
{
if (pstm == NULL)
return E_INVALIDARG;
HRESULT hr = InitGlobals();
if (SUCCEEDED(hr))
{
ENTERCRITICAL;
ILFILEHEADER ilfh = {0};
HBITMAP hbmImageI = NULL;
HBITMAP hbmMaskI = NULL;
HBITMAP hbmMirroredImage;
HBITMAP hbmMirroredMask;
BOOL bMirroredIL = FALSE;
// fist read in the old struct
hr = pstm->Read(&ilfh, ILFILEHEADER_SIZE0, NULL);
if (SUCCEEDED(hr) && (ilfh.magic != IMAGELIST_MAGIC ||
(ilfh.version != IMAGELIST_VER6 && ilfh.version != IMAGELIST_VER0)))
{
hr = E_FAIL;
}
if (ilfh.version == IMAGELIST_VER0)
dwFlags |= ILP_DOWNLEVEL;
if (ilfh.version == IMAGELIST_VER6)
dwFlags &= ~ILP_DOWNLEVEL; // Uplevel, Don't run in compat mode.
if (SUCCEEDED(hr))
{
PVOID pvBits, pvBitsMirror;
hbmMaskI = NULL;
hbmMirroredMask = NULL;
hr = Stream_ReadBitmap(pstm, (ilfh.flags&ILC_COLORMASK), &hbmImageI, &pvBits);
if (SUCCEEDED(hr))
{
if (dwFlags & ILP_DOWNLEVEL)
CreateUplevelBitmap(ilfh.cx, ilfh.cy, ilfh.cAlloc, (ilfh.flags & ILC_COLOR32), FALSE, &hbmImageI, &pvBits);
if (ilfh.flags & ILC_MASK)
{
hr = Stream_ReadBitmap(pstm, 1, &hbmMaskI, NULL);
if (FAILED(hr))
{
DeleteBitmap(hbmImageI);
hbmImageI = NULL;
}
else if (dwFlags & ILP_DOWNLEVEL)
{
CreateUplevelBitmap(ilfh.cx, ilfh.cy, ilfh.cAlloc, FALSE, TRUE, &hbmMaskI, NULL);
}
}
if (SUCCEEDED(hr))
{
// Read in the rest of the struct, new overlay stuff.
if (ilfh.flags & ILC_MOREOVERLAY)
{
hr = pstm->Read((LPBYTE)&ilfh + ILFILEHEADER_SIZE0, sizeof(ilfh) - ILFILEHEADER_SIZE0, NULL);
if (SUCCEEDED(hr))
{
ilfh.flags &= ~ILC_MOREOVERLAY;
}
}
else
{
// Properly initialize the new stuff since we are not reading it in...
for (int i = NUM_OVERLAY_IMAGES_0; i < NUM_OVERLAY_IMAGES; i++)
ilfh.aOverlayIndexes[i] = -1;
}
}
if (SUCCEEDED(hr))
{
if (ilfh.flags & ILC_MIRROR)
{
ilfh.flags &= ~ILC_MIRROR;
bMirroredIL = TRUE;
hr = Stream_ReadBitmap(pstm, (ilfh.flags&ILC_COLORMASK), &hbmMirroredImage, &pvBitsMirror);
if (SUCCEEDED(hr))
{
if (dwFlags & ILP_DOWNLEVEL)
CreateUplevelBitmap(ilfh.cx, ilfh.cy, ilfh.cAlloc, (ilfh.flags & ILC_COLOR32), FALSE, &hbmMirroredImage, &pvBitsMirror);
if (ilfh.flags & ILC_MASK)
{
hr = Stream_ReadBitmap(pstm, 1, &hbmMirroredMask, NULL);
if (FAILED(hr))
{
DeleteBitmap(hbmMirroredImage);
hbmMirroredImage = NULL;
}
else if (dwFlags & ILP_DOWNLEVEL)
{
CreateUplevelBitmap(ilfh.cx, ilfh.cy, ilfh.cAlloc, FALSE, TRUE, &hbmMirroredMask, NULL);
}
}
}
}
if (SUCCEEDED(hr))
{
hr = _Read(&ilfh, hbmImageI, pvBits, hbmMaskI);
if(SUCCEEDED(hr))
{
_ScanForAlpha();
if (bMirroredIL)
{
hr = E_OUTOFMEMORY;
_pimlMirror = new CImageList();
if (_pimlMirror)
{
hr = _pimlMirror->_Read(&ilfh, hbmMirroredImage, pvBitsMirror, hbmMirroredMask);
if (SUCCEEDED(hr))
_pimlMirror->_ScanForAlpha();
}
}
}
}
if (FAILED(hr))
{
if (hbmImageI)
DeleteBitmap(hbmImageI);
if (hbmMaskI)
DeleteBitmap(hbmMaskI);
}
}
}
}
LEAVECRITICAL;
}
return hr;
}
BOOL CImageList::_MoreOverlaysUsed()
{
int i;
for (i = NUM_OVERLAY_IMAGES_0; i < NUM_OVERLAY_IMAGES; i++)
if (_aOverlayIndexes[i] != -1)
return TRUE;
return FALSE;
}
// Object: Create a 4 strip bitmap from a single strip bitmap.
BOOL CreateDownlevelBitmap(int cx, int cy, int cCount, HDC hdc, HBITMAP hbmp, HBITMAP* hbmpDest)
{
BOOL fRet = FALSE;
HDC hdcDest = CreateCompatibleDC(hdc);
if (hdcDest)
{
*hbmpDest = CreateCompatibleBitmap(hdc, 4 * cx, cy * ((cCount / 4) + 1));
if (*hbmpDest)
{
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, *hbmpDest);
for (int i = 0; i < cCount; i++)
{
int xDest = cx * (i % 4);
int yDest = cy * (i / 4);
int ySrc = cy * i;
BitBlt(hdcDest, xDest, yDest, cx, cy, hdc, 0, ySrc, SRCCOPY);
}
fRet = TRUE;
SelectObject(hdcDest, hbmpOld);
}
DeleteDC(hdcDest);
}
return fRet;
}
STDMETHODIMP CImageList::SaveEx(DWORD dwFlags, IStream* pstm)
{
int i;
ILFILEHEADER ilfh;
HRESULT hr = S_OK;
HBITMAP hbmpMask = _hbmMask;
HBITMAP hbmpImage = _hbmImage;
if (pstm == NULL)
return E_INVALIDARG;
ilfh.magic = IMAGELIST_MAGIC;
if (dwFlags == ILP_NORMAL)
ilfh.version = IMAGELIST_VER6;
else
ilfh.version = IMAGELIST_VER0;
ilfh.cImage = (SHORT) _cImage;
ilfh.cAlloc = (SHORT) _cAlloc;
ilfh.cGrow = (SHORT) _cGrow;
ilfh.cx = (SHORT) _cx;
ilfh.cy = (SHORT) _cy;
ilfh.clrBk = _clrBk;
ilfh.flags = (SHORT) _flags;
if (dwFlags == ILP_DOWNLEVEL)
{
CreateDownlevelBitmap(_cx, _cy, _cImage, _hdcImage, _hbmImage, &hbmpImage);
if (_hbmMask)
CreateDownlevelBitmap(_cx, _cy, _cImage, _hdcMask, _hbmMask, &hbmpMask);
}
//
// Store mirror flags
//
if (_pimlMirror)
ilfh.flags |= ILC_MIRROR;
if (_MoreOverlaysUsed())
ilfh.flags |= ILC_MOREOVERLAY;
for (i=0; i < NUM_OVERLAY_IMAGES; i++)
ilfh.aOverlayIndexes[i] = (SHORT) _aOverlayIndexes[i];
hr = pstm->Write(&ilfh, ILFILEHEADER_SIZE0, NULL);
if (SUCCEEDED(hr))
{
hr = Stream_WriteBitmap(pstm, hbmpImage, 0);
if (SUCCEEDED(hr))
{
if (_hdcMask)
{
hr = Stream_WriteBitmap(pstm, hbmpMask, 1);
}
if (SUCCEEDED(hr))
{
if (ilfh.flags & ILC_MOREOVERLAY)
hr = pstm->Write((LPBYTE)&ilfh + ILFILEHEADER_SIZE0, sizeof(ilfh) - ILFILEHEADER_SIZE0, NULL);
if (_pimlMirror)
{
HBITMAP hbmpImageM = _pimlMirror->_hbmImage;
HBITMAP hbmpMaskM = _pimlMirror->_hbmMask;
if (dwFlags == ILP_DOWNLEVEL)
{
CreateDownlevelBitmap(_cx, _cy, _pimlMirror->_cImage, _pimlMirror->_hdcImage, _pimlMirror->_hbmImage, &hbmpImageM);
if (_hbmMask)
CreateDownlevelBitmap(_cx, _cy, _pimlMirror->_cImage, _pimlMirror->_hdcMask, _pimlMirror->_hbmMask, &hbmpMaskM);
}
// Don't call pidlMirror's Save, because of the header difference.
hr = Stream_WriteBitmap(pstm, hbmpImageM, 0);
if (_pimlMirror->_hdcMask)
{
hr = Stream_WriteBitmap(pstm, hbmpMaskM, 1);
}
if (hbmpMaskM && (hbmpMaskM != _pimlMirror->_hbmMask))
DeleteObject(hbmpMaskM);
if (hbmpImageM && (hbmpImageM != _pimlMirror->_hbmImage))
DeleteObject(hbmpImageM);
}
}
}
}
if (hbmpMask && (hbmpMask != _hbmMask))
DeleteObject(hbmpMask);
if (hbmpImage && (hbmpImage != _hbmImage))
DeleteObject(hbmpImage);
return hr;
}
STDMETHODIMP CImageList::Load(IStream* pstm)
{
return LoadEx(ILP_NORMAL, pstm);
}
STDMETHODIMP CImageList::Save(IStream* pstm, int fClearDirty)
{
return SaveEx(ILP_NORMAL, pstm);
}
HRESULT Stream_WriteBitmap(LPSTREAM pstm, HBITMAP hbm, int cBitsPerPixel)
{
BOOL fSuccess;
BITMAP bm;
int cx, cy;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
BITMAPINFOHEADER * pbi;
BYTE * pbuf;
HDC hdc;
UINT cbColorTable;
int cLines;
int cLinesWritten;
HRESULT hr = E_INVALIDARG;
ASSERT(pstm);
fSuccess = FALSE;
hdc = NULL;
pbi = NULL;
pbuf = NULL;
if (GetObject(hbm, sizeof(bm), &bm) != sizeof(bm))
goto Error;
hdc = GetDC(HWND_DESKTOP);
cx = bm.bmWidth;
cy = bm.bmHeight;
if (cBitsPerPixel == 0)
cBitsPerPixel = bm.bmPlanes * bm.bmBitsPixel;
if (cBitsPerPixel <= 8)
cbColorTable = (1 << cBitsPerPixel) * sizeof(RGBQUAD);
else
cbColorTable = 0;
bi.biSize = sizeof(bi);
bi.biWidth = cx;
bi.biHeight = cy;
bi.biPlanes = 1;
bi.biBitCount = (WORD) cBitsPerPixel;
bi.biCompression = BI_RGB; // RLE not supported!
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
bf.bfType = BFTYPE_BITMAP;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + cbColorTable;
bf.bfSize = bf.bfOffBits + bi.biSizeImage;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
hr = E_OUTOFMEMORY;
pbi = (BITMAPINFOHEADER *)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + cbColorTable);
if (!pbi)
goto Error;
// Get the color table and fill in the rest of *pbi
//
*pbi = bi;
if (GetDIBits(hdc, hbm, 0, cy, NULL, (BITMAPINFO *)pbi, DIB_RGB_COLORS) == 0)
goto Error;
if (cBitsPerPixel == 1)
{
((DWORD *)(pbi+1))[0] = CLR_BLACK;
((DWORD *)(pbi+1))[1] = CLR_WHITE;
}
pbi->biSizeImage = WIDTHBYTES(cx, cBitsPerPixel) * cy;
hr = pstm->Write(&bf, sizeof(bf), NULL);
if (FAILED(hr))
goto Error;
hr = pstm->Write(pbi, sizeof(bi) + cbColorTable, NULL);
if (FAILED(hr))
goto Error;
//
// if we have a DIBSection just write the bits out
//
if (bm.bmBits != NULL)
{
hr = pstm->Write(bm.bmBits, pbi->biSizeImage, NULL);
if (FAILED(hr))
goto Error;
goto Done;
}
// Calculate number of horizontal lines that'll fit into our buffer...
//
cLines = CBDIBBUF / WIDTHBYTES(cx, cBitsPerPixel);
hr = E_OUTOFMEMORY;
pbuf = (PBYTE)LocalAlloc(LPTR, CBDIBBUF);
if (!pbuf)
goto Error;
for (cLinesWritten = 0; cLinesWritten < cy; cLinesWritten += cLines)
{
hr = E_OUTOFMEMORY;
if (cLines > cy - cLinesWritten)
cLines = cy - cLinesWritten;
if (GetDIBits(hdc, hbm, cLinesWritten, cLines,
pbuf, (BITMAPINFO *)pbi, DIB_RGB_COLORS) == 0)
goto Error;
hr = pstm->Write(pbuf, WIDTHBYTES(cx, cBitsPerPixel) * cLines, NULL);
if (FAILED(hr))
goto Error;
}
Done:
hr = S_OK;
Error:
if (hdc)
ReleaseDC(HWND_DESKTOP, hdc);
if (pbi)
LocalFree((HLOCAL)pbi);
if (pbuf)
LocalFree((HLOCAL)pbuf);
return hr;
}
HRESULT Stream_ReadBitmap(LPSTREAM pstm, int iExpectedBitdepth, HBITMAP* phbmp, PVOID* ppvBits)
{
HDC hdc = NULL;
HBITMAP hbm = NULL;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
BITMAPINFOHEADER * pbi = NULL;
BYTE * pbuf=NULL;
int cBitsPerPixel;
UINT cbColorTable;
int cx, cy;
int cLines, cLinesRead;
HRESULT hr = pstm->Read(&bf, sizeof(bf), NULL);
if (FAILED(hr))
goto Error;
hr = E_INVALIDARG;
if (bf.bfType != BFTYPE_BITMAP)
goto Error;
hr = pstm->Read(&bi, sizeof(bi), NULL);
if (FAILED(hr))
goto Error;
hr = E_INVALIDARG;
if (bi.biSize != sizeof(bi))
goto Error;
cx = (int)bi.biWidth;
cy = (int)bi.biHeight;
cBitsPerPixel = (int)bi.biBitCount * (int)bi.biPlanes;
if (iExpectedBitdepth != ILC_COLORDDB &&
cBitsPerPixel != iExpectedBitdepth)
{
goto Error;
}
if (cBitsPerPixel <= 8)
cbColorTable = (1 << cBitsPerPixel) * sizeof(RGBQUAD);
else
cbColorTable = 0;
hr = E_OUTOFMEMORY;
pbi = (BITMAPINFOHEADER*)LocalAlloc(LPTR, sizeof(bi) + cbColorTable);
if (!pbi)
goto Error;
*pbi = bi;
pbi->biSizeImage = WIDTHBYTES(cx, cBitsPerPixel) * cy;
if (cbColorTable)
{
hr = pstm->Read(pbi + 1, cbColorTable, NULL);
if (FAILED(hr))
goto Error;
}
hdc = GetDC(HWND_DESKTOP);
//
// see if we can make a DIBSection
//
if ((cBitsPerPixel > 1) && (iExpectedBitdepth != ILC_COLORDDB))
{
//
// create DIBSection and read the bits directly into it!
//
hr = E_OUTOFMEMORY;
hbm = CreateDIBSection(hdc, (LPBITMAPINFO)pbi, DIB_RGB_COLORS, (void**)&pbuf, NULL, 0);
if (hbm == NULL)
goto Error;
hr = pstm->Read(pbuf, pbi->biSizeImage, NULL);
if (FAILED(hr))
goto Error;
if (ppvBits)
*ppvBits = pbuf;
pbuf = NULL; // dont free this
goto Done;
}
//
// cant make a DIBSection make a mono or color bitmap.
//
else if (cBitsPerPixel > 1)
hbm = CreateColorBitmap(cx, cy);
else
hbm = CreateMonoBitmap(cx, cy);
hr = E_OUTOFMEMORY;
if (!hbm)
goto Error;
// Calculate number of horizontal lines that'll fit into our buffer...
//
cLines = CBDIBBUF / WIDTHBYTES(cx, cBitsPerPixel);
hr = E_OUTOFMEMORY;
pbuf = (PBYTE)LocalAlloc(LPTR, CBDIBBUF);
if (!pbuf)
goto Error;
for (cLinesRead = 0; cLinesRead < cy; cLinesRead += cLines)
{
if (cLines > cy - cLinesRead)
cLines = cy - cLinesRead;
hr = pstm->Read(pbuf, WIDTHBYTES(cx, cBitsPerPixel) * cLines, NULL);
if (FAILED(hr))
goto Error;
hr = E_OUTOFMEMORY;
if (!SetDIBits(hdc, hbm, cLinesRead, cLines,
pbuf, (BITMAPINFO *)pbi, DIB_RGB_COLORS))
{
goto Error;
}
}
Done:
hr = S_OK;
Error:
if (hdc)
ReleaseDC(HWND_DESKTOP, hdc);
if (pbi)
LocalFree((HLOCAL)pbi);
if (pbuf)
LocalFree((HLOCAL)pbuf);
if (FAILED(hr) && hbm)
{
DeleteBitmap(hbm);
hbm = NULL;
}
*phbmp = hbm;
return hr;
}