Pregunta

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 realmente necesito "pintar". el canal alfa.

¿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 se ha hecho clic con el mouse en la imagen o en el área transparente.

¡Cualquier tipo de ayuda será apreciada!

Gracias.

Editado (JDePedro): como algunos de ustedes han sugerido, he estado tratando de usar la mezcla alfa para pintar el mapa de bits con el canal alfa. Esta es solo una prueba que he implementado donde cargo un mapa de bits de 32 bits de los recursos y trato de pintarlo 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 solo una prueba, así que puse el código en OnPaint del cuadro de diálogo (también probé la función AlphaBlend del objeto CDC).

Las áreas no transparentes se pintan correctamente, pero obtengo un color blanco donde el mapa de bits debe ser transparente.

¿Alguna ayuda ???

Esta es una captura de pantalla ... no es fácil de ver, pero hay un rectángulo blanco alrededor del círculo azul: texto alternativo http://img385.imageshack.us/img385/7965/alphamh8.png

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

¿Fue útil?

Solución

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. Desafortunadamente no hay ningún soporte MFC para DIBSections: debe usar la función Win32 CreateDIBSection () para usarlo.

Comience cargando 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, cree una sección DIB de tamaño adecuado. Luego, en la rutina de pintura

  • Copie los datos del mapa de bits en los datos del mapa de bits de la sección DIB, utilizando el byte del canal alfa para combinar la imagen del mapa de bits con el color de fondo.
  • Cree un contexto de dispositivo y seleccione la Sección DIB en él.
  • Use BitBlt () para copiar desde el nuevo contexto del dispositivo al contexto del dispositivo de pintura.

Puede crear una máscara dados los datos de mapa de bits sin procesar simplemente mirando los valores del canal alfa; no estoy seguro de lo que está preguntando aquí.

Otros consejos

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

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

Debe hacer una mezcla alfabética con su color de fondo, luego saque el alfa canal para pintarlo al control.

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

Pintar es muy fácil con la función AlphaBlend .

En cuanto a su máscara, deberá obtenga los bits del mapa de bits y examine el byte del canal alfa para cada píxel que le interese.

Una forma optimizada de multiplicar previamente los canales RGB con el canal alfa es configurar una matriz [256] [256] que contenga 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 multiplicados previamente que necesita.

Con esta matriz configurada correctamente, puede calcular el valor que necesita de esta manera:

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

Estás en el camino correcto, pero necesitas arreglar dos cosas.

Primer uso :: LoadImage (.. LR_CREATEDIBSECTION ..) en lugar de CBitmap :: LoadBitmap. Dos, tienes que "pre-multiplicar" 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 esta página de MSDN . T

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top