|
Formas no rectangulares - Parte II
Copyright © 2001 Alirio A. Gavidia B.. Para ver más de
mis artículos visite Programación Orientada en Delphi
Introducción
Esta es la segunda parte de un artículo orientado a la utilización de
formas no rectangulares con Delphi bajo Windows. En
la parte anterior se
le dio forma no rectangular a un formulario creando un reloj de agujas
como ejemplo. En esta parte se pretende poder crear formularios cuyas
formas se definan desde algún archivo externo a la aplicación. Tal
comportamiento corresponde al muy difundido uso de "Skins". Como ejemplo
"Windows Media Player".
Implantación básica de "skins"
La primera aproximación que aquí presentamos no sólo nos permite definir
el fondo sino además la figura del formulario. En el mercado existe un
componente VCL que hace este trabajo y más. El referido componente VCL
hace uso de dos archivos BMP, el primero con la figura en blanco y negro
y el segundo con el fondo a todo color. Para el presente ensayo se
combinan estas dos características en un sólo archivo, simplemente
tomando el color del primer pixel como color transparente, en realidad
como color que permite definir el límite de la figura (hay una pequeña
diferencia). El resto del dibujo, dentro de la figura, se toma como
fondo del formulario.
Nota: El uso de BMP tiene muchos contras. Los principales: tamaño y
escalabilidad. Del primero quizás la alternativa sería un JPG, pero
el proceso de compresión altera la frontera de la figura generando
colores intermedios y la consecuencia es un frontera difuminada. Por
otro lado están los GIF, pero han sido desterrados de mis rutinas,
librerías y funciones debido a que cierta empresa pretende ahora
cobrar derechos por el uso de los algoritmos de compresión presentes
en los archivos tipo GIF. En cuanto a la escalabilidad, una alternativa
es el uso de Metafile, mejor aún sospecho que los metafiles son
definiciones de regiones completamente análogas a las que se utilizan
aquí. Sin embargo dejaré el estudio de Metafiles para otra ocasión.
Una versión completa de un "Skinner" requiere poder definir el dibujo de
fondo del formulario, su figura así como el de cada control presente.
Nos concentraremos en el formulario dejando de lado los controles.
El fondo
En problema del dibujo de fondo es enfrentable de dos maneras a mi
entender. Una es interceptando algún mensaje de Windows para dibujar
sobre el canvas de fondo (como lo hacen algunos controles VCL para
colocar fondos en formularios MDI y No-MDI) y la otra es colocando un
control tipo TImage como el último control en el área cliente del
formulario. Optaré por la última forma porque además será posible hacer
uso de algunos eventos del TImage.
La forma
El problema de la figura reside básicamente en crear un región de
determinada forma. La forma determinante que he asumido es dada a través
de un bitmap. Así que por ejemplo un bitmap consistente en un círculo
azul en un fondo blanco debe generarme un formulario visible como un
círculo. La figura puede ser casi cualquier cosa como el rostro de
Mickey Mouse o la silueta de una araña. Por ello se leerá el bitmap para
generar a partir de él una forma poligonal, eso nos da libertad, pero
elimina en mucho las posibilidades de escalamiento.
Paso a paso
En principio el algoritmo va así:
Se ajusta el formulario a las dimensiones del archivo BMP. Esto es
simple. Para ello bastan las siguientes líneas de código:
// Ajustar el área / to Ajust the area
ClientWidth := Image1.Width;
ClientHeight:= Image1.Height;
Se lee el primer punto del formulario y se toma como "transparente".
Se busca el primer punto no "transparente" y a partir de allí se
genera un polígono con la silueta de la parte no "transparente"
(ver GenRegion en el archivo ffigura.pas).
Eliminamos la barra de título (si aún nos estorba). Para ello:
BorderStyle := bsNone; // Titulo estorba / no Title
Cambiamos la figura del formulario conforme al polígono. Para ello
hacemos uso ahora de la
rutina CreatePolygonRgn y SetWindowRgn
(ver ActiveSkin en ffigura.pas). A diferencia del ejemplo
de la primera parte de este artículo, ya no definimos una región elíptica
(circular).
Establecemos desde el TImage los eventos necesarios para el arrastre
de formularios como se hizo en el ejemplo del primer artículo.
Excepto por el tercer punto nada parece en extremo difícil. Para generar
la región poligonal sólo se busca la frontera de la figura, luego se
avanza por toda la orilla de la figura hasta estar en el punto inicial
(o acabar con la memoria, en este caso 1024 puntos). Esta serie de puntos
definen un polígono para generar un región con la
función CreatePolygonRgn. Luego se
utiliza SetWindowRgn y ya se tiene el
formulario con la silueta dada. Funciona pero tiene limitaciones:
nuestra figura debe tener una sola frontera, por ello no puede ser una
figura con un agujero en el medio (en todo caso el algoritmo ignorará
el agujero en el medio).
Como de costumbre este artículo va acompañado con un ejemplo para
ilustrar los puntos aquí referidos.
Nota: El algoritmo que sigue la silueta dada en el bitmap es
relativamente lento y tosco, sin embargo es mantenible y realiza una
cierta optimización con figuras no muy complejas. Hay un límite dado
que se genera un arreglo en memoria cuando aún no se conoce cuantos
puntos definirán la figura. Queda al usuario el variar este límite a
su conveniencia.
Los controles
El ejemplo adjunto a este artículo contiene además botones con forma. En
modo de diseño son cuadrados pues son de tipo TBitBtn, pero en
el evento OnCreate del formulario se le aplica a cada botón la
forma del gráfico que contiene, de la misma manera que usamos para el
formulario en el procedimiento ActiveSkin:
procedure TForm1.FormCreate(Sender: TObject);
Var
hRegion : Thandle;
Par : Array [0..1024] of TPoint; // Data -> region
Cnt : Integer;
begin
Cnt := GenRegion(Par, BitBtn1.Glyph,
(BitBtn1.Width - BitBtn1.Glyph.Width+1) div 2,
(BitBtn1.Height - BitBtn1.Glyph.Height+1) div 2);
hRegion := CreatePolygonRgn(Par, Cnt, ALTERNATE);
SetWindowRgn(BitBtn1.handle, hRegion, true);
...
Esto demuestra que la definición de regiones funciona más allá de las
ventanas.
Más allá
El problema del tamaño del BMP sigue latente, pero prodríamos empaquetar
el BMP y usarlo empaquetado como hace Winamp. Para este enfoque hay dos
vías: podríamos usar algún algoritmo casi propietario semi-secreto o
poco común (inevitablemente pienso en el Windows Media Player) o algo
como el Zip. Esta última opción tiene la ventaja de que no estamos
restringidos en cuanto a su uso. Pueden encontrar información para saber
como manejarla en el sitio web de Latium Software:
Si las imágenes son simples, como las que usé en el ejemplo, puede
guardarlas usando 16 colores con compresión RLE 4 (esto puede hacerse
por ejemplo con el viejo y querido Borland Resource Workshop que viene
en el CD de instalación de Delphi).
Lo que otros hacen
Deberíamos separar las áreas del formulario en cliente y no cliente, así
como considerar los captions. Eventualmente podríamos usar semi-transparencias,
lo que será cada vez más común (gracias a Steve Jobs por los
favores recibidos, aunque no utilicemos todos una Mac ;)
Por allí hay una librería que considera sombras, animación, morphing y
hasta un lenguaje de scripts, pero van más allá del objetivo de este
artículo.
Los fuentes de este artículo están a su disposición
para descargar.
|