在 ComboBox DrawItem 事件中从 TImageList 复制透明(32 位 alpha)位图
Copying transparent (32bit alpha) bitmap from TImageList in ComboBox DrawItem event
我正在自定义 OnDrawItem 事件以在项目名称旁边绘制图标。
到目前为止,这是我为事件 OnDrawItem 编写的代码:
void __fastcall Form1::ComboBox1DrawItem(TWinControl *Control, int Index,
TRect &Rect, TOwnerDrawState State)
{
TComboBox* CB = static_cast<TComboBox*>(Control);
CB->Canvas->FillRect(Rect);
boost::scoped_ptr<Graphics::TBitmap> bitmap(new Graphics::TBitmap());
bitmap->PixelFormat = pf32bit;
bitmap->AlphaFormat = afPremultiplied;
ImageList1->GetBitmap(Index, bitmap.get());
bitmap->AlphaFormat = afPremultiplied;
if (bitmap->Canvas->Handle)
{
// structure for alpha blending
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xFF; // 0x00 (transparent) through 0xFF (opaque)
bf.AlphaFormat = AC_SRC_ALPHA; // Use bitmap alpha
::AlphaBlend(CB->Canvas->Handle, // handle to destination DC
Rect.Left + 2, // x-coord of upper-left corner
Rect.Top, // y-coord of upper-left corner
bitmap->Width, // destination width
bitmap->Height, // destination height
bitmap->Canvas->Handle, // handle to source DC
0, // x-coord of upper-left corner
0, // y-coord of upper-left corner
bitmap->Width, // source width
bitmap->Height, // source height
bf // alpha-blending function
);
}
Rect = Bounds(Rect.Left + 20 + 2, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
DrawTextW(CB->Canvas->Handle, CB->Items->Strings[Index].c_str(), -1, &Rect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
问题当然是让透明 TImageList1
复制到透明 TBitmap
并保留 32 位 alpha transparency/semi-transparency。目前我在结果 TBitmap
.
中用白色背景得到它
明确一点,TImageList
ColorDepth
设置为 cd32bit
和 DrawingStyle = dsTransparent
在加载图像之前,上面的图像是透明的,没有问题.
解决这个问题的技巧是什么?
更新和我的最终解决方案
根据此处的回复,这是我的最终工作代码,供将来可能需要它的其他人使用。这当然只是一个模板代码,您可能希望根据自己的需要进一步自定义。
void __fastcall TForm1::ComboBox1DrawItem(TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State)
{
if (Index >= 0)
{
TComboBox* CB = static_cast<TComboBox*>(Control);
CB->Canvas->FillRect(Rect);
// Note - ImageList1 already has DrawingStyle set to dsTransparent
ImageList1->Draw(CB->Canvas, Rect.Left + 2, Rect.Top, 0);
Rect = Bounds(Rect.Left + ImageList1->Width + 2 + 2, Rect.Top, Rect.Right - Rect.Left - ImageList1->Width - 2, Rect.Bottom - Rect.Top);
DrawTextW(CB->Canvas->Handle, CB->Items->Strings[Index].c_str(), -1, &Rect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
}
您无需尝试从图像列表中获取原始位图,因为图像列表本身知道如何绘制尊重透明度信息。您可以为此使用其 Draw
方法。
否则,答案 here 建议在调用 GetBitmap
之前将 AlphaFormat
设置为 'afIgnored' 应该保持透明度。
我正在自定义 OnDrawItem 事件以在项目名称旁边绘制图标。 到目前为止,这是我为事件 OnDrawItem 编写的代码:
void __fastcall Form1::ComboBox1DrawItem(TWinControl *Control, int Index,
TRect &Rect, TOwnerDrawState State)
{
TComboBox* CB = static_cast<TComboBox*>(Control);
CB->Canvas->FillRect(Rect);
boost::scoped_ptr<Graphics::TBitmap> bitmap(new Graphics::TBitmap());
bitmap->PixelFormat = pf32bit;
bitmap->AlphaFormat = afPremultiplied;
ImageList1->GetBitmap(Index, bitmap.get());
bitmap->AlphaFormat = afPremultiplied;
if (bitmap->Canvas->Handle)
{
// structure for alpha blending
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xFF; // 0x00 (transparent) through 0xFF (opaque)
bf.AlphaFormat = AC_SRC_ALPHA; // Use bitmap alpha
::AlphaBlend(CB->Canvas->Handle, // handle to destination DC
Rect.Left + 2, // x-coord of upper-left corner
Rect.Top, // y-coord of upper-left corner
bitmap->Width, // destination width
bitmap->Height, // destination height
bitmap->Canvas->Handle, // handle to source DC
0, // x-coord of upper-left corner
0, // y-coord of upper-left corner
bitmap->Width, // source width
bitmap->Height, // source height
bf // alpha-blending function
);
}
Rect = Bounds(Rect.Left + 20 + 2, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
DrawTextW(CB->Canvas->Handle, CB->Items->Strings[Index].c_str(), -1, &Rect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
问题当然是让透明 TImageList1
复制到透明 TBitmap
并保留 32 位 alpha transparency/semi-transparency。目前我在结果 TBitmap
.
明确一点,TImageList
ColorDepth
设置为 cd32bit
和 DrawingStyle = dsTransparent
在加载图像之前,上面的图像是透明的,没有问题.
解决这个问题的技巧是什么?
更新和我的最终解决方案
根据此处的回复,这是我的最终工作代码,供将来可能需要它的其他人使用。这当然只是一个模板代码,您可能希望根据自己的需要进一步自定义。
void __fastcall TForm1::ComboBox1DrawItem(TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State)
{
if (Index >= 0)
{
TComboBox* CB = static_cast<TComboBox*>(Control);
CB->Canvas->FillRect(Rect);
// Note - ImageList1 already has DrawingStyle set to dsTransparent
ImageList1->Draw(CB->Canvas, Rect.Left + 2, Rect.Top, 0);
Rect = Bounds(Rect.Left + ImageList1->Width + 2 + 2, Rect.Top, Rect.Right - Rect.Left - ImageList1->Width - 2, Rect.Bottom - Rect.Top);
DrawTextW(CB->Canvas->Handle, CB->Items->Strings[Index].c_str(), -1, &Rect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
}
您无需尝试从图像列表中获取原始位图,因为图像列表本身知道如何绘制尊重透明度信息。您可以为此使用其 Draw
方法。
否则,答案 here 建议在调用 GetBitmap
之前将 AlphaFormat
设置为 'afIgnored' 应该保持透明度。