我正在更新问题,以删除无关的细节。我得出的结论是,如果存在有效的Alpha通道,它将认可它,但是如果不存在(例如,一个不带Alpha通道的24位PNG),它将使用F0F0F0作为透明颜色。
我在对话框中将图像加载到静态“图片控件”(在Visual Studio中选择)。我注意到颜色0xF0F0F0被显示为“透明”颜色(对话框的背景流血)。通过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;
}
答案 0 :(得分:1)
在我的系统上(Windows 10),颜色0xF0F0F0
等于GetSysColor(COLOR_BTNFACE)
,这是默认对话框背景色。绘制时,静态控件似乎用从父窗口的OnCtlColor()
处理程序返回的画笔替换了背景图像中的这种颜色。它确实具有功能性而不是bug的味道(尽管我在引用中找不到指定此行为的任何内容)。
Here is a code snippet甚至不使用CPngImage
或CImage
来重现此问题,只需绘制颜色为0xF0F0F0
的内存DC。
由于该行为仅在源图像不包含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 );
注释:
当您将32位bpp具有非零alpha通道的位图分配给控件¹时,还有另一种静态控件的麻烦(就像在遵循我的解决方案时所做的那样)。在这种情况下,静态控件将复制您传入的位图,而您负责销毁该副本!
必读OldNewThing :
¹ )更准确地说:使用通用控件的版本6时,如今几乎所有应用程序都在使用它。
答案 1 :(得分:0)
这不是PNG问题,而是颜色深度问题。
根据您的代码,我使用格式转换工具将8位PNG图片转换为8位BMP图片,图片仍然显示背景颜色。
为什么?
答案:GIF文件包含两个部分:颜色表和图像像素数据。颜色表是该图像中使用的颜色的列表(8位GIF在颜色表中最多可以具有2 ^ 8 = 256种颜色,而4位GIF只能具有2 ^ 4 = 16种颜色) ,并为每种颜色分配一个数字。图像像素数据用于图像本身,并且为每个像素分配一个数字,该数字指向颜色表中的颜色。例如,如果颜色表中的颜色#10为红色(#FF0000),则图像中编号为10的任何像素将显示为红色。颜色表中的颜色会根据图像本身从GIF文件到GIF文件而有所不同。颜色#10并不总是红色。颜色表是渲染该图像所需的多达256种颜色的集合。
当我们添加索引透明度时,除了颜色数据(即RGB值)外,颜色表中的每种颜色都被赋予透明度名称:
零(在布尔代数中,o = False)表示不显示此颜色,或者 一个(在布尔代数中为1 = True)表示显示此颜色。 没有中间的混浊;颜色显示或不显示。最终结果是将不会显示具有索引透明颜色的像素,并且该像素后面的背景中的任何内容都将显示出来。例如,如果颜色#10为红色(#FF0000)并被指定为透明(索引透明度= 0),则将不显示任何颜色为#10的像素,并且背景将始终显示。
索引透明中可以有多种透明颜色,因为颜色表中的每种颜色都具有不透明(1)或透明(0)的名称。大多数图形程序都假定画布颜色(通常是白色,但可以是任何颜色)是默认的透明颜色,但是您可以将任何颜色(或任何数量的颜色)指定为透明或不透明。
这种类型的透明度在GIF和PNG8文件中非常常见,并且易于识别,因为没有褪色,没有部分透明的像素,并且边缘通常被描述为“硬”或“像素化”。 / p>
答案 2 :(得分:0)
恐怕以下是我要获得的最佳答案。 MFC / Win32做一些有趣的事情,还不清楚原因。但是,如果手动绘制图像,则可以正确显示图像。
我创建了一个自定义(非常粗糙)的CStatic类,并在下面添加了OnPaint。在屏幕截图中,第一个使用我的类,第二个使用常规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);
}