From c5e87ae1e03b33c34266f6c7fed2cb5b159e0e65 Mon Sep 17 00:00:00 2001 From: Jens Arnold Date: Thu, 7 Jul 2005 22:32:42 +0000 Subject: Heavily extended bmp2rb conversion tool: Handles 4, 16, 24 and 32 bit BMPs in addition to 1 and 8 bit. Generates one of 3 output formats: (0) Archos recorder, Ondio, Gmini, H1x0 monochrome; this is the default. (1) Archos player graphics library. (2) H1x0 4-shade greyscale. Decision about the pixel value is based on the true pixel brightness, so hopefully no more inverted images. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7058 a1c6a512-1295-4272-9138-f99709370657 --- tools/bmp2rb.c | 771 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 429 insertions(+), 342 deletions(-) diff --git a/tools/bmp2rb.c b/tools/bmp2rb.c index f874fed677..52941fca15 100644 --- a/tools/bmp2rb.c +++ b/tools/bmp2rb.c @@ -16,13 +16,17 @@ * KIND, either express or implied. * ****************************************************************************/ -/********************************************************************* +/**************************************************************************** * * Converts BMP files to Rockbox bitmap format * * 1999-05-03 Linus Nielsen Feltzing * - **********************************************/ + * 2005-07-06 Jens Arnold + * added reading of 4, 16, 24 and 32 bit bmps + * added 2 new target formats (playergfx and iriver 4-grey) + * + ****************************************************************************/ #include #include @@ -44,33 +48,32 @@ struct Fileheader { - unsigned short Type; /* signature - 'BM' */ - unsigned long Size; /* file size in bytes */ - unsigned short Reserved1; /* 0 */ - unsigned short Reserved2; /* 0 */ - unsigned long OffBits; /* offset to bitmap */ - unsigned long StructSize; /* size of this struct (40) */ - unsigned long Width; /* bmap width in pixels */ - unsigned long Height; /* bmap height in pixels */ - unsigned short Planes; /* num planes - always 1 */ - unsigned short BitCount; /* bits per pixel */ - unsigned long Compression; /* compression flag */ - unsigned long SizeImage; /* image size in bytes */ - long XPelsPerMeter; /* horz resolution */ - long YPelsPerMeter; /* vert resolution */ - unsigned long ClrUsed; /* 0 -> color table size */ - unsigned long ClrImportant; /* important color count */ + unsigned short Type; /* signature - 'BM' */ + unsigned long Size; /* file size in bytes */ + unsigned short Reserved1; /* 0 */ + unsigned short Reserved2; /* 0 */ + unsigned long OffBits; /* offset to bitmap */ + unsigned long StructSize; /* size of this struct (40) */ + unsigned long Width; /* bmap width in pixels */ + unsigned long Height; /* bmap height in pixels */ + unsigned short Planes; /* num planes - always 1 */ + unsigned short BitCount; /* bits per pixel */ + unsigned long Compression; /* compression flag */ + unsigned long SizeImage; /* image size in bytes */ + long XPelsPerMeter; /* horz resolution */ + long YPelsPerMeter; /* vert resolution */ + unsigned long ClrUsed; /* 0 -> color table size */ + unsigned long ClrImportant; /* important color count */ } STRUCT_PACKED; struct RGBQUAD { - unsigned char rgbBlue; - unsigned char rgbGreen; - unsigned char rgbRed; - unsigned char rgbReserved; + unsigned char rgbBlue; + unsigned char rgbGreen; + unsigned char rgbRed; + unsigned char rgbReserved; } STRUCT_PACKED; - short readshort(void* value) { unsigned char* bytes = (unsigned char*) value; @@ -83,366 +86,450 @@ int readlong(void* value) return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); } -/********************************************************************* +unsigned char brightness(unsigned char red, unsigned char green, + unsigned char blue) +{ + return (3 * (unsigned int)red + 6 * (unsigned int)green + + (unsigned int)blue) / 10; +} + +/**************************************************************************** * read_bmp_file() * - * Reads a 8bit BMP file and puts the data in a 1-pixel-per-byte - * array. Returns 0 on success. + * Reads an uncompressed BMP file and puts the data in a 1-pixel-per-byte + * array, sorted by brightness. Returns 0 on success. * - *********************************************************************/ + ***************************************************************************/ + int read_bmp_file(char* filename, - int *get_width, /* in pixels */ - int *get_height, /* in pixels */ - char **bitmap) + long *get_width, /* in pixels */ + long *get_height, /* in pixels */ + unsigned char **bitmap) { struct Fileheader fh; - struct RGBQUAD palette[2]; /* two colors only */ + struct RGBQUAD palette[256]; + unsigned char bright[256]; - unsigned int bitmap_width, bitmap_height; - - long PaddedWidth; - int background; int fd = open(filename, O_RDONLY); - long size; - long allocsize; - unsigned int row, col; - int l; + unsigned short data; unsigned char *bmp; - int width; - int height; + long width, height; + long padded_width; + long size; + long row, col, i; + long numcolors, compression; int depth; - if(fd == -1) + if (fd == -1) { debugf("error - can't open '%s'\n", filename); return 1; } - else - { - if(read(fd, &fh, sizeof(struct Fileheader)) != + if (read(fd, &fh, sizeof(struct Fileheader)) != sizeof(struct Fileheader)) - { - debugf("error - can't Read Fileheader Stucture\n"); - close(fd); - return 2; - } - - /* Exit if more than 8 bits */ - depth = readshort(&fh.BitCount); - if(depth > 8) - { - debugf("error - Bitmap uses more than 8 bit depth, got %d\n", - depth); - close(fd); - return 2; - } - - /* Exit if too wide */ - if(readlong(&fh.Width) > 160) - { - debugf("error - Bitmap is too wide for iRiver models (%d pixels, max is 160)\n", - readlong(&fh.Width)); - return 3; - } - if(readlong(&fh.Width) > 112) - { - debugf("info - Bitmap is too wide for Archos models (%d pixels, max is 112)\n", - readlong(&fh.Width)); - } - - /* Exit if too high */ - if(readlong(&fh.Height) > 128) - { - debugf("error - Bitmap is too high for iRiver models (%d pixels, max is 128)\n", - readlong(&fh.Height)); - return 4; - } - if(readlong(&fh.Height) > 64) - { - debugf("info - Bitmap is too high for Archos models (%d pixels, max is 64)\n", - readlong(&fh.Height)); - } - - for(l=0;l < 2;l++) - { - if(read(fd, &palette[l],sizeof(struct RGBQUAD)) != - sizeof(struct RGBQUAD)) - { - debugf("error - Can't read bitmap's color palette\n"); - close(fd); - return 5; - } - } - if(depth == 8 ) { - /* pass the other palettes */ - lseek(fd, 254*sizeof(struct RGBQUAD), SEEK_CUR); - } - - /* Try to guess the foreground and background colors. - We assume that the foreground color is the darkest. */ - if(((int)palette[0].rgbRed + - (int)palette[0].rgbGreen + - (int)palette[0].rgbBlue) > - ((int)palette[1].rgbRed + - (int)palette[1].rgbGreen + - (int)palette[1].rgbBlue)) - { - background = 0; - } - else - { - background = 1; - } - - width = readlong(&fh.Width); - - if(depth == 8) - PaddedWidth = ((width+3)&(~0x3)); /* aligned 4-bytes boundaries */ - else - PaddedWidth = ((width+31)&(~0x1f))/8; - - height = readlong(&fh.Height); - - allocsize = size = PaddedWidth*height; /* read this many bytes */ - bmp = (unsigned char *)malloc(size); - - if(height%8) { - /* not even 8 bytes, add up to a full 8 pixels boundary */ - height += 8-(height%8); - allocsize = PaddedWidth*height; /* bytes to alloc */ - } - - *bitmap = (unsigned char *)malloc(allocsize); - - if(bmp == NULL) - { - debugf("error - Out of memory\n"); - close(fd); - return 6; - } - else - { - if(read(fd, (unsigned char*)bmp,(long)size) != size) { - debugf("error - Can't read image\n"); - close(fd); - return 7; - } - } - - bitmap_height = readlong(&fh.Height); - bitmap_width = readlong(&fh.Width); - - *get_width = bitmap_width; - *get_height = bitmap_height; - - if(depth == 8) - { - /* Now convert the bitmap into an array with 1 byte per pixel, - exactly the size of the image */ - for(row = 0;row < bitmap_height;row++) { - for(col = 0;col < bitmap_width;col++) { - if(bmp[(bitmap_height-1 -row) * PaddedWidth + col]) { - (*bitmap)[ (row/8) * bitmap_width + col ] &= - ~ (1<<(row&7)); - } - else { - (*bitmap)[ (row/8) * bitmap_width + col ] |= - 1<<(row&7); - } - } - } - } - else - { - int bit; - int byte; - /* monocrome BMP conversion uses 8 pixels per byte */ - for(row = 0; row < bitmap_height; row++) { - bit = 7; - byte = 0; - for(col = 0;col < bitmap_width;col++) { - if((bmp[(bitmap_height - row - 1) * PaddedWidth + byte] & - (1 << bit))) { - (*bitmap)[(row/8) * bitmap_width + col ] &= - ~(1<<(row&7)); - } - else { - (*bitmap)[(row/8) * bitmap_width + col ] |= - 1<<(row&7); - } - if(bit) { - bit--; - } - else { - bit = 7; - byte++; - } - } - } - } - - free(bmp); - - } - close(fd); - return 0; /* success */ + { + debugf("error - can't Read Fileheader Stucture\n"); + close(fd); + return 2; + } + + compression = readlong(&fh.Compression); + + if (compression != 0) + { + debugf("error - Unsupported compression %ld\n", compression); + close(fd); + return 3; + } + + depth = readshort(&fh.BitCount); + + if (depth <= 8) + { + numcolors = readlong(&fh.ClrUsed); + if (numcolors == 0) + numcolors = 1 << depth; + + if (read(fd, &palette[0], numcolors * sizeof(struct RGBQUAD)) + != numcolors * sizeof(struct RGBQUAD)) + { + debugf("error - Can't read bitmap's color palette\n"); + close(fd); + return 4; + } + + /* Precalculate weighted brightnesses for the palette */ + for (i = 0; i < numcolors; i++) + bright[i] = brightness(palette[i].rgbRed, palette[i].rgbGreen, + palette[i].rgbBlue); + } + + width = readlong(&fh.Width); + height = readlong(&fh.Height); + padded_width = (width * depth / 8 + 3) & ~3; /* aligned 4-bytes boundaries */ + + size = padded_width * height; /* read this many bytes */ + bmp = (unsigned char *)malloc(size); + *bitmap = (unsigned char *)malloc(width * height); + + if ((bmp == NULL) || (*bitmap == NULL)) + { + debugf("error - Out of memory\n"); + close(fd); + return 5; + } + + if (lseek(fd, (off_t)readlong(&fh.OffBits), SEEK_SET) < 0) + { + debugf("error - Can't seek to start of image data\n"); + close(fd); + return 6; + } + if (read(fd, (unsigned char*)bmp, (long)size) != size) + { + debugf("error - Can't read image\n"); + close(fd); + return 7; + } + + close(fd); + *get_width = width; + *get_height = height; + + switch (depth) + { + case 1: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + data = (bmp[(height - 1 - row) * padded_width + col / 8] + >> (~col & 7)) & 1; + (*bitmap)[row * width + col] = bright[data]; + } + break; + + case 4: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + data = (bmp[(height - 1 - row) * padded_width + col / 2] + >> (4 * (~col & 1))) & 0x0F; + (*bitmap)[row * width + col] = bright[data]; + } + break; + + case 8: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + data = bmp[(height - 1 - row) * padded_width + col]; + (*bitmap)[row * width + col] = bright[data]; + } + break; + + case 16: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + data = readshort(&bmp[(height - 1 - row) * padded_width + 2 * col]); + (*bitmap)[row * width + col] = brightness((data >> 7) & 0xF8, + (data >> 2) & 0xF8, (data << 3) & 0xF8); + } + break; + + case 24: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + i = (height - 1 - row) * padded_width + 3 * col; + (*bitmap)[row * width + col] = + brightness(bmp[i+2], bmp[i+1], bmp[i]); + } + break; + + case 32: + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + i = (height - 1 - row) * padded_width + 4 * col; + (*bitmap)[row * width + col] = + brightness(bmp[i+2], bmp[i+1], bmp[i]); + } + break; + + default: /* should never happen */ + debugf("error - Unsupported bitmap depth %d.\n", depth); + return 8; + } + + free(bmp); + + return 0; /* success */ +} + +/**************************************************************************** + * transform_bitmap() + * + * Transform a 1-byte-per-pixel bitmap into one of the supported + * destination formats + ****************************************************************************/ + +int transform_bitmap(const unsigned char *src, long width, long height, + int format, unsigned char **dest, long *dst_width, + long *dst_height) +{ + long row, col; + long dst_w, dst_h; + + switch (format) + { + case 0: /* Archos recorders, Ondio, Gmini 120/SP, Iriver H1x0 monochrome */ + dst_w = width; + dst_h = (height + 7) / 8; + break; + + case 1: /* Archos player graphics library */ + dst_w = (width + 7) / 8; + dst_h = height; + break; + + case 2: /* Iriver H1x0 4-grey */ + dst_w = width; + dst_h = (height + 3) / 4; + break; + + default: /* unknown */ + debugf("error - Undefined destination format\n"); + return 1; + } + + *dest = (unsigned char *)malloc(dst_w * dst_h); + if (*dest == NULL) + { + debugf("error - Out of memory.\n"); + return 2; + } + memset(*dest, 0, dst_w * dst_h); + *dst_width = dst_w; + *dst_height = dst_h; + + switch (format) + { + case 0: /* Archos recorders, Ondio, Gmini 120/SP, Iriver H1x0 b&w */ + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + (*dest)[(row/8) * dst_w + col] |= + (~src[row * width + col] & 0x80) >> (~row & 7); + } + break; + + case 1: /* Archos player graphics library */ + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + (*dest)[row * dst_w + (col/8)] |= + (~src[row * width + col] & 0x80) >> (col & 7); + } + break; + + case 2: /* Iriver H1x0 4-grey */ + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + (*dest)[(row/4) * dst_w + col] |= + (~src[row * width + col] & 0xC0) >> (2 * (~row & 3)); + } + break; + } + + return 0; } -/********************************************************************* -** generate_c_source() -** -** Outputs a C source code with the bitmap in an array, accompanied by -** some #define's -**********************************************************************/ -void generate_c_source(char *id, int width, int height, unsigned char *bitmap) +/**************************************************************************** + * generate_c_source() + * + * Outputs a C source code with the bitmap in an array, accompanied by + * some #define's + ****************************************************************************/ + +void generate_c_source(char *id, long width, long height, + const unsigned char *t_bitmap, long t_width, + long t_height) { - FILE *f; - unsigned int i, a, eline; + FILE *f; + long i, a; - f = stdout; + f = stdout; - if(!id || !id[0]) - id = "bitmap"; + if (!id || !id[0]) + id = "bitmap"; - fprintf(f, - "#define BMPHEIGHT_%s %d" - "\n#define BMPWIDTH_%s %d" - "\nconst unsigned char %s[] = {\n", - id, height, id, width, id ); + fprintf(f, + "#define BMPHEIGHT_%s %ld\n" + "#define BMPWIDTH_%s %ld\n" + "const unsigned char %s[] = {\n", + id, height, id, width, id); - for(i=0, eline=0; i< height; i+=8, eline++) { - for (a=0; a] [-a] \n" - "\t-i Bitmap name (default is filename without extension)\n" - "\t-a Show ascii picture of bitmap\n", - APPLICATION_NAME); - printf("build date: " __DATE__ "\n\n"); + printf("Usage: %s [-i ] [-a] \n" + "\t-i Bitmap name (default is filename without extension)\n" + "\t-a Show ascii picture of bitmap\n" + "\t-f Generate destination format n, default = 0\n" + "\t 0 Archos recorder, Ondio, Gmini 120/SP, Iriver H1x0 mono\n" + "\t 1 Archos player graphics library\n" + "\t 2 Iriver H1x0 4-grey\n" + , APPLICATION_NAME); + printf("build date: " __DATE__ "\n\n"); } int main(int argc, char **argv) { - char *bmp_filename = NULL; - char *id = NULL; - int i; - int height, width; - int ascii = false; - char* bitmap = NULL; - - - for(i = 1;i < argc;i++) - { - if(argv[i][0] == '-') - { - switch(argv[i][1]) - { - case 'i': /* ID */ - if(argv[i][2]) - { - id = &argv[i][2]; - } - else if(argc > i+1) - { - id = argv[i+1]; - i++; - } - else - { - print_usage(); - exit(1); - } - break; - - case 'a': /* Assembly */ - ascii = true; - break; - - default: - print_usage(); - exit(1); - break; - } - } - else - { - if(!bmp_filename) - { - bmp_filename = argv[i]; - } - else - { - print_usage(); - exit(1); - } - } - } - - if (!bmp_filename) - { - print_usage(); - exit(1); - } - - if (!id) - { - char *ptr=strrchr(bmp_filename, '/'); - if(ptr) - ptr++; - else - ptr = bmp_filename; - id = strdup(ptr); - for (i = 0; id[i]; i++) - if (id[i] == '.') - id[i] = '\0'; - } - - if (read_bmp_file(bmp_filename, &width, &height, &bitmap)) - return 0; - - if (ascii) - generate_ascii(width, height, bitmap); - else - generate_c_source(id, width, height, bitmap); - - return 0; + char *bmp_filename = NULL; + char *id = NULL; + int i; + int ascii = false; + int format = 0; + unsigned char *bitmap = NULL; + unsigned char *t_bitmap = NULL; + long width, height; + long t_width, t_height; + + + for (i = 1;i < argc;i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'i': /* ID */ + if (argv[i][2]) + { + id = &argv[i][2]; + } + else if (argc > i+1) + { + id = argv[i+1]; + i++; + } + else + { + print_usage(); + exit(1); + } + break; + + case 'a': /* Ascii art */ + ascii = true; + break; + + case 'f': + if (argv[i][2]) + { + format = atoi(&argv[i][2]); + } + else if (argc > i+1) + { + format = atoi(argv[i+1]); + i++; + } + else + { + print_usage(); + exit(1); + } + break; + + default: + print_usage(); + exit(1); + break; + } + } + else + { + if (!bmp_filename) + { + bmp_filename = argv[i]; + } + else + { + print_usage(); + exit(1); + } + } + } + + if (!bmp_filename) + { + print_usage(); + exit(1); + } + + if (!id) + { + char *ptr=strrchr(bmp_filename, '/'); + if (ptr) + ptr++; + else + ptr = bmp_filename; + id = strdup(ptr); + for (i = 0; id[i]; i++) + if (id[i] == '.') + id[i] = '\0'; + } + + if (read_bmp_file(bmp_filename, &width, &height, &bitmap)) + exit(1); + + + if (ascii) + { + generate_ascii(width, height, bitmap); + } + else + { + if (transform_bitmap(bitmap, width, height, format, &t_bitmap, + &t_width, &t_height)) + exit(1); + generate_c_source(id, width, height, t_bitmap, t_width, t_height); + } + + return 0; } -- cgit v1.2.3