X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6d167489bdf17d55d9bd11be834bc17277661063..f0c5ebdc0d881e5478f166bab335265460409ec9:/src/msw/bitmap.cpp diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index fe11b93585..d5b37d2cfd 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -46,16 +46,19 @@ #include "wx/msw/dib.h" #include "wx/image.h" +// missing from mingw32 header +#ifndef CLR_INVALID + #define CLR_INVALID ((COLORREF)-1) +#endif // no CLR_INVALID + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARIES - IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject) - IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject) +IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject) +IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject) - IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject) -#endif +IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject) // ============================================================================ // implementation @@ -71,6 +74,7 @@ wxBitmapRefData::wxBitmapRefData() m_selectedInto = NULL; m_numColors = 0; m_bitmapMask = NULL; + m_hBitmap = (WXHBITMAP) NULL; } void wxBitmapRefData::Free() @@ -82,7 +86,7 @@ void wxBitmapRefData::Free() { if ( !::DeleteObject((HBITMAP)m_hBitmap) ) { - wxLogLastError("DeleteObject(hbitmap)"); + wxLogLastError(wxT("DeleteObject(hbitmap)")); } } @@ -113,7 +117,7 @@ bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon) ICONINFO iconInfo; if ( !::GetIconInfo(hicon, &iconInfo) ) { - wxLogLastError("GetIconInfo"); + wxLogLastError(wxT("GetIconInfo")); return FALSE; } @@ -121,12 +125,19 @@ bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon) wxBitmapRefData *refData = new wxBitmapRefData; m_refData = refData; - refData->m_width = icon.GetWidth(); - refData->m_height = icon.GetHeight(); + int w = icon.GetWidth(), + h = icon.GetHeight(); + + refData->m_width = w; + refData->m_height = h; refData->m_depth = wxDisplayDepth(); refData->m_hBitmap = (WXHBITMAP)iconInfo.hbmColor; - refData->m_bitmapMask = new wxMask((WXHBITMAP)iconInfo.hbmMask); + + // the mask returned by GetIconInfo() is inversed compared to the usual + // wxWin convention + refData->m_bitmapMask = new wxMask((WXHBITMAP) + wxInvertMask(iconInfo.hbmMask, w, h)); #if WXWIN_COMPATIBILITY_2 refData->m_ok = TRUE; @@ -148,9 +159,9 @@ bool wxBitmap::CopyFromCursor(const wxCursor& cursor) wxFAIL_MSG( _T("don't know how to convert cursor to bitmap") ); return FALSE; -#endif // Win16 - +#else return CopyFromIconOrCursor(cursor); +#endif // Win16 } bool wxBitmap::CopyFromIcon(const wxIcon& icon) @@ -203,34 +214,78 @@ wxBitmap::~wxBitmap() wxTheBitmapList->DeleteObject(this); } -wxBitmap::wxBitmap(const char bits[], int the_width, int the_height, int no_bits) +wxBitmap::wxBitmap(const char bits[], int width, int height, int depth) { Init(); wxBitmapRefData *refData = new wxBitmapRefData; m_refData = refData; - refData->m_width = the_width; - refData->m_height = the_height; - refData->m_depth = no_bits; + refData->m_width = width; + refData->m_height = height; + refData->m_depth = depth; refData->m_numColors = 0; refData->m_selectedInto = NULL; - HBITMAP hbmp = ::CreateBitmap(the_width, the_height, 1, no_bits, bits); + char *data; + if ( depth == 1 ) + { + // we assume that it is in XBM format which is not quite the same as + // the format CreateBitmap() wants because the order of bytes in the + // line is inversed! + static const size_t bytesPerLine = (width + 7) / 8; + static const size_t padding = bytesPerLine % 2; + static const size_t len = height * ( padding + bytesPerLine ); + data = (char *)malloc(len); + const char *src = bits; + char *dst = data; + + for ( int rows = 0; rows < height; rows++ ) + { + for ( size_t cols = 0; cols < bytesPerLine; cols++ ) + { + unsigned char val = *src++; + unsigned char reversed = 0; + + for ( int bits = 0; bits < 8; bits++) + { + reversed <<= 1; + reversed |= (val & 0x01); + val >>= 1; + } + *dst++ = reversed; + } + + if ( padding ) + *dst++ = 0; + } + } + else + { + // bits should already be in Windows standard format + data = (char *)bits; // const_cast is harmless + } + + HBITMAP hbmp = ::CreateBitmap(width, height, 1, depth, data); if ( !hbmp ) { - wxLogLastError("CreateBitmap"); + wxLogLastError(wxT("CreateBitmap")); + } + + if ( data != bits ) + { + free(data); } SetHBITMAP((WXHBITMAP)hbmp); } // Create from XPM data -wxBitmap::wxBitmap(char **data, wxControl *WXUNUSED(anItem)) +bool wxBitmap::CreateFromXpm(const char **data) { Init(); - (void)Create((void *)data, wxBITMAP_TYPE_XPM_DATA, 0, 0, 0); + return Create((void *)data, wxBITMAP_TYPE_XPM_DATA, 0, 0, 0); } wxBitmap::wxBitmap(int w, int h, int d) @@ -271,7 +326,7 @@ bool wxBitmap::Create(int w, int h, int d) hbmp = ::CreateBitmap(w, h, 1, d, NULL); if ( !hbmp ) { - wxLogLastError("CreateBitmap"); + wxLogLastError(wxT("CreateBitmap")); } } else @@ -280,7 +335,7 @@ bool wxBitmap::Create(int w, int h, int d) hbmp = ::CreateCompatibleBitmap(dc, w, h); if ( !hbmp ) { - wxLogLastError("CreateCompatibleBitmap"); + wxLogLastError(wxT("CreateCompatibleBitmap")); } GetBitmapData()->m_depth = wxDisplayDepth(); @@ -327,8 +382,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 %d defined."), type); return FALSE; } @@ -357,6 +411,49 @@ bool wxBitmap::SaveFile(const wxString& filename, int type, const wxPalette *pal } } +// ---------------------------------------------------------------------------- +// sub bitmap extraction +// ---------------------------------------------------------------------------- + +wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const +{ + wxCHECK_MSG( Ok() && + (rect.x >= 0) && (rect.y >= 0) && + (rect.x+rect.width <= GetWidth()) && + (rect.y+rect.height <= GetHeight()), + wxNullBitmap, wxT("Invalid bitmap or bitmap region") ); + + wxBitmap ret( rect.width, rect.height, GetDepth() ); + wxASSERT_MSG( ret.Ok(), wxT("GetSubBitmap error") ); + + // 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); + + // copy mask if there is one + 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); + + wxMask *mask = new wxMask((WXHBITMAP) hbmpMask); + ret.SetMask(mask); + } + + SelectObject(dcDst, NULL); + SelectObject(dcSrc, NULL); + DeleteDC(dcDst); + DeleteDC(dcSrc); + + return ret; +} + // ---------------------------------------------------------------------------- // wxBitmap accessors // ---------------------------------------------------------------------------- @@ -398,7 +495,7 @@ void wxBitmap::SetMask(wxMask *mask) wxBitmap wxBitmap::GetBitmapForDC(wxDC& dc) const { wxMemoryDC memDC; - wxBitmap tmpBitmap(this->GetWidth(), this->GetHeight(), dc.GetDepth()); + wxBitmap tmpBitmap(GetWidth(), GetHeight(), dc.GetDepth()); HPALETTE hPal = (HPALETTE) NULL; LPBITMAPINFO lpDib; void *lpBits = (void*) NULL; @@ -484,15 +581,15 @@ wxMask::~wxMask() // Create a mask from a mono bitmap (copies the bitmap). bool wxMask::Create(const wxBitmap& bitmap) { + wxCHECK_MSG( bitmap.Ok() && bitmap.GetDepth() == 1, FALSE, + _T("can't create mask from invalid or not monochrome bitmap") ); + if ( m_maskBitmap ) { ::DeleteObject((HBITMAP) m_maskBitmap); m_maskBitmap = 0; } - if (!bitmap.Ok() || bitmap.GetDepth() != 1) - { - return FALSE; - } + m_maskBitmap = (WXHBITMAP) CreateBitmap( bitmap.GetWidth(), bitmap.GetHeight(), @@ -535,38 +632,65 @@ bool wxMask::Create(const wxBitmap& bitmap, int paletteIndex) // the transparent area bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour) { + wxCHECK_MSG( bitmap.Ok(), FALSE, _T("invalid bitmap in wxMask::Create") ); + if ( m_maskBitmap ) { ::DeleteObject((HBITMAP) m_maskBitmap); m_maskBitmap = 0; } - if (!bitmap.Ok()) + + int width = bitmap.GetWidth(), + height = bitmap.GetHeight(); + + // scan the bitmap for the transparent colour and set the corresponding + // pixels in the mask to BLACK and the rest to WHITE + COLORREF maskColour = wxColourToRGB(colour); + m_maskBitmap = (WXHBITMAP)::CreateBitmap(width, height, 1, 1, 0); + + HDC srcDC = ::CreateCompatibleDC(NULL); + HDC destDC = ::CreateCompatibleDC(NULL); + if ( !srcDC || !destDC ) { - return FALSE; + wxLogLastError(wxT("CreateCompatibleDC")); + } + + bool ok = TRUE; + + HGDIOBJ hbmpSrcOld = ::SelectObject(srcDC, GetHbitmapOf(bitmap)); + if ( !hbmpSrcOld ) + { + wxLogLastError(wxT("SelectObject")); + + ok = FALSE; } - // 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()); - m_maskBitmap = (WXHBITMAP) ::CreateBitmap( - bitmap.GetWidth(), - bitmap.GetHeight(), - 1, 1, 0 - ); - HDC srcDC = ::CreateCompatibleDC(0); - ::SelectObject(srcDC, (HBITMAP) bitmap.GetHBITMAP()); - HDC destDC = ::CreateCompatibleDC(0); - ::SelectObject(destDC, (HBITMAP) m_maskBitmap); - - // this is not very efficient, but I can't think - // of a better way of doing it - for (int w = 0; w < bitmap.GetWidth(); w++) + HGDIOBJ hbmpDstOld = ::SelectObject(destDC, (HBITMAP)m_maskBitmap); + if ( !hbmpDstOld ) { - for (int h = 0; h < bitmap.GetHeight(); h++) + wxLogLastError(wxT("SelectObject")); + + ok = FALSE; + } + + // this is not very efficient, but I can't think of a better way of doing + // it + for ( int w = 0; ok && (w < width); w++ ) + { + for ( int h = 0; ok && (h < height); h++ ) { COLORREF col = GetPixel(srcDC, w, h); - if (col == maskColour) + if ( col == CLR_INVALID ) + { + wxLogLastError(wxT("GetPixel")); + + // doesn't make sense to continue + ok = FALSE; + + break; + } + + if ( col == maskColour ) { ::SetPixel(destDC, w, h, RGB(0, 0, 0)); } @@ -576,11 +700,13 @@ bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour) } } } - ::SelectObject(srcDC, 0); + + ::SelectObject(srcDC, hbmpSrcOld); ::DeleteDC(srcDC); - ::SelectObject(destDC, 0); + ::SelectObject(destDC, hbmpDstOld); ::DeleteDC(destDC); - return TRUE; + + return ok; } // ---------------------------------------------------------------------------- @@ -594,7 +720,7 @@ bool wxBitmapHandler::Create(wxGDIImage *image, { wxBitmap *bitmap = wxDynamicCast(image, wxBitmap); - return bitmap ? Create(bitmap, data, width, height, depth) : FALSE; + return bitmap ? Create(bitmap, data, flags, width, height, depth) : FALSE; } bool wxBitmapHandler::Load(wxGDIImage *image, @@ -695,4 +821,47 @@ void wxFreeDIB(LPBITMAPINFO lpDIBHeader) free(lpDIBHeader); } +// ---------------------------------------------------------------------------- +// other helper functions +// ---------------------------------------------------------------------------- + +extern HBITMAP wxInvertMask(HBITMAP hbmpMask, int w, int h) +{ + wxCHECK_MSG( hbmpMask, 0, _T("invalid bitmap in wxInvertMask") ); + + // get width/height from the bitmap if not given + if ( !w || !h ) + { + BITMAP bm; + ::GetObject(hbmpMask, sizeof(BITMAP), (LPVOID)&bm); + w = bm.bmWidth; + h = bm.bmHeight; + } + + HDC hdcSrc = ::CreateCompatibleDC(NULL); + HDC hdcDst = ::CreateCompatibleDC(NULL); + if ( !hdcSrc || !hdcDst ) + { + wxLogLastError(wxT("CreateCompatibleDC")); + } + + HBITMAP hbmpInvMask = ::CreateBitmap(w, h, 1, 1, 0); + if ( !hbmpInvMask ) + { + wxLogLastError(wxT("CreateBitmap")); + } + ::SelectObject(hdcSrc, hbmpMask); + ::SelectObject(hdcDst, hbmpInvMask); + if ( !::BitBlt(hdcDst, 0, 0, w, h, + hdcSrc, 0, 0, + NOTSRCCOPY) ) + { + wxLogLastError(wxT("BitBlt")); + } + + ::DeleteDC(hdcSrc); + ::DeleteDC(hdcDst); + + return hbmpInvMask; +}