X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3ca22d5e92f2a6689ebf569239eb127613c6c3cc..dab73021272104a5997f43658831658d8a783474:/src/msw/bitmap.cpp diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index 9be4159f48..dd6423fff7 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -5,8 +5,8 @@ // 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 ///////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -50,6 +50,10 @@ #include "wx/image.h" #include "wx/xpmdecod.h" +#ifdef wxHAVE_RAW_BITMAP +#include "wx/rawbmp.h" +#endif + // missing from mingw32 header #ifndef CLR_INVALID #define CLR_INVALID ((COLORREF)-1) @@ -98,10 +102,18 @@ public: wxDC *m_selectedInto; #endif // __WXDEBUG__ + // when GetRawData() is called for a DDB we need to convert it to a DIB + // first to be able to provide direct access to it and we cache that DIB + // here and convert it back to DDB when UngetRawData() is called + wxDIB *m_dib; + // true if we have alpha transparency info and can be drawn using // AlphaBlend() bool m_hasAlpha; + // true if our HBITMAP is a DIB section, false if it is a DDB + bool m_isDIB; + private: // optional mask for transparent drawing wxMask *m_bitmapMask; @@ -122,6 +134,34 @@ IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject) // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +// decide whether we should create a DIB or a DDB for the given parameters +static bool wxShouldCreateDIB(int w, int h, int d, WXHDC hdc) +{ + // here is the logic: + // + // (a) if hdc is specified, the caller explicitly wants DDB + // (b) otherwise, create a DIB if depth >= 24 (we don't support 16bpp or + // less DIBs anyhow) + // (c) finally, create DIBs under Win9x even if the depth hasn't been + // explicitly specified but the current display depth is 24 or more + // and the image is "big", i.e. > 16Mb which is the theoretical limit + // for DDBs under Win9x + // + // consequences (all of which seem to make sense): + // + // (i) by default, DDBs are created (depth == -1 usually) + // (ii) DIBs can be created by explicitly specifying the depth + // (iii) using a DC always forces creating a DDB + return !hdc && + (d >= 24 || + (d == -1 && + wxDIB::GetLineSize(w, wxDisplayDepth())*h > 16*1024*1024)); +} + // ---------------------------------------------------------------------------- // wxBitmapRefData // ---------------------------------------------------------------------------- @@ -132,7 +172,11 @@ wxBitmapRefData::wxBitmapRefData() m_selectedInto = NULL; #endif m_bitmapMask = NULL; + m_hBitmap = (WXHBITMAP) NULL; + m_dib = NULL; + + m_isDIB = m_hasAlpha = FALSE; } @@ -141,6 +185,8 @@ void wxBitmapRefData::Free() wxASSERT_MSG( !m_selectedInto, wxT("deleting bitmap still selected into wxMemoryDC") ); + wxASSERT_MSG( !m_dib, _T("forgot to call wxBitmap::UngetRawData()!") ); + if ( m_hBitmap) { if ( !::DeleteObject((HBITMAP)m_hBitmap) ) @@ -161,7 +207,6 @@ void wxBitmapRefData::Free() void wxBitmap::Init() { // m_refData = NULL; done in the base class ctor - } wxGDIImageRefData *wxBitmap::CreateData() const @@ -277,6 +322,38 @@ bool wxBitmap::CopyFromIcon(const wxIcon& icon) #endif // Win16/Win32 } +bool wxBitmap::CopyFromDIB(const wxDIB& dib) +{ + wxCHECK_MSG( dib.IsOk(), FALSE, _T("invalid DIB in CopyFromDIB") ); + + HBITMAP hbitmap = dib.CreateDDB(); + if ( !hbitmap ) + return FALSE; + + UnRef(); + + wxBitmapRefData *refData = new wxBitmapRefData; + m_refData = refData; + + refData->m_width = dib.GetWidth(); + refData->m_height = dib.GetHeight(); + refData->m_depth = dib.GetDepth(); + + refData->m_hBitmap = (WXHBITMAP)hbitmap; + +#if wxUSE_PALETTE + wxPalette *palette = dib.CreatePalette(); + if ( palette ) + { + refData->m_bitmapPalette = *palette; + } + + delete palette; +#endif // wxUSE_PALETTE + + return TRUE; +} + wxBitmap::~wxBitmap() { } @@ -373,6 +450,13 @@ wxBitmap::wxBitmap(int w, int h, int d) (void)Create(w, h, d); } +wxBitmap::wxBitmap(int w, int h, const wxDC& dc) +{ + Init(); + + (void)Create(w, h, dc); +} + wxBitmap::wxBitmap(void *data, long type, int width, int height, int depth) { Init(); @@ -387,26 +471,54 @@ wxBitmap::wxBitmap(const wxString& filename, wxBitmapType type) LoadFile(filename, (int)type); } -bool wxBitmap::Create(int w, int h, int d) +bool wxBitmap::Create(int width, int height, int depth) +{ + return DoCreate(width, height, depth, 0); +} + +bool wxBitmap::Create(int width, int height, const wxDC& dc) +{ + wxCHECK_MSG( dc.Ok(), FALSE, _T("invalid HDC in wxBitmap::Create()") ); + + return DoCreate(width, height, -1, dc.GetHDC()); +} + +bool wxBitmap::DoCreate(int w, int h, int d, WXHDC hdc) { UnRef(); m_refData = new wxBitmapRefData; -#if wxUSE_DIB_FOR_BITMAP - if ( w && h && d >= 16 ) + GetBitmapData()->m_width = w; + GetBitmapData()->m_height = h; + + HBITMAP hbmp; + + if ( wxShouldCreateDIB(w, h, d, hdc) ) { - if ( !CreateDIB(w, h, d) ) + if ( d == -1 ) + { + // create DIBs without alpha channel by default + d = 24; + } + + wxDIB dib(w, h, d); + if ( !dib.IsOk() ) return FALSE; + + // don't delete the DIB section in dib object dtor + hbmp = dib.Detach(); + + GetBitmapData()->m_isDIB = TRUE; + GetBitmapData()->m_depth = d; } - else -#endif // wxUSE_DIB_FOR_BITMAP + else // create a DDB { - GetBitmapData()->m_width = w; - GetBitmapData()->m_height = h; + if ( d == -1 ) + d = wxDisplayDepth(); + GetBitmapData()->m_depth = d; - HBITMAP hbmp; #ifndef __WXMICROWIN__ if ( d > 0 ) { @@ -428,13 +540,13 @@ bool wxBitmap::Create(int w, int h, int d) 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(); } @@ -449,7 +561,7 @@ bool wxBitmap::Create(int w, int h, int d) // make sense to use #ifdefs inside the function bodies #ifdef __WXMICROWIN__ -bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) +bool wxBitmap::CreateFromImage(const wxImage& image, int depth, const wxDC& dc) { // Set this to 1 to experiment with mask code, // which currently doesn't work @@ -637,7 +749,20 @@ wxImage wxBitmap::ConvertToImage() const // wxImage to/from conversions // ---------------------------------------------------------------------------- -bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) +bool wxBitmap::CreateFromImage(const wxImage& image, int depth) +{ + return CreateFromImage(image, depth, 0); +} + +bool wxBitmap::CreateFromImage(const wxImage& image, const wxDC& dc) +{ + wxCHECK_MSG( dc.Ok(), FALSE, + _T("invalid HDC in wxBitmap::CreateFromImage()") ); + + return CreateFromImage(image, -1, dc.GetHDC()); +} + +bool wxBitmap::CreateFromImage(const wxImage& image, int depth, WXHDC hdc ) { wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") ); @@ -656,7 +781,6 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) wxBitmapRefData *refData = new wxBitmapRefData; refData->m_width = w; refData->m_height = h; - refData->m_depth = dib.GetDepth(); refData->m_hasAlpha = image.HasAlpha(); m_refData = refData; @@ -665,86 +789,22 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth ) // next either store DIB as is or create a DDB from it HBITMAP hbitmap; - // TODO: if we're ready to use DIB as is, we can just do this: - // if ( ... ) - // hbitmap = dib.Detach(); - // else + // are we going to use DIB? + // + // NB: DDBs don't support alpha so if we have alpha channel we must use DIB + if ( image.HasAlpha() || wxShouldCreateDIB(w, h, depth, hdc) ) { - // create and set the device-dependent bitmap - // - // VZ: why don't we just use SetDIBits() instead? because of the - // palette or is there some other reason? - hbitmap = ::CreateCompatibleBitmap(ScreenHDC(), w, h); - if ( !hbitmap ) - { - wxLogLastError(_T("CreateCompatibleBitmap()")); - - return FALSE; - } - - MemoryHDC hdcMem; - SelectInHDC select(hdcMem, hbitmap); - if ( !select ) - { - wxLogLastError(_T("SelectObjct(hBitmap)")); - } - -#if wxUSE_PALETTE - const wxPalette& palette = image.GetPalette(); - - HPALETTE hOldPalette; - if ( palette.Ok() ) - { - SetPalette(palette); - - hOldPalette = ::SelectPalette - ( - hdcMem, - GetHpaletteOf(palette), - FALSE // ignored for hdcMem - ); - - if ( !hOldPalette ) - { - wxLogLastError(_T("SelectPalette()")); - } - - if ( ::RealizePalette(hdcMem) == GDI_ERROR ) - { - wxLogLastError(_T("RealizePalette()")); - } - } - else // no valid palette - { - hOldPalette = 0; - } -#endif // wxUSE_PALETTE - - DIBSECTION ds; - if ( !::GetObject(dib.GetHandle(), sizeof(ds), &ds) ) - { - wxLogLastError(_T("GetObject(hDIB)")); - } + // don't delete the DIB section in dib object dtor + hbitmap = dib.Detach(); - if ( ::StretchDIBits(hdcMem, - 0, 0, w, h, - 0, 0, w, h, - dib.GetData(), - (BITMAPINFO *)&ds.dsBmih, - DIB_RGB_COLORS, - SRCCOPY) == GDI_ERROR ) - { - wxLogLastError(_T("StretchDIBits()")); - - return FALSE; - } + refData->m_isDIB = TRUE; + refData->m_depth = dib.GetDepth(); + } + else // we need to convert DIB to DDB + { + hbitmap = dib.CreateDDB((HDC)hdc); -#if wxUSE_PALETTE - if ( hOldPalette ) - { - ::SelectPalette(hdcMem, hOldPalette, FALSE); - } -#endif // wxUSE_PALETTE + refData->m_depth = depth == -1 ? wxDisplayDepth() : depth; } // validate this object @@ -1034,11 +1094,13 @@ wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const // wxBitmap accessors // ---------------------------------------------------------------------------- +#if wxUSE_PALETTE wxPalette* wxBitmap::GetPalette() const { return GetBitmapData() ? &GetBitmapData()->m_bitmapPalette : (wxPalette *) NULL; } +#endif wxMask *wxBitmap::GetMask() const { @@ -1063,6 +1125,12 @@ int wxBitmap::GetQuality() const #endif // WXWIN_COMPATIBILITY_2_4 +void wxBitmap::UseAlpha() +{ + if ( GetBitmapData() ) + GetBitmapData()->m_hasAlpha = true; +} + bool wxBitmap::HasAlpha() const { return GetBitmapData() && GetBitmapData()->m_hasAlpha; @@ -1120,69 +1188,142 @@ void wxBitmap::SetQuality(int WXUNUSED(quality)) #endif // WXWIN_COMPATIBILITY_2_4 // ---------------------------------------------------------------------------- -// TODO: to be replaced by something better +// raw bitmap access support // ---------------------------------------------------------------------------- -// 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. -// Contributed by Frederic Villeneuve -wxBitmap wxBitmap::GetBitmapForDC(wxDC& dc) const +#ifdef wxHAVE_RAW_BITMAP +void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp) { -#ifdef __WXMICROWIN__ - return *this; -#else - wxMemoryDC memDC; - wxBitmap tmpBitmap(GetWidth(), GetHeight(), dc.GetDepth()); - HPALETTE hPal = (HPALETTE) NULL; - LPBITMAPINFO lpDib; - void *lpBits = (void*) NULL; + if ( !Ok() ) + { + // no bitmap, no data (raw or otherwise) + return NULL; + } -#if wxUSE_PALETTE - if( GetPalette() && GetPalette()->Ok() ) + // if we're already a DIB we can access our data directly, but if not we + // need to convert this DDB to a DIB section and use it for raw access and + // then convert it back + HBITMAP hDIB; + if ( !GetBitmapData()->m_isDIB ) { - tmpBitmap.SetPalette(*GetPalette()); - memDC.SelectObject(tmpBitmap); - memDC.SetPalette(*GetPalette()); - hPal = (HPALETTE)GetPalette()->GetHPALETTE(); + wxCHECK_MSG( !GetBitmapData()->m_dib, FALSE, + _T("GetRawData() may be called only once") ); + + wxDIB *dib = new wxDIB(*this); + if ( !dib->IsOk() ) + { + delete dib; + + return NULL; + } + + // we'll free it in UngetRawData() + GetBitmapData()->m_dib = dib; + + hDIB = dib->GetHandle(); } - else + else // we're a DIB { - hPal = (HPALETTE) ::GetStockObject(DEFAULT_PALETTE); - wxPalette palette; - palette.SetHPALETTE( (WXHPALETTE)hPal ); - tmpBitmap.SetPalette( palette ); - memDC.SelectObject(tmpBitmap); - memDC.SetPalette( palette ); + hDIB = GetHbitmap(); } -#else // !wxUSE_PALETTE - hPal = (HPALETTE) ::GetStockObject(DEFAULT_PALETTE); -#endif // wxUSE_PALETTE/!wxUSE_PALETTE - // set the height negative because in a DIB the order of the lines is - // reversed - if ( !wxCreateDIB(GetWidth(), -GetHeight(), GetDepth(), hPal, &lpDib) ) + DIBSECTION ds; + if ( ::GetObject(hDIB, sizeof(ds), &ds) != sizeof(DIBSECTION) ) { - return wxNullBitmap; + wxFAIL_MSG( _T("failed to get DIBSECTION from a DIB?") ); + + return NULL; } - lpBits = malloc(lpDib->bmiHeader.biSizeImage); + // check that the bitmap is in correct format + if ( ds.dsBm.bmBitsPixel != bpp ) + { + wxFAIL_MSG( _T("incorrect bitmap type in wxBitmap::GetRawData()") ); + + return NULL; + } - ::GetBitmapBits(GetHbitmap(), lpDib->bmiHeader.biSizeImage, lpBits); + // ok, store the relevant info in wxPixelDataBase + const LONG h = ds.dsBm.bmHeight; - ::SetDIBitsToDevice(GetHdcOf(memDC), 0, 0, - GetWidth(), GetHeight(), - 0, 0, 0, GetHeight(), - lpBits, lpDib, DIB_RGB_COLORS); + data.m_width = ds.dsBm.bmWidth; + data.m_height = h; - free(lpBits); + // remember that DIBs are stored in top to bottom order! + const LONG bytesPerRow = ds.dsBm.bmWidthBytes; + data.m_stride = -bytesPerRow; - wxFreeDIB(lpDib); + char *bits = (char *)ds.dsBm.bmBits; + if ( h > 1 ) + { + bits += (h - 1)*bytesPerRow; + } - return tmpBitmap; -#endif + return bits; } +void wxBitmap::UngetRawData(wxPixelDataBase& dataBase) +{ + if ( !Ok() ) + return; + + // the cast is ugly but we can't do without it and without making this + // function template (and hence inline) unfortunately + typedef wxPixelData PixelData; + PixelData& data = (PixelData &)dataBase; + + if ( !data ) + { + // invalid data, don't crash -- but don't assert neither as we're + // called automatically from wxPixelDataBase dtor and so there is no + // way to prevent this from happening + return; + } + + if ( GetBitmapData()->m_hasAlpha ) + { + // AlphaBlend() wants to have premultiplied source alpha but + // wxRawBitmap API uses normal, not premultiplied, colours, so adjust + // them here now + PixelData::Iterator p(data); + + const int w = data.GetWidth(); + const int h = data.GetHeight(); + + for ( int y = 0; y < h; y++ ) + { + PixelData::Iterator rowStart = p; + + for ( int x = 0; x < w; x++ ) + { + const unsigned alpha = p.Alpha(); + + p.Red() = (p.Red() * alpha + 127) / 255; + p.Blue() = (p.Blue() * alpha + 127) / 255; + p.Green() = (p.Green() * alpha + 127) / 255; + + ++p; + } + + p = rowStart; + p.OffsetY(data, 1); + } + + // if we're a DDB we need to convert DIB back to DDB now to make the + // changes made via raw bitmap access effective + if ( !GetBitmapData()->m_isDIB ) + { + wxDIB *dib = GetBitmapData()->m_dib; + GetBitmapData()->m_dib = NULL; + + // TODO: convert + + delete dib; + } + } +} +#endif // #ifdef wxHAVE_RAW_BITMAP + // ---------------------------------------------------------------------------- // wxMask // ---------------------------------------------------------------------------- @@ -1464,10 +1605,81 @@ void wxFreeDIB(LPBITMAPINFO lpDIBHeader) #endif // ---------------------------------------------------------------------------- -// other helper functions +// global helper functions implemented here // ---------------------------------------------------------------------------- -extern HBITMAP wxInvertMask(HBITMAP hbmpMask, int w, int h) +// helper of wxBitmapToHICON/HCURSOR +static +HICON wxBitmapToIconOrCursor(const wxBitmap& bmp, + bool iconWanted, + int hotSpotX, + int hotSpotY) +{ + if ( !bmp.Ok() ) + { + // we can't create an icon/cursor form nothing + return 0; + } + + wxMask *mask = bmp.GetMask(); + if ( !mask ) + { + // we must have a mask for an icon, so even if it's probably incorrect, + // do create it (grey is the "standard" transparent colour) + mask = new wxMask(bmp, *wxLIGHT_GREY); + } + + ICONINFO iconInfo; + iconInfo.fIcon = iconWanted; // do we want an icon or a cursor? + if ( !iconWanted ) + { + iconInfo.xHotspot = hotSpotX; + iconInfo.yHotspot = hotSpotY; + } + + iconInfo.hbmMask = wxInvertMask((HBITMAP)mask->GetMaskBitmap()); + iconInfo.hbmColor = GetHbitmapOf(bmp); + + // black out the transparent area to preserve background colour, because + // Windows blits the original bitmap using SRCINVERT (XOR) after applying + // the mask to the dest rect. + { + MemoryHDC dcSrc, dcDst; + SelectInHDC selectMask(dcSrc, (HBITMAP)mask->GetMaskBitmap()), + selectBitmap(dcDst, iconInfo.hbmColor); + + if ( !::BitBlt(dcDst, 0, 0, bmp.GetWidth(), bmp.GetHeight(), + dcSrc, 0, 0, SRCAND) ) + { + wxLogLastError(_T("BitBlt")); + } + } + + HICON hicon = ::CreateIconIndirect(&iconInfo); + + if ( !bmp.GetMask() ) + { + // we created the mask, now delete it + delete mask; + } + + // delete the inverted mask bitmap we created as well + ::DeleteObject(iconInfo.hbmMask); + + return hicon; +} + +HICON wxBitmapToHICON(const wxBitmap& bmp) +{ + return wxBitmapToIconOrCursor(bmp, TRUE, 0, 0); +} + +HCURSOR wxBitmapToHCURSOR(const wxBitmap& bmp, int hotSpotX, int hotSpotY) +{ + return (HCURSOR)wxBitmapToIconOrCursor(bmp, FALSE, hotSpotX, hotSpotY); +} + +HBITMAP wxInvertMask(HBITMAP hbmpMask, int w, int h) { #ifndef __WXMICROWIN__ wxCHECK_MSG( hbmpMask, 0, _T("invalid bitmap in wxInvertMask") );