<-- Capítulo II

Regresar a la página principal

Capítulo IV -->

Capítulo III

API'S WIN32 GRAFICAS (GDI)

Windows a nivel grafico usa la GDI (Graphics Device Interface o Interfaz grafica de dispositivo), la GDI no es mas que un conjunto de librerías las cuales se usan para la graficación en Windows, es curiosos pero si pensamos un poquito todo gira en la GDI, claro es como obvio, ya que todo Windows es grafico, estas funciones o librerías se encuentran en su mayoría en el archivo gdi32.dll, con estas funciones podemos dibujar formularios, botones, textos, rayas, formas, etc. Pero vamos a pensar un poco mas abstracto, y no ver la GDI como las funciones usadas para obtener un resultado para presentar en el monitor, una hoja de papel impresa en color también es grafica por lo tanto la GDI juega un papel importante.

Ahora como juega nuestro HDC con todo esto, imaginemos a un pintor, básicamente que tiene: un cabestrillo, un papel, un pincel, y colores, bueno nuestro cabestrillo es el HWND, es nuestro soporte donde nosotros ponemos nuestro papel que sería el HDC, el HDC es el papel donde nosotros dibujamos y ese papel puede ser enviado a una pantalla o impresora, o cualquier dispositivo de salida grafico. Claro el reto esta en ¿Cómo hacemos para pintar en nuestra hoja?. Voy a mostrar a continuación herramientas de dibujo útiles para el desarrollo de nuestros proyectos, claro esta, que Visual Basic esta parte la esconde en su interior ya que algunas cosas suelen ser un poco tediosas, pero es claro entender que todo lo que hablaremos aquí esta relacionado con nuestra hoja (HDC) donde pondremos nuestra imaginación a correr.

BROCHAS

Volviendo a pintor, imaginemos que necesita pintar un mural en una calle, para el fondo de su pintura es claro pensar que agarrara una brocha, pintara el fondo de un color y luego continuara el resto, ¿es esto algo parecido a la computación?, claro que si, una brocha no es mas que pintar el fondo de un HDC o parte del fondo, usando una forma geométrica para ello.

Pero vamos a lo importante, abran un proyecto en VB (Visual Basic) luego coloquen un listbox, el listbox aléjenlo del borde izquierdo superior (0,0), es decir, colóquenlo en left = 3000, y top = 0, la imagen bloque.bmp que viene con este manual colóquenla en donde esta el proyecto y luego coloquen este código:

Ver Codigo

Cuando arranquen la aplicación verán que el listbox se llena con valores y al seleccionar cada opción aparece un recuadro en la esquina superior izquierda que ve cambiando de color y de fondo a medida que nos movemos por el listado, en este momento ustedes dirán ¿Cómo se hace todo esto?.

Bueno primero que nada vamos explicar que existen 3 tipos de brochas, las sólidas, las tramadas y la texturizada.

SOLIDAS

Esta se encarga de crear una brocha de un color sólido. Como se puede notar viendo su API.

CreateSolidBrush

Api: Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long

Pueden ver que como único parámetro toma un color, pueden usar la función RGB para ello, mas adelante les explicare lo que hice.

TRAMADAS

No es mas que crear una brocha con tramas ya definidas, estas la vemos en las primeras 6 opciones.

CreateHatchBrush

Api: Declare Function CreateHatchBrush Lib "gdi32" (ByVal nIndex As Long, ByVal crColor As Long) As Long

Esta API en este caso toma un color, y un “nIndex”, este nIndex pueden ser 6 valores, que son:

Private Const HS_BDIAGONAL = 3

Private Const HS_CROSS = 4

Private Const HS_DIAGCROSS = 5

Private Const HS_FDIAGONAL = 2

Private Const HS_HORIZONTAL = 0

Private Const HS_VERTICAL = 1

Y es fácil ver en el programa la forma del tramo que forma cada constante, y el color dependerá, de como configuren la variable “crColor” de la función.

Texturizadas

Con esta creamos una brocha que en ves de tener color tiene una textura (imágenes), lamentablemente en Window95/98/ME solamente se pueden usar texturas 8x8 píxeles, no pueden usar una mas grande ya que en caso de colocar una mas grande Windows les tomara los primeros 8x8 píxeles, en caso de trabajar con Window2000/NT pueden usar imágenes de tamaño indefinido. Y supongo que XP debe de tener también esa posibilidad.

Ustedes dirán: hago el programa en Windows 2000, uso una imagen grande la cual funciona y crees que al compilar el .exe en Windows 2000 no vas tener problema con Windows 98 o 95, déjame decirte que eso no funciona así!, el hecho que lo compiles en Windows 2000 no implica que te funcionara en Windows 98 o 95, ya que el gdi32.dll en Windows 2000 es distinto que el de Windows 98 por mas que se llamen igual. Y NI SE LES OCURRA suplantar el gdi32.dll del Windows 98 con el de Windows 2000, creyendo que así solucionan el problema, por que lo que van a ganar es que Windows se ponga un poco loco y eso no lo queremos. Ahora veamos el API involucrada.

CreatePatternBrush

Api: Declare Function CreatePatternBrush Lib "gdi32" (ByVal hBitmap As Long) As Long

Aquí pueden ver que el único parámetro es un BITMAP.

OTRAS API’S

Seguramente vieron sencilla la explicación de las 3 API ‘S que se usan para crear una brocha pero de seguro no entienden todavía el programa, ya que hay mas API’S en juego que de seguro los confunde, básicamente para crear una brocha se necesita las API que ya mencione y las que voy a explicar ahora son algunas API’S adicionales y otras API’S obligadas para el funcionamiento de la brocha.

Pero antes de entrar en detalle necesito explicar una cosita, verán que hay dos API SelectObject y DeleteObject.

Cuando uno crea un HDC el viene con un paquete incluido, ese paquete tiene brocha, un pincel, una fuente, etc. En este momento cubriremos nada más la brocha. Es algo así:

Noten que en las API’S que crean las brochas no se menciona el HDC en ningún lado, la misión de esas API’S es simple: ¡crear algo! , en este caso una brocha. Pero es la función SelectObJect la encargada de cambiar la brocha existente por una nueva.

SelectObject

Api: Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long

Vean que aquí sí hay un hdc, que es nuestro primer parámetro y hay una variable que es nuestro objeto. Analicemos una de las líneas de código del programa:

brocha = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0))

SelectObject Me.hdc, brocha

Vean que primero creamos la brocha, la cual almacenamos en una variable “brocha”, pero esa brocha esta en el aire, esta en memoria, no pertenece a nadie, y luego SelectObject entra en juego, lo que hace en “español” es lo siguiente: “Cámbiame la brocha que tiene Me.hdc, por esta nueva que se llama brocha”. Ustedes dirán: pero si Me.hdc no tiene brocha ya que yo no configure ninguna, ¡error!, Como dije arriba todo HDC viene con un paquete y ese paquete contiene una brocha.

Ahora ¿se pueden hacer mas cosas con SelectObject?.si!. Que sucede si hago esto:

brocha = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0))

brochavieja = SelectObject(Me.hdc, brocha)

Se ve como obvio, si ustedes tomaran el valor de retorno de SelectObject, no sería más que el objeto que fue suplantado, que quiero decir, recuerdan que Me.hdc ya tiene una brocha, y con SelectObject la cambian, el valor que retorna SelectObject es la brocha antigua que tenia nuestro HDC. ¿Para que quiero la brocha antigua?. No se!, dependerá del programa que hagas pero algunas veces suele ser útil, ¿Cómo retorno la brocha antigua? por ejemplo:

brocha = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0))

brochavieja = SelectObject(Me.hdc, brocha)

Rectangle Me.hdc, 0, 0, 100, 100

SelectObject Me.hdc, brochavieja

Rectangle Me.hdc, 0, 0, 100, 100

Lo que estamos haciendo es pintar un rectángulo con una brocha nueva, en este caso de tramado, luego suplantamos o cambiamos la brocha nueva por la vieja, y pintamos un rectángulo con la brocha vieja. Pregunto algo: ¿si yo hiciera esto en el código de arriba?:

BrochaX = SelectObject(Me.hdc, brochavieja)

Pregunto ¿Que brocha tiene brochaX?. Después de todo de lo que he explicado deberían de conocer la respuesta por ende no se las diré.

Esta parte no la tengo en el código de ejemplo, por que no era necesario almacenar la brocha antigua, ya que no me interesaba, pero uno nunca sabe los programas que tenemos que crear.

Si creamos un objeto en memoria, ese objeto lo tenemos que eliminar, como las API es independiente de VB si ustedes crean un objeto con las funciones API, el hecho que cierren el programa no implica que VB quitara de memoria ese objeto, y para ello usamos DeleteObject.

DeleteObject

Api: Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long

Fácil de explicar, lo que hace es eliminar el objeto creado. En nuestro código en Form_Unload tenemos lo siguiente:

DeleteObject brocha

Lo que hacemos aquí es eliminar nuestra brocha creada. Para que no quede en la memoria. Pero ustedes dirán: “Eduardo pero como sabemos que API’S usan SelectObject para su funcionamiento”, básicamente en la documentación les dice, pero aquí les menciono algunas funciones que usan SelectObject en su funcionamiento, y evidentemente si usan SelectObject deberían de usar DeleteObject para borrarlo, aquí están las funciones:

CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection, CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush, CreateFont, CreateFontIndirect, CreatePen, CreatePenIndirect, CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect

Aunque en este manual no trabajaremos todas estas API, es importante que sepan cuando tienen que usar SelectObject.

Sigamos con las API’S extrañas que están en el código. Nos faltan 3 Api’s que la explicamos a continuación:

GetStockObject

Api: Declare Function GetStockObject Lib "gdi32" (ByVal nIndex As Long) As Long

Windows como sistema operativo tiene objetos predefinidos, es decir, objetos ya creados y la forma de invocarlos o usarlos es con esta función GetStockObject, su único parámetro es un índice que dependiendo del índice que sea, retornara un objeto especifico.

Los posibles nIndex son:

Private Const BLACK_BRUSH = 4 ‘Brocha de color NEGRO

Private Const DKGRAY_BRUSH = 3 ‘Brocha de color Gris Oscuro

Private Const GRAY_BRUSH = 2 ‘Brocha de color Gris

Private Const HOLLOW_BRUSH = 5 ‘Brocha Nula o vacia

Private Const LTGRAY_BRUSH = 1 ‘Brocha gris claro

Private Const WHITE_BRUSH = 0 ‘Brocha blanca

Private Const WHITE_PEN = 6 ‘Pincel blanco

Private Const ANSI_FIXED_FONT = 11 ‘Fuente utilizada por el sistema

Private Const ANSI_VAR_FONT = 12 ‘Igual que la anterior, pero con paso de variable.

Private Const DEFAULT_GUI_FONT = 17 ‘Fuente por defecto en Windows utilizada en los menús y cajas de diálogos

Private Const SYSTEM_FONT = 13 ‘Fuente del sistema

Private Const SYSTEM_FIXED_FONT = 16 'Esta permite compatibilidad con window 3.1 o 16 bits.

Private Const DEFAULT_PALETTE = 15 ‘Paleta por defecto, usada en la creación de imágenes.

Estas últimas variables poco usadas se usan cuando se crean fuentes.

¿Cómo usamos esta función un nuestro código?. Veamos:

SelectObject Me.hdc, GetStockObject(LTGRAY_BRUSH)

Rectangle Me.hdc, 0, 0, 100, 100

Observen que en ningún momento uso ninguna función Create…Brush, ya que la función GetStockObject me retorna una brocha del sistema o ya configurada en Windows, por lo que el trabajo se hace directo.

GetSysColor

Api: Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long

Así como Windows posee objetos predefinidos o creados, también Windows posee una configuración de los colores del sistema. Que quiero decir, si tuvieran Microsoft Plus, notaran que cuando cambia las opciones ven diferentes colores, en los botones, formularios, caption, etc. Windows mantiene un registro de esos colores en el registro de Windows y la forma de acceder a esos colores es por esta función, con esto lo que se quiere es que las aplicaciones sean compatible con el color que define el cliente. Esta API es sencilla lo que hace es regresar el color correspondiente a un índice. Los cuales presento a continuación:

Valores posibles para “nIndex”:

Private Const COLOR_3DDKSHADOW = 21 ‘Color Sombra oscura de los elementos tridimensionales

Private Const COLOR_3DLIGHT = 22 ‘Color de la cara de los elementos tridimensionales

Private Const COLOR_BTNFACE = 15 ‘El mismo color que el anterior pero de los botones

Private Const COLOR_BTNHIGHLIGHT = 20 ‘Color brillante de los elementos tridimensionales

Private Const COLOR_BTNSHADOW = 16 ‘Color Sombra de los elementos tridimensionales

Private Const COLOR_ACTIVEBORDER = 10 ‘Color del borde activo

Private Const COLOR_ACTIVECAPTION = 2 ‘Color de la barra de titulo

Private Const COLOR_BACKGROUND = 1 ‘Color del escritorio

Private Const COLOR_BTNTEXT = 18 ‘Color del texto en los botones

Private Const COLOR_CAPTIONTEXT = 9 ‘Color del texto en las etiquetas

Private Const COLOR_GRADIENTACTIVECAPTION = 27 ‘Color del lado izquierdo de la barra de titulo cuando esta activo.

Private Const COLOR_GRADIENTINACTIVECAPTION = 28 ‘Color del lado izquierdo de la barra de titulo cuando esta inactivo. Estas ultimas 2 son nada mas para NT 5.0 y Windows 98, si ven la barra superior del formulario verán un degradado, de un color a otro, bueno ambas variables toman el color del lado izquierdo el primero es cuando esta activo y el segundo cuando esta inactivo.

Private Const COLOR_GRAYTEXT = 17 ‘Color del texto cuando esta desactivado

Private Const COLOR_HIGHLIGHT = 13 ‘Color de los ítem’s seleccionados de un control.

Private Const COLOR_HIGHLIGHTTEXT = 14 ‘Color del texto cuando es seleccionado de un control

Private Const COLOR_INACTIVEBORDER = 11 ‘Color del borde inactivo

Private Const COLOR_INACTIVECAPTION = 3 ‘Color del caption inactivo

Private Const COLOR_INACTIVECAPTIONTEXT = 19 ‘Color del texto inactivo

Private Const COLOR_INFOBK = 24 ‘Color del fondo de los cuadritos ToolTip

Private Const COLOR_INFOTEXT = 23 ‘Color del texto de los cuadritos ToolTip

Private Const COLOR_MENU = 4 ‘Color del fondo del menú

Private Const COLOR_MENUTEXT = 4 ‘Color del texto del menú

Private Const COLOR_SCROLLBAR = 0 ‘Color del scroll bar

Private Const COLOR_WINDOW = 5 ‘Color del fondo de window (formulario)

Private Const COLOR_WINDOWFRAME = 6 ‘Color del frame de window (formulario)

Private Const COLOR_WINDOWTEXT = 8 ‘Color del texto de window (formulario)

Y si revisan la documentación oficial en Internet pueden encontrar un poquito mas. Pero básicamente esto son todos los colores del sistema.

El uso de esta API se hace de esta manera:

brocha = CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR))

SelectObject Me.hdc, brocha

Creo que es fácil ver que hace, GetSysColor nos regresa un color dependiendo de las constante que le pasamos.

La última API Rectangle, no la explico ya que tengo una sección para ella, pero creo que es muy fácil de entender.

Esto no es todo sobra brochas, hay mas API’S pero para mi estas son las mas importante y las mas usadas. De todas maneras en la documentación oficial en Internet encontraran todos las API’S Win32 referente a las brochas.

PINCELES

Muy bien, ya vimos las brochas, ahora nos toca los pinceles pero, ¿Qué es el pincel? si volvemos al pintor, pincel es lo que se usa para pintar las líneas de una figura, claro que no todos están de acuerdo conmigo, ya que con una brocha en la vida real ustedes puede pintar líneas, eso es cierto, pero en la computadora el pincel es el encargado del pintado de las líneas y las brochas es en el encargado del relleno. Es como si el pintor usara un pincel fino para delimitar un triangulo y luego usa un pincel grueso “brocha” para rellenar el triangulo de un color.

Ahora en la API Rectangle, pudimos ver como se llenaba el fondo con un color pero si notan bien había un marco que delimitaba al rectángulo, trabajar con ese marco es trabajar con el pincel, para cualquier figura que veamos en el futuro, veremos que todas las figuras están delimitadas por líneas, y usamos los pinceles para alterar o modificar esas líneas, para obtener un efecto cualquiera.

Windows trabaja con 2 tipos de pinceles: Cosméticos y Geométricos.

Los cosméticos son pinceles que se dibujan rápidamente. Estos pinceles se dibujan directamente en el dispositivo, sin pasar por el motor de transformación de Windows. Es decir, sus unidades son de dispositivo y no lógicas.

En cambio, los Geométricos son pinceles un poco lentos, pero hacen un uso completo del motor de transformación de Windows. Por lo que sus unidades son lógicas y no físicas. Ustedes dirán, son malos estos pinceles, no!, son mejores, tardan mas pero se pueden lograr efectos mucho mas avanzados que con los pinceles cosméticos. Lo único lamentable es que dichos efectos tienen resultado en su mayoría en Windows 2000/XP/NT.

En este momento ustedes están confundidos ya que les mencione “motor de transformación de Windows”, “unidades lógicas”, “unidades físicas”, ya se que talvez esto no lo entienden ahora, pero aguanten un rato que después de los pinceles explicare esto. Algunos libros explican esta parte primero antes de explicar las herramientas graficas, particularmente yo prefiero explicar brochas y pinceles primero ya que necesito que vayan entrando a este mundo de las API, y mas que cuando explique esa parte utilizare lo ya explicado. Así que no importa si no entienden ahorita, después verán que todo era simple.

Pongan este programa en VB (para este proyecto necesitan 2 combobox, debajo del Combo1 pongan un TextBox llamado Text1 y pónganle un valor por defecto que sea 1), y coloquen la imagen “pens.bmp” en la carpeta del proyecto:

Ver Codigo

Como se puede ver, el combo1 se encarga de los pinceles Cosméticos y el combo2 se encarga de los Geométricos. En caso de tener Windows 98/95/ME, no verán ningún efecto en el combo2. Verán un rectángulo con colores pero en Windows 2000/NT/XP se ve distinto.

CreatePen

Api: Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long

Api sencilla encargada de crear un nuevo pincel “cosmético”. Como primer parámetro se pasa “nPenStyle” los cuales puede poseer los siguientes valores:

Private Const PS_DASH = 1 ' -------

Private Const PS_DASHDOT = 3 ' _._._._

Private Const PS_DASHDOTDOT = 4 ' _.._.._

Private Const PS_DOT = 2 ' .......

Private Const PS_SOLID = 0

Private Const PS_NULL = 5

Estos valores se usan para configurar que tipo de trazado queremos. El parámetro “nWidth” se encarga de establecer el ancho del pincel, este parámetro solo sirve para el estilo PS_SOLID, en caso de no ser PS_SOLID, no importa el número que coloquen siempre será 1. Por último tenemos “crColor” encargado de determinar el color del pincel.

Creo que es fácil de ver lo que hace si paseamos por los distintos valores del combo1. Nada más explicare uno de ellos, que vale para todos.

nuevopen = CreatePen(PS_SOLID, Text1.Text, RGB(255, 0, 255))

SelectObject Me.hdc, nuevopen

Rectangle Me.hdc, 10, 10, 100, 100

DeleteObject nuevopen

Al igual que la brocha, CreatePen nos retorna un valor el cual le pasamos a nuestro HDC por medio de SelecObject.

nuevopen = CreatePen(PS_SOLID, Text1.Text, RGB(255, 0, 255))

Aquí estamos configurando un pincel SÓLIDO, cuya anchura es el valor que pongan en Text1, y cuyo color es morado. Como pueden ver se usa prácticamente de la misma manera que una brocha con la diferencia que esta altera el marco del rectángulo y no el fondo del mismo como lo hace la brocha. Prueben cambiando la anchura en PS_SOLID para que vean el efecto.

ExtCreatePen

Api: Declare Function ExtCreatePen Lib "gdi32" (ByVal dwPenStyle As Long, ByVal dwWidth As Long, lplb As LOGBRUSH, ByVal dwStyleCount As Long, lpStyle As Long) As Long

Una API mucho mas completa que permite la creación tanto de pinceles “cosméticos” como “geométricos”, en este código use esta API para los pinceles Geométricos, pero cuando explique los parámetros verán como usarla para los cosméticos.

Primer parámetro:

DwPenStyle: esta variable es mucho más compleja en esta función API. Ya que se divide en cuatro grupos.

Grupo 1 (Tipo de Pincel):

Private Const PS_GEOMETRIC = &H10000

Private Const PS_COSMETIC = &H0

Grupo 2 (Estilo del Trazado)

Private Const PS_DASH = 1 ' -------

Private Const PS_DASHDOT = 3 ' _._._._

Private Const PS_DASHDOTDOT = 4 ' _.._.._

Private Const PS_DOT = 2 ' .......

Private Const PS_SOLID = 0

Private Const PS_ALTERNATE = 8

Grupo 3 (Acabado).

Private Const PS_ENDCAP_FLAT = &H200

Private Const PS_ENDCAP_SQUARE = &H100

Private Const PS_ENDCAP_ROUND = &H0

Imagen tomada de la ayuda de Microsoft MSDN en internet.

Grupo 4 (Enlace entre trazados).

Private Const PS_JOIN_BEVEL = &H1000

Private Const PS_JOIN_MITER = &H2000

Private Const PS_JOIN_ROUND = &H0

Imagen tomada de la ayuda de Microsoft MSDN en internet.

DwWidth: este parámetro controla el ancho del pincel. Cabe destacar que este ancho en los pinceles cosméticos cumple las mismas restricciones que en el API CreatePen.

Lplb: es una estructura para configurar una brocha para un pincel, esta brocha surte efecto cuando el ancho “dwWidth” es un número relativamente alto. Lo que hace es pintar la parte rellena del pincel con la brocha que se seleccione. Esta estructura es de este tipo:

Private Type LOGBRUSH

lbStyle As Long

lbColor As Long

lbHatch As Long

End Type

LbStyle: se encarga de configurar el tipo de brocha:

Private Const BS_SOLID = 0 ‘ Color SÓLIDO

Private Const BS_HATCHED = 2 ‘ Tramado de la brocha, son las mismas que las vistas anteriores.

Private Const BS_PATTERN = 3 ‘ Utilizada en caso que se desea un bitmap como tramado

lbColor : determina el color de la brocha.

LbHatch: determina el tipo de tramado. Puede ser:

Sus constantes son:

Private Const HS_BDIAGONAL = 3 ' /////

Private Const HS_CROSS = 4 ' +++++

Private Const HS_DIAGCROSS = 5 ' xxxxx

Private Const HS_FDIAGONAL = 2 ' \\\\\

Private Const HS_HORIZONTAL = 0 ' -----

Private Const HS_VERTICAL = 1 ' |||||

Queda claro que si “lbStyle” es BS_SOLID no importa que valor pongan en lbHatch, la API no lo tomara en cuenta.

Regresando con los parámetros de la API ExtCreatePen, tenemos:

dwStyleCount y lpStyle no son tratadas en este manual y son utilizadas para pinceles personalizados. Si quieren más información diríjanse a la pagina de MSDN en Internet.

Ustedes dirán: en el primer parámetro ¿como combino todas esas constantes?. Algunas API’S win32 se le puede pasar como parámetro varias constantes, para ello usamos el operador lógico OR. Es decir, si queremos un pincel Geométrico, Sólido, con ENDCAP_ROUND, y Join_Bevel sería algo así:

ExtCreatePen( PS_GEOMETRIC OR PS_SOLID OR PS_ENCAP_ROUND OR PS_JOIN_LEVEL, ……..)

Esta sería la manera de pasarle 4 constantes en un solo parámetro. Cada API win32 en su documentación especifica si la API puede con esta posibilidad.

Pueden notar que es el mismo principio que las otras, con cierta diferencia. Es evidente que las combinaciones que se pueden lograr con el primer parámetro son muchas y no cubro todas sino 4 de ellas. Ustedes pueden jugar con ellas como quieran. Claro esta y creo que es como obvio que no se podría usar por ejemplo:

PS_JOIN_LEVEL OR PS_JOIN_MITER esto es por que o es un estilo o es el otro, pero no puedes mezclar dos estilos del mismo grupo.

¿Que funciona en Windows 98 y que funciona en Windows 2000/NT/XP?.-

Según la documentación encontre lo siguiente:

· El grupo 1 sirve para todos los Windows.

· El grupo 2 tiene lo siguiente:

o PS_DASH: en Windows 95 no funciona para pinceles geométricos. En Windows 98/ME: no funciona.

o PS_DOT: en Windows 95/98/ME no funciona con pinceles geométricos.

o PS_DASHDOT y PS_DASHDOTDOT: la misma restricción que PS_DASH.

· El grupo 3 y 4 solo funciona para pinceles geométricos.

Ahora todo esto se le dejo para quien lo quiera examinar minuciosamente, algunas condiciones no me convencen. Ya que como pueden ver en el combo2, ninguna selección funciona en Windows 98/95/ME. Solo vi el efecto en Windows 2000 y supongo que también funcionara en XP y NT.

Pero solo me remito a especificar lo que dice la documentación de Windows, puede que sea yo el que no sabe usar muy bien los parámetros.

SISTEMA DE COORDENADAS Y MAPEADO

En esta sección necesito de su concentración ya que lo que voy a tratar es algo muy bonito e interesante pero el entenderlo implica un poco de esfuerzo por parte de nosotros, no tanto por las funciones API’S a tratar, sino que para hacerlas funcionar en VB tuve que valerme de un truco que estoy seguro que para la mayoría de ustedes será algo nuevo, así que ¡empecemos!.

Arriba mencione algo llamado “unidades lógicas” y “unidades físicas”, pero para llegar a que significa esto, vayamos a nuestro dispositivo de contexto HDC. Ustedes se han preguntado ¿Cuándo dibujábamos con la función RECTANGLE las coordenadas de los puntos, son píxeles?. Bueno la respuesta es si. Pero Windows permite dibujar en otro tipo de unidades, por ejemplo, podemos diseñar un rectángulo cuya medida en ves de ser píxel, puede ser milímetro o pulgadas. Es decir, veamos esta función:

Rectangle Me.hdc, 10, 10, 100, 100

Lo que quiero decir arriba es que las coordenadas 10,10,100,100 pueden ser píxeles, pulgadas, milímetros, o una escala personalizada. En este momento de seguro mas de uno esta sorprendido, ya que creían que si querían hacer algo por el estilo ustedes pensaron que tenían que programar el código de conversión, pues debo decirles que Windows posee lo que se llama un “motor de transformación”. Esto es curioso, ya que ustedes se pueden preguntar ¿Si yo tengo mis coordenadas en milímetros, como Windows sabe cuantos píxeles equivale cada milímetro, o como maneja todo eso?. Esto sería una pregunta interesante, ya que si se dan cuenta el monitor dibuja en unidades de PIXELES y no en milímetro, ni pulgadas ni nada por el estilo. Entonces es aquí donde Windows separa la cuestión en dos estructuras el VIEWPORT y WINDOW (Ventana).

La ventana (window) se mide en “unidades lógicas” , cuando me refiero a unidades lógicas me refiero, a unidades que pueden ser distinta de píxeles, como milímetro, pulgada, etc, y el viewport es medido en unidades físicas, es decir, PIXELES en caso del monitor, pero una unidad física de la impresora puede ser medida de otra manera, aunque en nuestro ejemplos usamos píxeles, ya que a la final todo es enviado al monitor; lo que hace Windows es una proyección desde la ventana al viewport.

Esa proyección es transparente para nosotros, el como Windows lo hace no importa. El motor de transformación de Windows esta formado por 4 espacios que veremos a continuación:

1.Espacio Mundo: este espacio es utilizado para efectuar transformaciones geométricas al dispositivo de contexto (HDC) seleccionado. Estas transformaciones son rotación, traslación, escalado, distorsión, espejo. Lamentablemente este espacio solo esta disponible en Windows 2000/XP/NT, los demás sistemas no poseen este espacio de trabajo.

2.Espacio Pagina: en este espacio es donde establecemos los unidades lógicas a utilizar, o véanlo como el lugar donde dibujamos nuestras figuras utilizando las unidades lógicas.

3.Espacio dispositivo: en este espacio se prepara el dispositivo de contexto para ser visualizado en la pantalla.

4.Espacio dispositivo físico: nos referimos a la unidad física que maneja el hardware en cuestión, si hablamos del monitor pues será píxeles, se es una impresora sería otra medida, etc. Ya aquí se muestra al cliente el resultado final, que es en pocas palabras lo que el cliente ve.

¿Qué es esto de espacio?. Cuando dibujamos en Windows, él pasa por los espacios 2,3,4, lo que sucede es que ustedes no se dan cuenta por que eso transparente tanto para el usuario como para nosotros, aunque de aquí en adelante estudiaremos el espacio 1 y 2, ya que son los únicos espacios que se pueden alterar, ya que el 3 y el 4 dependen mas de hardware que de software.

¿Por qué el espacio 1 no esta predeterminado?. Por el simple hecho de que no todas las versiones de Windows lo soporta, por lo tanto hay que activarlo.

También es importante saber que por ejemplo en nuestra ventana no necesariamente se debe de configurar el píxel o punto 0,0 en la parte superior izquierda, a nivel de hardware siempre va ser el 0,0 la parte superior izquierda, pero a nivel lógico el sistema de coordenadas puede cambiar pero ya hablaremos de eso mas adelante.

Una representación grafica de lo que hace Windows es algo por el estilo:

Básicamente lo que hace es lo siguiente, toma la función GDI, la cual se le pasa parámetros en unidades lógicas, llega al motor de conversión, si esta activado el espacio mundo, aplica las transformaciones solicitadas, en nuestro ejemplo es una transformación de distorsión, luego pasamos del espacio mundo al de pagina, aquí la imagen puede crecer o empequeñecer dependiendo de la escala programada (noten que nuestro sistema de coordenada no es necesariamente el 0,0 en la esquina superior izquierda), ya en el espacio de dispositivo la imagen se prepara para ser presentada, vean que el sistema de coordenada cambia al sistema conocido, luego se aplica por último la escala de visualización, es decir, el tamaño real que vera el usuario en su monitor, impresora, plotter, etc, y ya al final se muestra al usuario la imagen obviamente en unidades de píxeles si hablamos del monitor. En caso de no estar activado el espacio mundo, todo el proceso empieza en el espacio pagina.

Cabe mencionar que este proceso puede ir en orden inverso, es decir del hardware, al dispositivo de contexto.

Todos deben de estar esperando el proyecto de ejemplo, para ver como funciona las API que trabajan en todo este proceso, pero antes de eso necesito explicar algo.

En este proyecto verán que necesitamos un modulo, ¿Por qué?. Como todo sabemos VB tiene un formulario ya configurado cuando abren un proyecto, ese formulario tiene eventos, pero ahora necesito que recuerden el código inicial hecho en C, se recuerdan de la función “GestordeMensajes”, que yo les dije que era nuestro oído, que todos los mensajes que eran enviados a nuestra aplicación pasaban por esa función, es evidente ver que el formulario de VB no gestiona los mensajes de esa manera (desde nuestro punto de vista), ya que internamente el formulario debe de tener una función que gestiona los mensajes, lo que sucede es que es transparente para nosotros.

¿A que quiero llegar?. Existe un mensaje llamado WM_PAINT, este mensaje es enviado a nuestra ventana cuando ella requiere ser repintada, ya sea por que se mueve, o por que una ventana tapo a la nuestra, por X situación, internamente el formulario de VB debe gestionar el repintado del formulario, cosa o procedimiento transparente para nosotros, el problema que me encontré al trabajar en VB con las funciones correspondiente a este tema, es que de alguna manera el como gestiona VB el mensaje WM_PAINT, hacia que no funcionaran correctamente las funciones API’S, lo que producía errores extraños a las cuales le tuve que buscar solución.

Una de la solución fue crear mi propio “oído”, es decir, una función que capturara el mensaje WM_PAINT, y el pintado del formulario ya no quedaba bajo la responsabilidad de VB, sino bajo mi responsabilidad, es decir, en este proyecto nosotros somos los encargado de procesar lo que el HDC del formulario va a mostrar. Ya que si dejaba que el gestor que coloca VB para ello controlara el pintado, los resultados con estas funciones no eran óptimos.

Por esa razón, en este proyecto veremos funciones extrañas que no tienen que ver con el tema que tratamos pero que son necesaria para crear nuestro propio “oído”.

Para verlo mas claro vayamos al código. Para este proyecto necesitan de un formulario, con un control ComboBox, llamado combo1, traten de poner mas o menos grande el formulario y el combo pónganlo en la parte derecha del mismo, y van a crear un modulo, llamado “modulo1”.

En el formulario ponen este código:

Ver Codigo

Las siguientes funciones están afuera del contexto que queremos explicar acerca del sistema de coordenadas y modo de mapeo: SetWindowLong, CallWindowProc, BeginPaint, EndPaint. La pregunta principal debe de ser ¿Cómo creamos nuestro oído?.

Cabe mencionar lo siguiente, ya explique con anterioridad que VB coloca una función que gestiona los mensajes que nosotros no vemos, en ves de eso VB nos presenta ciertos eventos en donde nosotros ponemos nuestro código, pero estos eventos los coloca como funciones separadas y no juntas como en realidad sucede.

Como explique cuando mostré el código en C en el primer capitulo, toda ventana tiene una función que sirve como “oído” de la aplicación, en realidad es una función situada en memoria, lo que vamos hacer no es eliminar dicha función, sino crear otra función que sirva para “escuchar” y luego mediante un API cambiar nuestro “oído” viejo por el nuevo.

Para empezar vayamos a nuestro formulario, en la parte de Form_load y verán escrita esta línea:

Cambiar_Proc (Me.hwnd)

Ahora vayamos al modulo:

Sub Cambiar_Proc(hwnd2 As Long)

nuevoproc = ObtenerLong(AddressOf MenuProc)

viejoproc = SetWindowLong(hwnd2, GWL_WNDPROC, nuevoproc)

End Sub

¿Que hacemos con la variable “nuevoproc”?, lo que estamos haciendo es obtener una referencia de la ubicación en memoria de la función “Menuproc”, para ello sirve el comando AddressOf, la función ObtenerLong no es nada del otro mundo, es la función expresada a continuación:

Function ObtenerLong(ByVal direcfunt As Long) As Long

ObtenerLong = direcfunt

End Function

Lo que hace es convertir en un tipo long el valor que obtiene AddressOf.

Vean que nuestra nueva función, se llama MenuProc que en el modulo esta declarada así:

Private Function MenuProc(ByVal hwnd As Long, ByVal message As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Noten que esta función sirve como “oído”, ya que la estructura se presta para ello, ya que tiene los parámetros mencionados en el segundo capitulo, que son necesarios para hacer que nuestra aplicación “escuche”.

Ya con la ubicación de memoria o referencia almacenada en la variable “nuevoproc” utilizamos la función:

viejoproc = SetWindowLong(hwnd2, GWL_WNDPROC, nuevoproc)

con esta función cambiamos nuestro “oído” viejo por uno nuevo, vean que como primer parámetro tenemos el HWND de nuestra ventana, como segundo parámetro establecemos la acción que queremos hacer, en este caso GWL_WNDPROC el cual es utilizado para manipular la función de mensajes, y como tercer parámetro le pasamos la dirección de la nueva función. Es fácil darse cuenta que lo que retorna la función es la dirección de la función vieja, ósea, la que VB configura por defecto. Esto quiere decir que ahora nuestra aplicación tiene 2 funciones aptas para escuchar mensajes, pero solo una esta activada por defecto, que en nuestro caso es “nuevoproc”, que referencia a la función “MenuProc”.

¿Cómo escuchamos?.

Recuerdan que yo dije que para este proyecto funcionara bien teníamos que controlar de manera independiente el mensaje WM_PAINT, bueno creo que es fácil ver, que el IF hace eso. Aquí lo que estoy haciendo es que SI MESSAGE = WM_PAINT (ENTONCES YO ME ENCARGO DE PINTAR) DE LO CONTRARIO (ELSE) QUE LA FUNCIÓN VIEJA SE ENCARGE DEL MENSAJE

If message = WM_PAINT Then

Dim p As PAINTSTRUCT

hdc = BeginPaint(hwnd, p)

.

.

.

EndPaint hwnd, p

Else

MenuProc = CallWindowProc(viejoproc, hwnd, message, wParam, lParam)

End If

Presten atención a lo que esta en azul; esta función (MenuProc) es la encargada de “escuchar”, pero yo no quiero capturar todos los mensajes ¿Por que?. por que sería un fastidio, ya VB me tiene una función ya configurada con esas cosas, lo que hago es que si MESSAGE es diferente a WM_PAINT se ejecuta la línea en azul, esta línea que usa CallWindowProc, es una función que se encarga de llamar a otra función que esta en capacidad de recibir un mensaje, y le dejo a la función vieja el trabajo de todos los demás mensajes. Ya que pueden observar que entre sus parámetros esta la variable “viejoproc”. Aislando solamente el mensaje WM_PAINT.

Vean en el código de arriba que hay dos funciones y una estructura, BEGINPAINT, ENDPAINT y la estructura PAINTSTRUCT. Cuando uno pinta por el mensaje WM_PAINT uno tiene que preparar a la ventana para ser pintada y la manera de prepararla es con BEGINPAINT, luego de haber pintado cerramos con ENDPAINT y así le decimos a la ventana que esta lista para dibujarse, la estructura PAINTSTRUCT solo sirve de carácter informativo.

Mas o menos espero que hayan entendido un poco lo que hice, yo no voy a explicar esas funciones API’S mencionadas arriba, todavía, ya que esas funciones tienen un apartado especial en otra sección, solo necesito que sepan por encima lo que hice ya que lo que realmente voy explicar de ahora en adelante, va ser el código que hay entre BEGINPAINT y ENDPAINT, ese código es lo referente al tópico que estamos estudiando. Las funciones restantes que me sirvieron como ayuda para lograr mi meta en este capitulo serán estudiadas en sus respectivo tópicos o secciones mas adelante.

ESPACIO MUNDO

Este espacio que solo puede ser utilizado en sistemas operativos Windows 2000/NT/XP, es utilizado para hacer transformaciones geométricas al HDC.

Cabe mencionar que estas transformaciones se aplican a través de matrices, que en nuestro caso será una estructura de tipo XFORM, la cual llenándola de manera apropiada realiza cierta transformación.

Translación: esta transformación es fácil de entender ya que nada mas consta de trasladar nuestro HDC especificando el desplazamiento en el eje x e y.

Siendo dx y dy, el desplazamiento horizontal y vertical respectivamente.

Escalado: transformación utilizada para escalar una imagen, logrando efectos de ampliación y disminución del tamaño del HDC.

Siendo dx y dy, el factor de multiplicación en el eje x e y respectivamente.

Rotación: transformación utilizada para rotar el HDC.

Siendo el ángulo de giro.

Distorsión: transformación utilizada para distorsionar un HDC.

Siendo x e y , la distorsión unitaria en su respectivos ejes.

Espejo: transformación utilizada para reflejar.

Si rx = -1 y ry = 1 se hace un reflejo horizontal

Si rx = 1 y ry = -1 se hace un reflejo vertical.

Los únicos valores posibles para rx y ry son 1 y –1

Funciones involucradas:

SetGraphicsMode

Api: Declare Function SetGraphicsMode Lib "gdi32" (ByVal hdc As Long, ByVal iMode As Long) As Long

Esta función es la encargada de activar el espacio mundo, por defecto sin importar el sistema operativo que se use, las transformaciones empiezan desde el espacio pagina, si se desea aplicar las transformaciones mencionadas, hay que utilizar esta función.

Como primer parámetro se le pasa el dispositivo de contexto, y como segundo parámetro se le pasa la constante:

Const GM_ADVANCED = 2

Pueden darse cuenta que en las opciones desde el 6 hasta el 11 del SELECT CASE colocado en la función MenuProc, utilizo esta función para activar el espacio mundo, queda entendido que estas transformaciones no sirven en Window 98/95/ME.

SetWorldTransform

Api: Declare Function SetWorldTransform Lib "gdi32" (ByVal hdc As Long, lpXform As XFORM) As Long

Función utilizada para realizar las transformaciones geométricas mencionadas arriba, como parámetro tiene el HDC al cual se aplicara la transformación que se le pasa en la estructura “lpXForm” que es del tipo XFORM. Los valores de esta estructura deben estar acorde a los parámetros explicados arriba, según sea la transformación que se quiera aplicar.

En los ejemplos notaran que primero lleno la estructura “matriz” luego activo el espacio mundo con la función ya mencionada y luego realizo mi transformación, Ej.:

SetWorldTransform hdc, matriz

Después que aplicas tu transformación procedes a dibujar lo que quieras dibujar, en nuestro caso dibujo un rectángulo.

CombineTransform

Api: Declare Function CombineTransform Lib "gdi32" (lpxformResult As XFORM, lpxform1 As XFORM, lpxform2 As XFORM) As Long

Algunas veces queremos aplicar mas de una transformación al mismo tiempo, y para ello hay dos opciones o aplican álgebra para lograr una matriz transformada o utilizan la función que le da Windows que en este caso es CombineTransform.

Vean que tiene nada mas 3 parámetros del tipo XFORM, de los cuales el primero es la matriz resultante de combinar las transformaciones de la estructura “lpxForm1” y “lpxForm2”.

El ejemplo en el código esta en la opción 11 del SELECT CASE y es así:

CombineTransform matriz, matriz2, matriz3

Donde “matriz”, es la estructura que posee los valores ya transformados de la matrices “matriz2” y “matriz3”.

En mi caso combine la translación con rotación.

ESPACIO PAGINA

En el espacio de pagina, Windows se encarga de realizar las conversiones pertinentes para luego enviar la información al dispositivo, dependiendo del sistema de mapeo seleccionado.

¿Qué es sistema de mapeo?. Es como determinar que medidas se va usar para graficar, es intuitivo pensar que cuando dibujamos lo estamos haciendo con una medida la cual es píxel; pero Windows permite la utilización de otros tipos de medidas e inclusive permite la creación de medidas personalizadas.

Modos de Mapeo

Cuando me refiero a “independiente” me refiero a que es independiente del dispositivo. Con esto podemos ver que cuando configuramos las coordenadas como MM_HIMETRIC, por ejemplo, las medidas que toman las funciones de dibujo son en milímetros, es decir, cada píxel equivale en este caso a 0,01 milímetro.

Antes de hablar de las funciones encargadas de este trabajo, vamos a hablar del sistema de coordenada, nosotros estamos acostumbrados a que la coordenada 0,0 es la esquina superior izquierda, pero esto no tiene por que ser así, se puede cambiar para formar un eje como el que usamos en matemáticas, y me refiero al eje de los positivos y negativos, con el sistema de coordenadas por defecto tenemos nada mas a nuestra disposición los valores positivos y aunque los valores negativos están, no lo vemos ya que están fuera del área de visualización del usuario.

Algunos sistema de mapeo funcionan como el sistema tradicional, es decir, los positivos del eje X están a la derecha y los del eje Y están hacia arriba, y no como estamos acostumbrados donde el los positivos del eje Y están hacia abajo.

MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC, MM_TWIPS, MM_HIENGLISH = el eje X se incrementa izquierda a derecha, y el eje Y de abajo hacia arriba, como el típico sistema matemático.

MM_TEXT = el eje X se incrementa izquierda a derecha, y el eje Y de arriba hacia abajo.

Esto lo menciono ya que primero voy explicar la función encargada de mover el sistema de coordenadas.

SetWindowOrgEx

Api: Declare Function SetWindowOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI) As Long

SetViewportOrgEx

Api: Declare Function SetWindowOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI) As Long

Estas funciones hacen exactamente lo mismo pero una trabaja en el espacio de pagina (SetWindowOrgEx) y la otra funciona solo en el espacio de dispositivo (SetViewportOrgEx). Ahora ¿Qué es eso de ViewPort y Window? Aunque arriba explique un poco esto, no importa, lo vuelvo a explicar. Véanlo así, la ventana (window) no es mas que una proyección al Viewport, ¿Qué esto?. Ustedes trabajan en un HDC, el cual puede estar configurado con un sistema de coordenada distinto, al sistema de coordenada del viewport, el cual es la imagen final que ve el cliente en su pantalla, pueden tener un sistema de coordenada en el espacio pagina, con valores negativos, pero si dejan el viewport como esta por defecto, puede ser que lo que tengan en esa área, no se muestre ya que lo que él hace es una proyección de un lugar a otro, inclusive en el HDC la figura puede ser muy pequeña y en el momento de mostrarlo por pantalla se puede ver grande, todo depende de cómo se configure todo esto, pero eso le veremos mas adelante.

Estas funciones fijan el centro de nuestro sistema de coordenada en el valor X e Y especificado, el parámetro “lpPoint” sirve para almacenar el punto anterior donde estaba el sistema de coordenadas.

Vean que en nuestro código es así:

SetViewportOrgEx hdc, 10, 10, punto

Lo que hago aquí es mover el centro de nuestro sistema de coordenada que inicialmente esta en el 0,0 al punto 10,10, es decir, que si en nuestro HDC se encuentra algo dibujado en el punto 0,0 cuando el se proyecte al espacio de dispositivo no va ir al punto 0,0, sino que empieza en el punto 10,10, ¿Lo ven?. Fíjense que yo no cambie el sistema de coordenada de nuestro HDC, nuestro HDC maneja el sistema tradicional 0,0 esquina superior izquierda, solo cambie el del espacio de dispositivo.

Yo se que suena todo como complicado, pero pónganse a cambiar valores, jueguen con todo esto y de seguro entenderán mucho mas.

OffsetViewportOrgEx

Api: Declare Function OffsetViewportOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI) As Long

OffsetWindowOrgEx

Api: Declare Function OffsetWindowOrgEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpPoint As POINTAPI) As Long

Alguna veces no queremos mover el sistema de coordenada de un punto a otro de manera arbitraria, sino queremos desplazarlo, estas funciones tienen los mismos parámetros que las dos funciones mencionadas arriba con la diferencia que estas dos funciones desplazan el origen. Veamos un ejemplo:

Imaginemos que tenemos el origen en 10,10 y hago esta instrucción:

SetViewportOrgEx hdc, 20, 20, punto

En este momento nuestro origen esta en el punto 20,20, pero si hago esto:

OffsetViewportOrgEx hdc, 20, 20, punto

Nuestro origen esta en el punto (30,30), ¿Ven por que?. Esta función desplaza al origen dependiendo los parámetro que ponen en “nX” y “nY”, desplazar es como sumarle al punto actual.

Estas funciones no las uso en el programa pero es importante explicarlas.

SetMapMode

Api: Declare Function SetMapMode Lib "gdi32" (ByVal hdc As Long, ByVal nMapMode As Long) As Long

Función encargada de establecer el sistema de mapeo que se quiera usar para el HDC escogido, el cual se le pasa como primer parámetro. “nMapMode”: en el se le coloca la constante correspondiente de la tabla Modos de Mapeo la cual mencione arriba.

Viendo un ejemplo:

SetMapMode hdc, MM_HIENGLISH

SetViewportOrgEx hdc, 50, 50, punto

Rectangle hdc, 0, 0, 100, 100

Aquí cambio nuestro sistema a pulgadas, es decir un píxel ahora valdrá 0,001 pulgadas, es esa la razón del por que ven cuadros con distintos tamaños. Vean que el API Rectangle en casi todos los ejemplos tiene como coordenadas (0,0,100,100). Aquí este 100 no se refiere a píxeles sino pulgadas.

Esta explicación vale para cada opción ¿Por qué moví el centro del eje de coordenadas?. Recuerdan que arriba les dije como se orientan los ejes dependiendo del modo de mapeo escogido, lo que sucede es que si ponen en comentario esta línea no verán el rectángulo ¿Por qué?, por que el 100, correspondiente al eje Y va orientado hacia arriba y no hacia abajo como estamos acostumbrados.

SetWindowExtEx

Api: Declare Function SetWindowExtEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Size) As Long

SetViewportExtEx

Api: Declare Function SetViewportExtEx Lib "gdi32" (ByVal hdc As Long, ByVal nX As Long, ByVal nY As Long, lpSize As Size) As Long

Cuando usamos el modo de mapeo MM_ISOTROPIC o MM_ANISOTROPIC, estamos diciéndole que vamos a personalizar las medidas de nuestro sistema de coordenadas, con SetWindowExtEx establecemos las medidas en nuestro HDC y con SetViewportExtEx establecemos las medidas del espacio de dispositivo.

Los parámetros son iguales a los anteriores así que me evito repetir lo mismo.

Para entender como es esto veamos el ejemplo del programa:

SetWindowExtEx hdc, 396, 197, oldsize1

SetViewportExtEx hdc, 792, 394, oldsize2

Rectangle hdc, 0, 0, 198, 98

Para entender los números, primero tengo que decir que el formulario donde hice el ejemplo tenia de ancho 12000 y de alto 6315, lo equivalente en píxel es 792 x 394, vean que al viewport le estoy asignando ese mismo tamaño, y que al HDC o espacio de pagina le asigno la mitad de los valores.

Si ustedes ponen el formulario del mismo tamaño verán el efecto, cuando arrancan y seleccionan esa opción verán un rectángulo que cubre la mitad de ancho del formulario y la mitad de alto.

¿POR QUÉ?. Si la mitad de 792 es 396 y la de 394 es 197, por que con las coordenadas 198, 98 se llego hasta la mitad, suena como ilógico, pero no lo es, lo que hice fue que cada píxel en el HDC equivale a 2 píxeles en el Viewport ¡GUAO! dirán!, pongan ahora en ves de 198, ponen 396 verán que se llena todo el ancho del formulario ¡GUAO! de nuevo, ven lo que sucede, esto no es mas que relacionar un sistema de coordenadas del espacio de pagina con el sistema de coordenadas del espacio de dispositivo, con esto pueden lograr efectos muy bueno a nivel de ZOOM o en programas que requieran medidas personalizadas. Pare entender esto, pónganse a cambiar los valores.

A partir de ahora vamos a ver funciones que se utilizan para dibujar, las coordenadas de esta funciones son en unidades lógicas, no menciono eso, ya que sería repetir lo mismo para cada función API por lo que aviso desde ahorita, tomen esto como una regla, todo función que tenga un HDC, las coordenadas que maneja vienen en unidades lógicas, “lógicas”significa que dependerá del modo de mapeo seleccionado o configurado. Hay API’S que son excepciones pero son muy pocas, de todas maneras cuando lean en la documentación oficial verán la palabra “unit logical” = “unidades lógicas” y ya sabrán de lo que significa.

NOTA: pueden que al modificar el código, se hayan encontrado que si se equivocaron en el nombre de una variable o escribieron mal algo, VB los saca de manera arbitraria del código, en Windows 98 he notado que me sale un error y me saca, y en Windows 2000 lo que hace es cerrarme el programa, si las personas no saben de lo que hablo, pongan un nombre de variable que no exista en el código. Al correr el programa se darán cuenta del error, en realidad no se por que la causa del mismo, he de suponer que ha VB no le gusta que cambie el Gestor de mensajes “oído”, por lo que encuentra un conflicto interno cuando quiere lanzar el error y se da cuenta que el gestor de mensajes es otro. Pero esto es un detalle que no implica mal funcionamiento. Inclusive que esto no les de miedo de hacer una aplicación usando estas funciones, mientras programen bien, quitando todos los errores posibles no habrá problema. Ahora si alguien pudo codificar estas transformaciones utilizando el formulario sin la necesidad de cambiar la función que gestiona los mensajes le agradecería que me dijera como lo hizo, ya que yo trate y no se por que no funcionaba. De todas maneras seguiré buscando la causa.

FIGURAS GEOMÉTRICAS

Ahora vamos a introducir algunas figuras geométricas que posee Windows, para este proyecto solo necesito un formulario y un ComboBox llamado “Combo1”, traten de colocar el ComboBox pegado a la derecha del formulario.

Luego que tengan esto listo peguen este código en el formulario:

Ver Codigo

Como ya habrán visto, al moverse por el ComboBox, el formulario visualiza cada figura, las cuales explicamos sus API’S a continuación (cabe destacar, que todas las API’S reciben entre sus parámetros el HDC o dispositivo de contexto en donde se dibujara la imagen):

Chord

Api: Declare Function Chord Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long, ByVal X4 As Long, ByVal Y4 As Long) As Long

Para visualizar más claramente el papel que juega cada parámetro vean la figura:

Como pueden ver la figura habla por si sola, cabe destacar que no importa que vean un circulo, elipse, pie, etc, todo esas figuras se encuentran enmarcadas por un rectángulo. Los puntos X3,Y3, X4, Y4 son lo que determinan donde corta la recta a la elipse.

Ellipse

Api: Declare Function Ellipse Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

Función API sencilla como pueden ver, lo único que hay que pasarle como parámetro son las coordenadas del rectángulo en la cual va estar encerrada la elipse.

FillRect

Api: Declare Function FillRect Lib "user32" (ByVal hdc As Long, lpRect As rect, ByVal hBrush As Long) As Long

Esta se encarga de mostrar un rectángulo con relleno, como vimos arriba para rellenar una figura necesitamos una brocha, la diferencia de esta API con las vistas anteriormente, es que la brocha no se configura a un dispositivo de contexto (HDC), sino a la figura misma, el hecho de que usemos una brocha tramada, de color, y texturizada para esta función, no implica que el HDC tome esa brocha.

Las coordenadas del rectángulo son pasadas por la estructura de tipo RECT.

InvertRect

Api: Declare Function InvertRect Lib "user32" (ByVal hdc As Long, lpRect As rect) As Long

En FILLRECT vieron un rectángulo amarillo, y InvertRect observaron un rectángulo azul, lo que hace InvertRect es pintar un rectángulo, pero el color que toma, es el color de los píxeles pero invertidos, que se encuentren en el HDC en cuestión. Dejando en claro que el agarra solo los píxeles del HDC en donde se va a dibujar el rectángulo con InvertRect.

¿Que significa invertir?. El color amarillo es: RGB(255,255,0) cada color es un byte. Lo que se traduce en:

Amarillo          11111111   11111111   00000000

Si invertimos  00000000   00000000   11111111

Lo que equivale en RGB(0,0,255), y aquí esta el color azul que muestra el programa, de todas manera estas operaciones booleanas lo visualizarán mas claramente con la API BitBlt.

Pie

Api: Declare Function Pie Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long, ByVal X4 As Long, ByVal Y4 As Long) As Long

Los parámetros cumplen exactamente el mismo patrón que la figura mostrada en CHORD, con la diferencia que muestra lo que no muestra chord, si se dan cuenta, es como lo contrario, Chord muestra lo que Pie no muestra, y viceversa.

Polygon

Api: Declare Function Polygon Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long

Un polígono es una figura de X cantidad de vértices y cada vértice es conectado por una recta, esto se puede ver como una definición simple de polígono, lo que hacemos en la función es pasarle como parámetros:

lpPoint = es el arreglo que contiene a los puntos

nCount = la cantidad de puntos del polígono.

Luego Windows se encarga de conectarlos trazando una línea entre los vértices o puntos.

coordPoly(1).X = 50

coordPoly(1).Y = 10

coordPoly(2).X = 25

coordPoly(2).Y = 100

coordPoly(3).X = 75

coordPoly(3).Y = 100

Polygon Me.hdc, coordPoly(1), 3

Como pueden ver en el ejemplo usamos un arreglo cuyos valores son estructuras de tipo POINT

Si ustedes configuran un arreglo con 100 puntos y ponen 40 en el parámetro “nCount” la función solo dibujara los primeros 40 puntos del arreglo.

PolyPolygon

Api: Declare Function PolyPolygon Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, lpPolyCounts As Long, ByVal nCount As Long) As Long

Se podría decir que esta función permite crear más de un polígono de manera independiente, que quiero decir, en el código lo puse como si fuera uno solo. Pero ahora pongan este código:

coordPoly2(1).X = 50

coordPoly2(1).Y = 10

coordPoly2(2).X = 25

coordPoly2(2).Y = 100

coordPoly2(3).X = 75

coordPoly2(3).Y = 100

vertPoly(0) = 3

coordPoly2(4).X = 50

coordPoly2(4).Y = 150

coordPoly2(5).X = 50

coordPoly2(5).Y = 80

coordPoly2(6).X = 100

coordPoly2(6).Y = 120

vertPoly(1) = 3

PolyPolygon Me.hdc, coordPoly2(1), vertPoly(0), 2

Como segundo parámetro (lpPoint) se pasa los puntos que conforman a todos los polígonos, como tercer parámetro (lpPolyCounts) se le especifica la cantidad de puntos que posee cada polígono, y como último parámetro, se le pasa la cantidad de polígonos. Si corrieron el programa con el código cambiado habrán visto 2 triángulos.

¿Por qué?

Configure primero 3 puntos, y vertPoly(0) = 3, con esto quiero decir que el primer polígono tiene 3 puntos. Luego configure otros 3 puntos y le dige a vertPoly(1) = 3, lo que significa que el segundo polígono también tiene 3 puntos, paso los parámetros y en el último parámetro pongo 2, y con esto le digo a la función, que todo esta adaptado para 2 polígonos.

A primera vista puede ser difícil de ver, pero hagan pruebas para que clarifiquen sus dudas.

Rectangle

Api: Declare Function Rectangle Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

Creo que ya la hemos usado bastante para que sepan como funciona.

RoundRect

Api: Declare Function RoundRect Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long) As Long

Función que dibuja un rectángulo cuyos bordes son redondeados; al igual que Chord usare un grafico para ilustrar los parámetros:

LÍNEAS Y CURVAS

Vamos a introducir algunas funciones que nos permitirán utilizar líneas y curvas, en este caso curvas de Bezier. Dejo claro que las siguientes funciones no funcionan en Windows 95/98/ME: ArcTo, PolyDraw y AngleArc.

Para el proyecto nada mas necesitamos un combobox, llamado Combo1, el cual colóquenlo a la derecha del formulario y peguen este código:

Ver Codigo

Voy a explicar las funciones según el orden de colocación en el ComboBox.

MoveToEx

Api: Declare Function MoveToEx Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, lpPoint As Any) As Long

LineTo

Api: Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

Estas funciones las coloco así, ya que para entenderlas hay que explicar las dos al mismo tiempo. En algunas funciones que habrán visto en el código verán que algunas terminan en TO. ¿que significa ese TO?: Windows tiene un puntero grafico, el cual esta colocado inicialmente en el punto 0,0, ustedes dirán ¿Puntero grafico?. En una base de datos el puntero se encuentra dirigido al registro activo en ese momento. Por lo que nuestro “puntero grafico” esta dirigido al píxel en el cual Windows empezara a dibujar.

Esto tiene efecto nada mas en las funciones que terminen en TO, funciones graficas que no terminen en TO, no hacen caso a ese puntero grafico, y dibujan según las coordenadas pasadas a la función respectiva.

Ahora es mas claro ver, el por que la función LINETO solo tiene un X y un Y, ya que el otro punto para formar la recta es el “puntero grafico”, que mencione arriba.

MOVETOEX, es el API encargada de mover ese “puntero grafico”, pónganla en comentario y corran el programa verán que la línea dibujada parte desde el 0,0 hasta el primer punto 150,30.

Esta función (MOVETOEXE) toma 4 parámetros: el dispositivo de contexto, el X y Y donde trasladamos el “puntero grafico”, y como cuarto parámetro le podemos pasar una variable de tipo POINT, el cual almacena el punto “viejo” donde estaba el “puntero grafico”

LINETO, es una función que traza una línea desde el “puntero grafico”, hasta el punto especificado en el X y Y que tiene entre sus parámetros. Como esta función tiene el TO, esto significa que esta función mueve el puntero grafico, después de dibujar la línea.

Veamos lo que hicimos:

MoveToEx Me.hdc, 30, 30, ByVal 0& ‘Movemos el puntero grafico del 0,0 al punto 30,30

LineTo Me.hdc, 150, 30 ‘Dibujamos una línea desde el punto 30,30 hasta 150,30, ahora el “puntero grafico” se encuentra en 150,30.

LineTo Me.hdc, 30, 100 ‘Dibujamos una línea desde el punto 150,30 hasta 30,100, ahora el “puntero grafico” se encuentra en 30,100.

¡claro!, espero que si, ya que vamos a ver mas funciones que siguen esta metodología.

AngleArc

Api: Declare Function AngleArc Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal dwRadius As Long, ByVal eStartAngle As Single, ByVal eSweepAngle As Single) As Long

Esta función es utilizada para hacer un circulo pero el cual tiene una parte que no se dibuja, parecido a un pacman, o una torta redonda cortada.

Entre sus parámetros tenemos:

Hdc = dispositivo de contexto en el cual se dibuja

x e y = coordenadas de donde se empieza a dibujar la circunferencia.

dwRadius = radio de la circunferencia.

eStartAngle = Angulo en donde empieza el barrido.

eSweepAngle = Angulo que barre a partir del angulo especificado en el parámetro de arriba.

¿Qué es eso de barrer?

Viendo nuestro ejemplo puedo explicar la pregunta

AngleArc Me.hdc, 100, 100, 100, 0, 180

Lo que estoy haciendo aquí es, un circulo de x e y igual a 100, de radio 100, y hace un barrido desde el angulo 0 hasta el angulo 180. Por esa razón solo ven la mitad de la circunferencia. Si ponen en ves de 0 colocan 90 verán ¾ partes de la circunferencia.

Arc

Api: Declare Function Arc Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long, ByVal X4 As Long, ByVal Y4 As Long) As Long

Esta función como pueden ver pinta el arco de una circunferencia, los 4 primeros parámetros después del HDC determinan la ubicación de la circunferencia, los últimos cuatros determinan el arco que no se ve.

¿Cómo se determina el arco que no se ve?

Las coordenadas X3 y Y3 determinen el punto final de una recta que va desde el radio del círculo hasta ese punto. Luego el punto X4 y Y4 determinan otro punto final de otra recta que va desde el radio hasta ese punto, luego Windows de alguna manera detecta los dos puntos de intersección y no dibuja el arco que hay entre ellos.

Si están confundidos pongan este código antes de la función Arc:

MoveToEx Me.hdc, 100, 100, ByVal 0&

LineTo Me.hdc, 50, 50

MoveToEx Me.hdc, 100, 100, ByVal 0&

LineTo Me.hdc, 400, 50

Con esto deben tenerlo mas claro, la interpretación de estas cuatro líneas de arriba se las dejo a ustedes para forzarlos a entender como funciona esta función.

ArcTo

Api: Declare Function ArcTo Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long, ByVal X4 As Long, ByVal Y4 As Long) As Long

Lo mismo que arc con la diferencia que mueve el “puntero grafico”.

PolyBezier

Api: Declare Function PolyBezier Lib "gdi32" (ByVal hdc As Long, lppt As POINTAPI, ByVal cPoints As Long) As Long

Esta función nos grafica las curvas de Bezier, se podría decir, que esta función nos permite crear cualquier tipo de curva sin restricciones, ahora, para formar esta curva inicialmente necesitamos 4 puntos, ya que necesitamos un punto de inicio, uno final y mínimo 2 de control.

¿Qué es punto de control?

Esos son los puntos que controlan la curva o la forma de la curva propiamente dicha, es decir, controlan al ancho de la curva, o abertura de la misma.

Como parámetros tenemos lo siguiente: como siempre tenemos nuestro hdc, pero vean que tenemos una estructura de tipo POINTAPI, a la cual no se le pasa una estructura propiamente dicha, sino un arreglo que esta formada en su interior por esa estructura, si vemos el ejemplo:

puntos(0).X = 10

puntos(0).Y = 100

puntos(1).X = 200

puntos(1).Y = 300

puntos(2).X = 300

puntos(2).Y = -100

puntos(3).X = 500

puntos(3).Y = 100

Vean como cada índice posee dos variables esto se debe a que declaramos a “puntos” como un arreglo de 6 POINTAPI:

Dim puntos(0 To 5) As POINTAPI

Ustedes dirán: ¡un momento Eduardo! este arreglo tiene 6 casillas y llenastes solo 4, pero aquí es donde juega el tercer parámetro de la función el cual es “cPoints” aquí le pasamos la cantidad de puntos que queremos que procese la función. Yo puedo tener un arreglo de 100 espacios pero puedo usar los primeros 5 y solo tengo que poner a cPoints = 5 y listo!, el agarra solo los 5 primeros espacios del arreglo y obvia al resto.

Lo que sucede es que lo declare de 6 para la función que vamos a ver mas adelantes.

Si quieren ver como puede cambiar la forma de la curva cambien los valores de los puntos 1 y 2, para que vean las diferentes formas que se crean.

NOTA: para realizar una curva de Bezier se pueden usar X cantidad de puntos es decir, puedes usar 10, 20, 30 puntos siempre que no sea 3 o 2.

PolyBezierTo

Api: Declare Function PolyBezierTo Lib "gdi32" (ByVal hdc As Long, lppt As POINTAPI, ByVal cCount As Long) As Long

Exactamente la misma explicación que POLIBEZIER con la diferencia de que esta función termina en TO, lo que significa que mueve al “puntero grafico”. Y como podrán ver, aquí si uso los 6 puntos.

PolyDraw

Api: Declare Function PolyDraw Lib "gdi32" (ByVal hdc As Long, lppt As POINTAPI, lpbTypes As Byte, ByVal cCount As Long) As Long

Esta es un API multifuncional, ¿por que multifuncional?, con esta API se puede usar simultáneamente LINETO, MOVETO y POLYBEZIERTO. Los parámetros son lo siguientes:

lppt = son los puntos que forman a la figura.

lpbTypes = esto se usa para darle a conocer a la función “el para que sirve el punto”.

Valores que puede tomar:

Const PT_LINETO = &H2

Const PT_BEZIERTO = &H4

Const PT_CLOSEFIGURE = &H1

Const PT_MOVETO = &H6

cCount = al igual que las anteriores funciones establece la cantidad de puntos que forman la figura.

Para entenderlo mejor veamos el ejemplo:

puntos(0).X = 10

puntos(0).Y = 100

puntos(1).X = 200

puntos(1).Y = 300

puntos(2).X = 300

puntos(2).Y = 20

puntos(3).X = 500

puntos(3).Y = 100

b(0) = PT_MOVETO

b(1) = PT_LINETO

b(2) = PT_LINETO

b(3) = PT_LINETO Or PT_CLOSEFIGURE

PolyDraw Me.hdc, puntos(0), b(0), 4

¿Qué hago con el arreglo b?. Con esto le estoy diciendo.

B(0) = PT_MOVETO significa que la acción es “mueve el puntero grafico al punto 10,100”, noten que B(0) se corresponde al índice puntos(0).

B(1) = PT_LINETO con esto le digo traza una recta desde el “puntero grafico” hasta 200,300”

Y así con B(2) y B(3) pero en B(3) además le digo que me cierre la figura. Vean que aparece la palabra TO, por lo que deben de entender que el “puntero grafico” se esta moviendo.

Fíjense que esta función traza un polígono con una solo llamada , si lo hiciéramos por separado tendríamos que llamar 1 vez al API MOVETOEX y 3 veces LINETO.

Y en el “case 7” tenemos la misma función pero con Bezier:

puntos(0).X = 10

puntos(0).Y = 100

puntos(1).X = 200

puntos(1).Y = 300

puntos(2).X = 300

puntos(2).Y = 20

puntos(3).X = 500

puntos(3).Y = 100

b(0) = PT_MOVETO

b(1) = PT_BEZIERTO

b(2) = PT_BEZIERTO

b(3) = PT_BEZIERTO

PolyDraw Me.hdc, puntos(0), b(0), 4

Aquí configuramos 3 puntos como PT_BEZIER, ustedes podrían preguntar ¿Dónde esta el punto de inicio? El esta donde esta inicialmente el “puntero Grafico” que en este caso es 10, 100 ya que la primera acción b(0) = PT_MOVETO.

Es decir, con el primer arreglo, ustedes configuran los puntos, y con el segundo configuran para que sirve cada punto. Y luego la función hace el resto. Lo único malo es que estas función no esta habilitada para Windows 98/95/ME, sino para 2000/NT/XP.

Polyline

Api: Declare Function Polyline Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long

Los parámetros cumplen la misma función que en POLIBEZIER con la diferencia que ahora en ves de formar una curva, trazan una recta que empieza desde “puntos(0)”, va pasando por los demas puntos y termina en “puntos(3)”. Creo que es fácil de entender si entendieron Bezier.

PolylineTo

Api: Private Declare Function PolylineTo Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long

Igual que POLYLINE con la diferencia que mueve el “puntero grafico”.

PolyPolyline

Api: Private Declare Function PolyPolyline Lib "gdi32" (ByVal hdc As Long, lppt As POINTAPI, lpdwPolyPoints As Long, ByVal cCount As Long) As Long

Esta función al igual con POLYLINE sirve para crear polígonos, la diferencia es que puedes crear más de un polígono. ¿Qué quiero decir? Se recuerdan de la función PolyDraw, bueno es más o menos el mismo sentido. Para entenderlo mejor expliquemos con el ejemplo:

puntos(0).X = 10

puntos(0).Y = 100

puntos(1).X = 200

puntos(1).Y = 300

puntos(2).X = 300

puntos(2).Y = 20

puntos(3).X = 500

puntos(3).Y = 100

Dim b2(0 To 4) As Long

b2(0) = 2

b2(1) = 2

PolyPolyline Me.hdc, puntos(0), b2(0), 2

Cuando ustedes arrancan el programa ¿Qué ven? Dos líneas, como dije arriba esta función puede pintar polígonos independientes.

Veamos los parámetros:

lppt = son los puntos que forman a la figura.

lpdwPolyPoints = este es un arreglo que determina cuantas figuras vamos a manejar y la cantidad de puntos que manejan.

nCount = establece la cantidad de figuras que graficara la función.

¿Por qué vemos 2 líneas?

Observen que en el arreglo “puntos” configure 4 puntos, y en el arreglo “b2” solo configure 2 casillas o espacios de 4 que originalmente había inicializado en DIM, con esto le digo a la función que mi matriz “puntos” consta de 4 coordenadas (x,y) para 2 figuras.

Cuando hago b2(0) = 2, le estoy diciendo a la función que la primera figura esta formada por dos puntos, y b2(1) = 2, le estoy diciendo lo mismo.

Luego en nCount = 2, y aquí le digo que tome del arreglo solo dos casilla o espacios.

Se que puede sonar, engorroso, complicado, igual fue para mi entender esto por primera vez, pero inventen cositas y verán que entenderán todas estas funciones rápidamente.

NOTA: la opción de "PoliBezier - Interactivo", para que funcione tiene que darle clic 4 veces a la pantalla en puntos distintos, cuando le den por cuarta vez, el programa le pintara una curva de bezier, formada por esos 4 puntos. Fue algo adicional que quise colocar.

CAMINOS (PATH)

Imaginemos que tenemos una imagen en un HDC, esa imagen la queremos recortar, si fuera un recorte rectangular sería fácil con el API BitBlt que veremos mas adelante se puede hacer, pero imaginen que quieren recortar la imagen pero con formas raras, como curvas, elipses, círculos, o polígonos irregulares, sería algo grandioso, pero programar eso sería muy complicado y haría de nuestro código algo engorroso y feo, pero Windows tiene una solución para ello y es “Caminos”, “Caminos” no es mas que trazar mediante una herramienta de dibujo un camino para luego manipularlo, como por ejemplo recortar el camino y pintar el área enmarcada.

Las funciones API’S son pocas y sencillas así que vamos al ejemplo, este programa sigue la filosofía del programa de “Sistema de coordenadas”, me refiero a la captura del mensaje WM_PAINT, pero para que funcione pongan en la carpeta del proyecto la imagen “image.bmp”, que acompaña a este manual.

Ver Codigo

Algunas líneas están puesta en comentario a propósito, ya que en el transcurso de la explicación iremos quitando esos comentarios y poniendo otros para explicar las funciones. En este código vuelven aparecer funciones API’S nuevas que no entran por completo en el tema, estas son : LOADIMAGE, CREATECOMPATIBLEDC, BITBLT, DELETEDC, la única que no voy a explicar ya que tiene una sección especial es BITBLT, el resto debido a que no tengo una sección especifica para ellas las voy explicar a continuación.

Antes de entrar a la explicación de “Caminos”, voy explicar las funciones LOADIMAGE, CREATECOMPATIBLEDC y DELETEDC.

Antes de empezar ustedes se podrían preguntar ¿Por qué no cargastes la imagen en la propiedad Picture del formulario?. Bueno tan simple como recordarles que nosotros en este código nos encargamos de pintar nuestro formulario al 100%, ¿qué quiero decir?. Que no le dejamos a la función de mensajes de VB procesar el mensaje WM_PAINT, si ustedes colocan la imagen por la propiedad picture del formulario se darán cuenta que la imagen no aparece, lo que es lógico ya que el mensaje WM_PAINT lo capturamos nosotros y no la función de mensajes que pone VB. ¿como hacemos entonces?. Aquí es donde entran las 4 funciones arriba mencionadas.

LoadImage

Api: Declare Function LoadImage Lib "user32" Alias "LoadImageA" (ByVal hInst As Long, ByVal lpsz As String, ByVal dwImageType As Long, ByVal dwDesiredWidth As Long, ByVal dwDesiredHeight As Long, ByVal dwFlags As Long) As Long

Creo que el nombre de la función lo dice todo, carga una imagen en memoria, fíjense que no tiene un HDC ya que la función retorna la referencia a la imagen cargada. Que luego ubicaremos en un HDC pero eso ya lo veremos.

Parámetros:

hInst = aquí especificamos el handle de la instancia de nuestra aplicación.

lpsz = en caso de que la imagen este en un archivo de recursos, pues aquí se le coloca el nombre o ID del recurso especifico, pero este no es nuestro caso, ya que nosotros estamos cargando la imagen a través de un fichero, así que, para nosotros este parámetro toma la dirección de la imagen. Lo del recurso lo menciono para aquellos que usen C++.

dwImageType: especifica el tipo de imagen a cargar:

Const IMAGE_BITMAP = 0

Const IMAGE_ICON = 1

Const IMAGE_CURSOR = 2

dwDesiredWidth y dwDesiredHeight = especifica el ancho y alto de la imagen que se va a cargar.

dwFlags = puede tomar los valores que se muestran a continuación:

LR_DEFAULTCOLOR = valor por defecto y no tiene ningún significado.

LR_CREATEDIBSECTION = solo aplicable para imágenes bitmap, y hace que la función retorne una imagen independiente de dispositivo.

LR_DEFAULTSIZE = Se utiliza para decirle al sistema las dimensiones que debe de tomar la imagen.

LR_LOADFROMFILE = Utilizado cuando se quiere recuperar una imagen ubicada o contenida en un archivo.

LR_MONOCHROME = carga la imagen en blanco y negro.

Hay mas pero considero que estos son los mas importantes, en la documentación oficial encontrarán los restantes.

Si verificamos en el código:

bitmap = LoadImage(App.hInstance, App.Path & "\image.bmp", IMAGE_BITMAP, ByVal 0&, ByVal 0&, LR_LOADFROMFILE)

Podemos ver que estamos cargando una imagen desde un fichero, por el valor LR_LOADFROMFILE, a las dimensiones les paso 0 para que tome las dimensiones completa de la imagen.

Creo que es sencillo de ver el funcionamiento de esta función. Pero vean que “bitmap” toma el valor de referencia de la imagen ¿Cómo hacemos para pegarla en nuestro HDC?. Con la función que explico a continuación solucionamos el problema.

CreateCompatibleDC

Api: Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long

Nosotros no podemos cargar una imagen y pegarla directamente a nuestro HDC del formulario, hay que usar un HDC intermedio donde almacenemos la imagen para luego pasarla a nuestro HDC original. Esta función lo que hace es crear un DC compatible de uno ya existente.

¿Qué significa compatible?. Se recuerdan que yo les dije que un HDC o dispositivo de contexto viene con objetos configurados (pincel, brocha, fuentes, etc), cuando crean un DC o HDC con esta función lo que están haciendo es creando un HDC que tenga TODOS los objetos tal cual como el HDC original, es esa la razón de que solo la función tome un parámetro y es el HDC original del cual queremos tomar esos objetos.

dc = CreateCompatibleDC(hdc)

En el código vemos esto, aquí la variable “dc” esta tomando los objetos de la variable “hdc” que es nuestro dispositivo de contexto original, es decir, de nuestro formulario. Ahora la variable “dc” es un dispositivo de contexto, ya que tomo de “hdc”, la brocha, pinceles, fuentes, etc, y todo lo que tenga nuestro “hdc” original.

Ahora un bitmap es un objeto, si queremos que el HDC creado (dc) tome la imagen pues usemos la siguiente línea:

SelectObject dc, bitmap

Aquí estamos pegando el objeto “bitmap” a nuestra hoja de dibujo (HDC). Pero aquí no acaba todo, ya que nosotros queremos que la imagen este en la variable “hdc” que es el dispositivo de contexto del formulario, y para ello usamos una función para copiar imágenes:

BitBlt hdc, 0, 0, 800, 600, dc, 0, 0, vbSrcCopy

Esta función tiene un apartado en la sección de “Imágenes” que se ve mas adelante.

DeleteDC

Api: Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long

Un DC o HDC (OJO: en realidad el dispositivo de contexto su abreviatura es DC no HDC, el problema es que uso HDC ya que ese el nombre que le da VB en sus objetos, pero me refiero a lo mismo cuando hablo de DC o HDC), es un objeto particular que tiene su propia función para eliminarlos, los DC que creamos hay que eliminarlos, sino quedan en memoria y VB no los borra, para eliminarlo usamos esta función, que se usa solo para DC.

DeleteDC dc

En nuestro código uso esta función para eliminar la variable “dc” después de haberla utilizado.

Ya explicado estas 3 funciones que permiten la inserción de una imagen ya podemos entrar a las funciones de este tema. Vean que no es tan fácil insertar una imagen, como nosotros creemos, lo que sucede es que los lenguajes en general esconde esta parte del código, por ejemplo en VB es mas fácil insertar una imagen ya que le asigna a la propiedad “Picture” la imagen y listo!. Pero ya se pueden imaginar mas o menos lo que sucede en su interior.

BeginPath

Api: Declare Function BeginPath Lib "gdi32" (ByVal hdc As Long) As Long

EndPath

Api: Declare Function EndPath Lib "gdi32" (ByVal hdc As Long) As Long

Estas funciones son las encargadas de empezar y finalizar un camino, cuando queremos trazar un camino tenemos que usar BeginPath pasándole como parámetro el hdc donde se trazara, luego se emplean las funciones de dibujo para trazar el camino, y cuando se haya terminado, este se cierra con EndPath pasándole el hdc como parámetro y así le indicamos a Windows que el camino a terminado.

Las funciones para crear caminos se usan dependiendo del sistema operativo, a continuación se muestra las funciones disponibles dependiendo de la versión de Windows.

Windows 2000/NT:

AngleArc, Arc, ArcTo, Chord, CloseFigure, Ellipse, ExtTextOut, LineTo, MoveToEx, Pie, PolyBezier, PolyBezierTo, PolyDraw, Polygon, Polyline, PolyLineTo, PolyPolygon, PolyPolyline, Rectangle, RoundRect, TextOut.

Windows 95/98/ME:

CloseFigure, ExtTextOut, LineTo, MoveToEx, PolyBezier, PolyBezierTo, Polygon, Polyline, PolylineTo, PolyPolygon, PolyPolyLine, TextOut.

Es por esa razón que en entre BeginPath y EndPath tengo la función Polygon y PoliBezier. Ya que quería hacerlo compatible con cualquier versión del sistema operativo.

¿Qué hacemos con el camino?. Ya tenemos el camino trazado, ahora vamos a ver las funciones que me permiten manipular los caminos, si corrieron el programa vieron que se dibuja la curva de Bezier cuyo fondo es parte de la imagen. Para ese efecto usamos la función siguiente.

SelectClipPath

Api: Declare Function SelectClipPath Lib "gdi32" (ByVal hdc As Long, ByVal iMode As Long) As Long

Esta función aplica una operación booleana, entre el HDC que se pasa como parámetro y el camino antes trazado, esa operación booleana se especifica en el parámetro “iMode”, que puede tomar las valores que se muestran a continuación:

Public Const RGN_AND = 1 (INTERSECCION)

Public Const RGN_COPY = 5 (COPIA)

Public Const RGN_DIFF = 4 (DIFERENCIA)

Public Const RGN_OR = 2 (UNION)

Public Const RGN_XOR = 3 (UNION EXCEPTO DE LAS AREAS COMUNES, QUE SE EXCLUYEN)

En nuestra caso usamos RGN_COPY, otro interesante es RGN_DIFF, pónganlo para que vean el efecto, que es como “inverso” a RGN_COPY.

CloseFigure

Api: Declare Function CloseFigure Lib "gdi32" (ByVal hdc As Long) As Long

Esta función como su nombre lo indica “cierra una figura”, vean que la curva PoliBezier que use, esta cerrada por una línea recta entre el punto inicial y el punto final, cuando un polígono cualquiera se dibuja y no es cerrado, al usar esta función la figura es cerrada trazando una recta desde el punto inicial al punto final. Como único parámetro se le pasa el HDC en donde se esta dibujando.

Ahora en el código vamos hacer esto, pongan en comentario lo que esta en azul y quítenle el comentario a lo que esta en rojo:

‘SelectClipPath hdc, RGN_DIFF

brocha = CreateSolidBrush(RGB(200, 200, 0))

SelectObject hdc, brocha

FillPath hdc

‘StrokePath hdc

‘StrokeAndFillPath hdc

DeleteObject brocha

‘bitmap = LoadImage(App.hInstance, App.Path & "\image.bmp", IMAGE_BITMAP, ByVal 0&, ByVal 0&, LR_LOADFROMFILE)

‘dc = CreateCompatibleDC(hdc)

‘SelectObject dc, bitmap

‘BitBlt hdc, 0, 0, 800, 600, dc, 0, 0, vbSrcCopy

‘DeleteDC dc

Cuando corran el programa verán que muestra el área de la curva rellena con un color. Esto es gracia a la siguiente función.

FillPath

Api: Declare Function FillPath Lib "gdi32" (ByVal hdc As Long) As Long

Esta función rellena los caminos trazados con la brocha actual, en mi caso yo hice una nueva brocha para que vieran el efecto. Como parámetro se le pasa el hdc en donde estamos dibujando.

Ahora pongan en comentario la línea donde esta FillPath, y quítenle el comentario a “StrokePath”.

StrokePath

Api: Declare Function StrokePath Lib "gdi32" (ByVal hdc As Long) As Long

Esta función solo traza la figura que dibujamos ¡no le rellena!, ni siquiera la cierra como pueden ver. Al igual que la anterior su único parámetro es el hdc.

Ahora pongan en comentario la línea donde esta StrokePath, y quítenle el comentario a “StrokeAndFillPath”.

StrokeAndFillPath

Api: Declare Function StrokeAndFillPath Lib "gdi32" (ByVal hdc As Long) As Long

Esta función es una mezcla de las 2 anteriores, es decir, rellena y traza la figura al mismo tiempo, en este caso cerrándola si esta abierta. Su único parámetro es un hdc.

WidenPath

Api: Declare Function WidenPath Lib "gdi32" (ByVal hdc As Long) As Long

Esta API que no uso en el código hace lo siguiente. Recuerdan que ustedes para trazar la figura pueden usar un pincel con un grueso mayor que 1, eso lo vimos en la parte de pinceles, esta función (WidenPath) se utiliza cuando se trazan caminos con pinceles de grueso mayor que 1, sirve para 1, pero no se ve el efecto ya que la línea es muy delgada.

¿Qué hace esta función?. Para que funcione WidenPath tienen que mostrar la imagen de nuevo, con la diferencia que la porción de la imagen que se muestra no es el área de la figura, sino el grueso del pincel. ¿Están confundido, verdad?. Vean el cuadro.

Con la función SelectClipPath la parte que esta en azul se rellanaba con la imagen, esto ya lo probamos, ahora WindPath lo que hace es rellenar la parte negra con la imagen que ahí se encuentre y la parte azul no lo hace nada ¿Ahora lo ven?. Esa es la razón del por que tiene que usar un pincel con un grosor considerable.

REGIONES

A continuación, se presenta un tema muy bonito, en lo que a librerías graficas se refiere, ya que las funciones que veremos a continuación presentan un potencial muy grande para desarrollar nuestras aplicaciones.

Las regiones son parecidas a los caminos, pero mucho mas complejas y completas, caminos nos permitía crear secciones o áreas, dependiendo de una figura ya estipulada, pero nada mas!, en cambio regiones se trabaja solo con áreas, y no solo áreas simples como trazábamos en caminos sino áreas que pueden ser muy complejas y también nos permite la manipulación de pulsaciones del mouse. Es decir, imaginen un triangulo, ustedes quieren que haga algo cuando se pulse el botón del mouse sobre él, hacerlo con las herramientas de VB es complicado ya que hay que ver las coordenadas de pulsación compararlas con los vértices, etc, etc, etc..., con regiones este trabajo es fácil. Imaginen crear un mapa de países, donde cada país es una forma irregular, con las herramientas conocidas sería un arduo trabajo programar todo eso, incluyendo las pulsaciones del mouse.

Las funciones referentes a regiones son bastante por lo que se va dividir este tema en varios proyectos, en uno de ellos donde explico lo básico y el otro donde trabajo con imágenes; es con regiones donde veremos formularios irregulares, verán como hacemos un formulario en forma irregular y luego usaremos una imagen para crear nuestros formularios.

En esta sección voy a empezar al revés, es decir, primero voy a explicar algunas funciones API’S referente a regiones antes de colocar el código. Las regiones no van ligadas directamente al HDC o al HWDN, es decir, las funciones para crear las regiones no tienen como parámetro un HDC, ya que primero se crea una región y luego se le asigna a un HDC o HWND.

Para crear las regiones, no es como los caminos, que usaban las funciones graficas de Windows, para crearlos, las regiones tienen sus propias funciones para trazar las figuras.

CreateRectRgn

Api: Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

CreateRectRgnIndirect

Api: Declare Function CreateRectRgnIndirect Lib "gdi32" (lpRect As RECT) As Long

CreateRoundRectRgn

Api:Declare Function CreateRoundRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long) As Long

Estas 3 funciones sirven para crear regiones cuya forma es rectangular, las dos primeras hacen exactamente lo mismo, con la diferencia que una usa la estructura RECT y la otra usa las 4 coordenadas necesarias para crear un rectángulo. La tercera función tiene el mismo efecto que la función RoundRect.

CreateEllipticRgn

Api: Declare Function CreateEllipticRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

CreateEllipticRgnIndirect

Api: Declare Function CreateEllipticRgnIndirect Lib "gdi32" (lpRect As RECT) As Long

CreatePolygonRgn

Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As POINTAPI, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long

CreatePolyPolygonRgn

Declare Function CreatePolyPolygonRgn Lib "gdi32" (lpPoint As POINTAPI, lpPolyCounts As Long, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long

Las primeras dos funciones, es fácil ver lo que hacen, crean una región en forma de elipse, y las ultimas 2 crean polígonos.

Vean que los parámetros son exactamente los mismos que sus homólogos presentados en la sección de “Figuras Geométricas” y “Líneas y Curvas”. A diferencia de caminos que teníamos todas las funciones graficas a nuestra disposición, con regiones solo podemos crear regiones de las formas arriba mencionadas. Dense cuenta que entre sus parámetro no existe un HDC.

Proyecto 1

Ya mostrada estas funciones básicas, ya podemos ir al ejemplo, que consta de un formulario. En él introducen este código:

Ver Codigo

CombineRgn

Api: Declare Function CombineRgn Lib "gdi32" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long

Esta función es utilizada para combinar regiones. Como primer parámetro “hDestRgn”, pasamos la variable donde almacenaremos la nueva región, creado a partir de los dos siguientes parámetros que también son regiones, “hSrcRgn1” y “hSrcRgn2”, y como último parámetro “nCombineMode”, le pasamos que tipo de operación lógica va a realizar, los valores que puede tomar son los siguientes:

Const RGN_AND = 1

Const RGN_COPY = 5

Const RGN_DIFF = 4

Const RGN_OR = 2

Const RGN_XOR = 3

Que si se dan cuenta son los mismos que en “caminos”.

Veamos el ejemplo:

CombineRgn region(0), region(0), region(1), RGN_OR

CombineRgn region(0), region(0), region(2), RGN_OR

Estas son las líneas usadas en el código, region(0) corresponde inicialmente a un rectángulo, y region(1) y region(2) son 2 triángulos con distintas coordenadas. Noten que estoy almacenando las combinaciones en region(0). Y uso el operador lógico RGN_OR.

En la primera linea almaceno en region(0), la combinación de la region(0) inicial y el triangulo en la region(1), y luego combino lo que tiene ahora region(0) con el otro triangulo trazado en region(2). Y como resultado tengo 3 regiones almacenadas, por decirlo así, en una sola variable.

Esta función puede retornar valores, que pueden ser lo siguientes:

Public Const NULLREGION = 1 ‘ La región resultante es nula

Public Const SIMPLEREGION = 2 ‘ La región resultante es simple

Public Const COMPLEXREGION = 3 ‘ La región resultante es una compleja

Esto no lo he probado pero he de suponer que Windows llama a SIMPLEREGION regiones como rectángulos, elipse o polígonos simples, y habla de complejas a regiones que pueden ser combinación de ambas. Les dejo a ustedes probar eso.

¿Qué podemos hacer con las regiones? Observen las siguientes funciones, vean que todavía no hemos usado ningún dispositivo de contexto (HDC) o ventana (HWND).

FillRgn

Api: Declare Function FillRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long, ByVal hBrush As Long) As Long

Esta función rellena las regiones con una brocha especificada en el parámetro “hBrush”, noten que aquí si hay un “hdc”, que es el dispositivo de contexto que contiene la region o regiones, como segundo parámetro se le pasa la región que se quiere rellenar.

En el código:

FillRgn Me.hdc, region(0), CreateSolidBrush(RGB(0, 255, 200))

Vean que creo la brocha de manera directa, es decir, pongo la función CreateSolidBrush directo, evitándome así declarar variables que en algunos casos son necesarias pero otra veces no. Como segundo parámetro paso la region(0), que es la region a rellenar.

La función que muestro a continuación no esta en el código, pero hace exactamente lo mismo que FillRgn, con la diferencia que la brocha que toma es la que tiene configurado el dispositivo de contexto (HDC).

PaintRgn

Api: Declare Function PaintRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long) As Long

Como dije arriba hace lo mismo pero la brocha que usa es la del hdc, que se le pasa como primer parámetro.

FrameRgn

Api: Declare Function FrameRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long, ByVal hBrush As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long

Para ver el efecto de esta función, tienen que quitarle el comentario a esta línea en el código:

FrameRgn Me.hdc, region(0), CreateSolidBrush(RGB(0, 255, 100)), 10, 10

Y ponerle comentario a la linea:

'FillRgn Me.hdc, region(0), CreateSolidBrush(RGB(0, 255, 200))

Que era la linea que estaba activada antes. Se darán cuenta rápidamente de lo que hace la función cuando arrancan el programa, sus 3 primeros parámetros son idénticos que FillRgn, con la diferencia que la brocha no rellena la figura sino los bordes de la figura, cuyo grosor es controlado por las 2 ultimas variables “nWidth” que controla el grosor de ancho y “nHeight” controla el grosor de alto.

InvertRgn

Api: Declare Function InvertRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long) As Long

Para ver el efecto de la función al igual que arriba ponen en comentario esta linea

‘FrameRgn Me.hdc, region(0), CreateSolidBrush(RGB(0, 255, 100)), 10, 10

Y le quitan el comentario a:

InvertRgn Me.hdc, region(0)

Esta función es sencilla lo que hace es invertir los colores, es decir, si el HDC pasado en el primer parámetro tiene un color blanco, pues la region será negra, y si el fondo es negro pues la region será blanca.

PtInRegion

Api: Declare Function PtInRegion Lib "gdi32" (ByVal hRgn As Long, ByVal X As Long, ByVal Y As Long) As Long

Aquí presentamos la función que le da una versatilidad y poder al uso de regiones, que es la posibilidad de saber si ha dado clic a una región, corran el programa y denle clic a cualquier región, notaran que le aparece un mensaje indicándole la región a la cual se ha dado clic, siempre aparece el número 0 y eso por que recuerden que combinamos las regiones. Pongan en comentario estas líneas:

‘CombineRgn region(0), region(0), region(1), RGN_OR

‘CombineRgn region(0), region(0), region(2), RGN_OR

Y quítenle el comentario a estas líneas:

FillRgn Me.hdc, region(0), CreateSolidBrush(RGB(0, 255, 200))

FillRgn Me.hdc, region(1), CreateSolidBrush(RGB(0, 255, 200))

FillRgn Me.hdc, region(2), CreateSolidBrush(RGB(0, 255, 200))

Lo que estoy haciendo es pintar las regiones una a una sin combinarlas . Corran el programa y denle clic a las regiones, verán que el mensaje indica el número de region en donde fue pulsado el ratón, ¡no les parece asombroso!, la posibilidad de crear regiones complejas y que nos brinden la posibilidad de capturar el clic del mouse. Imaginen la creación de un mapa, donde podamos capturar los clic de los mapas con una precisión exacta.

Esta función se encuentra en el código así:

Dim i As Integer

Dim resultado As Long

For i = 0 To UBound(region)

resultado = PtInRegion(region(i), X / 15, Y / 15)

If resultado <> 0 Then

MsgBox "Se le ha dada click a la region " & i, vbInformation + vbOKOnly, ""

Exit For

End If

Next

Lo que hago aquí es verificar en mi arreglo de regiones, cual region fue seleccionado por el clic del mouse. La función PtInRegion funciona de la siguiente manera: como primer parámetro le pasamos la región a evaluar, como segundo y tercer parámetro le pasamos las coordenadas en donde se dio clic. Estas coordenadas son en PIXELES es por esa razón que divido entre 15.

Si la coordenada X e Y se encuentran un una región, la variable “resultado” tiene un valor distinto de 0, en caso de que una región no se encuentre en las coordenadas pasadas, retorna 0.

Proyecto 2

Vamos a mostrar el segundo proyecto sobre regiones, que trata sobre dos funciones, que bien empleadas pueden hacer cosas curiosas. Para este proyecto es necesario un formulario y que la imagen “image.bmp” se encuentre en la carpeta en donde esta el proyecto.

Luego pegar este código:

Ver Codigo

Después de haber arrancado el programa notaran que al mover el mouse se mueve un círculo que visualiza la imagen de fondo que hay en el formulario.

¿Cómo hacemos eso? En la sección de “caminos” vimos una función que permitía recortar una sección de una imagen que usábamos de fondo. Pues regiones también tiene una.

SelectClipRgn

Api: Declare Function SelectClipRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long) As Long

Esta función es la encargada de hacer ese recorte, se le pasa como primer parámetro el HDC que vamos a cortar y luego como segundo parámetro le pasamos la region. Hay algo curioso, veamos el evento Form_Paint:

SelectClipRgn hdc, region(0)

Dim r As RECT

GetClientRect hwnd, r

dc = CreateCompatibleDC(hdc)

SelectObject dc, bitmap

StretchBlt hdc, 0, 0, r.Right, r.Bottom, dc, 0, 0, 800, 600, vbSrcCopy

DeleteDC dc

Aparte de las funciones GetClientRect y StretchBlt no hay nada nuevo, estas funciones serán explicadas en sus respectivas secciones, en general puede decirse que con GetClientRect obtenemos el tamaño del área cliente o dispositivo de contexto, y con StretchBlt ajustamos una imagen a un tamaño específico.

Lo que quería que vieran es que primero hay que hacer el SelectClipRgn antes de pegar la imagen, no se puede hacer al revés, ya que de hacerlo verán la imagen completa sin el recorte. Es esa la razón del por que no use la propiedad “Picture” del formulario, ya que al usarlo VB pinta primero la imagen y luego ejecuta Form_Paint por lo que no tenia los efectos esperados.

OffsetRgn

Api: Declare Function OffsetRgn Lib "gdi32" (ByVal hRgn As Long, ByVal X As Long, ByVal Y As Long) As Long

Una de los cosas curiosas es que cuando movemos el mouse se mueve la region, en realidad lo que estamos haciendo es “desplazando” la region, para ello usamos esta función, que tiene como primer parámetro la region a desplazar, y como segundo y tercer parámetro tenemos la cantidad de unidades lógicas a desplazar para el eje X e Y, respectivamente.

ExtSelectClipRgn

Api: Declare Function ExtSelectClipRgn Lib "gdi32" (ByVal hdc As Long, ByVal hRgn As Long, ByVal fnMode As Long) As Long

Esta función es casi igual a SelectClipRgn con la diferencia que permite aplicar las operaciones lógicas mencionadas en caminos, pero que vuelvo a repetir:

Const RGN_AND = 1

Const RGN_COPY = 5

Const RGN_DIFF = 4

Const RGN_OR = 2

Const RGN_XOR = 3

Estas son las constantes para el parámetro “fnMode”, SelectClipRgn es lo equivalente a usar ExtSelectClipRgn con el parámetro “fnMode” = RGN_COPY.

Proyecto 3

Para este proyecto se necesita 2 botones, cuyo nombre sea “Command1” y “Command2” y una etiqueta o caption con nombre “label1” y la propiedad “BorderStyle” del formulario, ponerle el valor de “0- None”. Luego pegar este codigo:

Ver Codigo

Cuando arranquen el proyecto se sorprenderán que el formulario ya no tiene la forma rectangular tradicional sino que ha cambiado por elipses, ¡sorprendente!, imaginen los formularios que a partir de ahora pueden crear, sin necesidad de continuar con el estilo rectangular tradicional, y si se ponen a ver los programas de hoy en día, tienen formas extrañas y originales. Aquí les digo como hacerlo. Como verán el código es simple, ya que hice algo sencillo, en realidad una aplicación completa requeriría de tiempo y de código.

La única función nueva es SetWindowRgn que explicamos a continuación.

SetWindowRgn

Api: Private Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long

Esta función es simple, como pueden ver esta función no toma el HDC del formulario sino directamente el HWND de la ventana. ¿Por qué se ven la elipses y no el rectángulo? Cuando ustedes crean un formulario, Windows le establece una region predeterminada en este caso la región rectangular, cuando el formulario se va a pintar pinta en base a la información de esta región, esta región abarca TODA la ventana, cuando me refiero a “TODO”, me refiero a la parte del CAPTION o barra de titulo, bordes, y el área cliente.

Es decir, esta función cambia el área de region que pinta la ventana, OJO ventana, no HDC, es decir, estamos hablando de todo el formulario.

Como primer parámetro le pasamos el “hwnd” de la ventana, como segundo parámetro la región y como tercer parámetro le especificamos si repinta la ventana o no. Si se coloca TRUE inmediatamente se envía un mensaje para pintar el formulario, en caso de pasarle FALSE el formulario no se pinta inmediatamente.

Noten, que un efecto que pudimos haber considerado complicado, vean lo sencillo que es. A ustedes les dejo, realizar el código de MOVER el formulario, como pueden ver el formulario se queda estático y no es posible moverlo, pero echando unas líneas de código, de seguro no tendrán problema para hacerlo.

Proyecto 4

En el proyecto 3 quedamos seguramente sorprendidos al ver que podíamos crear un formulario con forma irregular, pues en este proyecto tal vez queden aun mas sorprendido al ver la versatilidad de la creación de este tipo de formularios. Puede que para algunas aplicaciones sea complicado y engorroso la creación de regiones para crear un formulario de manera irregular.

Imaginen que tiene una imagen, y tiene como fondo un color que denominamos “color de transparencia”. Y también imaginen que pudiéramos quitar el color de transparencia dejando nada más la imagen que nos interesa.

Pues en este proyecto hacemos eso con la utilización de regiones. Para este proyecto es necesario un formulario con la propiedad BorderStyle = “0-None”, un botón cuyo nombre sea “Command1” y que la imagen “imagen3.bmp” que acompaña a este manual, sea colocada en el lugar donde esta el proyecto.

Luego ponen este código en el formulario:

Ver Codigo

Al correr el proyecto, se sorprenderán al ver un formulario con una forma no tradicional, hecha a partir de una imagen, si ven la imagen por un programa grafico notaran que tiene fondo amarillo, el amarillo es mi “color de transparencia” le digo así ya que en realidad lo que vamos hacer es crear regiones a partir de los píxeles que no son amarillos.

¿Cómo lo hacemos? Veamos la función encargada de crear la región, cabe mencionar que la imagen tiene una medida de 260 X 220:

Sub ChequeoPixeles()

Dim i As Long, j As Long

Dim pixel As Long

For i = 1 To 259 ‘ Verificamos las 259 filas que tiene la imagen

For j = 1 To 219 ‘ Verificamos las 219 columnas de cada fila

pixel = GetPixel(dc, i, j) ‘ Obtenemos el color de 1 pixel en especifico

If pixel <> RGB(255, 255, 0) Then ‘ Si el píxel es distinto de amarillo

If primeravez Then ‘ ¿Es primera vez que entra?

region(0) = CreateRectRgn(i, j, i + 1, j + 1) ‘ Si es primera vez entonces almacenamos la primera region en region(0)

primeravez = False ‘ Lo ponemos false para que no vuelva a entrar

Else ‘ Si no es primera vez

region(1) = CreateRectRgn(i, j, i + 1, j + 1) ‘ La siguiente region la almacenamos en region(1)

CombineRgn region(0), region(0), region(1), RGN_OR ‘ Combinamos las dos regiones

DeleteObject region(1) ‘ Eliminamos region(1) ya que no es útil en este momento

End If

End If

Next ‘ Continuamos hasta chequear todas las columnas de una fila

Next ‘ Continuamos hasta chequear todas las filas

End Sub

Creo que los comentarios en azul explican mas o menos con detalle lo que hace la función, básicamente lo que estoy haciendo es crear regiones de 1 X 1 píxel, por cada píxel que no es de color amarillo, acumulo todas esas regiones cuando uso CombineRgn, y luego al final de todo, region(0) tiene todas las regiones o véanlo así, todos los píxeles que no son de color amarillo. Luego con la función SetWindowRgn, establecemos nuestra nueva region.

Dejo en claro, que el código es un poco rudimentario, en el sentido que si la imagen es muy grande se puede hacer lento el arranque del programa, en Internet y en libros pueden encontrar formas de optimizar este código, aunque dudo mucho que consigan el código en VB. A ustedes les dejo la optimización de todo esto. Recuerden que yo les doy la idea y las herramientas. Ustedes, se sientan a mejorar y optimizar todo esto, personalmente yo no he trabajado mucho con esto, ya que no se me ha dado el momento. Pero de seguro que muchas personas habrán visto interesante todo esto. Y estoy seguro que por sus cabezas pasan millones de ideas de cómo crear una aplicación, que antes no podían hacer.

Funciones Extras

EqualRgn

Api: Declare Function EqualRgn Lib "gdi32" (ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long) As Long

Función utilizada para ver si dos regiones son iguales. Esta función retorna TRUE si son iguales y FALSE en caso de no serlo. Estas dos regiones vienen dadas por los dos únicos parámetro de la función.

RectInRegion

Api: Declare Function RectInRegion Lib "gdi32" (ByVal hRgn As Long, lpRect As RECT) As Long

Función que sirve para ver si un rectángulo, cuyas coordenadas se pasan en el segundo parámetro, que es de tipo RECT, se encuentra dentro de la región especificada en el primer parámetro. Al igual que la anterior esta función devuelve TRUE o FALSE

GetRgnBox

Api: Declare Function GetRgnBox Lib "gdi32" (ByVal hRgn As Long, lpRect As RECT) As Long

Algunas veces tenemos regiones irregulares, pero queremos saber el rectángulo mínimo que enmarca la región, pues esta función es la encargada de ello, como primer parámetro si le pasa la region a enmarcar y como segundo parámetro se le pasa una estructura de tipo RECT la cual al finalizar la función, tendrá las coordenadas del rectángulo mínimo que enmarca la región.

Esto es todo acerca de regiones, si quieren ver todas las funciones API’S referente a este tema vean la documentación oficial de Microsoft en Internet, yo nada mas introduje las funciones que considere como las mas importantes. A partir de ahora estoy seguro que tienen otra visión para crear sus proyectos, con efectos especiales muy buenos y fáciles de hacer.

FUENTES

Vamos a introducir un tema que es relativamente complejo, ya que las funciones que manejan o manipulan las fuentes están llenas de muchos detalles y muchas variables, inclusive en esta parte no soy muy experto así que me voy a limitar a explicar lo que conozco. Ya que hay algunas constantes que en realidad no entiendo muy bien.

¿Por qué estudiar las fuentes? Windows proporciona unas funciones muy poderosas para la manipulación de fuentes, particularmente yo nunca las he usado, pero algunas veces si necesitamos escribir un texto directamente en el HDC, pues será necesario conocer como funcionan estas funciones, ya que tal vez los controles en este caso no nos sirvan.

Antes de explicar la parte teórica de todo esto, vamos a decir que es un “serif”, para empezar no es lo que yo creía, en particular ya pensaba que era un tipo de letra, pero no es exactamente eso, para explicar lo que es, veamos una letra Times New Roman y otra Arial:

Al lado izquierdo tengo la T de Times New Roman y del lado derecho tengo la T de arial. Vean las puntas de la T de Times New Roman, hay como una pequeña silueta o triangulito al final de cada extremo, esa silueta particular es lo que se llama Serif, es decir, Times New Roman es una fuente Serif, se dice así a las fuentes que tienen cierta extensión en los extremos. Arial por ejemplo, no es serif ya que carece de esa extensión, como pueden ver sus extremos son completamente rectangulares. Otro ejemplo de una fuente no serif es la Courier New, cuya T:

Noten que aquí el extremo tiene como una extensión, pero esa extensión sigue siendo recto!, es decir, como rectangular. Esto lo explico ya que ahora me toca explicar los diferentes tipos de familias que conforman a Windows.

Familias

DECORATIVE

Valor: FF_DECORATIVE = 80

Son esos estilos de fuentes sofisticados, empleados en esos documentos de estilo medieval.

DONTCARE

Valor: FF_DONTCARE = 0

Especifica una familia genérica. Este se utiliza cuando la información acerca de la fuente no importa o no existe. Y se emplea la fuente por defecto.

MODERN

Valor: FF_MODERN = 48

Son fuentes cuyo ancho en todos los caracteres son iguales, y no son serif. Ejemplo Courire New:

ABCDEFGHI, pueden ver a simple vista que en la cadena mostrada, cada letra tiene la misma anchura.

ROMAN

Valor: FF_ROMAN = 16

Son fuentes cuyo ancho de los caracteres son variables, y son Serif. Ejemplo: Times New Roman.

SCRIPT

Valor: FF_SCRIPT = 64

Son fuentes que se asemeja mucho a la escritura humana.

SWISS

Valor: FF_SWISS = 32

En esta familia están contenidas las fuentes cuyo ancho de caracteres son variables pero no son Serif, por ejemplo: Arial.

Tipos de Fuente

Raster:

En este tipo de fuente los caracteres vienen representados por BITMAP, que el sistema usa para pintar cada carácter.

Vector:

Aquí los caracteres son una sería de líneas que definen segmentos, que el sistema usa para dibujar el carácter.

TrueType y OpenType:

Es una colección de instrucciones que dibujan líneas y curvas, dando forma a cada letra.

Grupo de caracteres

La mayoría debe de conocer que todos los caracteres no son más que números, que son identificados por Windows y se le asigna un carácter dependiendo de su código. Por ejemplo es clásico saber que para la letra “ñ” si presionamos ALT-164, se muestra en pantalla la “ñ” pequeña, el código es 164, eso significa que existe un grupo de caracteres con cierto código y dependiendo del código se muestra el carácter. Existen varios grupos de caracteres que voy a explicarlos brevemente:

Grupo de caracteres “Windows”.

Este el mas usado consta de 8 bit, es decir, cada carácter es un byte, este va desde 0x20 (decimal 32) hasta 0xFF (255), es decir, tenemos 132 caracteres disponibles, este grupo empieza con el carácter en blanco (“ “). Si lo quieren ver aprieten ALT-32.

Grupo de caracteres “UNICODE”.

El grupo de caracteres “Windows”, funciona perfecto para algunos lenguajes, pero existía un problema, cuando esto de la computación se globaliza a nivel mundial, nació la necesidad de hacer compatibles los software con cada lenguaje, el problema es que existen lenguajes con mas de 255 caracteres, esto obligo a los programadores, a crear un nuevo grupo de caracteres que se llama Unicode. Este grupo esta formado por 16 bit (2 byte), lo que permite 65,536 caracteres, en los cuales se incluye símbolos matemáticos, signos de puntuación ,etc.

Grupo de caracteres “OEM”.

Este grupo es el usado en el sistema MS-DOS®. En este grupo desde los caracteres 32 hasta el 127 son idénticos a los del grupo ASCII, y Windows.

Grupo de caracteres “símbolos”.

Grupo que contiene símbolos de uso matemático y científico.

Dimensiones

Cuando vimos las familias mencione que algunas consistían de grupo de caracteres de tamaño variable. Eso significa que de alguna manera, para hacer una aplicación que manipula letras, deberíamos de conocer como son esas dimensiones. Esto en realidad es todo un problema, ya que hay muchas medidas, en realidad como vamos a ver mas adelante existe una estructura TEXTMETRIC el cual contiene esas medidas, evidentemente contendrá las medidas de la fuente que este configurada en nuestro dispositivo de contexto.

La estructura es así:

Private Type TEXTMETRIC

tmHeight As Long / Altura de la letra o fuente

tmAscent As Long

tmDescent As Long

tmInternalLeading As Long

tmExternalLeading As Long

tmAveCharWidth As Long / Anchura media

tmMaxCharWidth As Long / Anchura Maxima

tmWeight As Long / Grosor o Peso de la letra o fuente

tmOverhang As Long

tmDigitizedAspectX As Long

tmDigitizedAspectY As Long

tmFirstChar As Byte / Primer carácter de la fuente

tmLastChar As Byte / Último carácter de la fuente

tmDefaultChar As Byte / carácter por defecto

tmBreakChar As Byte

tmItalic As Byte / Indica si la letra esta cursiva

tmUnderlined As Byte / Indica si la letra esta subrayada

tmStruckOut As Byte / Indica si la letra esta tachada

tmPitchAndFamily As Byte / Indica a la familia a la cual pertenece

tmCharSet As Byte / Indica el grupo de caracteres al cual pertenece

End Type

Algunas de los parámetros los explico de manera grafica y para ello voy a tomar la imagen que se encuentra en la documentación oficial de MSDN:

En este caso, otmMacAscent es igual a tmAscent, y otmMacDescent es igual a tmDescent. Lo que sucede es que las variables que empiezan con “otm” pertenecen a otra estructura mas completa que es NEWTEXTMETRIC, esta estructura tiene todos los parámetros de TEXTMETRIC y unos adicionales, los parámetros que no explico en realidad no los entiendo, tal vez por que todavía no le he visto utilidad a esos parámetros.

¿Para que todo esto? Si ustedes quisieran diseñar un programa parecido a WORD tendrían que conocer muy bien esto, por que con esto controlan la posición de cada carácter en el momento de dibujar. En el ejemplo que mostrare a continuación yo no trabaje esta parte, ya que esto requiere de cierto trabajo, que en realidad no lo veo mucha utilidad ya que existen controles que hacen ese trabajo. Pero uno nunca sabe! Por ejemplo, si por alguna circunstancia necesitan hacer su propio TEXTBOX le aseguro que tendría que usar todo lo hablado con anterioridad.

Vayamos al proyecto, para este programa es necesario los siguientes controles: 3 ListBox y 3 etiquetas o Label y un modulo. Luego pongan este código:

En el formulario pongan este código:

Ver Codigo

Debo mencionar que hay varias funciones encargadas de presentar texto en un HDC, particularmente voy a explicar la que yo considero mas completa y que cubre todo lo que hacen las demás funciones, hablo de la función DrawText.

Al arrancar el proyecto lo primero que vemos son 3 listbox, uno de ellos contiene las fuentes de tipo Raster, el otro las fuentes del tipo TrueType, y el último las fuentes de dispositivo, particularmente yo, no tenia ninguna fuente en el tercer listbox, es decir, las fuentes de dispositivo. También notaran que a medida que pasean por las fuentes es mostrado en la parte inferior un texto “Hola Mundo!”, con la fuente seleccionada. Empecemos explicando la función encargada de retornar la familia de fuentes instaladas en su computadora.

EnumFontFamilies

Api: Declare Function EnumFontFamilies Lib "gdi32" Alias "EnumFontFamiliesA" (ByVal hdc As Long, ByVal lpszFamily As String, ByVal lpEnumFontFamProc As Long, ByVal lParam As Long) As Long

Esta función se encarga de retornarnos las fuentes instaladas en nuestro sistema, existe otra función que es EnumFonts, pero esta que explicamos hace lo mismo y es un poco mas completa.

Como primer parámetro le pasamos el HDC, como segundo parámetro podemos filtrar las fuentes en base a la familia a la cual pertenecen, como tercer parámetro le pasamos la dirección de una función y por último podemos pasarle alguna información que nosotros creamos conveniente.

Este tipo de funciones es raro para ustedes, ya que es la primera vez que usamos una de este estilo. ¿Qué quiero decir?. Vean que como parámetro le pasamos la dirección de una función, eso nada mas lo habíamos hecho cuando cambiamos nuestro gestor de mensaje, en la sección de Sistema de coordenadas. Ustedes se preguntaran ¿Para que una función?. Vayamos al ejemplo:

EnumFontFamilies Me.hdc, vbNullString, AddressOf FamiliaLetras, ByVal 0&

Le pasamos nuestro HDC, indicamos “vbnullstring” en el segundo parámetro y con esto le indicamos que queremos a todas las familias de fuentes, como tercer parámetro le pasamos la dirección de la función que se encuentra en el modulo, y como no quiero pasar ninguna información adicional, le paso 0 al último parámetro.

Veamos la función en el modulo:

Function FamiliaLetras(lpNLF As ENUMLOGFONT, lpNTM As NEWTEXTMETRIC, ByVal FontType As Long, lParam As Long) As Long

Dim FaceName As String, Familia As String

FaceName = StrConv(lpNLF.elfLogFont.lfFaceName, vbUnicode)

If FontType And RASTER_FONTTYPE Then

frmfamilias.List1.AddItem Left$(FaceName, InStr(FaceName, vbNullChar) - 1)

cantidad(1) = cantidad(1) + 1

End If

.

.

.

.

FamiliaLetras = 1

End Function

Esta función tiene que cumplir cierta estructura, es decir, debe de recibir los parámetros especificados en el enunciado de dicha función. Como primer parámetro tenemos una variable de tipo ENUMLOGFONT, como segundo parámetro tenemos una variables de tipo NEWTEXTMETRIC, que es casi igual a TEXTMETRIC, tenemos como tercer parámetro una variable que nos informa del tipo de fuente que esta siendo pasada, y por último tenemos la información adicional que pudimos haber pasado cuando invocamos a la función EnumFontFamilies.

Es importante ver que hacemos FamiliaLetras = 1, con esto le decimos a Windows que todavía hay fuentes por verificar, es decir, lo que hace el programa es entrar a esta función tantas veces como fuentes existan. Por cada fuente que exista, Windows invocara a dicha función.

¿Qué hacemos adentro de la función?. En primer lugar tomamos el nombre de la fuente, eso lo hacemos con:

FaceName = StrConv(lpNLF.elfLogFont.lfFaceName, vbUnicode)

De la estructura ENUMLOGFONT, el parámetro que yo considero mas importante es la variable que es de tipo LOGFONT, los variables contenidas en esta estructura las explicare cuando explique la función CreateFont. Pero la única variable que nos interesa ahora es lfFaceName, la cual contiene el nombre de la fuente. ¿Por qué usar StrConv?. StrConv es una función de VB, si ven de que tipo es lfFaceName, se darán cuenta que un arreglo de Byte. Lo que hace StrConv, es convertir ese arreglo de Byte, en una cadena de texto completamente valida que se pueda almacenar en FaceName que es tipo String.

Después vemos un if:

If FontType And RASTER_FONTTYPE Then

Y con esto lo que hago es separar las fuentes por su tipo. Llenando como pueden ver los respectivos ListBox en los 3 IF que tiene la función.

Teniendo el nombre de las fuentes, ahora vamos ver las funciones necesarias para manipularlas.

CreateFont

Api: Declare Function CreateFont Lib "gdi32" Alias "CreateFontA" (ByVal nHeight As Long, ByVal nWidth As Long, ByVal nEscapement As Long, ByVal nOrientation As Long, ByVal nWeight As Long, ByVal Italic As Long, ByVal underline As Long, ByVal strikeout As Long, ByVal Charset As Long, ByVal OutPutPrecision As Long, ByVal ClipPrecicion As Long, ByVal Quality As Long, ByVal PitchAndFamily As Long, ByVal Face As String) As Long

Antes que nada esta función no es para crear una fuente nueva!. Esta función lo que hace es crear un objeto tipo fuente, cosa muy distinta. Es decir, se manipula como un objeto, igual como hacíamos con la brocha y pincel. Como pueden ver los parámetros son los mismos que las variables que se encuentran en la estructura LOGFONT. Por consiguiente la explicación de estos parámetros se aplica perfectamente a la estructura LOGFONT.

nHeight = Este parámetro establece la altura de la fuente. Pero dependiendo del valor que tenga se puede comportar de manera distinta.

Si es igual 0, el sistema toma la altura de la fuente por defecto.

Si es mayor que 0, la altura será la medida tmHeight, mostrada en la figura de arriba.

Si es menor que 0, la altura será la suma de tmHeight + tmInternalLeading.

Esta altura se establece en unidades lógica. Si trabajamos con MM_TEXT que es lo equivalente a trabajar con píxeles, la altura viene dada por la siguiente formula:

MulDiv(30, GetDeviceCaps(Me.hdc, LOGPIXELSY), 72)

Si quieren alterar el tamaño, modifiquen el primer parámetro de la función MulDiv, es decir, donde esta el 30. Al final explico estas dos funciones.

nWidth = determina el ancho de la fuente.

nEscapement = Aquí especificamos un ángulo de rotación, es decir, no necesariamente el texto se tiene que presentar de forma horizontal, el texto o carácter puede presentarse con cierto grado de inclinación. Ejm

Este parámetro toma el ángulo de rotación, si el valor es positivo se toma la rotación en sentido contrario a las agujas del reloj, en caso de ser negativo la rotación se realiza en sentido de las agujas del reloj. Los grados que vayan a pasar deben de ser multiplicado por 10, es decir, si queremos rotar 45 grados, tienen que colocar 450.

nOrientation = este parámetro solo funciona en Windows NT/2000, es un ángulo de giro, pero por carácter. Y antes de crear la fuente hay que llamar la función SetGraphicsMode, pasándole el parámetro GM_ADVANCED. Ejm:

Pueden ver como los caracteres están rotados o girados en cierto grado.

nWeight = Determina el grosor o peso de la fuente, es decir, si quieren poner en negritas un texto esta es la variables donde especifican la intensidad. Los valores a tomar pueden ser:

Private Const FW_BLACK = FW_HEAVY

Private Const FW_BOLD = 700

Private Const FW_DEMIBOLD = FW_SEMIBOLD

Private Const FW_DONTCARE = 0

Private Const FW_EXTRABOLD = 800

Private Const FW_EXTRALIGHT = 200

Private Const FW_HEAVY = 900

Private Const FW_LIGHT = 300

Private Const FW_MEDIUM = 500

Private Const FW_NORMAL = 400

Private Const FW_REGULAR = FW_NORMAL

Private Const FW_SEMIBOLD = 600

Private Const FW_THIN = 100

Private Const FW_ULTRABOLD = FW_EXTRABOLD

Private Const FW_ULTRALIGHT = FW_EXTRALIGHT

Italic = determina si la fuente se crea “cursiva”, si se coloca 1 la fuente será cursiva, en caso contrario deberán poner 0.

Underline = determina si la fuente estará “subrayada”, al igual que Italic el colocar 1 implica activar la propiedad y 0 no tomarla en cuenta.

StrikeOut = determina si la fuente estará “tachada”, al igual que Italic el colocar 1 implica activar la propiedad y 0 no tomarla en cuenta.

Charset = en este parámetro hablamos de los “grupos de caracteres” que explicamos arriba, es decir, algunos caracteres pueden tener diferentes representación dependiendo de la familia a la cual pertenece, por ejemplo, puede existir una Arial (Alemán) y una Arial (China), la letra “A” tiene distintas formas de representarse, pero ambas son Ariales, pero se representan de manera distinta. Los valores que puede tomar este parámetro son:

Private Const ANSI_CHARSET = 0

Private Const BALTIC_CHARSET = 186

Private Const CHINESEBIG5_CHARSET = 136

Private Const DEFAULT_CHARSET = 1

Private Const EASTEUROPE_CHARSET = 238

Private Const GB2312_CHARSET = 134

Private Const GREEK_CHARSET = 161

Private Const HANGUL_CHARSET = 129

Private Const HANGEUL_CHARSET = 129

Private Const MAC_CHARSET = 77

Private Const OEM_CHARSET = 255

Private Const RUSSIAN_CHARSET = 204

Private Const SHIFTJIS_CHARSET = 128

Private Const SYMBOL_CHARSET = 2

Private Const TURKISH_CHARSET = 162

OutPutPrecision = Aquí especificamos que tipo de fuente se quiere tomar, es decir, podemos tener una fuente Arial de tipo TrueType y otra Arial del tipo Raster, con este parámetro especificamos cual de las 2 queremos tomar, ya que ambas serían Arial, es decir, tienen el mismo nombre. Los valores que puede tomar son:

Private Const OUT_DEVICE_PRECIS = 5

Private Const OUT_DEFAULT_PRECIS = 0

Private Const OUT_OUTLINE_PRECIS = 8

Private Const OUT_RASTER_PRECIS = 6

Private Const OUT_TT_ONLY_PRECIS = 7

Private Const OUT_TT_PRECIS = 4

ClipPresicion = Aquí se especifica el como se recortaran los caracteres cuando la cadena sea mas grande que el área de recorte. Este es uno de los parámetros que todavía no entiendo bien lo que hace, pero como valores puede tomar:

Private Const CLIP_DEFAULT_PRECIS = 0

Private Const CLIP_CHARACTER_PRECIS = 1

Private Const CLIP_EMBEDDED = 128

Private Const CLIP_MASK = &HF

Private Const CLIP_STROKE_PRECIS = 2

Private Const CLIP_LH_ANGLES = 16

Private Const CLIP_TT_ALWAYS = 32

Quality = Con este parámetro establecemos la calidad de la letra, es decir, si ven una letra con un tamaño grande, verán que las líneas son como rectángulos uno encima de otro, con este parámetro podemos hacer lo que se llama ANTIALIASED, en donde esos rectángulos son manipulados para parecerse mas a una línea completamente recta, logrando así una calidad mucho mejor en el momento de mostrar los caracteres. Los valores que puede tomar:

Private Const ANTIALIASED_QUALITY = 4 ‘ Mejora la calidad de presentación

Private Const DEFAULT_QUALITY = 0 ‘ Dejamos a Windows que tome valores por defecto

Private Const DRAFT_QUALITY = 1 ‘ Este es como la calidad media, no muy buena, pero tan poco ni muy mala.

Private Const NONANTIALIASED_QUALITY = 3 ‘ Se presentán sin antidentado

Private Const PROOF_QUALITY = 2

PitchAndFamily = Este parámetro debe ser la combinación de 2 constante. La primera determina el “Pitch” este puede tomar los siguientes valores:

Private Const FIXED_PITCH = 1

Private Const VARIABLE_PITCH = 2

Private Const DEFAULT_PITCH = 0

La segunda constante determina la familia a la cual pertenece la fuente, cuyos valores de la constante fueron especificados arriba cuando se hablo del tema. Para combinarlos hay que usar el operador “OR”. Ejm: FF_ROMAN Or VARIABLE_PITCH. En realidad el termino “Pitch” no lo tengo muy claro, por lo que dejo a las personas que quieran averiguar, leer la documentación oficial.

FaceName = Es una cadena que identifica el nombre de la fuente.

Como pueden ver cada parámetro tiene muchas opciones, y se pueden hacer muchas combinaciones, en nuestro proyecto trabajamos de manera sencilla:

fuente = CreateFont(-MulDiv(30, GetDeviceCaps(Me.hdc, LOGPIXELSY), 72), 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFUALT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, letra)

Ya que utilice en su mayor parte los valores por defecto. Excepto en ANTIALIASED_QUALITY en donde verán que las fuentes TrueType se ven de manera nítida, si quieren ver la diferencia quiten esta constante y pongan DEFAULT_QUALITY y notaran cierta diferencia.

La variable “fuente” es quien contiene nuestro nuevo objeto “fuente”, en caso de algún error en la creación de la fuente, la variable “fuente” contendrá NULL. En caso de ser diferente de NULL implica que la creación de la fuente fue exitosa. Y procedemos a colocarla en nuestro HDC.

SelectObject Me.hdc, fuente

Y al final usamos:

DeleteObject fuente

Para liberar el espacio de memoria.

Ahora que tenemos una nueva fuente, procedemos a usarla, para ello debemos de pintar un texto con las herramientas que Windows posee, y como dije trabajare solo con DrawText, la función mas sencilla es TextOut, pero como es sencilla les dejo a ustedes dicha documentación, si logran entender DrawText les aseguro que la función TextOut no la van a usar.

DrawText

Api: Declare Function DrawText Lib "user32" Alias "DrawTextA" (ByVal hdc As Long, ByVal lpStr As String, ByVal nCount As Long, lpRect As RECT, ByVal wFormat As Long) As Long

Esta función se encarga de pintar una cadena de texto en un HDC, el cual es su primer parámetro, como segundo parámetro tenemos la cadena de texto que queremos mostrar, como tercer parámetro tenemos la longitud de dicha cadena, como cuarto parámetro tenemos un rectángulo que delimita a dicha cadena. Y el parámetro más importante es “wFormat” que es la forma de cómo manipulamos la cadena de texto al momento de imprimirla.

Pueden ver que hay un rectángulo, eso significa que la cadena de texto se imprimirá en el recuadro que pasamos en “lpRect”, si la cadena es muy larga, ella no se imprime completa, a menos que sepamos manipular los valores que puede tener “wFormat”.

wFormat = puede tomar la combinación de los siguientes parámetro, antes de ir a estos valores noten que el proyecto tiene 7 bloques de comentarios y esta activado el último que es el mas sencillo, a medida que explique algunos valores de “wFormat” vamos a ir quitando los comentarios a algunas líneas.

Valores que puede tomar.

Private Const DT_BOTTOM = &H8

Utilizada par alinear el texto en la parte inferior del rectángulo. Es necesario combinarlo con DT_SINGLELINE

Private Const DT_CALCRECT = &H400

Este parámetro hace que no se imprima nada, lo único que hace es calcular el rectángulo que puede contener al texto, es decir, aquí la variable del parámetro “lpRect” se le pasa sin valores, la función lo que hace es calcular el ancho y alto que ocupa dicha cadena.

Private Const DT_CENTER = &H1

Utilizado para centrar el texto horizontalmente en el rectángulo especificado

Private Const DT_TOP = &H0

Alinea el texto en la parte superior del rectángulo

Private Const DT_VCENTER = &H4

Alinea el texto en el centro, pero verticalmente.

Private Const DT_LEFT = &H0

Alinea el texto hacia la izquierda del rectángulo

Private Const DT_EDITCONTROL = &H2000

Si se activa esta propiedad, le decimos a la función, que si el Texto no cabe en el rectángulo, que no lo imprima.

Private Const DT_END_ELLIPSIS = &H8000

Esta propiedad, hace que si la línea es muy grande y no cabe en el ancho del rectángulo, pues la función podrá cortar dicha cadena y ponerle 3 puntos al final, Ejm = “Hola Mun…”. Este parámetro solo es usado cuando la propiedad DT_SINGLELINE esta activada.

Private Const DT_EXTERNALLEADING = &H200

Este parámetro hace que se le sume a la altura de la línea la cantidad establecida por el diseñador de la fuente como distancia entre líneas.

Private Const DT_HIDEPREFIX = &H100000

Solo funcional en Windows 2000, lo que hace es que si existe un & el lo sustituye con un subrayado al carácter delante de él. Ejm “La Cade&na” = “La Cadena”, en caso de poner && entonces el sistema tomara el carácter &.

Private Const DT_NOPREFIX = &H800

Deshabilita la propiedad antes mencionada (DT_HIDEPREFIX).

Private Const DT_PREFIXONLY = &H200000

Igual que DT_HIDEPREFIX con la diferencia que no imprime el carácter. Ejm “La Cade&na” = “La Cade_a”

Private Const DT_INTERNAL = &H1000

Supongo que esta funciona si la usamos con el DT_CALCRECT, ya que lo que hace es realizar cualquier operación de cálculo, con la fuente del sistema, sin importar la fuente activada en el HDC

Private Const DT_NOCLIP = &H100

No toma en cuenta el rectángulo especificado en el parámetro “lpRect”

Private Const DT_RTLREADING = &H20000

En este caso la función imprimirá de derecha a izquierda, y no de izquierda a derecha. He de suponer que esto se utiliza en caso que deseen usar letras Árabes.

Private Const DT_SINGLELINE = &H20

Con esta propiedad lo que hacemos es indicarle a la función que solo imprima en una sola línea.

Private Const DT_WORDBREAK = &H10

Este es el caso Multiline, si el texto no cabe en una línea se imprime en las líneas que sean necesarias, por supuesto, verán lo que entre en el rectángulo.

En la documentación oficial encontraran un poco mas de constante, pero considero que hable la mas importantes.

NOTA: El rectángulo enmarcado que imprimí con la función Rectangle, no es mas que un dibujo indicativo, es decir, ese rectángulo visualiza el área de impresión del texto, siendo mas fácil entender las distintas constantes explicadas arriba.

Veamos el ejemplo más simple, que se encuentra en el proyecto:

cadena = "Hola Mundo!"

DrawText Me.hdc, cadena, Len(cadena), r, DT_TOP

Imprimimos una cadena, alineada en la parte superior del rectángulo. Pero ahora veamos que pasa si jugamos con los parámetros. Pongan en comentario al bloque de código correspondiente al comentario 7 y quitenle los comentarios a las líneas del comentario 3:

cadena = "Hola! esta es una cadena muyyyy larga"

DrawText Me.hdc, cadena, Len(cadena), r, DT_TOP Or DT_CENTER Or DT_WORDBREAK

Se darán cuenta que al pasar por diferentes tipos de fuentes, si la cadena no entra en el rectángulo, la cadena es cortada e impresa en otra línea. Pongan en comentario las líneas del comentario 3 y activen el comentario 4.

cadena = "Hola! esta es una cadena muyyyy larga"

DrawText Me.hdc, cadena, Len(cadena), r, DT_TOP Or DT_CENTER Or DT_END_ELLIPSIS Or DT_MODIFYSTRING

En este caso, si la línea no entra en el rectángulo, es cortada por la función mostrando 3 puntos al final.

Los demás comentarios serán explicados con las funciones nuevas que las acompaña. Y empecemos con el comentario 1. Si activamos las líneas del bloque comentario 1, verán que al imprimir se muestra un fondo azul, y las letras blancas.

SetBkMode

Api: Declare Function SetBkMode Lib "gdi32" (ByVal hdc As Long, ByVal nBkMode As Long) As Long

Con esta función establecemos si el fondo de los caracteres es transparente u opaco. Es decir, el parámetro “nBkMode” puede tomar 2 valores:

Const OPAQUE = 2

Const TRANSPARENT = 1

Es idéntico a lo que hace la propiedad “BackStyle” de los controles “Label”.

SetBkColor

Api: Declare Function SetBkColor Lib "gdi32" (ByVal hdc As Long, ByVal crColor As Long) As Long

En caso que el fondo sea Opaco, se tomara el color que se le pase a esta función en su segundo parámetro “crColor”, en caso de ser transparente, pues no se toma en cuenta.

SetTextColor

Api: Declare Function SetTextColor Lib "gdi32" (ByVal hdc As Long, ByVal crColor As Long) As Long

Con esta función determinamos el color de las letras. El color se le pasa en el segundo parámetro.

GetTextExtentPoint32

Api: Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long

Esta función sirve para calcular la longitud en unidades lógicas de una cadena. En el parámetro “lpsz” se le pasa dicha cadena, en el parámetro “cbString” se le pasa la longitud de la cadena, y en el parámetro “lpSize”, se la pasa una variable de tipo Size, que contiene un X e Y. X es lo que abarca la cadena Horizontalmente e Y tendrá la longitud vertical.

En el ejemplo, se usa la función Rectangle para enmarcar dicha cadena. Para verlo en el ejemplo quiten los comentarios al bloque de código “comentario 2”.

SetTextJustification

Api: Declare Function SetTextJustification Lib "gdi32" (ByVal hdc As Long, ByVal nBreakExtra As Long, ByVal nBreakCount As Long) As Long

Esta función permite alinear un texto de manera Justificada. El problema esta en los parámetros que hay que pasarles a la función para que ella haga la alineación. Primero le pasamos el hdc, el segundo parámetro es el espacio vacío, es decir:

En el tercer parámetro le indicamos cuantos espacios en blanco hay entre cada palabra. En nuestro ejemplo usamos "Hola otra vez Mundo!", como pueden ver esta cadena posee 4 espacios en blanco.

Con estos datos la función adapta la cadena según el ancho especificado en nBreakExtra.

Para ver el ejemplo de esta función quitenle los comentarios al bloque de líneas “comentario 5”.

SetTextCharacterExtra

Api: Declare Function SetTextCharacterExtra Lib "gdi32" (ByVal hdc As Long, ByVal nCharExtra As Long) As Long

Un carácter tiene cierta separación de otro carácter, esa separación puede ser controlada, esta es la función a cargo de controlar ese espacio. Es fácil ver lo que hace, quitenle los comentarios al bloque del comentario 6. Este espacio es pasado a través del parámetro “nCharExtra”.

Otras funciones

En esta sección trabajamos algunas funciones que no se usan en el proyecto, pero son importantes mencionarlas.

GetTextCharacterExtra

Api: Declare Function GetTextCharacterExtra Lib "gdi32" (ByVal hdc As Long) As Long

GetTextColor

Api: Declare Function GetTextColor Lib "gdi32" (ByVal hdc As Long) As Long

GetBkColor

Api: Declare Function GetBkColor Lib "gdi32" (ByVal hdc As Long) As Long

GetBkMode

Api: Declare Function GetBkMode Lib "gdi32" (ByVal hdc As Long) As Long

Normalmente toda función que empieza con Set tiene su Get, con estas 5 funciones lo que hacemos es tomar el valor correspondiente dependiendo de la función. Basta con asignar a una variable el valor que retorna la función.

GetTextMetrics

Api: Declare Function GetTextMetrics Lib "gdi32" Alias "GetTextMetricsA" (ByVal hdc As Long, lpMetrics As TEXTMETRIC) As Long

Esta función es utilizada para tomar la información correspondiente a la fuente que se encuentra en el dispositivo de contexto. Esa información se encuentra en la variables “lpMetricS” que es de tipo TEXTMETRIC, parámetros explicados con anterioridad.

MulDiv

Api: Declare Function MulDiv Lib "kernel32" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long

Esta función multiplica 2 valores de 32 Bits, y el resultado de 64 Bits lo divide por un tercer valor de 32 Bits.

nNumber = es el multiplicando.

nNumerator = es con el que se multiplica.

nDenominator = es el denominador de dividir la multiplicación de los dos de arriba.

Si la función es exitosa ella retorna dicho resultado, de lo contrario retorna –1.

La función GetDeviceCaps la explicare en un capitulo donde ella se encuentra.

<-- Capítulo II

Regresar arriba

Regresar a la página principal

Capítulo IV -->

Desarrollado por Eduardo Roa. Copyright 2002-2003 Todos los derechos reservados