///////////////////////////////////////////////////////////////////////////// // Name: src/osx/core/bitmap.cpp // Purpose: wxBitmap // Author: Stefan Csomor // Modified by: // Created: 1998-01-01 // RCS-ID: $Id$ // Copyright: (c) Stefan Csomor // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #include "wx/wxprec.h" #include "wx/bitmap.h" #ifndef WX_PRECOMP #include "wx/log.h" #include "wx/dcmemory.h" #include "wx/icon.h" #include "wx/image.h" #endif #include "wx/metafile.h" #include "wx/xpmdecod.h" #include "wx/rawbmp.h" IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject) IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject) #if wxOSX_USE_CARBON #include "wx/osx/uma.h" #else #include "wx/osx/private.h" #endif #ifndef __WXOSX_IPHONE__ #include #endif CGColorSpaceRef wxMacGetGenericRGBColorSpace(); CGDataProviderRef wxMacCGDataProviderCreateWithMemoryBuffer( const wxMemoryBuffer& buf ); // Implementation Notes // -------------------- // // we are always working with a 32 bit deep pixel buffer // under QuickDraw its alpha parts are going to be ignored in the GWorld, // therefore we have a separate GWorld there for blitting the mask in // under Quartz then content is transformed into a CGImageRef representing the same data // which can be transferred to the GPU by the OS for fast rendering class WXDLLEXPORT wxBitmapRefData: public wxGDIRefData { friend class WXDLLIMPEXP_FWD_CORE wxIcon; friend class WXDLLIMPEXP_FWD_CORE wxCursor; public: wxBitmapRefData(int width , int height , int depth); wxBitmapRefData(); wxBitmapRefData(const wxBitmapRefData &tocopy); virtual ~wxBitmapRefData(); virtual bool IsOk() const { return m_ok; } void Free(); void SetOk( bool isOk) { m_ok = isOk; } void SetWidth( int width ) { m_width = width; } void SetHeight( int height ) { m_height = height; } void SetDepth( int depth ) { m_depth = depth; } int GetWidth() const { return m_width; } int GetHeight() const { return m_height; } int GetDepth() const { return m_depth; } void *GetRawAccess() const; void *BeginRawAccess(); void EndRawAccess(); bool HasAlpha() const { return m_hasAlpha; } void UseAlpha( bool useAlpha ); public: #if wxUSE_PALETTE wxPalette m_bitmapPalette; #endif // wxUSE_PALETTE wxMask * m_bitmapMask; // Optional mask CGImageRef CreateCGImage() const; // returns true if the bitmap has a size that // can be natively transferred into a true icon // if no is returned GetIconRef will still produce // an icon but it will be generated via a PICT and // rescaled to 16 x 16 bool HasNativeSize(); // caller should increase ref count if needed longer // than the bitmap exists IconRef GetIconRef(); #ifndef __WXOSX_IPHONE__ // returns a Pict from the bitmap content PicHandle GetPictHandle(); #endif CGContextRef GetBitmapContext() const; int GetBytesPerRow() const { return m_bytesPerRow; } private : bool Create(int width , int height , int depth); void Init(); int m_width; int m_height; int m_bytesPerRow; int m_depth; bool m_hasAlpha; wxMemoryBuffer m_memBuf; int m_rawAccessCount; bool m_ok; mutable CGImageRef m_cgImageRef; IconRef m_iconRef; #ifndef __WXOSX_IPHONE__ PicHandle m_pictHandle; #endif CGContextRef m_hBitmap; }; #define wxOSX_USE_PREMULTIPLIED_ALPHA 1 static const int kBestByteAlignement = 16; static const int kMaskBytesPerPixel = 1; static int GetBestBytesPerRow( int rawBytes ) { return (((rawBytes)+kBestByteAlignement-1) & ~(kBestByteAlignement-1) ); } #if wxUSE_GUI && !defined(__WXOSX_IPHONE__) // this is used for more controls than just the wxBitmap button, also for notebooks etc void wxMacCreateBitmapButton( ControlButtonContentInfo*info , const wxBitmap& bitmap , int forceType ) { memset( info , 0 , sizeof(ControlButtonContentInfo) ) ; if ( bitmap.Ok() ) { wxBitmapRefData * bmap = bitmap.GetBitmapData() ; if ( bmap == NULL ) return ; if ( forceType == 0 ) { forceType = kControlContentCGImageRef; } if ( forceType == kControlContentIconRef ) { wxBitmap scaleBmp ; wxBitmapRefData* bmp = bmap ; if ( !bmap->HasNativeSize() ) { // as PICT conversion will only result in a 16x16 icon, let's attempt // a few scales for better results int w = bitmap.GetWidth() ; int h = bitmap.GetHeight() ; int sz = wxMax( w , h ) ; if ( sz == 24 || sz == 64 ) { scaleBmp = wxBitmap( bitmap.ConvertToImage().Scale( w * 2 , h * 2 ) ) ; bmp = scaleBmp.GetBitmapData() ; } } info->contentType = kControlContentIconRef ; info->u.iconRef = bmp->GetIconRef() ; AcquireIconRef( info->u.iconRef ) ; } else if ( forceType == kControlContentCGImageRef ) { info->contentType = kControlContentCGImageRef ; info->u.imageRef = (CGImageRef) bmap->CreateCGImage() ; } else { #ifndef __LP64__ info->contentType = kControlContentPictHandle ; info->u.picture = bmap->GetPictHandle() ; #endif } } } CGImageRef wxMacCreateCGImageFromBitmap( const wxBitmap& bitmap ) { wxBitmapRefData * bmap = bitmap.GetBitmapData() ; if ( bmap == NULL ) return NULL ; return (CGImageRef) bmap->CreateCGImage(); } void wxMacReleaseBitmapButton( ControlButtonContentInfo*info ) { if ( info->contentType == kControlContentIconRef ) { ReleaseIconRef( info->u.iconRef ) ; } else if ( info->contentType == kControlNoContent ) { // there's no bitmap at all, fall through silently } else if ( info->contentType == kControlContentPictHandle ) { // owned by the bitmap, no release here } else if ( info->contentType == kControlContentCGImageRef ) { CGImageRelease( info->u.imageRef ) ; } else { wxFAIL_MSG(wxT("Unexpected bitmap type") ) ; } } #endif //wxUSE_BMPBUTTON #define M_BITMAPDATA ((wxBitmapRefData *)m_refData) void wxBitmapRefData::Init() { m_width = 0 ; m_height = 0 ; m_depth = 0 ; m_bytesPerRow = 0; m_ok = false ; m_bitmapMask = NULL ; m_cgImageRef = NULL ; #ifndef __WXOSX_IPHONE__ m_iconRef = NULL ; m_pictHandle = NULL ; #endif m_hBitmap = NULL ; m_rawAccessCount = 0 ; m_hasAlpha = false; } wxBitmapRefData::wxBitmapRefData(const wxBitmapRefData &tocopy) { Init(); Create(tocopy.m_width, tocopy.m_height, tocopy.m_depth); if (tocopy.m_bitmapMask) m_bitmapMask = new wxMask(*tocopy.m_bitmapMask); else if (tocopy.m_hasAlpha) UseAlpha(true); unsigned char* dest = (unsigned char*)GetRawAccess(); unsigned char* source = (unsigned char*)tocopy.GetRawAccess(); size_t numbytes = m_bytesPerRow * m_height; memcpy( dest, source, numbytes ); } wxBitmapRefData::wxBitmapRefData() { Init() ; } wxBitmapRefData::wxBitmapRefData( int w , int h , int d ) { Init() ; Create( w , h , d ) ; } bool wxBitmapRefData::Create( int w , int h , int d ) { m_width = wxMax(1, w); m_height = wxMax(1, h); m_depth = d ; m_hBitmap = NULL ; m_bytesPerRow = GetBestBytesPerRow( w * 4 ) ; size_t size = m_bytesPerRow * h ; void* data = m_memBuf.GetWriteBuf( size ) ; if ( data != NULL ) { memset( data , 0 , size ) ; m_memBuf.UngetWriteBuf( size ) ; m_hBitmap = CGBitmapContextCreate((char*) data, m_width, m_height, 8, m_bytesPerRow, wxMacGetGenericRGBColorSpace(), kCGImageAlphaNoneSkipFirst ); wxASSERT_MSG( m_hBitmap , wxT("Unable to create CGBitmapContext context") ) ; CGContextTranslateCTM( m_hBitmap, 0, m_height ); CGContextScaleCTM( m_hBitmap, 1, -1 ); } /* data != NULL */ m_ok = ( m_hBitmap != NULL ) ; return m_ok ; } void wxBitmapRefData::UseAlpha( bool use ) { if ( m_hasAlpha == use ) return ; m_hasAlpha = use ; CGContextRelease( m_hBitmap ); m_hBitmap = CGBitmapContextCreate((char*) m_memBuf.GetData(), m_width, m_height, 8, m_bytesPerRow, wxMacGetGenericRGBColorSpace(), m_hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ); wxASSERT_MSG( m_hBitmap , wxT("Unable to create CGBitmapContext context") ) ; CGContextTranslateCTM( m_hBitmap, 0, m_height ); CGContextScaleCTM( m_hBitmap, 1, -1 ); } void *wxBitmapRefData::GetRawAccess() const { wxCHECK_MSG( IsOk(), NULL , wxT("invalid bitmap") ) ; return m_memBuf.GetData() ; } void *wxBitmapRefData::BeginRawAccess() { wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") ) ; wxASSERT( m_rawAccessCount == 0 ) ; #ifndef __WXOSX_IPHONE__ wxASSERT_MSG( m_pictHandle == NULL && m_iconRef == NULL , wxT("Currently, modifing bitmaps that are used in controls already is not supported") ) ; #endif ++m_rawAccessCount ; // we must destroy an existing cached image, as // the bitmap data may change now if ( m_cgImageRef ) { CGImageRelease( m_cgImageRef ) ; m_cgImageRef = NULL ; } return m_memBuf.GetData() ; } void wxBitmapRefData::EndRawAccess() { wxCHECK_RET( IsOk() , wxT("invalid bitmap") ) ; wxASSERT( m_rawAccessCount == 1 ) ; --m_rawAccessCount ; } bool wxBitmapRefData::HasNativeSize() { int w = GetWidth() ; int h = GetHeight() ; int sz = wxMax( w , h ) ; return ( sz == 128 || sz == 48 || sz == 32 || sz == 16 ); } #ifndef __WXOSX_IPHONE__ IconRef wxBitmapRefData::GetIconRef() { if ( m_iconRef == NULL ) { // Create Icon Family Handle IconFamilyHandle iconFamily = (IconFamilyHandle) NewHandle( 0 ); int w = GetWidth() ; int h = GetHeight() ; int sz = wxMax( w , h ) ; OSType dataType = 0 ; OSType maskType = 0 ; switch (sz) { case 128: #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if ( UMAGetSystemVersion() >= 0x1050 ) { dataType = kIconServices128PixelDataARGB ; } else #endif { dataType = kThumbnail32BitData ; maskType = kThumbnail8BitMask ; } break; case 48: #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if ( UMAGetSystemVersion() >= 0x1050 ) { dataType = kIconServices48PixelDataARGB ; } else #endif { dataType = kHuge32BitData ; maskType = kHuge8BitMask ; } break; case 32: #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if ( UMAGetSystemVersion() >= 0x1050 ) { dataType = kIconServices32PixelDataARGB ; } else #endif { dataType = kLarge32BitData ; maskType = kLarge8BitMask ; } break; case 16: #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if ( UMAGetSystemVersion() >= 0x1050 ) { dataType = kIconServices16PixelDataARGB ; } else #endif { dataType = kSmall32BitData ; maskType = kSmall8BitMask ; } break; default: break; } if ( dataType != 0 ) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if ( maskType == 0 && UMAGetSystemVersion() >= 0x1050 ) { size_t datasize = sz * sz * 4 ; Handle data = NewHandle( datasize ) ; HLock( data ) ; unsigned char* ptr = (unsigned char*) *data ; memset( ptr, 0, datasize ); bool hasAlpha = HasAlpha() ; wxMask *mask = m_bitmapMask ; unsigned char * sourcePtr = (unsigned char*) GetRawAccess() ; unsigned char * masksourcePtr = mask ? (unsigned char*) mask->GetRawAccess() : NULL ; for ( int y = 0 ; y < h ; ++y, sourcePtr += m_bytesPerRow , masksourcePtr += mask ? mask->GetBytesPerRow() : 0 ) { unsigned char * source = sourcePtr; unsigned char * masksource = masksourcePtr; unsigned char * dest = ptr + y * sz * 4 ; unsigned char a, r, g, b; for ( int x = 0 ; x < w ; ++x ) { a = *source ++ ; r = *source ++ ; g = *source ++ ; b = *source ++ ; if ( mask ) { a = 0xFF - *masksource++ ; } else if ( !hasAlpha ) a = 0xFF ; else { #if wxOSX_USE_PREMULTIPLIED_ALPHA // this must be non-premultiplied data if ( a != 0xFF && a!= 0 ) { r = r * 255 / a; g = g * 255 / a; b = b * 255 / a; } #endif } *dest++ = a ; *dest++ = r ; *dest++ = g ; *dest++ = b ; } } HUnlock( data ); OSStatus err = SetIconFamilyData( iconFamily, dataType , data ); wxASSERT_MSG( err == noErr , wxT("Error when adding bitmap") ); DisposeHandle( data ); } else #endif { // setup the header properly Handle data = NULL ; Handle maskdata = NULL ; unsigned char * maskptr = NULL ; unsigned char * ptr = NULL ; size_t datasize, masksize ; datasize = sz * sz * 4 ; data = NewHandle( datasize ) ; HLock( data ) ; ptr = (unsigned char*) *data ; memset( ptr , 0, datasize ) ; masksize = sz * sz ; maskdata = NewHandle( masksize ) ; HLock( maskdata ) ; maskptr = (unsigned char*) *maskdata ; memset( maskptr , 0 , masksize ) ; bool hasAlpha = HasAlpha() ; wxMask *mask = m_bitmapMask ; unsigned char * sourcePtr = (unsigned char*) GetRawAccess() ; unsigned char * masksourcePtr = mask ? (unsigned char*) mask->GetRawAccess() : NULL ; for ( int y = 0 ; y < h ; ++y, sourcePtr += m_bytesPerRow , masksourcePtr += mask ? mask->GetBytesPerRow() : 0 ) { unsigned char * source = sourcePtr; unsigned char * masksource = masksourcePtr; unsigned char * dest = ptr + y * sz * 4 ; unsigned char * maskdest = maskptr + y * sz ; unsigned char a, r, g, b; for ( int x = 0 ; x < w ; ++x ) { a = *source ++ ; r = *source ++ ; g = *source ++ ; b = *source ++ ; *dest++ = 0 ; *dest++ = r ; *dest++ = g ; *dest++ = b ; if ( mask ) *maskdest++ = 0xFF - *masksource++ ; else if ( hasAlpha ) *maskdest++ = a ; else *maskdest++ = 0xFF ; } } OSStatus err = SetIconFamilyData( iconFamily, dataType , data ) ; wxASSERT_MSG( err == noErr , wxT("Error when adding bitmap") ) ; err = SetIconFamilyData( iconFamily, maskType , maskdata ) ; wxASSERT_MSG( err == noErr , wxT("Error when adding mask") ) ; HUnlock( data ) ; HUnlock( maskdata ) ; DisposeHandle( data ) ; DisposeHandle( maskdata ) ; } } else { PicHandle pic = GetPictHandle() ; SetIconFamilyData( iconFamily, 'PICT' , (Handle) pic ) ; } // transform into IconRef // cleaner version existing from 10.3 upwards HLock((Handle) iconFamily); OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &m_iconRef ); HUnlock((Handle) iconFamily); DisposeHandle( (Handle) iconFamily ) ; wxCHECK_MSG( err == noErr, NULL, wxT("Error when constructing icon ref") ); } return m_iconRef ; } PicHandle wxBitmapRefData::GetPictHandle() { if ( m_pictHandle == NULL ) { #ifndef __LP64__ GraphicsExportComponent exporter = 0; OSStatus err = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePicture, &exporter); if (noErr == err) { m_pictHandle = (PicHandle) NewHandle(0); if ( m_pictHandle ) { // QT does not correctly export the mask // TODO if we get around to it create a synthetic PICT with the CopyBits and Mask commands CGImageRef imageRef = CreateCGImage(); err = GraphicsExportSetInputCGImage( exporter, imageRef ); err = GraphicsExportSetOutputHandle(exporter, (Handle)m_pictHandle); err = GraphicsExportDoExport(exporter, NULL); CGImageRelease( imageRef ); size_t handleSize = GetHandleSize( (Handle) m_pictHandle ); // the 512 bytes header is only needed for pict files, but not in memory if ( handleSize >= 512 ) { memmove( *m_pictHandle , (char*)(*m_pictHandle)+512, handleSize - 512 ); SetHandleSize( (Handle) m_pictHandle, handleSize - 512 ); } } CloseComponent( exporter ); } #endif } return m_pictHandle ; } #endif CGImageRef wxBitmapRefData::CreateCGImage() const { wxASSERT( m_ok ) ; wxASSERT( m_rawAccessCount >= 0 ) ; CGImageRef image ; if ( m_rawAccessCount > 0 || m_cgImageRef == NULL ) { if ( m_depth != 1 && m_bitmapMask == NULL ) { if ( m_bitmapMask ) { CGImageRef tempImage = CGBitmapContextCreateImage( m_hBitmap ); CGImageRef tempMask = CGBitmapContextCreateImage((CGContextRef) m_bitmapMask->GetHBITMAP() ); image = CGImageCreateWithMask( tempImage, tempMask ); CGImageRelease(tempMask); CGImageRelease(tempImage); } else image = CGBitmapContextCreateImage( m_hBitmap ); } else { size_t imageSize = m_height * m_bytesPerRow ; void * dataBuffer = m_memBuf.GetData() ; int w = m_width ; int h = m_height ; CGImageAlphaInfo alphaInfo = kCGImageAlphaNoneSkipFirst ; wxMemoryBuffer membuf; if ( m_bitmapMask ) { alphaInfo = kCGImageAlphaFirst ; unsigned char *destalphastart = (unsigned char*) membuf.GetWriteBuf( imageSize ) ; memcpy( destalphastart , dataBuffer , imageSize ) ; unsigned char *sourcemaskstart = (unsigned char *) m_bitmapMask->GetRawAccess() ; int maskrowbytes = m_bitmapMask->GetBytesPerRow() ; for ( int y = 0 ; y < h ; ++y , destalphastart += m_bytesPerRow, sourcemaskstart += maskrowbytes) { unsigned char *sourcemask = sourcemaskstart ; unsigned char *destalpha = destalphastart ; for ( int x = 0 ; x < w ; ++x , sourcemask += kMaskBytesPerPixel , destalpha += 4 ) { *destalpha = 0xFF - *sourcemask ; } } membuf.UngetWriteBuf( imageSize ); } else { if ( m_hasAlpha ) { #if wxOSX_USE_PREMULTIPLIED_ALPHA alphaInfo = kCGImageAlphaPremultipliedFirst ; #else alphaInfo = kCGImageAlphaFirst ; #endif } membuf = m_memBuf; } CGDataProviderRef dataProvider = NULL ; if ( m_depth == 1 ) { // TODO CHECK ALIGNMENT wxMemoryBuffer maskBuf; unsigned char * maskBufData = (unsigned char*) maskBuf.GetWriteBuf( m_width * m_height ); unsigned char * bufData = (unsigned char *) membuf.GetData() ; // copy one color component size_t i = 0; for( int y = 0 ; y < m_height ; bufData+= m_bytesPerRow, ++y ) { unsigned char *bufDataIter = bufData+3; for ( int x = 0 ; x < m_width ; bufDataIter += 4, ++x, ++i ) { maskBufData[i] = *bufDataIter; } } maskBuf.UngetWriteBuf( m_width * m_height ); dataProvider = wxMacCGDataProviderCreateWithMemoryBuffer( maskBuf ); image = ::CGImageMaskCreate( w, h, 8, 8, m_width , dataProvider, NULL, false ); } else { CGColorSpaceRef colorSpace = wxMacGetGenericRGBColorSpace(); dataProvider = wxMacCGDataProviderCreateWithMemoryBuffer( membuf ); image = ::CGImageCreate( w, h, 8 , 32 , m_bytesPerRow , colorSpace, alphaInfo , dataProvider, NULL , false , kCGRenderingIntentDefault ); } CGDataProviderRelease( dataProvider); } } else { image = m_cgImageRef ; CGImageRetain( image ) ; } if ( m_rawAccessCount == 0 && m_cgImageRef == NULL) { // we keep it for later use m_cgImageRef = image ; CGImageRetain( image ) ; } return image ; } CGContextRef wxBitmapRefData::GetBitmapContext() const { return m_hBitmap; } void wxBitmapRefData::Free() { wxASSERT_MSG( m_rawAccessCount == 0 , wxT("Bitmap still selected when destroyed") ) ; if ( m_cgImageRef ) { CGImageRelease( m_cgImageRef ) ; m_cgImageRef = NULL ; } #ifndef __WXOSX_IPHONE__ if ( m_iconRef ) { ReleaseIconRef( m_iconRef ) ; m_iconRef = NULL ; } #ifndef __LP64__ if ( m_pictHandle ) { KillPicture( m_pictHandle ) ; m_pictHandle = NULL ; } #endif #endif if ( m_hBitmap ) { CGContextRelease(m_hBitmap); m_hBitmap = NULL ; } if (m_bitmapMask) { delete m_bitmapMask; m_bitmapMask = NULL; } } wxBitmapRefData::~wxBitmapRefData() { Free() ; } bool wxBitmap::CopyFromIcon(const wxIcon& icon) { bool created = false ; int w = icon.GetWidth() ; int h = icon.GetHeight() ; Create( icon.GetWidth() , icon.GetHeight() ) ; #ifndef __WXOSX_IPHONE__ if ( w == h && ( w == 16 || w == 32 || w == 48 || w == 128 ) ) { IconFamilyHandle iconFamily = NULL ; Handle imagehandle = NewHandle( 0 ) ; Handle maskhandle = NewHandle( 0 ) ; OSType maskType = 0; OSType dataType = 0; IconSelectorValue selector = 0 ; switch (w) { case 128: dataType = kThumbnail32BitData ; maskType = kThumbnail8BitMask ; selector = kSelectorAllAvailableData ; break; case 48: dataType = kHuge32BitData ; maskType = kHuge8BitMask ; selector = kSelectorHuge32Bit | kSelectorHuge8BitMask ; break; case 32: dataType = kLarge32BitData ; maskType = kLarge8BitMask ; selector = kSelectorLarge32Bit | kSelectorLarge8BitMask ; break; case 16: dataType = kSmall32BitData ; maskType = kSmall8BitMask ; selector = kSelectorSmall32Bit | kSelectorSmall8BitMask ; break; default: break; } OSStatus err = IconRefToIconFamily( MAC_WXHICON(icon.GetHICON()) , selector , &iconFamily ) ; err = GetIconFamilyData( iconFamily , dataType , imagehandle ) ; err = GetIconFamilyData( iconFamily , maskType , maskhandle ) ; size_t imagehandlesize = GetHandleSize( imagehandle ) ; size_t maskhandlesize = GetHandleSize( maskhandle ) ; if ( imagehandlesize != 0 && maskhandlesize != 0 ) { wxASSERT( GetHandleSize( imagehandle ) == w * 4 * h ) ; wxASSERT( GetHandleSize( maskhandle ) == w * h ) ; UseAlpha() ; unsigned char *source = (unsigned char *) *imagehandle ; unsigned char *sourcemask = (unsigned char *) *maskhandle ; unsigned char* destination = (unsigned char*) BeginRawAccess() ; for ( int y = 0 ; y < h ; ++y ) { for ( int x = 0 ; x < w ; ++x ) { unsigned char a = *sourcemask++; *destination++ = a; source++ ; #if wxOSX_USE_PREMULTIPLIED_ALPHA *destination++ = ( (*source++) * a + 127 ) / 255; *destination++ = ( (*source++) * a + 127 ) / 255; *destination++ = ( (*source++) * a + 127 ) / 255; #else *destination++ = *source++ ; *destination++ = *source++ ; *destination++ = *source++ ; #endif } } EndRawAccess() ; DisposeHandle( imagehandle ) ; DisposeHandle( maskhandle ) ; created = true ; } DisposeHandle( (Handle) iconFamily ) ; } #endif if ( !created ) { wxMemoryDC dc ; dc.SelectObject( *this ) ; dc.DrawIcon( icon , 0 , 0 ) ; dc.SelectObject( wxNullBitmap ) ; } return true; } wxBitmap::wxBitmap() { } wxBitmap::~wxBitmap() { } wxBitmap::wxBitmap(const char bits[], int the_width, int the_height, int no_bits) { wxBitmapRefData* bitmapRefData; m_refData = bitmapRefData = new wxBitmapRefData( the_width , the_height , no_bits ) ; if (bitmapRefData->IsOk()) { if ( no_bits == 1 ) { int linesize = ( the_width / (sizeof(unsigned char) * 8)) ; if ( the_width % (sizeof(unsigned char) * 8) ) linesize += sizeof(unsigned char); unsigned char* linestart = (unsigned char*) bits ; unsigned char* destptr = (unsigned char*) BeginRawAccess() ; for ( int y = 0 ; y < the_height ; ++y , linestart += linesize, destptr += M_BITMAPDATA->GetBytesPerRow() ) { unsigned char* destination = destptr; int index, bit, mask; for ( int x = 0 ; x < the_width ; ++x ) { index = x / 8 ; bit = x % 8 ; mask = 1 << bit ; if ( linestart[index] & mask ) { *destination++ = 0xFF ; *destination++ = 0 ; *destination++ = 0 ; *destination++ = 0 ; } else { *destination++ = 0xFF ; *destination++ = 0xFF ; *destination++ = 0xFF ; *destination++ = 0xFF ; } } } EndRawAccess() ; } else { wxFAIL_MSG(wxT("multicolor BITMAPs not yet implemented")); } } /* bitmapRefData->IsOk() */ } wxBitmap::wxBitmap(int w, int h, int d) { (void)Create(w, h, d); } wxBitmap::wxBitmap(const void* data, wxBitmapType type, int width, int height, int depth) { (void) Create(data, type, width, height, depth); } wxBitmap::wxBitmap(const wxString& filename, wxBitmapType type) { LoadFile(filename, type); } wxGDIRefData* wxBitmap::CreateGDIRefData() const { return new wxBitmapRefData; } wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const { return new wxBitmapRefData(*wx_static_cast(const wxBitmapRefData *, data)); } void * wxBitmap::GetRawAccess() const { wxCHECK_MSG( Ok() , NULL , wxT("invalid bitmap") ) ; return M_BITMAPDATA->GetRawAccess() ; } void * wxBitmap::BeginRawAccess() { wxCHECK_MSG( Ok() , NULL , wxT("invalid bitmap") ) ; return M_BITMAPDATA->BeginRawAccess() ; } void wxBitmap::EndRawAccess() { wxCHECK_RET( Ok() , wxT("invalid bitmap") ) ; M_BITMAPDATA->EndRawAccess() ; } CGImageRef wxBitmap::CreateCGImage() const { wxCHECK_MSG( Ok(), NULL , wxT("invalid bitmap") ) ; return M_BITMAPDATA->CreateCGImage() ; } #ifndef __WXOSX_IPHONE__ IconRef wxBitmap::GetIconRef() const { wxCHECK_MSG( Ok(), NULL , wxT("invalid bitmap") ) ; return M_BITMAPDATA->GetIconRef() ; } IconRef wxBitmap::CreateIconRef() const { IconRef icon = GetIconRef(); verify_noerr( AcquireIconRef(icon) ); return icon; } #endif 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") ); int destwidth = rect.width ; int destheight = rect.height ; { unsigned char *sourcedata = (unsigned char*) GetRawAccess() ; unsigned char *destdata = (unsigned char*) ret.BeginRawAccess() ; wxASSERT( (sourcedata != NULL) && (destdata != NULL) ) ; int sourcelinesize = GetBitmapData()->GetBytesPerRow() ; int destlinesize = ret.GetBitmapData()->GetBytesPerRow() ; unsigned char *source = sourcedata + rect.x * 4 + rect.y * sourcelinesize ; unsigned char *dest = destdata ; for (int yy = 0; yy < destheight; ++yy, source += sourcelinesize , dest += destlinesize) { memcpy( dest , source , destlinesize ) ; } } ret.EndRawAccess() ; if ( M_BITMAPDATA->m_bitmapMask ) { wxMemoryBuffer maskbuf ; int rowBytes = GetBestBytesPerRow( destwidth * kMaskBytesPerPixel ); size_t maskbufsize = rowBytes * destheight ; int sourcelinesize = M_BITMAPDATA->m_bitmapMask->GetBytesPerRow() ; int destlinesize = rowBytes ; unsigned char *source = (unsigned char *) M_BITMAPDATA->m_bitmapMask->GetRawAccess() ; unsigned char *destdata = (unsigned char * ) maskbuf.GetWriteBuf( maskbufsize ) ; wxASSERT( (source != NULL) && (destdata != NULL) ) ; source += rect.x * kMaskBytesPerPixel + rect.y * sourcelinesize ; unsigned char *dest = destdata ; for (int yy = 0; yy < destheight; ++yy, source += sourcelinesize , dest += destlinesize) { memcpy( dest , source , destlinesize ) ; } maskbuf.UngetWriteBuf( maskbufsize ) ; ret.SetMask( new wxMask( maskbuf , destwidth , destheight , rowBytes ) ) ; } else if ( HasAlpha() ) ret.UseAlpha() ; return ret; } bool wxBitmap::Create(int w, int h, int d) { UnRef(); if ( d < 0 ) d = wxDisplayDepth() ; m_refData = new wxBitmapRefData( w , h , d ); return M_BITMAPDATA->IsOk() ; } bool wxBitmap::LoadFile(const wxString& filename, wxBitmapType type) { UnRef(); wxBitmapHandler *handler = FindHandler(type); if ( handler ) { m_refData = new wxBitmapRefData; return handler->LoadFile(this, filename, type, -1, -1); } else { #if wxUSE_IMAGE wxImage loadimage(filename, type); if (loadimage.Ok()) { *this = loadimage; return true; } #endif } wxLogWarning(wxT("no bitmap handler for type %d defined."), type); return false; } bool wxBitmap::Create(const void* data, wxBitmapType type, int width, int height, int depth) { UnRef(); m_refData = new wxBitmapRefData; wxBitmapHandler *handler = FindHandler(type); if ( handler == NULL ) { wxLogWarning(wxT("no bitmap handler for type %d defined."), type); return false; } return handler->Create(this, data, type, width, height, depth); } #if wxUSE_IMAGE wxBitmap::wxBitmap(const wxImage& image, int depth) { wxCHECK_RET( image.Ok(), wxT("invalid image") ); // width and height of the device-dependent bitmap int width = image.GetWidth(); int height = image.GetHeight(); wxBitmapRefData* bitmapRefData; m_refData = bitmapRefData = new wxBitmapRefData( width , height , depth ) ; if ( bitmapRefData->IsOk()) { // Create picture bool hasAlpha = false ; if ( image.HasMask() ) { // takes precedence, don't mix with alpha info } else { hasAlpha = image.HasAlpha() ; } if ( hasAlpha ) UseAlpha() ; unsigned char* destinationstart = (unsigned char*) BeginRawAccess() ; register unsigned char* data = image.GetData(); if ( destinationstart != NULL && data != NULL ) { const unsigned char *alpha = hasAlpha ? image.GetAlpha() : NULL ; for (int y = 0; y < height; destinationstart += M_BITMAPDATA->GetBytesPerRow(), y++) { unsigned char * destination = destinationstart; for (int x = 0; x < width; x++) { if ( hasAlpha ) { const unsigned char a = *alpha++; *destination++ = a ; #if wxOSX_USE_PREMULTIPLIED_ALPHA *destination++ = ((*data++) * a + 127) / 255 ; *destination++ = ((*data++) * a + 127) / 255 ; *destination++ = ((*data++) * a + 127) / 255 ; #else *destination++ = *data++ ; *destination++ = *data++ ; *destination++ = *data++ ; #endif } else { *destination++ = 0xFF ; *destination++ = *data++ ; *destination++ = *data++ ; *destination++ = *data++ ; } } } EndRawAccess() ; } if ( image.HasMask() ) SetMask( new wxMask( *this , wxColour( image.GetMaskRed() , image.GetMaskGreen() , image.GetMaskBlue() ) ) ) ; } /* bitmapRefData->IsOk() */ } wxImage wxBitmap::ConvertToImage() const { wxImage image; wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") ); // create an wxImage object int width = GetWidth(); int height = GetHeight(); image.Create( width, height ); unsigned char *data = image.GetData(); wxCHECK_MSG( data, wxNullImage, wxT("Could not allocate data for image") ); unsigned char* sourcestart = (unsigned char*) GetRawAccess() ; bool hasAlpha = false ; bool hasMask = false ; int maskBytesPerRow = 0 ; unsigned char *alpha = NULL ; unsigned char *mask = NULL ; if ( HasAlpha() ) hasAlpha = true ; if ( GetMask() ) { hasMask = true ; mask = (unsigned char*) GetMask()->GetRawAccess() ; maskBytesPerRow = GetMask()->GetBytesPerRow() ; } if ( hasAlpha ) { image.SetAlpha() ; alpha = image.GetAlpha() ; } int index = 0; // The following masking algorithm is the same as well in msw/gtk: // the colour used as transparent one in wxImage and the one it is // replaced with when it actually occurs in the bitmap static const int MASK_RED = 1; static const int MASK_GREEN = 2; static const int MASK_BLUE = 3; static const int MASK_BLUE_REPLACEMENT = 2; for (int yy = 0; yy < height; yy++ , sourcestart += M_BITMAPDATA->GetBytesPerRow() , mask += maskBytesPerRow ) { unsigned char * maskp = mask ; unsigned char * source = sourcestart; unsigned char a, r, g, b; long color; for (int xx = 0; xx < width; xx++) { color = *((long*) source) ; #ifdef WORDS_BIGENDIAN a = ((color&0xFF000000) >> 24) ; r = ((color&0x00FF0000) >> 16) ; g = ((color&0x0000FF00) >> 8) ; b = (color&0x000000FF); #else b = ((color&0xFF000000) >> 24) ; g = ((color&0x00FF0000) >> 16) ; r = ((color&0x0000FF00) >> 8) ; a = (color&0x000000FF); #endif if ( hasMask ) { if ( *maskp++ == 0xFF ) { r = MASK_RED ; g = MASK_GREEN ; b = MASK_BLUE ; } else if ( r == MASK_RED && g == MASK_GREEN && b == MASK_BLUE ) b = MASK_BLUE_REPLACEMENT ; } else if ( hasAlpha ) { *alpha++ = a ; #if wxOSX_USE_PREMULTIPLIED_ALPHA // this must be non-premultiplied data if ( a != 0xFF && a!= 0 ) { r = r * 255 / a; g = g * 255 / a; b = b * 255 / a; } #endif } data[index ] = r ; data[index + 1] = g ; data[index + 2] = b ; index += 3; source += 4 ; } } if ( hasMask ) image.SetMaskColour( MASK_RED, MASK_GREEN, MASK_BLUE ); return image; } #endif //wxUSE_IMAGE bool wxBitmap::SaveFile( const wxString& filename, wxBitmapType type, const wxPalette *palette ) const { bool success = false; wxBitmapHandler *handler = FindHandler(type); if ( handler ) { success = handler->SaveFile(this, filename, type, palette); } else { #if wxUSE_IMAGE wxImage image = ConvertToImage(); success = image.SaveFile(filename, type); #else wxLogWarning(wxT("no bitmap handler for type %d defined."), type); #endif } return success; } int wxBitmap::GetHeight() const { wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); return M_BITMAPDATA->GetHeight(); } int wxBitmap::GetWidth() const { wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); return M_BITMAPDATA->GetWidth() ; } int wxBitmap::GetDepth() const { wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); return M_BITMAPDATA->GetDepth(); } wxMask *wxBitmap::GetMask() const { wxCHECK_MSG( Ok(), (wxMask *) NULL, wxT("invalid bitmap") ); return M_BITMAPDATA->m_bitmapMask; } bool wxBitmap::HasAlpha() const { wxCHECK_MSG( Ok(), false , wxT("invalid bitmap") ); return M_BITMAPDATA->HasAlpha() ; } void wxBitmap::SetWidth(int w) { AllocExclusive(); M_BITMAPDATA->SetWidth(w); } void wxBitmap::SetHeight(int h) { AllocExclusive(); M_BITMAPDATA->SetHeight(h); } void wxBitmap::SetDepth(int d) { AllocExclusive(); M_BITMAPDATA->SetDepth(d); } void wxBitmap::SetOk(bool isOk) { AllocExclusive(); M_BITMAPDATA->SetOk(isOk); } #if wxUSE_PALETTE wxPalette *wxBitmap::GetPalette() const { wxCHECK_MSG( Ok(), NULL, wxT("Invalid bitmap GetPalette()") ); return &M_BITMAPDATA->m_bitmapPalette; } void wxBitmap::SetPalette(const wxPalette& palette) { AllocExclusive(); M_BITMAPDATA->m_bitmapPalette = palette ; } #endif // wxUSE_PALETTE void wxBitmap::SetMask(wxMask *mask) { AllocExclusive(); // Remove existing mask if there is one. delete M_BITMAPDATA->m_bitmapMask; M_BITMAPDATA->m_bitmapMask = mask ; } WXHBITMAP wxBitmap::GetHBITMAP(WXHBITMAP* mask) const { wxUnusedVar(mask); return WXHBITMAP(M_BITMAPDATA->GetBitmapContext()); } // ---------------------------------------------------------------------------- // wxMask // ---------------------------------------------------------------------------- wxMask::wxMask() { Init() ; } wxMask::wxMask(const wxMask &tocopy) { Init(); m_bytesPerRow = tocopy.m_bytesPerRow; m_width = tocopy.m_width; m_height = tocopy.m_height; size_t size = m_bytesPerRow * m_height; unsigned char* dest = (unsigned char*)m_memBuf.GetWriteBuf( size ); unsigned char* source = (unsigned char*)tocopy.m_memBuf.GetData(); memcpy( dest, source, size ); m_memBuf.UngetWriteBuf( size ) ; RealizeNative() ; } // Construct a mask from a bitmap and a colour indicating // the transparent area wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour ) { Init() ; Create( bitmap, colour ); } // Construct a mask from a mono bitmap (copies the bitmap). wxMask::wxMask( const wxBitmap& bitmap ) { Init() ; Create( bitmap ); } // Construct a mask from a mono bitmap (copies the bitmap). wxMask::wxMask( const wxMemoryBuffer& data, int width , int height , int bytesPerRow ) { Init() ; Create( data, width , height , bytesPerRow ); } wxMask::~wxMask() { if ( m_maskBitmap ) { CGContextRelease( (CGContextRef) m_maskBitmap ); m_maskBitmap = NULL ; } } void wxMask::Init() { m_width = m_height = m_bytesPerRow = 0 ; m_maskBitmap = NULL ; } void *wxMask::GetRawAccess() const { return m_memBuf.GetData() ; } // The default ColorTable for k8IndexedGrayPixelFormat in Intel appears to be broken, so we'll use an non-indexed // bitmap mask instead; in order to keep the code simple, the change applies to PowerPC implementations as well void wxMask::RealizeNative() { if ( m_maskBitmap ) { CGContextRelease( (CGContextRef) m_maskBitmap ); m_maskBitmap = NULL ; } CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); // from MouseTracking sample : // Ironically, due to a bug in CGImageCreateWithMask, you cannot use // CGColorSpaceCreateWithName(kCGColorSpaceGenericGray) at this point! m_maskBitmap = CGBitmapContextCreate((char*) m_memBuf.GetData(), m_width, m_height, 8, m_bytesPerRow, colorspace, kCGImageAlphaNone ); CGColorSpaceRelease( colorspace ); wxASSERT_MSG( m_maskBitmap , wxT("Unable to create CGBitmapContext context") ) ; } // Create a mask from a mono bitmap (copies the bitmap). bool wxMask::Create(const wxMemoryBuffer& data,int width , int height , int bytesPerRow) { m_memBuf = data ; m_width = width ; m_height = height ; m_bytesPerRow = bytesPerRow ; wxASSERT( data.GetDataLen() == (size_t)(height * bytesPerRow) ) ; RealizeNative() ; return true ; } // Create a mask from a mono bitmap (copies the bitmap). bool wxMask::Create(const wxBitmap& bitmap) { m_width = bitmap.GetWidth() ; m_height = bitmap.GetHeight() ; m_bytesPerRow = GetBestBytesPerRow( m_width * kMaskBytesPerPixel ) ; size_t size = m_bytesPerRow * m_height ; unsigned char * destdatabase = (unsigned char*) m_memBuf.GetWriteBuf( size ) ; wxASSERT( destdatabase != NULL ) ; memset( destdatabase , 0 , size ) ; unsigned char * srcdata = (unsigned char*) bitmap.GetRawAccess() ; for ( int y = 0 ; y < m_height ; ++y , destdatabase += m_bytesPerRow ) { unsigned char *destdata = destdatabase ; unsigned char r, g, b; for ( int x = 0 ; x < m_width ; ++x ) { srcdata++ ; r = *srcdata++ ; g = *srcdata++ ; b = *srcdata++ ; if ( ( r + g + b ) > 0x10 ) *destdata++ = 0xFF ; else *destdata++ = 0x00 ; } } m_memBuf.UngetWriteBuf( size ) ; RealizeNative() ; return true; } // Create a mask from a bitmap and a colour indicating // the transparent area bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour) { m_width = bitmap.GetWidth() ; m_height = bitmap.GetHeight() ; m_bytesPerRow = GetBestBytesPerRow( m_width * kMaskBytesPerPixel ) ; size_t size = m_bytesPerRow * m_height ; unsigned char * destdatabase = (unsigned char*) m_memBuf.GetWriteBuf( size ) ; wxASSERT( destdatabase != NULL ) ; memset( destdatabase , 0 , size ) ; unsigned char * srcdatabase = (unsigned char*) bitmap.GetRawAccess() ; size_t sourceBytesRow = bitmap.GetBitmapData()->GetBytesPerRow(); for ( int y = 0 ; y < m_height ; ++y , srcdatabase+= sourceBytesRow, destdatabase += m_bytesPerRow) { unsigned char *srcdata = srcdatabase ; unsigned char *destdata = destdatabase ; unsigned char r, g, b; for ( int x = 0 ; x < m_width ; ++x ) { srcdata++ ; r = *srcdata++ ; g = *srcdata++ ; b = *srcdata++ ; if ( colour == wxColour( r , g , b ) ) *destdata++ = 0xFF ; else *destdata++ = 0x00 ; } } m_memBuf.UngetWriteBuf( size ) ; RealizeNative() ; return true; } WXHBITMAP wxMask::GetHBITMAP() const { return m_maskBitmap ; } // ---------------------------------------------------------------------------- // Standard Handlers // ---------------------------------------------------------------------------- #if !defined( __LP64__ ) && !defined(__WXOSX_IPHONE__) class WXDLLEXPORT wxPICTResourceHandler: public wxBitmapHandler { DECLARE_DYNAMIC_CLASS(wxPICTResourceHandler) public: inline wxPICTResourceHandler() { SetName(wxT("Macintosh Pict resource")); SetExtension(wxEmptyString); SetType(wxBITMAP_TYPE_PICT_RESOURCE); }; virtual bool LoadFile(wxBitmap *bitmap, const wxString& name, long flags, int desiredWidth, int desiredHeight); }; IMPLEMENT_DYNAMIC_CLASS(wxPICTResourceHandler, wxBitmapHandler) bool wxPICTResourceHandler::LoadFile(wxBitmap *bitmap, const wxString& name, long WXUNUSED(flags), int WXUNUSED(desiredWidth), int WXUNUSED(desiredHeight)) { #if wxUSE_METAFILE Str255 theName ; wxMacStringToPascal( name , theName ) ; PicHandle thePict = (PicHandle ) GetNamedResource( 'PICT' , theName ) ; if ( thePict ) { wxMetafile mf ; mf.SetPICT( thePict ) ; bitmap->Create( mf.GetWidth() , mf.GetHeight() ) ; wxMemoryDC dc ; dc.SelectObject( *bitmap ) ; mf.Play( &dc ) ; dc.SelectObject( wxNullBitmap ) ; return true ; } #endif return false ; } #endif void wxBitmap::InitStandardHandlers() { #if !defined( __LP64__ ) && !defined(__WXOSX_IPHONE__) AddHandler( new wxPICTResourceHandler ) ; #endif #if wxOSX_USE_COCOA_OR_CARBON AddHandler( new wxICONResourceHandler ) ; #endif } // ---------------------------------------------------------------------------- // raw bitmap access support // ---------------------------------------------------------------------------- void *wxBitmap::GetRawData(wxPixelDataBase& data, int WXUNUSED(bpp)) { if ( !Ok() ) // no bitmap, no data (raw or otherwise) return NULL; data.m_width = GetWidth() ; data.m_height = GetHeight() ; data.m_stride = GetBitmapData()->GetBytesPerRow() ; return BeginRawAccess() ; } void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(dataBase)) { EndRawAccess() ; } void wxBitmap::UseAlpha() { // remember that we are using alpha channel: // we'll need to create a proper mask in UngetRawData() M_BITMAPDATA->UseAlpha( true ); }