X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/54b37e2e8bdb7b41eee67ed4c31542aaa685d185..4e15d1caa03346c126015019c1fdf093033ef40b:/src/msw/imaglist.cpp diff --git a/src/msw/imaglist.cpp b/src/msw/imaglist.cpp index 6c80416dd8..266af3feb0 100644 --- a/src/msw/imaglist.cpp +++ b/src/msw/imaglist.cpp @@ -1,125 +1,171 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: imaglist.cpp -// Purpose: wxImageList +// Name: src/msw/imaglist.cpp +// Purpose: wxImageList implementation for Win32 // Author: Julian Smart // Modified by: // Created: 04/01/98 // RCS-ID: $Id$ -// Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows license +// Copyright: (c) Julian Smart +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "imaglist.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif -#if defined(__WIN95__) - #ifndef WX_PRECOMP -#include -#include "wx/setup.h" -#include "wx/window.h" -#include "wx/icon.h" -#include "wx/dc.h" -#include "wx/string.h" + #include "wx/msw/wrapcctl.h" // include "properly" + #include "wx/window.h" + #include "wx/icon.h" + #include "wx/dc.h" + #include "wx/string.h" + #include "wx/dcmemory.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/image.h" + #include #endif -#include "wx/log.h" -#include "wx/intl.h" - -#include "wx/msw/imaglist.h" +#include "wx/imaglist.h" +#include "wx/dc.h" +#include "wx/msw/dc.h" +#include "wx/msw/dib.h" #include "wx/msw/private.h" -#if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__) || defined(wxUSE_NORLANDER_HEADERS) -#include -#endif +// ---------------------------------------------------------------------------- +// wxWin macros +// ---------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARY IMPLEMENT_DYNAMIC_CLASS(wxImageList, wxObject) -#endif -wxImageList::wxImageList(void) +#define GetHImageList() ((HIMAGELIST)m_hImageList) + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +// returns the mask if it's valid, otherwise the bitmap mask and, if it's not +// valid neither, a "solid" mask (no transparent zones at all) +static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask); + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxImageList creation/destruction +// ---------------------------------------------------------------------------- + +wxImageList::wxImageList() { m_hImageList = 0; } -wxImageList::~wxImageList(void) +// Creates an image list +bool wxImageList::Create(int width, int height, bool mask, int initial) { - if ( m_hImageList ) - ImageList_Destroy((HIMAGELIST) m_hImageList); - m_hImageList = 0; + UINT flags = 0; + + // as we want to be able to use 32bpp bitmaps in the image lists, we always + // use ILC_COLOR32, even if the display resolution is less -- the system + // will make the best effort to show the bitmap if we do this resulting in + // quite acceptable display while using a lower depth ILC_COLOR constant + // (e.g. ILC_COLOR16) shows completely broken bitmaps +#ifdef __WXWINCE__ + flags |= ILC_COLOR; +#else + flags |= ILC_COLOR32; +#endif + + if ( mask ) + flags |= ILC_MASK; + + // Grow by 1, I guess this is reasonable behaviour most of the time + m_hImageList = (WXHIMAGELIST) ImageList_Create(width, height, flags, + initial, 1); + if ( !m_hImageList ) + { + wxLogLastError(wxT("ImageList_Create()")); + } + + return m_hImageList != 0; } +wxImageList::~wxImageList() +{ + if ( m_hImageList ) + { + ImageList_Destroy(GetHImageList()); + m_hImageList = 0; + } +} -// Attributes -//////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- +// wxImageList attributes +// ---------------------------------------------------------------------------- // Returns the number of images in the image list. -int wxImageList::GetImageCount(void) const +int wxImageList::GetImageCount() const { - return ImageList_GetImageCount((HIMAGELIST) m_hImageList); -} + wxASSERT_MSG( m_hImageList, wxT("invalid image list") ); -// Operations -//////////////////////////////////////////////////////////////////////////// + return ImageList_GetImageCount(GetHImageList()); +} -// Creates an image list -bool wxImageList::Create(int width, int height, bool mask, int initial) +// Returns the size (same for all images) of the images in the list +bool wxImageList::GetSize(int WXUNUSED(index), int &width, int &height) const { - UINT flags = 0; - if ( mask ) - flags |= ILC_MASK; + wxASSERT_MSG( m_hImageList, wxT("invalid image list") ); - // Grow by 1, I guess this is reasonable behaviour most of the time - m_hImageList = (WXHIMAGELIST) ImageList_Create(width, height, flags, initial, 1); - return (m_hImageList != 0); + return ImageList_GetIconSize(GetHImageList(), &width, &height) != 0; } +// ---------------------------------------------------------------------------- +// wxImageList operations +// ---------------------------------------------------------------------------- + // Adds a bitmap, and optionally a mask bitmap. // Note that wxImageList creates new bitmaps, so you may delete // 'bitmap' and 'mask'. int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask) { - HBITMAP hBitmap1 = (HBITMAP) bitmap.GetHBITMAP(); - HBITMAP hBitmap2 = 0; - if ( mask.Ok() ) - hBitmap2 = (HBITMAP) mask.GetHBITMAP(); - else if (bitmap.GetMask()) - hBitmap2 = (HBITMAP) bitmap.GetMask()->GetMaskBitmap(); - - HBITMAP hBitmapI=0; - if(hBitmap2!=0) { - // Microsoft imagelist masks are inverted from wxWindows mask standard (white is mask color) - BITMAP bm; - ::GetObject(hBitmap2,sizeof(BITMAP),(LPVOID)&bm); - int w=bm.bmWidth; - int h=bm.bmHeight; - HDC hdc = ::CreateCompatibleDC(NULL); - HDC hdci = ::CreateCompatibleDC(NULL); - hBitmapI = ::CreateCompatibleBitmap(hdci, w, h); - ::SelectObject(hdc, hBitmap2); - ::SelectObject(hdci, hBitmapI); - ::BitBlt(hdci, 0, 0, w, h, hdc, 0, 0, NOTSRCCOPY); - ::DeleteDC(hdc); - ::DeleteDC(hdci); - } - - int index = ImageList_Add((HIMAGELIST) GetHIMAGELIST(), hBitmap1, hBitmapI); - if ( index == -1 ) + HBITMAP hbmp; + +#if wxUSE_WXDIB && wxUSE_IMAGE + // wxBitmap normally stores alpha in pre-multiplied format but + // ImageList_Draw() does pre-multiplication internally so we need to undo + // the pre-multiplication here. Converting back and forth like this is, of + // course, very inefficient but it's better than wrong appearance so we do + // this for now until a better way can be found. + AutoHBITMAP hbmpRelease; + if ( bitmap.HasAlpha() ) { - wxLogError(_("Couldn't add an image to the image list.")); + hbmp = wxDIB(bitmap.ConvertToImage(), + wxDIB::PixelFormat_NotPreMultiplied).Detach(); + hbmpRelease.Init(hbmp); } + else +#endif // wxUSE_WXDIB && wxUSE_IMAGE + hbmp = GetHbitmapOf(bitmap); - // Clean up inverted mask - if(hBitmapI!=0) - ::DeleteObject(hBitmapI); + AutoHBITMAP hbmpMask(GetMaskForImage(bitmap, mask)); + + int index = ImageList_Add(GetHImageList(), hbmp, hbmpMask); + if ( index == -1 ) + { + wxLogError(_("Couldn't add an image to the image list.")); + } return index; } @@ -129,127 +175,279 @@ int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask) // 'bitmap'. int wxImageList::Add(const wxBitmap& bitmap, const wxColour& maskColour) { -#ifdef __TWIN32__ - wxFAIL_MSG("ImageList_AddMasked not implemented in TWIN32"); - return -1; -#else - HBITMAP hBitmap1 = (HBITMAP) bitmap.GetHBITMAP(); - COLORREF colorRef = PALETTERGB(maskColour.Red(), maskColour.Green(), maskColour.Blue()); - return ImageList_AddMasked((HIMAGELIST) GetHIMAGELIST(), hBitmap1, colorRef); -#endif + HBITMAP hbmp; + +#if wxUSE_WXDIB && wxUSE_IMAGE + // See the comment in overloaded Add() above. + AutoHBITMAP hbmpRelease; + if ( bitmap.HasAlpha() ) + { + hbmp = wxDIB(bitmap.ConvertToImage(), + wxDIB::PixelFormat_NotPreMultiplied).Detach(); + hbmpRelease.Init(hbmp); + } + else +#endif // wxUSE_WXDIB && wxUSE_IMAGE + hbmp = GetHbitmapOf(bitmap); + + int index = ImageList_AddMasked(GetHImageList(), + hbmp, + wxColourToRGB(maskColour)); + if ( index == -1 ) + { + wxLogError(_("Couldn't add an image to the image list.")); + } + + return index; } // Adds a bitmap and mask from an icon. int wxImageList::Add(const wxIcon& icon) { - HICON hIcon = (HICON) icon.GetHICON(); - return ImageList_AddIcon((HIMAGELIST) GetHIMAGELIST(), hIcon); + int index = ImageList_AddIcon(GetHImageList(), GetHiconOf(icon)); + if ( index == -1 ) + { + wxLogError(_("Couldn't add an image to the image list.")); + } + + return index; } // Replaces a bitmap, optionally passing a mask bitmap. // Note that wxImageList creates new bitmaps, so you may delete // 'bitmap' and 'mask'. -bool wxImageList::Replace(int index, const wxBitmap& bitmap, const wxBitmap& mask) +bool wxImageList::Replace(int index, + const wxBitmap& bitmap, + const wxBitmap& mask) { -#ifdef __TWIN32__ - wxFAIL_MSG("ImageList_Replace not implemented in TWIN32"); - return FALSE; -#else - HBITMAP hBitmap1 = (HBITMAP) bitmap.GetHBITMAP(); - HBITMAP hBitmap2 = 0; - if ( mask.Ok() ) - hBitmap2 = (HBITMAP) mask.GetHBITMAP(); - return (ImageList_Replace((HIMAGELIST) GetHIMAGELIST(), index, hBitmap1, hBitmap2) != 0); -#endif -} + HBITMAP hbmp; -/* Not supported by Win95 -// Replacing a bitmap, using the specified colour to create the mask bitmap -// Note that wxImageList creates new bitmaps, so you may delete -// 'bitmap'. -bool wxImageList::Replace(int index, const wxBitmap& bitmap, const wxColour& maskColour) -{ - HBITMAP hBitmap1 = (HBITMAP) bitmap.GetHBITMAP(); - COLORREF colorRef = PALETTERGB(maskColour.Red(), maskColour.Green(), maskColour.Blue()); - return (bool) ImageList_ReplaceMasked((HIMAGELIST) GetHIMAGELIST(), index, hBitmap1, colorRef); +#if wxUSE_WXDIB && wxUSE_IMAGE + // See the comment in Add() above. + AutoHBITMAP hbmpRelease; + if ( bitmap.HasAlpha() ) + { + hbmp = wxDIB(bitmap.ConvertToImage(), + wxDIB::PixelFormat_NotPreMultiplied).Detach(); + hbmpRelease.Init(hbmp); + } + else +#endif // wxUSE_WXDIB && wxUSE_IMAGE + hbmp = GetHbitmapOf(bitmap); + + AutoHBITMAP hbmpMask(GetMaskForImage(bitmap, mask)); + + if ( !ImageList_Replace(GetHImageList(), index, hbmp, hbmpMask) ) + { + wxLogLastError(wxT("ImageList_Replace()")); + return false; + } + + return true; } -*/ // Replaces a bitmap and mask from an icon. -bool wxImageList::Replace(int index, const wxIcon& icon) +bool wxImageList::Replace(int i, const wxIcon& icon) { - HICON hIcon = (HICON) icon.GetHICON(); - return (ImageList_ReplaceIcon((HIMAGELIST) GetHIMAGELIST(), index, hIcon) != 0); + bool ok = ImageList_ReplaceIcon(GetHImageList(), i, GetHiconOf(icon)) != -1; + if ( !ok ) + { + wxLogLastError(wxT("ImageList_ReplaceIcon()")); + } + + return ok; } // Removes the image at the given index. bool wxImageList::Remove(int index) { -#ifdef __TWIN32__ - wxFAIL_MSG("ImageList_Replace not implemented in TWIN32"); - return FALSE; -#else - return (ImageList_Remove((HIMAGELIST) GetHIMAGELIST(), index) != 0); -#endif + bool ok = ImageList_Remove(GetHImageList(), index) != 0; + if ( !ok ) + { + wxLogLastError(wxT("ImageList_Remove()")); + } + + return ok; } // Remove all images -bool wxImageList::RemoveAll(void) +bool wxImageList::RemoveAll() { - // TODO: Is this correct? - while ( GetImageCount() > 0 ) - { - Remove(0); - } - return TRUE; + // don't use ImageList_RemoveAll() because mingw32 headers don't have it + return Remove(-1); } // Draws the given image on a dc at the specified position. -// If 'solidBackground' is TRUE, Draw sets the image list background +// If 'solidBackground' is true, Draw sets the image list background // colour to the background colour of the wxDC, to speed up // drawing by eliminating masked drawing where possible. -bool wxImageList::Draw(int index, wxDC& dc, int x, int y, - int flags, bool solidBackground) +bool wxImageList::Draw(int index, + wxDC& dc, + int x, int y, + int flags, + bool solidBackground) { -#ifdef __TWIN32__ - wxFAIL_MSG("ImageList_Replace not implemented in TWIN32"); - return FALSE; -#else - HDC hDC = (HDC) dc.GetHDC(); - if ( !hDC ) - return FALSE; - - if ( solidBackground ) - { - wxBrush *brush = & dc.GetBackground(); - if ( brush && brush->Ok()) - { - wxColour col(brush->GetColour()); - ImageList_SetBkColor((HIMAGELIST) GetHIMAGELIST(), - PALETTERGB(col.Red(), col.Green(), col.Blue())); - } - else - ImageList_SetBkColor((HIMAGELIST) GetHIMAGELIST(), - CLR_NONE); - } - else - ImageList_SetBkColor((HIMAGELIST) GetHIMAGELIST(), - CLR_NONE); + wxDCImpl *impl = dc.GetImpl(); + wxMSWDCImpl *msw_impl = wxDynamicCast( impl, wxMSWDCImpl ); + if (!msw_impl) + return false; + + HDC hDC = GetHdcOf(*msw_impl); + wxCHECK_MSG( hDC, false, wxT("invalid wxDC in wxImageList::Draw") ); + + COLORREF clr = CLR_NONE; // transparent by default + if ( solidBackground ) + { + const wxBrush& brush = dc.GetBackground(); + if ( brush.IsOk() ) + { + clr = wxColourToRGB(brush.GetColour()); + } + } + + ImageList_SetBkColor(GetHImageList(), clr); UINT style = 0; - if ( flags & wxIMAGELIST_DRAW_NORMAL ) - style |= ILD_NORMAL; - if ( flags & wxIMAGELIST_DRAW_TRANSPARENT ) - style |= ILD_TRANSPARENT; - if ( flags & wxIMAGELIST_DRAW_SELECTED ) - style |= ILD_SELECTED; - if ( flags & wxIMAGELIST_DRAW_FOCUSED ) - style |= ILD_FOCUS; - - return (ImageList_Draw((HIMAGELIST) GetHIMAGELIST(), index, hDC, - x, y, style) != 0); -#endif + if ( flags & wxIMAGELIST_DRAW_NORMAL ) + style |= ILD_NORMAL; + if ( flags & wxIMAGELIST_DRAW_TRANSPARENT ) + style |= ILD_TRANSPARENT; + if ( flags & wxIMAGELIST_DRAW_SELECTED ) + style |= ILD_SELECTED; + if ( flags & wxIMAGELIST_DRAW_FOCUSED ) + style |= ILD_FOCUS; + + bool ok = ImageList_Draw(GetHImageList(), index, hDC, x, y, style) != 0; + if ( !ok ) + { + wxLogLastError(wxT("ImageList_Draw()")); + } + + return ok; } +// Get the bitmap +wxBitmap wxImageList::GetBitmap(int index) const +{ +#if wxUSE_WXDIB && wxUSE_IMAGE + int bmp_width = 0, bmp_height = 0; + GetSize(index, bmp_width, bmp_height); + + wxBitmap bitmap(bmp_width, bmp_height); + wxMemoryDC dc; + dc.SelectObject(bitmap); + + // draw it the first time to find a suitable mask colour + ((wxImageList*)this)->Draw(index, dc, 0, 0, wxIMAGELIST_DRAW_TRANSPARENT); + dc.SelectObject(wxNullBitmap); + + // find the suitable mask colour + wxImage image = bitmap.ConvertToImage(); + unsigned char r = 0, g = 0, b = 0; + image.FindFirstUnusedColour(&r, &g, &b); + + // redraw whole image and bitmap in the mask colour + image.Create(bmp_width, bmp_height); + image.Replace(0, 0, 0, r, g, b); + bitmap = wxBitmap(image); + + // redraw icon over the mask colour to actually draw it + dc.SelectObject(bitmap); + ((wxImageList*)this)->Draw(index, dc, 0, 0, wxIMAGELIST_DRAW_TRANSPARENT); + dc.SelectObject(wxNullBitmap); + + // get the image, set the mask colour and convert back to get transparent bitmap + image = bitmap.ConvertToImage(); + image.SetMaskColour(r, g, b); + bitmap = wxBitmap(image); +#else + wxBitmap bitmap; #endif + return bitmap; +} + +// Get the icon +wxIcon wxImageList::GetIcon(int index) const +{ + HICON hIcon = ImageList_ExtractIcon(0, GetHImageList(), index); + if (hIcon) + { + wxIcon icon; + icon.SetHICON((WXHICON)hIcon); + + int iconW, iconH; + GetSize(index, iconW, iconH); + icon.SetSize(iconW, iconH); + + return icon; + } + else + return wxNullIcon; +} + +// ---------------------------------------------------------------------------- +// helpers +// ---------------------------------------------------------------------------- +static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask) +{ +#if wxUSE_IMAGE + wxBitmap bitmapWithMask; +#endif // wxUSE_IMAGE + + HBITMAP hbmpMask; + wxMask *pMask; + bool deleteMask = false; + + if ( mask.IsOk() ) + { + hbmpMask = GetHbitmapOf(mask); + pMask = NULL; + } + else + { + pMask = bitmap.GetMask(); + +#if wxUSE_IMAGE + // check if we don't have alpha in this bitmap -- we can create a mask + // from it (and we need to do it for the older systems which don't + // support 32bpp bitmaps natively) + if ( !pMask ) + { + wxImage img(bitmap.ConvertToImage()); + if ( img.HasAlpha() ) + { + img.ConvertAlphaToMask(); + bitmapWithMask = wxBitmap(img); + pMask = bitmapWithMask.GetMask(); + } + } +#endif // wxUSE_IMAGE + + if ( !pMask ) + { + // use the light grey count as transparent: the trouble here is + // that the light grey might have been changed by Windows behind + // our back, so use the standard colour map to get its real value + wxCOLORMAP *cmap = wxGetStdColourMap(); + wxColour col; + wxRGBToColour(col, cmap[wxSTD_COL_BTNFACE].from); + + pMask = new wxMask(bitmap, col); + + deleteMask = true; + } + + hbmpMask = (HBITMAP)pMask->GetMaskBitmap(); + } + + // windows mask convention is opposite to the wxWidgets one + HBITMAP hbmpMaskInv = wxInvertMask(hbmpMask); + + if ( deleteMask ) + { + delete pMask; + } + + return hbmpMaskInv; +}