Что бы сделать статический Picture Control отображать некоторые пиксели как прозрачные (цвет bg) - PullRequest
1 голос
/ 22 июня 2019

Я обновляю вопрос, чтобы удалить ненужные детали. Вывод, который я сделал, состоит в том, что если существует действительный альфа-канал, он выполнит его, но если его нет (скажем, 24-битный PNG без альфа-канала), он использует F0F0F0 в качестве прозрачного цвета.

У меня есть изображение, загружаемое в статический «контроль изображения» (выбранный в visual studio) в диалоге. Я заметил, что цвет 0xF0F0F0 отображается как «прозрачный» цвет (фон диалога пропускает свет). Растровое изображение загружается через CStatic :: SetBitmap.

Для прозрачного флага Picture Control установлено значение false.

Изображение загружается через CImage :: Load.

Если бы я хотел замаскировать цвет из растрового изображения CStatic, установленного через SetBitmap, как бы я это сделал? Я не знаю, но, возможно, это поможет мне найти причину.

Минимальный пример ниже. Я создал проект диалога с мастером 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;
}

А вот и файл изображения, с которым я тестирую.

https://i.imgur.com/OA9CCh8.png

А вот как это выглядит в диалоге:

enter image description here

// 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;
}

Ответы [ 3 ]

1 голос
/ 26 июня 2019

В моей системе (Windows 10) цвет 0xF0F0F0 равен GetSysColor(COLOR_BTNFACE), который является цветом фона диалога по умолчанию.При рисовании статический элемент управления заменяет этот цвет на фоновом изображении кистью, возвращенной из обработчика OnCtlColor() родительского окна.Это имеет вкус функции, а не ошибки (хотя я не смог найти ничего в справочнике, который определяет это поведение).

Вот фрагмент кода , чтобы воспроизвести эту проблемудаже без использования CPngImage или CImage, просто рисуя в памяти DC цветом 0xF0F0F0.

Поскольку поведение появляется только тогда, когда исходное изображение не содержит альфа-канал, решение будет преобразовать исходное изображение в формат ARGB 32 бит / с.Таким образом, нам не нужно переопределять 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 битами на бит с ненулевым альфа-каналом для элемента управления существует еще один недостаток статического контроля¹ (как вы делаете, следуя моему решению),В этом случае статический элемент управления создаст копию переданного вами растрового изображения, в то время как вы несете ответственность за уничтожение этой копии!

Обязательное чтение OldNewThing :

" Когда статический контроль автоматически удалит загруженное в него изображение и когда это является ответственностью приложения? "

¹ ) Подробнееточно: при использовании версии 6 общих элементов управления, что в наши дни делают почти все приложения.

0 голосов
/ 24 июня 2019

Боюсь, следующий ответ - лучший ответ, который я получу.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);
}

enter image description here

0 голосов
/ 24 июня 2019

Это не проблема PNG, это проблема глубины цвета.

Согласно вашему коду, я преобразовал 8-битное изображение PNG в 8-битное изображение BMP с помощью инструмента преобразования формата, и изображение все еще показываетцвет фона.

Итак, я сохранил 8-битное изображение PNG в 32-битное изображение PNG, и все в порядке.1

2

Почему это так?

Ответ: 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 и его легко идентифицировать, потому что нет замирания, нет частично прозрачных пикселей, а края часто описываются как «жесткие» или «пиксельные».

3

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...