1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - tools - ppmconvbg
3   File:     ppmconvbg.c
4 
5   Copyright 2003-2008 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date::            $
14   $Rev:$
15   $Author:$
16  *---------------------------------------------------------------------------*/
17 //
18 //  The 'ppmconvbg' tool is for $TwlSDK/build/demos/tips/OnScreenWarning
19 //
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>                    // getopt()
25 #include <ctype.h>
26 
27 #ifndef	MAX_COLORS
28 #define	MAX_COLORS		256    // 256 color limits
29 #endif
30 
31 #define	TRUE			1
32 #define	FALSE			0
33 
34 #define NUM_COLUMN_BYTE		16
35 #define NUM_COLUMN_HALF		8
36 
37 #define	V5bit( x )		((x)>>3)
38 #define	RGB5551( r, g, b, a )	((V5bit(r)<<0) | (V5bit(g)<<5) | (V5bit(b)<<10)| (((a)&1)<<15))
39 
40 #define	SIZE_READBUFFER	1024
41 
42 enum
43 {
44     PHASE_READ_MAGIC_NUMBER,
45     PHASE_READ_WIDTH,
46     PHASE_READ_HEIGHT,
47     PHASE_READ_DEPTH,
48 };
49 
50 enum
51 {
52     COLOR_MAX_8bit = 256,
53     COLOR_MAX_4bit = 16,
54 };
55 
56 enum
57 {
58     PARTS_CLUT = 1,
59     PARTS_INDEX = 2,
60     PARTS_ALL = 3,
61 };
62 
63 typedef unsigned char u8;
64 typedef unsigned short u16;
65 
66 typedef struct
67 {
68     u16    *color;
69     int     num_colors;
70     int     max_colors;
71 }
72 ColorTable;
73 
usage(char * str,int num)74 static void usage(char *str, int num)
75 {
76     if (str)
77     {
78         fprintf(stderr, "Error: ");
79         fprintf(stderr, str, num);
80         fprintf(stderr, "\n\n");
81     }
82     fprintf(stderr,
83             "Usage: ppmconvbg [-a labelname] [-b] [-c [4|8]] [-p] [-i] ppmfile outputfile \n");
84     fprintf(stderr, "\n");
85     fprintf(stderr, "  - Read [ppmfile] and convert it to [outputfile]\n");
86     fprintf(stderr, "\n");
87     fprintf(stderr, "          -a labelname : output as C source code         \n");
88     fprintf(stderr, "          -b           : output as binary file   (default:binary)\n");
89     fprintf(stderr, "          -c 4 or 8    : color mode 4bit or 8bit (default:8bit  )\n");
90     fprintf(stderr, "          -p           : output palette data    \n");
91     fprintf(stderr, "          -i           : output color index data\n");
92     fprintf(stderr, "\n");
93 
94     exit(1);
95 }
96 
97 static int Rgb2Index(int num_pixels, u8 *rgb_buffer, u8 *index_buffer, int max_colors,
98                      ColorTable * cp);
99 static void OutputColumnInit(void);
100 static void OutputByte(FILE * fp, int val);
101 static void OutputHalf(FILE * fp, int val);
102 static void OutputIndex(FILE * fp, char *label, int max_colors, int width, int height, u8 *pixel);
103 static void OutputCLUT(FILE * fp, char *label, ColorTable * cp);
104 
105 int     DebugMode = FALSE;
106 
107 /*---------------------------------------------------------------------------*
108   Name:         ReadHeader
109 
110   Description:  Read header information of ppm file
111 
112   Arguments:    fp      ppm file
113                 pwidth  output ptr for the width  of ppm picture
114                 pheight output ptr for the height of ppm picture
115                 pdepth  output ptr for the depth  of ppm picture
116 
117   Returns:      1 if success, 0 if error
118  *---------------------------------------------------------------------------*/
ReadHeader(FILE * fp,int * pwidth,int * pheight,int * pdepth)119 static int ReadHeader(FILE * fp, int *pwidth, int *pheight, int *pdepth)
120 {
121     // Get ppm header
122     //
123     // P6[WS]256[WS]192[WS]255[WS]\n   WS=Space,Tab,CR
124     //
125     char    buffer[SIZE_READBUFFER], *p;
126     int     n;
127     int     phase = PHASE_READ_MAGIC_NUMBER;
128 
129     while (buffer == fgets(buffer, sizeof(buffer), fp))
130     {
131         if (buffer[0] == '#')          // comment
132         {
133             continue;
134         }
135 
136         p = buffer;
137         while (*p != '\0')
138         {
139             switch (phase)
140             {
141             case PHASE_READ_MAGIC_NUMBER:
142                 if (0 != sscanf(p, "P6%n", &n))
143                 {
144                     return FALSE;      // not ppm file
145                 }
146                 phase = PHASE_READ_WIDTH;
147                 break;
148 
149             case PHASE_READ_WIDTH:
150                 if (1 != sscanf(p, "%d%n", pwidth, &n))
151                 {
152                     return FALSE;
153                 }
154                 phase = PHASE_READ_HEIGHT;
155                 break;
156 
157             case PHASE_READ_HEIGHT:
158                 if (1 != sscanf(p, "%d%n", pheight, &n))
159                 {
160                     return FALSE;
161                 }
162                 phase = PHASE_READ_DEPTH;
163                 break;
164 
165             case PHASE_READ_DEPTH:
166                 if (1 != sscanf(p, "%d%n", pdepth, &n))
167                 {
168                     return FALSE;
169                 }
170                 return TRUE;
171 
172             default:
173                 break;
174             }
175 
176             for (p += n; isspace(*p); p++)
177             {
178                 // Do nothing
179             }
180         }
181     }
182     return FALSE;
183 }
184 
185 
186 /*---------------------------------------------------------------------------*
187   Name:         ReadBody
188 
189   Description:  Read picture image
190 
191   Arguments:    fp      ppm file
192                 buffer	output ptr for image
193                 size    buffer size
194 
195   Returns:      1 if success, 0 if error
196  *---------------------------------------------------------------------------*/
ReadBody(FILE * fp,u8 * buffer,int size)197 static int ReadBody(FILE * fp, u8 *buffer, int size)
198 {
199     return size == fread(buffer, sizeof(u8), size, fp);
200 }
201 
202 
203 /*---------------------------------------------------------------------------*
204   Name:         ColorTableInit
205 
206   Description:  Initialize Color Table
207 
208   Arguments:    None
209 
210   Returns:      N/A
211  *---------------------------------------------------------------------------*/
ColorTableInit(ColorTable * t,int max_colors)212 static void ColorTableInit(ColorTable * t, int max_colors)
213 {
214     if (NULL == (t->color = (u16 *)calloc(max_colors, sizeof(u16))))
215     {
216         fprintf(stderr, "Cannot allocate memory.");
217         exit(1);
218     }
219 
220     t->num_colors = 0;
221     t->max_colors = max_colors;
222 }
223 
224 /*---------------------------------------------------------------------------*
225   Name:         ColorTableAppend
226 
227   Description:  Get color index from Color Table
228 
229   Arguments:    t      ptr for color table
230                 color  color to be added
231 
232   Returns:      >=  0 : index number if color is in color table
233                 == -1 : error no room to append color
234  *---------------------------------------------------------------------------*/
ColorTableAppend(ColorTable * t,u16 color)235 static int ColorTableAppend(ColorTable * t, u16 color)
236 {
237     int     i;
238 
239     for (i = 0; i < t->num_colors; i++)
240     {
241         if (t->color[i] == color)
242         {
243             return i;
244         }
245     }
246 
247     if (i >= t->max_colors)
248     {
249         return -1;
250     }
251 
252     t->color[i] = color;
253     t->num_colors++;
254     return i;
255 }
256 
257 
258 /*---------------------------------------------------------------------------*
259   Name:         ColorTableGetColor
260 
261   Description:  Get color from Color Table
262 
263   Arguments:    None
264 
265   Returns:      color u16
266  *---------------------------------------------------------------------------*/
ColorTableGetColor(ColorTable * t,int index)267 static u16 ColorTableGetColor(ColorTable * t, int index)
268 {
269     return index < t->num_colors ? t->color[index] : 0x0000;
270 }
271 
272 
273 /*---------------------------------------------------------------------------*
274   Name:         Rgb2Index
275 
276   Description:  Convert Rgb to index
277 
278   Arguments:    None
279 
280   Returns:
281  *---------------------------------------------------------------------------*/
Rgb2Index(int num_pixels,u8 * rgb_buffer,u8 * index_buffer,int max_colors,ColorTable * cp)282 static int Rgb2Index(int num_pixels, u8 *rgb_buffer, u8 *index_buffer, int max_colors,
283                      ColorTable * cp)
284 {
285     int     i, n;
286 
287     ColorTableInit(cp, max_colors);
288 
289     for (i = n = 0; i < num_pixels; i++, n += 3)
290     {
291         int     index;
292         u16     color;
293 
294         color = RGB5551(rgb_buffer[n], rgb_buffer[n + 1], rgb_buffer[n + 2], 1);
295         index = ColorTableAppend(cp, color);
296 
297         if (index < 0)
298         {
299             fprintf(stderr, "ppmconvbg: Error too many colors (over %d)\n", max_colors);
300             return FALSE;
301         }
302 
303         index_buffer[i] = index;
304     }
305     return TRUE;
306 }
307 
308 
309 /*---------------------------------------------------------------------------*
310   Name:         Output Byte/Half
311 
312   Description:  Output Byte
313 
314   Arguments:    fp	output file
315                 val	byte value
316 
317   Returns:      None
318  *---------------------------------------------------------------------------*/
319 static int OutputColumn = 0;
320 
OutputColumnInit(void)321 static void OutputColumnInit(void)
322 {
323     OutputColumn = 0;
324 }
325 
OutputByte(FILE * fp,int val)326 static void OutputByte(FILE * fp, int val)
327 {
328     if (OutputColumn % NUM_COLUMN_BYTE == 0)
329         fprintf(fp, "\t");
330 
331     fprintf(fp, "0x%02x, ", val);
332 
333     OutputColumn++;
334     if (OutputColumn % NUM_COLUMN_BYTE == 0)
335         fprintf(fp, "\n");
336 }
337 
OutputHalf(FILE * fp,int val)338 static void OutputHalf(FILE * fp, int val)
339 {
340     if (OutputColumn % NUM_COLUMN_HALF == 0)
341         fprintf(fp, "\t");
342 
343     fprintf(fp, "0x%04x, ", val);
344 
345     OutputColumn++;
346     if (OutputColumn % NUM_COLUMN_HALF == 0)
347         fprintf(fp, "\n");
348 }
349 
350 
351 /*---------------------------------------------------------------------------*
352   Name:         Output Color Table
353 
354   Description:  Output Color Lookup Table
355 
356   Arguments:    fp	output file
357                 label   label name
358                 cp      CLUT
359 
360   Returns:      None
361  *---------------------------------------------------------------------------*/
OutputCLUT(FILE * fp,char * label,ColorTable * cp)362 static void OutputCLUT(FILE * fp, char *label, ColorTable * cp)
363 {
364     // Round up num_colors for alignment
365     if (cp->num_colors % 2)
366     {
367         cp->num_colors++;
368     }
369 
370     // Show color table
371     if (label)
372     {
373         int     i;
374         OutputColumnInit();
375 
376         fprintf(fp, "const int Num_%s_Palette = %d;\n\n", label, cp->num_colors);
377         fprintf(fp, "const unsigned short %s_Palette[] =\n{\n", label);
378 
379         for (i = 0; i < cp->num_colors; i++)
380         {
381             OutputHalf(fp, ColorTableGetColor(cp, i));
382         }
383         fprintf(fp, "\n};\n\n");
384     }
385     else
386     {
387         // Colors
388         fwrite(cp->color, sizeof(u16), cp->num_colors, fp);
389     }
390     return;
391 }
392 
393 
394 /*---------------------------------------------------------------------------*
395   Name:         Output Index
396 
397   Description:  Output Index
398 
399   Arguments:    fp	output file
400                 label   label name
401                 cp      CLUT
402 
403   Returns:      None
404  *---------------------------------------------------------------------------*/
OutputIndex(FILE * fp,char * label,int max_colors,int width,int height,u8 * pixel)405 static void OutputIndex(FILE * fp, char *label, int max_colors, int width, int height, u8 *pixel)
406 {
407     // Show color index
408     if (label)
409     {
410         int     i, j, ii, jj, n;
411 
412         OutputColumnInit();
413 
414         fprintf(fp, "const int Num_%s_Texel = %d * %d;\n\n", label, width, height);
415         fprintf(fp, "const unsigned char %s_Texel[] =\n{\n", label);
416 
417         for (i = 0; i < height; i += 8)
418         {
419             for (j = 0; j < width; j += 8)
420             {
421                 for (ii = 0; ii < 8; ii++)
422                 {
423                     for (jj = 0; jj < 8; jj++)
424                     {
425                         n = (i + ii) * width + j + jj;
426 
427                         if (max_colors == COLOR_MAX_8bit)
428                         {
429                             OutputByte(fp, pixel[n]);
430                         }
431                         else if (n % 2 == 1)
432                         {
433                             OutputByte(fp, (pixel[n] << 4) | (pixel[n - 1] & 15));
434                         }
435                     }
436                 }
437             }
438         }
439         fprintf(fp, "\n};\n\n");
440     }
441     else
442     {
443         int     i, j, ii, jj, n;
444         u8      buffer;
445 
446         // Colors
447         for (i = 0; i < height; i += 8)
448         {
449             for (j = 0; j < width; j += 8)
450             {
451                 for (ii = 0; ii < 8; ii++)
452                 {
453                     for (jj = 0; jj < 8; jj++)
454                     {
455                         n = (i + ii) * width + j + jj;
456 
457                         if (max_colors == COLOR_MAX_8bit)
458                         {
459                             fwrite(&pixel[n], sizeof(u8), 1, fp);
460                         }
461                         else if (n % 2 == 1)
462                         {
463                             buffer = (u8)(pixel[n] << 4) | (pixel[n - 1] & 15);
464                             fwrite(&buffer, sizeof(u8), 1, fp);
465                         }
466                     }
467                 }
468             }
469         }
470     }
471     return;
472 }
473 
474 
475 /*---------------------------------------------------------------------------*
476   Name:         Convert
477 
478   Description:  Convert ppm file
479 
480   Arguments:    ppmfile		ppm file
481                 outfile		output file
482                 colormax	max of color
483                 valuelabel      labelname for C source mode if not NULL
484                                 binary mode if NULL
485 
486   Returns:
487  *---------------------------------------------------------------------------*/
Convert(char * ppmfile,char * outfile,int parts,int colormax,char * valuelabel)488 int Convert(char *ppmfile, char *outfile, int parts, int colormax, char *valuelabel)
489 {
490     int     result;
491     FILE   *fp = NULL;
492     u8     *rgb_buffer = NULL;
493     u8     *index_buffer = NULL;
494 
495     int     width;
496     int     height;
497     int     depth;
498     int     num_pixels;
499 
500     ColorTable clut = { 0 };
501 
502     //
503     // Read PPM file into rgb_buffer
504     //
505     fp = fopen(ppmfile, "rb");
506     if (!fp)
507         goto error;
508 
509     result = ReadHeader(fp, &width, &height, &depth);
510     if (!result || depth != 255)
511         goto error;
512 
513     num_pixels = width * height;
514 
515     rgb_buffer = (u8 *)malloc(num_pixels * 3);
516     if (!rgb_buffer)
517         goto error;
518 
519     result = ReadBody(fp, rgb_buffer, num_pixels * 3);
520     if (!result)
521         goto error;
522 
523     fclose(fp);
524     fp = NULL;
525 
526     //
527     // Convert to index_buffer & clut
528     //
529     index_buffer = (u8 *)malloc(num_pixels * 1);
530     if (!index_buffer)
531         goto error;
532 
533     result = Rgb2Index(num_pixels, rgb_buffer, index_buffer, colormax, &clut);
534     if (!result)
535         goto error;
536 
537     free(rgb_buffer);
538     rgb_buffer = NULL;
539 
540     //
541     // Write clut & index
542     //
543     fp = fopen(outfile, valuelabel ? "w" : "wb");
544     if (!fp)
545         goto error;
546 
547     if (parts & PARTS_CLUT)
548     {
549         OutputCLUT(fp, valuelabel, &clut);
550     }
551     if (parts & PARTS_INDEX)
552     {
553         OutputIndex(fp, valuelabel, colormax, width, height, index_buffer);
554     }
555     fclose(fp);
556     free(index_buffer);
557     free(clut.color);
558     return TRUE;
559 
560   error:
561     if (rgb_buffer)
562         free(rgb_buffer);
563     if (clut.color)
564         free(clut.color);
565     if (index_buffer)
566         free(index_buffer);
567     if (fp)
568         fclose(fp);
569 
570     fprintf(stderr, "Cannot convert file \"%s\".\n", ppmfile);
571     return FALSE;
572 }
573 
574 
575 /*---------------------------------------------------------------------------*
576   Name:         main
577 
578   Description:  output 256 color-ed ppm image bitmap as the format
579                 like C source code
580  *---------------------------------------------------------------------------*/
main(int argc,char * argv[])581 int main(int argc, char *argv[])
582 {
583     int     n;
584     int     ColorMax = COLOR_MAX_8bit;
585     char   *ValueLabel = NULL;
586     int     Parts = PARTS_ALL;
587     int     result;
588 
589     while ((n = getopt(argc, argv, "ba:c:pihvd")) != -1)
590     {
591         switch (n)
592         {
593         case 'b':
594             if (ValueLabel)
595                 free(ValueLabel);
596             ValueLabel = NULL;         // BinaryMode
597             break;
598 
599         case 'a':
600             if (ValueLabel)
601                 free(ValueLabel);
602             ValueLabel = strdup(optarg);        // Source Mode
603             break;
604 
605         case 'c':
606             if (!strcmp(optarg, "8"))
607                 ColorMax = COLOR_MAX_8bit;
608             else if (!strcmp(optarg, "4"))
609                 ColorMax = COLOR_MAX_4bit;
610             else
611                 usage("-c : must be followed by 8 or 4", 0);    // Never returns
612             break;
613 
614         case 'd':
615             DebugMode = TRUE;
616             break;
617 
618         case 'p':
619             Parts = (Parts == PARTS_INDEX) ? PARTS_ALL : PARTS_CLUT;
620             break;
621 
622         case 'i':
623             Parts = (Parts == PARTS_CLUT) ? PARTS_ALL : PARTS_INDEX;
624             break;
625 
626         case 'h':
627         case 'v':
628             if (ValueLabel)
629                 free(ValueLabel);
630             usage(NULL, 0);            // Never returns
631             break;
632 
633         default:
634             if (ValueLabel)
635                 free(ValueLabel);
636             usage("-%c : Unknown option", n);   // Never returns
637             break;
638         }
639 
640         if (DebugMode)
641         {
642             fprintf(stderr, "option -%c: %s\n", n, optarg ? optarg : "No ARG");
643         }
644     }
645 
646     if (DebugMode)
647     {
648         int     i;
649 
650         fprintf(stderr, "argc=%d  optind=%d\n", argc, optind);
651         for (i = optind; i < argc; i++)
652         {
653             fprintf(stderr, "argv[%d] = [%s]\n", i, argv[i]);
654         }
655     }
656 
657     argc -= optind;
658     argv += optind;
659 
660     if (argc != 2)
661     {
662         if (ValueLabel)
663             free(ValueLabel);
664         usage("need 2 arguments", 0);
665         // Never returns
666     }
667 
668     result = Convert(argv[0], argv[1], Parts, ColorMax, ValueLabel);
669 
670     if (ValueLabel)
671         free(ValueLabel);
672 
673     return result ? 0 : 1;
674 }
675