[C/C++] Introducción a la librería GD

Este es tu lugar para hablar de programación, compartir, crear y desarrollar nuevos proyectos

Moderador: Moderadores

[C/C++] Introducción a la librería GD

Notapor AnimAlf » Mié Mar 09, 2016 5:02 am

Este hilo lo publique hace un tiempo en CPH, lo pongo por aquí a ver que les parece :-)

En este hilo voy a exponer mis impresiones con la librería gd para la creación rápida de imágenes.

GD es una librería en código abierto escrita en C, que permite a los programadores la creación dinámica de imágenes. De ella se han creado conectores con otros lenguajes, para que también la puedan utilizar como perl, php, ... podemos dibujar en múltiples colores lineas, arcos, textos, y rellenar zonas con el color que queramos. Podemos utilizar zonas de otras imágenes o utilizar varias imágenes existente para nuestra composición.

Entre otros formatos, con GD podemos crear archivos jpg, gif o png. GD se suele utilizar para generar gráficos, barras, miniaturas para las presentaciones, marcas de agua. Hay un montón de posibilidades que encontramos con esta librería.

Tiene una larga lista de funciones y también una buena documentación de referencia que acompaña a la librería, la web oficial ahora está en trámite supongo que por desacuerdos [s]http://libgd.org[/s] por lo que la localizaremos en http://www.boutell.com/gd, el código fuente lo podemos encontrar en https://bitbucket.org

Su instalación, depende del sistema que esté utilizando, aunque posiblemente ya la tienes instalada en tu equipo. Si se trata del desorganizado win en temas de programación, cada cual dónde tiene las librerías de desarrollo. No voy a comentar cómo instalarlo, si decides probarlas en tu equipo y tienes problemas, comentalo en el hilo y te ayudaremos.

Empecemos con probar la librería para ver si nos funciona correctamente o no. Básicamente en los proyectos que utilicemos gd tendremos claro que debemos incluir el archivo gd.h y linkar con la librería, desde el compilador de gnu, añadiendo -lgd en el linkado. El los compiladores ms pues con la directiva #pragma comment(lib, "bgd.lib")

Código: Seleccionar todo
#include <stdio.h>
#include <gd.h>

int main ( void )
{
  /* Declaramos la imagen */
  gdImagePtr imagen;
 
  /* Declare los archivos que crearemos */
  FILE *archivo_png, *archivo_jpg;

  /* Declaramos los colores que utilizaremos */
  int negro;
  int blanco;

  /* generaremos una imagen cuadrada de 64x64 pixeles */
  imagen = gdImageCreate ( 64, 64 );

  /* Establecemos los colores (RGB). */
  negro = gdImageColorAllocate ( imagen, 0, 0, 0) ; 
  blanco = gdImageColorAllocate ( imagen, 255, 255, 255 ); 
 
  /* Vamos a dibujar un par de líneas de extremo a extremo
     formando un X en color blanco */
  gdImageLine ( imagen, 0, 0, 63, 63, blanco ); 
  gdImageLine ( imagen, 0, 63, 63, 0, blanco ); 

  /* Abrimos los archivos para escribir en binario  (wb) */
  archivo_png = fopen ( "prueba.png", "wb" );
  archivo_jpg = fopen ( "prueba.jpg", "wb" );

  /* Lanzamos el contenido del archivo en formato png
   * y luego el del archivo jpg (el -1 es para las opciones
   * por defecto del formato jpg ) */
  gdImagePng ( imagen, archivo_png );
  gdImageJpeg ( imagen, archivo_jpg, -1 );

  /* Cerramos los archivos. */
  fclose ( archivo_png );
  fclose ( archivo_jpg );

  /* Liberamos la imagen de la memoria. */
  gdImageDestroy ( imagen );
  return 0;
}


Si lo compilamos y lo lanzamos, veremos que genera los dos archivos en el mismo directorio en el que se encuentra.

Las funciones no tienes mucha complicación, en las funciones de la librería gd, veremos que el primer parámetro, es la imagen que tenemos en memoria, la primera que hemos declarado, luego, pues por ejemplo en gdImageColorAllocate, están los valores para rojo, verde y azúl. Con lo que podemos conseguir cualquiera de los colores reales con su combinación.

La función gdImageLine crea una línea entre dos pares de coordenadas x y en la superficie de la imagen, en el color que elijamos.

gdImagePng no tiene ningún misterio, gdImageJpeg tiene el argumento quality, que si es un valor negativo, se utilizará la calidad por defecto en el formato jpeg, el rango de calidad oscila entre 0 y 95.

a gdImageDestroy tampoco le hace falta ninguna explicación :))

Con esto ya podemos comprobar si nos funcionan los programas con la librería.

Donde la librería GD es muy utilizada en con aplicaciones web. Así que vamos a seguir por ese lado. Vamos a hacer que nuestra aplicación sea CGI para utilizarla desde un servidor WEB.

¿En qué consisten las aplicaciones CGI? pues en lanzar los caracteres que el servidor pueda entender. ¿Cómo lo entiende? pues porque lo primero que leerá será el tipo de dato que vendrá a continuación.

Es decir, si nosotros queremos que se vea una página web, o un txt, una imagen, un pdf, los primero caracteres que saldrán serán los que identifiquen el tipo de dato.

¿Cómo se identifica? con esta cadena "Content-type: TIPO-DOCUMENTO", y tipo de documento debe ser un tipo mime, si trabajáis desde GNU/Linux, podeis ver los diferentes tipos en este archivo: /etc/mime.types, entre ellos: text/plain, text/html, image/jpeg, image/gif, application/pdf ...

Con lo que si queremos que se optenga una imagen por ejemplo gif, pues lo primero que se debe lanzar es "Content-type: image/gif", esto debe ir separado del contenido con una línea en blanco. El contenido es lo que irá despues de esa línea vacía.

Lo más importante es entender esto, para los cgis, si has trabajado mucho en la terminal con aplicaciones para ella, pues imagina que lo primero que se escribe en la cónsola sea eso Content-type: text/plain, luego una línea en blanco y el contenido en texto que esperas recibir. Veamoslo con un ejemplo, y además lo podréis comprobar en el servidor onLine.

En la aplicación podemos diferenciar si la llamada procede del servidor web o es una ejecucion normal. Cuando se trata de una llamada para el servidor web, tendremos una variable de entorno llamada GATEWAY_INTERFACE, si utilizamos getenv para obtener la variable, si no existe, sabremos que es una llamada normal

Código: Seleccionar todo
#include <stdio.h>

int main ( void )
{
   char * esCgi = getenv ( "GATEWAY_INTERFACE" );
   if ( esCgi ) printf ( "Content-type: text/plain\r\n\r\n" );
   printf ( "Inicio de la aplicación del trato de imagenes con gd\n" );
    return 0;
}


La anterior aplicación funcionará igual si la ejecutamos en una terminal, como si la ejecutamos en un servidor web, mirarlo en esta dirección: http://animalf.users.sourceforge.net/cgi-bin/gd4cph

Supongo que se entiende, esta mini aplicación nos mostrará lo mismo en la consola que el servidor web. Pero el servidor web digamos que primero puede recibir unos parámetros que el interpreta. Si lanzaremos antes cualquier carácter, entonces en el servidor nos daría error.

Para probar el código de la imagen vamos a hacer lo siguiente, utilizaremos otra variable que es relevante en los cgi, se trata de PATH_INFO, que en principio crea un directorio de traslado en el servidor, pero no entraremos en detalles sobre ello, nosotros sólo utilizaremos su valor. Esta variable puede aparecer o no aparecer, forma parte de la url. Vamos a utilizar el mismo programa anterior, pero llamándolo de esta manera:

http://animalf.users.sourceforge.net/cgi-bin/gd4cph/imagen.jpg con lo que la variable PATH_INFO tendrá este valor: /imagen.jpg, comprobemoslo:

Código: Seleccionar todo
#include <stdio.h>
#include <gd.h>

int main ( void )
{
   char * esCgi = getenv ( "GATEWAY_INTERFACE" );
   char * rutaInfo = getenv ( "PATH_INFO" );
   if ( esCgi )
   {
      if ( rutaInfo && ! strcmp ( rutaInfo, "/imagen.jpg" ) )
      {
         /* Declaramos la imagen */
         gdImagePtr imagen;
         /* Declaramos los colores que utilizaremos */
         int negro;
         int blanco;
         /* generaremos una imagen cuadrada de 64x64 pixeles */
         imagen = gdImageCreate ( 64, 64 );

         /* Establecemos los colores (RGB). */
         negro = gdImageColorAllocate ( imagen, 0, 0, 0) ; 
         blanco = gdImageColorAllocate ( imagen, 255, 255, 255 ); 
         /* Dibujamos la X blanca */
         gdImageLine ( imagen, 0, 0, 63, 63, blanco ); 
         gdImageLine ( imagen, 0, 63, 63, 0, blanco );
         
         /* Empecemos con la salida de caracteres, primero le diremos
          * al servidor web que se trata de una imagen y enviaremos una
          * línea en blanco */
         printf ( "Content-type: image/jpeg\r\n\r\n" );
         /* A continuación volcamos el contenido de la imagen */
         gdImageJpeg ( imagen, stdout, -1 );
         
         /* Liberamos a la memoria de la imagen y terminamos */
         gdImageDestroy ( imagen );
         return 0;
      /* si no es una entrada correcta entonces le decimos que
       * lanzaremos texto , nuestros caracteres normales de toda la vida */   
      } else { printf ( "Content-type: text/plain\r\n\r\n" ); }
   }
   /* Finalmente lanzamos el texto esperado */
   if ( !rutaInfo ) printf ( "Inicio de la aplicación del trato de imagenes con gd\n" );
   /* y si ruta info existe y llegamos aquí es que no es una entrada válida */
   else printf ( "La url que has escrito no es correcta." );
    return 0;
}


podéis comprobar que nuestra aplicación ya proporciona las imagenes, en este caso no utilizamos archivos, bueno utilizamos uno stdout (la salida estandar) que es considerado un archivo X'D y la imagen ya queda disponible, mirarla aquí

Edit: La imagen pertenece al siguiente mensaje. Modifiqué el código siendo la misma referencia O:-)
Imagen

hasta luego. ¿Dudas?

SaludOS
Última edición por AnimAlf el Mié Mar 09, 2016 5:36 am, editado 1 vez en total
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

[C/C++] Introducción a la librería GD - II

Notapor AnimAlf » Mié Mar 09, 2016 5:12 am

Son temas muy interesantes, y podemos aplicar aquellos conocimientos que tenemos si lo juntamos con la programación web.

Por ejemplo, si modificamos el final del código anterior

Código: Seleccionar todo
      } else { printf ( "Content-type: text/plain\r\n\r\n" ); }
   }
   /* Finalmente lanzamos el texto esperado */
   if ( !rutaInfo ) printf ( "Inicio de la aplicación del trato de imagenes con gd\n" );
   /* y si ruta info existe y llegamos aquí es que no es una entrada válida */
   else printf ( "La url que has escrito no es correcta." );
    return 0;
}


por este otro:

Código: Seleccionar todo
      } else { printf ( "Content-type: text/html\r\n\r\n" );
         /* Aquí lanzamos la salida de caracteres. Los que lancemos y hasta
          * que finalice serán caracteres que mostrará el navegador. Seran
          * en html y todo lo que ello conlleva, una vez llegados a este punto
          * tenemos que tener todas las variables ya analizadas, no hay camino
          * de vuelta, el servidor web espera responder rapidamnte y fuera */
         fprintf ( stdout, "<html><head>\n\t<title>gd4cph</title>\n\t<style type=\"text/css\">\n" \
            "\t\tbody {\n\t\t\tbackground-color: green;\n\t\t\tcolor: #408040;\n\t\t}\n" \
            "\t\t#descripcion {\n\t\t\tfloat: left;\n\t\t}\n\t</style>\n" \
            "</head>\n<body>\n\t<div id=\"superficie\">\n\t\t<div id=\"imagenZone\">\n" \
            "\t\t\t<div id=\"descripcion\"><big>La imagen generada:&nbsp;&nbsp;</big></div>\n\t\t\t<div id=\"imagen\">" \
            "<img src=\"%s/imagen.jpg\"></div>\n\t\t</div>\n\t</div>\n</body></html>", getenv ("REQUEST_URI") );      
         return 0;
      }
   }
   /* Esto que viene a continuación ocurrirá si la aplicación se
    *  lanza desde la consola */
   else printf ( "Inicio de la aplicación del trato de imágenes con gd\n\nTODO\n" );
   return 0;
}


Entonces ya tocamos la relación con html, dónde por ejemplo le podemos decir a nuestra aplicación que empiece a generar cosas que al vuelo para ella misma, mirad: http://animalf.users.sourceforge.net/cgi-bin/gd4cph

No se, iremos viendo, a ver a donde llegamos :D

Hemos visto cómo generar una imagen, vamos a animar un poco esto. Vamos ahora a generar un gif animado, veréis que es muy sencillo.

Hemos visto cómo crear una imagen y esta la teníamos en memoria. Luego la volcábamos a un archivo o a la salida estándar, que la conocemos por el nombre stdin, que se considerado un archivo.

Las funciones que tenemos sobre gif en gd, nos van a permitir ir enlazando las diferentes imágenes que formarán la animación.

Primero crearemos el archivo, o si utilizamos la salida estandar, entonces lanzaremos el tipo mime correspondiente, image/gif, para que el navegador esté informado del tipo de datos que le vamos a enviar.

Las siguientes funciones son las que enviaran esos datos, primero la cabecera del archivo gif, que que lo haremos con la función gdImageGifAnimBegin y se le indican uno parametros globales a todo el gif

void gdImageGifAnimBegin ( gdImagePtr im, FILE *out, int GlobalCM, int Loops );

Los dos primeros ya los conocemos, im son los datos de la imagen que tenemos en memoria y out es el archivo donde irán a parar los datos. Las desconocidas son GlobalCM que le indicará si añadir o no la paleta de colores en la cabecera y loops que indica las veces que se repetirá la animación, si su valor es -1 entonces sólo se mostrará el primer fotograma, si su valor es 0 se repetirá siempre y valores mayores a 0 indican las veces que se repetirá.

Volviendo a GlobalCM, un valor diferente de 0, indica que se puede utilizar un mapa de colores global, para reducir el tamaño del archivo. Esto no es una buena idea, si el mapa de colores de los diferentes fotogramas son muy diferentes del global. Si utilizamos 1 entonces se escribe una paleta global, si indicamos -1 entonces utilizará la paleta que tengamos actual como paleta para el archivo. (o eso creo, no lo tengo muy claro este punto)

Ya se ha iniciado nuestro archivo gif, pero en el aún no hay ningún fotograma, esto los iremos añadiendo con la función gdImageGifAnimAdd, que cada vez que se añada una imagen la unirá a la anterior. Vamos con ella para comprenderla en detalle

void gdImageGifAnimAdd ( gdImagePtr im, FILE *out, int LocalCM, int LeftOfs, int TopOfs, int Delay, int Disposal, gdImagePtr previm )

im y out ya los conocemos, LocalCM no indica la paleta de imagen, si vale 1 se añade la paleta para esta imagen.

LeftOfs y TopOfs, le indican donde se situará la imagen dentro de la superficie general, lo normal será en la esquina superior izquierda, ya que para no perdernos, de momento todas las imágenes tendrán las mismas dimensiones :D En Delay se le indica el tiempo en segundos de espera hasta que se pueda saltar al siguiente fotograma.

Y llegamos a Disposal, que es otra de esas cosas que entiendo y no entiendo de los gif, quizás así que sigamos avanzando se queda en una de esas dudas que desaparecen. Si has utilizado programas para hacer tus gifs animados, seguro que me puedes iluminar sobre ello, por ahora los tengo en duda y no voy a probar de explicaroslos, el que utilizaremos será el primero (su valor es 1) de la siguiente lista: gdDisposalNone, gdDisposalUnknown, gdDisposalRestoreBackground, gdDisposalRestorePrevious. Si entiedes estos valores en la creación de gif, no dudes en participar pls.

El último parámetro de la función es previm que se refiere a la imagen anterior, con la que se ha de enlazar la actual. Si se trata del primer fotograma su valor será NULL

Una vez hemos volcado todo el contenido del archivo tocará finalizarlo, que lo logramos on la función gdImageGifAnimEnd que recibe como parámetro el archivo que hemos creado para guardar la imagen, o la salida estandar. Esta función lo que hace es scribir el caracter ; que le indica el final, por lo que también lo podemos lograr con un fputc.

Podeis ver el código a continuación. Es una ampliación del anterior, recordemos que cogimos la variable PATH_INFO, para ver si coincidía con la cadena "/imgen.jpg", pues vamos a meterle un if else para que compruebe si se coincide con esta "/anim.gif" y incluiremos lo que acabamos de ver, ahí tenéis el resultado ==> Imagen

Para dibujar el cuadrado utilizarmos la función gdImageRectangle que funciona igual que la que vimos para las líneas, pero esta vez las coordenadas no son inicio y final del tramo, si no que las dos coordenadas indican la esquina superior izquierda, y la esquina inferior derecha.

El código voy a dividirlo, incluyendo un archivo de cabecera, en él sólo habrá de momento, una constante, que será todo el texto html, aquí se ve muy mal, si queréis ver el código de la cadena, utilizar ver código desde el navegador. Dentro de esa cadena hay dos valores para sustitución "%s", son para las rutas de las imágenes, la variable de entorno REQUEST_URI que nos ofrece el servidor web, nos informa de la ruta al archivo en el que estamos.

Bueno, el código está bastante comentado, ya sabéis, no os quedéis con dudas.

Archivo de cabecera init4cph.h
Código: Seleccionar todo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gd.h>

const char TODO_EL_HTML [] =
         "<html>\n<head>\n<title>gd4cph</title>\n<style type=\"text/css\">\n\tbody {\n\t\t" \
            "background-color: green;\n\t\tcolor: #408040;\n\t}\n\n\ta:link {\n\t\tco" \
            "lor: #cccccc;\n\t\tbackground:transparent;\n\t}\n\n\ta:visited {\n\t\tc" \
            "olor: #cccccc;\n\t\tbackground:transparent;\n\t}\n\n\ta:active {\n\t\tc" \
            "olor: #ff99ff;\n\t\tbackground:transparent;\n\t}\n\n\t#superficie {\n\t" \
            "\tmargin: 0 auto;\n\t\twidth: 800px;\n\t\tbackground-color: #660000;\n\t" \
            "\theight: 600px;\n\t}\n\n\t.descripcion {\n\t\tfloat: left;\n\t}\n\n\t." \
            "zonaEjemplo {\n\t\tborder-style: solid;\n\t\tborder-width: 1px;\n\t\tfl" \
            "oat: left;\n\t\twidth: 386px;\n\t\tmargin-top: 12px;\n\t\tmargin-right:" \
            " 6px;\n\t\tmargin-left: 6px;\n\t}\n</style>\n</head>\n<body>\n<div id=" \
            "\"superficie\">\n\t<div class=\"bloqueTexto\">\n\t\t<h2>Ejemplos del thr" \
            "ead sobre <a\n\t\thref=\"http://www.portalhacker.net/index.php/topic,13" \
            "7769.0.html\">iniciaci&oacute;n\n\t\ta la librer&iacute;a GD</a></h2><p><br /></p>\n\t" \
            "</div>\n\t<p><br /></p>\n\n\t<div class=\"zonaEjemplo\">\n\t\t<div clas" \
            "s=\"descripcion\"><big>Ejemplo 1<br />Imagen jpg generada:&nbsp;&nbsp;<" \
            "/big></div>\n\t\t<div id=\"imagen\"><img src=\"%s/imagen.j" \
            "pg\"></div>\n\n\t</div>\n\t<div class=\"zonaEjemplo\">\n\t\t<div class=" \
            "\"descripcion\"><big>Ejemplo 2<br />Animaci&oacute;n gif generada:&nbsp" \
            ";&nbsp;</big></div>\n\n\t\t<div id=\"imagen\"><img src=\"%s/anim.gif\">" \
            "</div>\n\t</div>\n</div>\n</body>\n</html>";


Código fuente:
Código: Seleccionar todo
#include "init4cph.h"

int main ( int cuantos, char * elementos [] )
{
   char * esCgi = getenv ( ( char * ) "GATEWAY_INTERFACE" );
   char * rutaInfo = getenv ( "PATH_INFO" );
   if ( esCgi )
   {
      if ( rutaInfo )
      {
         int negro, blanco;
         if ( ! strcmp ( rutaInfo, "/imagen.jpg" ) )
         {
            /* Declaramos la imagen */
            gdImagePtr imagen;
            /* generaremos una imagen cuadrada de 64x64 pixeles */
            imagen = gdImageCreate ( 64, 64 );

            /* Establecemos los colores (RGB). */
            negro = gdImageColorAllocate ( imagen, 0, 0, 0) ; 
            blanco = gdImageColorAllocate ( imagen, 255, 255, 255 ); 
            /* Dibujamos la X blanca */
            gdImageLine ( imagen, 0, 0, 63, 63, blanco ); 
            gdImageLine ( imagen, 0, 63, 63, 0, blanco );
         
            /* Empecemos con la salida de caracteres, primero le diremos
             * al servidor web que se trata de una imagen y enviaremos una
             * línea en blanco */
            printf ( "Content-type: image/jpeg\r\n\r\n" );
            /* A continuación volcamos el contenido de la imagen */
            gdImageJpeg ( imagen, stdout, -1 );
         
            /* Liberamos a la memoria de la imagen y terminamos */
            gdImageDestroy ( imagen );
            return 0;            
         } else if ( ! strcmp ( rutaInfo, "/anim.gif" ) ) {
            gdImagePtr imagen, imagen2, imagen3, imagen4;

            /* Creamos la imagen */
            imagen = gdImageCreate ( 100, 100 );
            /* Establecemos los colores */
            negro = gdImageColorAllocate ( imagen, 0, 0, 0 );
            blanco = gdImageColorAllocate ( imagen, 255, 255, 255 );

            /* Dibujamos un rectángulo */
            gdImageRectangle ( imagen, 45, 45, 55, 55, blanco );

            /* Empecemos con la salida de caracteres, el servidor web
             * tiene que conocer el tipo de dato que enviaremos */
            printf ( "Content-type: image/gif\r\n\r\n" );

            /* Escribimos la cabecera del GIF. Mapa global de color.  Ciclos de poco tiempo */
            gdImageGifAnimBegin ( imagen, stdout, 1, 0 );
            /* Escribimos el primer fotograma, Sin mapa de color local y tiempo de espera 1 segundo = 1 s */
            gdImageGifAnimAdd ( imagen, stdout, 0, 0, 0, 100, gdDisposalNone , NULL);

            /* Construimos el segundo fotograma */
            imagen2 = gdImageCreate ( 100, 100 );   
            /* Establecemos su fondo blanco */
            (void) gdImageColorAllocate ( imagen2, 0, 0, 0);
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy ( imagen2, imagen );
            /* Dibujamos un restangulo más grande */
            gdImageRectangle ( imagen2, 35, 35, 65, 65, blanco );
            /* Añadimos este segundo fotograma al primero */
            gdImageGifAnimAdd ( imagen2, stdout, 0, 0, 0, 100, gdDisposalNone , imagen );

            /* Construimos el tercer fotograma */
            imagen3 = gdImageCreate ( 100, 100 );
            /* Establecemos su fondo blanco */
            (void) gdImageColorAllocate ( imagen3, 0, 0, 0 );
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy (imagen3, imagen);
            /* Dibujamos un rectangulo más grande */
            gdImageRectangle(imagen3, 30, 30, 70, 70, blanco );
            /* Añadimos este tercer fotograma al segundo */
            gdImageGifAnimAdd ( imagen3, stdout, 0, 0, 0, 100, gdDisposalNone , imagen2 );

            /* Construimos el cuarto fotograma */
            imagen4 = gdImageCreate ( 100, 100 );
            /* Establecemos su fondo blanco */
            (void) gdImageColorAllocate ( imagen4, 0, 0, 0 );
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy ( imagen4, imagen );
            /* Dibujamos un rectangulo más grande */
            gdImageRectangle ( imagen4, 25, 25, 75, 75, blanco );
            /* Añadimos este tercer fotograma al segundo */
            gdImageGifAnimAdd ( imagen4, stdout, 0, 0, 0, 100, gdDisposalNone , imagen3 );

            /* Escribimos el final de la animación */
            /* gdImageGifAnimEnd(stdout); que viene a ser lo mismo que lo siguiente: */
            putc ( ';', stdout );
            
            /* Liberamos las imagenes de la memoria */
            gdImageDestroy ( imagen );
            gdImageDestroy ( imagen2 );
            gdImageDestroy ( imagen3 );
            gdImageDestroy ( imagen4 );
            return 0;

         /*
          * si no es una entrada correcta entonces le decimos que
          * lanzaremos texto , nuestros caracteres normales de toda la vida
          * TODO
          */   
         
         } else  printf ( "Content-type: text/plainr\n\r\nBuen Intento ..." );
      } else {
         printf ( "Content-type: text/html\r\n\r\n" );
         fprintf ( stdout, TODO_EL_HTML,   getenv ( "REQUEST_URI" ), getenv ( "REQUEST_URI" ) );
         return 0;
      }
   }
   /* Esto que viene a continuación ocurrirá si la aplicación se
    *  lanza desde la consola */
   else printf ( "Inicio de la aplicación del trato de imagenes con gd\n\nTODO\n" );
   return 0;
}


Podemos comprobar que hace el código, aquí: http://animalf.users.sourceforge.net/cgi-bin/gd4cph

SaludOS
Última edición por AnimAlf el Mié Mar 09, 2016 5:46 am, editado 2 veces en total
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

[C/C++] Introducción a la librería GD - III

Notapor AnimAlf » Mié Mar 09, 2016 5:21 am

Estube leyendo este hilo y le contesté que si quería dimensionar una imagen, podría relizar un programa que lo hiciese por si mismo si seguia este hilo.

Claro, uno, todo emperrado, quiere que se siga pudiendo comprobar onLine, y ahí me compliqué la vida para conseguirlo, y aunque ha pasado un poco de tiempo desde entonces, hoy ya lo tengo listo.

Veamos, el tema de redimensionar una imagen por medio de gd, es muy sencillo. Tenemos una función que se encarga de ello. Pero como uno no quería escribir nada en la máquina del server, pues me ha llevado a tener que crear archivos virtuales en memoria, para conseguirlo. No está nada mal. Siempre se aprende algo.

Lo que veremos es cómo recibir los datos a través de un formulario, diferenciarlos, una vez tenemos la imagen en una variable, crearemos otra con las dimensiones triplicadas, copiaremos la otra imagen redimensionada y la devolveremos.

Hay un fallo que de momento está ahí. Y es que si el archivo está vacío, entonces causa errror. Pero que le vamos a hacer, prefiero presentarlo antes de depurarlo, así si veis como evitarlo, lo podeis comunicar.

En la interface podreis comprobar su funcionamiento. Se selecciona un archivo, y al pulsar redimensionar imagen, entonces devolverá un archivo en formato jpeg con la imagen en sus nuevas dimensiones.

Vamos al code. Utilizaremos estas funciones que no hemos visto de la librería gd:

gdImagePtr gdImageCreateFromJpeg ( FILE *in );
gdImagePtr gdImageCreateFromPng ( FILE * in );
gdImagePtr gdImageCreateFromGif ( FILE * in );


Que son como las que vimos anteriormente, para la creación de una superficie de imagen gd. Pero aquí en lugar de indicarle las dimensiones, le decimos que la cree desde una imagen y que la contenga con sus dimensiones.

La otra función que utilizaremos de la librería gd es

void gdImageCopyResized ( gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int destW, int destH, int srcW, int srcH )

    dst: Es la superficie destino, donde estará la imagen redimensionada.
    src: Es la superficie original, la que se quiere redimensionar.
    dstX: Coodenanda X en la superficie destino donde se colocará la cópia
    dstY: Coodenanda Y en la superficie destino donde se colocará la cópia
    srcX: Coordenada X del origen donde empezará la cópia.
    srcY: Coordenada Y del origen donde empezará la cópia.
    destW: Ancho que tendrá la cópia destino.
    destH: Alto que tendrá la imagen destino.
    srcW: Ancho sobre la superficie a copiar.
    srcH: Alto sobre la superficie a copiar.

Vemos que podemos redimensionar recuadros independientes de una imagen y volcarlo en un cuadro independiente en el resultado que no tiene porqué coincidir con los tamaños. Pero nosotros lo veremos utilizando toda la superficie en ambos, y manteniendo proporciones.

Si realizamos la pruebas localmente, no tiene ninguna complicación.

Código: Seleccionar todo
gdImagePtr imgOrigen;
gdImagePtr imgDestino;
int x, y;
FILE *archivoOrigen;
FILE *archivoDestino;

/* Cargamos una imagen png */
archivoOrigen = fopen ( "diminuta.png", "rb" );
imgOrigen = gdImageCreateFromPng ( archivoOrigen );
fclose ( archivoOrigen );

/* Creamos la superfície destino tres veces más grande que la de origen */
imgDestino = gdImageCreate ( imgOrigen->sx * 3, imgOrigen->sy * 3 );

/* Ahora incrustaremos en la superficie destino la imagen origen redimensionada */
gdImageCopyResized ( imgDestino, imgOrigen, 0, 0, 0, 0, imgDestino->sx, imgDestino->sy,
   archivoOrigen->sx, archivoOrigen->sy );

/* Finalmente escribimos en el disco */
archivoDestino = fopen ( "ampliada.png", "wb" );
gdImagePng ( imgDestino, archivoDestino );
fclose ( archivoDestino );

/* y liberamos */
gdImageDestroy ( imgOrigen );
gdImageDestroy ( imgDestino );


Y ya está, para hacerlo localmete no tenemos más problema.

Ahora vamos a ver que hacemos si se trata de una aplicación CGI. Continuando con el código anterior, el trozo en el que estamos es este: (está un poco cutre, ya irá mejorando)

Código: Seleccionar todo
      /* Separar la cabecera
       * Diferenciar el metode GET o POST
       */
       if ( ! strcmp ( metodoCgi, "POST" ) )
       {
          /*Si es POST entonces nos tiene que indicar en bytes que se
          envia por la entrada estandar */
         bytesEntrada = atoi ( getenv ( "CONTENT_LENGTH" ) );
         if ( strstr ( tipoDcontenido, "multipart/form-data" ) )
         {
            /* entonces la entrada estandar está dividida en dos
             * primero no da la imformacion y los destalles junto con
             * el tipo de dato que hay en la entrada
             * luego separado por una linea en blanco \r\n
             * tenemos el contenido del tipo mime que nos indica
             */
            char * contenido = NULL;
            char * nombreArchivo = NULL;
            char * tipoDato = NULL;
            char * archivoRemoto = NULL;
            char lineaTemporal [1024] = "", c;
            do
            {   
               int contador = 0;
               while ( ( c = getc ( stdin ) ) != '\n' )
                  c == '\r' ? ( lineaTemporal [ contador ++ ] = '\0' ) : ( lineaTemporal [ contador ++ ] = c );
               if ( lineaTemporal [ contador - 1 ] != '\0' )lineaTemporal [ contador - 1 ] = '\0';
               /* si no tiene caracteres entonces es la linea en blanco */
               if ( ! strlen ( lineaTemporal ) ) break;
               /* La primera linea si llega con guiones y numeros no tengo
                * ni pajotera idea de que o para qué es me la saltaré ya que
                * no tiene dospuntos
                */
               char * tmpstr =  lineaTemporal;
               if ( ! strchr ( tmpstr, ':' ) ) continue;
               if ( ( tmpstr = strstr ( lineaTemporal, "; filename=\"" ) ) != NULL )
               {
                  tmpstr = strtok ( tmpstr, "\"" );
                  nombreArchivo = strtok ( NULL, "\"" );
               }
               if ( ( tmpstr = strstr ( lineaTemporal, "ype: " ) ) != NULL )
               {
                  tmpstr = trim ( strchr ( tmpstr, ' ' ) );
                  tipoDato = malloc ( sizeof ( tmpstr ) );
                  strcpy ( tipoDato, tmpstr );
               }
            } while ( 1 );
            /* Comparar el tipo de archivo con los que aceptamos */
            if ( ! strcmp ( tipoDato, "image/png" ) ||
               ! strcmp ( tipoDato, "image/jpeg" ) ||
               ! strcmp ( tipoDato, "image/gif" ) )
            {
               /* Empieza la acción, todo parece correcto */
               if ( ( archivoRemoto = ( char * ) malloc ( ( bytesEntrada + 1 ) * sizeof ( char ) ) ) != NULL )
               {
                  int i;
                  for ( i = 0; i < bytesEntrada; i++ )
                  {             
                     c = getc ( stdin );
                     archivoRemoto[i] = c;
                  }
                  archivoRemoto[i] = '\0';
               } else return -1;
               /* Ara comece el cachondeo amb l'arxiu y gd */
               gdImagePtr imagen;
               FILE * archivoVirtual = fmemopen ( archivoRemoto, bytesEntrada + 1, "rb" );
               if ( ! strcmp ( tipoDato, "image/png" ) )
                  imagen = gdImageCreateFromPng ( archivoVirtual );
               else if ( ! strcmp ( tipoDato, "image/jpeg" ) )
                  imagen = gdImageCreateFromJpeg ( archivoVirtual );
               else if ( ! strcmp ( tipoDato, "image/gif" ) )
                  imagen = gdImageCreateFromGif ( archivoVirtual );
               fclose ( archivoVirtual );
               /* Tenemos la imagen recibida en un archivo virtual
                * ahora crearemos otro con el triple de su tamaño
                * y copiaremos la imagen recibida en su interior
                * redimensionandola */
               gdImagePtr nuevaImagen = gdImageCreate ( imagen->sx * 3, imagen->sy * 3 );
               gdImageCopyResized ( nuevaImagen, imagen, 0, 0, 0, 0,
                  nuevaImagen->sx, nuevaImagen->sy, imagen->sx, imagen->sy ); 
               /* y finalmente la enviaremos como jpg redimensionada */
               size_t tamanioArchivo;
               char * contenidoArchivo;               
               FILE * archivoRetornado = open_memstream ( &contenidoArchivo, &tamanioArchivo );
               gdImageJpeg ( nuevaImagen, archivoRetornado, -1 );
               fclose ( archivoRetornado );
               printf ( "Content-Type: text/html\r\n" );
               printf ( "Content-Disposition: attachment; filename=\"ImagenRedimensionada.jpg\"\r\n" );
               printf ( "Content-Length: %d\r\n\r\n", tamanioArchivo );
               gdImageJpeg ( nuevaImagen, stdout, -1 );
               gdImageDestroy(imagen);
               gdImageDestroy(nuevaImagen);
               return 0;
            }
         }
       }


Para enviar archivos al servidor, antes se utilizaba el método PUT, pero dejó de utilizarse hace mucho. Ahora se utiliza POST, indicando que esa entrada estandar está dividica en partes. Las partes están divididas por una línea en blanco. Indicandolo en la etiqueta form enctype="multipart/form-data". Y en este punto estoy muy verde, aunque ya iremos depurando. (existen unos separadores que empiezan por guiones por cada parte, pero de momento no se nada de ello, como cuando se envian multiples archivos)

En este caso particular del envio de un archivo, no escontramos por la entrada estandar una primera línea con guiones seguidos y terminada en un gran dígito (que ya os digo que ni idea) y nos la saltamos, luego nos encontramos Content-Disposition: que nos enviará una ristra de valores del tipo identificador=valor;, dónde estarán los valores de los elementos del formulario y el nombre del archivo.
A continuación nos encontramos Content-Type: que nos dice el tipo mime del archivo que recibimos.

Una línea en blanco, separará los valores anteriores, del contenido del archivo que viene a continuacion.

Hay unas variables de entorno que nos ofrece el servidor web, que tenemos que tener en cuenta. CONTENT_LENGTH, que indica el tamaño en bytes del archivo que recibiremos y CONTENT_TYPE que nos indica que la entrada estandar llega con apartados (multipart/form-data).

Como tenemos la variable Content-Type que nos indica el tipo mime del archivo que recibimos, entonces podemos decidir si operar con los datos dependiendo de ese valor, es decir, sólo realizar las operaciones si el archivo es png, jpg o gif (en este ejemplo)

Aquí me quedé atascado, ya que los datos los recibimos por la entrada estandar, pero, ¿quée podemos hacer?, almacenarla en un puntero de caracteres.

Pero las funciones necesitan un tipo archivo, no una ristra de caracteres con su contenido.

Aquí es donde utilicé fmemopen de la librería estandar, que trabaja como fopen, pero en lugar de coger la referencia al archivo en disco, coje una ristra de caracteres.

FILE *fmemopen (void *buf, size_t size, const char *mode );

Así consigo tener un archivo, existiendo éste sólo en memoria. Dónde buf es la ristra de caracteres que contendrá el archivo, size, es el tamaño en bytes de la ristra de caracteres y mode, ya sabeis como en fopen "r", "rb" ...

Así lo que recibimos ya es un archivo.

Ok, antes de enviar el archivo, necesito saber el tamaño que este tiene, y no se me ocurria la forma de hacerlo, las funciones gd hacen el vuelco de caracteres dentro del archivo y si lo lanzo directamente a stdin, no puedo ver su tamaño. Utilicé

FILE *open_memstream (char **ptr, size_t *sizeloc);

Que nos crea un archivo, en el que podemos ir enviando datos, ptr es su contenido y sizeloc indica el tamaño del archivo. Bueno eso es relativo, más bien indica el offset en el que se encuentra dentro de él, como normalmente lo tendremos al final, pues eso, su tamaño, y al cerrarlo ahi estará.

Aquí me he perdido un poco ya que prt, no se cómo daemons utilizarlo par optener su contenido, pero bueno, lo que me importaba era el tamaño.

Bueno, se que es un tema un poco complicado, pero, ya sabeis, lo mejor es que no os quedeis con dudas y pregunteis aquello que no entandais.

Aquí podeis comprobar el resultado

El code entero:
Código: Seleccionar todo
#include "init4cph.h"

char * trim ( char * );

int main ( int cuantos, char * elementos [] )
{
   char * esCgi = getenv ( ( char * ) "GATEWAY_INTERFACE" );
   char * rutaInfo = getenv ( "PATH_INFO" );
   char * metodoCgi = getenv ( "REQUEST_METHOD" );
   char * tipoDcontenido = getenv ( "CONTENT_TYPE" );
   int bytesEntrada = 0;
   
   if ( esCgi )
   {
      /* Separar la cabecera
       * Diferenciar el metode GET o POST
       */
       if ( ! strcmp ( metodoCgi, "POST" ) )
       {
          /*Si es POST entonces nos tiene que indicar en bytes que se
          envia por la entrada estandar */
         bytesEntrada = atoi ( getenv ( "CONTENT_LENGTH" ) );
         if ( strstr ( tipoDcontenido, "multipart/form-data" ) )
         {
            /* entonces la entrada estandar está dividida en dos
             * primero no da la imformacion y los destalles junto con
             * el tipo de dato que hay en la entrada
             * luego separado por una linea en blanco \r\n
             * tenemos el contenido del tipo mime que nos indica
             */
            char * contenido = NULL;
            char * nombreArchivo = NULL;
            char * tipoDato = NULL;
            char * archivoRemoto = NULL;
            char lineaTemporal [1024] = "", c;
            do
            {   
               int contador = 0;
               while ( ( c = getc ( stdin ) ) != '\n' )
                  c == '\r' ? ( lineaTemporal [ contador ++ ] = '\0' ) : ( lineaTemporal [ contador ++ ] = c );
               if ( lineaTemporal [ contador - 1 ] != '\0' )lineaTemporal [ contador - 1 ] = '\0';
               /* si no tiene caracteres entonces es la linea en blanco */
               if ( ! strlen ( lineaTemporal ) ) break;
               /* La primera linea si llega con guiones y numeros no tengo
                * ni pajotera idea de que o para qué es me la saltaré ya que
                * no tiene dospuntos
                */
               char * tmpstr =  lineaTemporal;
               if ( ! strchr ( tmpstr, ':' ) ) continue;
               if ( ( tmpstr = strstr ( lineaTemporal, "; filename=\"" ) ) != NULL )
               {
                  tmpstr = strtok ( tmpstr, "\"" );
                  nombreArchivo = strtok ( NULL, "\"" );
               }
               if ( ( tmpstr = strstr ( lineaTemporal, "ype: " ) ) != NULL )
               {
                  tmpstr = trim ( strchr ( tmpstr, ' ' ) );
                  tipoDato = malloc ( sizeof ( tmpstr ) );
                  strcpy ( tipoDato, tmpstr );
               }
            } while ( 1 );
            /* Comparar el tipo de archivo con los que aceptamos */
            if ( ! strcmp ( tipoDato, "image/png" ) ||
               ! strcmp ( tipoDato, "image/jpeg" ) ||
               ! strcmp ( tipoDato, "image/gif" ) )
            {
               /* Empieza la acción, todo parece correcto */
               if ( ( archivoRemoto = ( char * ) malloc ( ( bytesEntrada + 1 ) * sizeof ( char ) ) ) != NULL )
               {
                  int i;
                  for ( i = 0; i < bytesEntrada; i++ )
                  {             
                     c = getc ( stdin );
                     archivoRemoto[i] = c;
                  }
                  archivoRemoto[i] = '\0';
               } else return -1;
               /* Ara comece el cachondeo amb l'arxiu y gd */
               gdImagePtr imagen;
               FILE * archivoVirtual = fmemopen ( archivoRemoto, bytesEntrada + 1, "rb" );
               if ( ! strcmp ( tipoDato, "image/png" ) )
                  imagen = gdImageCreateFromPng ( archivoVirtual );
               else if ( ! strcmp ( tipoDato, "image/jpeg" ) )
                  imagen = gdImageCreateFromJpeg ( archivoVirtual );
               else if ( ! strcmp ( tipoDato, "image/gif" ) )
                  imagen = gdImageCreateFromGif ( archivoVirtual );
               fclose ( archivoVirtual );
               /* Tenemos la imagen recibida en un archivo virtual
                * ahora crearemos otro con el triple de su tamaño
                * y copiaremos la imagen recibida en su interior
                * redimensionandola */
               gdImagePtr nuevaImagen = gdImageCreate ( imagen->sx * 3, imagen->sy * 3 );
               gdImageCopyResized ( nuevaImagen, imagen, 0, 0, 0, 0,
                  nuevaImagen->sx, nuevaImagen->sy, imagen->sx, imagen->sy ); 
               /* y finalmente la enviaremos como jpg redimensionada */
               size_t tamanioArchivo;
               char * contenidoArchivo;               
               FILE * archivoRetornado = open_memstream ( &contenidoArchivo, &tamanioArchivo );
               gdImageJpeg ( nuevaImagen, archivoRetornado, -1 );
               fclose ( archivoRetornado );
               printf ( "Content-Type: text/html\r\n" );
               printf ( "Content-Disposition: attachment; filename=\"ImagenRedimensionada.jpg\"\r\n" );
               printf ( "Content-Length: %d\r\n\r\n", tamanioArchivo );
               gdImageJpeg ( nuevaImagen, stdout, -1 );
               gdImageDestroy(imagen);
               gdImageDestroy(nuevaImagen);
               return 0;
            }
         }
       }
       /* realitzar una acció o una altra
        * que te que está en una variable accio
        * reescriure el que segueix*/
      if ( rutaInfo )
      {
         int negro, blanco;
         if ( ! strcmp ( rutaInfo, "/imagen.jpg" ) )
         {
            /* Declaramos la imagen */
            gdImagePtr imagen;
            /* generaremos una imagen cuadrada de 64x64 pixeles */
            imagen = gdImageCreate ( 64, 64 );

            /* Establecemos los colores (RGB). */
            negro = gdImageColorAllocate ( imagen, 0, 0, 0) ; 
            blanco = gdImageColorAllocate ( imagen, 255, 255, 255 ); 
            /* Dibujamos la X blanca */
            gdImageLine ( imagen, 0, 0, 63, 63, blanco ); 
            gdImageLine ( imagen, 0, 63, 63, 0, blanco );
         
            /* Empecemos con la salida de caracteres, primero le diremos
             * al servidor web que se trata de una imagen y enviaremos una
             * línea en blanco */
            printf ( "Content-type: image/jpeg\r\n\r\n" );
            /* A continuación volcamos el contenido de la imagen */
            gdImageJpeg ( imagen, stdout, -1 );
         
            /* Liberamos a la memoria de la imagen y terminamos */
            gdImageDestroy ( imagen );
            return 0;            
         } else if ( ! strcmp ( rutaInfo, "/anim.gif" ) ) {
            gdImagePtr imagen, imagen2, imagen3, imagen4;

            /* Creamos la imagen */
            imagen = gdImageCreate ( 100, 100 );
            /* Establecemos los colores */
            negro = gdImageColorAllocate ( imagen, 0, 0, 0 );
            blanco = gdImageColorAllocate ( imagen, 255, 255, 255 );

            /* Dibujamos un rectángulo */
            gdImageRectangle ( imagen, 45, 45, 55, 55, blanco );

            /* Empecemos con la salida de caracteres, el servidor web
             * tiene que conocer el tipo de dato que enviaremos */
            printf ( "Content-type: image/gif\r\n\r\n" );

            /* Escribimos la cabecera del GIF. Mapa global de color.  Ciclos de poco tiempo */
            gdImageGifAnimBegin ( imagen, stdout, 1, 0 );
            /* Escribimos el primer fotograma, Sin mapa de color local y tiempo de espera 1 segundo = 1 s */
            gdImageGifAnimAdd ( imagen, stdout, 0, 0, 0, 100, gdDisposalNone , NULL);

            /* Construimos el segundo fotograma */
            imagen2 = gdImageCreate ( 100, 100 );   
            /* Establecemos su fondo blanco */
            (void) gdImageColorAllocate ( imagen2, 1, 1, 1);
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy ( imagen2, imagen );
            /* Dibujamos un restangulo más grande */
            gdImageRectangle ( imagen2, 35, 35, 65, 65, blanco );
            /* Añadimos este segundo fotograma al primero */
            gdImageGifAnimAdd ( imagen2, stdout, 0, 0, 0, 100, gdDisposalNone , imagen );

            /* Construimos el tercer fotograma */
            imagen3 = gdImageCreate ( 100, 100 );
            /* Establecemos su fondo blanco */
            (void)gdImageColorAllocate(imagen3, 1, 1, 1);
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy (imagen3, imagen);
            /* Dibujamos un rectangulo más grande */
            gdImageRectangle(imagen3, 30, 30, 70, 70, blanco );
            /* Añadimos este tercer fotograma al segundo */
            gdImageGifAnimAdd ( imagen3, stdout, 0, 0, 0, 100, gdDisposalNone , imagen2 );

            /* Construimos el cuarto fotograma */
            imagen4 = gdImageCreate ( 100, 100 );
            /* Establecemos su fondo blanco */
            (void) gdImageColorAllocate ( imagen4, 1, 1, 1 );
            /* Nos aseguramos que la paleta de colores sea idética */
            gdImagePaletteCopy ( imagen4, imagen );
            /* Dibujamos un rectangulo más grande */
            gdImageRectangle ( imagen4, 25, 25, 75, 75, blanco );
            /* Añadimos este tercer fotograma al segundo */
            gdImageGifAnimAdd ( imagen4, stdout, 0, 0, 0, 100, gdDisposalNone , imagen3 );

            /* Escribimos el final de la animación */
            /* gdImageGifAnimEnd(stdout); que viene a ser lo mismo que lo siguiente: */
            putc ( ';', stdout );
            
            /* Liberamos las imagenes de la memoria */
            gdImageDestroy ( imagen );
            gdImageDestroy ( imagen2 );
            gdImageDestroy ( imagen3 );
            gdImageDestroy ( imagen4 );
            return 0;

         /*
          * si no es una entrada correcta entonces le decimos que
          * lanzaremos texto , nuestros caracteres normales de toda la vida
          * TODO
          */   
         
         } else  printf ( "Content-type: text/plain\r\n\r\nBuen Intento ..." );
      /* } else if () { */
      } else {
         printf ( "Content-type: text/html\r\n\r\n" );
         fprintf ( stdout, TODO_EL_HTML,   getenv ( "REQUEST_URI" ),
            getenv ( "REQUEST_URI" ), getenv ( "REQUEST_URI" ) );
         return 0;
      }
   }
   /* Esto que viene a continuación ocurrirá si la aplicación se
    *  lanza desde la consola */
   else printf ( "Inicio de la aplicación del trato de imagenes con gd\n\nTODO\n" );
   return 0;
}

char * trim ( char * s )
{
  char * principo = s;
  char * i = principo;
  char * final = principo;

  /* Nos comemos los espacios al inicio */
  while ( * principo && isspace ( * principo ) )
    ++ principo;

  /* Nos comemos los espacios al final */
  while ( * i )
  {
    if ( ! isspace ( * ( i ++ ) ) )
      final = i;
  }
  * final = 0;

  return principo;
}


Archivo de Cabecera

Código: Seleccionar todo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gd.h>

const char TODO_EL_HTML [] =
   "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" \
   "<html><head>\n  <title>gd4cph</title>\n\n\t\n  <style type=\"text/css\">\n\tbody {\n\t\tbackgr" \
   "ound-color: green;\n\t\tcolor: #408040;\n\t}\n\t\n\ta:link {\n\t\tbackground: transparent none " \
   "repeat scroll 0\%;\n\t\tcolor: #cccccc;\n\t}\n\n\ta:visited {\n\t\tbackground: transparent non" \
   "e repeat scroll 0\%;\n\t\tcolor: #cccccc;\n\t}\n\n\ta:active {\n\t\tbackground: transparent no" \
   "ne repeat scroll 0\%;\n\t\tcolor: #ff99ff;\n\t}\n\t#superficie {\n\t\tmargin: 0 auto;\n\t\twid" \
   "th: 800px;\n\t\tbackground-color: #660000;\n\t\theight: 600px;\n\t}\n\t.descripcion {\n\t\tflo" \
   "at: left;\n\t}\n\t.zonaEjemplo {\n\t\tborder-style: solid;\n\t\tborder-width: 1px;\n\t\tfloat:" \
   "  left;\n\t\twidth: 386px;\n\t\tmargin-top: 12px;\n\t\tmargin-right: 6px;\n\t\tmargin-left: 6p" \
   "x;\n\t\tbackground-color: #9e0000;\n\t}\n\t.parrafo {\n\t\tclear: left;\n\t\tcolor: #ffffcc;\n" \
   "\t\tpadding-top: 20px;\n\t}\n\t#uploadSimple {\n\t\ttext-align: center;\n\t}\n  </style>\n</he" \
   "ad><body>\n<div id=\"superficie\">\n<div class=\"bloqueTexto\">\n<h2>Ejemplos del thread sobre" \
   " la <a href=\"http://www.portalhacker.net/index.php/topic,137769.0.html\">\niniciación a la li" \
   "brería GD</a></h2>\n</div>\n<div class=\"parrafo\">En la introducción vimos como iniciar la li" \
   "brería,\ny crear una superfície sobre la que podemos dibujar líneas rectas y rectangulos, y\nc" \
   "omo guardarlas en un archivo, o utilizar la salida estandar para lanzar los  caracteres, como " \
   "\nvemos en los siguientes ejemplos..</div>\n\n<div class=\"zonaEjemplo\">\n<div class=\"descri" \
   "pcion\"><big>Ejemplo 1<br>\nImagen jpg generada:&nbsp;&nbsp;</big></div>\n<div id=\"imagen\"><" \
   "img src=\"%s/imagen.jpg\"></div>\n</div>\n<div class=\"zonaEjemplo\">\n<div class=\"descripcio" \
   "n\"><big>Ejemplo 2<br>\nAnimación gif generada:&nbsp;&nbsp;</big></div>\n<div id=\"imagen\"><i" \
   "mg src=\"%s/anim.gif\"></div>\n</div>\n<div class=\"parrafo\">\nEstaba leyendo <a href=\"http:" \
   "//www.portalhacker.net/index.php/topic,137868.msg651993.html#msg651993\" target=\"_blank\">est" \
   "e mensaje</a>, dónde se solicita un programa de fotografía para redimensionarlas.\nY se me ocu" \
   "rrió óomo continuaría el hilo.<br>\n<br>\n\nEl siguiente ejempo se encrgará de ellos, vamos a " \
   "redimensionar <span style=\"text-decoration: underline;\">al vuelo</span>, cualquier imagen\nq" \
   "ue se nos mande, triplicando su tamaño.\nComprobarlo:</div><br>\n<div id=\"uploadSimple\">\n<f" \
   "orm enctype=\"multipart/form-data\" method=\"post\" action=\"%s\" name=\"ProcesadorDeImagen\">" \
   "<input accept=\"image/png,image/gif,image/jpeg\" name=\"ImagenOrigen\" type=\"file\"><br>\n<in" \
   "put value=\"Redimensiona imagen\" type=\"submit\"></form>\n</div>\n</div>\n\n</body></html>";


Compruebalo

:embudito: :embudito: :embudito: This is the end ... 4 now :embudito: :embudito: :embudito:

SaludOS
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

Re: [C/C++] Introducción a la librería GD

Notapor Newhack » Jue Mar 10, 2016 7:40 pm

Hola.

Ahora mismo solo he leido dos o tres parrafos del primer post y el primer código, pero parece interesante.
Si lo he entendido bien teniendo esa libreria puedes crear gráficos, (me ha parecido que incluso en movimiento),
con un simple compilador de C. Sin sharp, sin clases, sin objetos. Un compilador ansi y un poco de
habilidad y ya lo tienes.
¿Lo capto bien ?.

Si es asi, cuando venga el verano y vuelva a mi periodo de practica del C será interesante probarlo, ya que
hasta ahora nunca me he aventurado por el modo gráfico que creo que tiene, es una puerta que aún no me
he atrevido a abrir.

Lo leeré y lo guardaré hasta que desempolve mi portátil de compilar, que está fuera.

Gracias por el texto.
Avatar de Usuario
Newhack
<|:-D
<|:-D
 
Mensajes: 1825
Registrado: Jue Dic 20, 2007 7:36 pm

Re: [C/C++] Introducción a la librería GD

Notapor Yorkshire » Sab Mar 12, 2016 12:19 pm

Gracias por la info.
Linux registered user #346840
Avatar de Usuario
Yorkshire
Gran Wadalbertita
Gran Wadalbertita
 
Mensajes: 4473
Registrado: Mié Ene 26, 2005 5:05 pm
Ubicación: -<|:-P[G]

Re: [C/C++] Introducción a la librería GD

Notapor AnimAlf » Sab Mar 12, 2016 7:46 pm

Aja, crearlos, modificarlos, combinarlos, ... pero no presentarlos. No se dispone de una superficiea como en sdl o alegro. En el ejemplo utilizo el servidor web para presentar los datos. Hay una cosa muy interesante, al final, y es la creación de archivos en memoria para si por ejemplo no se dispone de espacio de escritura en remoto o no se sabe dónde está (*nix) :-)
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

Re: [C/C++] Introducción a la librería GD

Notapor Newhack » Dom Mar 13, 2016 8:55 pm

Bueno, si. se usa la libreria y el programa para componerlos y prepararlos, y luego un navegador para ver el efecto conseguido.

¿Es eso, no?.
Avatar de Usuario
Newhack
<|:-D
<|:-D
 
Mensajes: 1825
Registrado: Jue Dic 20, 2007 7:36 pm

Re: [C/C++] Introducción a la librería GD

Notapor AnimAlf » Dom Mar 13, 2016 9:04 pm

Éstá todo en CGI ;-)
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

Re: [C/C++] Introducción a la librería GD

Notapor Newhack » Jue Mar 17, 2016 9:20 pm

Bueno, pero si no se puede ver "sobre la marcha" el resultado obtenido, una vez lo tengas si se le puede
pasar a un navegador normal, ¿no?.

Donde la librería GD es muy utilizada en con aplicaciones web. Así que vamos a seguir por ese lado. Vamos a hacer que nuestra aplicación sea CGI para utilizarla desde un servidor WEB.

¿En qué consisten las aplicaciones CGI? pues en lanzar los caracteres que el servidor pueda entender. ¿Cómo lo entiende? pues porque lo primero que leerá será el tipo de dato que vendrá a continuación.

Es decir, si nosotros queremos que se vea una página web, o un txt, una imagen, un pdf, los primero caracteres que saldrán serán los que identifiquen el tipo de dato.

¿Cómo se identifica? con esta cadena "Content-type: TIPO-DOCUMENTO", y tipo de documento debe ser un tipo mime, si trabajáis desde GNU/Linux, podeis ver los diferentes tipos en este archivo: /etc/mime.types, entre ellos: text/plain, text/html, image/jpeg, image/gif, application/pdf ...

Con lo que si queremos que se optenga una imagen por ejemplo gif, pues lo primero que se debe lanzar es "Content-type: image/gif", esto debe ir separado del contenido con una línea en blanco. El contenido es lo que irá despues de esa línea vacía.

Lo mismo que procesan esos bloques de carácteres y parámentros de los scripts también saben procesar
cabeceras como la que mencionas.
Avatar de Usuario
Newhack
<|:-D
<|:-D
 
Mensajes: 1825
Registrado: Jue Dic 20, 2007 7:36 pm

Re: [C/C++] Introducción a la librería GD

Notapor AnimAlf » Vie Mar 18, 2016 4:25 am

Newhack escribió:Bueno, pero si no se puede ver "sobre la marcha" el resultado obtenido, una vez lo tengas si se le puede
pasar a un navegador normal, ¿no?.


Los resultados serán archivos de imágenes. Se podrán utilizar como todas las imagenes.

(cairo puede hacer algo similar a lo que realiza gd pero con gráficos vectoriales)

Para conseguir una base/superficie dónde presentarla, puede ser utilizando las primitivas que hayan en el sistema (pero más elevado que la Xlib)

Imagen

Pero tendríamos que adivinar qué tipo de librería de widgets utiliza el sistema una descendiente de XT o las que descienden directamente de Xlib como gtk+

Es mejor una librería que suela ser normal tenerla en todos los equipos, por lo que apostaría por sdl, un ejemplo para mostrar una imagen podría ser el siguiente con sdl

Código: Seleccionar todo
int main ( void )
{
   SDL_Surface *superficie = NULL, *laImagen = NULL;
   SDL_Rect posicionImg;
   posicionImg.x = 0;
   posicionImg.y = 0;
   SDL_Init ( SDL_INIT_VIDEO );
   superficie = SDL_SetVideoMode ( 800, 600, 32, SDL_HWSURFACE );
   SDL_WM_SetCaption ( "Cargar y ver una imagen con SDL", NULL );
   laImagen = SDL_LoadBMP ( "elArchivo.bmp" );
   SDL_BlitSurface ( laImagen, NULL, superficie, &posicionImg );
   SDL_Flip ( superficie );
   getchar (); /* esperamos hasta que se pulse enter */
   SDL_FreeSurface ( laImagen );
   SDL_Quit();
   return 0;
}


Sdl base sólo carga bmp's para los otro tipos de formatos de imagenes debemos utilizar la complementaria "SDL_Image"

jijijiji recuerdo de un tema muy caprichoso con SDL [newbies][SDL] CoN FirmA DemoS con imagenes y audio

Newhack escribió:Lo mismo que procesan esos bloques de carácteres y parámentros de los scripts también saben procesar
cabeceras como la que mencionas.


Quizás te debes referir a php, pero sólo añade antes de cualquier salida de caracteres la cabecera, indicando el tipo mime text/html. Para indicar algo distinto se tiene que utilizar la funcion header (así también consiguen presentar los errores en el navegador, si no sería error del servidor web y para averiguar el error tocaría ver los logs). Cómo no, en C / C++ existen librerías que se encargan del procesado de las cabeceras, pero como no es un protocolo muy complicado (después de tanto tiempo, se aprende quiera uno o no) el rfc 2616 pues creo que no hace falta ayuda extra :-) Otros lenguajes de script requieren el mismo proceso, al igual que "php cli" (el que se utiliza para scripts de terminal o para cgi).

Saludos
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn

Re: [C/C++] Introducción a la librería GD

Notapor Newhack » Mar Mar 22, 2016 9:00 pm

Vamos, que tampoco es tan tan fácil, la cosa tiene su truco, y hasta que no te
aprendas que programa o que tag ha de utilizarse para mostrar eso o aquello puede
que estes dando algunas vueltas, ¿no?

Bueno, si todo lo que sale tiene formato de imagen, se podrá hacer un "preview"
de lo que estás haciendo con tu visor de imágenes habitual. Y si te da problemas,
entonces directo al gimp, que ese sabe mostrarlo casi todo.
Y luego ya te buscas como integrarlo donde lo quieres. ¿es eso?.

En cuanto a las librerias, por suerte, sdlib, sdl, y gtk+ suelen estar presentes en
casi cualquier linux que tenga escritorio, ya que muchos programas las tienen como
requerimiento.
Y para saber que programa las soporta ... cuestión de preguntarle al sistema quién
fué el que las pidió. ;-)

Aunque imagino que para el momento que me ponga manos a la obra y empieze con las
pruebas mejor que tenga un compilador basado en los entornos actuales, ya que no se
si mi turbo C, (el de Borland, para Dos :roll: ), entenderá mucho de esto de las sdlibs. :lol:

Y esto me recuerda, Un compilador, justito para c ansi, sin cosas "raras", que tenga
un entorno integrado (ide), donde puedas ejecutar paso a paso mientras vas viendo la
evolución de las variables y strings o los punteros, y que tengas alli mismo los
avisos de errores, etc. (Al estilo del turbo C, vamos, que es genial ).
¿Cual me recomendariais instalar hoy dia ?, (Teniendo en cuenta que mis ordenadores son
no solo de aupa si no de época :-) )

Quizás te debes referir a php, pero sólo añade antes de cualquier salida de caracteres la cabecera, indicando el tipo mime text/html. Para indicar algo distinto se tiene que utilizar la funcion header
Si, efectivamente. A la hora de encajarlo en el lugar donde lo quieres, la habilidad de ejecutar php de
los navegadores te permitirá, mediante los headers y las indicaciones necesarias ver tu obra desde alli sin más, supongo.


Gracias por los links, del otro hilo, la otra página, y el RFC. Los adjunto a este
tutorial, como material a inspeccionar antes de ponerme a hacer.
Avatar de Usuario
Newhack
<|:-D
<|:-D
 
Mensajes: 1825
Registrado: Jue Dic 20, 2007 7:36 pm

Re: [C/C++] Introducción a la librería GD

Notapor AnimAlf » Mié Mar 23, 2016 7:30 pm

:-)
En busca del TuXeR perdido
Avatar de Usuario
AnimAlf
<|:-)
<|:-)
 
Mensajes: 628
Registrado: Mar Ago 08, 2006 4:54 am
Ubicación: tgn


Volver a Programación

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados

cron