Imágenes
Funciones
Image* | image_from_pixels (...) |
Image* | image_from_pixbuf (...) |
Image* | image_from_file (...) |
Image* | image_from_data (...) |
const Image* | image_from_resource (...) |
Image* | image_copy (...) |
Image* | image_trim (...) |
Image* | image_rotate (...) |
Image* | image_scale (...) |
Image* | image_read (...) |
bool_t | image_to_file (...) |
void | image_write (...) |
void | image_destroy (...) |
pixformat_t | image_format (...) |
uint32_t | image_width (...) |
uint32_t | image_height (...) |
Pixbuf* | image_pixels (...) |
bool | image_codec (...) |
codec_t | image_get_codec (...) |
uint32_t | image_num_frames (...) |
real32_t | image_frame_length (...) |
void | image_data (...) |
type* | image_get_data (...) |
void* | image_native (...) |
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).
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.
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.
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); |
- Utiliza image_from_file para cargar una imagen de disco.
- Utiliza image_from_data para crear una imagen desde un búfer de memoria.
- Utiliza image_from_resource para obtener una imagen de un paquete de recursos.
- Utiliza image_read para crear una imagen a partir de Streams.
- En la demo UrlImg tienes un ejemplo de como descargarlas desde un servidor Web.
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.
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.
- Utiliza image_from_pixels para crear una imagen a partir de puntos.
- Utiliza image_from_pixbuf para crear una imagen a partir de un pixel búfer.
- Utiliza image_pixels para obtener un búfer con los píxeles de la imagen.
- Utiliza image_width para obtener la anchura.
- Utiliza image_height para obtener la altura.
- Utiliza image_format para obtener el formato de píxel.
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).
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.
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.
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 |
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 |
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.
const Image* image_from_resource(const ResPack *pack, const ResId id);
pack | El paquete de recursos. |
id | El identificador del recurso. |
Retorna
La imagen.
Observaciones
La imagen no debe ser destruida con image_destroy ya que forma parte del propio paquete (es constante). Hacer una copia con image_copy en caso de que deba conservarse tras la destrucción de los recursos. 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_trim ()
Crea una imagen recortando otra imagen.
Image* image_trim(const uint32_t x, const uint32_t y, const uint32_t width, const uint32_t height);
x | Coordenada X del origen de la sub-imagen. |
y | Coordenada Y del origen de la sub-imagen. |
width | Ancho en pixels de la sub-imagen. |
height | Alto en pixels de la sub-imagen. |
Retorna
La nueva imagen.
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 |
|
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 |
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 | El alto de la nueva imagen. Pasar UINT32_MAX para que se mantenga la relación de aspecto con respecto a |
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 |
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 |
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.
image_data ()
Vincula datos de usuario con la imagen.
void image_data(Image *image, type *data, FPtr_destroy func_destroy_data, type );
image | La imagen. |
data | Los datos de usuario. |
func_destroy_data | Destructor de los datos de usuario. |
Tipo de datos de usuario. |
image_get_data ()
Obtiene los datos de usuario de la imagen.
type* image_get_data(const Image *image, type );
image | La imagen. |
Tipo de datos de usuario. |
Retorna
Los datos de usuario.
image_native ()
Obtiene la imagen en el formato nativo de cada plataforma.
void* image_native(const Image *image);
image | La imagen. |
Retorna
La imagen nativa. Gdiplus::Bitmap
en Windows, GdkPixbuf
en Linux y NSImage
en macOS.