Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I draw an image with an alpha channel to a TSpeedButton?

I've got a TSpeedButton, and a TImageList that holds various glyphs. One of them has an alpha channel, and it looks very nice when drawn to certain parts of the UI... but when that glyph is drawn on a TSpeedButton, it doesn't take the alpha channel into effect.

Looking at the relevant code in TButtonGlyph.DrawButtonGlyph, it's getting passed true for the Transparent parameter, but Transparent isn't taken into account at all in the if FThemesEnabled code path; it only gets referenced in the else section. Since this program has themes enabled, does that mean it's impossible to draw an alpha-blended image to a TSpeedButton?

Is there any way to work around this?

EDIT: Looking at this a bit more closely, it appears that it takes transparency into account... sort of. Pixels that are fully transparent don't get drawn at all, which is correct behavior. But the antialiasing (partial transparency) around the edges gets drawn as fully opaque.

like image 889
Mason Wheeler Avatar asked Sep 10 '14 23:09

Mason Wheeler


2 Answers

From what I can see, TSpeedButton takes your bitmap and adds it to an image list. That's FGlyphList in the private section of TButtonGlyph. That image list has ColorDepth of cdDeviceDependent. You'd need that image list to have ColorDepth of cd32Bit if you wanted alpha transparency to be respected. So that right there explains the artifacts.

I'm not sure how you go about working around this. I think that I'd personally subclass TButtonGlyph and connect it directly to an image list of your selection. Then replace TButtonGlyph.DrawButtonGlyph with code that draws from your image list. I'm sure you'll be able to work out the coding.

But there'll be no simple workaround. Without major modifications to the VCL, that image list is going to use cdDeviceDependent and there's no simple way for you to change that. There's no obvious quick fix that I can see.

Actually, what I would do would be to use TButton, but that's just me.


Regarding the Transparent property of the speed button the documentation says:

Specifies whether the background of the button is transparent.

Use Transparent to specify whether the background of the button is transparent.

This property works only if the Flat property of the TSpeedButton is set to True.

When Windows themes are enabled, the Transparent property cannot make the button background transparent.

That is, the property influences the button background, for flat buttons, when unthemed, and has no relevance to glyph drawing.

like image 82
David Heffernan Avatar answered Sep 17 '22 01:09

David Heffernan


In the interest of full disclosure: I ran into a similar problem and used this Q&A to base my initial solution on. Since I needed a quick fix for my app and compiler (I use C++ Builder 2009) I decided to 'hack' the VCL's private members and set Glyphlist's ColorDepth to cd32Bit, until I had time to work on something more permanent. This hack worked, with a few more changes, but was far from ideal, for all the obvious reasons, but also functionality wise, there were issues to consider.

However, while doing so I experimented further and finally arrived at below, working solution, without any dirty tricks needed. Not being that familiar with the internal workings of VCL and the rather 'it's not possible as is' feedback from people who are far more in depth in VCL I'm a bit unsure on what I'm missing right now !? Because below solution works for me. Confirmed on XP, XP-High-contrast, W7 and W10. The Glyphs don't look good on W2K but then that is true for Main Menu and Popup icons as well, so that is that.

I code in C++ but use the same underlying VCL, so I will post my C++ solution here, as answer to a Delphi question. I hope that is OK, in the interest of offering a path to a solution rather than keeping it to myself. A Delphi guru can convert and post as answer as well then, unless I'm missing something and my solution isn't what was needed all along.

Since I started with the 'hack' approach first I subclassed TSpeedButton and put the code in a MyTSpeedButton function, but that is not needed to make this work. However, it is the code that I'm posting:

header

class MyVCLTSpeedButton : public TSpeedButton
    {
    public:

    __fastcall MyVCLTSpeedButton(Classes::TComponent* AOwner) ;

    void __fastcall AddGlyphWithAlphaChannel(Imglist::TCustomImageList* ImageList, int UpIndex, int DisabledIndex = -1, int ClickedIndex = -1, int DownIndex = -1) ;

    };

cpp

void __fastcall MyVCLTSpeedButton::AddGlyphWithAlphaChannel(Imglist::TCustomImageList* ImageList, int UpIndex, int DisabledIndex, int ClickedIndex, int DownIndex)
{
Glyph->Assign(NULL) ; // Clear the current bitmap 

NumGlyphs = (DisabledIndex > -1)?((ClickedIndex > -1)?((DownIndex > -1)?(4):(3)):(2)):(1) ;

Graphics::TBitmap *BitMap = new Graphics::TBitmap ;

//BitMap->AlphaFormat = afDefined ; // Don't Change AlphaFormat, it will negatively affect rendering !
BitMap->PixelFormat = pf32bit ;   // Doesn't seem to be that important
BitMap->Height = ImageList->Height ;
BitMap->Width  = (ImageList->Width * NumGlyphs) ;

BitMap->Canvas->Brush->Color = Parent->Brush->Color ;
BitMap->Canvas->Brush->Style = bsSolid ;
BitMap->Canvas->FillRect(BitMap->Canvas->ClipRect) ;

TIcon *Icon = new TIcon() ;

for (int x = 0 ; x < NumGlyphs ; x++)
    {
    ImageList->GetIcon((x == 0)?(UpIndex):((x == 1)?(DisabledIndex):((x == 2)?(ClickedIndex):(DownIndex))), Icon) ;
    BitMap->Canvas->Draw((ImageList->Width * x), 0, Icon) ;
    }

Glyph->Assign(BitMap) ;

delete Icon ;
delete BitMap ;
}
like image 26
Peter Avatar answered Sep 17 '22 01:09

Peter