start of alpha transparency support

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19459 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2003-03-04 02:12:28 +00:00
parent 0c543b7a19
commit acf8e3d29e
3 changed files with 238 additions and 390 deletions

View File

@@ -84,7 +84,6 @@ public:
wxMask *GetMask() const { return m_bitmapMask; }
public:
int m_numColors;
#if wxUSE_PALETTE
wxPalette m_bitmapPalette;
#endif // wxUSE_PALETTE
@@ -92,15 +91,16 @@ public:
// MSW-specific
// ------------
#ifdef __WXDEBUG__
// 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;
#endif // __WXDEBUG__
#if wxUSE_DIB_FOR_BITMAP
// file mapping handle for large DIB's
HANDLE m_hFileMap;
#endif // wxUSE_DIB_FOR_BITMAP
// true if we have alpha transparency info and can be drawn using
// AlphaBlend()
bool m_hasAlpha;
private:
// optional mask for transparent drawing
@@ -129,12 +129,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject)
wxBitmapRefData::wxBitmapRefData()
{
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
m_hasAlpha = FALSE;
}
void wxBitmapRefData::Free()
@@ -150,15 +147,6 @@ void wxBitmapRefData::Free()
}
}
#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;
}
@@ -302,8 +290,6 @@ wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
refData->m_width = width;
refData->m_height = height;
refData->m_depth = depth;
refData->m_numColors = 0;
refData->m_selectedInto = NULL;
char *data;
if ( depth == 1 )
@@ -451,88 +437,21 @@ bool wxBitmap::Create(int w, int h, int d)
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
// ----------------------------------------------------------------------------
#if wxUSE_IMAGE
// ----------------------------------------------------------------------------
// wxImage to/from conversions for Microwin
// ----------------------------------------------------------------------------
// Microwin versions are so different from normal ones that it really doesn't
// make sense to use #ifdefs inside the function bodies
#ifdef __WXMICROWIN__
bool wxBitmap::CreateFromImage( const wxImage& image, int depth )
{
#ifdef __WXMICROWIN__
// Set this to 1 to experiment with mask code,
// which currently doesn't work
#define USE_MASKS 0
// Set this to 1 to experiment with mask code,
// which currently doesn't work
#define USE_MASKS 0
m_refData = new wxBitmapRefData();
@@ -645,284 +564,10 @@ bool wxBitmap::CreateFromImage( const wxImage& image, int depth )
#endif // WXWIN_COMPATIBILITY_2
return TRUE;
#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;
#endif
// width and height of the device-dependent bitmap
int width = image.GetWidth();
int bmpHeight = image.GetHeight();
// calc the number of bytes per scanline and padding
int bytePerLine = width*3;
int sizeDWORD = sizeof( DWORD );
int lineBoundary = bytePerLine % sizeDWORD;
int padding = 0;
if( lineBoundary > 0 )
{
padding = sizeDWORD - lineBoundary;
bytePerLine += padding;
}
// calc the number of DIBs and heights of DIBs
int numDIB = 1;
int hRemain = 0;
int height = sizeLimit/bytePerLine;
if( height >= bmpHeight )
height = bmpHeight;
else
{
numDIB = bmpHeight / height;
hRemain = bmpHeight % height;
if( hRemain >0 ) numDIB++;
}
// set bitmap parameters
wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") );
SetWidth( width );
SetHeight( bmpHeight );
if (depth == -1) depth = wxDisplayDepth();
SetDepth( depth );
#if wxUSE_PALETTE
// Copy the palette from the source image
SetPalette(image.GetPalette());
#endif // wxUSE_PALETTE
// create a DIB header
int headersize = sizeof(BITMAPINFOHEADER);
BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
wxCHECK_MSG( lpDIBh, FALSE, wxT("could not allocate memory for DIB header") );
// Fill in the DIB header
lpDIBh->bmiHeader.biSize = headersize;
lpDIBh->bmiHeader.biWidth = (DWORD)width;
lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
// the general formula for biSizeImage:
// ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
lpDIBh->bmiHeader.biPlanes = 1;
lpDIBh->bmiHeader.biBitCount = 24;
lpDIBh->bmiHeader.biCompression = BI_RGB;
lpDIBh->bmiHeader.biClrUsed = 0;
// These seem not really needed for our purpose here.
lpDIBh->bmiHeader.biClrImportant = 0;
lpDIBh->bmiHeader.biXPelsPerMeter = 0;
lpDIBh->bmiHeader.biYPelsPerMeter = 0;
// memory for DIB data
unsigned char *lpBits;
lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
if( !lpBits )
{
wxFAIL_MSG( wxT("could not allocate memory for DIB") );
free( lpDIBh );
return FALSE;
}
// create and set the device-dependent bitmap
HDC hdc = ::GetDC(NULL);
HDC memdc = ::CreateCompatibleDC( hdc );
HBITMAP hbitmap;
hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
::SelectObject( memdc, hbitmap);
#if wxUSE_PALETTE
HPALETTE hOldPalette = 0;
if (image.GetPalette().Ok())
{
hOldPalette = ::SelectPalette(memdc, (HPALETTE) image.GetPalette().GetHPALETTE(), FALSE);
::RealizePalette(memdc);
}
#endif // wxUSE_PALETTE
// copy image data into DIB data and then into DDB (in a loop)
unsigned char *data = image.GetData();
int i, j, n;
int origin = 0;
unsigned char *ptdata = data;
unsigned char *ptbits;
for( n=0; n<numDIB; n++ )
{
if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
{
// redefine height and size of the (possibly) last smaller DIB
// memory is not reallocated
height = hRemain;
lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
}
ptbits = lpBits;
for( j=0; j<height; j++ )
{
for( i=0; i<width; i++ )
{
*(ptbits++) = *(ptdata+2);
*(ptbits++) = *(ptdata+1);
*(ptbits++) = *(ptdata );
ptdata += 3;
}
for( i=0; i< padding; i++ ) *(ptbits++) = 0;
}
::StretchDIBits( memdc, 0, origin, width, height,\
0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
origin += height;
// if numDIB = 1, lines below can also be used
// hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
// The above line is equivalent to the following two lines.
// hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
// ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
// or the following lines
// hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
// HDC memdc = ::CreateCompatibleDC( hdc );
// ::SelectObject( memdc, hbitmap);
// ::SetDIBitsToDevice( memdc, 0, 0, width, height,
// 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
// ::SelectObject( memdc, 0 );
// ::DeleteDC( memdc );
}
SetHBITMAP( (WXHBITMAP) hbitmap );
#if wxUSE_PALETTE
if (hOldPalette)
SelectPalette(memdc, hOldPalette, FALSE);
#endif // wxUSE_PALETTE
// similarly, created an mono-bitmap for the possible mask
if( image.HasMask() )
{
hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
HGDIOBJ hbmpOld = ::SelectObject( memdc, hbitmap);
if( numDIB == 1 ) height = bmpHeight;
else height = sizeLimit/bytePerLine;
lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
origin = 0;
unsigned char r = image.GetMaskRed();
unsigned char g = image.GetMaskGreen();
unsigned char b = image.GetMaskBlue();
unsigned char zero = 0, one = 255;
ptdata = data;
for( n=0; n<numDIB; n++ )
{
if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
{
// redefine height and size of the (possibly) last smaller DIB
// memory is not reallocated
height = hRemain;
lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
}
ptbits = lpBits;
for( int j=0; j<height; j++ )
{
for(i=0; i<width; i++ )
{
// was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) || (cb!=b) )
unsigned char cr = (*(ptdata++)) ;
unsigned char cg = (*(ptdata++)) ;
unsigned char cb = (*(ptdata++)) ;
if( ( cr !=r) || (cg!=g) || (cb!=b) )
{
*(ptbits++) = one;
*(ptbits++) = one;
*(ptbits++) = one;
}
else
{
*(ptbits++) = zero;
*(ptbits++) = zero;
*(ptbits++) = zero;
}
}
for( i=0; i< padding; i++ ) *(ptbits++) = zero;
}
::StretchDIBits( memdc, 0, origin, width, height,\
0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
origin += height;
}
// create a wxMask object
wxMask *mask = new wxMask();
mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
SetMask( mask );
// It will be deleted when the wxBitmap object is deleted (as of 01/1999)
/* The following can also be used but is slow to run
wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
wxMask *mask = new wxMask( *this, colour );
SetMask( mask );
*/
::SelectObject( memdc, hbmpOld );
}
// free allocated resources
::DeleteDC( memdc );
::ReleaseDC(NULL, hdc);
free(lpDIBh);
free(lpBits);
#endif // wxUSE_DIB_FOR_BITMAP
#if WXWIN_COMPATIBILITY_2
// check the wxBitmap object
GetBitmapData()->SetOk();
#endif // WXWIN_COMPATIBILITY_2
return TRUE;
#endif // __WXMICROWIN__/!__WXMICROWIN__
}
wxImage wxBitmap::ConvertToImage() const
{
#ifdef __WXMICROWIN__
// Initial attempt at a simple-minded implementation.
// The bitmap will always be created at the screen depth,
// so the 'depth' argument is ignored.
@@ -982,8 +627,144 @@ wxImage wxBitmap::ConvertToImage() const
#endif // wxUSE_PALETTE
return image;
}
#else // !__WXMICROWIN__
#endif // __WXMICROWIN__
// ----------------------------------------------------------------------------
// wxImage to/from conversions
// ----------------------------------------------------------------------------
bool wxBitmap::CreateFromImage( const wxImage& image, int depth )
{
wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") );
UnRef();
// first convert the image to DIB
const int h = image.GetHeight();
const int w = image.GetWidth();
wxDIB dib(image);
if ( !dib.IsOk() )
return FALSE;
// store the bitmap parameters
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;
// 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
{
// 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)"));
}
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;
}
#if wxUSE_PALETTE
if ( hOldPalette )
{
::SelectPalette(hdcMem, hOldPalette, FALSE);
}
#endif // wxUSE_PALETTE
}
// validate this object
SetHBITMAP((WXHBITMAP)hbitmap);
#if WXWIN_COMPATIBILITY_2
m_refData->m_ok = TRUE;
#endif // WXWIN_COMPATIBILITY_2
// finally also set the mask if we have one
if ( image.HasMask() )
{
SetMask(new wxMask(*this, wxColour(image.GetMaskRed(),
image.GetMaskGreen(),
image.GetMaskBlue())));
}
return TRUE;
}
wxImage wxBitmap::ConvertToImage() const
{
wxImage image;
wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") );
@@ -1108,11 +889,14 @@ wxImage wxBitmap::ConvertToImage() const
free(lpBits);
return image;
#endif // __WXMICROWIN__/!__WXMICROWIN__
}
#endif // wxUSE_IMAGE
// ----------------------------------------------------------------------------
// loading and saving bitmaps
// ----------------------------------------------------------------------------
bool wxBitmap::LoadFile(const wxString& filename, long type)
{
UnRef();
@@ -1196,17 +980,25 @@ wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
(rect.y+rect.height <= GetHeight()),
wxNullBitmap, wxT("Invalid bitmap or bitmap region") );
wxBitmap ret( rect.width, rect.height, GetDepth() );
wxBitmap ret( rect.width, rect.height );
wxASSERT_MSG( ret.Ok(), wxT("GetSubBitmap error") );
#ifndef __WXMICROWIN__
// TODO: copy alpha channel data if any
// copy bitmap data
MemoryHDC dcSrc, dcDst;
MemoryHDC dcSrc,
dcDst;
{
SelectInHDC selectSrc(dcSrc, GetHbitmap()),
selectDst(dcDst, GetHbitmapOf(ret));
if ( !selectSrc || !selectDst )
{
wxLogLastError(_T("SelectObjct(hBitmap)"));
}
if ( !::BitBlt(dcDst, 0, 0, rect.width, rect.height,
dcSrc, rect.x, rect.y, SRCCOPY) )
{
@@ -1256,15 +1048,6 @@ wxDC *wxBitmap::GetSelectedInto() const
return GetBitmapData() ? GetBitmapData()->m_selectedInto : (wxDC *) NULL;
}
#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
@@ -1274,6 +1057,11 @@ int wxBitmap::GetQuality() const
#endif // WXWIN_COMPATIBILITY_2_4
bool wxBitmap::HasAlpha() const
{
return GetBitmapData() && GetBitmapData()->m_hasAlpha;
}
// ----------------------------------------------------------------------------
// wxBitmap setters
// ----------------------------------------------------------------------------