2008-11-20 25 views
8

Necesito crear un control personalizado para mostrar imágenes bmp con canal alfa. El fondo se puede pintar en diferentes colores y las imágenes tienen sombras, así que necesito "pintar" realmente el canal alfa.Cómo dibujar mapas de bits de canal alfa de 32 bits?

¿Alguien sabe cómo hacerlo?

También quiero, si es posible, crear una máscara utilizando la información del canal alfa para saber si el mouse ha hecho clic en la imagen o en el área transparente.

¡Cualquier tipo de ayuda será apreciada!

Gracias.

Editado (JDePedro): Como algunos de ustedes han sugerido, he intentado usar la mezcla alfa para pintar el mapa de bits con el canal alfa. Esto sólo una prueba he implementado en el que se carga un mapa de bits de 32 bits de recursos y trato de pintar usando la función 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(); 
} 

Esto es sólo una prueba de lo que poner el código en el OnPaint de la diálogo (también probé la función AlphaBlend del objeto CDC).

Las áreas no transparentes se están pintando correctamente, pero me pongo blanco donde el mapa de bits debe ser transparente.

¿Alguna ayuda?

Ésta es una screenshot..it no es fácil de ver, pero hay un rectángulo blanco alrededor del círculo azul: alt text http://img385.imageshack.us/img385/7965/alphamh8.png

Ok. ¡Entiendo! Tengo que multiplicar previamente cada píxel por el valor alfa. Alguien puede sugerir la forma optimizada para hacer eso?

Respuesta

5

La forma en que generalmente hago esto es a través de una DIBSection, un mapa de bits independiente del dispositivo que puede modificar los píxeles directamente. Lamentablemente, no hay compatibilidad con MFC para DIBSections: debe usar la función CreateDIBSection() de Win32 para usarla.

Comience por cargar el mapa de bits como RGBA de 32 bits (es decir, cuatro bytes por píxel: uno rojo, uno verde, uno azul y uno para el canal alfa). En el control, crea una DIBSección de tamaño adecuado. A continuación, en la pintura de rutina

  • Copiar los datos de mapa de bits en datos de mapa de bits de la DIBSection, utilizando el byte de canal alfa para mezclar la imagen de mapa de bits con el color de fondo.
  • Crea un contexto de dispositivo y selecciona la sección DIB en él.
  • Utilice BitBlt() para copiar desde el nuevo contexto del dispositivo al contexto del dispositivo de pintura.

Puede crear una máscara con los datos brutos del mapa de bits simplemente mirando los valores del canal alfa. No estoy seguro de lo que está preguntando aquí.

1

Tiene que hacer un alpha blend con el color de fondo, luego sacar el canal alfa para pintarlo al control.

El canal alfa debe estar a cada 4to byte de su imagen. Puede usarlo directamente para su máscara, o simplemente puede copiar cada 4to byte a una nueva imagen de máscara.

+0

Hola Marcos, vamos a ver si me entiende tu sugerencia Imagine que obtengo mi control de CStatic/CWnd y agrego un CBitmap como miembro. Entonces, cada vez que se va a pintar el control (en el método OnPaint), ¿tengo que obtener el fondo y mezclarlo con mi imagen? –

+0

(continuar) ¿Estoy en lo cierto? ¿Tengo que hacer todo por mi cuenta? Parece que puede tener un alto costo de rendimiento ... ¿podría (alguien) decirme la mejor manera (más óptima) para hacerlo? Tal vez algunos artículos godd o incluso codesnippet? Gracias. –

1

Pintar es muy fácil con el AlphaBlend function.

En cuanto a enmascarar, que necesitará para get the bits del mapa de bits y examinar el byte de canal alfa para cada píxel que le interesa.

1

una forma optimizada para pre-multiplicar los canales RGB con la alfa canal es configurar una matriz [256] [256] que contiene los resultados de multiplicación calculados. La primera dimensión es el valor alfa, la segunda es el valor R/G/B, los valores en la matriz son los valores pre-multiplicados que necesita.

Con esta matriz ha configurado correctamente, se puede calcular el valor que necesita de esta manera:

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

Está en el buen camino, pero debe arreglar dos cosas.

Primer uso :: LoadImage (.. LR_CREATEDIBSECTION ..) en lugar de CBitmap :: LoadBitmap. Dos, debe "pre-multiplicar" los valores RGB de cada píxel en un mapa de bits a su valor A respectivo. Este es un requisito de la función AlphaBlend, consulte la descripción de AlphaFormat en this MSDN page. T

El lpng tiene un código de trabajo que realiza la premultiplicación de los datos DIB.

7

Para futuros usuarios de Google, aquí hay una función de pre-multiplicación en funcionamiento. Tenga en cuenta que esto fue tomado desde 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); 
} 

Así entonces su OnPaint se convierte en:

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(); 
} 

Y la carga de la imagen (en el constructor de su control):

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

¿Qué sucede si quiero que la imagen transparente aparezca en una etiqueta estática? pStatic-> ModifyStyle (0xF, SS_BITMAP); pStatic-> SetBitmap (mapa de bits); –

Cuestiones relacionadas