[ contenidos *| #winprog ]

Bitmaps Transparentes

Ejemplo: bmp_two

[images/bmp_two.gif]

Transparencia

Darles a los bitmaps la apariencia de tener secciones transparentes es bastante simple e involucra el uso de una M�scara blanca y negra junto al color de la imagen que queremos que luzca transparente.

Para que el efecto funcione apropiadamente se necesitan reunir las siguientes condiciones: Primero la imagen debe ser de color negro en todas las �reas que queremos que luzca transparente. Segundo, La m�scara debe ser blanca en las �reas que queremos que sean transparentes y negro en caso contrario. La im�gen y la m�scara son mostradas en la figura inclu�da como ejemplo en esta secci�n y corresponden a las dos im�genes que se encuentran alineadas mas a la izquierda.

Operaciones BitBlt

�Como hace esto para proveernos transparencia? Primero usamos BitBlt( ) sobre la m�scara usando la operaci�n SRCAND como �ltimo par�metro y luego encima de esto usamos BitBlt( ) sobre la imagen usando la operacion SRCPAINT. El resultado es que las �reas que queremos que sean transparentes no cambian en el HDC destino mientras el resto de la imagen es dibujada de forma usual.

    SelectObject(hdcMem, g_hbmMask);
    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND);

    SelectObject(hdcMem, g_hbmBall);
    BitBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);

Bastante simple eh? Afortunadamente lo es, pero resta una pregunta... �de donde viene la m�scara? Hay b�sicamente dos formas de obtener la m�scara...

Debido a que la primer opci�n no es nada nuevo, deber�as ser capaz de hacerlo si lo deseas. La segunda forma proviene de aplicar algunos malabares usando BitBlt( ) y por lo tanto voy a mostrar una forma de realizarla.

Como crear una M�scara

La forma m�s simple de hacerlo es recorrer cada pixel en la imagen, chequear su valor y luego poner en el correspondiente pixel de la m�scara el color blanco o negro. Si usamos BitBlt( ) (usando SRCCOPY) desde un HDC que referencia una imagen a color dentro de un HDC que referencia una imagen blanco y negro, �ste chequear� que color est� especificado como color de fondo en la imagen a color y pondr� todos aquellos pixeles en Blanco, culaquier otro pixel que no sea del color del fondo terminar� siendo Negro.

Esto funciona perfectamente, debido a que todos lo que necesitamos hacer es poner el color de fondo con el color que queremos transparentar y hacemos BitBlt( ) desde nuestra imagen a la m�scara. Observa que esto solo funciona con una m�scara monocromo (blanco y negro)... esto es, bitmaps que solo tienen 1 bit per pixel para definir el color. Si intentamos esto con una imagen a color que s�lo tiene pixeles blanco y negro, pero que el tama�o para definir el color por pixel es mayor que un bit, digamos 16 bits o 24 bits, esto no funciona.

�Receuerdas la primer condici�n anterior para obtener m�scaras exitosas? Esta dec�a que nuestra imagen necesitaba ser negra donde quer�amos que fuera transparente. Debido a que el bitmap que he usado en este ejemplo ya cumple esa condici�n, no necesita ning�n tratamiento especial. Pero si deseas usar este c�digo en otra imagen que tiene un color diferente para transparentar (rosa es una elecci�n com�n), entonces necesitamos tomar un segundo paso, esto es, usar la m�scara que acabamos de crear para alterar la imagen original, donde todo lo que queremos transparentar es negro. No hay problema si otros lugares de la imagen tambi�n son de color negro, debido a que esos lugares no son blancos en la m�scara y por lo tanto no ser�n tranparentes. Podemos realizar esto utilizando BitBlt( ) desde la nueva m�scara a la imagen original, usando la operaci�n SCRINVERT, la cual fija todas la �reas que son blancas en la m�scara a negras en la imagen a color.

Esta es una parte de un proceso complejo, por lo tanto es bueno tener a mano una funci�n muy �til que hace todo esto por nosotros, aqu� est�:

HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
    HDC hdcMem, hdcMem2;
    HBITMAP hbmMask;
    BITMAP bm;

    //Crea una m�scara monocromo (1 bit).	

    GetObject(hbmColour, sizeof(BITMAP), &bm);
    hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);

    //Obtiene algunos HDCs que son compatibles con la driver de video	

    hdcMem = CreateCompatibleDC(0);
    hdcMem2 = CreateCompatibleDC(0);

    SelectBitmap(hdcMem, hbmColour);
    SelectBitmap(hdcMem2, hbmMask);

    //Ponemos el color de fondo de la imagen a color con el color que
    //queremos que sea transparente	    

    SetBkColor(hdcMem, crTransparent);

    //Copiamos los bits de la imagen a color a la m�scara 
    //Blanco y Negro... todo lo que sea del color de fondo termina siendo
    //blanco y todo lo dem�s termina siendo negro... Como quer�amos

    BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);

    //Tomamos la nueva m�scara y la usamos para poner el color transparente
    //en dentro de la imagen a color original, de esta manera el efecto
    //de transparencia funcionar� bien. 

    BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);

    // Liberamos.

    DeleteDC(hdcMem);
    DeleteDC(hdcMem2);

    return hbmMask;
}

NOTA: Esta funci�n llama a SelectObject( ) para seleccionar temporalmente el bitmap a color que le pasamos dentro de un HDC. Un bitmap mo puede seleccionarse en mas de un HDC a la vez, por lo tanto aseg�rate que el bitmap no est� seleccionado por otro HDC cuando llames a esta funci�n, de ser as� la funci�n fallar�.

Ahora que tenemos nuestra super funci�n, podemos crear una m�scara a partir de la imagen original una vez que la hallamos cargado:

    case WM_CREATE:
        g_hbmBall = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BALL));
        if(g_hbmBall == NULL)
            MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK | MB_ICONEXCLAMATION);

        g_hbmMask = CreateBitmapMask(g_hbmBall, RGB(0, 0, 0));
        if(g_hbmMask == NULL)
            MessageBox(hwnd, "Could not create mask!", "Error", MB_OK | MB_ICONEXCLAMATION);
    break;

El segundo par�metro es, por su puesto, el color de la imagen original que queremos que sea transparente, en este caso es negro.

�Como funciona todo esto?

... te estar�s preguntando. Seguramente tu experiencia con C y C++ significa que comprendes operaciones binarias como OR, XOR, AND NOT etc... No voy a explicar este proceso completamente, pero intentar� mostrarte como lo he usado en este ejemplo. Si mi expicaci�n no es lo suficientemente clara, puedes leer algo sobre operaciones binarias, que seguramente te ayudar� a entenderlo. Comprender esto no es determinante para usar esta funci�n y puedes ignorar como funciona confiando siempre en que lo har� correctamente.

SRCAND

La operaci�n SCRAND o ROP (Raster Operation) para BitBlt( ), significa combinar los bits usando AND. Esto es: solo los bits que valen 1 en la imagen fuente Y en la imagen destino quedan el el resultado final. Usamos esto con nuestra m�scara para poner en negro todos los pixeles que tendr�n eventualmente un color de la imagen original. La m�scara tiene el color negro (en binario, todos 0) donde queremos que halla color y blanco (todos 1 en binario) donde queremos que halla trasnsparencia. Cualquier valor combinado con 0, usando AND, es 0 y por lo tanto todos los pixeles que son negros en la m�scara son puesto en 0 en el resultado final. Cualquier valor que es combinado con 1 usando AND permanece inalterable, por lo tanto si val�a 1 al comienzo, finaliza valiendo 1 y si val�a 0 finaliza valiendo 0... Por lo tanto todos los pixeles que eran blanco en nuestra m�scara, no alteran su valor despu�s de la llamada a BitBlt(). El resultado es el del la imagen de la esquina derecha del ejemplo.

SCRPAINT

SCRPAINT usa la operaci�n OR, por lo tanto si al menos uno de los 2 bits vale 1, entonces el resultado valdr� 1. Usamos esto en la imagen a color. Cuando la parte negra (transparente) de nuestra imagen a color es combinada con los datos en el desitno usando OR, el resultado es que los datos permanecen inalterados, debido a que cualquier valor combinado con 0 usando la operaci�n OR permanece igual.

Sin embargo, el resto de la imagen a color no es negro, y el si destino tampoco es negro, entonces tenemos una combinaci�n de los colores fuente y destino. El resultado puede verse en la segunda bola de la segunda fila de la figura ejemplo. Esta es la raz�n por la cual primero usamos la m�scara para poner en negro los pixeles que queremos que tengan color, ya que cuando usamos OR con la imagen a color, los pixeles coloreados no se mezclan con lo que haya dentro de ellos.

SCRINVERT

Esta es la operaci�n XOR usada para poner en negro el color transparente en nuestra imagen original (Si no ya no es negro). Combinando un pixel negro de la m�scara con un pixel que no sea del color de fondo en el destino lo deja inalterable, mientras que al combinar un pixel blanco de la m�scara (que generamos definiendo un color particular como "fondo") con el color de un pixel del fondo del destino, esto hace que se cancele y se ponga en negro.

Ejemplo 2

El c�digo ejemplo en el proyecto bmp_two que viene junto con esta secci�n contiene el c�digo para la imagen del ejemplo de esta secci�n. Este consiste en primero dibujar la m�scara y la imagen a color exactamente como son usando SRCCOPY, luego usa en cada una por separado las operaciones SRCAND y SRCPAINT repectivamente y finalmente las combina para producir el resultado final.

El fondo en este ejemplo es gris para hacer mas obvia la transparencia ya que usar estas operaciones sobre un fondo blanco o negro hace dificil darse cuenta si est� funcionando o no.


Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.

Versi�n en espa�ol: Federico Pizarro - 2003