xref: /linux/drivers/video/logo/pnmtologo.c (revision 044b14b51d78a549664efac3a5a4c9edd1211b79)
1 /*
2  *  Convert a logo in ASCII PNM format to C source suitable for inclusion in
3  *  the Linux kernel
4  *
5  *  (C) Copyright 2001-2003 by Geert Uytterhoeven <geert@linux-m68k.org>
6  *
7  *  --------------------------------------------------------------------------
8  *
9  *  This file is subject to the terms and conditions of the GNU General Public
10  *  License. See the file COPYING in the main directory of the Linux
11  *  distribution for more details.
12  */
13 
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 
23 static const char *programname;
24 static const char *filename;
25 static const char *logoname = "linux_logo";
26 static const char *outputname;
27 static FILE *out;
28 
29 
30 #define LINUX_LOGO_MONO		1	/* monochrome black/white */
31 #define LINUX_LOGO_VGA16	2	/* 16 colors VGA text palette */
32 #define LINUX_LOGO_CLUT224	3	/* 224 colors */
33 #define LINUX_LOGO_GRAY256	4	/* 256 levels grayscale */
34 
35 static const char *logo_types[LINUX_LOGO_GRAY256+1] = {
36 	[LINUX_LOGO_MONO] = "LINUX_LOGO_MONO",
37 	[LINUX_LOGO_VGA16] = "LINUX_LOGO_VGA16",
38 	[LINUX_LOGO_CLUT224] = "LINUX_LOGO_CLUT224",
39 	[LINUX_LOGO_GRAY256] = "LINUX_LOGO_GRAY256"
40 };
41 
42 #define MAX_LINUX_LOGO_COLORS	224
43 
44 struct color {
45 	unsigned char red;
46 	unsigned char green;
47 	unsigned char blue;
48 };
49 
50 static const struct color clut_vga16[16] = {
51 	{ 0x00, 0x00, 0x00 },
52 	{ 0x00, 0x00, 0xaa },
53 	{ 0x00, 0xaa, 0x00 },
54 	{ 0x00, 0xaa, 0xaa },
55 	{ 0xaa, 0x00, 0x00 },
56 	{ 0xaa, 0x00, 0xaa },
57 	{ 0xaa, 0x55, 0x00 },
58 	{ 0xaa, 0xaa, 0xaa },
59 	{ 0x55, 0x55, 0x55 },
60 	{ 0x55, 0x55, 0xff },
61 	{ 0x55, 0xff, 0x55 },
62 	{ 0x55, 0xff, 0xff },
63 	{ 0xff, 0x55, 0x55 },
64 	{ 0xff, 0x55, 0xff },
65 	{ 0xff, 0xff, 0x55 },
66 	{ 0xff, 0xff, 0xff },
67 };
68 
69 
70 static int logo_type = LINUX_LOGO_CLUT224;
71 static unsigned int logo_width;
72 static unsigned int logo_height;
73 static struct color **logo_data;
74 static struct color logo_clut[MAX_LINUX_LOGO_COLORS];
75 static unsigned int logo_clutsize;
76 static int is_plain_pbm = 0;
77 
78 static void die(const char *fmt, ...)
79 __attribute__((noreturn)) __attribute((format (printf, 1, 2)));
80 static void usage(void) __attribute((noreturn));
81 
82 
83 static unsigned int get_number(FILE *fp)
84 {
85 	int c, val;
86 
87 	/* Skip leading whitespace */
88 	do {
89 		c = fgetc(fp);
90 		if (c == EOF)
91 			die("%s: end of file\n", filename);
92 		if (c == '#') {
93 			/* Ignore comments 'till end of line */
94 			do {
95 				c = fgetc(fp);
96 				if (c == EOF)
97 					die("%s: end of file\n", filename);
98 			} while (c != '\n');
99 		}
100 	} while (isspace(c));
101 
102 	/* Parse decimal number */
103 	val = 0;
104 	while (isdigit(c)) {
105 		val = 10*val+c-'0';
106 		/* some PBM are 'broken'; GiMP for example exports a PBM without space
107 		 * between the digits. This is Ok cause we know a PBM can only have a '1'
108 		 * or a '0' for the digit.
109 		 */
110 		if (is_plain_pbm)
111 			break;
112 		c = fgetc(fp);
113 		if (c == EOF)
114 			die("%s: end of file\n", filename);
115 	}
116 	return val;
117 }
118 
119 static unsigned int get_number255(FILE *fp, unsigned int maxval)
120 {
121 	unsigned int val = get_number(fp);
122 
123 	return (255*val+maxval/2)/maxval;
124 }
125 
126 static void read_image(void)
127 {
128 	FILE *fp;
129 	unsigned int i, j;
130 	int magic;
131 	unsigned int maxval;
132 
133 	/* open image file */
134 	fp = fopen(filename, "r");
135 	if (!fp)
136 		die("Cannot open file %s: %s\n", filename, strerror(errno));
137 
138 	/* check file type and read file header */
139 	magic = fgetc(fp);
140 	if (magic != 'P')
141 		die("%s is not a PNM file\n", filename);
142 	magic = fgetc(fp);
143 	switch (magic) {
144 	case '1':
145 	case '2':
146 	case '3':
147 		/* Plain PBM/PGM/PPM */
148 		break;
149 
150 	case '4':
151 	case '5':
152 	case '6':
153 		/* Binary PBM/PGM/PPM */
154 		die("%s: Binary PNM is not supported\n"
155 		"Use pnmnoraw(1) to convert it to ASCII PNM\n", filename);
156 
157 	default:
158 		die("%s is not a PNM file\n", filename);
159 	}
160 	logo_width = get_number(fp);
161 	logo_height = get_number(fp);
162 
163 	/* allocate image data */
164 	logo_data = (struct color **)malloc(logo_height*sizeof(struct color *));
165 	if (!logo_data)
166 		die("%s\n", strerror(errno));
167 	for (i = 0; i < logo_height; i++) {
168 		logo_data[i] = malloc(logo_width*sizeof(struct color));
169 	if (!logo_data[i])
170 		die("%s\n", strerror(errno));
171 	}
172 
173 	/* read image data */
174 	switch (magic) {
175 	case '1':
176 		/* Plain PBM */
177 		is_plain_pbm = 1;
178 		for (i = 0; i < logo_height; i++)
179 			for (j = 0; j < logo_width; j++)
180 				logo_data[i][j].red = logo_data[i][j].green =
181 					logo_data[i][j].blue = 255*(1-get_number(fp));
182 		break;
183 
184 	case '2':
185 		/* Plain PGM */
186 		maxval = get_number(fp);
187 		for (i = 0; i < logo_height; i++)
188 			for (j = 0; j < logo_width; j++)
189 				logo_data[i][j].red = logo_data[i][j].green =
190 					logo_data[i][j].blue = get_number255(fp, maxval);
191 		break;
192 
193 	case '3':
194 		/* Plain PPM */
195 		maxval = get_number(fp);
196 		for (i = 0; i < logo_height; i++)
197 			for (j = 0; j < logo_width; j++) {
198 				logo_data[i][j].red = get_number255(fp, maxval);
199 				logo_data[i][j].green = get_number255(fp, maxval);
200 				logo_data[i][j].blue = get_number255(fp, maxval);
201 			}
202 		break;
203 	}
204 
205 	/* close file */
206 	fclose(fp);
207 }
208 
209 static inline int is_black(struct color c)
210 {
211 	return c.red == 0 && c.green == 0 && c.blue == 0;
212 }
213 
214 static inline int is_white(struct color c)
215 {
216 	return c.red == 255 && c.green == 255 && c.blue == 255;
217 }
218 
219 static inline int is_gray(struct color c)
220 {
221 	return c.red == c.green && c.red == c.blue;
222 }
223 
224 static inline int is_equal(struct color c1, struct color c2)
225 {
226 	return c1.red == c2.red && c1.green == c2.green && c1.blue == c2.blue;
227 }
228 
229 static void write_header(void)
230 {
231 	/* open logo file */
232 	if (outputname) {
233 		out = fopen(outputname, "w");
234 		if (!out)
235 			die("Cannot create file %s: %s\n", outputname, strerror(errno));
236 	} else {
237 		out = stdout;
238 	}
239 
240 	fputs("/*\n", out);
241 	fputs(" *  DO NOT EDIT THIS FILE!\n", out);
242 	fputs(" *\n", out);
243 	fprintf(out, " *  It was automatically generated from %s\n", filename);
244 	fputs(" *\n", out);
245 	fprintf(out, " *  Linux logo %s\n", logoname);
246 	fputs(" */\n\n", out);
247 	fputs("#include <linux/linux_logo.h>\n\n", out);
248 	fprintf(out, "static unsigned char %s_data[] __initdata = {\n",
249 		logoname);
250 }
251 
252 static void write_footer(void)
253 {
254 	fputs("\n};\n\n", out);
255 	fprintf(out, "const struct linux_logo %s __initconst = {\n", logoname);
256 	fprintf(out, "\t.type\t\t= %s,\n", logo_types[logo_type]);
257 	fprintf(out, "\t.width\t\t= %d,\n", logo_width);
258 	fprintf(out, "\t.height\t\t= %d,\n", logo_height);
259 	if (logo_type == LINUX_LOGO_CLUT224) {
260 		fprintf(out, "\t.clutsize\t= %d,\n", logo_clutsize);
261 		fprintf(out, "\t.clut\t\t= %s_clut,\n", logoname);
262 	}
263 	fprintf(out, "\t.data\t\t= %s_data\n", logoname);
264 	fputs("};\n\n", out);
265 
266 	/* close logo file */
267 	if (outputname)
268 		fclose(out);
269 }
270 
271 static int write_hex_cnt;
272 
273 static void write_hex(unsigned char byte)
274 {
275 	if (write_hex_cnt % 12)
276 		fprintf(out, ", 0x%02x", byte);
277 	else if (write_hex_cnt)
278 		fprintf(out, ",\n\t0x%02x", byte);
279 	else
280 		fprintf(out, "\t0x%02x", byte);
281 	write_hex_cnt++;
282 }
283 
284 static void write_logo_mono(void)
285 {
286 	unsigned int i, j;
287 	unsigned char val, bit;
288 
289 	/* validate image */
290 	for (i = 0; i < logo_height; i++)
291 		for (j = 0; j < logo_width; j++)
292 			if (!is_black(logo_data[i][j]) && !is_white(logo_data[i][j]))
293 				die("Image must be monochrome\n");
294 
295 	/* write file header */
296 	write_header();
297 
298 	/* write logo data */
299 	for (i = 0; i < logo_height; i++) {
300 		for (j = 0; j < logo_width;) {
301 			for (val = 0, bit = 0x80; bit && j < logo_width; j++, bit >>= 1)
302 				if (logo_data[i][j].red)
303 					val |= bit;
304 			write_hex(val);
305 		}
306 	}
307 
308 	/* write logo structure and file footer */
309 	write_footer();
310 }
311 
312 static void write_logo_vga16(void)
313 {
314 	unsigned int i, j, k;
315 	unsigned char val;
316 
317 	/* validate image */
318 	for (i = 0; i < logo_height; i++)
319 		for (j = 0; j < logo_width; j++) {
320 			for (k = 0; k < 16; k++)
321 				if (is_equal(logo_data[i][j], clut_vga16[k]))
322 					break;
323 			if (k == 16)
324 				die("Image must use the 16 console colors only\n"
325 				    "Use ppmquant(1) -map clut_vga16.ppm to reduce the number "
326 				    "of colors\n");
327 		}
328 
329 	/* write file header */
330 	write_header();
331 
332 	/* write logo data */
333 	for (i = 0; i < logo_height; i++)
334 		for (j = 0; j < logo_width; j++) {
335 			for (k = 0; k < 16; k++)
336 				if (is_equal(logo_data[i][j], clut_vga16[k]))
337 					break;
338 			val = k<<4;
339 			if (++j < logo_width) {
340 				for (k = 0; k < 16; k++)
341 					if (is_equal(logo_data[i][j], clut_vga16[k]))
342 						break;
343 				val |= k;
344 			}
345 			write_hex(val);
346 		}
347 
348 	/* write logo structure and file footer */
349 	write_footer();
350 }
351 
352 static void write_logo_clut224(void)
353 {
354 	unsigned int i, j, k;
355 
356 	/* validate image */
357 	for (i = 0; i < logo_height; i++)
358 		for (j = 0; j < logo_width; j++) {
359 			for (k = 0; k < logo_clutsize; k++)
360 				if (is_equal(logo_data[i][j], logo_clut[k]))
361 					break;
362 			if (k == logo_clutsize) {
363 				if (logo_clutsize == MAX_LINUX_LOGO_COLORS)
364 					die("Image has more than %d colors\n"
365 					    "Use ppmquant(1) to reduce the number of colors\n",
366 					    MAX_LINUX_LOGO_COLORS);
367 				logo_clut[logo_clutsize++] = logo_data[i][j];
368 			}
369 		}
370 
371 	/* write file header */
372 	write_header();
373 
374 	/* write logo data */
375 	for (i = 0; i < logo_height; i++)
376 		for (j = 0; j < logo_width; j++) {
377 			for (k = 0; k < logo_clutsize; k++)
378 				if (is_equal(logo_data[i][j], logo_clut[k]))
379 					break;
380 			write_hex(k+32);
381 		}
382 	fputs("\n};\n\n", out);
383 
384 	/* write logo clut */
385 	fprintf(out, "static unsigned char %s_clut[] __initdata = {\n",
386 		logoname);
387 	write_hex_cnt = 0;
388 	for (i = 0; i < logo_clutsize; i++) {
389 		write_hex(logo_clut[i].red);
390 		write_hex(logo_clut[i].green);
391 		write_hex(logo_clut[i].blue);
392 	}
393 
394 	/* write logo structure and file footer */
395 	write_footer();
396 }
397 
398 static void write_logo_gray256(void)
399 {
400 	unsigned int i, j;
401 
402 	/* validate image */
403 	for (i = 0; i < logo_height; i++)
404 		for (j = 0; j < logo_width; j++)
405 			if (!is_gray(logo_data[i][j]))
406 				die("Image must be grayscale\n");
407 
408 	/* write file header */
409 	write_header();
410 
411 	/* write logo data */
412 	for (i = 0; i < logo_height; i++)
413 		for (j = 0; j < logo_width; j++)
414 			write_hex(logo_data[i][j].red);
415 
416 	/* write logo structure and file footer */
417 	write_footer();
418 }
419 
420 static void die(const char *fmt, ...)
421 {
422 	va_list ap;
423 
424 	va_start(ap, fmt);
425 	vfprintf(stderr, fmt, ap);
426 	va_end(ap);
427 
428 	exit(1);
429 }
430 
431 static void usage(void)
432 {
433 	die("\n"
434 	"Usage: %s [options] <filename>\n"
435 	"\n"
436 	"Valid options:\n"
437 	"	-h		  : display this usage information\n"
438 	"	-n <name>   : specify logo name (default: linux_logo)\n"
439 	"	-o <output> : output to file <output> instead of stdout\n"
440 	"	-t <type>   : specify logo type, one of\n"
441 	"					  mono	: monochrome black/white\n"
442 	"					  vga16   : 16 colors VGA text palette\n"
443 	"					  clut224 : 224 colors (default)\n"
444 	"					  gray256 : 256 levels grayscale\n"
445 	"\n", programname);
446 }
447 
448 int main(int argc, char *argv[])
449 {
450 	int opt;
451 
452 	programname = argv[0];
453 
454 	opterr = 0;
455 	while (1) {
456 		opt = getopt(argc, argv, "hn:o:t:");
457 		if (opt == -1)
458 			break;
459 
460 		switch (opt) {
461 		case 'h':
462 			usage();
463 			break;
464 
465 		case 'n':
466 			logoname = optarg;
467 			break;
468 
469 		case 'o':
470 			outputname = optarg;
471 			break;
472 
473 		case 't':
474 			if (!strcmp(optarg, "mono"))
475 				logo_type = LINUX_LOGO_MONO;
476 			else if (!strcmp(optarg, "vga16"))
477 				logo_type = LINUX_LOGO_VGA16;
478 			else if (!strcmp(optarg, "clut224"))
479 				logo_type = LINUX_LOGO_CLUT224;
480 			else if (!strcmp(optarg, "gray256"))
481 				logo_type = LINUX_LOGO_GRAY256;
482 			else
483 				usage();
484 			break;
485 
486 		default:
487 			usage();
488 			break;
489 		}
490 	}
491 	if (optind != argc-1)
492 		usage();
493 
494 	filename = argv[optind];
495 
496 	read_image();
497 	switch (logo_type) {
498 	case LINUX_LOGO_MONO:
499 		write_logo_mono();
500 		break;
501 
502 	case LINUX_LOGO_VGA16:
503 		write_logo_vga16();
504 		break;
505 
506 	case LINUX_LOGO_CLUT224:
507 		write_logo_clut224();
508 		break;
509 
510 	case LINUX_LOGO_GRAY256:
511 		write_logo_gray256();
512 		break;
513 	}
514 	exit(0);
515 }
516