![]() |
![]() |
| Volver al índice | Por Luis Roche
|
En esta unidad crearemos un componente gráfico del tipo que se utiliza como fondo en la instalación de aplicaciones: TGradiente. Pero nuestro componente irá más allá y añadirá nuevas posibilidades al gráfico estandard. Aprenderemos a dibujar sobre el canvas que disponen todos los componentes gráficos. Estudiaremos el método paint y cómo utilizarlo en nuestros componentes.
Nuestro propósito es crear un componente en el que dibujaremos un gradiente de colores similar al que se muestra en la mayoría de los programas de instalación de aplicaciones (y en otro tipo de programas). Entre las características adicionales que nuestro componente debe tener cabe destacar que el gradiente de colores no finalizará en el color negro, sino que será capaz de comenzar y terminar en dos colores cualesquiera. Además, el gradiente podrá ser horizontal o vertical. Por último, el tamaño de nuestro dibujo (es decir, del canvas del componente) será variable a voluntad, para de este modo poder combinar dos o más componentes para crear efectos de colores espectaculares.
Nuestro componente derivará de TGraphicControl ya que necesitamos que disponga de Canvas, pero no es necesario que disponga de manejador (handler). Esta elección la hacemos en base a lo que se vio en la unidad 2.
Tradicionalmente, la programación gráfica en windows se ha realizado mediante el uso del interface gráfico de dispositivo (GDI). Este interface es una herramienta poderosa que permite el dibujo de gráficos mediante el uso de pinceles, brochas, rectángulos, elipses, etc.
Pero el GDI tiene un inconveniente: su programación es muy laboriosa. Cuando se va a hacer uso de una fución GDI, se necesita un handle a un contexto dispositivo, asi como crear y destruir las diversas herramientas de dibujo (recursos) que se utilicen. Pr último, al finalizar, se debe restaurar el contexto de dispositivo a su estado original antes de destruirlo.
Delphi encapsula el GDI, haciendo que no nos tengamos que preocupar de contextos de dispositivo ni de si hemos liberado o no los recursos utilizados. De este modo nos podemos centrar en lo que es nuestro objetivo principal: dibujar los gráficos. De todas formas, si lo deseamos, podemos seguir utilizando las funciones GDI si nos interesa. Así tenemos un doble abanico de posibilidades, Delphi o GDI, que utilizaremos según nos convenga.
Como se ha mencionado en el párrafo anterior, Delphi nos proporciona un completo interface gráfico. El objeto principal de este interface es el objeto Canvas. El objeto Canvas se encarga de tener un contexto de dispositivo válido, así como de liberarlo cuando no lo utilicemos. Además, el objeto Canvas (o simplemente Canvas) dispone de diversas propiedades que representan el lapiz actual (pen), brocha (brush) y fuente (font).
El canvas maneja todos estos recursos por nosotros, por lo cual nos basta con informarle de que clase de pen queremos manejar y él se encarga del resto. Además, dejando que Delphi se encargue de crear y liberar los recursos gráficos, en muchos casos obtendremos un aumento de velocidad frente a si los manejáramos nosotros mismos.
El objeto canvas encapsula la programación gráfica a tres niveles de profundidad, que son los siguientes:
| Nivel | Operación | Herramientas |
| Alto | Dibujo de líneas y formas Visualización y modificación de texto Relleno de áreas | Métodos MoveTo, LineTo, Rectangle, Ellipse Métodos TextOut, TextHeight, TextWidth, TextRect Métodos FillRect, FloodFill |
| Intermedio | Personalizar texto y gráficos Manipulación pixels Copia y unión de imágenes | Propiedades Pen, Brush y Font Propiedad Pixels Draw, StretchDraw, BrushCopy, CopyRect, CopyMode |
| Bajo | Llamadas a funciones GDI | Propiedad Handle |
Ahora no nos vamos a centrar en explicar todos y cada uno de las herramientas disponibles para la creación de gráficos, sino que lo haremos según vayamos utilizándolas en sucesivas unidades. De todas formas, una descripción detallada de cada una de ellas se encuentra en la ayuda en línea de delphi
Bien, ya conocemos que el objeto canvas nos proporciona un interface gráfico y poderoso de creación gráfica. Pero ahora se nos pueden plantear las siguientes preguntas: ¿Donde reside el objeto canvas?, ¿todos los componentes lo tienen?, y si no es así, ¿qué componentes lo incorporan?
La respuesta a estas preguntas es sencilla: Todos los objetos derivados de TGraphicComponent poseen Canvas. Respecto a otros componentes, depende. En caso de duda, lo más sencillo es consultar el Object Browser y verificar si el componente en cuestión posee o no canvas (bien sea declarado de forma protegida o pública). Así por ejemplo, los componentes Tlabel, Timage y TPanel lo poseen, mientras que componentes tales como TButton o TRadioButton no. Este aspecto determinará en gran medida el ancestor que hemos de elegir cuando queramos desarrollar un componente que deba disponer de Canvas.
La forma de acceder al canvas de un objeto es muy sencilla. Supongamos que tenemos un componente (TGradiente) derivado de la clase TGraphicControl. Y supongamos que queremos dibujar en el canvas una línea desde el punto (0,0) hasta el punto (20,20). Para realizar esta tarea deberiamos escribir la siguiente sentencia:
TGradiente.Canvas.MoveTo(0,0); TGradiente.Canvas.LineTo(20,20);
Centremonos ahora en las propiedades del canvas que utilizaremos en el desarrollo de nuestro componente. Básicamente TGradiente debe dibujar una serie de rectángulos coloreados con un determinado color. Para ello utilizaremos el método FillRect. FillRect recibe como parámetro un objeto del tipo TRect con las coordenadas superior izquierda e inferior derecha del recuadro a pintar. De esta forma, el código que utilizaremos en nuestro componente será similar al siguiente (aún no tenemos en cuenta el color de relleno)
Canvas.FillRect(TRect);
Para asignar el color de relleno utilizaremos la propiedad brush. El objeto brush determina determina el color y patrón que el canvas utiliza para rellenar formas gráficas y fondos. En nuestro caso, utilizaremos la propiedad color del brush para asignar el color de relleno del canvas, de modo que una posterior llamada a FillRect utilice dicho color asignado.
Tambien necesitaremos utilizar la propiedad pen del canvas. El objeto pen determina que clase de lapiz utilizará el canvas para dibujar líneas, puntos y recuadros gráficos. En nuestro caso, utilizaremos la propiedad style del pen que determina que tipo de línea dibujar (en nuestro componente psSolid) y la propiedad mode que determina el modo con que el lapiz dibujará sobre el canvas (en nuestro componente pmCopy)
En Delphi, un color se representa por 4 bytes hexadecimales. El byte más alto se utiliza para determinar el ajuste que Windows hace de la paleta y no vamos a verlo con más detalle. Los otros tres bytes (los tres bytes más bajos) representan la cantidad de rojo, verde y azul que forman el color. Nuestro componente debe calcular un gradiente de colores desde uno inicial (FColorDesde en el código fuente) hasta otro final (FColorHasta). La mejor manera de calcular este gradiente es descomponiendo dichos colores en sus componentes RGB (Rojo, Verde y Azul). De este modo, sabremos la cantidad de cada uno de estos tres colores básicos que forman un color determinado. Por ejemplo el color rojo puro se representa por 255,0,0 (255 de rojo, 0 de verde y azul), y un tono gris tiene los tres valores de rojo, verde y azul iguales (p.e. 150,150,150).
La descomposición de un color en sus tres componentes básicos la realizan tres funciones: GetRValue, GetGValue y GetBValue pertenecientes al API de Windows. Estas funciones toman como parámetro el color del que deseamos obtener la descomposición y devuelven respectivamente la "cantidad" de rojo, verde y azul que componen dicho color.
En el código fuente, una vez descompuestos los colores inicial y final en las variables RGBDesde[0..2] y RGBHasta[0..2] (0 para color rojo, 1 para verde y 2 para azul), el proceso de calculo del gradiente es el siguiente:
Rojo:=RGBDesde[0]+factor[0]*MulDiv(contador,RGBDif[0],255);(de forma análoga para el verde y el azul)
Canvas.Brush.Color:=RGB(Rojo,Verde,Azul);Como es fácil suponer, la función RGB toma tres valores de rojo, verde y azul, y forma el color correspondiente a dichos colores básicos.
Canvas.FillRect(Banda);Banda es una variable de tipo TRect que guarda las coordenadas del recuadro a dibujar.
Un último detalle: cómo se ha mencionado, nuestro gradiente constará de 256 colores. Sin embargo, dependiendo del modo gráfico en que tengamos configurado Windows y de la existencia de otros objetos con sus propios colores, Windows ajustará los colores disponibles para que el resultado final sea lo más aproximado posible al pedido.
Cuando queremos dibujar sobre el canvas, debemos colocar el código correspondiente dentro del método paint perteneciente al objeto TGraphicControl (el objeto TCustomControl tambien dispone de este método). Esto es así porque cuando nuestro componente debe ser dibujado (p.e. el gradiente de colores), bien porque se trate de la primera vez o en respuesta a una solicitud de Windows (p.e. por un solapamiento de ventanas), Windows enviará un mensaje del tipo WM_PAINT a nuestro componente. El componente, de forma automática (herencia), hace una llamada al método paint para redibujar el componente (en nuestro caso, el gradiente).
En general no tendremos la necesidad de investigar más en el mensaje WM_PAINT. Cómo acabamos de explicar, lo único que nos interesa de él (al menos para nuestro componente), es que cuando se reciba este mensaje, Delphi ejecutará el método paint asociado al componente. Y es dentro de este método donde debemos añadir el código necesario para dibujar el gradiente.
Por tanto, todo los pasos que hemos visto anteriormente que son necesarios para dibujar el gradiente, estarán situados dentro del método TGradiente.Paint, que será declarado como override. El método paint lo declararemos de tipo protected, ya que no se debe poder acceder a él desde fuera de nuestro componente.
Como último detalle, señalar que cuando cambiemos el valor de algunas de las propiedades que afectan al dibujo del gradiente, deberemos redibujar el gradiente. Esto se consigue mediante el método repaint, que fuerza el redibujado del gradiente. En él código fuente se observa claramente como cada vez que se cambia el valor de la dirección del gradiente o del color inicial o final, se hace una llamada a repaint.
published propery align
Es importante hacer notar dos aspectos: el primero es que una redeclaración sólo puede hacer menos restrictivo el acceso a una propiedad (p.e paso de protected a public), pero no más restrictivo (p.e paso de public a protected).
El segundo aspecto es que al redeclarar, no es necesario especificar el tipo de la propiedad, basta con indicar su nombre. Lo que si podemos hacer en el momento de la redeclaración es definir un nuevo valor por defecto para dicha propiedad.
unit Gradient;
interface
uses
Classes, Controls, Graphics, WinTypes, WinProcs;
type
TDireccion = (dHorizontal, dVertical); {Tipo de dirección del gradiente}
TGradiente = class(TGraphicControl)
private
FDireccion : TDireccion; {Dirección del gradiente}
FColorDesde, FColorHasta : TColor; {Color del gradiente}
procedure SetDireccion(valor : TDireccion);
procedure SetColorDesde(valor : TColor);
procedure SetColorHasta(valor : TColor);
protected
procedure Paint; override;
public
constructor Create(AOwner : TComponent); override;
published
property Direccion : TDireccion read FDireccion write SetDireccion default dHorizontal;
property ColorDesde : TColor read FColorDesde write SetColorDesde default clBlue;
property ColorHasta : TColor read FColorHasta write SetColorHasta default clBlack;
property Align; {Redeclaración de la propiedad como publicada}
end;
procedure Register;
implementation
constructor TGradiente.Create(AOwner : TComponent);
begin
inherited Create(AOwner); {Siempre lo primero a hacer}
FDireccion:=dHorizontal; {Valores por defecto}
FColorDesde:=clBlue;
FColorHasta:=clBlack;
Width:=100;
Height:=100;
end;
procedure TGradiente.SetDireccion(Valor : TDireccion);
begin
if FDireccion <> valor then
begin
FDireccion := Valor;
Repaint; {Fuerza redibujado del gradiente}
end;
end;
procedure TGradiente.SetColorDesde(Valor : TColor);
begin
if FColorDesde <> Valor then
begin
FColorDesde := Valor;
Repaint; {Fuerza redibujado del gradiente}
end;
end;
procedure TGradiente.SetColorHasta(Valor : TColor);
begin
if FColorHasta <> Valor then
begin
FColorHasta := Valor;
Repaint; {Fuerza redibujado del gradiente}
end;
end;
procedure TGradiente.Paint;
var
RGBDesde, RGBHasta, RGBDif : array[0..2] of byte; {Colores inicial y final y diferencia de colores}
contador, Rojo, Verde, Azul : integer;
Banda : TRect; {Coordenadas del recuadro a pintar}
Factor : array[0..2] of shortint; {+1 si gradiente creciente o -1 en caso decreciente}
begin
RGBDesde[0]:=GetRValue(ColorToRGB(FColorDesde));
RGBDesde[1]:=GetGValue(ColorToRGB(FColorDesde));
RGBDesde[2]:=GetBValue(ColorToRGB(FColorDesde)); {Se descomponen los colores en rojo, verde y azul}
RGBHasta[0]:=GetRValue(ColorToRGB(FColorHasta));
RGBHasta[1]:=GetGValue(ColorToRGB(FColorHasta));
RGBHasta[2]:=GetBValue(ColorToRGB(FColorHasta));
for contador:=0 to 2 do {Calculo de RGBDif[] y factor[]}
begin
RGBDif[contador]:=Abs(RGBHasta[contador]-RGBDesde[contador]);
If RGBHasta[contador]>RGBDesde[contador] then factor[contador]:=1 else factor[contador]:=-1;
end;
Canvas.Pen.Style:=psSolid; {Asignamos el estilo del pen}
Canvas.Pen.Mode:=pmCopy; {Idem modo}
if FDireccion = dHorizontal then {Si el canvas es horizontal...}
begin
Banda.Left:=0; {Coordenadas recuadro}
Banda.Right:=Width;
for contador:=0 to 255 do
begin
Banda.Top:=MulDiv(contador,height,256);
Banda.Bottom:=MulDIv(contador+1,height,256);
Rojo:=RGBDesde[0]+factor[0]*MulDiv(contador,RGBDif[0],255);
Verde:=RGBDesde[1]+factor[1]*MulDiv(contador,RGBDif[1],255);
Azul:=RGBDesde[2]+factor[2]*MulDiv(contador,RGBDif[2],255);
Canvas.Brush.Color:=RGB(Rojo,Verde,Azul);
Canvas.FillRect(Banda); {Pintamos el recuadro}
end;
end;
if FDireccion = dVertical then {Gradiente vertical}
begin
Banda.Top:=0;
Banda.Bottom:=Height;
for contador:=0 to 255 do
begin
Banda.Left:=MulDiv(contador,width,256);
Banda.Right:=MulDIv(contador+1,width,256);
Rojo:=RGBDesde[0]+factor[0]*MulDiv(contador,RGBDif[0],255);
Verde:=RGBDesde[1]+factor[1]*MulDiv(contador,RGBDif[1],255);
Azul:=RGBDesde[2]+factor[2]*MulDiv(contador,RGBDif[2],255);
Canvas.Brush.Color:=RGB(Rojo,Verde,Azul);
Canvas.FillRect(Banda);
end;
end;
end;
procedure Register;
begin
RegisterComponents('Curso', [TGradiente]);
end;
end.
Otros Links de Interés:
Cocina - Videos - Juegos Gratis - Postales cachondas - Cine - Programas Gratis - Letras de Canciones
Listas de todos los Tutoriales Gratis. 1998- 2007 - -
Los
tutoriales y cursos aquí reunidos son una recopilación de los mejores encontrados en
Internet.
El crédito y copyright de los mismos si lo hubiere corresponde al autor de cada
uno de ellos.
Si tu tutorial o curso está aquí, y deseas darlo de baja de esta recopilación o
quieres añadir el tuyo,
envíanos un mensaje desde
aquí