Marzo 2003
Cipriano Valdezate Sayalero y Manuel Valdezate Sayalero
La brocha
Las brochas se encargan de colorear los dibujos. VSNET ofrece varios tipos de brocha:
- SolidBrush. Rellena la forma con un solo color
- HatchBrush. Rellena la forma con dos colores según un modelo predefinido.
- TextureBrush. Rellena la forma con una textura
- LinearGradientBrush. Rellena un rectángulo con dos colores y transición graduada de uno a otro.
- PathGradientBrusg. Rellena la forma con colores que emanan desde el centro y los vértices de la forma y van graduando su color hasta coincidir con la emanación del color vecino.
SolidBrush es la brocha más sencilla pues se compone de un solo color:
Dim BrochaSólida As SolidBrush = New SolidBrush(Color.LightGreen)
Así como para dibujar formas utilizábamos métodos cuyo nombre se componía del verbo Draw y el nombre de la forma, los nombres de los métodos que las pintan se componen del verbo Fill y el nombre de la Forma: FillRectangle, FillEllipse, etc. La siguiente línea genera un rectángulo pintado de verde:
Lienzo.FillRectangle(BrochaSólida, New Rectangle(New Point(10, 10), New Size(300, 100)))
y el siguiente procedimiento dibuja algo parecido a un lazo:
Private Sub PintaCurvaCerrada()
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Puntos() As Point = _
{New Point(150, 150), New Point(100, 100), New Point(150, 50), _
New Point(250, 150), New Point(300, 100), New Point(250, 50)}
'Versión Fillxxxx de DrawCurve
Lienzo.FillClosedCurve(Brushes.DeepPink, Puntos, 1)
End Sub
Voilà:
Del mismo modo que podíamos definir el lapicero mediante la enumeración Pens, podemos definir la brocha mediante la enumeración Brushes:
Lienzo.FillRectangle(Brushes.LightGreen, New Rectangle(New Point(10, 10), New Size(300, 100)))
vamos a aprovechar el procedimiento que dibujaba círculos a partir del punto central y el radio para pintar una diana:
Private Sub PintaDiana()
Dim r As Short
Dim Tope As Boolean = True
Dim Lienzo As Graphics = Me.CreateGraphics
Dim BrochaSólida As SolidBrush
For r = 400 To 50 Step -50
Tope = Not Tope
If Tope Then BrochaSólida = Brushes.White Else BrochaSólida = Brushes.Black
PintaCírculo(Lienzo, BrochaSólida, New Point(300, 250), r)
Next
BrochaSólida.Dispose()
Lienzo.Dispose()
End Sub
Private Sub PintaCírculo(ByVal Lienzo As Graphics, _
ByVal Brocha As Brush, _
ByVal Punto As Point, _
ByVal Radio As Integer)
PintaElipse(Lienzo, Brocha, Punto, Radio, Radio)
End Sub
Private Sub PintaElipse(ByVal Lienzo As Graphics, _
ByVal Brocha As Brush, _
ByVal Punto As Point, _
ByVal RMayor As Integer, _
ByVal RMenor As Integer)
Dim MiPunto As Point = New Point(Punto.X - RMayor / 2, Punto.Y - RMenor / 2)
Dim Tamaño As Size = New Size(RMayor, RMenor)
Dim Rectángulo As Rectangle = New Rectangle(MiPunto, Tamaño)
Lienzo.FillEllipse(Brocha, Rectángulo) 'Hemos sustituido DrawEllipse por FillEllipse
End Sub
Aquí está la diana:
HatchBrush define un modelo de distribución de dos colores sin gradación de transición entre ellos dentro de una forma. Viene a ser un entramado. Los modelos disponibles en VSNET se encuentran en la enumeración HatchStyle. La clase HatchBrush no se puede heredar, por tanto no se pueden crear modelos personalizados a partir de ella. Con el modelo y dos colores tenemos definido un HatchBrush:
Dim Trama As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Blue, Color.Yellow)
A continuación incluyo un ejemplo que combina el método DrawRectangle con FillRectangle para crear un rectángulo con marco. Primero defino un objeto rectángulo, luego dibujo un rectángulo con un lápiz negro un poquito más grande que el rectángulo definido, y después pinto el rectángulo definido con un HatchBrush:
Dim Rectángulo As Rectangle = New Rectangle(New Point(100, 100), New Size(300, 100))
Dim Trama As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Blue, Color.Yellow)
‘Dibujo el marco aumentando levemente la anchura y la altura del rectángulo
Lienzo.DrawRectangle(New Pen(Color.Black, 3), _
New Rectangle(New Point(Rectángulo.X - 1, Rectángulo.Y - 1), _
New Size(Rectángulo.Size.Width + 1, Rectángulo.Size.Height + 1)))
Lienzo.FillRectangle(Trama, Rectángulo)
Lienzo.Dispose()
Este es el resultado:
TextureBrush no rellena la forma con colores, sino con una imagen. Una textura consiste en el relleno de una superficie con una imagen repetida tantas veces cuantas sea necesario hasta cubrir toda la superficie. Los parámetros del constructor de TextureBrush son, por tanto, la imagen y los diferentes modos en que se repiten las imagenes, contenidos en la enumeración WrapMode. He aquí un ejemplo (se supone que tenemos una imagen en un PictureBox):
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Textura As TextureBrush = New TextureBrush(PictureBox1.Image, WrapMode.TileFlipX)
Lienzo.FillRectangle(Textura, New Rectangle(New Point(0, 0), _
New Size(Me.ClientSize.Width, Me.ClientSize.Height)))
Textura.Dispose() ‘Es conveniente cerrar la brocha cuando no se utilice
La mejor explicación de los miembros de la enumeración WrapMode es ejecutar el código y verlo, por eso no nos vamos a detener más en ello.
LinearGradientBrush Como dije más arriba, esta brocha rellena un rectángulo con dos colores y transición graduada de uno a otro. Todo lo que tenemos que entregar al constructor es el rectángulo, los dos colores y la orientación de la graduación. La orientación de la graduación está incluida en la enumeración LinearGradientMode. Es fácil detectar la diferencia entre cada miembro de esta enumeración, por tanto aquí sólo mostraré un ejemplo y la imagen que genera:
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Rectángulo As Rectangle = New Rectangle(New Point(100, 100), New Size(400, 200))
Dim Brocha As LinearGradientBrush = New LinearGradientBrush(Rectángulo, Color.BlueViolet, Color.LightBlue, LinearGradientMode.Vertical)
Lienzo.FillRectangle(br, rec)
Brocha.Dispose()
Lienzo.Dispose()
La gradación uniforme de dos colores es el tipo más simple de gradación. Podemos añadir más colores y colocarlos en cualquier punto del recorrido de la gradación a discreción gracias a la clase ColorBlend. Un objeto ColorBlend espera un array de colores y otro array de tipo Single() de igual número de elementos y distribuye los colores del primer array en las posiciones relativas representadas por los valores del segundo
Los valores del array que representa las posiciones dentro del gradiente deben cumplir las siguientes condiciones:
- El primer valor del array ha de ser 0.0F
- El último valor del array ha de ser 1.0F
- Los valores restantes no pueden ser ni menores de 0.0F ni mayores de 1.0F
Un valor de, por ejemplo, 0.2F, representa la posición situada al final del tramo que recorre un 20% de la distancia del gradiente desde su punto inicial. El color del array de colores cuyo índice coincide con el de un determinado valor del array de posiciones inicia su gradación en esa posición, y se derrama por sus dos vertientes hasta que se funde con el derrame del color vecino. Veamos un ejemplo con el gradiente que genera:
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Rectángulo As Rectangle = New Rectangle(New Point(100, 100), New Size(400, 200))
Dim Brocha As LinearGradientBrush = New LinearGradientBrush(rec, _
Color.Transparent, Color.Transparent, LinearGradientMode.Vertical)
'Es indiferente qué colores entreguemos al objeto LinearGradientBrush
'Puesto que se mostrarán los que incluyamos en el array
'que entregamos a ColorBlend
Dim Mezcla As ColorBlend = New ColorBlend(7) 'Número de elementos de los arrays. Opcional
Dim c As Color
With c 'Los arrays se entregan a Colorblend mediante las propiedades Colors y Positions
Mezcla.Colors = New Color() _
{.Blue, .Red, .Violet, .Red, .Blue, .LightBlue, .Blue}
End With
Mezcla.Positions = New Single() {0.0F, 0.1F, 0.3F, 0.5F, 0.6F, 0.8F, 1.0F}
Brocha.InterpolationColors = Mezcla 'Imprescindible
Lienzo.FillRectangle(Brocha, Rectángulo)
Brocha.Dispose()
Lienzo.Dispose()
El gradiente de este rectángulo está distribuido de tal forma que nos permite formar un mosaico utilizándolo a modo de textura. He aquí el código y el mosaico generado:
Private Sub PintaMosaico()
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Tamaño As New Size(300, 150)
Dim i, j, Anchura, Altura As Short
Anchura = Me.ClientSize.Width \ Tamaño.Width
Altura = Me.ClientSize.Height \ Tamaño.Height
For i = 0 To Anchura
For j = 0 To Altura
PintaRectángulo(Lienzo, New Point(i * Tamaño.Width, j * Tamaño.Height), Tamaño)
Next
Next
Lienzo.Dispose()
End Sub
Private Sub PintaRectángulo(ByVal Lienzo As Graphics, ByVal Punto As Point, ByVal Tamaño As Size)
Dim Rectángulo As Rectangle = New Rectangle(Punto, Tamaño)
Dim Brocha As LinearGradientBrush = New LinearGradientBrush(Rectángulo, _
Color.Transparent, Color.Transparent, LinearGradientMode.Vertical)
'Es indiferente qué colores entreguemos al objeto LinearGradientBrush
'Puesto que se mostrarán los que incluyamos en el array
'que entregamos a ColorBlend
Dim Mezcla As ColorBlend = New ColorBlend(7) 'Número de elementos de los arrays. Opcional
Dim c As Color
With c 'Los arrays se entregan a Colorblend mediante las propiedades Colors y Positions
Mezcla.Colors = New Color() _
{.Blue, .Red, .Violet, .Red, .Blue, .LightBlue, .Blue}
End With
Mezcla.Positions = New Single() {0.0F, 0.1F, 0.3F, 0.5F, 0.6F, 0.8F, 1.0F}
Brocha.InterpolationColors = Mezcla 'Imprescindible
Lienzo.FillRectangle(Brocha, Rectángulo)
Brocha.Dispose()
End Sub
PathGradientBrush En vez de definir una gradación de dos colores en una figura cerrada, PathGradientBrush lanza los colores desde el centro y los vértices de la figura. El constructor de PathGradientBrush espera, por tanto, o bien un array de puntos que formarán los vértices de la figura y de los cuales deducirá el punto central, o bien un trayecto del cual extraerá los vértices y el punto central. Además también debemos entregar al constructor de PathGradientBrush un color para el punto central y un array de tantos colores como vértices tenga la figura. Veamos un ejemplo:
Dim Lienzo As Graphics = Me.CreateGraphics 'Creamos el lienzo donde dibujaremos
Dim Trayecto As GraphicsPath = New GraphicsPath() 'Creamos un GraphicsPath
Dim Rectángulo As Rectangle = New Rectangle(New Point(100, 100), New Size(300, 150))
Trayecto.AddRectangle(Rectángulo) 'Le añadimos un rectángulo
'Creamos un PathGradientBrush y le entregamos el GraphicsPath
'Del GraphicsPath exraerá los puntos de los vértices
'y deducirá el punto central
Dim Brocha As PathGradientBrush = New PathGradientBrush(Trayecto)
'Entregamos el color que emana del punto central
'mediante la propiedad CenterColor
Brocha.CenterColor = Color.LightYellow
'Entregamos un array de cuatro colores
'Uno para cada vértice del rectángulo
'encapsulado en el GraphicsPath
Brocha.SurroundColors = New Color() {Color.LightGreen, Color.LightGreen, Color.Green, Color.DarkGreen} ‘La combinación del mismo color en sus versiones Light-, normal y Dark- nos ha permitido crear efecto de sombra
'Llamamos al método FillPath para que pinte la forma
'entregándole el PathGradientBrush y el GraphicsPath
Lienzo.FillPath(Brocha, Trayecto)
'Cerramos los objetos
Brocha.Dispose()
Lienzo.Dispose()
Si en vez de un trayecto hubiéramos utilizado un array de puntos, el código sería el siguiente:
Dim Lienzo As Graphics = Me.CreateGraphics 'Creamos el lienzo donde dibujaremos
‘Definimos un array de puntos. Los puntos tenemos que ordenarlos como si dibujáramos la forma sin levantar el lápiz del papel.
Dim Puntos() As Point = {New Point(100, 100), New Point(400, 100), New Point(400, 250), New Point(100, 250)}
Dim Brocha As PathGradientBrush = New PathGradientBrush(Puntos)
'Entregamos el color que emana del punto central
'mediante la propiedad CenterColor
Brocha.CenterColor = Color.LightYellow
'Entregamos un array de cuatro colores
'Uno para cada vértice del rectángulo
Brocha.SurroundColors = New Color() {Color.LightGreen, Color.LightGreen, Color.Green, Color.DarkGreen}
'Llamamos al método FillPolygon para que pinte la forma
'entregándole el PathGradientBrush y el array de puntos
Lienzo.FillPolygon(Brocha, Puntos)
'Cerramos los objetos
Brocha.Dispose()
Lienzo.Dispose()
El resultado es el mismo.
Podemos mover el punto central indicándole sus coordenadas a PathGradientBrush mediante la propiedad CenterPoint:
‘Entrego un nuevo valor para la coordenada X y, para la coordenada Y, el valor por defecto
b.CenterPoint = New PointF(200, b.CenterPoint.Y)
El resultado es el siguiente:
Así como personalizamos LinearGradientBrush con ColorBlend, también podemos personalizar PathGradientBrush. He aquí un ejemplo:
Dim Lienzo As Graphics = Me.CreateGraphics
Dim Trayecto As GraphicsPath = New GraphicsPath()
Dim Rectángulo As New Rectangle(New Point(10, 10), New Size(500, 500))
Trayecto.AddArc(Rectángulo, 180, 180)
Dim Brocha As PathGradientBrush = New PathGradientBrush(Trayecto)
Brocha.CenterPoint = New PointF(260, 260)
Dim Posiciones (15) As Single : Dim Colores (15) As Color
Posiciones(0) = 0.0F : Colores(0) = Color.Firebrick
Posiciones(1) = 0.15F : Colores(1) = Color.Firebrick
Posiciones(2) = 0.25F : Colores(2) = Color.Goldenrod
Posiciones(3) = 0.35F : Colores(3) = Color.Firebrick
Posiciones(4) = 0.525F : Colores(4) = Color.Brown
Posiciones(5) = 0.6F : Colores(5) = Color.Goldenrod
Posiciones(6) = 0.66F : Colores(6) = Color.Brown
Posiciones(7) = 0.775F : Colores(7) = Color.Brown
Posiciones(8) = 0.825F : Colores(8) = Color.Goldenrod
Posiciones(9) = 0.85F : Colores(9) = Color.Brown
Posiciones(10) = 0.935F : Colores(10) = Color.DarkRed
Posiciones(11) = 0.95F : Colores(11) = Color.Goldenrod
Posiciones(12) = 0.965F : Colores(12) = Color.DarkRed
Posiciones(13) = 0.985F : Colores(13) = Color.DarkRed
Posiciones(14) = 0.995F : Colores(14) = Color.Goldenrod
Posiciones(15) = 1.0F : Colores(z) = Color.Goldenrod
Dim Mezcla As New ColorBlend()
Mezcla.Colors = Colores
Mezcla.Positions = Posiciones
Brocha.InterpolationColors = Mezcla
Lienzo.FillPath(Brocha, Trayecto)
Brocha.Dispose()
Lienzo.Dispose()