Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/comctl32/static.c | 80 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 4 deletions(-)
diff --git a/dlls/comctl32/static.c b/dlls/comctl32/static.c index d08710c06f..38c1d5299c 100644 --- a/dlls/comctl32/static.c +++ b/dlls/comctl32/static.c @@ -60,6 +60,7 @@ struct static_extra_info HBITMAP hbitmap; HENHMETAFILE hemf; } image; + BOOL image_has_alpha; };
typedef void (*pfPaint)( HWND hwnd, HDC hdc, DWORD style ); @@ -163,6 +164,56 @@ static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style ) return prevIcon; }
+static HBITMAP create_alpha_bitmap( HBITMAP hbitmap ) +{ + HBITMAP alpha = 0; + BITMAPINFO *info = NULL; + BITMAP bm; + HDC src, dst; + void *bits; + DWORD i; + const unsigned char *ptr; + BOOL has_alpha = FALSE; + + if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0; + if (bm.bmBitsPixel != 32) return 0; + + if (!(src = CreateCompatibleDC( 0 ))) return 0; + if (!(dst = CreateCompatibleDC( src ))) goto done; + if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done; + info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info->bmiHeader.biWidth = bm.bmWidth; + info->bmiHeader.biHeight = -bm.bmHeight; + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = 32; + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrUsed = 0; + info->bmiHeader.biClrImportant = 0; + if (!(alpha = CreateDIBSection( dst, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done; + + SelectObject( src, hbitmap ); + SelectObject( dst, alpha ); + BitBlt(dst, 0, 0, bm.bmWidth, bm.bmHeight, src, 0, 0, SRCCOPY); + + for (i = 0, ptr = bits; i < bm.bmWidth * bm.bmHeight; i++, ptr += 4) + if ((has_alpha = (ptr[3] != 0))) break; + +done: + DeleteDC( src ); + DeleteDC( dst ); + HeapFree( GetProcessHeap(), 0, info ); + + if (!has_alpha) + { + DeleteObject( alpha ); + alpha = 0; + } + return alpha; +} + /*********************************************************************** * STATIC_SetBitmap * @@ -170,7 +221,7 @@ static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style ) */ static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style ) { - HBITMAP hOldBitmap; + HBITMAP hOldBitmap, alpha; struct static_extra_info *extra;
if ((style & SS_TYPEMASK) != SS_BITMAP) return 0; @@ -184,7 +235,15 @@ static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style ) if (!extra) return 0;
hOldBitmap = extra->image.hbitmap; - extra->image.hbitmap = hBitmap; + alpha = create_alpha_bitmap( hBitmap ); + if (alpha) + { + extra->image.hbitmap = alpha; + extra->image_has_alpha = TRUE; + } + else + extra->image.hbitmap = hBitmap; + if (hBitmap && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL)) { BITMAP bm; @@ -386,7 +445,12 @@ static LRESULT CALLBACK STATIC_WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, if (style == SS_ICON) { struct static_extra_info *extra = get_extra_ptr( hwnd, FALSE ); - heap_free( extra ); + if (extra) + { + if (extra->image_has_alpha) + DeleteObject( extra->image.hbitmap ); + heap_free( extra ); + } /* * FIXME * DestroyIcon32( STATIC_SetIcon( wndPtr, 0 ) ); @@ -765,6 +829,8 @@ static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style ) BITMAP bm; RECT rcClient; LOGBRUSH brush; + BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + struct static_extra_info *extra = get_extra_ptr( hwnd, FALSE );
GetObjectW(hBitmap, sizeof(bm), &bm); oldbitmap = SelectObject(hMemDC, hBitmap); @@ -785,7 +851,13 @@ static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style ) rcClient.right = rcClient.left + bm.bmWidth; rcClient.bottom = rcClient.top + bm.bmHeight; } - StretchBlt(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left, + + if (extra->image_has_alpha) + GdiAlphaBlend(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left, + rcClient.bottom - rcClient.top, hMemDC, + 0, 0, bm.bmWidth, bm.bmHeight, blend); + else + StretchBlt(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); SelectObject(hMemDC, oldbitmap);
Dmitry Timoshkov dmitry@baikal.ru writes:
+static HBITMAP create_alpha_bitmap( HBITMAP hbitmap ) +{
- HBITMAP alpha = 0;
- BITMAPINFO *info = NULL;
- BITMAP bm;
- HDC src, dst;
- void *bits;
- DWORD i;
- const unsigned char *ptr;
- BOOL has_alpha = FALSE;
- if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0;
- if (bm.bmBitsPixel != 32) return 0;
- if (!(src = CreateCompatibleDC( 0 ))) return 0;
- if (!(dst = CreateCompatibleDC( src ))) goto done;
- if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
- info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info->bmiHeader.biWidth = bm.bmWidth;
- info->bmiHeader.biHeight = -bm.bmHeight;
- info->bmiHeader.biPlanes = 1;
- info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
- info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
- info->bmiHeader.biXPelsPerMeter = 0;
- info->bmiHeader.biYPelsPerMeter = 0;
- info->bmiHeader.biClrUsed = 0;
- info->bmiHeader.biClrImportant = 0;
- if (!(alpha = CreateDIBSection( dst, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
- SelectObject( src, hbitmap );
- SelectObject( dst, alpha );
- BitBlt(dst, 0, 0, bm.bmWidth, bm.bmHeight, src, 0, 0, SRCCOPY);
BitBlt() is not guaranteed to preserve alpha, it's not the right function to use here.
@@ -184,7 +235,15 @@ static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style ) if (!extra) return 0;
hOldBitmap = extra->image.hbitmap;
- extra->image.hbitmap = hBitmap;
- alpha = create_alpha_bitmap( hBitmap );
- if (alpha)
- {
extra->image.hbitmap = alpha;
extra->image_has_alpha = TRUE;
- }
- else
extra->image.hbitmap = hBitmap;
You'd need to free the old bitmap and reset the flag, though I'm not sure it makes sense to store the flag in the first place, you could handle this at paint time. You probably also need to return the original bitmap, this would need tests.
Alexandre Julliard julliard@winehq.org wrote:
Dmitry Timoshkov dmitry@baikal.ru writes:
+static HBITMAP create_alpha_bitmap( HBITMAP hbitmap ) +{
- HBITMAP alpha = 0;
- BITMAPINFO *info = NULL;
- BITMAP bm;
- HDC src, dst;
- void *bits;
- DWORD i;
- const unsigned char *ptr;
- BOOL has_alpha = FALSE;
- if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0;
- if (bm.bmBitsPixel != 32) return 0;
- if (!(src = CreateCompatibleDC( 0 ))) return 0;
- if (!(dst = CreateCompatibleDC( src ))) goto done;
- if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
- info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info->bmiHeader.biWidth = bm.bmWidth;
- info->bmiHeader.biHeight = -bm.bmHeight;
- info->bmiHeader.biPlanes = 1;
- info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
- info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
- info->bmiHeader.biXPelsPerMeter = 0;
- info->bmiHeader.biYPelsPerMeter = 0;
- info->bmiHeader.biClrUsed = 0;
- info->bmiHeader.biClrImportant = 0;
- if (!(alpha = CreateDIBSection( dst, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
- SelectObject( src, hbitmap );
- SelectObject( dst, alpha );
- BitBlt(dst, 0, 0, bm.bmWidth, bm.bmHeight, src, 0, 0, SRCCOPY);
BitBlt() is not guaranteed to preserve alpha, it's not the right function to use here.
I've heard that before but didn't see the reasoning, in which cases it's supposed to fail? Are you probably suggesting to use GdiAlphaBlend() or something else?
I'm using BitBlt() in all my test apps that play with alpha and I didn't see a single case where alpha is not preserved.
Dmitry Timoshkov dmitry@baikal.ru writes:
Alexandre Julliard julliard@winehq.org wrote:
Dmitry Timoshkov dmitry@baikal.ru writes:
+static HBITMAP create_alpha_bitmap( HBITMAP hbitmap ) +{
- HBITMAP alpha = 0;
- BITMAPINFO *info = NULL;
- BITMAP bm;
- HDC src, dst;
- void *bits;
- DWORD i;
- const unsigned char *ptr;
- BOOL has_alpha = FALSE;
- if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0;
- if (bm.bmBitsPixel != 32) return 0;
- if (!(src = CreateCompatibleDC( 0 ))) return 0;
- if (!(dst = CreateCompatibleDC( src ))) goto done;
- if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
- info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info->bmiHeader.biWidth = bm.bmWidth;
- info->bmiHeader.biHeight = -bm.bmHeight;
- info->bmiHeader.biPlanes = 1;
- info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
- info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
- info->bmiHeader.biXPelsPerMeter = 0;
- info->bmiHeader.biYPelsPerMeter = 0;
- info->bmiHeader.biClrUsed = 0;
- info->bmiHeader.biClrImportant = 0;
- if (!(alpha = CreateDIBSection( dst, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
- SelectObject( src, hbitmap );
- SelectObject( dst, alpha );
- BitBlt(dst, 0, 0, bm.bmWidth, bm.bmHeight, src, 0, 0, SRCCOPY);
BitBlt() is not guaranteed to preserve alpha, it's not the right function to use here.
I've heard that before but didn't see the reasoning, in which cases it's supposed to fail? Are you probably suggesting to use GdiAlphaBlend() or something else?
GetDIBits() is a better choice. Though in general you'd have a DIB section already, in which case you don't need to copy the bits at all. It's not clear that an alpha channel is even meaningful with a DDB, did you test that case?
Alexandre Julliard julliard@winehq.org wrote:
+static HBITMAP create_alpha_bitmap( HBITMAP hbitmap ) +{
- HBITMAP alpha = 0;
- BITMAPINFO *info = NULL;
- BITMAP bm;
- HDC src, dst;
- void *bits;
- DWORD i;
- const unsigned char *ptr;
- BOOL has_alpha = FALSE;
- if (!GetObjectW( hbitmap, sizeof(bm), &bm )) return 0;
- if (bm.bmBitsPixel != 32) return 0;
- if (!(src = CreateCompatibleDC( 0 ))) return 0;
- if (!(dst = CreateCompatibleDC( src ))) goto done;
- if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
- info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info->bmiHeader.biWidth = bm.bmWidth;
- info->bmiHeader.biHeight = -bm.bmHeight;
- info->bmiHeader.biPlanes = 1;
- info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
- info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
- info->bmiHeader.biXPelsPerMeter = 0;
- info->bmiHeader.biYPelsPerMeter = 0;
- info->bmiHeader.biClrUsed = 0;
- info->bmiHeader.biClrImportant = 0;
- if (!(alpha = CreateDIBSection( dst, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
- SelectObject( src, hbitmap );
- SelectObject( dst, alpha );
- BitBlt(dst, 0, 0, bm.bmWidth, bm.bmHeight, src, 0, 0, SRCCOPY);
BitBlt() is not guaranteed to preserve alpha, it's not the right function to use here.
I've heard that before but didn't see the reasoning, in which cases it's supposed to fail? Are you probably suggesting to use GdiAlphaBlend() or something else?
GetDIBits() is a better choice. Though in general you'd have a DIB section already, in which case you don't need to copy the bits at all.
In this particular case there is no a DIB section, so I'd need to copy. I still fail to see why BitBlt() won't work in general when copying a bitmap with an alpah channel, and my experiance clearly shows that it does both under Windows and Wine.
It's not clear that an alpha channel is even meaningful with a DDB, did you test that case?
I'm testing with DIB sections, why would I need to test with a DDB?
Alexandre Julliard julliard@winehq.org wrote:
It's not clear that an alpha channel is even meaningful with a DDB, did you test that case?
The app loads the bitmap using LoadBitmap(), i.e. it gets loaded without creating a DIB section, then the app assigns it to "Static" control. Calling BitBlt() with that bitmap as a source and a 32-bpp DIB section as a destination works just fine under Windows and Wine, and the result preserves alpha. Is that what you mean with testing a DDB?
Dmitry Timoshkov dmitry@baikal.ru writes:
Alexandre Julliard julliard@winehq.org wrote:
It's not clear that an alpha channel is even meaningful with a DDB, did you test that case?
The app loads the bitmap using LoadBitmap(), i.e. it gets loaded without creating a DIB section, then the app assigns it to "Static" control. Calling BitBlt() with that bitmap as a source and a 32-bpp DIB section as a destination works just fine under Windows and Wine, and the result preserves alpha. Is that what you mean with testing a DDB?
I don't particularly care about BitBlt() behavior, but I still think GetDIBits() is preferable, particularly since it works even if the bitmap is already selected in a DC.
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
Alexandre Julliard julliard@winehq.org wrote:
It's not clear that an alpha channel is even meaningful with a DDB, did you test that case?
The app loads the bitmap using LoadBitmap(), i.e. it gets loaded without creating a DIB section, then the app assigns it to "Static" control. Calling BitBlt() with that bitmap as a source and a 32-bpp DIB section as a destination works just fine under Windows and Wine, and the result preserves alpha. Is that what you mean with testing a DDB?
I don't particularly care about BitBlt() behavior, but I still think GetDIBits() is preferable, particularly since it works even if the bitmap is already selected in a DC.
I see, thanks for the explanation.
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Dmitry Timoshkov dmitry@baikal.ru writes:
Alexandre Julliard julliard@winehq.org wrote:
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Yes of course. My question was whether on Windows it's drawn transparent even for the DDB case. I guess the answer is yes since you have an app that does this, but a small test case wouldn't hurt.
Alexandre Julliard julliard@winehq.org wrote:
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Yes of course. My question was whether on Windows it's drawn transparent even for the DDB case. I guess the answer is yes since you have an app that does this, but a small test case wouldn't hurt.
I just added the code to my test app to draw a bitmap with BitBlt() loaded with LoadBitmap() (instead of assigning it to the Static control), and the result is drawn without transparency under Windows.
Dmitry Timoshkov dmitry@baikal.ru wrote:
Alexandre Julliard julliard@winehq.org wrote:
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Yes of course. My question was whether on Windows it's drawn transparent even for the DDB case. I guess the answer is yes since you have an app that does this, but a small test case wouldn't hurt.
I just added the code to my test app to draw a bitmap with BitBlt() loaded with LoadBitmap() (instead of assigning it to the Static control), and the result is drawn without transparency under Windows.
On the other hand using GdiAlphaBlend() instead of BitBlt() gives a very nicely looking image. So it's not necessary to create a copy of the source bitmap in the first place, but it's still necessary to verify that the bitmap has an alpha channel in order to use correct API for drawing.
Dmitry Timoshkov dmitry@baikal.ru writes:
Alexandre Julliard julliard@winehq.org wrote:
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Yes of course. My question was whether on Windows it's drawn transparent even for the DDB case. I guess the answer is yes since you have an app that does this, but a small test case wouldn't hurt.
I just added the code to my test app to draw a bitmap with BitBlt() loaded with LoadBitmap() (instead of assigning it to the Static control), and the result is drawn without transparency under Windows.
But if you assign it to the Static control it's transparent, right? And it's really a DDB, right?
Just want to make sure that we need special handling there, and that it's not the app getting a DDB when it should be getting a DIB or something like that.
Alexandre Julliard julliard@winehq.org wrote:
My question was whether it's drawn with transparency in that case, since it's not really specified in which cases a DDB is considered to have an alpha channel.
In order to draw it with a transparency it's necessary to create a 32-bpp DIB section, copy source bitmap bits, and use GdiAlphaBlend() with a DIB section. Basically my patch does all of this.
Yes of course. My question was whether on Windows it's drawn transparent even for the DDB case. I guess the answer is yes since you have an app that does this, but a small test case wouldn't hurt.
I just added the code to my test app to draw a bitmap with BitBlt() loaded with LoadBitmap() (instead of assigning it to the Static control), and the result is drawn without transparency under Windows.
But if you assign it to the Static control it's transparent, right? And it's really a DDB, right?
Right, it's a DDB. I've attached a test app to the bug 47018. That version of the test won't run under XP, I've fixed it in a later version, but that changes nothing to the loading and painting code, I'm just mentioning this in case you will try it under XP.
Just want to make sure that we need special handling there, and that it's not the app getting a DDB when it should be getting a DIB or something like that.
Sure.