X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/59ff46cb36f5730ebff84b7966dfa5c097ed34a2..647b8e379ed0f470f0f643fe816ba872be41541d:/src/msw/bitmap.cpp diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index a44f26ea0b..bdff57bb68 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -1,4 +1,4 @@ -///////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// // Name: bitmap.cpp // Purpose: wxBitmap // Author: Julian Smart @@ -40,8 +40,6 @@ #include "wx/icon.h" #endif -//#include "device.h" - #include "wx/msw/private.h" #include "wx/log.h" @@ -57,6 +55,60 @@ #define CLR_INVALID ((COLORREF)-1) #endif // no CLR_INVALID +// ---------------------------------------------------------------------------- +// Bitmap data +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxBitmapRefData : public wxGDIImageRefData +{ +public: + wxBitmapRefData(); + virtual ~wxBitmapRefData() { Free(); } + + virtual void Free(); + + // set the mask object to use as the mask, we take ownership of it + void SetMask(wxMask *mask) + { + delete m_bitmapMask; + m_bitmapMask = mask; + } + + // set the HBITMAP to use as the mask + void SetMask(HBITMAP hbmpMask) + { + SetMask(new wxMask((WXHBITMAP)hbmpMask)); + } + + // return the mask + wxMask *GetMask() const { return m_bitmapMask; } + +public: + int m_numColors; +#if wxUSE_PALETTE + wxPalette m_bitmapPalette; +#endif // wxUSE_PALETTE + + // MSW-specific + // ------------ + + // this field is solely for error checking: we detect selecting a bitmap + // into more than one DC at once or deleting a bitmap still selected into a + // DC (both are serious programming errors under Windows) + wxDC *m_selectedInto; + +#if wxUSE_DIB_FOR_BITMAP + // file mapping handle for large DIB's + HANDLE m_hFileMap; +#endif // wxUSE_DIB_FOR_BITMAP + +private: + // optional mask for transparent drawing + wxMask *m_bitmapMask; + + DECLARE_NO_COPY_CLASS(wxBitmapRefData) +}; + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -76,11 +128,13 @@ IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject) wxBitmapRefData::wxBitmapRefData() { - m_quality = 0; m_selectedInto = NULL; m_numColors = 0; m_bitmapMask = NULL; m_hBitmap = (WXHBITMAP) NULL; +#if wxUSE_DIB_FOR_BITMAP + m_hFileMap = 0; +#endif // wxUSE_DIB_FOR_BITMAP } void wxBitmapRefData::Free() @@ -90,15 +144,21 @@ void wxBitmapRefData::Free() if ( m_hBitmap) { - // printf("About to delete bitmap %d\n", (int) (HBITMAP) m_hBitmap); -#if 1 if ( !::DeleteObject((HBITMAP)m_hBitmap) ) { wxLogLastError(wxT("DeleteObject(hbitmap)")); } -#endif } +#if wxUSE_DIB_FOR_BITMAP + if ( m_hFileMap ) + { + ::CloseHandle(m_hFileMap); + + m_hFileMap = 0; + } +#endif // wxUSE_DIB_FOR_BITMAP + delete m_bitmapMask; m_bitmapMask = NULL; } @@ -114,6 +174,11 @@ void wxBitmap::Init() } +wxGDIImageRefData *wxBitmap::CreateData() const +{ + return new wxBitmapRefData; +} + #ifdef __WIN32__ bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon) @@ -144,8 +209,7 @@ bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon) // the mask returned by GetIconInfo() is inversed compared to the usual // wxWin convention - refData->m_bitmapMask = new wxMask((WXHBITMAP) - wxInvertMask(iconInfo.hbmMask, w, h)); + refData->SetMask(wxInvertMask(iconInfo.hbmMask, w, h)); // delete the old one now as we don't need it any more @@ -341,41 +405,121 @@ bool wxBitmap::Create(int w, int h, int d) m_refData = new wxBitmapRefData; - GetBitmapData()->m_width = w; - GetBitmapData()->m_height = h; - GetBitmapData()->m_depth = d; - - HBITMAP hbmp; -#ifndef __WXMICROWIN__ - if ( d > 0 ) +#if wxUSE_DIB_FOR_BITMAP + if ( w && h && d >= 16 ) { - hbmp = ::CreateBitmap(w, h, 1, d, NULL); - if ( !hbmp ) - { - wxLogLastError(wxT("CreateBitmap")); - } + if ( !CreateDIB(w, h, d) ) + return FALSE; } else -#endif +#endif // wxUSE_DIB_FOR_BITMAP { - ScreenHDC dc; - hbmp = ::CreateCompatibleBitmap(dc, w, h); - if ( !hbmp ) + GetBitmapData()->m_width = w; + GetBitmapData()->m_height = h; + GetBitmapData()->m_depth = d; + + HBITMAP hbmp; +#ifndef __WXMICROWIN__ + if ( d > 0 ) { - wxLogLastError(wxT("CreateCompatibleBitmap")); + hbmp = ::CreateBitmap(w, h, 1, d, NULL); + if ( !hbmp ) + { + wxLogLastError(wxT("CreateBitmap")); + } } + else +#endif // !__WXMICROWIN__ + { + ScreenHDC dc; + hbmp = ::CreateCompatibleBitmap(dc, w, h); + if ( !hbmp ) + { + wxLogLastError(wxT("CreateCompatibleBitmap")); + } - GetBitmapData()->m_depth = wxDisplayDepth(); - } + GetBitmapData()->m_depth = wxDisplayDepth(); + } - SetHBITMAP((WXHBITMAP)hbmp); + SetHBITMAP((WXHBITMAP)hbmp); #if WXWIN_COMPATIBILITY_2 - GetBitmapData()->m_ok = hbmp != 0; + GetBitmapData()->m_ok = hbmp != 0; #endif // WXWIN_COMPATIBILITY_2 + } + return Ok(); } +#if wxUSE_DIB_FOR_BITMAP + +void *wxBitmap::CreateDIB(int width, int height, int depth) +{ + void *dibBits; + const int infosize = sizeof(BITMAPINFOHEADER); + + BITMAPINFO *info = (BITMAPINFO *)malloc(infosize); + if ( info ) + { + memset(info, 0, infosize); + + info->bmiHeader.biSize = infosize; + info->bmiHeader.biWidth = width; + info->bmiHeader.biHeight = height; + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = depth; + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biSizeImage = + (((width * (depth/8)) + sizeof(DWORD) - 1) / + sizeof(DWORD) * sizeof(DWORD)) * height; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrUsed = 0; + info->bmiHeader.biClrImportant = 0; + GetBitmapData()->m_hFileMap = ::CreateFileMapping + ( + INVALID_HANDLE_VALUE, + 0, + PAGE_READWRITE | SEC_COMMIT, + 0, + info->bmiHeader.biSizeImage, + 0 + ); + + // No need to report an error here. If it fails, we just won't use a + // file mapping and CreateDIBSection will just allocate memory for us. + GetBitmapData()->m_handle = + (WXHANDLE)::CreateDIBSection + ( + 0, + info, + DIB_RGB_COLORS, + &dibBits, + GetBitmapData()->m_hFileMap, + 0 + ); + + if ( !GetBitmapData()->m_handle ) + wxLogLastError(wxT("CreateDIBSection")); + + SetWidth(width); + SetHeight(height); + SetDepth(depth); + + free(info); + } + else + { + wxFAIL_MSG( wxT("could not allocate memory for DIB header") ); + + dibBits = NULL; + } + + return dibBits; +} + +#endif // wxUSE_DIB_FOR_BITMAP + // ---------------------------------------------------------------------------- // wxImage to/from conversions // ---------------------------------------------------------------------------- @@ -395,9 +539,8 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) // Initial attempt at a simple-minded implementation. // The bitmap will always be created at the screen depth, // so the 'depth' argument is ignored. - + HDC hScreenDC = ::GetDC(NULL); - // printf("Screen planes = %d, bpp = %d\n", hScreenDC->psd->planes, hScreenDC->psd->bpp); int screenDepth = ::GetDeviceCaps(hScreenDC, BITSPIXEL); HBITMAP hBitmap = ::CreateCompatibleBitmap(hScreenDC, image.GetWidth(), image.GetHeight()); @@ -412,7 +555,7 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) if (hBitmap == NULL) { ::ReleaseDC(NULL, hScreenDC); - return FALSE; + return FALSE; } HDC hMemDC = ::CreateCompatibleDC(hScreenDC); @@ -439,7 +582,7 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) if (!hMaskBitmap) { hasMask = FALSE; - } + } else { hScreenDC = ::GetDC(NULL); @@ -447,7 +590,7 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) ::ReleaseDC(NULL, hScreenDC); hOldMaskBitmap = ::SelectObject( hMaskDC, hMaskBitmap); - } + } #else hasMask = FALSE; #endif @@ -456,13 +599,13 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) int i, j; for (i = 0; i < image.GetWidth(); i++) { - for (j = 0; j < image.GetHeight(); j++) - { - unsigned char red = image.GetRed(i, j); - unsigned char green = image.GetGreen(i, j); - unsigned char blue = image.GetBlue(i, j); + for (j = 0; j < image.GetHeight(); j++) + { + unsigned char red = image.GetRed(i, j); + unsigned char green = image.GetGreen(i, j); + unsigned char blue = image.GetBlue(i, j); - ::SetPixel(hMemDC, i, j, PALETTERGB(red, green, blue)); + ::SetPixel(hMemDC, i, j, PALETTERGB(red, green, blue)); if (hasMask) { @@ -472,8 +615,8 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) ::SetPixel(hMaskDC, i, j, PALETTERGB(0, 0, 0)); else ::SetPixel(hMaskDC, i, j, PALETTERGB(255, 255, 255)); - } - } + } + } } ::SelectObject(hMemDC, hOldBitmap); @@ -481,16 +624,16 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) if (hasMask) { ::SelectObject(hMaskDC, hOldMaskBitmap); - ::DeleteDC(hMaskDC); + ::DeleteDC(hMaskDC); - ((wxBitmapRefData*)m_refData)->m_bitmapMask = new wxMask((WXHBITMAP) hMaskBitmap); + ((wxBitmapRefData*)m_refData)->SetMask(hMaskBitmap); } - + SetWidth(image.GetWidth()); SetHeight(image.GetHeight()); SetDepth(screenDepth); SetHBITMAP( (WXHBITMAP) hBitmap ); - + #if wxUSE_PALETTE // Copy the palette from the source image SetPalette(image.GetPalette()); @@ -503,16 +646,56 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) return TRUE; -#else +#else // !__WXMICROWIN__ wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") ) m_refData = new wxBitmapRefData(); +#if wxUSE_DIB_FOR_BITMAP + int h = image.GetHeight(); + int w = image.GetWidth(); + unsigned char *dibBits = (unsigned char*)CreateDIB(w, h, 24); + if ( !dibBits ) + return FALSE; + + // DIBs are stored in bottom to top order so we need to copy bits line by + // line and starting from the end + const int srcBytesPerLine = w * 3; + const int dstBytesPerLine = (srcBytesPerLine + sizeof(DWORD) - 1) / + sizeof(DWORD) * sizeof(DWORD); + const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine); + for ( int i = 0; i < h; i++ ) + { + // copy one DIB line + int x = w; + const unsigned char *rgbBits = src; + while ( x-- ) + { + // also, the order of RGB is inversed for DIBs + *dibBits++ = rgbBits[2]; + *dibBits++ = rgbBits[1]; + *dibBits++ = rgbBits[0]; + + rgbBits += 3; + } + + // pass to the next line + src -= srcBytesPerLine; + dibBits += dstBytesPerLine - srcBytesPerLine; + } + + if ( image.HasMask() ) + { + SetMask(new wxMask(*this, wxColour(image.GetMaskRed(), + image.GetMaskGreen(), + image.GetMaskBlue()))); + } +#else // wxUSE_DIB_FOR_BITMAP // sizeLimit is the MS upper limit for the DIB size #ifdef WIN32 int sizeLimit = 1024*768*3; #else - int sizeLimit = 0x7fff ; + int sizeLimit = 0x7fff; #endif // width and height of the device-dependent bitmap @@ -726,6 +909,7 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) ::ReleaseDC(NULL, hdc); free(lpDIBh); free(lpBits); +#endif // wxUSE_DIB_FOR_BITMAP #if WXWIN_COMPATIBILITY_2 // check the wxBitmap object @@ -733,7 +917,7 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) #endif // WXWIN_COMPATIBILITY_2 return TRUE; -#endif +#endif // __WXMICROWIN__/!__WXMICROWIN__ } wxImage wxBitmap::ConvertToImage() const @@ -747,7 +931,7 @@ wxImage wxBitmap::ConvertToImage() const if (!Ok()) { wxFAIL_MSG( wxT("bitmap is invalid") ); - return wxNullImage; + return wxNullImage; } wxImage image; @@ -764,33 +948,33 @@ wxImage wxBitmap::ConvertToImage() const wxFAIL_MSG( wxT("could not allocate data for image") ); return wxNullImage; } - + HDC hScreenDC = ::GetDC(NULL); HDC hMemDC = ::CreateCompatibleDC(hScreenDC); ::ReleaseDC(NULL, hScreenDC); HBITMAP hBitmap = (HBITMAP) GetHBITMAP(); - + HBITMAP hOldBitmap = ::SelectObject(hMemDC, hBitmap); int i, j; for (i = 0; i < GetWidth(); i++) { - for (j = 0; j < GetHeight(); j++) - { - COLORREF color = ::GetPixel(hMemDC, i, j); - unsigned char red = GetRValue(color); - unsigned char green = GetGValue(color); - unsigned char blue = GetBValue(color); - - image.SetRGB(i, j, red, green, blue); - } + for (j = 0; j < GetHeight(); j++) + { + COLORREF color = ::GetPixel(hMemDC, i, j); + unsigned char red = GetRValue(color); + unsigned char green = GetGValue(color); + unsigned char blue = GetBValue(color); + + image.SetRGB(i, j, red, green, blue); + } } ::SelectObject(hMemDC, hOldBitmap); ::DeleteDC(hMemDC); - + #if wxUSE_PALETTE // Copy the palette from the source image if (GetPalette()) @@ -799,8 +983,7 @@ wxImage wxBitmap::ConvertToImage() const return image; -#else // __MICROWIN__ - +#else // !__WXMICROWIN__ wxImage image; wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") ); @@ -925,7 +1108,7 @@ wxImage wxBitmap::ConvertToImage() const free(lpBits); return image; -#endif +#endif // __WXMICROWIN__/!__WXMICROWIN__ } #endif // wxUSE_IMAGE @@ -948,7 +1131,7 @@ bool wxBitmap::LoadFile(const wxString& filename, long type) wxImage image; if ( image.LoadFile( filename, type ) && image.Ok() ) { - *this = image.ConvertToBitmap(); + *this = wxBitmap(image); return TRUE; } @@ -966,7 +1149,7 @@ bool wxBitmap::Create(void *data, long type, int width, int height, int depth) if ( !handler ) { - wxLogDebug(wxT("Failed to create bitmap: no bitmap handler for type %d defined."), type); + wxLogDebug(wxT("Failed to create bitmap: no bitmap handler for type %ld defined."), type); return FALSE; } @@ -990,7 +1173,7 @@ bool wxBitmap::SaveFile(const wxString& filename, else { // FIXME what about palette? shouldn't we use it? - wxImage image( *this ); + wxImage image = ConvertToImage(); if ( image.Ok() ) { return image.SaveFile(filename, type); @@ -1007,7 +1190,6 @@ bool wxBitmap::SaveFile(const wxString& filename, wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const { -#ifndef __WXMICROWIN__ wxCHECK_MSG( Ok() && (rect.x >= 0) && (rect.y >= 0) && (rect.x+rect.width <= GetWidth()) && @@ -1017,56 +1199,90 @@ wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const wxBitmap ret( rect.width, rect.height, GetDepth() ); wxASSERT_MSG( ret.Ok(), wxT("GetSubBitmap error") ); +#ifndef __WXMICROWIN__ // copy bitmap data - HDC dcSrc = ::CreateCompatibleDC(NULL); - HDC dcDst = ::CreateCompatibleDC(NULL); - SelectObject(dcSrc, (HBITMAP) GetHBITMAP()); - SelectObject(dcDst, (HBITMAP) ret.GetHBITMAP()); - BitBlt(dcDst, 0, 0, rect.width, rect.height, dcSrc, rect.x, rect.y, SRCCOPY); + MemoryHDC dcSrc, dcDst; + + { + SelectInHDC selectSrc(dcSrc, GetHbitmap()), + selectDst(dcDst, GetHbitmapOf(ret)); + + if ( !::BitBlt(dcDst, 0, 0, rect.width, rect.height, + dcSrc, rect.x, rect.y, SRCCOPY) ) + { + wxLogLastError(_T("BitBlt")); + } + } // copy mask if there is one - if (GetMask()) + if ( GetMask() ) { HBITMAP hbmpMask = ::CreateBitmap(rect.width, rect.height, 1, 1, 0); - SelectObject(dcSrc, (HBITMAP) GetMask()->GetMaskBitmap()); - SelectObject(dcDst, (HBITMAP) hbmpMask); - BitBlt(dcDst, 0, 0, rect.width, rect.height, dcSrc, rect.x, rect.y, SRCCOPY); + SelectInHDC selectSrc(dcSrc, (HBITMAP) GetMask()->GetMaskBitmap()), + selectDst(dcDst, hbmpMask); + + if ( !::BitBlt(dcDst, 0, 0, rect.width, rect.height, + dcSrc, rect.x, rect.y, SRCCOPY) ) + { + wxLogLastError(_T("BitBlt")); + } wxMask *mask = new wxMask((WXHBITMAP) hbmpMask); ret.SetMask(mask); } - - SelectObject(dcDst, NULL); - SelectObject(dcSrc, NULL); - DeleteDC(dcDst); - DeleteDC(dcSrc); +#endif // !__WXMICROWIN__ return ret; -#else - return wxBitmap(); -#endif } // ---------------------------------------------------------------------------- // wxBitmap accessors // ---------------------------------------------------------------------------- -void wxBitmap::SetQuality(int q) +wxPalette* wxBitmap::GetPalette() const { - EnsureHasData(); + return GetBitmapData() ? &GetBitmapData()->m_bitmapPalette + : (wxPalette *) NULL; +} - GetBitmapData()->m_quality = q; +wxMask *wxBitmap::GetMask() const +{ + return GetBitmapData() ? GetBitmapData()->GetMask() : (wxMask *) NULL; } -#if WXWIN_COMPATIBILITY_2 -void wxBitmap::SetOk(bool isOk) +wxDC *wxBitmap::GetSelectedInto() const { - EnsureHasData(); + return GetBitmapData() ? GetBitmapData()->m_selectedInto : (wxDC *) NULL; +} - GetBitmapData()->m_ok = isOk; +#if wxUSE_DIB_FOR_BITMAP + +bool wxBitmap::IsDIB() const +{ + return GetBitmapData() && GetBitmapData()->m_hFileMap != NULL; +} + +#endif // wxUSE_DIB_FOR_BITMAP + +#if WXWIN_COMPATIBILITY_2_4 + +int wxBitmap::GetQuality() const +{ + return 0; +} + +#endif // WXWIN_COMPATIBILITY_2_4 + +// ---------------------------------------------------------------------------- +// wxBitmap setters +// ---------------------------------------------------------------------------- + +void wxBitmap::SetSelectedInto(wxDC *dc) +{ + if ( GetBitmapData() ) + GetBitmapData()->m_selectedInto = dc; } -#endif // WXWIN_COMPATIBILITY_2 #if wxUSE_PALETTE @@ -1083,9 +1299,32 @@ void wxBitmap::SetMask(wxMask *mask) { EnsureHasData(); - GetBitmapData()->m_bitmapMask = mask; + GetBitmapData()->SetMask(mask); } +#if WXWIN_COMPATIBILITY_2 + +void wxBitmap::SetOk(bool isOk) +{ + EnsureHasData(); + + GetBitmapData()->m_ok = isOk; +} + +#endif // WXWIN_COMPATIBILITY_2 + +#if WXWIN_COMPATIBILITY_2_4 + +void wxBitmap::SetQuality(int WXUNUSED(quality)) +{ +} + +#endif // WXWIN_COMPATIBILITY_2_4 + +// ---------------------------------------------------------------------------- +// TODO: to be replaced by something better +// ---------------------------------------------------------------------------- + // Creates a bitmap that matches the device context, from // an arbitray bitmap. At present, the original bitmap must have an // associated palette. TODO: use a default palette if no palette exists. @@ -1260,7 +1499,7 @@ bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour) // scan the bitmap for the transparent colour and set the corresponding // pixels in the mask to BLACK and the rest to WHITE - COLORREF maskColour = RGB(colour.Red(), colour.Green(), colour.Blue()); + COLORREF maskColour = wxColourToPalRGB(colour); m_maskBitmap = (WXHBITMAP)::CreateBitmap(width, height, 1, 1, 0); HDC srcDC = ::CreateCompatibleDC(NULL);