7A69 Issue 15 - Layers en Windows
|-----------------------------------------------------------------------------|
[ 7a69#15 ] [ 23-11-2003 ]
\ /
\-------------------------------------------------------------------------/
{ 7 - Layers en Windows. }{ Marconi }
|-----------------------------------------------------------------------|
/************************************************************************/
/* Layers. una introduccion */
/* */
/* Ventanas irregulares/transparentes */
/* Windows 2000/XP/ME? */
/* */
/* Nivel: Basico */
/* */
/* Marconi */
/* inocraM@yahoo.com */
/* http://mmwin32asm.es.fm */
/* */
/* Canal #asm del irc-hispano: http://kickme.to/canalasm */
/* */
/* Domingo, 9 de Marzo del 2003 */
/* */
/* Saludos y gracias a FiddLE por su colaboracion */
/* */
/************************************************************************/
Hur hur!!
1.- Ventanas irregulares. Introduccion
Ya hace tiempo escribi un peque§o articulo sobre como hacer ventanas de formas irregulares. Windows y su sistema de regiones permite entre otras cosas indicar que partes de una ventana son visibles y que partes no lo son, y de esta manera hacer que la ventana tenga una forma diferente a la rectangular clasica. Se pueden crear regiones de formas rectangulares, poligonales o elipticas con funciones como CreateRectRgn, CreateEllipticRgn, etc. que pueden ser despues combinadas con la funcion CombineRgn. La funcion SetWindowRgn indica cual es la region de una ventana dada, o lo que es lo mismo, indica cual es su area, con lo que podemos es posible hacer ventanas de diversas formas.
Si bien con estas tenemos la capacidad de conseguir nuestro objetivo, es decir, el poder recortar el borde de nuestras ventanas a nuestro antojo, es tambien cierto que las facilidades son pocas.
La funcion clave para nuestro proposito seria ExtCreateRegion, que usa una estructura RGNDATA para guardar los datos con los que se construira la region. Esos datos no son mas que un array de rectangulos. Lo interesante de esta opcion es la existencia de programas como pueden ser el RGN Generator de Richard de Oude. Este programa generaba esa estructura a partir de una imagen haciendo que los puntos color especificado no formasen parte de la region.
Un articulo sobre todo esto escrito por mi se puede leer en: http://usuarios.lycos.es/mmwin32asm/nproyectos/cdplayer/norect.htm
Ahora bien, en windows 2000, windows XP y windows ME ( en ME no estoy seguro al 100%, no conozco a nadie que use ME y no pude comprobarlo) contamos con una segunda posibilidad que nos permite hacer todo lo anterior y algunas cosas mas: me refiero al nuevo estilo extendido WS_EX_LAYERED y a las ventanas que lo tienen. En las ventanas que poseen este estilo se puede fijar un grado de transparencia para dicha ventana, grado que varia entre 0 y 255, y ademas se puede escoger un color como color transparente.
Como ventajas de este segundo metodo sobre el primero podemos citar por un lado que no come tantos recursos, siendo este segundo sistema mas rapido y eficiente. Ademas con este sistema no necesitamos de ninguna estructura extra pues nos basta con especificar un color para que este se vuelva transparente. Y por ultimo, contamos con la posibilidad de hacer ventanas semitransparentes, cosa que no era posible en win9X (al menos no a priori). La principal desventaja de este metodo es que los programas que hagan uso de el no funcionaran en varias versiones de windows, lo cual no es en absoluto deseable :(
2.- Layers... Funciones
Este tuto, como reza el titulo pretende ser una peque§a introduccion a este segundo metodo. En realidad tampoco hay mucho que contar, puesto que se trata de unas pocas funciones y constantes.
Como he dicho en la introduccion, este sistema permite a una ventana indicar que color se identiricara con el color transparene y/o el grado de transparencia de la ventana. Las partes transparentes de la ventana no capturan los clicks del raton, con lo que a todos los efectos es como si esos puntos no perteneciesen a la ventana.
Para que a una ventana se le puedan hacer todas estas cosas tiene que tener el estilo extendido WS_EX_LAYERED, constante que se define como:
WS_EX_LAYERED equ 80000h
Este estilo se puede indicar tanto al crear la ventana en cuestion como a a§adirlo a posteriori si lo necesitamos. Tendriamos las opciones de:
invoke CreateWindowEx, WS_EX_LAYERED <or ... >, ...
o bien, dado un handle hWnd de una cierta ventana:
invoke GetWindowLong, hWnd, GWL_EXSTYLE
or eax, WS_EX_LAYERED
invoke SetWindowLong, hWnd, GWL_EXSTYLE, eax
Una vez que la ventana tiene ese estilo disponemos de una serie de funciones relacionadas con este estilo.
BOOL SetLayeredWindowAttributes {
HWND hWnd
COLORREF crKey
BYTE bAlpha
DWORD dwFlags
};
hWnd: Handle de la ventana, que debe tener el estilo WS_EX_LAYERED
crKey: En caso de que se quiera idicar un color como transparente se debe rellenar este argumetno con el valor de tipo COLORREF que identica dicho color. Este valor del tipo COLORREF se obtiene normalmente usando la macro RGB, y da lugar a un valor de la forma 00bbggrrh donde rr indica la componente roja, gg la verde y bb la azul.
bAlpha: Indica la opacidad/transparencia de la ventana. Un valor 0 haria la ventana totalmente transparente y un valor 0ffh la haria totalmente opaca.
dwFlags: Indican cual es la accion a realizar. Puede ser cualquier combinacion de los valores siguientes:
- LWA_COLORKEY: Indica que lo que queremos hacer es indicar que color ha de ser el transparente
- LWA_ALPHA: Indica que lo que queremos hacer es indicar la transparencia/opacidad de la ventana.
Esas constantes estan definidas con los siguientes valores:
LWA_COLORKEY equ 1
LWA_ALPHA equ 2
El valor devuelto por esta funcion es diferente de 0 si la operacion finalizo satisfactoriamente y 0 en otro caso.
El parametro de transparencia lo que hace es imitar el parametro BLENDFUNCTION de la funcion AlphaBlend. Esta funcion se usa para mezlcar informacion de dos bitmaps.La estructura BLENDFUNCTION es mas versatil que el simple parametro con el que contamos en el caso de la funcion SetLayeredWindowAttributes.
La otra funcion que hay que nombrar a este respecto es:
BOOL UpdateLayeredWindow {
HWND hwnd,
HDC hdcDst,
POINT* pptDst,
SIZE* psize,
HDC hdcSrc,
POINT* pptSrc,
COLORREF crKey,
BLENDFUNCTION* pblend,
DWORD dwFlags
};
Esta funcion actualiza la posicion, tama§o, bode, contenido y traslucencia de una vantana que tenga el estilo WS_EX_LAYERED.
hwnd: Handle de la ventana en cuestion
hdcDst: Handle del contexto de dispositivo para la pantalla, y es obtenido cuando se llama a la funcion especificando este parametro como NULL. Es usado para la adaptacion de la paleta de colores cuando los contenidos de la ventana es actualizado. cuando este parametro es NULL se usa la paleta por defecto, y ademas si hdcSrc es NULL, este parametro tambien debe serlo.
ppDst: Puntero a una estructura POINT que especifica la nueva posicion de la ventana. Debe ser NULL si la posicion no varia.
psize: Puntero a una estructura de tipo SIZE que indica el nuevo tamaño de la ventana. Si este tamaño no varia, debe ser null.
hdcSrc: Handle del dispositivo de contexto que define la ventana. Si la forma y aspecto de la ventana no esta cambiando, hdcSrc puede ser NULL.
pptSrc: Puntero a una estructura de tipo POINT que especifica la posicion de la layer en el contexto de dispositivo. Si hdcSrc es NULL este parametro debe ser NULL.
crKey: Valor COLORREF usado cuando se esta componiendo la ventana.
pblend: Puntero a una estructura BLENDFUNCTION que especifica el valor de transparencia de la ventana.
dwflags: Este parametro puede ser uno de los siguientes valores
- ULW_ALPHA: Usar pblend como funcion de mezclado.
- ULW_COLORKEY: Usar crKey como color de transparencia
- ULW_OPAQUE: Dibujar una ventana opaca
Esta funcion delvuelve 0 si se produjo algun error y distinto de 0 en otro caso.
La estructura BLENDFUNCTION esta definida de la siguiente forma:
typedef struct _BLENDFUNCTION {
BYTE BlendOp;
BYTE BlendFlags;
BYTE SourceConstantAlpha;
BYTE AlphaFormat;
}BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;
A pesar de parecer algo complejo, puesto que pasamos de tener un solo numero a toda esta estructura para definir nuestro grado de transparencia, lo cierto es que tal complejidad no lo es. El unico valor establecido para BlendOp es AC_SRC_OVER, y para AlphaFormat es AC_SRC_OVER, ademas de que BlendFlags debe ser 0. Asi pues tenemos dos valores que funcionarian como booleanos ( uno o es AC_SRC_OVER o cualquier otro valor, otro que es AC_SRC_ALPHA o es cualquier otro valor),tenemos un valor que debe ser 0 y tenemos nuestro valor alpha, SourceConstantAlpha, SCA en lo sucesivo. El parametro AC_SRC_ALPHA indica que el bitmap orgien (la funcion AlphaBlend asi como la estructura BLENDFUNCTION esta pensada para la mezcla de bitmaps) tiene un canal alfa ( un canal alfa no es mas que un canal reservado para aplicaciones que procesan imagenes, proporcionando informacion adicional, como puede ser transparencia. Se trata en nuestro caso de un canal que proporciona un valor alpha de transparencia por pixel). La constante AC_SRC_OVER indica que el bitmpa fuente se debe situar sobre el bitmap destino. Cuando el fuente no tiene un canal alfa, entonces la operacion que se lleva a cabo es:
- Dst.Red = Src.Red * (SCA/255.0) + Dst.Red * (1.0 - (SCA/255.0))
- Dst.Green = Src.Green * (SCA/255.0) + Dst.Green * (1.0 - (SCA/255.0))
- Dst.Blue = Src.Blue * (SCA/255.0) + Dst.Blue * (1.0 - (SCA/255.0))
y en cambio cuando tiene canal alfa nos encontramos con:
- Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0))
Y una vez explicado todo esto, las constantes que aqui he mencinado tienen los siguientes valores:
ULW_ALPHA equ LWA_ALPHA
ULW_COLORKEY equ LWA_COLORKEY
ULW_OPAQUE equ 4
AC_SRC_OVER equ 0
AC_SRC_ALPHA equ 1
Esta funcion siempre repinta la ventana completa, y esta indicada para hacer animaciones,cambios de tamaño y cosas por el estilo. En otro caso el mensaje WM_PAINT funcionara perfectamente.
Y por ultimo contamos con una funcion que en vez de servirnos para modificar los distintos valores, nos permite comprobar su valor:
BOOL GetLayeredWindowAttributes {
HWND hwnd,
COLORREF* pcrKey,
BYTE* pbAlpha,
DWORD* pdwFlags
};
Como es obvio, dado el handle de la ventana la funcion rellena el resto de campos para que nosotros posteriormente podamos revisar su valor. La funcion devuelve un valor distinto de 0 si todo fue bien y cero en caso contrario.
Sobre estas funciones solo resta decir que se encuentran en la libreria user32.dll y no estan disponibles en windows 95, 98 ni nt.
Ademas de lo expuesto hay otras funciones que aunque no estan relacionadas directamente con esto son importantes como puede ser el caso de SetLayout. Esta funcion especifica el orden en el que texto y graficos son expuestos, y puede es interesante en el caso de querer invertir imagenes, hacer reflejos, etc.
3.- Ejemplos...
Como habeis visto solo se tratan de unas pocas constantes y estructuras y tres funciones. Lo cierto es que con tan poco, incluso con mucho menos, tenemos la oportunidad de hacer algo que a priori parece complicado. En los dos ejemplos que he hecho, podemos ver como con el solo uso del estilo del que estamos hablando y con el uso de la funcion SetLayeredWindowAttributes podemos hacer las ventanas irregulares y/o transparentes a nuestro antojo.
Ejemplo 1.- En este primer ejemplo solo se depliega una ventana irregular sobre la pantalla. Ademas, con ayuda de un timer y una variable auxiliar hacemos que la ventana aparezca con un fundido. Nuestra ventana esta basada en un bitmap, fondo.bmp, y el que sera color transparente esta marcado con un color rosado. Los tonos rosados del borde de la ventana no se debe a ningun malfuncionamiento, sino a que no son exactamente el mismo tono de rosa que la parte que es invisible ( photoshop sucks)
Primero tenemos que conseguir la direccion de la funcion, eso lo haremos de la siguienet forma:
invoke GetModuleHandle, addr userdll
mov huser, eax
invoke GetProcAddress, huser, addr Func
mov SetLayeredWindowAtts, eax
En este trozo de codigo userdll apunta a la cadena "User32.dll" y Func a "SetLayeredWinowAttributes", con lo que al final tendremos en nuestra variable SetLayeredWindowAtts la direccion de la funcion que nos interesa.
En lo que se refiere al uso de los recursos que nos competen, tenemos por un lado que al crear la ventana indicamos el estilo extendido:
invoke CreateWindowEx, WS_EX_LAYERED, ADDR NombreClase....
y despues en el codigo del WM_TIMER nos encontramos con el resto del codigo que nos interesa:
add transparencia, 15
invoke GetDC, hWnd
push eax
invoke GetPixel, eax, 0, 0
push LWA_ALPHA or LWA_COLORKEY
push transparencia
push eax
push hWnd
call [SetLayeredWindowAtts]
call ReleaseDC
cmp transparencia, 255
jne finfuncion
invoke KillTimer, hWnd, TimerId
Aqui vemos como primero conseguimos el valor del color transparente, color que es el mismo que el de la posicion (0,0) del bitmap que estamos usando como fondo de pantalla. Desupes llamamos a SetLayeredWindowAttributes usando ese color como color tansparente y dandole un valor idnicado por la variable transparencia de transparencia para el conjunto de la ventana. Si hemos llegado a 255 ( 0ffh == opacidad total) es que hemos acabado con el efecto de fundido por lo que acabamos con el timer.
Ejemplo2.- Este ejemplo es un pequeño programa que tan solo sirve para hacer semitransparente la ventana que le indiquemos. Para ello se vale de la funcion SetLayeredWindowAttributes, igual que el primer ejemplo. En este caso el nivel de transparencia se obtiene de un slider que tiene la ventana, y un static subclasificado nos devuelve el handle de la ventana a transparentar.
Primero tenemos que darle a la ventana en cuestion el nuevo estilo:
invoke GetWindowLong, hWnd, GWL_EXSTYLE
test eax, eax
je fin
or eax, WS_EX_LAYERED
invoke SetWindowLong, hWnd, GWL_EXSTYLE, eax
test eax, eax
je fin
y posteriormente hacemos transparente la ventana:
push LWA_ALPHA
push pt.x
push 0
push hWnd
call [SetLayeredWindowAtts]
4.- Saludos y despedida
Con esto llego al final de este pequeño tutorial. La verdad es que creo que queda mas que demostrado que el hacer este tipo de ventanas es cosa de niños.
Como siempre digo, sugerencias, comentarios, criticas, aviso de bugs, etc, etc, me podeis mandar un mail: inocraM@yahoo.com
Saludos a la gente de los canales #asm, #win32asm, #ensamblador y #crackers del irc-hispano, asi como a tod@s mis amig@s ( ya saben ellos quienes son).
Y tambien saludos al conjunto del CF de la UJCE, y en especial a Lillo, a quie esta dedicado el segundo ejemplo que acompaña a este cutre-tuto. ( 8.2.6!!!) ;)
Marconi [JCA][ECC] - inocraM@yahoo.com - http://mmwin32asm.es.fm/ lo normal es aburrido
*EOF*