Pergunta

Eu preciso criar um controle personalizado para exibir bmp imagens com alfa canal. O fundo pode ser pintado em cores diferentes e as imagens têm sombras então eu preciso verdadeiramente "pintar" o canal alfa.

Alguém sabe como fazê-lo?

Eu também quero, se possível, para criar uma máscara usando as informações do canal alfa para saber se o rato tem sido, clique na imagem ou na área transparente.

Qualquer tipo de ajuda será apreciada!

Graças.

editada (JDePedro): Como alguns de vocês têm sugerido que eu tenho tentado usar alpha blend para pintar o bitmap com canal alfa. Este apenas um teste Eu tenho implementado onde eu carregar um bitmap de 32 bits a partir de recursos e tento pintá-lo usando a função AlphaBlend:

void CAlphaDlg::OnPaint()
{
    CClientDC dc(this);
    CDC  dcMem;
    dcMem.CreateCompatibleDC(&dc);

    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP);

    BITMAP BitMap;
    bitmap.GetBitmap(&BitMap);
    int nWidth = BitMap.bmWidth;
    int nHeight = BitMap.bmHeight;
    CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);

    BLENDFUNCTION m_bf;
    m_bf.BlendOp = AC_SRC_OVER;
    m_bf.BlendFlags = 0;
    m_bf.SourceConstantAlpha = 255;
    m_bf.AlphaFormat = AC_SRC_ALPHA;
    AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf); 

    dcMem.SelectObject(pOldBitmap);

    CDialog::OnPaint();
}

Este é apenas um teste por isso eu coloquei o código no OnPaint do diálogo (eu também tentei a função AlphaBlend do objeto CDC).

As áreas não transparentes estão sendo pintadas corretamente, mas eu recebo branca onde o bitmap deve ser transparente.

Qualquer ajuda ???

Esta é uma screenshot..it não é fácil de ver, mas há um retângulo branco em torno do círculo azul: alt texto http://img385.imageshack.us/img385/7965/alphamh8.png

Ok. Deixa comigo! Tenho de pré-multiplicar cada pixel para o valor alfa. Alguém pode sugerir a forma otimizada de fazer isso?

Foi útil?

Solução

A forma como eu costumo fazer isso é através de um DIBSection - um bitmap independente dispositivo que você pode modificar os pixels de diretamente. Infelizmente não há nenhum suporte MFC para DIBSections:. Você tem que usar a função CreateDIBSection Win32 () para usá-lo

Começar por carregamento do mapa de bits como de 32 bits rgba (isto é, quatro bytes por pixel: uma vermelha, uma verde, um azul e um para o canal alfa). No controlo, criar um DIBSection dimensionados adequadamente. Então, na rotina de pintura

  • copiar os dados de mapa de bits em dados de mapa de bits da DIBSECTION, utilizando o byte canal alfa para misturar a imagem de bitmap com a cor do fundo.
  • Criar um contexto de dispositivo e selecione o DIBSection para ele.
  • Use BitBlt () para copiar a partir do novo contexto dispositivo para o contexto dispositivo de pintura.

Você pode criar uma máscara com base nos dados de bitmap não processados ??simplesmente por olhar para os valores do canal alfa -. Eu não tenho certeza do que você está pedindo aqui

Outras dicas

Para usuários de futuro do Google, aqui é uma função de trabalho pré-multiplicar. Note-se que esta foi tomada a partir http://www.viksoe.dk/code/alphatut1.htm.

inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
   BITMAP bm = { 0 };
   GetObject(hBmp, sizeof(bm), &bm);
   BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   ::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
   bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
   if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
   LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
   if( pBitData == NULL ) return;
   LPBYTE pData = pBitData;
   ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
   for( int y = 0; y < bm.bmHeight; y++ ) {
      for( int x = 0; x < bm.bmWidth; x++ ) {
         pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
         pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
         pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
         pData += 4;
      }
   }
   ::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
   ::LocalFree(pBitData);
}

Assim, pois, o seu OnPaint torna-se:

void MyButton::OnPaint()
{
    CPaintDC dc(this);

    CRect rect(0, 0, 16, 16);

    static bool pmdone = false;
    if (!pmdone) {
        PremultiplyBitmapAlpha(dc, m_Image);
        pmdone = true;
    }

    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;

    HDC src_dc = m_Image.GetDC();
    ::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
    m_Image.ReleaseDC();
}

E o carregamento da imagem (no construtor do seu controle):

if ((HBITMAP)m_Image == NULL) {
    m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}

Você precisa fazer um alpha blend com a sua cor de fundo, em seguida, retire o alfa canalizar para pintá-lo para o controlo.

O canal alfa deve ser apenas a cada 4 bytes da sua imagem. Você pode usar isso diretamente para a sua máscara, ou você só pode copiar cada 4 bytes para uma nova imagem de máscara.

Pintura é muito fácil com a função AlphaBlend .

Quanto a você máscara, você precisa obter os bits do bitmap e examinar o byte canal alfa para cada pixel você está interessado.

Uma forma optimizada para pré-multiplicam os canais RGB com o canal alfa é a criação de uma matriz [256] [256] contendo os calculados multiplicação resultados. A primeira dimensão é o valor de alfa, o segundo é o R / G valor / B, os valores na matriz são os valores pré-multiplicado precisas.

Com essa matriz configurado corretamente, você pode calcular o valor que você precisa como esta:

R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];

Você está no caminho certo, mas a necessidade de corrigir duas coisas.

Primeiro uso :: LoadImage (.. LR_CREATEDIBSECTION ..) em vez de CBitmap :: LoadBitmap. Dois, você tem que "pré-multiplicam" valores RGB de cada pixel em um bitmap de seu respectivo valor A. Esta é uma exigência da função AlphaBlend, ver descrição AlphaFormat em desta página MSDN . T

O lprng tem um código de trabalho que faz a premultiplication dos dados DIB.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top