#include "ctlspriv.h" #include "image.h" #include "math.h" #ifndef AC_MIRRORBITMAP #define AC_MIRRORBITMAP 0 // BUGBUG: Remove me #endif void ImageList_DeleteDragBitmaps(); BOOL ImageList_SetDragImage(HIMAGELIST piml, int i, int dxHotspot, int dyHotspot); HDC g_hdcSrc = NULL; HBITMAP g_hbmSrc = NULL; HBITMAP g_hbmDcDeselect = NULL; HDC g_hdcDst = NULL; HBITMAP g_hbmDst = NULL; int g_iILRefCount = 0; HRESULT WINAPI HIMAGELIST_QueryInterface(HIMAGELIST himl, REFIID riid, void** ppv) { *ppv = NULL; if (himl) { // First Convert the HIMAGELIST to an IUnknown. IUnknown* punk = reinterpret_cast(himl); // Now, we need to validate the object. CImageListBase contains the goo needed to figure out if this // is a valid imagelist. CImageListBase* pval = FindImageListBase(punk); // Now we call some private member. if (pval->IsValid()) { // If it's valid then we can QI safely. return punk->QueryInterface(riid, ppv); } } return E_POINTER; } HRESULT WimpyDrawEx(IImageList* pux, int i, HDC hdcDst, int x, int y, int cx, int cy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) { IMAGELISTDRAWPARAMS imldp = {0}; imldp.cbSize = sizeof(imldp); imldp.himl = reinterpret_cast(pux); imldp.i = i; imldp.hdcDst = hdcDst; imldp.x = x; imldp.y = y; imldp.cx = cx; imldp.cy = cy; imldp.rgbBk = rgbBk; imldp.rgbFg = rgbFg; imldp.fStyle = fStyle; imldp.dwRop = SRCCOPY; return pux->Draw(&imldp); } HRESULT WimpyDraw(IImageList* pux, int i, HDC hdcDst, int x, int y, UINT fStyle) { IMAGELISTDRAWPARAMS imldp = {0}; imldp.cbSize = sizeof(imldp); imldp.himl = reinterpret_cast(pux); imldp.i = i; imldp.hdcDst = hdcDst; imldp.x = x; imldp.y = y; imldp.rgbBk = CLR_DEFAULT; imldp.rgbFg = CLR_DEFAULT; imldp.fStyle = fStyle; imldp.dwRop = SRCCOPY; return pux->Draw(&imldp); } CImageList::CImageList() : _cRef(1) { } CImageList::~CImageList() { if (_pimlMirror) { _pimlMirror->Release(); } _Destroy(); } DWORD CImageList::_GetItemFlags(int i) { DWORD dw = 0; // NOTE: Currently we only add the flags in 32bit mode. If needed, you have // to modify ::Load in order to add items during a load. I'm just lazy if ((_flags & ILC_COLORMASK) == ILC_COLOR32) DSA_GetItem(_dsaFlags, i, &dw); return dw; } void CImageList::SetItemFlags(int i, DWORD dwFlags) { if (_dsaFlags) DSA_SetItem(_dsaFlags, i, &dwFlags); } HRESULT CImageList::Initialize(int cxI, int cyI, UINT flagsI, int cInitialI, int cGrowI) { HRESULT hr = E_OUTOFMEMORY; if (cGrowI < 4) { cGrowI = 4; } else { // round up by 4's cGrowI = (cGrowI + 3) & ~3; } _cStrip = 1; _cGrow = cGrowI; _cx = cxI; _cy = cyI; _clrBlend = CLR_NONE; _clrBk = CLR_NONE; _hbrBk = (HBRUSH)GetStockObject(BLACK_BRUSH); _fSolidBk = TRUE; _flags = flagsI; _pimlMirror = NULL; // // Initialize the overlay indexes to -1 since 0 is a valid index. // for (int i = 0; i < NUM_OVERLAY_IMAGES; i++) { _aOverlayIndexes[i] = -1; } _hdcImage = CreateCompatibleDC(NULL); if (_hdcImage) { hr = S_OK; if (_flags & ILC_MASK) { _hdcMask = CreateCompatibleDC(NULL); if (!_hdcMask) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { hr = _ReAllocBitmaps(cInitialI + 1); if (FAILED(hr)) { hr = _ReAllocBitmaps(2); } } } // Don't do this if we are already initialized, we just want to pass new information.... if (!_fInitialized) g_iILRefCount++; _fInitialized = TRUE; return hr; } HRESULT CImageList::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CImageList, IImageListPriv), QITABENT(CImageList, IImageList), QITABENT(CImageList, IImageListPersistStream), QITABENT(CImageList, IPersistStream), QITABENTMULTI(CImageList, IPersist, IPersistStream), { 0 }, }; return QISearch(this, (LPCQITAB)qit, riid, ppv); } ULONG CImageList::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CImageList::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CImageList::GetPrivateGoo(HBITMAP* phbmp, HDC* phdc, HBITMAP* phbmpMask, HDC* phdcMask) { if (phbmp) *phbmp = _hbmImage; if (phdc) *phdc = _hdcImage; if (phbmpMask) *phbmpMask = _hbmMask; if (phdcMask) *phdcMask = _hdcMask; return S_OK; } HRESULT CImageList::GetMirror(REFIID riid, void** ppv) { if (_pimlMirror) return _pimlMirror->QueryInterface(riid, ppv); return E_NOINTERFACE; } // // global work buffer, this buffer is always a DDB never a DIBSection // HBITMAP g_hbmWork = NULL; // work buffer. BITMAP g_bmWork = {0}; // work buffer size HBRUSH g_hbrMonoDither = NULL; // gray dither brush for dragging HBRUSH g_hbrStripe = NULL; #define NOTSRCAND 0x00220326L #define ROP_PSo 0x00FC008A #define ROP_DPo 0x00FA0089 #define ROP_DPna 0x000A0329 #define ROP_DPSona 0x00020c89 #define ROP_SDPSanax 0x00E61ce8 #define ROP_DSna 0x00220326 #define ROP_PSDPxax 0x00b8074a #define ROP_PatNotMask 0x00b8074a // D <- S==0 ? P : D #define ROP_PatMask 0x00E20746 // D <- S==1 ? P : D #define ROP_MaskPat 0x00AC0744 // D <- P==1 ? D : S #define ROP_DSo 0x00EE0086L #define ROP_DSno 0x00BB0226L #define ROP_DSa 0x008800C6L static int g_iDither = 0; void InitDitherBrush() { HBITMAP hbmTemp; static const WORD graybits[] = {0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555}; if (g_iDither) { g_iDither++; } else { // build the dither brush. this is a fixed 8x8 bitmap hbmTemp = CreateBitmap(8, 8, 1, 1, graybits); if (hbmTemp) { // now use the bitmap for what it was really intended... g_hbrMonoDither = CreatePatternBrush(hbmTemp); DeleteObject(hbmTemp); g_iDither++; } } } void TerminateDitherBrush() { g_iDither--; if (g_iDither == 0) { DeleteObject(g_hbrMonoDither); g_hbrMonoDither = NULL; } } /* ** GetScreenDepth() */ int GetScreenDepth() { int i; HDC hdc = GetDC(NULL); i = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES); ReleaseDC(NULL, hdc); return i; } // // should we use a DIB section on the current device? // // the main goal of using DS is to save memory, but they draw slow // on some devices. // // 4bpp Device (ie 16 color VGA) dont use DS // 8bpp Device (ie 256 color SVGA) use DS if DIBENG based. // >8bpp Device (ie 16bpp 24bpp) always use DS, saves memory // #define CAPS1 94 /* other caps */ #define C1_DIBENGINE 0x0010 /* DIB Engine compliant driver */ // // create a bitmap compatible with the given ImageList // HBITMAP CImageList::_CreateBitmap(int cx, int cy, RGBQUAD** ppargb) { HDC hdc; HBITMAP hbm; struct { BITMAPINFOHEADER bi; DWORD ct[256]; } dib; hdc = GetDC(NULL); // no color depth was specifed // // if we are on a DIBENG based DISPLAY, we use 4bit DIBSections to save // memory. // if ((_flags & ILC_COLORMASK) == 0) { _flags |= ILC_COLOR4; } if ((_flags & ILC_COLORMASK) != ILC_COLORDDB) { dib.bi.biSize = sizeof(BITMAPINFOHEADER); dib.bi.biWidth = cx; dib.bi.biHeight = cy; dib.bi.biPlanes = 1; dib.bi.biBitCount = (_flags & ILC_COLORMASK); dib.bi.biCompression = BI_RGB; dib.bi.biSizeImage = 0; dib.bi.biXPelsPerMeter = 0; dib.bi.biYPelsPerMeter = 0; dib.bi.biClrUsed = 16; dib.bi.biClrImportant = 0; dib.ct[0] = 0x00000000; // 0000 black dib.ct[1] = 0x00800000; // 0001 dark red dib.ct[2] = 0x00008000; // 0010 dark green dib.ct[3] = 0x00808000; // 0011 mustard dib.ct[4] = 0x00000080; // 0100 dark blue dib.ct[5] = 0x00800080; // 0101 purple dib.ct[6] = 0x00008080; // 0110 dark turquoise dib.ct[7] = 0x00C0C0C0; // 1000 gray dib.ct[8] = 0x00808080; // 0111 dark gray dib.ct[9] = 0x00FF0000; // 1001 red dib.ct[10] = 0x0000FF00; // 1010 green dib.ct[11] = 0x00FFFF00; // 1011 yellow dib.ct[12] = 0x000000FF; // 1100 blue dib.ct[13] = 0x00FF00FF; // 1101 pink (magenta) dib.ct[14] = 0x0000FFFF; // 1110 cyan dib.ct[15] = 0x00FFFFFF; // 1111 white if (dib.bi.biBitCount == 8) { HPALETTE hpal; int i; if (hpal = CreateHalftonePalette(NULL)) { i = GetPaletteEntries(hpal, 0, 256, (LPPALETTEENTRY)&dib.ct[0]); DeleteObject(hpal); if (i > 64) { dib.bi.biClrUsed = i; for (i=0; i<(int)dib.bi.biClrUsed; i++) dib.ct[i] = RGB(GetBValue(dib.ct[i]),GetGValue(dib.ct[i]),GetRValue(dib.ct[i])); } } else { dib.bi.biBitCount = (_flags & ILC_COLORMASK); dib.bi.biClrUsed = 256; } if (dib.bi.biClrUsed <= 16) dib.bi.biBitCount = 4; } hbm = CreateDIBSection(hdc, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, (PVOID*)ppargb, NULL, 0); } else { hbm = CreateCompatibleBitmap(hdc, cx, cy); } ReleaseDC(NULL, hdc); return hbm; } EXTERN_C HBITMAP CreateColorBitmap(int cx, int cy) { HBITMAP hbm; HDC hdc; hdc = GetDC(NULL); // // on a multimonitor system with mixed bitdepths // always use a 32bit bitmap for our work buffer // this will prevent us from losing colors when // blting to and from the screen. this is mainly // important for the drag & drop offscreen buffers. // if (!(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) && GetSystemMetrics(SM_CMONITORS) > 1 && GetSystemMetrics(SM_SAMEDISPLAYFORMAT) == 0) { void* p; BITMAPINFO bi = {sizeof(BITMAPINFOHEADER), cx, cy, 1, 32}; hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &p, NULL, 0); } else { hbm = CreateCompatibleBitmap(hdc, cx, cy); } ReleaseDC(NULL, hdc); return hbm; } HBITMAP CreateDIB(HDC h, int cx, int cy, RGBQUAD** pprgb) { BITMAPINFO bi = {0}; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = cx; bi.bmiHeader.biHeight = cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; return CreateDIBSection(h, &bi, DIB_RGB_COLORS, (void**)pprgb, NULL, 0); } BOOL DIBHasAlpha(int cx, int cy, RGBQUAD* prgb) { int cTotal = cx * cy; for (int i = 0; i < cTotal; i++) { if (prgb[i].rgbReserved != 0) return TRUE; } return FALSE; } void PreProcessDIB(int cx, int cy, RGBQUAD* pargb) { int cTotal = cx * cy; for (int i = 0; i < cTotal; i++) { RGBQUAD* prgb = &pargb[i]; if (prgb->rgbReserved != 0) { prgb->rgbRed = ((prgb->rgbRed * prgb->rgbReserved) + 128) / 255; prgb->rgbGreen = ((prgb->rgbGreen * prgb->rgbReserved) + 128) / 255; prgb->rgbBlue = ((prgb->rgbBlue * prgb->rgbReserved) + 128) / 255; } else { *((DWORD*)prgb) = 0; } } } EXTERN_C HBITMAP CreateMonoBitmap(int cx, int cy) { #ifdef MONO_DIB struct { BITMAPINFOHEADER bi; DWORD ct[2]; } dib = {0}; dib.bi.biSize = sizeof(dib.bi); dib.bi.biWidth = cx; dib.bi.biHeight = cy; dib.bi.biPlanes = 1; dib.bi.biBitCount = 1; dib.bi.biCompression = BI_RGB; dib.ct[0] = 0x00000000; dib.ct[1] = 0x00ffffff; HBITMAP hbmp = NULL; HDC hdc = CreateCompatibleDC(NULL); if (hdc) { hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&dib, DIB_PAL_COLORS, NULL, NULL, 0); DeleteDC(hdc); } return hbmp; #else return CreateBitmap(cx, cy, 1, 1, NULL); #endif } //============================================================================ BOOL CImageList::GlobalInit(void) { HDC hdcScreen; static const WORD stripebits[] = {0x7777, 0xdddd, 0x7777, 0xdddd, 0x7777, 0xdddd, 0x7777, 0xdddd}; HBITMAP hbmTemp; TraceMsg(TF_IMAGELIST, "CImageList::GlobalInit"); // if already initialized, there is nothing to do if (g_hdcDst) return TRUE; hdcScreen = GetDC(HWND_DESKTOP); g_hdcSrc = CreateCompatibleDC(hdcScreen); g_hdcDst = CreateCompatibleDC(hdcScreen); InitDitherBrush(); hbmTemp = CreateBitmap(8, 8, 1, 1, stripebits); if (hbmTemp) { // initialize the deselect 1x1 bitmap g_hbmDcDeselect = SelectBitmap(g_hdcDst, hbmTemp); SelectBitmap(g_hdcDst, g_hbmDcDeselect); g_hbrStripe = CreatePatternBrush(hbmTemp); DeleteObject(hbmTemp); } ReleaseDC(HWND_DESKTOP, hdcScreen); if (!g_hdcSrc || !g_hdcDst || !g_hbrMonoDither) { CImageList::GlobalUninit(); TraceMsg(TF_ERROR, "ImageList: Unable to initialize"); return FALSE; } return TRUE; } void CImageList::GlobalUninit() { TerminateDitherBrush(); if (g_hbrStripe) { DeleteObject(g_hbrStripe); g_hbrStripe = NULL; } ImageList_DeleteDragBitmaps(); if (g_hdcDst) { CImageList::SelectDstBitmap(NULL); DeleteDC(g_hdcDst); g_hdcDst = NULL; } if (g_hdcSrc) { CImageList::SelectSrcBitmap(NULL); DeleteDC(g_hdcSrc); g_hdcSrc = NULL; } if (g_hbmWork) { DeleteBitmap(g_hbmWork); g_hbmWork = NULL; } } void CImageList::SelectDstBitmap(HBITMAP hbmDst) { ASSERTCRITICAL; if (hbmDst != g_hbmDst) { // If it's selected in the source DC, then deselect it first // if (hbmDst && hbmDst == g_hbmSrc) CImageList::SelectSrcBitmap(NULL); SelectBitmap(g_hdcDst, hbmDst ? hbmDst : g_hbmDcDeselect); g_hbmDst = hbmDst; } } void CImageList::SelectSrcBitmap(HBITMAP hbmSrc) { ASSERTCRITICAL; if (hbmSrc != g_hbmSrc) { // If it's selected in the dest DC, then deselect it first // if (hbmSrc && hbmSrc == g_hbmDst) CImageList::SelectDstBitmap(NULL); SelectBitmap(g_hdcSrc, hbmSrc ? hbmSrc : g_hbmDcDeselect); g_hbmSrc = hbmSrc; } } HDC ImageList_GetWorkDC(HDC hdc, BOOL f32bpp, int dx, int dy) { ASSERTCRITICAL; int iDepth = GetDeviceCaps(hdc, BITSPIXEL); if (g_hbmWork == NULL || iDepth != g_bmWork.bmBitsPixel || g_bmWork.bmWidth != dx || g_bmWork.bmHeight != dy || (f32bpp && iDepth != 32)) { CImageList::_DeleteBitmap(g_hbmWork); g_hbmWork = NULL; if (dx == 0 || dy == 0) return NULL; if (f32bpp) g_hbmWork = CreateDIB(hdc, dx, dy, NULL); else g_hbmWork = CreateCompatibleBitmap(hdc, dx, dy); if (g_hbmWork) { GetObject(g_hbmWork, sizeof(g_bmWork), &g_bmWork); } } CImageList::SelectSrcBitmap(g_hbmWork); if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { HPALETTE hpal = (HPALETTE)SelectPalette(hdc, (HPALETTE)GetStockObject(DEFAULT_PALETTE), TRUE); SelectPalette(g_hdcSrc, hpal, TRUE); } return g_hdcSrc; } void ImageList_ReleaseWorkDC(HDC hdc) { ASSERTCRITICAL; ASSERT(hdc == g_hdcSrc); if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { SelectPalette(hdc, (HPALETTE)GetStockObject(DEFAULT_PALETTE), TRUE); } } void CImageList::_DeleteBitmap(HBITMAP hbm) { ASSERTCRITICAL; if (hbm) { if (g_hbmDst == hbm) CImageList::SelectDstBitmap(NULL); if (g_hbmSrc == hbm) CImageList::SelectSrcBitmap(NULL); DeleteBitmap(hbm); } } #define ILC_WIN95 (ILC_MASK | ILC_COLORMASK | ILC_SHARED | ILC_PALETTE) //============================================================================ HRESULT CImageList::InitGlobals() { HRESULT hr = S_OK; ENTERCRITICAL; if (!g_iILRefCount) { if (!CImageList::GlobalInit()) { hr = E_OUTOFMEMORY; } } LEAVECRITICAL; return S_OK; } CImageList* CImageList::Create(int cx, int cy, UINT flags, int cInitial, int cGrow) { CImageList* piml = NULL; HRESULT hr = S_OK; if (cx < 0 || cy < 0) return NULL; // Validate the flags if (flags & ~ILC_VALID) return NULL; hr = InitGlobals(); ENTERCRITICAL; if (SUCCEEDED(hr)) { piml = new CImageList(); // allocate the bitmap PLUS one re-usable entry if (piml) { hr = piml->Initialize(cx, cy, flags, cInitial, cGrow); if (FAILED(hr)) { piml->Release(); piml = NULL; } } } LEAVECRITICAL; return piml; } void CImageList::_Destroy() { ENTERCRITICAL; // nuke dc's if (_hdcImage) { SelectObject(_hdcImage, g_hbmDcDeselect); DeleteDC(_hdcImage); } if (_hdcMask) { SelectObject(_hdcMask, g_hbmDcDeselect); DeleteDC(_hdcMask); } // nuke bitmaps if (_hbmImage) _DeleteBitmap(_hbmImage); if (_hbmMask) _DeleteBitmap(_hbmMask); if (_hbrBk) DeleteObject(_hbrBk); //Clean up DSA if (_dsaFlags) DSA_Destroy(_dsaFlags); if (_fInitialized) { // one less use of imagelists. if it's the last, terminate the imagelist g_iILRefCount--; if (!g_iILRefCount) CImageList::GlobalUninit(); } LEAVECRITICAL; } HRESULT CImageList::GetImageCount(int* pi) { *pi = _cImage; return S_OK; } HRESULT CImageList::SetImageCount(UINT uAlloc) { ENTERCRITICAL; HRESULT hr = _ReAllocBitmaps(-((int)uAlloc + 2)); // Two because we need a spare image if (SUCCEEDED(hr)) { _cImage = (int)uAlloc; } LEAVECRITICAL; return hr; } HRESULT CImageList::GetIconSize(int* pcx, int* pcy) { if (!pcx || !pcy) return E_INVALIDARG; *pcx = _cx; *pcy = _cy; return S_OK; } // // change the size of a existing image list // also removes all items // HRESULT CImageList::_SetIconSize(int cxImage, int cyImage) { if (_cx == cxImage && _cy == cyImage) return S_FALSE; // no change if (_cx < 0 || _cy < 0) return E_INVALIDARG; // invalid dimensions _cx = cxImage; _cy = cyImage; return Remove(-1); } HRESULT CImageList::SetIconSize(int cxImage, int cyImage) { if (_pimlMirror) { _pimlMirror->_SetIconSize(cxImage, cyImage); } return _SetIconSize(cxImage, cyImage); } // // ImageList_SetFlags // // change the image list flags, then rebuilds the bitmaps. // // the only reason to call this function is to change the // color depth of the image list, the shell needs to do this // when the screen depth changes and it wants to use HiColor icons. // HRESULT CImageList::SetFlags(UINT uFlags) { HBITMAP hOldImage; // check for valid input flags if (uFlags & ~ILC_VALID) return E_INVALIDARG; // you cant change these flags. if ((uFlags ^ _flags) & ILC_SHARED) return E_INVALIDARG; if (_pimlMirror) _pimlMirror->SetFlags(uFlags); // now change the flags and rebuild the bitmaps. _flags = uFlags; // set the old bitmap to NULL, so when Imagelist_remove calls // ImageList_createBitmap, it will not call CreatecomptibleBitmap, // it will create the spec for the bitmap from scratch.. hOldImage = _hbmImage; _hbmImage = NULL; Remove(-1); // imagelist::remove will have ensured that the old image is no longer selected // thus we can now delete it... if ( hOldImage ) DeleteObject( hOldImage ); return S_OK; } HRESULT CImageList::GetFlags(UINT* puFlags) { *puFlags = (_flags & ILC_VALID) | (_pimlMirror ? ILC_MIRROR : 0); return S_OK; } // reset the background color of images iFirst through iLast void CImageList::_ResetBkColor(int iFirst, int iLast, COLORREF clr) { HBRUSH hbrT=NULL; DWORD rop; if (_hdcMask == NULL) return; if (clr == CLR_BLACK || clr == CLR_NONE) { rop = ROP_DSna; } else if (clr == CLR_WHITE) { rop = ROP_DSo; } else { ASSERT(_hbrBk); ASSERT(_clrBk == clr); rop = ROP_PatMask; hbrT = SelectBrush(_hdcImage, _hbrBk); } for ( ;iFirst <= iLast; iFirst++) { RECT rc; GetImageRect(iFirst, &rc); if (_GetItemFlags(iFirst) == 0) { BitBlt(_hdcImage, rc.left, rc.top, _cx, _cy, _hdcMask, rc.left, rc.top, rop); } } if (hbrT) SelectBrush(_hdcImage, hbrT); } // // GetNearestColor is problematic. If you have a 32-bit HDC with a 16-bit bitmap // selected into it, and you call GetNearestColor, GDI ignores the // color-depth of the bitmap and thinks you have a 32-bit bitmap inside, // so of course it returns the same color unchanged. // // So instead, we have to emulate GetNearestColor with SetPixel. // COLORREF GetNearestColor32(HDC hdc, COLORREF rgb) { COLORREF rgbT; rgbT = GetPixel(hdc, 0, 0); rgb = SetPixel(hdc, 0, 0, rgb); SetPixelV(hdc, 0, 0, rgbT); return rgb; } COLORREF CImageList::_SetBkColor(COLORREF clrBkI) { COLORREF clrBkOld; // Quick out if there is no change in color if (_clrBk == clrBkI) { return _clrBk; } // The following code deletes the brush, resets the background color etc., // so, protect it with a critical section. ENTERCRITICAL; if (_hbrBk) { DeleteBrush(_hbrBk); } clrBkOld = _clrBk; _clrBk = clrBkI; if (_clrBk == CLR_NONE) { _hbrBk = (HBRUSH)GetStockObject(BLACK_BRUSH); _fSolidBk = TRUE; } else { _hbrBk = CreateSolidBrush(_clrBk); _fSolidBk = GetNearestColor32(_hdcImage, _clrBk) == _clrBk; } if (_cImage > 0) { _ResetBkColor(0, _cImage - 1, _clrBk); } LEAVECRITICAL; return clrBkOld; } HRESULT CImageList::SetBkColor(COLORREF clrBk, COLORREF* pclr) { if (_pimlMirror) { _pimlMirror->_SetBkColor(clrBk); } *pclr = _SetBkColor(clrBk); return S_OK; } HRESULT CImageList::GetBkColor(COLORREF* pclr) { *pclr = _clrBk; return S_OK; } HRESULT CImageList::_ReAllocBitmaps(int cAllocI) { HBITMAP hbmImageNew = NULL; HBITMAP hbmMaskNew = NULL; RGBQUAD* pargbImageNew = NULL; int cxL, cyL; // HACK: don't shrink unless the caller passes a negative count if (cAllocI > 0) { if (_cAlloc >= cAllocI) return S_OK; } else cAllocI *= -1; cxL = _cx * _cStrip; cyL = _cy * ((cAllocI + _cStrip - 1) / _cStrip); if (cAllocI > 0) { if (_flags & ILC_MASK) { hbmMaskNew = CreateMonoBitmap(cxL, cyL); if (!hbmMaskNew) { TraceMsg(TF_ERROR, "ImageList: Can't create bitmap"); return E_OUTOFMEMORY; } } hbmImageNew = _CreateBitmap(cxL, cyL, &pargbImageNew); if (!hbmImageNew) { if (hbmMaskNew) CImageList::_DeleteBitmap(hbmMaskNew); TraceMsg(TF_ERROR, "ImageList: Can't create bitmap"); return E_OUTOFMEMORY; } if (_dsaFlags == NULL) _dsaFlags = DSA_Create(sizeof(DWORD), _cGrow); if (!_dsaFlags) { if (hbmMaskNew) CImageList::_DeleteBitmap(hbmMaskNew); if (hbmImageNew) CImageList::_DeleteBitmap(hbmImageNew); TraceMsg(TF_ERROR, "ImageList: Can't create flags array"); return E_OUTOFMEMORY; } } if (_cImage > 0) { int cyCopy = _cy * ((min(cAllocI, _cImage) + _cStrip - 1) / _cStrip); if (_flags & ILC_MASK) { CImageList::SelectDstBitmap(hbmMaskNew); BitBlt(g_hdcDst, 0, 0, cxL, cyCopy, _hdcMask, 0, 0, SRCCOPY); } CImageList::SelectDstBitmap(hbmImageNew); BitBlt(g_hdcDst, 0, 0, cxL, cyCopy, _hdcImage, 0, 0, SRCCOPY); } // select into DC's, delete then assign CImageList::SelectDstBitmap(NULL); CImageList::SelectSrcBitmap(NULL); SelectObject(_hdcImage, hbmImageNew); if (_hdcMask) SelectObject(_hdcMask, hbmMaskNew); if (_hbmMask) CImageList::_DeleteBitmap(_hbmMask); if (_hbmImage) CImageList::_DeleteBitmap(_hbmImage); _hbmMask = hbmMaskNew; _hbmImage = hbmImageNew; _pargbImage = pargbImageNew; _clrBlend = CLR_NONE; _cAlloc = cAllocI; return S_OK; } HBITMAP CImageList::_CreateMirroredBitmap(HBITMAP hbmOrig, BOOL fMirrorEach, int cx) { HBITMAP hbm = NULL, hOld_bm1, hOld_bm2; BITMAP bm; if (!hbmOrig) return NULL; if (!GetObject(hbmOrig, sizeof(BITMAP), &bm)) return NULL; // Grab the screen DC HDC hdc = GetDC(NULL); HDC hdcMem1 = CreateCompatibleDC(hdc); if (!hdcMem1) { ReleaseDC(NULL, hdc); return NULL; } HDC hdcMem2 = CreateCompatibleDC(hdc); if (!hdcMem2) { DeleteDC(hdcMem1); ReleaseDC(NULL, hdc); return NULL; } if (bm.bmBitsPixel == 32) { void* p; BITMAPINFO bi = {sizeof(BITMAPINFOHEADER), bm.bmWidth, bm.bmHeight, 1, 32}; hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &p, NULL, 0); } else { hbm = CreateColorBitmap(bm.bmWidth, bm.bmHeight); } if (!hbm) { DeleteDC(hdcMem2); DeleteDC(hdcMem1); ReleaseDC(NULL, hdc); return NULL; } // // Flip the bitmap // hOld_bm1 = (HBITMAP)SelectObject(hdcMem1, hbmOrig); hOld_bm2 = (HBITMAP)SelectObject(hdcMem2 , hbm ); SET_DC_RTL_MIRRORED(hdcMem2); if (fMirrorEach) { for (int i = 0; i < bm.bmWidth; i += cx) // Flip the bits in the imagelist... { BitBlt(hdcMem2, bm.bmWidth - i - cx, 0, cx, bm.bmHeight, hdcMem1, i, 0, SRCCOPY); } } else { BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem1, 0, 0, SRCCOPY); } SelectObject(hdcMem1, hOld_bm1 ); SelectObject(hdcMem1, hOld_bm2 ); DeleteDC(hdcMem2); DeleteDC(hdcMem1); ReleaseDC(NULL, hdc); return hbm; } HRESULT CImageList::SetColorTable(int start, int len, RGBQUAD *prgb, int* pi) { // mark it that we have set the color table so that it won't be overwritten // by the first bitmap add.... _fColorsSet = TRUE; if (_hdcImage) { *pi = SetDIBColorTable(_hdcImage, start, len, prgb); return S_OK; } return E_FAIL; } BOOL CImageList::_HasAlpha(int i) { if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { RECT rc; if (SUCCEEDED(GetImageRectInverted(i, &rc))) { for (int y = rc.top; y < rc.bottom; y++) { for (int x = rc.left; x < rc.right; x++) { if (_pargbImage[x + y * _cx].rgbReserved != 0) return TRUE; } } } } return FALSE; } void CImageList::_ScanForAlpha() { if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { for (int i = 0; i < _cImage; i++) { SetItemFlags(i, _HasAlpha(i)? ILIF_ALPHA : 0); } } } BOOL CImageList::_PreProcessImage(int i) { if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { RECT rc; GetImageRectInverted(i, &rc); #ifdef _X86_ if (IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE)) { _asm { pxor mm0, mm0 pxor mm1, mm1 pxor mm5, mm5 movq mm6, qw128 // mm6 is filled with 128 movq mm7, qw1 // mm7 is filled with 1 } for (int y = rc.top; y < rc.bottom; y++) { int Offset = y * _cx; RGBQUAD* prgb = &_pargbImage[rc.left + Offset]; for (int x = rc.left; x < rc.right; x++) { _asm { push ecx mov edx, dword ptr [prgb] // Read alpha channel mov ecx, dword ptr [edx] mov ebx, ecx shr ebx, 24 // a >> 24 mov eax, ebx // a -> b or eax, eax jz EarlyOut shl ebx, 8 // b << 8 or eax, ebx // a |= b shl ebx, 8 // b << 8 or eax, ebx // a |= b shl ebx, 8 // b << 8 // Note high byte of alpha is zero. movd mm0, eax // a -> mm0 movd mm1, ecx // Load the pixel punpcklbw mm0,mm5 // mm0 -> Expands <- mm0 Contains the Alpha channel for this multiply punpcklbw mm1,mm5 // Unpack the pixel pmullw mm1, mm0 // Multiply by the alpha channel <- mm1 contains c * alpha paddusw mm1, mm6 // perform the (c * alpha) + 128 psrlw mm1, 8 // Divide by 255 paddusw mm1, mm7 // Add 1 to finish the divide by 255 packuswb mm1, mm5 movd eax, mm1 or eax, ebx // Transfer alpha channel EarlyOut: mov dword ptr [edx], eax pop ecx } prgb++; } } _asm emms } else #endif { for (int y = rc.top; y < rc.bottom; y++) { int Offset = y * _cx; for (int x = rc.left; x < rc.right; x++) { RGBQUAD* prgb = &_pargbImage[x + Offset]; if (prgb->rgbReserved) { prgb->rgbRed = ((prgb->rgbRed * prgb->rgbReserved) + 128) / 255; prgb->rgbGreen = ((prgb->rgbGreen * prgb->rgbReserved) + 128) / 255; prgb->rgbBlue = ((prgb->rgbBlue * prgb->rgbReserved) + 128) / 255; } else { *((DWORD*)prgb) = 0; } } } } return TRUE; } return FALSE; } HRESULT CImageList::_Add(HBITMAP hbmImageI, HBITMAP hbmMaskI, int cImageI, int xStart, int yStart, int* pi) { int i = -1; HRESULT hr = S_OK; ENTERCRITICAL; // // if the ImageList is empty clone the color table of the first // bitmap you add to the imagelist. // // the ImageList needs to be a 8bpp image list // the bitmap being added needs to be a 8bpp DIBSection // if (hbmImageI && _cImage == 0 && (_flags & ILC_COLORMASK) != ILC_COLORDDB) { if (!_fColorsSet) { int n; RGBQUAD argb[256]; CImageList::SelectDstBitmap(hbmImageI); if (n = GetDIBColorTable(g_hdcDst, 0, 256, argb)) { int i; SetColorTable(0, n, argb, &i); } CImageList::SelectDstBitmap(NULL); } _clrBlend = CLR_NONE; } if (_cImage + cImageI + 1 > _cAlloc) { hr = _ReAllocBitmaps(_cAlloc + max(cImageI, _cGrow) + 1); } if (SUCCEEDED(hr)) { i = _cImage; _cImage += cImageI; if (hbmImageI) { hr = _Replace(i, cImageI, hbmImageI, hbmMaskI, xStart, yStart); if (FAILED(hr)) { _cImage -= cImageI; i = -1; } } } LEAVECRITICAL; *pi = i; return hr; } HRESULT CImageList::_AddValidated(HBITMAP hbmImage, HBITMAP hbmMask, int* pi) { BITMAP bm; int cImageI; if (GetObject(hbmImage, sizeof(bm), &bm) != sizeof(bm) || bm.bmWidth < _cx) { return E_INVALIDARG; } ASSERT(hbmImage); ASSERT(_cx); cImageI = bm.bmWidth / _cx; // # of images in source // serialization handled within Add2. return _Add(hbmImage, hbmMask, cImageI, 0, 0, pi); } HRESULT CImageList::Add(HBITMAP hbmImage, HBITMAP hbmMask, int* pi) { if (_pimlMirror) { HBITMAP hbmMirroredImage = _CreateMirroredBitmap(hbmImage, (ILC_PERITEMMIRROR & _flags), _cx); HBITMAP hbmMirroredMask = _CreateMirroredBitmap(hbmMask, (ILC_PERITEMMIRROR & _flags), _cx); _pimlMirror->_AddValidated(hbmMirroredImage, hbmMirroredMask, pi); // The caller will take care of deleting hbmImage, hbmMask // He knows nothing about hbmMirroredImage, hbmMirroredMask DeleteObject(hbmMirroredImage); DeleteObject(hbmMirroredMask); } return _AddValidated(hbmImage, hbmMask, pi); } HRESULT CImageList::_AddMasked(HBITMAP hbmImageI, COLORREF crMask, int* pi) { HRESULT hr = S_OK; COLORREF crbO, crtO; HBITMAP hbmMaskI; int cImageI; int n,i; BITMAP bm; DWORD ColorTableSave[256]; DWORD ColorTable[256]; *pi = -1; if (GetObject(hbmImageI, sizeof(bm), &bm) != sizeof(bm)) return E_INVALIDARG; hbmMaskI = CreateMonoBitmap(bm.bmWidth, bm.bmHeight); if (!hbmMaskI) return E_OUTOFMEMORY; ENTERCRITICAL; // copy color to mono, with crMask turning 1 and all others 0, then // punch all crMask pixels in color to 0 CImageList::SelectSrcBitmap(hbmImageI); CImageList::SelectDstBitmap(hbmMaskI); // crMask == CLR_DEFAULT, means use the pixel in the upper left // if (crMask == CLR_DEFAULT) crMask = GetPixel(g_hdcSrc, 0, 0); // DIBSections dont do color->mono like DDBs do, so we have to do it. // this only works for <=8bpp DIBSections, this method does not work // for HiColor DIBSections. // // This code is a workaround for a problem in Win32 when a DIB is converted to // monochrome. The conversion is done according to closeness to white or black // and without regard to the background color. This workaround is is not required // under MainWin. // // Please note, this code has an endianship problems the comparision in the if statement // below is sensitive to endianship // ----> if (ColorTableSave[i] == RGB(GetBValue(crMask),GetGValue(crMask),GetRValue(crMask)) // if (bm.bmBits != NULL && bm.bmBitsPixel <= 8) { n = GetDIBColorTable(g_hdcSrc, 0, 256, (RGBQUAD*)ColorTableSave); for (i=0; i_AddMasked(hbmMirroredImage, crMask, pi); // The caller will take care of deleting hbmImage // He knows nothing about hbmMirroredImage DeleteObject(hbmMirroredImage); } return _AddMasked(hbmImage, crMask, pi); } HRESULT CImageList::_ReplaceValidated(int i, HBITMAP hbmImage, HBITMAP hbmMask) { HRESULT hr = E_INVALIDARG; if (!IsImageListIndex(i)) return hr; ENTERCRITICAL; hr = _Replace(i, 1, hbmImage, hbmMask, 0, 0); LEAVECRITICAL; return hr; } HRESULT CImageList::Replace(int i, HBITMAP hbmImage, HBITMAP hbmMask) { if (_pimlMirror) { HBITMAP hbmMirroredImage = CImageList::_CreateMirroredBitmap(hbmImage, (ILC_PERITEMMIRROR & _flags), _cx); if (hbmMirroredImage) { HBITMAP hbmMirroredMask = NULL; if (hbmMask) hbmMirroredMask = CImageList::_CreateMirroredBitmap(hbmMask, (ILC_PERITEMMIRROR & _flags), _cx); _pimlMirror->_ReplaceValidated(i, hbmMirroredImage, hbmMirroredMask); if (hbmMirroredMask) DeleteObject(hbmMirroredMask); DeleteObject(hbmMirroredImage); } } return _ReplaceValidated(i, hbmImage, hbmMask); } // replaces images in piml with images from bitmaps // // in: // piml // i index in image list to start at (replace) // _cImage count of images in source (hbmImage, hbmMask) // HRESULT CImageList::_Replace(int i, int cImageI, HBITMAP hbmImageI, HBITMAP hbmMaskI, int xStart, int yStart) { RECT rcImage; int x, iImage; BOOL fBitmapIs32 = FALSE; ASSERT(_hbmImage); BITMAP bm; GetObject(hbmImageI, sizeof(bm), &bm); if (bm.bmBitsPixel == 32) { fBitmapIs32 = TRUE; } CImageList::SelectSrcBitmap(hbmImageI); if (_hdcMask) CImageList::SelectDstBitmap(hbmMaskI); // using as just a second source hdc for (x = xStart, iImage = 0; iImage < cImageI; iImage++, x += _cx) { GetImageRect(i + iImage, &rcImage); if (_hdcMask) { BitBlt(_hdcMask, rcImage.left, rcImage.top, _cx, _cy, g_hdcDst, x, yStart, SRCCOPY); } BitBlt(_hdcImage, rcImage.left, rcImage.top, _cx, _cy, g_hdcSrc, x, yStart, SRCCOPY); if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { DWORD dw = 0; if (fBitmapIs32) { BOOL fHasAlpha = _HasAlpha(i + iImage); if (fHasAlpha) { dw = ILIF_ALPHA; _PreProcessImage(i + iImage); } } SetItemFlags(i + iImage, dw); } } _ResetBkColor(i, i + cImageI - 1, _clrBk); CImageList::SelectSrcBitmap(NULL); if (_hdcMask) CImageList::SelectDstBitmap(NULL); return S_OK; } void UnPremultiply(RGBQUAD* pargb, int cx, int cy) { int cTotal = cx * cy; for (int i = 0; i < cTotal; i++) { RGBQUAD* prgb = &pargb[i]; if (prgb->rgbReserved != 0) { prgb->rgbRed = ((255 * prgb->rgbRed) - 128)/prgb->rgbReserved; prgb->rgbGreen = ((255 * prgb->rgbGreen) - 128)/prgb->rgbReserved; prgb->rgbBlue = ((255 * prgb->rgbBlue) - 128)/prgb->rgbReserved; } } } HRESULT CImageList::GetIcon(int i, UINT flags, HICON* phicon) { UINT cxImage, cyImage; HICON hIcon = NULL; HBITMAP hbmMask = NULL; HBITMAP hbmColor = NULL; ICONINFO ii; HRESULT hr = E_OUTOFMEMORY; RGBQUAD* prgb; DWORD fHasAlpha = FALSE; ENTERCRITICAL; if (!IsImageListIndex(i)) { hr = E_INVALIDARG; } else { fHasAlpha = (_GetItemFlags(i) & ILIF_ALPHA); } LEAVECRITICAL; if (E_INVALIDARG == hr) return hr; cxImage = _cx; cyImage = _cy; if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { // If the source image is not an alpha image, we need to create a lower than 32bpp dib. // We need to do this because if the overlay contains an alpha channel, this will // be propogated to the final icon, and the only visible portion will be the link item. BITMAPINFO bi = {0}; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = cxImage; bi.bmiHeader.biHeight = cyImage; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = fHasAlpha?32:24; bi.bmiHeader.biCompression = BI_RGB; HDC hdcScreen = GetDC(NULL); if (hdcScreen) { hbmColor = CreateDIBSection(hdcScreen, &bi, DIB_RGB_COLORS, (void**)&prgb, NULL, 0); ReleaseDC(NULL, hdcScreen); } flags |= ILD_PRESERVEALPHA; } else { fHasAlpha = FALSE; hbmColor = CreateColorBitmap(cxImage, cyImage); } if (hbmColor) { hbmMask = CreateMonoBitmap(cxImage, cyImage); if (hbmMask) { ENTERCRITICAL; CImageList::SelectDstBitmap(hbmMask); PatBlt(g_hdcDst, 0, 0, cxImage, cyImage, WHITENESS); WimpyDraw(SAFECAST(this, IImageList*), i, g_hdcDst, 0, 0, ILD_MASK | flags); CImageList::SelectDstBitmap(hbmColor); PatBlt(g_hdcDst, 0, 0, cxImage, cyImage, BLACKNESS); WimpyDraw(SAFECAST(this, IImageList*), i, g_hdcDst, 0, 0, ILD_TRANSPARENT | flags); CImageList::SelectDstBitmap(NULL); LEAVECRITICAL; if (fHasAlpha) { UnPremultiply(prgb, _cx, _cy); } ii.fIcon = TRUE; ii.xHotspot = 0; ii.yHotspot = 0; ii.hbmColor = hbmColor; ii.hbmMask = hbmMask; hIcon = CreateIconIndirect(&ii); DeleteObject(hbmMask); hr = S_OK; } DeleteObject(hbmColor); } *phicon = hIcon; return hr; } // this removes an image from the bitmap but doing all the // proper shuffling. // // this does the following: // if the bitmap being removed is not the last in the row // it blts the images to the right of the one being deleted // to the location of the one being deleted (covering it up) // // for all rows until the last row (where the last image is) // move the image from the next row up to the last position // in the current row. then slide over all images in that // row to the left. void CImageList::_RemoveItemBitmap(int i) { RECT rc1; RECT rc2; int dx, y; int x; GetImageRect(i, &rc1); GetImageRect(_cImage - 1, &rc2); if (i < _cImage && (_flags & ILC_COLORMASK) == ILC_COLOR32) { DSA_DeleteItem(_dsaFlags, i); } SetItemFlags(_cImage, 0); // the row with the image being deleted, do we need to shuffle? // amount of stuff to shuffle dx = _cStrip * _cx - rc1.right; if (dx) { // yes, shuffle things left BitBlt(_hdcImage, rc1.left, rc1.top, dx, _cy, _hdcImage, rc1.right, rc1.top, SRCCOPY); if (_hdcMask) BitBlt(_hdcMask, rc1.left, rc1.top, dx, _cy, _hdcMask, rc1.right, rc1.top, SRCCOPY); } y = rc1.top; // top of row we are working on x = _cx * (_cStrip - 1); // x coord of last bitmaps in each row while (y < rc2.top) { // copy first from row below to last image position on this row BitBlt(_hdcImage, x, y, _cx, _cy, _hdcImage, 0, y + _cy, SRCCOPY); if (_hdcMask) BitBlt(_hdcMask, x, y, _cx, _cy, _hdcMask, 0, y + _cy, SRCCOPY); y += _cy; // jump to row to slide left if (y <= rc2.top) { // slide the rest over to the left BitBlt(_hdcImage, 0, y, x, _cy, _hdcImage, _cx, y, SRCCOPY); // slide the rest over to the left if (_hdcMask) { BitBlt(_hdcMask, 0, y, x, _cy, _hdcMask, _cx, y, SRCCOPY); } } } } // // ImageList_Remove - remove a image from the image list // // i - image to remove, or -1 to remove all images. // // NOTE all images are "shifted" down, ie all image index's // above the one deleted are changed by 1 // HRESULT CImageList::_Remove(int i) { HRESULT hr = S_OK; ENTERCRITICAL; if (i == -1) { _cImage = 0; _cAlloc = 0; for (i=0; i _cGrow) _ReAllocBitmaps(_cAlloc - _cGrow); } } LEAVECRITICAL; return hr; } HRESULT CImageList::Remove(int i) { if (_pimlMirror) { _pimlMirror->_Remove(i); } return _Remove(i); } BOOL CImageList::_IsSameObject(IUnknown* punk) { BOOL fRet = FALSE; IUnknown* me; IUnknown* them; if (punk == NULL) return FALSE; QueryInterface(IID_PPV_ARG(IUnknown, &me)); if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IUnknown, &them)))) { fRet = (me == them); them->Release(); } me->Release(); return fRet; } // // ImageList_Copy - move an image in the image list // HRESULT CImageList::Copy(int iDst, IUnknown* punkSrc, int iSrc, UINT uFlags) { RECT rcDst, rcSrc, rcTmp; CImageList* pimlTmp; CImageList* pimlSrc; HRESULT hr = E_FAIL; if (uFlags & ~ILCF_VALID) { // don't let hosers pass bogus flags RIPMSG(0, "ImageList_Copy: Invalid flags %08x", uFlags); return E_INVALIDARG; } // Not supported if (!_IsSameObject(punkSrc)) { return E_INVALIDARG; } // We only support copies on ourself... Weird pimlSrc = this; ENTERCRITICAL; pimlTmp = (uFlags & ILCF_SWAP)? pimlSrc : NULL; if (SUCCEEDED(GetImageRect(iDst, &rcDst)) && SUCCEEDED(pimlSrc->GetImageRect(iSrc, &rcSrc)) && (!pimlTmp || pimlTmp->GetSpareImageRect(&rcTmp))) { int cx = pimlSrc->_cx; int cy = pimlSrc->_cy; // // iff we are swapping we need to save the destination image // if (pimlTmp) { BitBlt(pimlTmp->_hdcImage, rcTmp.left, rcTmp.top, cx, cy, _hdcImage, rcDst.left, rcDst.top, SRCCOPY); if (pimlTmp->_hdcMask) { BitBlt(pimlTmp->_hdcMask, rcTmp.left, rcTmp.top, cx, cy, _hdcMask, rcDst.left, rcDst.top, SRCCOPY); } } // // copy the image // BitBlt(_hdcImage, rcDst.left, rcDst.top, cx, cy, pimlSrc->_hdcImage, rcSrc.left, rcSrc.top, SRCCOPY); if (pimlSrc->_hdcMask) { BitBlt(_hdcMask, rcDst.left, rcDst.top, cx, cy, pimlSrc->_hdcMask, rcSrc.left, rcSrc.top, SRCCOPY); } // // iff we are swapping we need to copy the saved image too // if (pimlTmp) { BitBlt(pimlSrc->_hdcImage, rcSrc.left, rcSrc.top, cx, cy, pimlTmp->_hdcImage, rcTmp.left, rcTmp.top, SRCCOPY); if (pimlSrc->_hdcMask) { BitBlt(pimlSrc->_hdcMask, rcSrc.left, rcSrc.top, cx, cy, pimlTmp->_hdcMask, rcTmp.left, rcTmp.top, SRCCOPY); } } hr = S_OK; } LEAVECRITICAL; return hr; } // IS_WHITE_PIXEL, BITS_ALL_WHITE are macros for looking at monochrome bits // to determine if certain pixels are white or black. Note that within a byte // the most significant bit represents the left most pixel. // #define IS_WHITE_PIXEL(pj,x,y,cScan) \ ((pj)[((y) * (cScan)) + ((x) >> 3)] & (1 << (7 - ((x) & 7)))) #define BITS_ALL_WHITE(b) (b == 0xff) // Set the image iImage as one of the special images for us in combine // drawing. to draw with these specify the index of this // in: // piml imagelist // iImage image index to use in speical drawing // iOverlay index of special image, values 1-4 HRESULT CImageList::_SetOverlayImage(int iImage, int iOverlay) { RECT rcImage; RECT rc; int x,y; int cxI,cyI; ULONG cScan; ULONG cBits; HBITMAP hbmMem; HRESULT hr = S_FALSE; iOverlay--; // make zero based if (_hdcMask == NULL || iImage < 0 || iImage >= _cImage || iOverlay < 0 || iOverlay >= NUM_OVERLAY_IMAGES) { return E_INVALIDARG; } if (_aOverlayIndexes[iOverlay] == (SHORT)iImage) return S_OK; _aOverlayIndexes[iOverlay] = (SHORT)iImage; // // find minimal rect that bounds the image // GetImageRect(iImage, &rcImage); SetRect(&rc, 0x7FFF, 0x7FFF, 0, 0); // // now compute the black box. This is much faster than GetPixel but // could still be improved by doing more operations looking at entire // bytes. We basicaly get the bits in monochrome form and then use // a private GetPixel. This decreased time on NT from 50 milliseconds to // 1 millisecond for a 32X32 image. // cxI = rcImage.right - rcImage.left; cyI = rcImage.bottom - rcImage.top; // compute the number of bytes in a scan. Note that they are WORD alligned cScan = (((cxI + (sizeof(SHORT)*8 - 1)) / 16) * 2); cBits = cScan * cyI; hbmMem = CreateMonoBitmap(cxI,cyI); if (hbmMem) { HDC hdcMem = CreateCompatibleDC(_hdcMask); if (hdcMem) { PBYTE pBits = (PBYTE)LocalAlloc(LMEM_FIXED,cBits); PBYTE pScan; if (pBits) { SelectObject(hdcMem,hbmMem); // // map black pixels to 0, white to 1 // BitBlt(hdcMem, 0, 0, cxI, cyI, _hdcMask, rcImage.left, rcImage.top, SRCCOPY); // // fill in the bits // GetBitmapBits(hbmMem,cBits,pBits); // // for each scan, find the bounds // for (y = 0, pScan = pBits; y < cyI; ++y,pScan += cScan) { int i; // // first go byte by byte through white space // for (x = 0, i = 0; (i < (cxI >> 3)) && BITS_ALL_WHITE(pScan[i]); ++i) { x += 8; } // // now finish the scan bit by bit // for (; x < cxI; ++x) { if (!IS_WHITE_PIXEL(pBits, x,y,cScan)) { rc.left = min(rc.left, x); rc.right = max(rc.right, x+1); rc.top = min(rc.top, y); rc.bottom = max(rc.bottom, y+1); // now that we found one, quickly jump to the known right edge if ((x >= rc.left) && (x < rc.right)) { x = rc.right-1; } } } } if (rc.left == 0x7FFF) { rc.left = 0; TraceMsg(TF_ERROR, "SetOverlayImage: Invalid image. No white pixels specified"); } if (rc.top == 0x7FFF) { rc.top = 0; TraceMsg(TF_ERROR, "SetOverlayImage: Invalid image. No white pixels specified"); } _aOverlayDX[iOverlay] = (SHORT)(rc.right - rc.left); _aOverlayDY[iOverlay] = (SHORT)(rc.bottom- rc.top); _aOverlayX[iOverlay] = (SHORT)(rc.left); _aOverlayY[iOverlay] = (SHORT)(rc.top); _aOverlayF[iOverlay] = 0; // // see if the image is non-rectanglar // // if the overlay does not require a mask to be drawn set the // ILD_IMAGE flag, this causes ImageList_DrawEx to just // draw the image, ignoring the mask. // for (y=rc.top; y_SetOverlayImage(iImage, iOverlay); } return _SetOverlayImage(iImage, iOverlay); } /* ** BlendCT ** */ void CImageList::BlendCTHelper(DWORD *pdw, DWORD rgb, UINT n, UINT count) { UINT i; for (i=0; i>3)&0x1F)<<10) | ((((g)>>3)&0x1F)<<5) | (((b)>>3)&0x1F)) #define R_555(w) (int)(((w) >> 7) & 0xF8) #define G_555(w) (int)(((w) >> 2) & 0xF8) #define B_555(w) (int)(((w) << 3) & 0xF8) void CImageList::Blend16Helper(int xSrc, int ySrc, int xDst, int yDst, int cx, int cy, COLORREF rgb, int a) // alpha value { // If it's odd, Adjust. if ((cx & 1) == 1) { cx++; } if (rgb == CLR_NONE) { // blending with the destination, we ignore the alpha and always // do 50% (this is what the old dither mask code did) int ys = ySrc; int yd = yDst; for (; ys < ySrc + cy; ys++, yd++) { WORD* pSrc = &((WORD*)_pargbImage)[xSrc + ys * cx]; // Cast because we've gotten to this case because we are a 555 imagelist WORD* pDst = &((WORD*)_pargbImage)[xDst + yd * cx]; for (int x = 0; x < cx; x++) { *pDst++ = ((*pDst & 0x7BDE) >> 1) + ((*pSrc++ & 0x7BDE) >> 1); } } } else { // blending with a solid color // pre multiply source (constant) rgb by alpha int sr = GetRValue(rgb) * a; int sg = GetGValue(rgb) * a; int sb = GetBValue(rgb) * a; // compute inverse alpha for inner loop a = 256 - a; // special case a 50% blend, to avoid a multiply if (a == 128) { sr = RGB555(sr>>8,sg>>8,sb>>8); int ys = ySrc; int yd = yDst; for (; ys < ySrc + cy; ys++, yd++) { WORD* pSrc = &((WORD*)_pargbImage)[xSrc + ys * cx]; WORD* pDst = &((WORD*)_pargbImage)[xDst + yd * cx]; for (int x = 0; x < cx; x++) { int i = *pSrc++; i = sr + ((i & 0x7BDE) >> 1); *pDst++ = (WORD) i; } } } else { int ys = ySrc; int yd = yDst; for (; ys < ySrc + cy; ys++, yd++) { WORD* pSrc = &((WORD*)_pargbImage)[xSrc + ys * cx]; WORD* pDst = &((WORD*)_pargbImage)[xDst + yd * cx]; for (int x = 0; x < cx; x++) { int i = *pSrc++; int r = (R_555(i) * a + sr) >> 8; int g = (G_555(i) * a + sg) >> 8; int b = (B_555(i) * a + sb) >> 8; *pDst++ = RGB555(r,g,b); } } } } } /* ** ImageList_Blend16 ** ** copy the source to the dest blended with the given color. ** ** source is assumed to be a 16 bit (RGB 555) bottom-up DIBSection ** (this is the only kind of DIBSection we create) */ void CImageList::Blend16(HDC hdcDst, int xDst, int yDst, int iImage, int cx, int cy, COLORREF rgb, UINT fStyle) { BITMAP bm; RECT rc; RECT rcSpare; RECT rcSpareInverted; int a, x, y; // get bitmap info for source bitmap GetObject(_hbmImage, sizeof(bm), &bm); ASSERT(bm.bmBitsPixel==16); // get blend RGB if (rgb == CLR_DEFAULT) rgb = GetSysColor(COLOR_HIGHLIGHT); // get blend factor as a fraction of 256 // only 50% or 25% is currently used. if ((fStyle & ILD_BLENDMASK) == ILD_BLEND50) a = 128; else a = 64; GetImageRectInverted(iImage, &rc); x = rc.left; y = rc.top; // blend the image with the specified color and place at end of image list if (GetSpareImageRectInverted(&rcSpareInverted) && GetSpareImageRect(&rcSpare)) { // if blending with the destination, copy the dest to our work buffer if (rgb == CLR_NONE) BitBlt(_hdcImage, rcSpare.left, rcSpare.top, cx, cy, hdcDst, xDst, yDst, SRCCOPY); // sometimes the user can change the icon size (via plustab) between 32x32 and 48x48, // thus the values we have might be bigger than the actual bitmap. To prevent us from // crashing in Blend16 when this happens we do some bounds checks here if (rc.left + cx <= bm.bmWidth && rc.top + cy <= bm.bmHeight && x + cx <= bm.bmWidth && y + cy <= bm.bmHeight) { // Needs inverted coordinates Blend16Helper(x, y, rcSpareInverted.left, rcSpareInverted.top, cx, cy, rgb, a); } // blt blended image to the dest DC BitBlt(hdcDst, xDst, yDst, cx, cy, _hdcImage, rcSpare.left, rcSpare.top, SRCCOPY); } } #define ALPHA_50 128 #define ALPHA_25 64 void CImageList::_GenerateAlphaForImageUsingMask(int iImage, BOOL fSpare) { RECT rcImage; RECT rcInverted; HRESULT hr; GetImageRect(iImage, &rcImage); if (fSpare) { hr = GetSpareImageRectInverted(&rcInverted); } else { hr = GetImageRectInverted(iImage, &rcInverted); } if (!SUCCEEDED(hr)) return; BITMAPINFO bi = {0}; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = _cx; bi.bmiHeader.biHeight = _cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; HDC hdcMem = CreateCompatibleDC(_hdcMask); if (hdcMem) { RGBQUAD* pbMask; HBITMAP hbmp = CreateDIBSection(hdcMem, &bi, DIB_RGB_COLORS, (void**)&pbMask, NULL, 0); if (hbmp) { HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp); SetTextColor(hdcMem, RGB(0xFF, 0xFF, 0xFF)); ::SetBkColor(hdcMem, RGB(0x0,0x0,0x0)); BitBlt(hdcMem, 0, 0, _cx, _cy, _hdcMask, rcImage.left, rcImage.top, SRCCOPY); for (int y = 0; y < _cy; y++) { int Offset = (y + rcInverted.top) * _cx; int MaskOffset = y * _cx; for (int x = 0; x < _cx; x++) { RGBQUAD* prgb = &_pargbImage[x + rcInverted.left + Offset]; if (pbMask[x + MaskOffset].rgbBlue != 0) { prgb->rgbReserved = 255; } else { *(DWORD*)prgb = 0; } } } SelectObject(hdcMem, hbmpOld); DeleteObject(hbmp); } DeleteDC(hdcMem); } if (!fSpare) { SetItemFlags(iImage, ILIF_ALPHA); _PreProcessImage(iImage); } } void ScaleAlpha(RGBQUAD* prgbImage, RECT* prc, int aScale) { int cx = RECTWIDTH(*prc); for (int y = prc->top; y < prc->bottom; y++) { int Offset = y * cx; for (int x = prc->left; x < prc->right; x++) { RGBQUAD* prgb = &prgbImage[x + Offset]; if (prgb->rgbReserved != 0) { prgb->rgbReserved = (BYTE)(prgb->rgbReserved / aScale); // New alpha prgb->rgbRed = ((prgb->rgbRed * prgb->rgbReserved) + 128) / 255; prgb->rgbGreen = ((prgb->rgbGreen * prgb->rgbReserved) + 128) / 255; prgb->rgbBlue = ((prgb->rgbBlue * prgb->rgbReserved) + 128) / 255; } } } } #define COLORBLEND_ALPHA 128 BOOL CImageList::Blend32(HDC hdcDst, int xDst, int yDst, int iImage, int cx, int cy, COLORREF rgb, UINT fStyle) { BITMAP bm; RECT rc; RECT rcSpare; RECT rcSpareInverted; int aScale; BOOL fBlendWithColor = FALSE; int r,g,b; // get bitmap info for source bitmap GetObject(_hbmImage, sizeof(bm), &bm); ASSERT(bm.bmBitsPixel==32); // get blend RGB if (rgb == CLR_DEFAULT) { rgb = GetSysColor(COLOR_HIGHLIGHT); fBlendWithColor = TRUE; r = GetRValue(rgb) * COLORBLEND_ALPHA; g = GetGValue(rgb) * COLORBLEND_ALPHA; b = GetBValue(rgb) * COLORBLEND_ALPHA; } // get blend factor as a fraction of 256 // only 50% or 25% is currently used. if ((fStyle & ILD_BLENDMASK) == ILD_BLEND50 || rgb == CLR_NONE) aScale = 2; else aScale = 4; GetImageRect(iImage, &rc); if (GetSpareImageRectInverted(&rcSpareInverted) && GetSpareImageRect(&rcSpare)) { BitBlt(_hdcImage, rcSpare.left, rcSpare.top, _cx, _cy, _hdcImage, rc.left, rc.top, SRCCOPY); BOOL fHasAlpha = (_GetItemFlags(iImage) & ILIF_ALPHA); if (!fHasAlpha) { _GenerateAlphaForImageUsingMask(iImage, TRUE); } // if blending with the destination, copy the dest to our work buffer if (rgb == CLR_NONE) { ScaleAlpha(_pargbImage, &rcSpareInverted, aScale); BLENDFUNCTION bf = {0}; bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; bf.BlendFlags = AC_MIRRORBITMAP | ((fStyle & ILD_DPISCALE)?AC_USE_HIGHQUALITYFILTER:0); GdiAlphaBlend(hdcDst, xDst, yDst, cx, cy, _hdcImage, rcSpare.left, rcSpare.top, _cx, _cy, bf); return FALSE; } else { if (fBlendWithColor) { for (int y = rcSpareInverted.top; y < rcSpareInverted.bottom; y++) { int Offset = y * _cx; for (int x = rcSpareInverted.left; x < rcSpareInverted.right; x++) { RGBQUAD* prgb = &_pargbImage[x + Offset]; if (prgb->rgbReserved > 128) { prgb->rgbRed = (prgb->rgbRed * COLORBLEND_ALPHA + r) / 255; prgb->rgbGreen = (prgb->rgbGreen * COLORBLEND_ALPHA + g) / 255; prgb->rgbBlue = (prgb->rgbBlue * COLORBLEND_ALPHA + b) / 255; } } } } else { ScaleAlpha(_pargbImage, &rcSpareInverted, aScale); } BitBlt(hdcDst, xDst, yDst, cx, cy, _hdcImage, rcSpare.left, rcSpare.top, SRCCOPY); return TRUE; } } return FALSE; } /* ** ImageList_Blend ** ** copy the source to the dest blended with the given color. ** top level function to decide what blend function to call */ BOOL CImageList::Blend(HDC hdcDst, int xDst, int yDst, int iImage, int cx, int cy, COLORREF rgb, UINT fStyle) { BOOL fRet = FALSE; BITMAP bm; RECT rc; int bpp = GetDeviceCaps(hdcDst, BITSPIXEL); GetObject(_hbmImage, sizeof(bm), &bm); GetImageRect(iImage, &rc); // // if _hbmImage is a DIBSection and we are on a HiColor device // the do a "real" blend // if (bm.bmBits && bm.bmBitsPixel <= 8 && (bpp > 8 || bm.bmBitsPixel==8)) { // blend from a 4bit or 8bit DIB BlendCT(hdcDst, xDst, yDst, rc.left, rc.top, cx, cy, rgb, fStyle); } else if (bm.bmBits && bm.bmBitsPixel == 16 && bpp > 8) { // blend from a 16bit 555 DIB Blend16(hdcDst, xDst, yDst, iImage, cx, cy, rgb, fStyle); } else if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { fRet = Blend32(hdcDst, xDst, yDst, iImage, cx, cy, rgb, fStyle); } else { // simulate a blend with a dither pattern. BlendDither(hdcDst, xDst, yDst, rc.left, rc.top, cx, cy, rgb, fStyle); } return fRet; } #define RGB_to_Gray(x) ((54 * GetRValue((x)) + 183 * GetGValue((x)) + 19 * GetBValue((x))) >> 8) void TrueSaturateBits(void* pvBitmapBits, int Amount, int cx, int cy) { ULONG* pulSrc = (ULONG*)pvBitmapBits; if ((cx > 0) && (cy > 0) && pulSrc) { for (int i = cx*cy - 1; i >= 0; i--) { /* Enable this if you need true saturation adjustment justmann 25-JAN-2001 ULONG ulR = GetRValue(*pulSrc); ULONG ulG = GetGValue(*pulSrc); ULONG ulB = GetBValue(*pulSrc); ulGray = (54 * ulR + 183 * ulG + 19 * ulB) >> 8; ULONG ulTemp = ulGray * (0xff - Amount); ulR = (ulR * Amount + ulTemp) >> 8; ulG = (ulG * Amount + ulTemp) >> 8; ulB = (ulB * Amount + ulTemp) >> 8; *pulSrc = (*pulSrc & 0xff000000) | RGB(R, G, B); */ ULONG ulGray = RGB_to_Gray(*pulSrc); *pulSrc = (*pulSrc & 0xff000000) | RGB(ulGray, ulGray, ulGray); pulSrc++; } } else { // This should never happen, if it does somebody has a bogus DIB section or does not // understand what width or height is! ASSERT(0); } } BOOL CImageList::_MaskStretchBlt(BOOL fStretch, int i, HDC hdcDst, int xDst, int yDst, int cxDst, int cyDst, HDC hdcImage, int xSrc, int ySrc, int cxSrc, int cySrc, int xMask, int yMask, DWORD dwRop) { BOOL fRet = TRUE; if (fStretch == FALSE) { fRet = MaskBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcImage, xSrc, ySrc, _hbmMask, xMask, yMask, dwRop); } else { // // we have some special cases: // // if the background color is black, we just do a AND then OR // if the background color is white, we just do a OR then AND // otherwise change source, then AND then OR // COLORREF clrTextSave = SetTextColor(hdcDst, CLR_BLACK); COLORREF clrBkSave = ::SetBkColor(hdcDst, CLR_WHITE); // we cant do white/black special cases if we munged the mask or image if (i != -1 && _clrBk == CLR_WHITE) { StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, _hdcMask, xMask, yMask, cxSrc, cySrc, ROP_DSno); StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcImage, xSrc, ySrc, cxSrc, cySrc, ROP_DSa); } else if (i != -1 && (_clrBk == CLR_BLACK || _clrBk == CLR_NONE)) { StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, _hdcMask, xMask, yMask, cxSrc, cySrc, ROP_DSa); StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcImage, xSrc, ySrc, cxSrc, cySrc, ROP_DSo); } else { // black out the source image. BitBlt(hdcImage, xSrc, ySrc, cxSrc, cySrc, _hdcMask, xMask, yMask, ROP_DSna); StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, _hdcMask, xMask, yMask, cxSrc, cySrc, ROP_DSa); StretchBlt(hdcDst, xDst, yDst, cxDst, cyDst, hdcImage, xSrc, ySrc, cxSrc, cySrc, ROP_DSo); if (i != -1) _ResetBkColor(i, i, _clrBk); } SetTextColor(hdcDst, clrTextSave); ::SetBkColor(hdcDst, clrBkSave); } return fRet; } BOOL CImageList::_StretchBlt(BOOL fStretch, HDC hdc, int x, int y, int cx, int cy, HDC hdcSrc, int xs, int ys, int cxs, int cys, DWORD dwRop) { if (fStretch) return StretchBlt(hdc, x, y, cx, cy, hdcSrc, xs, ys, cxs, cys, dwRop); return BitBlt(hdc, x, y, cx, cy, hdcSrc, xs, ys, dwRop); } HRESULT CImageList::Draw(IMAGELISTDRAWPARAMS* pimldp) { RECT rcImage; RECT rc; HBRUSH hbrT; BOOL fImage; HDC hdcMaskI; HDC hdcImageI; int xMask, yMask; int xImage, yImage; int cxSource, cySource; DWORD dwOldStretchBltMode; BOOL fStretch; BOOL fDPIScale = FALSE; IMAGELISTDRAWPARAMS imldp = {0}; if (pimldp->cbSize != sizeof(IMAGELISTDRAWPARAMS)) { if (pimldp->cbSize == IMAGELISTDRAWPARAMS_V3_SIZE) { memcpy(&imldp, pimldp, IMAGELISTDRAWPARAMS_V3_SIZE); imldp.cbSize = sizeof(IMAGELISTDRAWPARAMS); pimldp = &imldp; } else return E_INVALIDARG; } if (!IsImageListIndex(pimldp->i)) return E_INVALIDARG; // // If we need to use the mirrored imagelist, then let's set it. // if (_pimlMirror && (IS_DC_RTL_MIRRORED(pimldp->hdcDst))) { return _pimlMirror->Draw(pimldp); } ENTERCRITICAL; dwOldStretchBltMode = SetStretchBltMode(pimldp->hdcDst, COLORONCOLOR); GetImageRect(pimldp->i, &rcImage); rcImage.left += pimldp->xBitmap; rcImage.top += pimldp->yBitmap; if (pimldp->rgbBk == CLR_DEFAULT) pimldp->rgbBk = _clrBk; if (pimldp->rgbBk == CLR_NONE) pimldp->fStyle |= ILD_TRANSPARENT; if (pimldp->cx == 0) pimldp->cx = RECTWIDTH(rcImage); if (pimldp->cy == 0) pimldp->cy = RECTHEIGHT(rcImage); BOOL fImageHasAlpha = (_GetItemFlags(pimldp->i) & ILIF_ALPHA); again: cxSource = RECTWIDTH(rcImage); cySource = RECTHEIGHT(rcImage); if (pimldp->cx <= 0 || pimldp->cy <= 0) { // caller asked to draw no (or negative) pixels; that's easy! // Early-out this case so other parts of the drawing // don't get confused. goto exit; } if (pimldp->fStyle & ILD_DPISCALE) { CCDPIScaleX(&pimldp->cx); CCDPIScaleY(&pimldp->cy); fDPIScale = TRUE; } fStretch = (pimldp->fStyle & ILD_SCALE) || (fDPIScale); if (fStretch) { dwOldStretchBltMode = SetStretchBltMode(pimldp->hdcDst, HALFTONE); } hdcMaskI = _hdcMask; xMask = rcImage.left; yMask = rcImage.top; hdcImageI = _hdcImage; xImage = rcImage.left; yImage = rcImage.top; if (pimldp->fStyle & ILD_BLENDMASK) { // make a copy of the image, because we will have to modify it HDC hdcT = ImageList_GetWorkDC(pimldp->hdcDst, (_flags & ILC_COLORMASK) == ILC_COLOR32, pimldp->cx, pimldp->cy); if (hdcT) { hdcImageI = hdcT; xImage = 0; yImage = 0; // // blend with the destination // by "oring" the mask with a 50% dither mask // if (pimldp->rgbFg == CLR_NONE && hdcMaskI) { fImageHasAlpha = FALSE; if ((_flags & ILC_COLORMASK) == ILC_COLOR32 && !(pimldp->fStyle & ILD_MASK)) { // copy dest to our work buffer _StretchBlt(fStretch, hdcImageI, 0, 0, pimldp->cx, pimldp->cy, pimldp->hdcDst, pimldp->x, pimldp->y, cxSource, cySource, SRCCOPY); Blend32(hdcImageI, 0, 0, pimldp->i, pimldp->cx, pimldp->cy, pimldp->rgbFg, pimldp->fStyle); } else if ((_flags & ILC_COLORMASK) == ILC_COLOR16 && !(pimldp->fStyle & ILD_MASK)) { // copy dest to our work buffer _StretchBlt(fStretch, hdcImageI, 0, 0, pimldp->cx, pimldp->cy, pimldp->hdcDst, pimldp->x, pimldp->y, cxSource, cySource, SRCCOPY); // blend source into our work buffer Blend16(hdcImageI, 0, 0, pimldp->i, pimldp->cx, pimldp->cy, pimldp->rgbFg, pimldp->fStyle); pimldp->fStyle |= ILD_TRANSPARENT; } else { GetSpareImageRect(&rc); xMask = rc.left; yMask = rc.top; // copy the source image _StretchBlt(fStretch, hdcImageI, 0, 0, pimldp->cx, pimldp->cy, _hdcImage, rcImage.left, rcImage.top, cxSource, cySource, SRCCOPY); // make a dithered copy of the mask hbrT = (HBRUSH)SelectObject(hdcMaskI, g_hbrMonoDither); _StretchBlt(fStretch, hdcMaskI, rc.left, rc.top, pimldp->cx, pimldp->cy, _hdcMask, rcImage.left, rcImage.top, cxSource, cySource, ROP_PSo); SelectObject(hdcMaskI, hbrT); pimldp->fStyle |= ILD_TRANSPARENT; } } else { // blend source into our work buffer if (Blend(hdcImageI, 0, 0, pimldp->i, pimldp->cx, pimldp->cy, pimldp->rgbFg, pimldp->fStyle)) { fImageHasAlpha = (_flags & ILC_COLORMASK) == ILC_COLOR32; } } } } // is the source image from the image list (not hdcWork) fImage = hdcImageI == _hdcImage; if (pimldp->cbSize >= sizeof(IMAGELISTDRAWPARAMS) && pimldp->fState & ILS_GLOW || pimldp->fState & ILS_SHADOW || pimldp->fState & ILS_SATURATE || pimldp->fState & ILS_ALPHA) { int z; ULONG* pvBits; HDC hdcMem = CreateCompatibleDC(pimldp->hdcDst); HBITMAP hbmpOld; HBITMAP hbmp; BITMAPINFO bi = {0}; BLENDFUNCTION bf = {0}; DWORD dwAlphaAmount = 0x000000ff; COLORREF crAlphaColor = pimldp->crEffect; // Need to make this a selectable color int x, y; int xOffset, yOffset; SIZE size = {cxSource, cySource}; if (hdcMem) { if (pimldp->fState & ILS_SHADOW) { x = 5; // This is a "Blur fudge Factor" y = 5; // xOffset = -(DROP_SHADOW - x); yOffset = -(DROP_SHADOW - y); size.cx = pimldp->cx + 10; size.cy = pimldp->cy + 10; dwAlphaAmount = 0x00000050; crAlphaColor = RGB(0, 0, 0); } else if (pimldp->fState & ILS_GLOW) { xOffset = x = 10; yOffset = y = 10; size.cx = pimldp->cx + (GLOW_RADIUS * 2); size.cy = pimldp->cy + (GLOW_RADIUS * 2); } else if (pimldp->fState & ILS_ALPHA) { xOffset = x = 0; yOffset = y = 0; size.cx = pimldp->cx; size.cy = pimldp->cy; } bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = size.cx; bi.bmiHeader.biHeight = size.cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; hbmp = CreateDIBSection(hdcMem, &bi, DIB_RGB_COLORS, (VOID**)&pvBits, NULL, 0); if (hbmp) { hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp); ZeroMemory(pvBits, size.cx * size.cy); if (pimldp->fState & ILS_SHADOW || pimldp->fState & ILS_GLOW || pimldp->fState & ILS_ALPHA) { if (_hbmMask) { MaskBlt(hdcMem, pimldp->x, pimldp->y, size.cx, size.cy, hdcImageI, xImage, yImage, _hbmMask, xMask, yMask, 0xCCAA0000); } else if (pimldp->fState & ILS_SHADOW) { RECT rc = {x, y, size.cx, size.cy}; FillRectClr(hdcMem, &rc, RGB(0x0F, 0x0F, 0x0F)); // White so that it gets inverted into a shadow } else { BitBlt(hdcMem, x, y, size.cx, size.cy, hdcImageI, xImage, yImage, SRCCOPY); } int iTotalSize = size.cx * size.cy; if (pimldp->fState & ILS_ALPHA) { for (z = 0; z < iTotalSize; z++) { RGBQUAD* prgb = &((RGBQUAD*)pvBits)[z]; prgb->rgbReserved = (BYTE)(pimldp->Frame & 0xFF); prgb->rgbRed = ((prgb->rgbRed * prgb->rgbReserved) + 128) / 255; prgb->rgbGreen = ((prgb->rgbGreen * prgb->rgbReserved) + 128) / 255; prgb->rgbBlue = ((prgb->rgbBlue * prgb->rgbReserved) + 128) / 255; } } else { for (z = 0; z < iTotalSize; z++) { if (((PULONG)pvBits)[z] != 0) ((PULONG)pvBits)[z] = dwAlphaAmount; } BlurBitmap(pvBits, size.cx, size.cy, crAlphaColor); if (!(pimldp->fState & ILS_SHADOW)) { for (z = 0; z < iTotalSize; z++) { if (((PULONG)pvBits)[z] > 0x09000000) ((PULONG)pvBits)[z] = dwAlphaAmount; } BlurBitmap(pvBits, size.cx, size.cy, crAlphaColor); BlurBitmap(pvBits, size.cx, size.cy, crAlphaColor); } } bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; bf.BlendFlags = fDPIScale?AC_USE_HIGHQUALITYFILTER:0; // Do not mirror the bitmap. By this point it is correctly mirrored GdiAlphaBlend(pimldp->hdcDst, pimldp->x - xOffset, pimldp->y - yOffset, pimldp->cx, pimldp->cy, hdcMem, 0, 0, size.cx, size.cy, bf); } else { BitBlt(hdcMem, 0, 0, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, SRCCOPY); TrueSaturateBits(pvBits, pimldp->Frame, size.cx, size.cy); if (fImageHasAlpha) { bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 150; bf.AlphaFormat = AC_SRC_ALPHA; // Do not mirror the bitmap. By this point it is correctly mirrored GdiAlphaBlend(pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcMem, 0, 0, cxSource, cySource, bf); } else if (_hbmMask) { _MaskStretchBlt(fStretch, -1, hdcMem, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcMem, 0, 0, cxSource, cySource, xMask, yMask, 0xCCAA0000); } else { _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcMem, 0, 0, cxSource,cySource, SRCCOPY); } } SelectObject(hdcMem, hbmpOld); DeleteObject(hbmp); pimldp->fStyle |= ILD_TRANSPARENT; } DeleteDC(hdcMem); } if (pimldp->fState & ILS_SHADOW || pimldp->fState & ILS_GLOW) { if (pimldp->fState & ILS_SHADOW) { pimldp->fState &= ~ILS_SHADOW; //pimldp->x -= DROP_SHADOW; //pimldp->y -= DROP_SHADOW; } else { pimldp->fState &= ~ILS_GLOW; } goto again; } } else if ((pimldp->fStyle & ILD_MASK) && hdcMaskI) { // // ILD_MASK means draw the mask only // DWORD dwRop; ASSERT(GetTextColor(pimldp->hdcDst) == CLR_BLACK); ASSERT(::GetBkColor(pimldp->hdcDst) == CLR_WHITE); if (pimldp->fStyle & ILD_ROP) dwRop = pimldp->dwRop; else if (pimldp->fStyle & ILD_TRANSPARENT) dwRop = SRCAND; else dwRop = SRCCOPY; _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcMaskI, xMask, yMask, cxSource, cySource, dwRop); } else if (fImageHasAlpha && // this image has alpha !(pimldp->fStyle & ILD_PRESERVEALPHA)) // But not if we're trying to preserve it. { if (!(pimldp->fStyle & ILD_TRANSPARENT)) { COLORREF clr = pimldp->rgbBk; if (clr == CLR_DEFAULT) clr = _clrBk; RECT rc = {pimldp->x, pimldp->y, pimldp->x + pimldp->cx, pimldp->y + pimldp->cy}; FillRectClr(pimldp->hdcDst, &rc, clr); } BLENDFUNCTION bf = {0}; bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; bf.BlendFlags = AC_MIRRORBITMAP | (fDPIScale?AC_USE_HIGHQUALITYFILTER:0); GdiAlphaBlend(pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, bf); } else if (pimldp->fStyle & ILD_IMAGE) { COLORREF clrBk = ::GetBkColor(hdcImageI); DWORD dwRop; if (pimldp->rgbBk != CLR_DEFAULT) { ::SetBkColor(hdcImageI, pimldp->rgbBk); } if (pimldp->fStyle & ILD_ROP) dwRop = pimldp->dwRop; else dwRop = SRCCOPY; _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, dwRop); ::SetBkColor(hdcImageI, clrBk); } else if ((pimldp->fStyle & ILD_TRANSPARENT) && hdcMaskI) { _MaskStretchBlt(fStretch, fImage?pimldp->i:-1,pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, xMask, yMask, 0xCCAA0000); } else if (fImage && pimldp->rgbBk == _clrBk && _fSolidBk) { _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, SRCCOPY); } else if (hdcMaskI && !fImageHasAlpha) { if (fImage && ((pimldp->rgbBk == _clrBk && !_fSolidBk) || GetNearestColor32(hdcImageI, pimldp->rgbBk) != pimldp->rgbBk)) { // make a copy of the image, because we will have to modify it hdcImageI = ImageList_GetWorkDC(pimldp->hdcDst, (_flags & ILC_COLORMASK) == ILC_COLOR32, pimldp->cx, pimldp->cy); xImage = 0; yImage = 0; fImage = FALSE; BitBlt(hdcImageI, 0, 0, pimldp->cx, pimldp->cy, _hdcImage, rcImage.left, rcImage.top, SRCCOPY); } SetBrushOrgEx(hdcImageI, xImage-pimldp->x, yImage-pimldp->y, NULL); hbrT = SelectBrush(hdcImageI, CreateSolidBrush(pimldp->rgbBk)); BitBlt(hdcImageI, xImage, yImage, pimldp->cx, pimldp->cy, hdcMaskI, xMask, yMask, ROP_PatMask); DeleteObject(SelectBrush(hdcImageI, hbrT)); SetBrushOrgEx(hdcImageI, 0, 0, NULL); _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, SRCCOPY); if (fImage) _ResetBkColor(pimldp->i, pimldp->i, _clrBk); } else { _StretchBlt(fStretch, pimldp->hdcDst, pimldp->x, pimldp->y, pimldp->cx, pimldp->cy, hdcImageI, xImage, yImage, cxSource, cySource, SRCCOPY); } // // now deal with a overlay image, use the minimal bounding rect (and flags) // we computed in ImageList_SetOverlayImage() // if (pimldp->fStyle & ILD_OVERLAYMASK) { int n = OVERLAYMASKTOINDEX(pimldp->fStyle); if (n < NUM_OVERLAY_IMAGES) { pimldp->i = _aOverlayIndexes[n]; if (!fImageHasAlpha) pimldp->fStyle &= ~ILD_PRESERVEALPHA; if (pimldp->fStyle & ILD_PRESERVEALPHA && !(_GetItemFlags(pimldp->i) & ILIF_ALPHA)) { _GenerateAlphaForImageUsingMask(pimldp->i, FALSE); } fImageHasAlpha = (_GetItemFlags(pimldp->i) & ILIF_ALPHA); GetImageRect(pimldp->i, &rcImage); int xOverlay = _aOverlayX[n]; int yOverlay = _aOverlayY[n]; int cxOverlay = _aOverlayDX[n]; int cyOverlay = _aOverlayDY[n]; if (fDPIScale) { CCDPIScaleX(&xOverlay ); CCDPIScaleY(&yOverlay ); } pimldp->cx = cxOverlay; pimldp->cy = cyOverlay; pimldp->x += xOverlay; pimldp->y += yOverlay; rcImage.left += _aOverlayX[n] + pimldp->xBitmap; rcImage.top += _aOverlayY[n] + pimldp->yBitmap; rcImage.right = rcImage.left + _aOverlayDX[n]; rcImage.bottom = rcImage.top + _aOverlayDY[n]; pimldp->fStyle &= ILD_MASK; pimldp->fStyle |= ILD_TRANSPARENT; pimldp->fStyle |= (fDPIScale?ILD_DPISCALE:0); pimldp->fStyle |= _aOverlayF[n]; if (fImageHasAlpha) pimldp->fStyle &= ~(ILD_IMAGE); if (pimldp->cx > 0 && pimldp->cy > 0) goto again; } } if (!fImage) { ImageList_ReleaseWorkDC(hdcImageI); } exit: SetStretchBltMode(pimldp->hdcDst, dwOldStretchBltMode); LEAVECRITICAL; return S_OK; } HRESULT CImageList::GetImageInfo(int i, IMAGEINFO * pImageInfo) { RIPMSG(pImageInfo != NULL, "ImageList_GetImageInfo: Invalid NULL pointer"); RIPMSG(IsImageListIndex(i), "ImageList_GetImageInfo: Invalid image index %d", i); if (!pImageInfo || !IsImageListIndex(i)) return E_POINTER; pImageInfo->hbmImage = _hbmImage; pImageInfo->hbmMask = _hbmMask; return GetImageRect(i, &pImageInfo->rcImage); } // // Parameter: // i -- -1 to add // HRESULT CImageList::_ReplaceIcon(int i, HICON hIcon, int* pi) { HICON hIconT = hIcon; RECT rc; HRESULT hr = S_OK; TraceMsg(TF_IMAGELIST, "ImageList_ReplaceIcon"); *pi = -1; // be win95 compatible if (i < -1) return E_INVALIDARG; // // re-size the icon (iff needed) by calling CopyImage // hIcon = (HICON)CopyImage(hIconT, IMAGE_ICON, _cx, _cy, LR_COPYFROMRESOURCE | LR_COPYRETURNORG); if (hIcon == NULL) return E_OUTOFMEMORY; // // alocate a slot for the icon // if (i == -1) hr = _Add(NULL,NULL,1,0,0,&i); if (i == -1) goto exit; ENTERCRITICAL; // // now draw it into the image bitmaps // hr = GetImageRect(i, &rc); if (FAILED(hr)) goto LeaveCritical; if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { BOOL fSuccess = FALSE; ICONINFO io; if (GetIconInfo(hIcon, &io)) { BITMAP bm; if (GetObject(io.hbmColor, sizeof(bm), &bm)) { if (bm.bmBitsPixel == 32) { HDC h = CreateCompatibleDC(_hdcImage); if (h) { HBITMAP hbmpOld = (HBITMAP)SelectObject(h, io.hbmColor); BitBlt(_hdcImage, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), h, 0, 0, SRCCOPY); if (_HasAlpha(i)) { SetItemFlags(i, ILIF_ALPHA); _PreProcessImage(i); fSuccess = TRUE; } SelectObject(h, hbmpOld); DeleteDC(h); } } } DeleteObject(io.hbmColor); DeleteObject(io.hbmMask); } if (!fSuccess) { // If it doesn't have alpha or we can't get info SetItemFlags(i, 0); } } if (_GetItemFlags(i) == 0) { FillRect(_hdcImage, &rc, _hbrBk); DrawIconEx(_hdcImage, rc.left, rc.top, hIcon, 0, 0, 0, NULL, DI_NORMAL); } if (_hdcMask) DrawIconEx(_hdcMask, rc.left, rc.top, hIcon, 0, 0, 0, NULL, DI_MASK); hr = S_OK; *pi = i; LeaveCritical: LEAVECRITICAL; exit: // // if we had user size a new icon, delete it. // if (hIcon != hIconT) DestroyIcon(hIcon); return hr; } HRESULT CImageList::ReplaceIcon(int i, HICON hIcon, int* pi) { // Let's add it first to the mirrored image list, if one exists if (_pimlMirror) { HICON hIconT = CopyIcon(hIcon); if (hIconT) { MirrorIcon(&hIconT, NULL); _pimlMirror->_ReplaceIcon(i, hIconT, pi); DestroyIcon(hIconT); } } return _ReplaceIcon(i, hIcon,pi); } // make a dithered copy of the source image in the destination image. // allows placing of the final image in the destination. HRESULT CImageList::CopyDitherImage(WORD iDst, int xDst, int yDst, IUnknown* punkSrc, int iSrc, UINT fStyle) { IImageList* pux; HRESULT hr = punkSrc->QueryInterface(IID_PPV_ARG(IImageList, &pux)); if (FAILED(hr)) return hr; RECT rc; int x, y; GetImageRect(iDst, &rc); // coordinates in destination image list x = xDst + rc.left; y = yDst + rc.top; fStyle &= ILD_OVERLAYMASK; WimpyDrawEx(pux, iSrc, _hdcImage, x, y, 0, 0, CLR_DEFAULT, CLR_NONE, ILD_IMAGE | fStyle); // // dont dither the mask on a hicolor device, we will draw the image // with blending while dragging. // if (_hdcMask && GetScreenDepth() > 8) { WimpyDrawEx(pux, iSrc, _hdcMask, x, y, 0, 0, CLR_NONE, CLR_NONE, ILD_MASK | fStyle); } else if (_hdcMask) { WimpyDrawEx(pux, iSrc, _hdcMask, x, y, 0, 0, CLR_NONE, CLR_NONE, ILD_BLEND50|ILD_MASK | fStyle); } if ((_flags & ILC_COLORMASK) == ILC_COLOR32) { SetItemFlags(iDst, _HasAlpha(iDst)?ILIF_ALPHA:0); } _ResetBkColor(iDst, iDst+1, _clrBk); pux->Release(); return hr; } // // ImageList_CopyBitmap // // Worker function for ImageList_Duplicate. // // Given a bitmap and an hdc, creates and returns a copy of the passed in bitmap. // HBITMAP CImageList::_CopyBitmap(HBITMAP hbm, HDC hdc) { ASSERT(hbm); BITMAP bm; HBITMAP hbmCopy = NULL; if (GetObject(hbm, sizeof(bm), &bm) == sizeof(bm)) { ENTERCRITICAL; if (hbmCopy = CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight)) { CImageList::SelectDstBitmap(hbmCopy); BitBlt(g_hdcDst, 0, 0, bm.bmWidth, bm.bmHeight, hdc, 0, 0, SRCCOPY); CImageList::SelectDstBitmap(NULL); } LEAVECRITICAL; } return hbmCopy; } HBITMAP CImageList::_CopyDIBBitmap(HBITMAP hbm, HDC hdc, RGBQUAD** ppargb) { ASSERT(hbm); BITMAP bm; HBITMAP hbmCopy = NULL; if (GetObject(hbm, sizeof(bm), &bm) == sizeof(bm)) { ENTERCRITICAL; hbmCopy = _CreateBitmap(bm.bmWidth, bm.bmHeight, ppargb); if (hbmCopy) { CImageList::SelectDstBitmap(hbmCopy); BitBlt(g_hdcDst, 0, 0, bm.bmWidth, bm.bmHeight, hdc, 0, 0, SRCCOPY); CImageList::SelectDstBitmap(NULL); } LEAVECRITICAL; } return hbmCopy; } HRESULT CImageList::Clone(REFIID riid, void** ppv) { HBITMAP hbmImageI; HBITMAP hbmMaskI = NULL; RGBQUAD* pargbImageI; HDSA dsaFlags = NULL; HRESULT hr = S_OK; CImageList* pimlCopy = NULL; *ppv = NULL; ENTERCRITICAL; hbmImageI = _CopyDIBBitmap(_hbmImage, _hdcImage, &pargbImageI); if (!hbmImageI) hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) { if (_hdcMask) { hbmMaskI = _CopyBitmap(_hbmMask, _hdcMask); if (!hbmMaskI) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr) && (_flags & ILC_COLORMASK) == ILC_COLOR32) { dsaFlags = DSA_Create(sizeof(DWORD), _cGrow); if (dsaFlags) { DWORD dw; for (int i = 0; i < _cImage; i++) { DSA_GetItem(_dsaFlags, i, &dw); if (!DSA_SetItem(dsaFlags, i, &dw)) { hr = E_OUTOFMEMORY; break; } } } } if (SUCCEEDED(hr)) { pimlCopy = CImageList::Create(_cx, _cy, _flags, 0, _cGrow); if (pimlCopy) { // Slam in our bitmap copies and delete the old ones SelectObject(pimlCopy->_hdcImage, hbmImageI); CImageList::_DeleteBitmap(pimlCopy->_hbmImage); if (pimlCopy->_hdcMask) { SelectObject(pimlCopy->_hdcMask, hbmMaskI); CImageList::_DeleteBitmap(pimlCopy->_hbmMask); } if (pimlCopy->_dsaFlags) DSA_Destroy(pimlCopy->_dsaFlags); pimlCopy->_dsaFlags = dsaFlags; pimlCopy->_hbmImage = hbmImageI; pimlCopy->_pargbImage = pargbImageI; pimlCopy->_hbmMask = hbmMaskI; // Make sure other info is correct pimlCopy->_cImage = _cImage; pimlCopy->_cAlloc = _cAlloc; pimlCopy->_cStrip = _cStrip; pimlCopy->_clrBlend = _clrBlend; pimlCopy->_clrBk = _clrBk; // Delete the old brush and create the correct one if (pimlCopy->_hbrBk) DeleteObject(pimlCopy->_hbrBk); if (pimlCopy->_clrBk == CLR_NONE) { pimlCopy->_hbrBk = (HBRUSH)GetStockObject(BLACK_BRUSH); pimlCopy->_fSolidBk = TRUE; } else { pimlCopy->_hbrBk = CreateSolidBrush(pimlCopy->_clrBk); pimlCopy->_fSolidBk = GetNearestColor32(pimlCopy->_hdcImage, pimlCopy->_clrBk) == pimlCopy->_clrBk; } } } LEAVECRITICAL; } if (FAILED(hr)) { if (hbmImageI) CImageList::_DeleteBitmap(hbmImageI); if (hbmMaskI) CImageList::_DeleteBitmap(hbmMaskI); if (dsaFlags) DSA_Destroy(dsaFlags); } if (pimlCopy) { hr = pimlCopy->QueryInterface(riid, ppv); pimlCopy->Release(); } return hr; } void CImageList::_Merge(IImageList* pux, int i, int dx, int dy) { if (_hdcMask) { IImageListPriv* puxp; if (SUCCEEDED(pux->QueryInterface(IID_PPV_ARG(IImageListPriv, &puxp)))) { HDC hdcMaskI; if (SUCCEEDED(puxp->GetPrivateGoo(NULL, NULL, NULL, &hdcMaskI)) && hdcMaskI) { RECT rcMerge; int cxI, cyI; pux->GetIconSize(&cxI, &cyI); UINT uFlags = 0; puxp->GetFlags(&uFlags); pux->GetImageRect(i, &rcMerge); BitBlt(_hdcMask, dx, dy, cxI, cyI, hdcMaskI, rcMerge.left, rcMerge.top, SRCAND); } puxp->Release(); } } WimpyDraw(pux, i, _hdcImage, dx, dy, ILD_TRANSPARENT | ILD_PRESERVEALPHA); if ((_flags & ILC_COLORMASK) == ILC_COLOR32) SetItemFlags(i, _HasAlpha(i)? ILIF_ALPHA : 0); } HRESULT CImageList::_Merge(int i1, IUnknown* punk, int i2, int dx, int dy, CImageList** ppiml) { CImageList* pimlNew = NULL; IImageListPriv* puxp; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IImageListPriv, &puxp)); if (SUCCEEDED(hr)) { IImageList* pux; hr = punk->QueryInterface(IID_PPV_ARG(IImageList, &pux)); if (SUCCEEDED(hr)) { RECT rcNew; RECT rc1; RECT rc2; int cxI, cyI; int c1, c2; UINT wFlags; UINT uSrcFlags; puxp->GetFlags(&uSrcFlags); pux->GetIconSize(&cxI, &cyI); ENTERCRITICAL; SetRect(&rc1, 0, 0, _cx, _cy); SetRect(&rc2, dx, dy, cxI + dx, cyI + dy); UnionRect(&rcNew, &rc1, &rc2); cxI = RECTWIDTH(rcNew); cyI = RECTHEIGHT(rcNew); // // If one of images are shared, create a shared image. // wFlags = (_flags | uSrcFlags) & ~ILC_COLORMASK; c1 = (_flags & ILC_COLORMASK); c2 = (uSrcFlags & ILC_COLORMASK); if ((c1 == 16 || c1 == 32) && c2 == ILC_COLORDDB) { c2 = c1; } wFlags |= max(c1,c2); pimlNew = CImageList::Create(cxI, cyI, ILC_MASK|wFlags, 1, 0); if (pimlNew) { pimlNew->_cImage++; if (pimlNew->_hdcMask) PatBlt(pimlNew->_hdcMask, 0, 0, cxI, cyI, WHITENESS); PatBlt(pimlNew->_hdcImage, 0, 0, cxI, cyI, BLACKNESS); pimlNew->_Merge(SAFECAST(this, IImageList*), i1, rc1.left - rcNew.left, rc1.top - rcNew.top); pimlNew->_Merge(pux, i2, rc2.left - rcNew.left, rc2.top - rcNew.top); } else hr = E_OUTOFMEMORY; LEAVECRITICAL; pux->Release(); } puxp->Release(); } *ppiml = pimlNew; return hr; } HRESULT CImageList::Merge(int i1, IUnknown* punk, int i2, int dx, int dy, REFIID riid, void** ppv) { CImageList* piml; HRESULT hr = _Merge(i1, punk, i2, dx, dy, &piml); if (piml) { hr = piml->QueryInterface(riid, ppv); piml->Release(); } return hr; } HRESULT CImageList::GetImageRectInverted(int i, RECT * prcImage) { int x, y; ASSERT(prcImage); ASSERT(_cStrip == 1); // If not, modify below to accomodate if (!prcImage || !IsImageListIndex(i)) return E_FAIL; x = 0; y = (_cy * _cAlloc) - (_cy * i) - _cy; SetRect(prcImage, x, y, x + _cx, y + _cy); return S_OK; } HRESULT CImageList::GetImageRect(int i, RECT * prcImage) { int x, y; ASSERT(prcImage); if (!prcImage || !IsImageListIndex(i)) return E_FAIL; x = _cx * (i % _cStrip); y = _cy * (i / _cStrip); SetRect(prcImage, x, y, x + _cx, y + _cy); return S_OK; } BOOL CImageList::GetSpareImageRect(RECT * prcImage) { BOOL fRet = FALSE; if (_cImage < _cAlloc) { // special hacking to use the one scratch image at tail of list :) _cImage++; fRet = (S_OK == GetImageRect(_cImage-1, prcImage)); _cImage--; } return fRet; } BOOL CImageList::GetSpareImageRectInverted(RECT * prcImage) { BOOL fRet = FALSE; if (_cImage < _cAlloc) { // special hacking to use the one scratch image at tail of list :) _cImage++; fRet = (S_OK == GetImageRectInverted(_cImage-1, prcImage)); _cImage--; } return fRet; } // Drag Drop // copy an image from one imagelist to another at x,y within iDst in pimlDst. // pimlDst's image size should be larger than pimlSrc void CImageList::_CopyOneImage(int iDst, int x, int y, CImageList* piml, int iSrc) { RECT rcSrc, rcDst; piml->GetImageRect(iSrc, &rcSrc); GetImageRect(iDst, &rcDst); if (piml->_hdcMask && _hdcMask) { BitBlt(_hdcMask, rcDst.left + x, rcDst.top + y, piml->_cx, piml->_cy, piml->_hdcMask, rcSrc.left, rcSrc.top, SRCCOPY); } BitBlt(_hdcImage, rcDst.left + x, rcDst.top + y, piml->_cx, piml->_cy, piml->_hdcImage, rcSrc.left, rcSrc.top, SRCCOPY); if ((_flags & ILC_COLORMASK) == ILC_COLOR32) SetItemFlags(iDst, _HasAlpha(iDst)? ILIF_ALPHA : 0); } // // Cached bitmaps that we use during drag&drop. We re-use those bitmaps // across multiple drag session as far as the image size is the same. // struct DRAGRESTOREBMP { int BitsPixel; HBITMAP hbmOffScreen; HBITMAP hbmRestore; SIZE sizeRestore; } g_drb = { 0, NULL, NULL, {-1,-1} }; BOOL CImageList::CreateDragBitmaps() { HDC hdc; hdc = GetDC(NULL); if (_cx != g_drb.sizeRestore.cx || _cy != g_drb.sizeRestore.cy || GetDeviceCaps(hdc, BITSPIXEL) != g_drb.BitsPixel) { ImageList_DeleteDragBitmaps(); g_drb.BitsPixel = GetDeviceCaps(hdc, BITSPIXEL); g_drb.sizeRestore.cx = _cx; g_drb.sizeRestore.cy = _cy; g_drb.hbmRestore = CreateColorBitmap(g_drb.sizeRestore.cx, g_drb.sizeRestore.cy); g_drb.hbmOffScreen = CreateColorBitmap(g_drb.sizeRestore.cx * 2 - 1, g_drb.sizeRestore.cy * 2 - 1); if (!g_drb.hbmRestore || !g_drb.hbmOffScreen) { ImageList_DeleteDragBitmaps(); ReleaseDC(NULL, hdc); return FALSE; } } ReleaseDC(NULL, hdc); return TRUE; } void ImageList_DeleteDragBitmaps() { if (g_drb.hbmRestore) { CImageList::_DeleteBitmap(g_drb.hbmRestore); g_drb.hbmRestore = NULL; } if (g_drb.hbmOffScreen) { CImageList::_DeleteBitmap(g_drb.hbmOffScreen); g_drb.hbmOffScreen = NULL; } g_drb.sizeRestore.cx = -1; g_drb.sizeRestore.cy = -1; } // // Drag context. We don't reuse none of them across two different // drag sessions. I'm planning to allocate it for each session // to minimize critical sections. // struct DRAGCONTEXT { CImageList* pimlDrag; // Image to be drawin while dragging IImageList* puxCursor; // Overlap cursor image CImageList* pimlDither; // Dithered image IImageList* puxDragImage; // The context of the drag. int iCursor; // Image index of the cursor POINT ptDrag; // current drag position (hwndDC coords) POINT ptDragHotspot; POINT ptCursor; BOOL fDragShow; BOOL fHiColor; HWND hwndDC; } g_dctx = { (CImageList*)NULL, (CImageList*)NULL, (CImageList*)NULL, (IImageList*)NULL, -1, {0, 0}, {0, 0}, {0, 0}, FALSE, FALSE, (HWND)NULL }; HDC ImageList_GetDragDC() { HDC hdc = GetDCEx(g_dctx.hwndDC, NULL, DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE); // // If hdc is mirrored then mirror the 2 globals DCs. // if (IS_DC_RTL_MIRRORED(hdc)) { SET_DC_RTL_MIRRORED(g_hdcDst); SET_DC_RTL_MIRRORED(g_hdcSrc); } return hdc; } void ImageList_ReleaseDragDC(HDC hdc) { // // If the hdc is mirrored then unmirror the 2 globals DCs. // if (IS_DC_RTL_MIRRORED(hdc)) { SET_DC_LAYOUT(g_hdcDst, 0); SET_DC_LAYOUT(g_hdcSrc, 0); } ReleaseDC(g_dctx.hwndDC, hdc); } // // x, y -- Specifies the initial cursor position in the coords of hwndLock, // which is specified by the previous ImageList_StartDrag call. // HRESULT CImageList::DragMove(int x, int y) { int IncOne = 0; ENTERCRITICAL; if (g_dctx.fDragShow) { RECT rcOld, rcNew, rcBounds; int dx, dy; dx = x - g_dctx.ptDrag.x; dy = y - g_dctx.ptDrag.y; rcOld.left = g_dctx.ptDrag.x - g_dctx.ptDragHotspot.x; rcOld.top = g_dctx.ptDrag.y - g_dctx.ptDragHotspot.y; rcOld.right = rcOld.left + g_drb.sizeRestore.cx; rcOld.bottom = rcOld.top + g_drb.sizeRestore.cy; rcNew = rcOld; OffsetRect(&rcNew, dx, dy); if (!IntersectRect(&rcBounds, &rcOld, &rcNew)) { // // No intersection. Simply hide the old one and show the new one. // ImageList_DragShowNolock(FALSE); g_dctx.ptDrag.x = x; g_dctx.ptDrag.y = y; ImageList_DragShowNolock(TRUE); } else { // // Some intersection. // HDC hdcScreen; int cx, cy; UnionRect(&rcBounds, &rcOld, &rcNew); hdcScreen = ImageList_GetDragDC(); if (hdcScreen) { // // If the DC is RTL mirrored, then restrict the // screen bitmap not to go beyond the screen since // we will end up copying the wrong bits from the // hdcScreen to the hbmOffScreen when the DC is mirrored. // GDI will skip invalid screen coord from the screen into // the destination bitmap. This will result in copying un-init // bits back to the screen (since the screen is mirrored). // [samera] // if (IS_DC_RTL_MIRRORED(hdcScreen)) { RECT rcWindow; GetWindowRect(g_dctx.hwndDC, &rcWindow); rcWindow.right -= rcWindow.left; if (rcBounds.right > rcWindow.right) { rcBounds.right = rcWindow.right; } if (rcBounds.left < 0) { rcBounds.left = 0; } } cx = rcBounds.right - rcBounds.left; cy = rcBounds.bottom - rcBounds.top; // // Copy the union rect from the screen to hbmOffScreen. // CImageList::SelectDstBitmap(g_drb.hbmOffScreen); BitBlt(g_hdcDst, 0, 0, cx, cy, hdcScreen, rcBounds.left, rcBounds.top, SRCCOPY); // // Hide the cursor on the hbmOffScreen by copying hbmRestore. // CImageList::SelectSrcBitmap(g_drb.hbmRestore); BitBlt(g_hdcDst, rcOld.left - rcBounds.left, rcOld.top - rcBounds.top, g_drb.sizeRestore.cx, g_drb.sizeRestore.cy, g_hdcSrc, 0, 0, SRCCOPY); // // Copy the original screen bits to hbmRestore // BitBlt(g_hdcSrc, 0, 0, g_drb.sizeRestore.cx, g_drb.sizeRestore.cy, g_hdcDst, rcNew.left - rcBounds.left, rcNew.top - rcBounds.top, SRCCOPY); // // Draw the image on hbmOffScreen // if (g_dctx.fHiColor) { WimpyDrawEx(SAFECAST(g_dctx.pimlDrag, IImageList*), 0, g_hdcDst, rcNew.left - rcBounds.left + IncOne, rcNew.top - rcBounds.top, 0, 0, CLR_NONE, CLR_NONE, ILD_BLEND50); if (g_dctx.puxCursor) { WimpyDraw(g_dctx.puxCursor, g_dctx.iCursor, g_hdcDst, rcNew.left - rcBounds.left + g_dctx.ptCursor.x + IncOne, rcNew.top - rcBounds.top + g_dctx.ptCursor.y, ILD_NORMAL); } } else { WimpyDraw(SAFECAST(g_dctx.pimlDrag, IImageList*), 0, g_hdcDst, rcNew.left - rcBounds.left + IncOne, rcNew.top - rcBounds.top, ILD_NORMAL); } // // Copy the hbmOffScreen back to the screen. // BitBlt(hdcScreen, rcBounds.left, rcBounds.top, cx, cy, g_hdcDst, 0, 0, SRCCOPY); ImageList_ReleaseDragDC(hdcScreen); } g_dctx.ptDrag.x = x; g_dctx.ptDrag.y = y; } } LEAVECRITICAL; return S_OK; } HRESULT CImageList::BeginDrag(int iTrack, int dxHotspot, int dyHotspot) { HRESULT hr = E_ACCESSDENIED; ENTERCRITICAL; if (!g_dctx.pimlDrag) { UINT newflags; int cxI = 0, cyI = 0; g_dctx.fDragShow = FALSE; g_dctx.hwndDC = NULL; g_dctx.fHiColor = GetScreenDepth() > 8; newflags = _flags|ILC_SHARED; if (g_dctx.fHiColor) { UINT uColorFlag = ILC_COLOR16; if (GetScreenDepth() == 32 || GetScreenDepth() == 24) { uColorFlag = ILC_COLOR32; } newflags = (newflags & ~ILC_COLORMASK) | uColorFlag; } g_dctx.pimlDither = CImageList::Create(_cx, _cy, newflags, 1, 0); if (g_dctx.pimlDither) { g_dctx.pimlDither->_cImage++; g_dctx.ptDragHotspot.x = dxHotspot; g_dctx.ptDragHotspot.y = dyHotspot; g_dctx.pimlDither->_CopyOneImage(0, 0, 0, this, iTrack); hr = ImageList_SetDragImage(NULL, 0, dxHotspot, dyHotspot)? S_OK : E_FAIL; } } LEAVECRITICAL; return hr; } HRESULT CImageList::DragEnter(HWND hwndLock, int x, int y) { HRESULT hr = S_FALSE; hwndLock = hwndLock ? hwndLock : GetDesktopWindow(); ENTERCRITICAL; if (!g_dctx.hwndDC) { g_dctx.hwndDC = hwndLock; g_dctx.ptDrag.x = x; g_dctx.ptDrag.y = y; ImageList_DragShowNolock(TRUE); hr = S_OK; } LEAVECRITICAL; return hr; } HRESULT CImageList::DragLeave(HWND hwndLock) { HRESULT hr = S_FALSE; hwndLock = hwndLock ? hwndLock : GetDesktopWindow(); ENTERCRITICAL; if (g_dctx.hwndDC == hwndLock) { ImageList_DragShowNolock(FALSE); g_dctx.hwndDC = NULL; hr = S_OK; } LEAVECRITICAL; return hr; } HRESULT CImageList::DragShowNolock(BOOL fShow) { HDC hdcScreen; int x, y; int IncOne = 0; x = g_dctx.ptDrag.x - g_dctx.ptDragHotspot.x; y = g_dctx.ptDrag.y - g_dctx.ptDragHotspot.y; if (!g_dctx.pimlDrag) return E_ACCESSDENIED; // // REVIEW: Why this block is in the critical section? We are supposed // to have only one dragging at a time, aren't we? // ENTERCRITICAL; if (fShow && !g_dctx.fDragShow) { hdcScreen = ImageList_GetDragDC(); CImageList::SelectSrcBitmap(g_drb.hbmRestore); BitBlt(g_hdcSrc, 0, 0, g_drb.sizeRestore.cx, g_drb.sizeRestore.cy, hdcScreen, x, y, SRCCOPY); if (g_dctx.fHiColor) { WimpyDrawEx(SAFECAST(g_dctx.pimlDrag, IImageList*), 0, hdcScreen, x + IncOne, y, 0, 0, CLR_NONE, CLR_NONE, ILD_BLEND50); if (g_dctx.puxCursor) { WimpyDraw(g_dctx.puxCursor, g_dctx.iCursor, hdcScreen, x + g_dctx.ptCursor.x + IncOne, y + g_dctx.ptCursor.y, ILD_NORMAL); } } else { WimpyDraw(SAFECAST(g_dctx.pimlDrag, IImageList*), 0, hdcScreen, x + IncOne, y, ILD_NORMAL); } ImageList_ReleaseDragDC(hdcScreen); } else if (!fShow && g_dctx.fDragShow) { hdcScreen = ImageList_GetDragDC(); CImageList::SelectSrcBitmap(g_drb.hbmRestore); BitBlt(hdcScreen, x, y, g_drb.sizeRestore.cx, g_drb.sizeRestore.cy, g_hdcSrc, 0, 0, SRCCOPY); ImageList_ReleaseDragDC(hdcScreen); } g_dctx.fDragShow = fShow; LEAVECRITICAL; return S_OK; } // this hotspot stuff is broken in design BOOL ImageList_MergeDragImages(int dxHotspot, int dyHotspot) { CImageList* pimlNew; BOOL fRet = FALSE; if (g_dctx.pimlDither) { if (g_dctx.puxCursor) { IImageList* pux = NULL; IImageListPriv* puxpCursor; if (SUCCEEDED(g_dctx.puxCursor->QueryInterface(IID_PPV_ARG(IImageListPriv, &puxpCursor)))) { // If the cursor list has a mirrored list, let's use that. if (FAILED(puxpCursor->GetMirror(IID_PPV_ARG(IImageList, &pux)))) { pux = g_dctx.puxCursor; if (pux) pux->AddRef(); } puxpCursor->Release(); } g_dctx.pimlDither->_Merge(0, pux, g_dctx.iCursor, dxHotspot, dyHotspot, &pimlNew); if (pimlNew && pimlNew->CreateDragBitmaps()) { // WARNING: Don't destroy pimlDrag if it is pimlDither. if (g_dctx.pimlDrag && (g_dctx.pimlDrag != g_dctx.pimlDither)) { g_dctx.pimlDrag->Release(); } g_dctx.pimlDrag = pimlNew; fRet = TRUE; } pux->Release(); } else { if (g_dctx.pimlDither->CreateDragBitmaps()) { g_dctx.pimlDrag = g_dctx.pimlDither; fRet = TRUE; } } } else { // not an error case if both aren't set yet // only an error if we actually tried the merge and failed fRet = TRUE; } return fRet; } BOOL ImageList_SetDragImage(HIMAGELIST piml, int i, int dxHotspot, int dyHotspot) { BOOL fVisible = g_dctx.fDragShow; BOOL fRet; ENTERCRITICAL; if (fVisible) ImageList_DragShowNolock(FALSE); // only do this last step if everything is there. fRet = ImageList_MergeDragImages(dxHotspot, dyHotspot); if (fVisible) ImageList_DragShowNolock(TRUE); LEAVECRITICAL; return fRet; } HRESULT CImageList::GetDragImage(POINT * ppt, POINT * pptHotspot, REFIID riid, void** ppv) { if (ppt) { ppt->x = g_dctx.ptDrag.x; ppt->y = g_dctx.ptDrag.y; } if (pptHotspot) { pptHotspot->x = g_dctx.ptDragHotspot.x; pptHotspot->y = g_dctx.ptDragHotspot.y; } if (g_dctx.pimlDrag) { return g_dctx.pimlDrag->QueryInterface(riid, ppv); } return E_ACCESSDENIED; } HRESULT CImageList::GetItemFlags(int i, DWORD *dwFlags) { if (IsImageListIndex(i) && _dsaFlags) { *dwFlags = _GetItemFlags(i); return S_OK; } return E_INVALIDARG; } HRESULT CImageList::GetOverlayImage(int iOverlay, int* piIndex) { if (iOverlay <= 0 || iOverlay >= NUM_OVERLAY_IMAGES) return E_INVALIDARG; *piIndex = _aOverlayIndexes[iOverlay - 1]; return S_OK; } HRESULT CImageList::SetDragCursorImage(IUnknown* punk, int i, int dxHotspot, int dyHotspot) { HRESULT hr = E_INVALIDARG; BOOL fVisible = g_dctx.fDragShow; IImageList* pux; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IImageList, &pux)))) { ENTERCRITICAL; // do work only if something has changed if ((g_dctx.puxCursor != pux) || (g_dctx.iCursor != i)) { if (fVisible) ImageList_DragShowNolock(FALSE); IImageList* puxOld = g_dctx.puxCursor; g_dctx.puxCursor = pux; g_dctx.puxCursor->AddRef(); if (puxOld) puxOld->Release(); g_dctx.iCursor = i; g_dctx.ptCursor.x = dxHotspot; g_dctx.ptCursor.y = dyHotspot; hr = ImageList_MergeDragImages(dxHotspot, dyHotspot)? S_OK : E_FAIL; if (fVisible) ImageList_DragShowNolock(TRUE); } LEAVECRITICAL; pux->Release(); } return hr; } HRESULT CImageList::EndDrag() { ENTERCRITICAL; ImageList_DragShowNolock(FALSE); // WARNING: Don't destroy pimlDrag if it is pimlDither. if (g_dctx.pimlDrag && (g_dctx.pimlDrag != g_dctx.pimlDither)) { g_dctx.pimlDrag->Release(); } g_dctx.pimlDrag = NULL; if (g_dctx.pimlDither) { g_dctx.pimlDither->Release(); g_dctx.pimlDither = NULL; } if (g_dctx.puxCursor) { g_dctx.puxCursor->Release(); g_dctx.puxCursor = NULL; } g_dctx.iCursor = -1; g_dctx.hwndDC = NULL; LEAVECRITICAL; return S_OK; } // APIs BOOL WINAPI ImageList_SetDragCursorImage(HIMAGELIST piml, int i, int dxHotspot, int dyHotspot) { BOOL fRet = FALSE; IUnknown* punk; HRESULT hr = HIMAGELIST_QueryInterface(piml, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hr)) { if (g_dctx.puxDragImage) { fRet = (S_OK == g_dctx.puxDragImage->SetDragCursorImage(punk, i, dxHotspot, dyHotspot)); } punk->Release(); } return fRet; } HIMAGELIST WINAPI ImageList_GetDragImage(POINT * ppt, POINT * pptHotspot) { if (g_dctx.puxDragImage) { IImageList* punk = NULL; if (SUCCEEDED(g_dctx.puxDragImage->GetDragImage(ppt, pptHotspot, IID_PPV_ARG(IImageList, &punk)))) { punk->Release(); } return reinterpret_cast(punk); } return NULL; } void WINAPI ImageList_EndDrag() { ENTERCRITICAL; if (g_dctx.puxDragImage) { g_dctx.puxDragImage->EndDrag(); g_dctx.puxDragImage->Release(); g_dctx.puxDragImage = NULL; } LEAVECRITICAL; } BOOL WINAPI ImageList_BeginDrag(HIMAGELIST pimlTrack, int iTrack, int dxHotspot, int dyHotspot) { IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(pimlTrack, IID_PPV_ARG(IImageList, &pux)))) { if (SUCCEEDED(pux->BeginDrag(iTrack, dxHotspot, dyHotspot))) { g_dctx.puxDragImage = pux; return TRUE; } } return FALSE; } BOOL WINAPI ImageList_DragEnter(HWND hwndLock, int x, int y) { BOOL fRet = FALSE; if (g_dctx.puxDragImage) { fRet = (S_OK == g_dctx.puxDragImage->DragEnter(hwndLock, x, y)); } return fRet; } BOOL WINAPI ImageList_DragMove(int x, int y) { BOOL fRet = FALSE; if (g_dctx.puxDragImage) { fRet = (S_OK == g_dctx.puxDragImage->DragMove(x, y)); } return fRet; } BOOL WINAPI ImageList_DragLeave(HWND hwndLock) { BOOL fRet = FALSE; if (g_dctx.puxDragImage) { fRet = (S_OK == g_dctx.puxDragImage->DragLeave(hwndLock)); } return fRet; } BOOL WINAPI ImageList_DragShowNolock(BOOL fShow) { BOOL fRet = FALSE; if (g_dctx.puxDragImage) { fRet = (S_OK == g_dctx.puxDragImage->DragShowNolock(fShow)); } return fRet; } //============================================================================ // ImageList_Clone - clone a image list // // create a new imagelist with the same properties as the given // imagelist, except mabey a new icon size // // piml - imagelist to clone // cx,cy - new icon size (0,0) to use clone icon size. // flags - new flags (used if no clone) // cInitial- initial size // cGrow - grow value (used if no clone) //============================================================================ EXTERN_C HIMAGELIST WINAPI ImageList_Clone(HIMAGELIST himl, int cx, int cy, UINT flags, int cInitial, int cGrow) { IImageListPriv* puxp; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageListPriv, &puxp)))) { // always use the clone flags puxp->GetFlags(&flags); IUnknown* punkMirror; if (SUCCEEDED(puxp->GetMirror(IID_PPV_ARG(IUnknown, &punkMirror)))) { flags |= ILC_MIRROR; punkMirror->Release(); } IImageList* pux; if (SUCCEEDED(puxp->QueryInterface(IID_PPV_ARG(IImageList, &pux)))) { int cxI, cyI; pux->GetIconSize(&cxI, &cyI); if (cx == 0) cx = cxI; if (cy == 0) cy = cyI; pux->Release(); } puxp->Release(); } return ImageList_Create(cx,cy,flags,cInitial,cGrow); } HRESULT WINAPI ImageList_CreateInstance(int cx, int cy, UINT flags, int cInitial, int cGrow, REFIID riid, void** ppv) { CImageList* piml=NULL; HRESULT hr = E_OUTOFMEMORY; *ppv = NULL; piml = CImageList::Create(cx, cy, flags, cInitial, cGrow); if (piml) { // // Let's create a mirrored imagelist, if requested. // if (piml->_flags & ILC_MIRROR) { piml->_flags &= ~ILC_MIRROR; piml->_pimlMirror = CImageList::Create(cx, cy, flags, cInitial, cGrow); if (piml->_pimlMirror) { piml->_pimlMirror->_flags &= ~ILC_MIRROR; } } hr = piml->QueryInterface(riid, ppv); piml->Release(); } return hr; } HIMAGELIST WINAPI ImageList_Create(int cx, int cy, UINT flags, int cInitial, int cGrow) { IImageList* pux; ImageList_CreateInstance(cx, cy, flags, cInitial, cGrow, IID_PPV_ARG(IImageList, &pux)); return reinterpret_cast(pux); } // // When this code is compiled Unicode, this implements the // ANSI version of the ImageList_LoadImage api. // HIMAGELIST WINAPI ImageList_LoadImageA(HINSTANCE hi, LPCSTR lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType, UINT uFlags) { HIMAGELIST lpResult; LPWSTR lpBmpW; if (!IS_INTRESOURCE(lpbmp)) { lpBmpW = ProduceWFromA(CP_ACP, lpbmp); if (!lpBmpW) { return NULL; } } else { lpBmpW = (LPWSTR)lpbmp; } lpResult = ImageList_LoadImageW(hi, lpBmpW, cx, cGrow, crMask, uType, uFlags); if (!IS_INTRESOURCE(lpbmp)) FreeProducedString(lpBmpW); return lpResult; } HIMAGELIST WINAPI ImageList_LoadImageW(HINSTANCE hi, LPCTSTR lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType, UINT uFlags) { HBITMAP hbmImage; HIMAGELIST piml = NULL; BITMAP bm; int cy, cInitial; UINT flags; hbmImage = (HBITMAP)LoadImage(hi, lpbmp, uType, 0, 0, uFlags); if (hbmImage && (sizeof(bm) == GetObject(hbmImage, sizeof(bm), &bm))) { // If cx is not stated assume it is the same as cy. // ASSERT(cx); cy = bm.bmHeight; if (cx == 0) cx = cy; cInitial = bm.bmWidth / cx; ENTERCRITICAL; flags = 0; if (crMask != CLR_NONE) flags |= ILC_MASK; if (bm.bmBits) flags |= (bm.bmBitsPixel & ILC_COLORMASK); piml = ImageList_Create(cx, cy, flags, cInitial, cGrow); if (piml) { int added; if (crMask == CLR_NONE) added = ImageList_Add(piml, hbmImage, NULL); else added = ImageList_AddMasked(piml, hbmImage, crMask); if (added < 0) { ImageList_Destroy(piml); piml = NULL; } } LEAVECRITICAL; } if (hbmImage) DeleteObject(hbmImage); return reinterpret_cast((IImageList*)piml); } // // #undef ImageList_AddIcon EXTERN_C int WINAPI ImageList_AddIcon(HIMAGELIST himl, HICON hIcon) { return ImageList_ReplaceIcon(himl, -1, hIcon); } EXTERN_C void WINAPI ImageList_CopyDitherImage(HIMAGELIST himlDst, WORD iDst, int xDst, int yDst, HIMAGELIST himlSrc, int iSrc, UINT fStyle) { IImageListPriv* puxp; if (SUCCEEDED(HIMAGELIST_QueryInterface(himlDst, IID_PPV_ARG(IImageListPriv, &puxp)))) { IUnknown* punk; if (SUCCEEDED(HIMAGELIST_QueryInterface(himlSrc, IID_PPV_ARG(IUnknown, &punk)))) { puxp->CopyDitherImage(iDst, xDst, yDst, punk, iSrc, fStyle); punk->Release(); } puxp->Release(); } } // // ImageList_Duplicate // // Makes a copy of the passed in imagelist. // HIMAGELIST WINAPI ImageList_Duplicate(HIMAGELIST himl) { IImageList* pret = NULL; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->Clone(IID_PPV_ARG(IImageList, &pret)); pux->Release(); } return reinterpret_cast(pret); } BOOL WINAPI ImageList_Write(HIMAGELIST himl, LPSTREAM pstm) { BOOL fRet = FALSE; IPersistStream* pps; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IPersistStream, &pps)))) { if (SUCCEEDED(pps->Save(pstm, TRUE))) { fRet = TRUE; } pps->Release(); } return fRet; } HIMAGELIST WINAPI ImageList_Read(LPSTREAM pstm) { CImageList* piml = new CImageList(); if (piml) { if (SUCCEEDED(piml->Load(pstm))) { return reinterpret_cast((IImageList*)piml); } piml->Release(); } return NULL; } WINCOMMCTRLAPI HRESULT WINAPI ImageList_ReadEx(DWORD dwFlags, LPSTREAM pstm, REFIID riid, PVOID* ppv) { HRESULT hr = E_OUTOFMEMORY; CImageList* piml = new CImageList(); if (piml) { hr = piml->LoadEx(dwFlags, pstm); if (SUCCEEDED(hr)) { hr = piml->QueryInterface(riid, ppv); } piml->Release(); } return hr; } WINCOMMCTRLAPI HRESULT WINAPI ImageList_WriteEx(HIMAGELIST himl, DWORD dwFlags, LPSTREAM pstm) { IImageListPersistStream* pps; HRESULT hr = HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageListPersistStream, &pps)); if (SUCCEEDED(hr)) { hr = pps->SaveEx(dwFlags, pstm); pps->Release(); } return hr; } BOOL WINAPI ImageList_GetImageRect(HIMAGELIST himl, int i, RECT * prcImage) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { if (SUCCEEDED(pux->GetImageRect(i, prcImage))) { fRet = TRUE; } pux->Release(); } return fRet; } BOOL WINAPI ImageList_Destroy(HIMAGELIST himl) { BOOL fRet = FALSE; IImageList* pux; // Weirdness: We are doing a Query Interface first to verify that // this is actually a valid imagelist, then we are calling release twice if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { // Release the interface we QI'd for pux->Release(); // Release a second time to destroy the object pux->Release(); fRet = TRUE; } return fRet; } int WINAPI ImageList_GetImageCount(HIMAGELIST himl) { int fRet = 0; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->GetImageCount(&fRet); pux->Release(); } return fRet; } BOOL WINAPI ImageList_SetImageCount(HIMAGELIST himl, UINT uNewCount) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->SetImageCount(uNewCount)); pux->Release(); } return fRet; } int WINAPI ImageList_Add(HIMAGELIST himl, HBITMAP hbmImage, HBITMAP hbmMask) { int fRet = -1; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->Add(hbmImage, hbmMask, &fRet); pux->Release(); } return fRet; } int WINAPI ImageList_ReplaceIcon(HIMAGELIST himl, int i, HICON hicon) { int fRet = -1; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->ReplaceIcon(i, hicon, &fRet); pux->Release(); } return fRet; } COLORREF WINAPI ImageList_SetBkColor(HIMAGELIST himl, COLORREF clrBk) { COLORREF fRet = clrBk; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->SetBkColor(clrBk, &fRet); pux->Release(); } return fRet; } COLORREF WINAPI ImageList_GetBkColor(HIMAGELIST himl) { COLORREF fRet = RGB(0,0,0); IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->GetBkColor(&fRet); pux->Release(); } return fRet; } BOOL WINAPI ImageList_SetOverlayImage(HIMAGELIST himl, int iImage, int iOverlay) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->SetOverlayImage(iImage, iOverlay)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_Replace(HIMAGELIST himl, int i, HBITMAP hbmImage, HBITMAP hbmMask) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->Replace(i, hbmImage, hbmMask)); pux->Release(); } return fRet; } int WINAPI ImageList_AddMasked(HIMAGELIST himl, HBITMAP hbmImage, COLORREF crMask) { int fRet = -1; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->AddMasked(hbmImage, crMask, &fRet); pux->Release(); } return fRet; } BOOL WINAPI ImageList_DrawEx(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { IMAGELISTDRAWPARAMS imldp = {0}; imldp.cbSize = sizeof(imldp); imldp.himl = himl; imldp.i = i; imldp.hdcDst = hdcDst; imldp.x = x; imldp.y = y; imldp.cx = dx; imldp.cy = dy; imldp.rgbBk = rgbBk; imldp.rgbFg = rgbFg; imldp.fStyle = fStyle; fRet = (S_OK == pux->Draw(&imldp)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_Draw(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, UINT fStyle) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { IMAGELISTDRAWPARAMS imldp = {0}; imldp.cbSize = sizeof(imldp); imldp.himl = himl; imldp.i = i; imldp.hdcDst = hdcDst; imldp.x = x; imldp.y = y; imldp.rgbBk = CLR_DEFAULT; imldp.rgbFg = CLR_DEFAULT; imldp.fStyle = fStyle; fRet = (S_OK == pux->Draw(&imldp)); pux->Release(); } return fRet; } // Note: no distinction between failure case (bad himl) and no flags set DWORD WINAPI ImageList_GetItemFlags(HIMAGELIST himl, int i) { DWORD dwFlags = 0; if (himl) { IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->GetItemFlags(i, &dwFlags); pux->Release(); } } return dwFlags; } BOOL WINAPI ImageList_DrawIndirect(IMAGELISTDRAWPARAMS* pimldp) { BOOL fRet = FALSE; IImageList* pux; if (!pimldp) return fRet; if (SUCCEEDED(HIMAGELIST_QueryInterface(pimldp->himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->Draw(pimldp)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_Remove(HIMAGELIST himl, int i) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->Remove(i)); pux->Release(); } return fRet; } HICON WINAPI ImageList_GetIcon(HIMAGELIST himl, int i, UINT flags) { HICON fRet = NULL; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { pux->GetIcon(i, flags, &fRet); pux->Release(); } return fRet; } BOOL WINAPI ImageList_Copy(HIMAGELIST himlDst, int iDst, HIMAGELIST himlSrc, int iSrc, UINT uFlags) { BOOL fRet = FALSE; if (himlDst == himlSrc) { IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himlDst, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->Copy(iDst,(IUnknown*)pux, iSrc, uFlags)); pux->Release(); } } return fRet; } BOOL WINAPI ImageList_GetIconSize(HIMAGELIST himl, int *cx, int *cy) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->GetIconSize(cx, cy)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_SetIconSize(HIMAGELIST himl, int cx, int cy) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->SetIconSize(cx, cy)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_GetImageInfo(HIMAGELIST himl, int i, IMAGEINFO* pImageInfo) { BOOL fRet = FALSE; IImageList* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageList, &pux)))) { fRet = (S_OK == pux->GetImageInfo(i, pImageInfo)); pux->Release(); } return fRet; } HIMAGELIST WINAPI ImageList_Merge(HIMAGELIST himl1, int i1, HIMAGELIST himl2, int i2, int dx, int dy) { IImageList* fRet = NULL; IImageList* pux1; IImageList* pux2; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl1, IID_PPV_ARG(IImageList, &pux1)))) { if (SUCCEEDED(HIMAGELIST_QueryInterface(himl2, IID_PPV_ARG(IImageList, &pux2)))) { pux1->Merge(i1, (IUnknown*)pux2, i2, dx, dy, IID_PPV_ARG(IImageList, &fRet)); pux2->Release(); } pux1->Release(); } return reinterpret_cast(fRet); } BOOL WINAPI ImageList_SetFlags(HIMAGELIST himl, UINT flags) { BOOL fRet = FALSE; IImageListPriv* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageListPriv, &pux)))) { fRet = (S_OK == pux->SetFlags(flags)); pux->Release(); } return fRet; } BOOL WINAPI ImageList_SetFilter(HIMAGELIST himl, PFNIMLFILTER pfnFilter, LPARAM lParamFilter) { return FALSE; } int ImageList_SetColorTable(HIMAGELIST himl, int start, int len, RGBQUAD *prgb) { int fRet = -1; IImageListPriv* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageListPriv, &pux)))) { pux->SetColorTable(start, len, prgb, &fRet); pux->Release(); } return fRet; } UINT WINAPI ImageList_GetFlags(HIMAGELIST himl) { UINT fRet = 0; IImageListPriv* pux; if (SUCCEEDED(HIMAGELIST_QueryInterface(himl, IID_PPV_ARG(IImageListPriv, &pux)))) { pux->GetFlags(&fRet); pux->Release(); } return fRet; }