如何在TSpeedButton上绘制带有Alpha通道的图像?

7
我有一个TSpeedButton和一个包含各种图像的TImageList。其中一个图像具有alpha通道,当绘制到UI的某些部分时,它看起来非常好...但是当该图像绘制在TSpeedButton上时,它不会考虑alpha通道。
查看TButtonGlyph.DrawButtonGlyph中相关的代码,发现传递给Transparent参数的是true,但在if FThemesEnabled的代码路径中根本没有考虑Transparent;它只在else部分中被引用。由于此程序启用了主题,这是否意味着无法向TSpeedButton绘制alpha混合的图像?
有没有办法解决这个问题?
编辑:仔细查看后,似乎确实考虑了透明度。完全透明的像素根本不会被绘制,这是正确的行为。但是边缘周围的抗锯齿(部分透明)被绘制为完全不透明。

1
如果David Heffernan看到这个信息:请不要要求代码示例,否则我会举报您。这不是关于我的代码的问题;这是关于VCL在一个高度特定的上下文中绘制图像的问题。谢谢。 - Mason Wheeler
2
DrawThemeIcon:使用视觉样式定义的图标效果从图像列表中绘制图像。这是图标效果,无论它意味着什么... - Sertac Akyuz
实际上,据我所见,它可以正确地绘制部分透明度,但是会在白色背景下进行绘制。也许这种颜色取决于视觉风格。 - Sertac Akyuz
@DavidHeffernan:正如您(出色的)答案所示,所有相关信息已经存在,知道这一点并不会改变任何事情。记录一下,该属性是使用ImageList.GetBitmap(index, SpeedButton.Glyph);设置的,但是知道这一点真的会有任何区别吗? - Mason Wheeler
2
@Mason 我不知道。可能不是。这里的答案需要研究相当多的代码,但它是你不能发布的vcl代码。我认为你的第一条评论有点刻薄。我经常出于好的原因请求代码。例如,请参见今天树形视图AV问题的编辑历史记录。我认为我知道自己在做什么。 - David Heffernan
2个回答

7
从我所看到的,TSpeedButton会接收你的位图并将其添加到图像列表中。在TButtonGlyph的私有部分中是FGlyphList。该图像列表具有cdDeviceDependentColorDepth。如果您希望透明度被尊重,则需要该图像列表具有cd32BitColorDepth。因此,这就解释了那些伪像。

我不确定如何解决这个问题。我认为我个人会子类化TButtonGlyph并直接连接到您选择的图像列表。然后用从您的图像列表中绘制的代码替换TButtonGlyph.DrawButtonGlyph。我相信您能够解决编码问题。

但是没有简单的解决方法。如果没有对VCL进行重大修改,该图像列表将使用cdDeviceDependent,您无法简单地更改它。我没有看到任何明显的快速修复。

实际上,我想做的是使用TButton,但这只是我的个人意见。


关于速度按钮的Transparent属性,文档中写道:

指定按钮背景是否透明。

使用 Transparent 指定按钮背景是否透明。

此属性仅在 TSpeedButton 的 Flat 属性设置为 True 时有效。

启用 Windows 主题时,Transparent 属性无法使按钮背景透明。

也就是说,该属性影响扁平按钮的背景,当未启用主题时,并与图标绘制无关。

仅仅使用TButton并不是一个选项,我需要一个带有“按下”状态的按钮,可以像复选框一样使用。如果这是底层实现的情况,我可能需要查看第三方组件。谢谢。 - Mason Wheeler
你可以使用 TSpeedButton 作为模板来构建自己的按钮。这样可以削减掉许多不必要的冗余代码。 - David Heffernan

1
为了完全公开披露:我遇到了类似的问题,并在这个问答中基于它来构建了我的初始解决方案。由于我需要快速修复我的应用程序和编译器(我使用C++ Builder 2009),所以我决定“黑客” VCL 的私有成员并将 Glyphlist 的 ColorDepth 设置为 cd32Bit,直到我有时间处理更永久的东西。这种黑客行为起初有效,但还远非理想,显而易见的原因是功能上也存在问题。
然而,在这样做的同时,我进一步进行了实验,并最终得出了下面的工作解决方案,无需任何肮脏的技巧。由于我对VCL的内部工作方式不太熟悉,并且从那些更深入了解VCL的人们得到了“它不可能是现成的”的反馈,所以我有点不确定我现在缺少什么?因为下面的解决方案适用于我。在XP、XP-High-contrast、W7和W10上已确认。Glyphs 在W2K上看起来不好,但对于主菜单和弹出式图标也是如此,所以就是这样。
我用C++编程,但使用相同的VCL基础库,所以我将在这里发布我的C++解决方案,作为对Delphi问题的回答。我希望这样做可以提供一种解决方案,而不是将其保留给自己。除非我漏掉了什么,否则Delphi大师也可以将其转换并发布为答案。
由于我首先采用了“hack”方法,子类化了TSpeedButton并将代码放在MyTSpeedButton函数中,但这不是使其工作所必需的。然而,我正在发布的就是这段代码: 头文件
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 ;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接