SDK Multiplataforma en C logo

SDK Multiplataforma en C

Primitivas de dibujo

❮ Anterior
Siguiente ❯

Funciones

voiddraw_line (...)
voiddraw_polyline (...)
voiddraw_arc (...)
voiddraw_bezier (...)
voiddraw_line_color (...)
voiddraw_line_fill (...)
voiddraw_line_width (...)
voiddraw_line_cap (...)
voiddraw_line_join (...)
voiddraw_line_dash (...)
voiddraw_rect (...)
voiddraw_rndrect (...)
voiddraw_circle (...)
voiddraw_ellipse (...)
voiddraw_polygon (...)
voiddraw_fill_color (...)
voiddraw_fill_linear (...)
voiddraw_fill_matrix (...)
voiddraw_fill_wrap (...)
voiddraw_font (...)
voiddraw_text_color (...)
voiddraw_text (...)
voiddraw_text_path (...)
voiddraw_text_width (...)
voiddraw_text_trim (...)
voiddraw_text_align (...)
voiddraw_text_halign (...)
voiddraw_text_extents (...)
voiddraw_image (...)
voiddraw_image_frame (...)
voiddraw_image_align (...)

En el capítulo anterior realizamos una introducción al dibujo vectorial y vimos como trabajar con contextos 2D. Ahora veremos como dibujar líneas, figuras, texto e imágenes. En ¡Hola Draw2d! tienes la aplicación que integra todo lo que iremos viendo a continuación.


1. Dibujo de líneas

La operación más elemental es dibujar una recta entre dos puntos. En contextos 2d las líneas son objetos sólidos y no una mera hilera de píxeles. Pensemos que estamos utilizando rotuladores de punta gruesa, donde la línea teórica permanecerá siempre en el centro del trazo (Figura 1). Podemos cambiar la forma de las terminaciones (linecap), de las uniones (linejoin) y establecer un patrón para líneas discontinuas.


2. Figuras y contornos

Para dibujar figuras o áreas cerradas tenemos los siguientes comandos:

Como vemos en (Figura 2) podemos dibujar el contorno de la figura, su interior o ambos. Para el contorno se tendrá en cuenta el estilo de línea establecido según hemos visto en el apartado anterior.

Imagen con diferentes figuras geométricas: Contornos y rellenos.
Figura 2: Solo contorno ekSTROKE. Solo relleno ekFILL. Primero contorno, después relleno ekSKFILL. Primero relleno, después contorno ekFILLSK.

Como ya vimos en Contextos 2D, el orden en que se efectúan las operaciones importa. No es lo mismo rellenar y luego dibujar el contorno que viceversa. El centro del trazo coincidirá con el contorno teórico de la figura.


3. Gradientes

Los gradientes permiten rellenar regiones utilizando un degradado en lugar de un color sólido (Figura 3). Se definen varios colores base y su posición relativa a lo largo de un vector (Listado 1). Las posiciones [0, 1] corresponden a los extremos y los valores dentro de este rango a las posibles paradas intermedias. Cada línea perpendicular al vector delimita un color uniforme que se extenderá indefinidamente hasta alcanzar los límites de la figura a rellenar.

3.1. Transformación del gradiente

Dado que el gradiente se define mediante un vector, es posible establecer una transformación que cambie la forma en la que es aplicado. Esta matriz es totalmente independiente a la que se aplica a las primitivas de dibujo draw_matrix, como vimos en Sistemas de referencia.

  • Utiliza draw_fill_matrix para establecer la transformación del gradiente. Con esto podemos conseguir varios efectos:
  • Gradiente global: El degradado se aplicará de forma global al fondo, y las figuras serán recortes del mismo patrón (Figura 4). Para ello estableceremos la matriz identidad como transformación del gradiente (Listado 2). Es la definida por defecto.
  • Animación de un gradiente lineal variando la dirección.
    Figura 4: Gradiente global. No se pierde la continuidad entre figuras.
    Listado 2: Matriz del gradiente para todo el dibujo.
    1
    2
    3
    
    draw_fill_linear(ctx, c, stop, 2, 0, 0, 600, 400);
    draw_fill_matrix(ctx, kT2D_IDENTITYf);
    i_draw_shapes(ctx);
    
  • Gradiente local: El vector es trasladado al origen de la figura o a un punto de su entorno cercano (Figura 5). Con esto, conseguiremos aplicar el degradado de forma local y que solo afecte a una figura concreta. En (Listado 3) hemos variado ligeramente la transformación para fijar el origen en una esquina y no en el centro de la elipse. Esto puede variar en función del efecto deseado.
  • Animación de varias figuras rellenas con un gradiente lineal variando la dirección.
    Figura 5: Gradiente local. El origen se sitúa en la figura.
    Listado 3: Matriz del gradiente para una figura.
    1
    2
    3
    4
    5
    6
    7
    
    t2d_movef(&matrix, kT2D_IDENTITYf, 250, 280);
    t2d_rotatef(&matrix, &matrix, - kBMATH_PIf / 10);
    draw_matrix(ctx, &matrix);
    draw_fill_linear(ctx, c, stop, 2, 0, 0, 200, 100);
    t2d_movef(&matrix, &matrix, -100, -50);
    draw_fill_matrix(ctx, &matrix);
    draw_ellipse(ctx, ekSKFILL, 0, 0, 100, 50); 
    

3.2. Gradientes en líneas

Además del relleno de regiones, los gradientes también pueden aplicarse a líneas y contornos (Figura 6) (Listado 4).

3.3. Límites del gradiente

Como hemos comentado, el relleno de color se extenderá de manera uniforme e indefinida por todas las líneas perpendiculares al vector, pero... ¿Que ocurre fuera de los límites de este vector? En (Listado 5) (Figura 7) el gradiente se ha definido en x=[200, 400], siendo esta medida inferior a la figura a rellenar:

  • Utiliza draw_fill_wrap para definir el comportamiento del gradiente fuera de límites.
  • ekFCLAMP se utiliza el valor del extremo como constante en el área exterior.
  • ekFTILE se repite el patrón de color.
  • ekFFLIP se repite el patrón, pero invirtiendo el sentido lo que evita la pérdida de continuidad en el color.
  • Listado 5: Color uniforme fuera de los límites del gradiente (Figura 7) (a).
    1
    2
    3
    
    draw_fill_linear(ctx, c, stop, 2, 200, 0, 400, 0);
    draw_fill_wrap(ctx, ekFCLAMP);
    draw_rect(ctx, ekFILLSK, 50, 25, 500, 100);
    
    Gradientes con diferentes tratamientos en el área exterior.
    Figura 7: Comportamiento en los límites: (a) ekFCLAMP, (b) ekFTILE, (c) ekFFLIP.

4. Dibujar texto

El renderizado del texto es la parte más importante de la interfaz de usuario. Antiguamente, se utilizaban pequeños bitmaps con la imagen de cada carácter, pero a principios de los 90 entraron en juego las fuentes vectoriales basadas en curvas de Bezier. La gran cantidad de tipografías, el inmenso juego de carácteres Unicode y la posibilidad de escalar, rotar, o maquetar el texto en párrafos supuso un gran reto técnico en aquellos años. Afortunadamente, toda esta casuística está ampliamente resuelta por las APIs nativas de cada sistema operativo, lo que nos permite proporcionar una interfaz simplificada para añadir texto a nuestros contextos gráficos.

Para dibujar textos de una sola línea, tan solo debemos llamar a la función pertinente, pasando una cadena UTF8 (Listado 6) (Figura 8). Previamente, podemos asignar la fuente, el color y su alineación.

Listado 6: Dibujo de una línea de texto.
1
2
3
4
5
Font *font = font_system(20, 0);
draw_font(ctx, font);
draw_text_color(ctx, kCOLOR_BLUE);
draw_text_align(ctx, ekLEFT, ekTOP);
draw_text(ctx, "Text 文本 Κείμενο", 25, 25);
Dibujo de una cadena de texto varias veces.
Figura 8: Textos de una sola línea, con alineación y transformaciones.

Si la cadena a visualizar tiene saltos de línea (carácter '\n') se tendrán en cuenta y el texto se mostrará en varias líneas (Listado 7) (Figura 9). También es posible obtener su medida en píxeles, útil para evitar solapamientos con otras primitivs.

Listado 7: Dibujo de textos con saltos de línea.
1
2
3
4
const char_t *text = "Text new line\n文字换行\nΓραμμή κειμένου";
real32_t width, height;
draw_text(ctx, text, 25, 25);
draw_text_extents(ctx, text, -1, &width, &height);
Dibujo varias veces de una cadena de texto que contiene caracteres nueva línea.
Figura 9: Textos con carácter '\n'.

Si el texto no contiene saltos de línea, se dibujará de forma contínua expandiéndose horizontalmente. Esto puede no ser lo más apropiado en párrafos largos, por lo que podemos establecer un ancho máximo, forzando su dibujo en varias líneas (Listado 8) (Figura 10).

Listado 8: Anchura máxima y alineación interna en bloques de texto.
1
2
3
4
5
const char_t *text = "Lorem ipsum dolor sit amet...consequat";
draw_text_width(ctx, 200);
draw_text_halign(ctx, ekLEFT);
draw_text(ctx, text, 25, 25);
draw_text_extents(ctx, text, 200, &width1, &height1);
Dibujo de bloques de texto con varias anchuras.
Figura 10: Párrafos de texto con límite de anchura. Se muestra la anchura máxima y la real obtenida con draw_text_extents.

Por último, podemos utilizar draw_text_path para tratar al texto como cualquier otra región geométrica, resaltando el borde o rellenando con gradientes. En este caso draw_text_color no tendrá efecto y se utilizarán los valores de draw_fill_color, draw_fill_linear y draw_line_color (Listado 9) (Figura 11).

Listado 9: Texto con borde punteado y relleno con gradiente.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
color_t c[2];
real32_t stop[2] = {0, 1};
real32_t dash[2] = {1, 1};
c[0] = kCOLOR_BLUE;
c[1] = kCOLOR_RED;
draw_line_dash(ctx, dash, 2);
draw_line_color(ctx, kCOLOR_GREEN);
draw_text_extents(ctx, "Gradient dashed text", -1, &width, &height);
draw_fill_linear(ctx, c, stop, 2, 25, 0, 25 + width, 0);
draw_text_path(ctx, ekFILLSK, "Gradient dashed text", 25, 250);
Texto con diferentes estilos de borde y relleno.
Figura 11: Combinando rellenos y bordes.
draw_text en mucho más rápida que draw_text_path, por lo que debemos delimitar el uso de esta última a lo estrictamente necesario.

5. Dibujar imágenes

Imágenes generadas de forma procedimental o leídas desde disco pueden utilizarse como una primitiva de dibujo más (Listado 10) (Figura 12). Al igual que ocurre con el texto u otras figuras, la transformación del contexto afectará a la geometría de la imagen.


6. Parámetros por defecto

Cada contexto mantiene ciertos parámetros de estado. Al comienzo del dibujo, bien mediante el método OnDraw o tras crear el contexto con dctx_bitmap los valores por defecto son los mostrados en (Tabla 1):

Tabla 1: Valores por defecto en contextos 2D.
Parámetro Valor Cambiar con
Matrix Identidad (0,0) Esquina Sup-Izq, píxeles. draw_matrix
Antialiasing TRUE draw_antialias
LineColor kCOLOR_BLACK draw_line_color
LineWidth 1 draw_line_width
Linecap ekLCFLAT draw_line_cap
Linejoin ekLJMITER draw_line_join
LineDash Sólido draw_line_dash
TextColor kCOLOR_BLACK draw_text_color
FillColor kCOLOR_BLACK draw_fill_color
Font System default, regular size. draw_font
Text max width -1 draw_text_width
Text vertical align ekLEFT draw_text_align
Text horizontal align ekTOP draw_text_align
Text internal align ekLEFT draw_text_halign
Image vertical align ekLEFT draw_image_align
Image horizontal align ekTOP draw_image_align

draw_line ()

Dibuja una línea.

void
draw_line(DCtx *ctx,
          const real32_t x0,
          const real32_t y0,
          const real32_t x1,
          const real32_t y1);
ctx

Contexto de dibujo.

x0

Coordenada x del primer punto.

y0

Coordenada y del primer punto.

x1

Coordenada x del segundo punto.

y1

Coordenada y del segundo punto.


draw_polyline ()

Dibuja varias líneas unidas.

void
draw_polyline(DCtx *ctx,
              const bool_t closed,
              const V2Df *points,
              const uint32_t n);
ctx

Contexto de dibujo.

closed

TRUE para unir el último punto con el primero.

points

Array de puntos que forman la polilínea.

n

Número de puntos.


draw_arc ()

Dibuja un arco (segmento circular).

void
draw_arc(DCtx *ctx,
         const real32_t x,
         const real32_t y,
         const real32_t radius,
         const real32_t start,
         const real32_t sweep);
ctx

Contexto de dibujo.

x

Coordenada x del centro del arco.

y

Coordenada y del centro del arco.

radius

Radio del arco.

start

Ángulo inicial con respecto al vector X=[1,0] en radianes.

sweep

Ángulo de barrido o tamaño del arco en radianes.

Observaciones

Los ángulos positivos son los que giran desde el vector X al vector Y. Ver Vectores 2D.


draw_bezier ()

Dibuja una curva de Bézier cúbica (grado 3) utilizando dos extremos (x0,y0)-(x3,y3) y dos puntos de control intermedios (x1,y1)-(x2,y2).

void
draw_bezier(DCtx *ctx,
            const real32_t x0,
            const real32_t y0,
            const real32_t x1,
            const real32_t y1,
            const real32_t x2,
            const real32_t y2,
            const real32_t x3,
            const real32_t y3);
ctx

Contexto de dibujo.

x0

Coordenada x del punto inicial.

y0

Coordenada y del punto inicial.

x1

Coordenada x del primer punto intermedio.

y1

Coordenada y del primer punto intermedio.

x2

Coordenada x del segundo punto intermedio.

y2

Coordenada y del segundo punto intermedio.

x3

Coordenada x del punto final.

y3

Coordenada y del punto final.


draw_line_color ()

Establece el color de dibujo de líneas y contornos.

void
draw_line_color(DCtx *ctx,
                const color_t color);
ctx

Contexto de dibujo.

color

Color de línea.


draw_line_fill ()

Establece el patrón de relleno actual para el dibujo de líneas.

void
draw_line_fill(DCtx *ctx);
ctx

Contexto de dibujo.

Observaciones

El patrón de relleno debe haber sido establecido previamente mediante draw_fill_linear. Ver Gradientes en líneas.


draw_line_width ()

Establece el grosor de línea.

void
draw_line_width(DCtx *ctx,
                const real32_t width);
ctx

Contexto de dibujo.

width

Grosor de línea.


draw_line_cap ()

Establece el estilo de los extremos de línea.

void
draw_line_cap(DCtx *ctx,
              const linecap_t cap);
ctx

Contexto de dibujo.

cap

Estilo.


draw_line_join ()

Establece el estilo de las uniones de línea.

void
draw_line_join(DCtx *ctx,
               const linejoin_t join);
ctx

Contexto de dibujo.

join

Estilo de la unión.


draw_line_dash ()

Establece un patrón para el dibujo de líneas.

void
draw_line_dash(DCtx *ctx,
               const real32_t *pattern,
               const uint32_t n);
ctx

Contexto de dibujo.

pattern

Array de valores que definen el patrón.

n

Número de valores.

Observaciones

El primer elemento de pattern define la longitud del primer trazo y el segundo la del primer hueco. Así sucesivamente. Las longitudes se escalan por el grosor de línea draw_line_width, es decir, un trazo de longitud 1 dibujará un cuadrado de lado line_width. Longitudes de valor 2 equivalen al doble del grosor de línea, etc. El patrón escalará proporcionalmente al cambiar el grosor o hacer zoom mediante transformaciones.


draw_rect ()

Dibuja un rectángulo.

void
draw_rect(DCtx *ctx,
          const drawop_t op,
          const real32_t x,
          const real32_t y,
          const real32_t width,
          const real32_t height);
ctx

Contexto de dibujo.

op

Operación de dibujo.

x

Coordenada x de la esquina superior izquierda del rectángulo.

y

Coordenada y de la esquina superior izquierda del rectángulo.

width

Ancho del rectángulo.

height

Alto del rectángulo.

Observaciones

En Sistemas cartesianos (x,y) indican el origen de la esquina inferior izquierda.


draw_rndrect ()

Dibuja un rectángulo con los bordes redondeados.

void
draw_rndrect(DCtx *ctx,
             const drawop_t op,
             const real32_t x,
             const real32_t y,
             const real32_t width,
             const real32_t height,
             const real32_t radius);
ctx

Contexto de dibujo.

op

Operación de dibujo.

x

Coordenada x de la esquina superior izquierda.

y

Coordenada y de la esquina superior izquierda.

width

Ancho del rectángulo.

height

Alto del rectángulo.

radius

Radio de curvatura de la esquina.

Observaciones

En Sistemas cartesianos (x,y) indican el origen de la esquina inferior izquierda.


draw_circle ()

Dibuja un círculo.

void
draw_circle(DCtx *ctx,
            const drawop_t op,
            const real32_t x,
            const real32_t y,
            const real32_t radius);
ctx

Contexto de dibujo.

op

Operación de dibujo.

x

Coordenada x del centro.

y

Coordenada y del centro.

radius

Radio.


draw_ellipse ()

Dibuja una elipse.

void
draw_ellipse(DCtx *ctx,
             const drawop_t op,
             const real32_t x,
             const real32_t y,
             const real32_t radx,
             const real32_t rady);
ctx

Contexto de dibujo.

op

Operación de dibujo.

x

Coordenada x del centro.

y

Coordenada y del centro.

radx

Radio en el eje X.

rady

Radio en el eje Y.


draw_polygon ()

Dibuja un polígono.

void
draw_polygon(DCtx *ctx,
             const drawop_t op,
             const V2Df *points,
             const uint32_t n);
ctx

Contexto de dibujo.

op

Operación de dibujo.

points

Array de puntos que forman el polígono.

n

Número de puntos.


draw_fill_color ()

Establece un color sólido para el relleno de figuras.

void
draw_fill_color(DCtx *ctx,
                const color_t color);
ctx

Contexto de dibujo.

color

Color de relleno.


draw_fill_linear ()

Establece un gradiente (o degradado) para el relleno de figuras.

void
draw_fill_linear(DCtx *ctx,
                 const color_t *color,
                 const real32_t *stop,
                 const uint32_t n,
                 const real32_t x0,
                 const real32_t y0,
                 const real32_t x1,
                 const real32_t y1);
ctx

Contexto de dibujo.

color

Array de colores.

stop

Posiciones de los colores.

n

Número de posiciones/colores.

x0

Coordenada x del punto inicial.

y0

Coordenada y del punto inicial.

x1

Coordenada x del punto final.

y1

Coordenada y del punto final.

Observaciones

Las posiciones deben ir del valor 0 al 1. Ver Gradientes.


draw_fill_matrix ()

Establece la matriz de transformación del patrón de relleno.

void
draw_fill_matrix(DCtx *ctx,
                 const T2Df *t2d);
ctx

Contexto de dibujo.

t2d

Transformación.

Observaciones

Solo será efectiva en rellenos no sólidos. Ver Gradientes.


draw_fill_wrap ()

Establece el comportamiento del gradiente o patrón de relleno en los límites.

void
draw_fill_wrap(DCtx *ctx,
               const fillwrap_t wrap);
ctx

Contexto de dibujo.

wrap

Comportamiento en el borde del relleno.

Observaciones

Solo será efectiva en rellenos no sólidos. Ver Gradientes.


draw_font ()

Establece la fuente para el dibujado de textos.

void
draw_font(DCtx *ctx,
          const Font *font);
ctx

Contexto de dibujo.

font

Fuente tipográfica.

Observaciones

Tendrá efecto a partir del siguiente texto dibujado. Ver Fuentes tipográficas.


draw_text_color ()

Establece el color del texto.

void
draw_text_color(DCtx *ctx,
                const color_t color);
ctx

Contexto de dibujo.

color

Color.


draw_text ()

Dibuja un bloque de texto.

void
draw_text(DCtx *ctx,
          const char_t *text,
          const real32_t x,
          const real32_t y);
ctx

Contexto de dibujo.

text

Cadena UTF8, terminada en carácter nulo '\0'.

x

Coordenada x en el lienzo del origen del texto.

y

Coordenada y en el lienzo del origen del texto.

Observaciones

El texto se dibujará con la fuente y color preestablecido y será sensible a la transformación del contexto (rotaciones, escalados, etc). Ver Dibujar texto.


draw_text_path ()

Dibuja un bloque de texto como un área geométrica. Similar a draw_text, pero permite utilizar gradientes o dibujar sólo el borde del texto.

void
draw_text_path(DCtx *ctx,
               const drawop_t op,
               const char_t *text,
               const real32_t x,
               const real32_t y);
ctx

Contexto de dibujo.

op

Operación de dibujo.

text

Cadena UTF8, terminada en carácter nulo '\0'.

x

Coordenada x en el lienzo del origen del texto.

y

Coordenada y en el lienzo del origen del texto.

Observaciones

El texto se dibujará con la fuente y estilo (relleno y línea) preestablecido y será sensible a la transformación del contexto. Ver Dibujar texto.


draw_text_width ()

Establece el ancho máximo de los bloques de texto.

void
draw_text_width(DCtx *ctx,
                const real32_t width);
ctx

Contexto de dibujo.

width

Anchura máxima.

Observaciones

Si el texto a dibujar con draw_text es más ancho que width, se fragmentará en varias líneas. Pasar -1 para dibujar todo el bloque en una sola línea. No se tiene en cuenta el escalado del contexto. La medición se realiza en función del tamaño de la fuente preestablecida. Ver Dibujar texto.


draw_text_trim ()

Establece como el texto será recortado cuando sea más ancho que el valor de draw_text_width.

void
draw_text_trim(DCtx *ctx,
               const ellipsis_t ellipsis);
ctx

Contexto de dibujo.

ellipsis

Estilo de recorte.


draw_text_align ()

Establece la alineación del texto con respecto al punto de inserción.

void
draw_text_align(DCtx *ctx,
                const align_t halign,
                const align_t valign);
ctx

Contexto de dibujo.

halign

Alineación horizontal.

valign

Alineación vertical.

Observaciones

El punto de inserción es la coordenada (x,y) de draw_text. Ver Dibujar texto.


draw_text_halign ()

Establece la alineación horizontal interna del texto, dentro de un bloque multilínea.

void
draw_text_halign(DCtx *ctx,
                 const align_t halign);
ctx

Contexto de dibujo.

halign

Alineación horizontal.

Observaciones

En textos de una sola línea, no tiene efecto. Ver Dibujar texto.


draw_text_extents ()

Calcula el tamaño que ocupa un bloque de texto.

void
draw_text_extents(DCtx *ctx,
                  const char_t *text,
                  const real32_t refwidth,
                  real32_t *width,
                  real32_t *height);
ctx

Contexto de dibujo.

text

Texto.

refwidth

Ancho máximo de referencia.

width

Ancho del bloque.

height

Alto del bloque.

Observaciones

Si refwidth es mayor de 0, width estará acotado por este valor y height se expandirá hasta dar cabida a todo el texto. Tiene en cuenta los posible saltos de línea '\n' de text.


draw_image ()

Dibuja una imagen.

void
draw_image(DCtx *ctx,
           const real32_t x,
           const real32_t y);
ctx

Contexto de dibujo.

x

Coordenada x en el lienzo del origen de la imagen.

y

Coordenada y en el lienzo del origen de la imagen.

Observaciones

La imagen se dibujará a su tamaño natural y en la posición indicada. Utilizar draw_matrix para realizar escalados y rotaciones. Ver Dibujar imágenes.


draw_image_frame ()

Igual que draw_image, pero indicando el número de secuencia de una animación.

void
draw_image_frame(DCtx *ctx,
                 const uint32_t frame,
                 const real32_t x,
                 const real32_t y);
ctx

Contexto de dibujo.

frame

Índice de la secuencia (frame) de la animación.

x

Coordenada x en el lienzo del origen de la imagen.

y

Coordenada y en el lienzo del origen de la imagen.

Observaciones

Únicamente imágenes creadas a partir de un archivo GIF soportan múltiples frames (animaciones). Ver image_num_frames.


draw_image_align ()

Establece la alineación de la imagen con respecto al punto de inserción.

void
draw_image_align(DCtx *ctx,
                 const align_t halign,
                 const align_t valign);
ctx

Contexto de dibujo.

halign

Alineación horizontal.

valign

Alineación vertical.

Observaciones

El punto de inserción es la coordenada (x,y) de draw_image. Ver Dibujar imágenes.

❮ Anterior
Siguiente ❯