SDK Multiplataforma en C logo

SDK Multiplataforma en C

Imágenes

❮ Anterior
Siguiente ❯

Funciones

Image*image_from_pixels (...)
Image*image_from_pixbuf (...)
Image*image_from_file (...)
Image*image_from_data (...)
Image*image_from_resource (...)
Image*image_copy (...)
Image*image_rotate (...)
Image*image_scale (...)
Image*image_read (...)
bool_timage_to_file (...)
voidimage_write (...)
voidimage_destroy (...)
pixformat_timage_format (...)
uint32_timage_width (...)
uint32_timage_height (...)
Pixbuf*image_pixels (...)
boolimage_codec (...)
codec_timage_get_codec (...)
uint32_timage_num_frames (...)
real32_timage_frame_length (...)

Existe una estrecha relación entre los pixel búfer y las imágenes. Si bien los primeros contienen la información de color "en crudo", las segundas son objetos vinculados directamente con la API gráfica de cada sistema, lo que permite dibujarlas en contextos 2d o visualizarlas en una ventana (Figura 1).

Gráfico de implementación del objeto Image como Gdiplus::Bitmap, NSBitmapImageRep o GdkPixbuf.
Figura 1: Los objetos Image tienen una vinculación directa con las APIs gráficas, mientras que los Pixbuf no.

La estructura de una imagen digital, también llamada bitmap o raster graphics, es la misma que la de un píxel búfer. Tenemos una rejilla discreta de puntos de color caracterizada por su resolución (ancho, alto) y profundidad, que es la cantidad de bits necesaria para codificar cada pixel (Figura 2). Las imágenes bitmap funcionan mejor para captar instantáneas del mundo real, donde es prácticamente imposible describir la escena mediante primitivas geométricas, como vimos en Primitivas de dibujo. Como contrapartida, al estar compuesta por puntos discretos, no se comporta bien ante los cambios de tamaño donde sufrirá una pérdida de calidad.

La misma imagen en dos resoluciones diferentes.
Figura 2: A la izquierda imagen de 64x64 píxeles y 16 colores. A la derecha 256x256 píxeles y 16 millones de colores.

1. Cargar y visualizar imágenes

En la mayoría de ocasiones, lo único que necesitaremos saber sobre imágenes será como leerlas de disco u otro origen de datos para, posteriormente, visualizarlas en pantalla como parte de la interfaz de usuario (Listado 1) (Figura 3). Consideramos que las imágenes están almacenadas en alguno de los formatos estándar: JPG, PNG, BMP o GIF.

Listado 1: Carga y visualización de imágenes.
1
2
3
4
5
Image *img = image_from_file("lenna.jpg", NULL);
Image *icon = image_from_resource(pack, ekCANCEL);
...
imageview_image(view, img);
button_image(button, icon);
Lectura de una imagen desde disco y visualización.
Figura 3: Uso elemental de imágenes en la interfaz de usuario.

Una vez cargado el objeto imagen en memoria, disponemos de varias formas de visualizarlo:

  • Utiliza draw_image para dibujar una imagen en un contexto 2d.
  • Utiliza imageview_image para asignar una imagen a una vista. Este control soporta animaciones GIF.
  • Utiliza button_image para asignar una imagen a un botón.
  • Utiliza popup_add_elem para asignar un texto e icono a una lista desplegable.

2. Generar imágenes

Como ya vimos en Contextos 2D, si fuera necesario podemos crear nuestras propias imágenes a partir de comandos de dibujo para, posteriormente, mostrarlas en la interfaz (Figura 4) o guardarlas en disco.

  • Utiliza dctx_image para crear una imagen a partir de un contexto 2d.
  • Imagen generada desde comandos de dibujo.
    Figura 4: Imagen generada mediante comandos de dibujo (a la derecha).

3. Acceso a píxeles

Las imágenes son objetos inmutables optimizados para el dibujo recurrente en pantalla, por lo que se permiten ciertas licencias, tanto en la organización interna de la información de color como en la gestión de posibles copias. Por este motivo no es posible manipular directamente los píxeles, sino que debemos acceder a ellos mediante un Pixel Buffer.

Documentación técnica de Apple: "Treat NSImage and its image representations as immutable objects. The goal of NSImage is to provide an efficient way to display images on the target canvas. Avoid manipulating the data of an image representation directly, especially if there are alternatives to manipulating the data, such as compositing the image and some other content into a new image object."

Los pixel buffers nos permiten de una forma óptima manipular el contenido de la imagen. Para visualizar el resultado o almacenarlo en cualquiera de los formatos soportados, deberemos crear una nueva imagen (Figura 5).

Proceso desde un pixel buffer a una imagen.
Figura 5: Proceso de edición de imágenes.

4. Guardar imágenes: Codecs

Uno de los mayores problemas que presentan las imágenes digitales es la gran cantidad de memoria que ocupan. Una imagen de tan solo 1024x768 píxeles y 32bits de color necesita 3 megabytes de memoria. Puede que no parezca mucho, pero a finales de los años 80 esto suponía un gran handicap ya que la memoria era muy cara y las transmisiones muy lentas. Por esto se idearon varios sistemas de codificación (compresión) que reducían la cantidad de memoria necesaria y que se consolidaron con el auge de Internet (Figura 6).

  • Utiliza image_get_codec para obtener el codec asociado a la imagen.
  • Utiliza image_codec para cambiar el codec asociado a la imagen.
  • Utiliza image_to_file para guardarla en disco.
  • Utiliza image_write para escribirla en un Stream.
  • Logotipos de BMP, GIF, JPG y PNG.
    Figura 6: Formatos de imagen soportados por NAppGUI.
Draw2D no soporta de forma nativa otros formatos diferentes a los mencionados. En caso necesario, deberás encontrar la forma de crear un Pixbuf a partir de los datos específicos de tu formato, con el fin de integrar dichas imágenes en la interfaz de usuario.
  • JPEG: Joint Photographic Experts Group es un formato con una muy buena tasa de compresión basada en la Transformada de Fourier. Ideal para captar instantáneas del mundo real, aunque restará algo de calidad a la captura original (compresión con pérdida).
  • PNG: Portable Network Graphics surgió como respuesta a los problemas legales con el formato GIF. Soporta compresión LZ77/Deflate sin pérdida y formatos de píxel indexado. Ideal para esquemas, gráficos o imágenes generadas por computador.
  • GIF: Graphics Interchange Format. Utiliza el algoritmo de compresión propietario LZW, aunque la patente expiró en 2003. Ha sobrevivido a PNG gracias a que puede incluir animaciones en un solo archivo, algo que ninguno de los dos formatos anteriores soporta.
  • BMP: BitMaP. Formato nativo de Windows ampliamente superado por los otros tres. Aunque soporta un tipo especial de compresión denominado Run-Length encoding, lo cierto es que la mayoría de archivos se guardan sin comprimir. Los archivos BMP ocupan mucho más espacio, por este motivo se utiliza muy poco en Internet y casi nada en máquinas ajenas a Windows. Es soportado por casi todos los programas y sistemas gracias a que es muy sencillo y rápido de interpretar.

Para poder visualizarse en pantalla, la imagen debe ser descomprimida (de-codificada), proceso que se realiza automáticamente al leer la imagen. Al guardarla a disco o enviarla por la red se realiza el proceso contrario, se comprime o codifica utilizando el algoritmo asociado a la misma (Tabla 1), pero se puede cambiar.

Tabla 1: Codecs de imagen por defecto.
Constructor Codec
image_from_file El codec original.
image_from_data El codec original.
image_from_resource El codec original.
image_from_pixels ¿Transparencias? Sí:ekPNG No:ekJPG.
dctx_image ekPNG.
Por lo general, el soporte que brindan GDI+, NSImage o GdkPixbuf para la configuración de los codecs es bastante limitado. Por ejemplo, no es posible generar archivos PNG indexados, algo muy útil a la hora de reducir el tamaño de las imágenes para la Web. Si la aplicación requiriera mayor control sobre la exportación, no tendremos más remedio que utilizar libpng, libjpeg o cualquier otra solución de terceros.

image_from_pixels ()

Crea una imagen a partir de un array de píxeles.

Image*
image_from_pixels(const uint32_t width,
                  const uint32_t height,
                  const pixformat_t format,
                  const byte_t *data,
                  const color_t *palette,
                  const uint32_t palsize);
width

El ancho de la imagen (en píxeles).

height

El alto de la imagen (en píxeles).

format

El formato de pixel.

data

Búfer que contiene el valor de color de cada pixel. Dependerá de la resolución y del formato.

palette

Paleta de color necesaria para representar imágenes indexadas. Si es NULL y el formato es indexado, se utilizará una Paleta predefinida.

palsize

Número de colores en la paleta.

Retorna

La imagen recién creada.

Observaciones

Ver Acceso a píxeles.


image_from_pixbuf ()

Crea una imagen a partir de un píxel buffer.

Image*
image_from_pixbuf(const Pixbuf *pixbuf,
                  const Palette *palette);
pixbuf

El buffer.

palette

La paleta.

Retorna

La imagen recién creada.

Observaciones

Igual a image_from_pixels evitando indicar los parámetros por separado.


image_from_file ()

Crea una imagen a partir de un archivo en disco.

Image*
image_from_file(const char_t *pathname,
                ferror_t *error);
pathname

La ruta del archivo. Filename y pathname.

error

Código de error si la función falla. Puede ser NULL.

Retorna

La imagen recién creada.

Observaciones

Solo los formatos jpg, png, bmp y gif son aceptados.


image_from_data ()

Crea una imagen a partir de un búfer que contiene los datos codificados.

Image*
image_from_data(const byte_t *data,
                const uint32_t size);
data

El búfer con los datos de la imagen.

size

El tamaño del búfer en bytes.

Retorna

La imagen recién creada.

Observaciones

El búfer representa datos codificados en jpg, png, bmp o gif. Para crear la imagen directamente desde píxeles utiliza image_from_pixels.


image_from_resource ()

Obtiene una imagen de un paquete de recursos.

Image*
image_from_resource(const ResPack *pack,
                    const ResId id);
pack

El paquete de recursos.

id

El identificador del recurso.

Retorna

La imagen.

Observaciones

Ver Recursos.


image_copy ()

Crea una copia de la imagen.

Image*
image_copy(const Image *image);
image

La imagen original.

Retorna

La imagen copiada.

Observaciones

Las imágenes son objetos inmutables. Copiar realmente significa incrementar un contador interno sin llegar a clonar el objeto. No obstante, la aplicación debe destruir la copia con image_destroy al igual que las creadas con cualquier otro constructor. Cuando todas las copias sean destruídas se eliminará realmente de memoria.


image_rotate ()

Crea una nueva imagen rotando otra ya existente.

Image*
image_rotate(const Image *image,
             const real32_t angle,
             const bool_t nsize,
             const color_t background,
             T2Df *t2d);
image

La imagen original.

angle

Ángulo en radianes.

nsize

TRUE la imagen resultado se redimensionará para que quepa toda la original. FALSE la imagen resultado tendrá las mismas dimensiones que la original, recortando parte del contenido (clipping).

background

Color de fondo. La nueva imagen tendrá zonas "en blanco" debido a la rotación.

t2d

Guarda la transformación aplicada a la imagen. Pueden ser NULL si no necesitamos este valor.

Retorna

La imagen recién creada.


image_scale ()

Crea una copia de la imagen, con un nuevo tamaño.

Image*
image_scale(const Image *image,
            const uint32_t nwidth,
            const uint32_t nheight);
image

La imagen original.

nwidth

El ancho de la nueva imagen. Pasar UINT32_MAX para que se mantenga la relación de aspecto con respecto a nheight.

nheight

El alto de la nueva imagen. Pasar UINT32_MAX para que se mantenga la relación de aspecto con respecto a nwidth.

Retorna

La imagen recién creada.

Observaciones

Si ambos valores nwidth, nheight son UINT32_MAX o las nuevas dimensiones son idénticas a las actuales se incrementará el contador de referencias interno, al igual que ocurre en image_copy.


image_read ()

Crea una imagen a partir de los datos leídos desde un Streams.

Image*
image_read(Stream *stm);
stm

Stream de lectura. Se esperan datos codificados en jpg, png, bmp o gif. La función detecta el formato automáticamente.

Retorna

La imagen recién creada.


image_to_file ()

Guarda una imagen en disco, utilizado el codec asociado a la misma.

bool_t
image_to_file(const Image *image,
              const char_t *pathname,
              ferror_t *error);
image

La imagen.

pathname

La ruta del archivo destino. Filename y pathname.

error

Código de error si la función falla. Puede ser NULL.

Retorna

TRUE si se ha guardado correctamente o FALSE y ha ocurrido algún error.

Observaciones

Utiliza image_codec para cambiar el codec por defecto.


image_write ()

Escribe una imagen en un stream de salida, utilizado el codec asociado a la misma.

void
image_write(Stream *stm,
            const Image *image);
stm

Stream de escritura. Se escribirán datos codificados en jpg, png, bmp o gif.

image

La imagen.

Observaciones

Utiliza image_codec para cambiar el codec por defecto.


image_destroy ()

Destruye la imagen.

void
image_destroy(Image **image);
image

La imagen. Será puesto a NULL tras la destrucción.


image_format ()

Obtiene el formato de pixel de la imagen.

pixformat_t
image_format(const Image *image);
image

La imagen.

Retorna

El formato de pixel.


image_width ()

Obtiene el ancho de la imagen en pixels.

uint32_t
image_width(const Image *image);
image

La imagen.

Retorna

Número de pixeles de anchura.


image_height ()

Obtiene el alto de la imagen en pixels.

uint32_t
image_height(const Image *image);
image

La imagen.

Retorna

Número de pixeles de altura.


image_pixels ()

Obtiene un búfer con los píxeles que forman la imagen decodificada.

Pixbuf*
image_pixels(const Image *image,
             const pixformat_t format);
image

La imagen.

format

El formato de pixel requerido.

Retorna

Píxel búfer con el contenido de la imagen.

Observaciones

Si en pixformat indicamos ekFIMAGE devolverá el búfer con el formato original de la imagen. Podemos indicar ekRGB24, ekRGBA32 o ekGRAY8 si necesitamos un formato específico. No es posible utilizar formatos indexados.


image_codec ()

Cambia el codec por defecto asociado con la imagen.

bool
image_codec(const Image *image,
            const codec_t codec);
1
2
3
4
Image *img = image_from_file("lenna.jpg", NULL);
Stream *stm = stm_socket(ip, port, NULL, NULL);
image_codec(img, ekPNG);
image_write(socket, img);
image

La imagen.

codec

El nuevo codec.

Retorna

TRUE si el API gráfico soporta el codec seleccionado. FALSE en caso contrario.

Observaciones

El cambio tendrá efecto la próxima vez que guardemos o escribamos la imagen. Por defecto, la imagen conserva el codec con el que fue leída. Cuando la creamos con image_from_pixels se le asigna el codec ekJPG por defecto. Para imágenes desde contextos 2d dctx_image, el codec por defecto es ekPNG. Todos los codecs son soportados por todas las APIs gráficas, salvo ekGIF en algunas versiones de Linux. Chequea en valor de retorno si es imperativo que tu aplicación exporte imágenes en GIF.


image_get_codec ()

Obtiene el codec asociado con la imagen.

codec_t
image_get_codec(const Image *image);
image

La imagen.

Retorna

El codec.

Observaciones

Ver image_codec.


image_num_frames ()

Obtiene el número de secuencias en imágenes animadas.

uint32_t
image_num_frames(const Image *image);
image

La imagen.

Retorna

El número de secuencias o frames.

Observaciones

Únicamente el formato gif soporta animaciones. Para el resto siempre se devolverá 1.


image_frame_length ()

Obtiene el tiempo que dura una secuencia de animación.

real32_t
image_frame_length(const Image *image,
                   const uint32_t findex);
image

La imagen.

findex

El índice del frame.

Retorna

El tiempo de la secuencia en segundos.

Observaciones

Únicamente el formato gif soporta animaciones.

❮ Anterior
Siguiente ❯