/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 by Linus Nielsen Feltzing * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /********************************************************************* * * Converts BMP files to Rockbox bitmap format * * 1999-05-03 Linus Nielsen Feltzing * **********************************************/ #include #include #include #include #include "file.h" #ifdef __GNUC__ #define STRUCT_PACKED __attribute__((packed)) #endif 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 */ } STRUCT_PACKED; struct RGBQUAD { unsigned char rgbBlue; unsigned char rgbGreen; unsigned char rgbRed; unsigned char rgbReserved; } STRUCT_PACKED; static struct Fileheader fh; static unsigned char* bmp; static struct RGBQUAD palette[2]; /* two colors only */ static unsigned int bitmap_width, bitmap_height; static unsigned char *bitmap; #ifdef STANDALONE static id_str[256]; static bool compress = false; static bool assembly = false; static unsigned char* converted_bmp; static unsigned char* compressed_bmp; static unsigned int width; static unsigned int converted_size; static unsigned int compressed_size; static unsigned int rounded_width; #endif #ifdef LITTLE_ENDIAN #define readshort(x) x #define readlong(x) x #else #define readshort(x) (((x&0xff00)>>8)|((x&0x00ff)<<8)) #define readlong(x) (((x&0xff000000)>>24)| \ ((x&0x00ff0000)>>8) | \ ((x&0x0000ff00)<<8) | \ ((x&0x000000ff)<<24)) #endif /********************************************************************* * read_bmp_file() * * Reads a monochrome BMP file and puts the data in a 1-pixel-per-byte * array. Returns 0 on success. * **********************************************/ int read_bmp_file(char* filename, int *get_width, /* in pixels */ int *get_height, /* in pixels */ char *bitmap) { long PaddedWidth; int background; int fd = open(filename, O_RDONLY); long size; unsigned int row, col, byte, bit; int l; unsigned char *bmp; int width; int height; if(fd == -1) { debugf("error - can't open '%s'\n", filename); return 1; } else { if(read(fd, &fh, sizeof(struct Fileheader)) != sizeof(struct Fileheader)) { debugf("error - can't Read Fileheader Stucture\n"); close(fd); return 2; } /* Exit if not monochrome */ if(readshort(fh.BitCount) > 8) { debugf("error - Bitmap must be less than 8, got %d\n", readshort(fh.BitCount)); close(fd); return 2; } /* Exit if too wide */ if(readlong(fh.Width) > 112) { debugf("error - Bitmap is too wide (%d pixels, max is 112)\n", readlong(fh.Width)); close(fd); return 3; } debugf("Bitmap is %d pixels wide\n", readlong(fh.Width)); /* Exit if too high */ if(readlong(fh.Height) > 64) { debugf("error - Bitmap is too high (%d pixels, max is 64)\n", readlong(fh.Height)); close(fd); return 4; } debugf("Bitmap is %d pixels heigh\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; } } /* 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)*readshort(fh.BitCount); */ width = readlong(fh.Width); /* PaddedWidth = ((width+31)&(~0x1f))/8; */ PaddedWidth = ((width+7)&(~0x7)); size = PaddedWidth*readlong(fh.Height); bmp = (unsigned char *)malloc(size); 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 0 /* 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++) { 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++; } } } #else /* 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); } } } #endif } close(fd); return 0; /* success */ } #ifdef STANDALONE /********************************************************************* ** read_next_converted_byte() ** ** Reads the next 6-pixel chunk from the 1-byte-per-pixel array, ** padding the last byte with zeros if the size is not divisible by 6. **********************************************/ unsigned char read_next_converted_byte(void) { unsigned char dest; unsigned int i; static unsigned int row = 0, col = 0; dest = 0; for(i = 0;i < 6 && col < bitmap_width;i++,col++) { if(bitmap[row * bitmap_width + col]) { dest |= (unsigned char)(1 << (5-i)); } } if(col >= bitmap_width) { col = 0; row++; } return dest; } /********************************************************************* ** convert_image() ** ** Converts the 1-byte-per-pixel array into a 6-pixel-per-byte array, ** i.e the BMP_FORMAT_VANILLA format. **********************************************/ void convert_image(void) { int newsize; unsigned int row, col; rounded_width = fh.Width/6 + ((fh.Width%6)?1:0); newsize = rounded_width * fh.Height; converted_bmp = (unsigned char *)malloc(newsize); for(row = 0;row < fh.Height;row++) { for(col = 0;col < rounded_width;col++) { converted_bmp[row * rounded_width + col] = read_next_converted_byte(); } } converted_size = rounded_width * fh.Height; } #define COMPRESSED_ZEROS_AHEAD 0x40 #define COMPRESSED_ONES_AHEAD 0x80 /********************************************************************* ** compress_image() ** ** Compresses the BMP_FORMAT_VANILLA format with a simple RLE ** algorithm. The output is in the BMP_FORMAT_RLE format. **********************************************/ void compress_image(void) { unsigned int i, j, count; unsigned int index = 0; unsigned char val; compressed_bmp = (unsigned char *)malloc(converted_size); for(i = 0;i < converted_size;i++) { val = converted_bmp[i]; if(val == 0|| val == 0x3f) { count = 0; while(count < 0x4000 && (i + count) < converted_size && converted_bmp[i+count] == val) { count++; } if(count > 2) { compressed_bmp[index++] = (unsigned char) (((val == 0)?COMPRESSED_ZEROS_AHEAD:COMPRESSED_ONES_AHEAD) | (count >> 8)); compressed_bmp[index++] = (unsigned char)(count & 0xff); } else { for(j = 0;j < count;j++) { compressed_bmp[index++] = val; } } i += count - 1; } else { compressed_bmp[index++] = val; } } compressed_size = index; } /********************************************************************* ** generate_c_source() ** ** Outputs a C source code with the converted/compressed bitmap in ** an array, accompanied by some #define's **********************************************/ void generate_c_source(char *id, BOOL compressed) { FILE *f; unsigned int i; unsigned int size; unsigned char *bmp; size = compressed?compressed_size:converted_size; bmp = compressed?compressed_bmp:converted_bmp; f = stdout; fprintf(f, "#define %s_WIDTH %d\n", id, rounded_width * 6); fprintf(f, "#define %s_HEIGHT %d\n", id, fh.Height); fprintf(f, "#define %s_SIZE %d\n", id, size + 6); if(compressed) { fprintf(f, "#define %s_ORIGINAL_SIZE %d\n", id, converted_size); } fprintf(f, "#define %s_FORMAT %s\n", id, compressed?"BMP_FORMAT_RLE":"BMP_FORMAT_VANILLA"); fprintf(f, "\nconst unsigned char bmpdata_%s[] = {", id); /* Header */ fprintf(f, "\n %s, 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,", compressed?"BMP_FORMAT_RLE":"BMP_FORMAT_VANILLA", size >> 8, size & 0xff, 2, fh.Height, rounded_width * 6); for(i = 0;i < size;i++) { if(i % 10 == 0) { fprintf(f, "\n "); } fprintf(f, "0x%02x,", bmp[i]); } fprintf(f, "\n};"); } /********************************************************************* ** generate_asm_source() ** ** Outputs an ASM source code with the converted/compressed bitmap in ** an array, the first 2 bytes describing the X and Y size **********************************************/ void generate_asm_source(char *id, BOOL compressed) { FILE *f; unsigned int i; unsigned int size; unsigned char *bmp; size = compressed?compressed_size:converted_size; bmp = compressed?compressed_bmp:converted_bmp; f = stdout; fprintf(f, "bmpdata_%s:\n", id); /* Header */ fprintf(f, "\n db %s, %.2xh,%.2xh,%.2xh,%.2xh,%.2xh,", compressed?"1":"0", size >> 8, size & 0xff, 2, fh.Height, rounded_width * 6); for(i = 0;i < size;i++) { if(i % 10 == 0) { fprintf(f, "\n db "); } fprintf(f, "%.2xh,", bmp[i]); } fprintf(f, "\n"); } void print_usage(void) { printf("bmp2mt - Converts BMP files to MT Pro source code format\n"); printf("build date: " __DATE__ "\n\n"); printf("Usage: %s [-i ] [-c] [-a] \n" "-i Bitmap ID (default is filename without extension)\n" "-c Compress (BMP_FORMAT_RLE)\n" "-f Frexx Format!!!\n" "-a Assembly format source code\n", APPLICATION_NAME); } #pragma argsused int main(int argc, char **argv) { char *bmp_filename = NULL; char *id = NULL; char errstr[80]; int i; 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 'c': /* Compressed */ compress = true; break; case 'a': /* Assembly */ assembly = 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) { id = strdup(bmp_filename); for(i = 0;id[i];i++) { if(id[i] == ' ') { id[i] = '_'; } else if(id[i] == '.') { id[i] = '\0'; break; } else { id[i] = (char)toupper(id[i]); } } } read_bmp_file(bmp_filename); convert_image(); if(fh.Width % 6) { sprintf(errstr, "warning - width is not divisible by 6 (%d), " "padding with zeros to %d\n", fh.Width, rounded_width*6); print_error(errstr, 0); } if(compress) { compress_image(); } if(assembly) { generate_asm_source(id, compress); } else { generate_c_source(id, compress); } return 0; } #endif