diff options
Diffstat (limited to 'rd/read-bmp.c')
-rw-r--r-- | rd/read-bmp.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/rd/read-bmp.c b/rd/read-bmp.c new file mode 100644 index 0000000..30b5574 --- /dev/null +++ b/rd/read-bmp.c @@ -0,0 +1,216 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif + +#include "readers.h" + +/* ---------------------------------------------------------------------- */ + +typedef unsigned int uint32; +typedef unsigned short uint16; + +/* bitmap files are little endian */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define le16_to_cpu(x) (x) +# define le32_to_cpu(x) (x) +#elif BYTE_ORDER == BIG_ENDIAN +# define le16_to_cpu(x) (((x>>8) & 0x00ff) |\ + ((x<<8) & 0xff00)) +# define le32_to_cpu(x) (((x>>24) & 0x000000ff) |\ + ((x>>8) & 0x0000ff00) |\ + ((x<<8) & 0x00ff0000) |\ + ((x<<24) & 0xff000000)) +#else +# error "Oops: unknown byte order" +#endif + +/* ---------------------------------------------------------------------- */ +/* load */ + +struct bmp_hdr { + uint32 foobar; + + uint32 size; /* == BitMapInfoHeader */ + uint32 width; + uint32 height; + uint16 planes; + uint16 bit_cnt; + char compression[4]; + uint32 image_size; + uint32 xpels_meter; + uint32 ypels_meter; + uint32 num_colors; /* used colors */ + uint32 imp_colors; /* important colors */ + /* may be more for some codecs */ +}; + +struct bmp_cmap { + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char unused; +}; + +struct bmp_state { + struct bmp_hdr hdr; + struct bmp_cmap cmap[256]; + FILE *fp; +}; + +static void* +bmp_init(FILE *fp, char *filename, unsigned int page, + struct ida_image_info *i, int thumbnail) +{ + struct bmp_state *h; + + h = malloc(sizeof(*h)); + memset(h,0,sizeof(*h)); + h->fp = fp; + + fseek(fp,10,SEEK_SET); + fread(&h->hdr,sizeof(struct bmp_hdr),1,fp); + +#if BYTE_ORDER == BIG_ENDIAN + h->hdr.foobar = le32_to_cpu(h->hdr.foobar); + h->hdr.size = le32_to_cpu(h->hdr.size); + h->hdr.width = le32_to_cpu(h->hdr.width); + h->hdr.height = le32_to_cpu(h->hdr.height); + + h->hdr.planes = le16_to_cpu(h->hdr.planes); + h->hdr.bit_cnt = le16_to_cpu(h->hdr.bit_cnt); + + h->hdr.image_size = le32_to_cpu(h->hdr.image_size); + h->hdr.xpels_meter = le32_to_cpu(h->hdr.xpels_meter); + h->hdr.ypels_meter = le32_to_cpu(h->hdr.ypels_meter); + h->hdr.num_colors = le32_to_cpu(h->hdr.num_colors); + h->hdr.imp_colors = le32_to_cpu(h->hdr.imp_colors); +#endif + + if (debug) + fprintf(stderr,"bmp: hdr=%d size=%dx%d planes=%d" + " bits=%d size=%d res=%dx%d colors=%d/%d | %d\n", + h->hdr.size,h->hdr.width,h->hdr.height, + h->hdr.planes,h->hdr.bit_cnt,h->hdr.image_size, + h->hdr.xpels_meter,h->hdr.ypels_meter, + h->hdr.num_colors,h->hdr.imp_colors,h->hdr.foobar); + if (h->hdr.bit_cnt != 1 && + h->hdr.bit_cnt != 4 && + h->hdr.bit_cnt != 8 && + h->hdr.bit_cnt != 24) { + fprintf(stderr,"bmp: can't handle depth [%d]\n",h->hdr.bit_cnt); + goto oops; + } + if (h->hdr.compression[0] || h->hdr.compression[1] || + h->hdr.compression[2] || h->hdr.compression[3]) { + fprintf(stderr,"bmp: can't handle compressed bitmaps [%c%c%c%c]\n", + h->hdr.compression[0], + h->hdr.compression[1], + h->hdr.compression[2], + h->hdr.compression[3]); + goto oops; + } + + if (0 == h->hdr.num_colors && h->hdr.bit_cnt <= 8) + h->hdr.num_colors = (1 << h->hdr.bit_cnt); + if (h->hdr.num_colors > 256) + h->hdr.num_colors = 256; + if (h->hdr.num_colors) { + fseek(fp,14+h->hdr.size,SEEK_SET); + fread(&h->cmap,sizeof(struct bmp_cmap),h->hdr.num_colors,fp); + } + + i->width = h->hdr.width; + i->height = h->hdr.height; + if (h->hdr.xpels_meter) + i->dpi = res_m_to_inch(h->hdr.xpels_meter); + i->npages = 1; + return h; + + oops: + free(h); + return NULL; +} + +static void +bmp_read(unsigned char *dst, unsigned int line, void *data) +{ + struct bmp_state *h = data; + unsigned int ll,y,x,pixel,byte = 0; + + ll = (((h->hdr.width * h->hdr.bit_cnt + 31) & ~0x1f) >> 3); + y = h->hdr.height - line - 1; + fseek(h->fp,h->hdr.foobar + y * ll,SEEK_SET); + + switch (h->hdr.bit_cnt) { + case 1: + for (x = 0; x < h->hdr.width; x++) { + if (0 == (x & 0x07)) + byte = fgetc(h->fp); + pixel = byte & (0x80 >> (x & 0x07)) ? 1 : 0; + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 4: + for (x = 0; x < h->hdr.width; x++) { + if (x & 1) { + pixel = byte & 0xf; + } else { + byte = fgetc(h->fp); + pixel = byte >> 4; + } + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 8: + for (x = 0; x < h->hdr.width; x++) { + pixel = fgetc(h->fp); + *(dst++) = h->cmap[pixel].red; + *(dst++) = h->cmap[pixel].green; + *(dst++) = h->cmap[pixel].blue; + } + break; + case 24: + for (x = 0; x < h->hdr.width; x++) { + dst[2] = fgetc(h->fp); + dst[1] = fgetc(h->fp); + dst[0] = fgetc(h->fp); + dst += 3; + } + break; + default: + memset(dst,128,h->hdr.width*3); + break; + } +} + +static void +bmp_done(void *data) +{ + struct bmp_state *h = data; + + fclose(h->fp); + free(h); +} + +static struct ida_loader bmp_loader = { + magic: "BM", + moff: 0, + mlen: 2, + name: "bmp", + init: bmp_init, + read: bmp_read, + done: bmp_done, +}; + +static void __init init_rd(void) +{ + load_register(&bmp_loader); +} |