Ejercicio obligatorio 3
Fecha de entrega: Lunes 11 de noviembre
Introducción
El formato de Windows Bitmap (BMP) es un formato binario con muchas limitaciones pero sencillo de implementar y entender y un formato ampliamente difundido.
Si bien el mismo tiene muchos modos de operación, vamos a centrarnos en la versión de 1 bit monocromo, no comprimida, etc. para simplificar sus particularidades.
Un archivo BMP consta de tres secciones:
Sección |
Tamaño |
---|---|
Encabezado de archivo |
14 |
Encabezado de imagen |
40 |
Tabla de colores |
8 |
Pixeles |
-- |
Cada una de estas secciones tiene un formato específico.
Si bien la mayor parte de los campos del archivo son de un solo byte, hay
números almacenados en más de un byte y en este caso esos números son números
signados almacenados según la convención little-endian. Es decir, si un campo
numérico contuviera la secuencia {0xAA, 0xBB, 0xCC, 0xDD}
el número
representado sería el 0xDDCCBBAA
.
Encabezado de archivo
El encabezado de archivo consiste en la siguiente secuencia:
Campo |
Tipo |
Valor |
---|---|---|
Tipo |
|
|
Tamaño |
|
El tamaño en bytes del archivo |
Reservado |
|
|
Reservado |
|
|
Offset |
|
|
Como se ve, el único valor no definido del formato es el tamaño en bytes del archivo. El parámetro del offset indica en qué posición comienza la tabla de píxeles en el archivo, pero siendo que estamos usando un formato simplificado donde el encabezado de imagen siempre mide 40 bytes y una paleta de 2 colores y 8 bytes entonces este valor queda fijo.
Encabezado de imagen
El encabezado de imagen consiste en la siguiente secuencia:
Campo |
Tipo |
Valor |
---|---|---|
Tamaño |
|
|
Ancho |
|
El ancho de la imagen |
Alto |
|
El alto de la imagen |
Planos |
|
|
Bits de color |
|
|
Compresión |
|
|
Tamaño de imagen |
|
|
Resolución X |
|
|
Resolución Y |
|
|
Tablas de color |
|
|
Colores importantes |
|
|
Como se ve, con todas las simiplificaciones adoptadas, los únicos dos parámetros variables son el ancho y el alto de la imagen. Al leer un archivo es importante validar que tanto los bits de color, como la compresión como las tablas de color estén en los valores indicados; si no, no sabemos cómo procesar ese archivo. Con nuestra simplificación sólo sabemos leer imágenes monocromáticas.
Cabe remarcar que la altura podría llegar a ser un valor negativo.
Tabla de colores
La tabla de colores será una sucesión de:
Rojo |
|
El valor de rojo |
Verde |
|
El valor de verde |
Azul |
|
El valor de azul |
--- |
|
Reservado |
Como tenemos sólo dos colores tendremos dos veces esa secuencia.
Pixeles
Como la imagen es monocroma los pixeles ocuparán sólo 1 bit. Es decir, en un byte se empaquetarán 8 bits.
Los archivos BMP se definen por scan lines, cada scan line es una fila de la imagen. Ahora bien, cada scan line tiene que ocupar un número múltiplo de 4 bytes, entonces, si una imagen tuviera, por ejemplo 20 pixeles de ancho, alcanzarían 2 bytes y medio, o sea 3 bytes, para representar una fila; bueno, la scan line igual ocupará 4 bytes, porque tiene que siempre ser un múltiplo de 4.
Notar que el largo de la scan line puede computarse mirando cuánto falta para que sea múltiplo de 4 la operación de dividir por 8 el ancho de la imagen.
En la scan line los bits se ordenan "como se leen". El bit más pesado del primer byte será el primer pixel, el bit que le sigue el segundo y el bit más liviano del primer byte será el octavo pixel. Luego el bit más pesado del segundo byte será el noveno pixel y así hasta terminar la línea. Una vez que se agota el ancho todo lo que sirve para completar la scan line son ceros.
La sección de píxeles consistirá en una secuencia de tantas scan lines como altura tenga la imagen. Si el parámetro de altura se hubiera indicado como un número positivo las scan lines están ordenadas de abajo hacia arriba, en cambio si fuera negativo estarán ordenadas de arriba hacia abajo.
Trabajo
Endianness
Escribir una función bool leer_int16_little_endian(FILE *f, int16_t *v);
que lea un entero de 16 bits en formato little-endian del archivo f
y lo
escriba en v
. La función debe devolver true
si puede leer correctamente.
Escribir una función bool leer_int32_little_endian(FILE *f, int32_t *v);
similar a la anterior pero que lea un entero de 32 bits.
Nota
No puede asumirse que la plataforma es big/little-endian. Las funciones deben operar con los bytes a bajo nivel y ser portables a cualquier arquitectura.
TDA Imagen
Diseñar un TDA imagen_t
que represente una imagen monocroma con las
siguientes primitivas:
imagen_t *imagen_desde_archivo_BMP(FILE *f);
que reciba un archivof
que debería ser un BMP monocromo y devuelva la imagen contenida en él. Si el formato fuera inválido o hubiera una falla de memoria devolveráNULL
.void imagen_destruir(imagen_t *im);
libera la memoria contenida en la imagen.size_t imagen_ancho(const imagen_t *im);
getter del ancho de la imagen.size_t imagen_alto(const imagen_t *im);
getter del alto de la imagen.bool imagen_get_pixel(const imagen_t *im, size_t fila, size_t columna);
getter de un pixel de la imagen.
Aplicación
Desarrollar una aplicación que se ejecute como:
$ ./ej3 imagen.bmp
que muestre por stdout
la imagen pasada como parámetro.
Ejemplo
Por ejemplo la siguiente imagen:
es una versión escalada de este original en BMP:
El contenido de esta imagen es:
$ hd 20242_ej3.bmp
00000000 42 4d 5e 00 00 00 00 00 00 00 3e 00 00 00 28 00 |BM^.......>...(.|
00000010 00 00 1a 00 00 00 08 00 00 00 01 00 01 00 00 00 |................|
00000020 00 00 20 00 00 00 13 0b 00 00 13 0b 00 00 02 00 |.. .............|
00000030 00 00 02 00 00 00 00 00 00 00 ff ff ff 00 dd dd |................|
00000040 9c 40 dd dd 6b 80 dc 1d eb 80 de bd eb 80 de bd |.@..k...........|
00000050 9b 80 de bd db 80 df 79 eb 80 07 7d 0c 40 |.......y...}.@|
0000005e
$
El archivo tiene 26 (0x0000001a
) pixels de ancho. Por ejemplo, tomar la
primera scanline que aparece en el archivo, que es la de abajo:
{0xdd, 0xdd, 0x9c, 0x40}
, como las scanlines tienen múltiplos de 4 bytes a
esta le sobran 6 bits. Los bits en binario serían 11011101110111011001110001
esa es la secuencia de blancos y negros (blanco: 1, negro: 0) en la última fila
de la imagen.
La aplicación al ser ejecutada debería mostrar algo como:
$ ./ej3 20242_ej3.bmp
* * * * * * * * * * * * * *
* * * * * * *
* * * * * * *
* * * * * * * *
* * * * * * *
* * * * * * * * * *
* * * * * * * *
* * * * * * * * *
$
Entrega
Deberá entregarse el código fuente del programa desarrollado o los fuentes y el Makefile en caso de haber modularizado.
El programa debe:
Compilar correctamente con los flags:
-Wall -Werror -std=c99 -pedantic
y generar la salida correcta para imágenes BMP monocromas.
La entrega se realiza a través del sistema de entregas.
El ejercicio es de entrega individual.