|
|
#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; }
|