connect progress signal directly to the logger.
[Rockbox.git] / tools / bmp2rb.c
blob10481917a40998b979c73990d9dc9fcf253a5f69
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 /****************************************************************************
21 * Converts BMP files to Rockbox bitmap format
23 * 1999-05-03 Linus Nielsen Feltzing
25 * 2005-07-06 Jens Arnold
26 * added reading of 4, 16, 24 and 32 bit bmps
27 * added 2 new target formats (playergfx and iriver 4-grey)
29 ****************************************************************************/
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdbool.h>
35 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
41 #define debugf printf
43 #ifdef __GNUC__
44 #define STRUCT_PACKED __attribute__((packed))
45 #else
46 #define STRUCT_PACKED
47 #pragma pack (push, 2)
48 #endif
50 struct Fileheader
52 unsigned short Type; /* signature - 'BM' */
53 unsigned int Size; /* file size in bytes */
54 unsigned short Reserved1; /* 0 */
55 unsigned short Reserved2; /* 0 */
56 unsigned int OffBits; /* offset to bitmap */
57 unsigned int StructSize; /* size of this struct (40) */
58 unsigned int Width; /* bmap width in pixels */
59 unsigned int Height; /* bmap height in pixels */
60 unsigned short Planes; /* num planes - always 1 */
61 unsigned short BitCount; /* bits per pixel */
62 unsigned int Compression; /* compression flag */
63 unsigned int SizeImage; /* image size in bytes */
64 int XPelsPerMeter; /* horz resolution */
65 int YPelsPerMeter; /* vert resolution */
66 unsigned int ClrUsed; /* 0 -> color table size */
67 unsigned int ClrImportant; /* important color count */
68 } STRUCT_PACKED;
70 struct RGBQUAD
72 unsigned char rgbBlue;
73 unsigned char rgbGreen;
74 unsigned char rgbRed;
75 unsigned char rgbReserved;
76 } STRUCT_PACKED;
78 short readshort(void* value)
80 unsigned char* bytes = (unsigned char*) value;
81 return bytes[0] | (bytes[1] << 8);
84 int readint(void* value)
86 unsigned char* bytes = (unsigned char*) value;
87 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
90 unsigned char brightness(struct RGBQUAD color)
92 return (3 * (unsigned int)color.rgbRed + 6 * (unsigned int)color.rgbGreen
93 + (unsigned int)color.rgbBlue) / 10;
96 #ifndef O_BINARY
97 #define O_BINARY 0 /* systems that don't have O_BINARY won't make a difference
98 on text and binary files */
99 #endif
101 /****************************************************************************
102 * read_bmp_file()
104 * Reads an uncompressed BMP file and puts the data in a 4-byte-per-pixel
105 * (RGBQUAD) array. Returns 0 on success.
107 ***************************************************************************/
109 int read_bmp_file(char* filename,
110 int *get_width, /* in pixels */
111 int *get_height, /* in pixels */
112 struct RGBQUAD **bitmap)
114 struct Fileheader fh;
115 struct RGBQUAD palette[256];
117 int fd = open(filename, O_RDONLY| O_BINARY);
118 unsigned short data;
119 unsigned char *bmp;
120 int width, height;
121 int padded_width;
122 int size;
123 int row, col, i;
124 int numcolors, compression;
125 int depth;
127 if (fd == -1)
129 debugf("error - can't open '%s'\n", filename);
130 return 1;
132 if (read(fd, &fh, sizeof(struct Fileheader)) !=
133 sizeof(struct Fileheader))
135 debugf("error - can't Read Fileheader Stucture\n");
136 close(fd);
137 return 2;
140 compression = readint(&fh.Compression);
142 if (compression != 0)
144 debugf("error - Unsupported compression %d\n", compression);
145 close(fd);
146 return 3;
149 depth = readshort(&fh.BitCount);
151 if (depth <= 8)
153 numcolors = readint(&fh.ClrUsed);
154 if (numcolors == 0)
155 numcolors = 1 << depth;
157 if (read(fd, &palette[0], numcolors * sizeof(struct RGBQUAD))
158 != (int)(numcolors * sizeof(struct RGBQUAD)))
160 debugf("error - Can't read bitmap's color palette\n");
161 close(fd);
162 return 4;
166 width = readint(&fh.Width);
167 height = readint(&fh.Height);
168 padded_width = ((width * depth + 31) / 8) & ~3; /* aligned 4-bytes boundaries */
170 size = padded_width * height; /* read this many bytes */
171 bmp = (unsigned char *)malloc(size);
172 *bitmap = (struct RGBQUAD *)malloc(width * height * sizeof(struct RGBQUAD));
174 if ((bmp == NULL) || (*bitmap == NULL))
176 debugf("error - Out of memory\n");
177 close(fd);
178 return 5;
181 if (lseek(fd, (off_t)readint(&fh.OffBits), SEEK_SET) < 0)
183 debugf("error - Can't seek to start of image data\n");
184 close(fd);
185 return 6;
187 if (read(fd, (unsigned char*)bmp, (int)size) != size)
189 debugf("error - Can't read image\n");
190 close(fd);
191 return 7;
194 close(fd);
195 *get_width = width;
196 *get_height = height;
198 switch (depth)
200 case 1:
201 for (row = 0; row < height; row++)
202 for (col = 0; col < width; col++)
204 data = (bmp[(height - 1 - row) * padded_width + col / 8]
205 >> (~col & 7)) & 1;
206 (*bitmap)[row * width + col] = palette[data];
208 break;
210 case 4:
211 for (row = 0; row < height; row++)
212 for (col = 0; col < width; col++)
214 data = (bmp[(height - 1 - row) * padded_width + col / 2]
215 >> (4 * (~col & 1))) & 0x0F;
216 (*bitmap)[row * width + col] = palette[data];
218 break;
220 case 8:
221 for (row = 0; row < height; row++)
222 for (col = 0; col < width; col++)
224 data = bmp[(height - 1 - row) * padded_width + col];
225 (*bitmap)[row * width + col] = palette[data];
227 break;
229 case 16:
230 for (row = 0; row < height; row++)
231 for (col = 0; col < width; col++)
233 data = readshort(&bmp[(height - 1 - row) * padded_width + 2 * col]);
234 (*bitmap)[row * width + col].rgbRed =
235 ((data >> 7) & 0xF8) | ((data >> 12) & 0x07);
236 (*bitmap)[row * width + col].rgbGreen =
237 ((data >> 2) & 0xF8) | ((data >> 7) & 0x07);
238 (*bitmap)[row * width + col].rgbBlue =
239 ((data << 3) & 0xF8) | ((data >> 2) & 0x07);
240 (*bitmap)[row * width + col].rgbReserved = 0;
242 break;
244 case 24:
245 for (row = 0; row < height; row++)
246 for (col = 0; col < width; col++)
248 i = (height - 1 - row) * padded_width + 3 * col;
249 (*bitmap)[row * width + col].rgbRed = bmp[i+2];
250 (*bitmap)[row * width + col].rgbGreen = bmp[i+1];
251 (*bitmap)[row * width + col].rgbBlue = bmp[i];
252 (*bitmap)[row * width + col].rgbReserved = 0;
254 break;
256 case 32:
257 for (row = 0; row < height; row++)
258 for (col = 0; col < width; col++)
260 i = (height - 1 - row) * padded_width + 4 * col;
261 (*bitmap)[row * width + col].rgbRed = bmp[i+2];
262 (*bitmap)[row * width + col].rgbGreen = bmp[i+1];
263 (*bitmap)[row * width + col].rgbBlue = bmp[i];
264 (*bitmap)[row * width + col].rgbReserved = 0;
266 break;
268 default: /* should never happen */
269 debugf("error - Unsupported bitmap depth %d.\n", depth);
270 return 8;
273 free(bmp);
275 return 0; /* success */
278 /****************************************************************************
279 * transform_bitmap()
281 * Transform a 4-byte-per-pixel bitmap (RGBQUAD) into one of the supported
282 * destination formats
283 ****************************************************************************/
285 int transform_bitmap(const struct RGBQUAD *src, int width, int height,
286 int format, unsigned short **dest, int *dst_width,
287 int *dst_height, int *dst_depth)
289 int row, col;
290 int dst_w, dst_h, dst_d;
292 switch (format)
294 case 0: /* Archos recorders, Ondio, Iriver H1x0 monochrome */
295 dst_w = width;
296 dst_h = (height + 7) / 8;
297 dst_d = 8;
298 break;
300 case 1: /* Archos player graphics library */
301 dst_w = (width + 7) / 8;
302 dst_h = height;
303 dst_d = 8;
304 break;
306 case 2: /* Iriver H1x0 4-grey */
307 dst_w = width;
308 dst_h = (height + 3) / 4;
309 dst_d = 8;
310 break;
312 case 3: /* Canonical 8-bit grayscale */
313 dst_w = width;
314 dst_h = height;
315 dst_d = 8;
316 break;
318 case 4: /* 16-bit packed RGB (5-6-5) */
319 case 5: /* 16-bit packed and byte-swapped RGB (5-6-5) */
320 dst_w = width;
321 dst_h = height;
322 dst_d = 16;
323 break;
325 case 6: /* greyscale iPods 4-grey */
326 dst_w = (width + 3) / 4;
327 dst_h = height;
328 dst_d = 8;
329 break;
331 case 7: /* greyscale X5 remote 4-grey */
332 dst_w = width;
333 dst_h = (height + 7) / 8;
334 dst_d = 16;
335 break;
337 default: /* unknown */
338 debugf("error - Undefined destination format\n");
339 return 1;
342 *dest = (unsigned short *)malloc(dst_w * dst_h * sizeof(short));
343 if (*dest == NULL)
345 debugf("error - Out of memory.\n");
346 return 2;
348 memset(*dest, 0, dst_w * dst_h * sizeof(short));
349 *dst_width = dst_w;
350 *dst_height = dst_h;
351 *dst_depth = dst_d;
353 switch (format)
355 case 0: /* Archos recorders, Ondio, Iriver H1x0 b&w */
356 for (row = 0; row < height; row++)
357 for (col = 0; col < width; col++)
359 (*dest)[(row/8) * dst_w + col] |=
360 (~brightness(src[row * width + col]) & 0x80) >> (~row & 7);
362 break;
364 case 1: /* Archos player graphics library */
365 for (row = 0; row < height; row++)
366 for (col = 0; col < width; col++)
368 (*dest)[row * dst_w + (col/8)] |=
369 (~brightness(src[row * width + col]) & 0x80) >> (col & 7);
371 break;
373 case 2: /* Iriver H1x0 4-grey */
374 for (row = 0; row < height; row++)
375 for (col = 0; col < width; col++)
377 (*dest)[(row/4) * dst_w + col] |=
378 (~brightness(src[row * width + col]) & 0xC0) >> (2 * (~row & 3));
380 break;
382 case 3: /* Canonical 8-bit grayscale */
383 for (row = 0; row < height; row++)
384 for (col = 0; col < width; col++)
386 (*dest)[row * dst_w + col] = brightness(src[row * width + col]);
388 break;
390 case 4: /* 16-bit packed RGB (5-6-5) */
391 case 5: /* 16-bit packed and byte-swapped RGB (5-6-5) */
392 for (row = 0; row < height; row++)
393 for (col = 0; col < width; col++)
395 unsigned short rgb =
396 (((src[row * width + col].rgbRed >> 3) << 11) |
397 ((src[row * width + col].rgbGreen >> 2) << 5) |
398 ((src[row * width + col].rgbBlue >> 3)));
400 if (format == 4)
401 (*dest)[row * dst_w + col] = rgb;
402 else
403 (*dest)[row * dst_w + col] = ((rgb&0xff00)>>8)|((rgb&0x00ff)<<8);
405 break;
407 case 6: /* greyscale iPods 4-grey */
408 for (row = 0; row < height; row++)
409 for (col = 0; col < width; col++)
411 (*dest)[row * dst_w + (col/4)] |=
412 (~brightness(src[row * width + col]) & 0xC0) >> (2 * (col & 3));
414 break;
416 case 7: /* greyscale X5 remote 4-grey */
417 for (row = 0; row < height; row++)
418 for (col = 0; col < width; col++)
420 unsigned short data = (~brightness(src[row * width + col]) & 0xC0) >> 6;
422 data = (data | (data << 7)) & 0x0101;
423 (*dest)[(row/8) * dst_w + col] |= data << (row & 7);
425 break;
428 return 0;
431 /****************************************************************************
432 * generate_c_source()
434 * Outputs a C source code with the bitmap in an array, accompanied by
435 * some #define's
436 ****************************************************************************/
438 void generate_c_source(char *id, char* header_dir, int width, int height,
439 const unsigned short *t_bitmap, int t_width,
440 int t_height, int t_depth)
442 FILE *f;
443 FILE *fh;
444 int i, a;
445 char header_name[1024];
447 if (!id || !id[0])
448 id = "bitmap";
450 f = stdout;
452 if (header_dir && header_dir[0])
454 snprintf(header_name,sizeof(header_name),"%s/%s.h",header_dir,id);
455 fh = fopen(header_name,"w+");
457 if (fh == NULL)
459 debugf("error - can't open '%s'\n", header_name);
460 return;
462 fprintf(fh,
463 "#define BMPHEIGHT_%s %d\n"
464 "#define BMPWIDTH_%s %d\n",
465 id, height, id, width);
466 if (t_depth <= 8)
467 fprintf(fh, "extern const unsigned char %s[];\n", id);
468 else
469 fprintf(fh, "extern const unsigned short %s[];\n", id);
471 fclose(fh);
472 } else {
473 fprintf(f,
474 "#define BMPHEIGHT_%s %d\n"
475 "#define BMPWIDTH_%s %d\n",
476 id, height, id, width);
479 if (t_depth <= 8)
480 fprintf(f, "const unsigned char %s[] = {\n", id);
481 else
482 fprintf(f, "const unsigned short %s[] = {\n", id);
484 for (i = 0; i < t_height; i++)
486 for (a = 0; a < t_width; a++)
488 if (t_depth <= 8)
489 fprintf(f, "0x%02x,%c", t_bitmap[i * t_width + a],
490 (a + 1) % 13 ? ' ' : '\n');
491 else
492 fprintf(f, "0x%04x,%c", t_bitmap[i * t_width + a],
493 (a + 1) % 10 ? ' ' : '\n');
495 fprintf(f, "\n");
498 fprintf(f, "\n};\n");
501 void generate_raw_file(const unsigned short *t_bitmap,
502 int t_width, int t_height, int t_depth)
504 FILE *f;
505 int i, a;
506 unsigned char lo,hi;
508 f = stdout;
510 for (i = 0; i < t_height; i++)
512 for (a = 0; a < t_width; a++)
514 if (t_depth <= 8)
516 lo = (t_bitmap[i * t_width + a] & 0x00ff);
517 fwrite(&lo, 1, 1, f);
519 else
521 lo = (t_bitmap[i * t_width + a] & 0x00ff);
522 hi = (t_bitmap[i * t_width + a] & 0xff00) >> 8;
523 fwrite(&lo, 1, 1, f);
524 fwrite(&hi, 1, 1, f);
530 /****************************************************************************
531 * generate_ascii()
533 * Outputs an ascii picture of the bitmap
534 ****************************************************************************/
536 void generate_ascii(int width, int height, struct RGBQUAD *bitmap)
538 FILE *f;
539 int x, y;
541 f = stdout;
543 /* for screen output debugging */
544 for (y = 0; y < height; y++)
546 for (x = 0; x < width; x++)
548 fprintf(f, (brightness(bitmap[y * width + x]) & 0x80) ? " " : "*");
550 fprintf(f, "\n");
554 void print_usage(void)
556 printf("Usage: %s [-i <id>] [-a] <bitmap file>\n"
557 "\t-i <id> Bitmap name (default is filename without extension)\n"
558 "\t-h <dir> Create header file in <dir>/<id>.h\n"
559 "\t-a Show ascii picture of bitmap\n"
560 "\t-r Generate RAW file (little-endian)\n"
561 "\t-f <n> Generate destination format n, default = 0\n"
562 "\t 0 Archos recorder, Ondio, Iriver H1x0 mono\n"
563 , APPLICATION_NAME);
564 printf("\t 1 Archos player graphics library\n"
565 "\t 2 Iriver H1x0 4-grey\n"
566 "\t 3 Canonical 8-bit greyscale\n"
567 "\t 4 16-bit packed 5-6-5 RGB (iriver H300)\n"
568 "\t 5 16-bit packed and byte-swapped 5-6-5 RGB (iPod)\n"
569 "\t 6 Greyscale iPod 4-grey\n"
570 "\t 7 Greyscale X5 remote 4-grey\n");
571 printf("build date: " __DATE__ "\n\n");
574 int main(int argc, char **argv)
576 char *bmp_filename = NULL;
577 char *id = NULL;
578 char* header_dir = NULL;
579 int i;
580 int ascii = false;
581 int format = 0;
582 struct RGBQUAD *bitmap = NULL;
583 unsigned short *t_bitmap = NULL;
584 int width, height;
585 int t_width, t_height, t_depth;
586 bool raw = false;
589 for (i = 1;i < argc;i++)
591 if (argv[i][0] == '-')
593 switch (argv[i][1])
595 case 'h': /* .h filename */
596 if (argv[i][2])
598 header_dir = &argv[i][2];
600 else if (argc > i+1)
602 header_dir = argv[i+1];
603 i++;
605 else
607 print_usage();
608 exit(1);
610 break;
612 case 'i': /* ID */
613 if (argv[i][2])
615 id = &argv[i][2];
617 else if (argc > i+1)
619 id = argv[i+1];
620 i++;
622 else
624 print_usage();
625 exit(1);
627 break;
629 case 'a': /* Ascii art */
630 ascii = true;
631 break;
633 case 'r': /* Raw File */
634 raw = true;
635 break;
637 case 'f':
638 if (argv[i][2])
640 format = atoi(&argv[i][2]);
642 else if (argc > i+1)
644 format = atoi(argv[i+1]);
645 i++;
647 else
649 print_usage();
650 exit(1);
652 break;
654 default:
655 print_usage();
656 exit(1);
657 break;
660 else
662 if (!bmp_filename)
664 bmp_filename = argv[i];
666 else
668 print_usage();
669 exit(1);
674 if (!bmp_filename)
676 print_usage();
677 exit(1);
680 if (!id)
682 char *ptr=strrchr(bmp_filename, '/');
683 if (ptr)
684 ptr++;
685 else
686 ptr = bmp_filename;
687 id = strdup(ptr);
688 for (i = 0; id[i]; i++)
689 if (id[i] == '.')
690 id[i] = '\0';
693 if (read_bmp_file(bmp_filename, &width, &height, &bitmap))
694 exit(1);
697 if (ascii)
699 generate_ascii(width, height, bitmap);
701 else
703 if (transform_bitmap(bitmap, width, height, format, &t_bitmap,
704 &t_width, &t_height, &t_depth))
705 exit(1);
706 if(raw)
707 generate_raw_file(t_bitmap, t_width, t_height, t_depth);
708 else
709 generate_c_source(id, header_dir, width, height, t_bitmap,
710 t_width, t_height, t_depth);
713 return 0;