SDL en C bajo GNU/Linux 011 – Mostrar números gráficamente


 
Esta entrada es importante por unas cuantas razones.
  
Por un lado para ver como podemos crear una tipografía en un mapa de bits y mostrarla. En el caso que nos ocupa será un mapa de bits solamente de números. Concretamente este:
  

  
Para ahorrar espacio los he creado y colocado todos juntitos uno tras otro de forma que si quisiera extraer uno determinado tendría que hacerlo situando una ventana en el eje x,y de un ancho y un alto especifico. Por ejemplo el 1, el 3 y el 5:
  

  
De modo que cargaremos el bmp y una vez cargado tenemos que ver cual es la posición de cada número y especificar un rectángulo (lo que podríamos llamar la ventana de corte deslizante) que copie solamente eso y lo muestre en pantalla cuando lo necesitemos.
  
Ya hemos trabajado con sprites en anteriores posts. Ahora mismo no es preocupante eso.
  
Lo interesante es que vamos a controlar el tema un poco. En vez de simplemente mostrar un número crearemos un programa que muestre de 0 a 999 (3 dígitos) un número que especifiquemos como valor.
  
El motivo de controlar el tema para mostrar 3 dígitos es que en una futura entrada usaremos este código para en vez de meter a fuego un valor para que se muestre en pantalla lo que vamos a hacer es que lo saque de arduino por el puerto serie, de un circuito sencillo con un motor cc.
  
No quería añadir en este programa el código para acceder al puerto serie usb ya que creo que es mejor ir por partes y una vez se entiendan unas pasar a otras y mezclar ambas incluso.
  
Desde aquí podemos bajarnos el código:
  
Descargar mostrarnumeros.zip
  
Desde aquí podemos ver el código (está en el zip, mejor que copiar de aquí usa el del zip):
  

#include <SDL.h>
#include <stdbool.h>
#include <stdio.h>

SDL_Window * ventana;
SDL_Renderer * render;
SDL_Rect fondo = { 0, 0, 640, 480 };
SDL_Surface * imgNumero;
SDL_Texture * texNumero;
SDL_Event evento;
bool quit = false;

int dibujarFondo(void){
  SDL_SetRenderDrawColor(render, 1, 1, 1, 255);
  SDL_RenderFillRect(render, &fondo);
  SDL_RenderDrawPoint(render, 0, 0);
  SDL_RenderPresent(render);
  SDL_RenderClear(render);
  return 0;
}

int inicializarVentana(void){
  SDL_Init(SDL_INIT_VIDEO);
  ventana = SDL_CreateWindow("Mostrar un número de 0 a 999",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL);
  if (ventana == NULL) { printf("No se pudo crear la ventana: %s\n", SDL_GetError());	return 1; }
  render = SDL_CreateRenderer(ventana, -1, 0);
  return 0;
}

int numeroDigitos(int numero){
  if(numero>=100){ return 3; }
  if(numero>=10){ return 2; }
  if(numero<10){ return 1; }
  return 0;
  }

int main(int argc, char **argv) {
  int ndigitos;
  int valor = 529; // De 1 a 999 

  imgNumero = SDL_LoadBMP("res/numeros.bmp");
  SDL_Rect dstDigito1 = { 5, 5, 37, 63 };
  SDL_Rect dstDigito2 = { 45, 5, 37, 63 };
  SDL_Rect dstDigito3 = { 85, 5, 37, 63 };
  SDL_Rect dig[] = { dstDigito1, dstDigito2, dstDigito3 };
   
  SDL_Rect srcNumero0 = { 0, 0, 37, 63 };
  SDL_Rect srcNumero1 = { 37, 0, 37, 63 };
  SDL_Rect srcNumero2 = { 74, 0, 37, 63 };
  SDL_Rect srcNumero3 = { 111, 0, 37, 63 };
  SDL_Rect srcNumero4 = { 148, 0, 37, 63 };
  SDL_Rect srcNumero5 = { 185, 0, 37, 63 };
  SDL_Rect srcNumero6 = { 222, 0, 37, 63 };
  SDL_Rect srcNumero7 = { 259, 0, 37, 63 };
  SDL_Rect srcNumero8 = { 296, 0, 37, 63 };
  SDL_Rect srcNumero9 = { 333, 0, 37, 63 };
  SDL_Rect numeros[] = { srcNumero0, srcNumero1, srcNumero2, srcNumero3, srcNumero4, srcNumero5, srcNumero6, srcNumero7, srcNumero8, srcNumero9 };

  inicializarVentana();
  dibujarFondo();
  texNumero = SDL_CreateTextureFromSurface(render, imgNumero);
  
  ndigitos = numeroDigitos(valor);
  int d; int dv[2]; int c = 0;
  while(valor){
    d = valor % 10; dv[c] = d; valor /= 10; c = c+1;
  }

  if ( ndigitos == 1 ) {
    SDL_RenderCopy(render, texNumero, &numeros[0], &dig[0]); SDL_RenderCopy(render, texNumero, &numeros[0], &dig[1]); SDL_RenderCopy(render, texNumero, &numeros[dv[0]], &dig[2]);
  }
  if ( ndigitos == 2 ) {
    SDL_RenderCopy(render, texNumero, &numeros[0], &dig[0]); SDL_RenderCopy(render, texNumero, &numeros[dv[1]], &dig[1]); SDL_RenderCopy(render, texNumero, &numeros[dv[0]], &dig[2]);	  
  }
  if ( ndigitos == 3 ) {
    SDL_RenderCopy(render, texNumero, &numeros[dv[2]], &dig[0]); SDL_RenderCopy(render, texNumero, &numeros[dv[1]], &dig[1]); SDL_RenderCopy(render, texNumero, &numeros[dv[0]], &dig[2]);
  }

  SDL_RenderPresent(render);

  while (!quit) {
    SDL_WaitEvent(&evento);
    switch (evento.type) {
    case SDL_QUIT:
      quit = true;
      break;
    }
  }
}

  
Hemos de fijarnos en que he añadido un par de funciones para que el main quede algo más claro y solamente con la chicha que interesa.
  
La función dibujaFondo() por ejemplo la llamaremos desde el main sin ningún parámetro cuando queramos limpiar los números y que se muestre todo negro. Es ideal para añadirla antes de que salga un nuevo valor (no es el caso de este programa ya que solamente muestra 3 digitos con 3 numeros que extrae de la variable valor.
  

int dibujarFondo(void){
  SDL_SetRenderDrawColor(render, 1, 1, 1, 255);
  SDL_RenderFillRect(render, &fondo);
  SDL_RenderDrawPoint(render, 0, 0);
  SDL_RenderPresent(render);
  SDL_RenderClear(render);
  return 0;
}

  
La función inicializarVentana() nos sirve un poco para quitarnos del maín esas líneas y tenerla aparte. Un poco para que no estorbe. Desde el main la llamamos en una línea y eso inicializará la ventana. Se podría adaptar para que le pasemos los valores de ancho y alto de la ventana, … pero es un poco tontería. La he dejado en void tambien. Retornará 0 si va bien o 1 si todo va mal.
  

int inicializarVentana(void){
  SDL_Init(SDL_INIT_VIDEO);
  ventana = SDL_CreateWindow("Mostrar un número de 0 a 999",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL);
  if (ventana == NULL) { printf("No se pudo crear la ventana: %s\n", SDL_GetError());	return 1; }
  render = SDL_CreateRenderer(ventana, -1, 0);
  return 0;
}

  
La función numeroDigitos() es sencilla. Cuando queramos saber el número de digitos de un valor la llamamos y nos retornará 3, 2 o 1 . La idea es que el valor esté comprendido entre 1 y 999 por aquello de más tarde poder usar esto con arduino (que normalmente usaremos valores de 1 a 255). Si deseas comprobar más dígitos tu mismo.
  

int numeroDigitos(int numero){
  if(numero>=100){ return 3; }
  if(numero>=10){ return 2; }
  if(numero<10){ return 1; }
  return 0;
  }

  
El resto puede mirarse el código para entenderlo :). No tiene mucho misterio. Básicamente del valor sacamos el número de dígitos y también sacamos a dv[0] dv[1] y dv[2] el número separado.
  
Lo interesante como comentaba antes es que con este código podemos ya solamente añadir la parte de capturar valores por puerto serie usb y mostrarlos. lo que es la variable valor ahora mismo seria un valor entero entre 1 y 999 que nos mande arduino.
  
Eso lo veremos pronto. Posiblemente en la siguiente entrada si me da tiempo a publicar otra sobre como montar un tinglado con arduino que sirva para esto.
  
No te preocupes que no voy a tirar por mezclar esto con arduino de ahora en adelante. Simplemente será un post, el resto de tutoriales seguirán en la misma línea sin necesidad de que tengas un arduino.
  
De todos modos si no tienes un arduino puedes ser tu mismo y meter por línea de comandos valores o simularlos con un for.

Saludos cordiales.

Un comentario

mierda 12 mayo, 2017 Contestar

He usado un mapa de bits pero … como hemos visto en anteriores posts podemos crear esos números ya que controlamos los pixeles y podemos crear un render de lo mismo con unas cuantas líneas. Algo que le va bien a eso es usar arrays multidimensionales.

Deja un comentario