什么会使静态优化校准将某些像素显示为透明(背景颜色)
What would make a static Picture Control display some pixels as transparent (bg color)
我正在更新问题以删除不相关的细节。我得出的结论是,如果存在有效的 alpha 通道,它会接受它,但如果不存在(比如 24 位 PNG w/o alpha 通道),它会使用 F0F0F0 作为透明颜色。
我在对话框中将图像加载到静态 "picture control"(在 visual studio 中选择)。我注意到颜色 0xF0F0F0 显示为 "transparent" 颜色(对话框的背景渗出)。位图通过 CStatic::SetBitmap.
加载
优化校准透明标志设置为 false。
图像通过CImage::Load加载。
如果我想从通过 SetBitmap 设置的 CStatic 位图中屏蔽颜色,我该怎么做?我不知道,但也许这会帮助我找到原因。
下面是最小示例。我用VS向导创建了一个对话框项目,并在主对话框中添加了一个图片控件。然后我只添加了以下代码:
//header code added
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
....
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
//cpp code added
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
....
ON_WM_CTLCOLOR()
....
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\temp\logo.png"));
pictureCtrl.SetBitmap(logoImage);
....
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
这是我正在测试的图像文件。
这是它在对话框中的样子:
// MFCApplication1Dlg.h : header file
//
#pragma once
// CMFCApplication1Dlg dialog
class CMFCApplication1Dlg : public CDialogEx
{
// Construction
public:
CMFCApplication1Dlg(CWnd* pParent = nullptr); // standard constructor
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
// MFCApplication1Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCApplication1Dlg dialog
CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
// CMFCApplication1Dlg message handlers
BOOL CMFCApplication1Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\temp\logo.png"));
pictureCtrl.SetBitmap(logoImage);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMFCApplication1Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
不是PNG的问题,是颜色深度的问题。
根据你的代码,我用格式转换工具把8位PNG图片转成了8位BMP图片,图片还是显示背景色。
于是我把8位的PNG图片存为32位的PNG图片,就可以了。
这是为什么?
答案:GIF 文件有两部分:颜色 table 和图像像素数据。颜色 table 是该图像中使用的颜色列表(一个 8 位 GIF 在颜色 table 中最多可以有 2^8 = 256 种颜色,但是一个 4 位 GIF 可以有只有 2^4 = 16 种颜色),并且每种颜色都分配了一个数字。图像像素数据是针对图像本身的,在颜色table中为每个像素分配一个指向其颜色的数字。例如,如果颜色 table 中的颜色 #10 是红色 (#FF0000),则图像中编号为 10 的任何像素都将显示为红色。颜色 table 中的颜色会根据图像本身的不同 GIF 文件而有所不同;颜色#10 不会总是红色。颜色 table 是渲染该图像所需的最多 256 种颜色的集合。
当我们添加索引透明度时,颜色 table 中的每种颜色除了其颜色数据(即 RGB 值)外,还被赋予透明度名称:
零(布尔代数中的 o = False)表示不显示此颜色,或者
one(布尔代数中的 1 = True)表示显示此颜色。
没有中间混浊;颜色要么显示,要么不显示。最终结果是具有索引透明颜色的像素将不会显示,并且该像素后面的背景中的任何内容都会显示出来。例如,如果颜色 #10 为红色 (#FF0000) 并指定为透明(索引透明度 = 0),则任何颜色为 #10 的像素都不会显示,背景会透出。
索引透明度可以有多种透明颜色,因为颜色 table 中的每种颜色都指定了不透明 (1) 或透明 (0)。大多数图形程序假定 canvas 颜色(通常是白色,但它可以是任何颜色)是默认的透明颜色,但您可以将任何颜色(或任何数量的颜色)指定为透明或不透明。
这种透明度在 GIF 和 PNG8 文件中很常见并且很容易识别,因为没有褪色,没有部分透明像素,边缘通常被描述为“硬”或“像素化”。 ”
恐怕以下是我将要得到的最佳答案。 MFC/Win32 做了一些有趣的事情,目前还不清楚为什么。但是手动绘制的图片是没有问题的。
我创建了一个自定义(非常粗糙)的 CStatic class 并在下面添加了 OnPaint。在屏幕截图中,第一个是使用我的 class,第二个是使用常规 CStatic。两者都使用相同的图像(24 位 BMP)。
struct AFX_CTLCOLOR {
HWND hWnd;
HDC hDC;
UINT nCtlType;
};
void CMyStatic::OnPaint() {
CPaintDC dc(this); // device context for painting
CRect r;
GetClientRect(r);
WPARAM w = (WPARAM)&dc;
AFX_CTLCOLOR ctl;
ctl.hWnd = this->GetSafeHwnd();
ctl.nCtlType = CTLCOLOR_STATIC;
ctl.hDC = dc.GetSafeHdc();
HBRUSH bg=(HBRUSH)::SendMessage(GetParent()->GetSafeHwnd(), WM_CTLCOLORSTATIC, w, (LPARAM)&ctl);
CBrush cbg;
cbg.Attach(bg);
dc.FillRect(r, &cbg);
cbg.Detach();
HBITMAP hbmp=GetBitmap();
BITMAP binfo;
CBitmap* cbmp = CBitmap::FromHandle(hbmp);
cbmp->GetBitmap(&binfo);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
dcMem.SelectObject(cbmp);
dc.BitBlt((r.Width()-binfo.bmWidth)/2, (r.Height() - binfo.bmHeight) / 2, binfo.bmWidth, binfo.bmHeight, &dcMem, 0, 0, SRCCOPY);
}
在我的系统 (Windows 10) 上,颜色 0xF0F0F0
等于 GetSysColor(COLOR_BTNFACE)
,这是默认的对话框背景颜色。绘制时,静态控件似乎用父 window 的 OnCtlColor()
处理程序返回的画笔替换背景图像中的这种颜色。这确实具有功能的味道而不是错误(尽管我在参考中找不到任何指定此行为的内容)。
Here is a code snippet 即使不使用 CPngImage
或 CImage
也能重现此问题,只需在内存 DC 中绘制颜色 0xF0F0F0
.
由于该行为仅在源图像不包含 alpha 通道时出现,解决方案是 将源图像转换为 32-bpp ARGB 格式。这样我们就不必覆盖 CStatic::OnPaint()
:
// Set the alpha channel of a 32-bpp ARGB image to the given value.
HRESULT SetAlphaChannel( CImage& image, std::uint8_t alpha )
{
if( ! image.GetBits() || image.GetBPP() != 32 )
return E_INVALIDARG;
GdiFlush(); // Make sure GDI has finished all drawing in source image.
for( int y = 0; y < image.GetHeight(); ++y )
{
DWORD* pPix = reinterpret_cast<DWORD*>( image.GetPixelAddress( 0, y ) );
for( int x = 0; x < image.GetWidth(); ++x, ++pPix )
{
*pPix = ( *pPix & 0xFFFFFF ) | ( alpha << 24 );
}
}
return S_OK;
}
// Load an image and convert to 32-bpp ARGB format, if necessary.
HRESULT LoadImageAndConvertToARGB32( CImage& image, LPCWSTR pFilePath )
{
CImage tempImage;
HRESULT hr = tempImage.Load( pFilePath );
if( FAILED( hr ) )
return hr;
if( tempImage.GetBPP() == 32 ) // Assume 32 bpp image already has an alpha channel
{
image.Attach( tempImage.Detach() );
return S_OK;
}
if( ! image.Create( tempImage.GetWidth(), tempImage.GetHeight(), 32, CImage::createAlphaChannel ) )
return E_FAIL;
HDC const imageDC = image.GetDC();
BOOL const bitBltSuccess = tempImage.BitBlt( imageDC, 0, 0, SRCCOPY );
image.ReleaseDC();
if( ! bitBltSuccess )
return E_FAIL;
SetAlphaChannel( image, 255 ); // set alpha to opaque
return S_OK;
}
用法:
将对 CImage::Load()
的调用替换为:
LoadImageAndConvertToARGB32( m_image, filePath );
备注:
当您将带有 non-zero alpha 通道的 32-bpp 位图分配给控件时,还有另一个静态控件问题¹(就像您在遵循我的解决方案时所做的那样)。在这种情况下,静态控件将复制您传入的位图,而您负责销毁该副本!
必读旧新事物:
¹) 更准确地说:当使用版本 6 的公共控件时,现在几乎所有应用程序都这样做。
我正在更新问题以删除不相关的细节。我得出的结论是,如果存在有效的 alpha 通道,它会接受它,但如果不存在(比如 24 位 PNG w/o alpha 通道),它会使用 F0F0F0 作为透明颜色。
我在对话框中将图像加载到静态 "picture control"(在 visual studio 中选择)。我注意到颜色 0xF0F0F0 显示为 "transparent" 颜色(对话框的背景渗出)。位图通过 CStatic::SetBitmap.
加载优化校准透明标志设置为 false。
图像通过CImage::Load加载。
如果我想从通过 SetBitmap 设置的 CStatic 位图中屏蔽颜色,我该怎么做?我不知道,但也许这会帮助我找到原因。
下面是最小示例。我用VS向导创建了一个对话框项目,并在主对话框中添加了一个图片控件。然后我只添加了以下代码:
//header code added
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
....
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
//cpp code added
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
....
ON_WM_CTLCOLOR()
....
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\temp\logo.png"));
pictureCtrl.SetBitmap(logoImage);
....
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
这是我正在测试的图像文件。
这是它在对话框中的样子:
// MFCApplication1Dlg.h : header file
//
#pragma once
// CMFCApplication1Dlg dialog
class CMFCApplication1Dlg : public CDialogEx
{
// Construction
public:
CMFCApplication1Dlg(CWnd* pParent = nullptr); // standard constructor
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
// MFCApplication1Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCApplication1Dlg dialog
CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
// CMFCApplication1Dlg message handlers
BOOL CMFCApplication1Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\temp\logo.png"));
pictureCtrl.SetBitmap(logoImage);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMFCApplication1Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
不是PNG的问题,是颜色深度的问题。
根据你的代码,我用格式转换工具把8位PNG图片转成了8位BMP图片,图片还是显示背景色。
于是我把8位的PNG图片存为32位的PNG图片,就可以了。
这是为什么?
答案:GIF 文件有两部分:颜色 table 和图像像素数据。颜色 table 是该图像中使用的颜色列表(一个 8 位 GIF 在颜色 table 中最多可以有 2^8 = 256 种颜色,但是一个 4 位 GIF 可以有只有 2^4 = 16 种颜色),并且每种颜色都分配了一个数字。图像像素数据是针对图像本身的,在颜色table中为每个像素分配一个指向其颜色的数字。例如,如果颜色 table 中的颜色 #10 是红色 (#FF0000),则图像中编号为 10 的任何像素都将显示为红色。颜色 table 中的颜色会根据图像本身的不同 GIF 文件而有所不同;颜色#10 不会总是红色。颜色 table 是渲染该图像所需的最多 256 种颜色的集合。
当我们添加索引透明度时,颜色 table 中的每种颜色除了其颜色数据(即 RGB 值)外,还被赋予透明度名称:
零(布尔代数中的 o = False)表示不显示此颜色,或者 one(布尔代数中的 1 = True)表示显示此颜色。 没有中间混浊;颜色要么显示,要么不显示。最终结果是具有索引透明颜色的像素将不会显示,并且该像素后面的背景中的任何内容都会显示出来。例如,如果颜色 #10 为红色 (#FF0000) 并指定为透明(索引透明度 = 0),则任何颜色为 #10 的像素都不会显示,背景会透出。
索引透明度可以有多种透明颜色,因为颜色 table 中的每种颜色都指定了不透明 (1) 或透明 (0)。大多数图形程序假定 canvas 颜色(通常是白色,但它可以是任何颜色)是默认的透明颜色,但您可以将任何颜色(或任何数量的颜色)指定为透明或不透明。
这种透明度在 GIF 和 PNG8 文件中很常见并且很容易识别,因为没有褪色,没有部分透明像素,边缘通常被描述为“硬”或“像素化”。 ”
恐怕以下是我将要得到的最佳答案。 MFC/Win32 做了一些有趣的事情,目前还不清楚为什么。但是手动绘制的图片是没有问题的。
我创建了一个自定义(非常粗糙)的 CStatic class 并在下面添加了 OnPaint。在屏幕截图中,第一个是使用我的 class,第二个是使用常规 CStatic。两者都使用相同的图像(24 位 BMP)。
struct AFX_CTLCOLOR {
HWND hWnd;
HDC hDC;
UINT nCtlType;
};
void CMyStatic::OnPaint() {
CPaintDC dc(this); // device context for painting
CRect r;
GetClientRect(r);
WPARAM w = (WPARAM)&dc;
AFX_CTLCOLOR ctl;
ctl.hWnd = this->GetSafeHwnd();
ctl.nCtlType = CTLCOLOR_STATIC;
ctl.hDC = dc.GetSafeHdc();
HBRUSH bg=(HBRUSH)::SendMessage(GetParent()->GetSafeHwnd(), WM_CTLCOLORSTATIC, w, (LPARAM)&ctl);
CBrush cbg;
cbg.Attach(bg);
dc.FillRect(r, &cbg);
cbg.Detach();
HBITMAP hbmp=GetBitmap();
BITMAP binfo;
CBitmap* cbmp = CBitmap::FromHandle(hbmp);
cbmp->GetBitmap(&binfo);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
dcMem.SelectObject(cbmp);
dc.BitBlt((r.Width()-binfo.bmWidth)/2, (r.Height() - binfo.bmHeight) / 2, binfo.bmWidth, binfo.bmHeight, &dcMem, 0, 0, SRCCOPY);
}
在我的系统 (Windows 10) 上,颜色 0xF0F0F0
等于 GetSysColor(COLOR_BTNFACE)
,这是默认的对话框背景颜色。绘制时,静态控件似乎用父 window 的 OnCtlColor()
处理程序返回的画笔替换背景图像中的这种颜色。这确实具有功能的味道而不是错误(尽管我在参考中找不到任何指定此行为的内容)。
Here is a code snippet 即使不使用 CPngImage
或 CImage
也能重现此问题,只需在内存 DC 中绘制颜色 0xF0F0F0
.
由于该行为仅在源图像不包含 alpha 通道时出现,解决方案是 将源图像转换为 32-bpp ARGB 格式。这样我们就不必覆盖 CStatic::OnPaint()
:
// Set the alpha channel of a 32-bpp ARGB image to the given value.
HRESULT SetAlphaChannel( CImage& image, std::uint8_t alpha )
{
if( ! image.GetBits() || image.GetBPP() != 32 )
return E_INVALIDARG;
GdiFlush(); // Make sure GDI has finished all drawing in source image.
for( int y = 0; y < image.GetHeight(); ++y )
{
DWORD* pPix = reinterpret_cast<DWORD*>( image.GetPixelAddress( 0, y ) );
for( int x = 0; x < image.GetWidth(); ++x, ++pPix )
{
*pPix = ( *pPix & 0xFFFFFF ) | ( alpha << 24 );
}
}
return S_OK;
}
// Load an image and convert to 32-bpp ARGB format, if necessary.
HRESULT LoadImageAndConvertToARGB32( CImage& image, LPCWSTR pFilePath )
{
CImage tempImage;
HRESULT hr = tempImage.Load( pFilePath );
if( FAILED( hr ) )
return hr;
if( tempImage.GetBPP() == 32 ) // Assume 32 bpp image already has an alpha channel
{
image.Attach( tempImage.Detach() );
return S_OK;
}
if( ! image.Create( tempImage.GetWidth(), tempImage.GetHeight(), 32, CImage::createAlphaChannel ) )
return E_FAIL;
HDC const imageDC = image.GetDC();
BOOL const bitBltSuccess = tempImage.BitBlt( imageDC, 0, 0, SRCCOPY );
image.ReleaseDC();
if( ! bitBltSuccess )
return E_FAIL;
SetAlphaChannel( image, 255 ); // set alpha to opaque
return S_OK;
}
用法:
将对 CImage::Load()
的调用替换为:
LoadImageAndConvertToARGB32( m_image, filePath );
备注:
当您将带有 non-zero alpha 通道的 32-bpp 位图分配给控件时,还有另一个静态控件问题¹(就像您在遵循我的解决方案时所做的那样)。在这种情况下,静态控件将复制您传入的位图,而您负责销毁该副本!
必读旧新事物:
¹) 更准确地说:当使用版本 6 的公共控件时,现在几乎所有应用程序都这样做。