Gráficos vectoriales con Visual Basic .NET

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 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:

 

 

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()

 

 


Índice del curso GDI+
 

la Luna del Guille o... el Guille que está en la Luna... tanto monta...