xref: /haiku/src/tools/generate_boot_screen.cpp (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
1 /*
2  *	Copyright (c) 2008, Haiku, Inc. All rights reserved.
3  *	Distributed under the terms of the MIT license.
4  *
5  *	Authors:
6  *		Artur Wyszynski <harakash@gmail.com>
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Philippe Saint-Pierre <stpere@gmail.com>
9  */
10 
11 //! Haiku boot splash image generator/converter
12 
13 #include <iostream>
14 #include <png.h>
15 #include <string>
16 #include <stdarg.h>
17 #include <stdint.h>
18 
19 // TODO: Generate the single optimal palette for all three images,
20 // store palette versions of these images as well, so that they are
21 // ready to be used during boot in case we need to run in palette
22 // VGA mode.
23 
24 
25 FILE* sOutput = NULL;
26 int sOffset = 0;
27 
28 static void
29 error(const char *s, ...)
30 {
31 	va_list args;
32 	va_start(args, s);
33 	vfprintf(stderr, s, args);
34 	fprintf(stderr, "\n");
35 	va_end(args);
36 	exit(-1);
37 }
38 
39 
40 class AutoFileCloser {
41 public:
42 	AutoFileCloser(FILE* file)
43 		: fFile(file)
44 	{}
45 	~AutoFileCloser()
46 	{
47 		fclose(fFile);
48 	}
49 private:
50 	FILE* fFile;
51 };
52 
53 
54 static void
55 read_png(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
56 	png_structp& pngPtr, png_infop& infoPtr)
57 {
58 	char header[8];
59 	FILE* input = fopen(filename, "rb");
60 	if (!input)
61 		error("[read_png] File %s could not be opened for reading", filename);
62 
63 	AutoFileCloser _(input);
64 
65 	fread(header, 1, 8, input);
66 	if (png_sig_cmp((png_byte *)header, 0, 8 ))
67 		error("[read_png] File %s is not recognized as a PNG file", filename);
68 
69 	pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
70 		NULL, NULL, NULL);
71 	if (!pngPtr)
72 		error("[read_png] png_create_read_struct failed");
73 
74 	infoPtr = png_create_info_struct(pngPtr);
75 	if (!infoPtr)
76 		error("[read_png] png_create_info_struct failed");
77 
78 // TODO: I don't know which version of libpng introduced this feature:
79 #if PNG_LIBPNG_VER > 10005
80 	if (setjmp(png_jmpbuf(pngPtr)))
81 		error("[read_png] Error during init_io");
82 #endif
83 
84 	png_init_io(pngPtr, input);
85 	png_set_sig_bytes(pngPtr, 8);
86 
87 	// make sure we automatically get RGB data with 8 bits per channel
88 	// also make sure the alpha channel is stripped, in the end, we
89 	// expect 24 bits BGR data
90 	png_set_expand(pngPtr);
91 	png_set_gray_1_2_4_to_8(pngPtr);
92 	png_set_palette_to_rgb(pngPtr);
93 	png_set_gray_to_rgb(pngPtr);
94 	png_set_strip_alpha(pngPtr);
95 	png_set_bgr(pngPtr);
96 
97 	png_read_info(pngPtr, infoPtr);
98 	width = infoPtr->width;
99 	height = infoPtr->height;
100 	if (infoPtr->bit_depth != 8)
101 		error("[read_png] File %s has wrong bit depth\n", filename);
102 	if ((int)infoPtr->rowbytes < width * 3) {
103 		error("[read_png] File %s has wrong color type (RGB required)\n",
104 			filename);
105 	}
106 
107 
108 	png_set_interlace_handling(pngPtr);
109 	png_read_update_info(pngPtr, infoPtr);
110 
111 #if PNG_LIBPNG_VER > 10005
112 	if (setjmp(png_jmpbuf(pngPtr)))
113 		error("[read_png] Error during read_image");
114 #endif
115 
116 	rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height);
117 	for (int y = 0; y < height; y++)
118 		rowPtrs[y] = (png_byte*)malloc(infoPtr->rowbytes);
119 
120 	png_read_image(pngPtr, rowPtrs);
121 }
122 
123 
124 static void
125 new_line_if_required()
126 {
127 	sOffset++;
128 	if (sOffset % 12 == 0)
129 		fprintf(sOutput, "\n\t");
130 }
131 
132 
133 static void
134 write_image(const char* baseName, int width, int height, png_bytep* rowPtrs)
135 {
136 	fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width);
137 	fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height);
138 	fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n");
139 
140 	int buffer[128];
141 	// buffer[0] stores count, buffer[1..127] holds the actual values
142 
143 	fprintf(sOutput, "static uint8 %sCompressedImage[] = {\n\t",
144 		baseName);
145 
146 	for (int c = 0; c < 3; c++) {
147 		// for each component i.e. R, G, B ...
148 		// NOTE : I don't care much about performance at this step,
149 		// decoding however...
150 		int currentValue = rowPtrs[0][c];
151 		int count = 0;
152 
153 		// When bufferActive == true, we store the number rather than writing
154 		// them directly; we use this to store numbers until we find a pair..
155 		bool bufferActive = false;
156 
157 		sOffset = 0;
158 
159 		for (int y = 0; y < height; y++) {
160 			png_byte* row = rowPtrs[y];
161 			for (int x = c; x < width * 3; x += 3) {
162 				if (row[x] == currentValue) {
163 					if (bufferActive) {
164 						bufferActive = false;
165 						count = 2;
166 						if (buffer[0] > 1) {
167 							fprintf (sOutput, "%d, ",
168 								128 + buffer[0] - 1);
169 							new_line_if_required();
170 							for (int i = 1; i < buffer[0] ; i++) {
171 								fprintf( sOutput, "%d, ",
172 									buffer[i] );
173 								new_line_if_required();
174 							}
175 						}
176 					} else {
177 						count++;
178 						if (count == 127) {
179 							fprintf(sOutput, "127, ");
180 							new_line_if_required();
181 							fprintf(sOutput, "%d, ", currentValue);
182 							new_line_if_required();
183 							count = 0;
184 						}
185 					}
186 				} else {
187 					if (bufferActive) {
188 						if (buffer[0] == 127) {
189 							// we don't have enough room,
190 							// flush the buffer
191 							fprintf(sOutput, "%d, ",
192 								128 + buffer[0] - 1);
193 							new_line_if_required();
194 							for (int i = 1; i < buffer[0]; i++) {
195 								fprintf(sOutput, "%d, ", buffer[i]);
196 								new_line_if_required();
197 							}
198 							buffer[0] = 0;
199 						}
200 						buffer[0]++;
201 						buffer[buffer[0]] = row[x];
202 					} else if (count > 0) {
203 						buffer[0] = 1;
204 						buffer[1] = row[x];
205 						bufferActive = true;
206 						if (count > 1) {
207 							fprintf(sOutput, "%d, ", count);
208 							new_line_if_required();
209 							fprintf(sOutput, "%d, ", currentValue);
210 							new_line_if_required();
211 						}
212 					}
213 					currentValue = row[x];
214 				}
215 			}
216 		}
217 		if (bufferActive) {
218 			// I could have written 127 + buffer[0],
219 			// but I think this is more readable...
220 			fprintf(sOutput, "%d, ", 128 + buffer[0] - 1);
221 			new_line_if_required();
222 			for (int i = 1; i < buffer[0] ; i++) {
223 				fprintf(sOutput, "%d, ", buffer[i]);
224 				new_line_if_required();
225 			}
226 		} else {
227 			fprintf(sOutput, "%d, %d, ", count, currentValue);
228 			new_line_if_required();
229 		}
230 		// we put a terminating zero for the next byte that indicates
231 		// a "count", just to indicate the end of the channel
232 		fprintf(sOutput, "0");
233 		if (c != 2)
234 			fprintf(sOutput, ",");
235 
236 		fprintf(sOutput, "\n\t");
237 	}
238 	fprintf(sOutput, "};\n");
239 	fprintf(sOutput, "#endif\n\n");
240 }
241 
242 
243 static void
244 parse_image(const char* filename, const char* baseName)
245 {
246 	int width;
247 	int height;
248 	png_bytep* rowPtrs = NULL;
249 	png_structp pngPtr;
250 	png_infop infoPtr;
251 	read_png(filename, width, height, rowPtrs, pngPtr, infoPtr);
252 	write_image(baseName, width, height, rowPtrs);
253 
254 	// free resources
255 	png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
256 	for (int y = 0; y < height; y++)
257 		free(rowPtrs[y]);
258 	free(rowPtrs);
259 }
260 
261 
262 int
263 main(int argc, char* argv[])
264 {
265 	if (argc < 8) {
266 		printf("Usage:\n");
267 		printf("\t%s <splash.png> <x placement in %%> <y placement in %%> "
268 			"<icons.png> <x placement in %%> <y placement in %%> <images.h>\n",
269 			argv[0]);
270 		return 0;
271 	}
272 
273 	int logoPlacementX = atoi(argv[2]);
274 	int logoPlacementY = atoi(argv[3]);
275 	int iconPlacementX = atoi(argv[5]);
276 	int iconPlacementY = atoi(argv[6]);
277 	if (logoPlacementX < 0 || logoPlacementX > 100) {
278 		printf("Bad X placement for logo: %d%%\n", logoPlacementX);
279 		return 1;
280 	}
281 	if (logoPlacementY < 0 || logoPlacementY > 100) {
282 		printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY);
283 		return 1;
284 	}
285 	if (iconPlacementX < 0 || iconPlacementX > 100) {
286 		printf("Bad X placement for icons: %d%%\n", iconPlacementX);
287 		return 1;
288 	}
289 	if (iconPlacementY < 0 || iconPlacementY > 100) {
290 		printf("Bad Y placement for icons: %d%%\n", iconPlacementY);
291 		return 1;
292 	}
293 
294 	const char* headerFileName = argv[7];
295 	sOutput = fopen(headerFileName, "wb");
296 	if (!sOutput)
297 		error("Could not open file \"%s\" for writing", headerFileName);
298 
299 	fputs("// This file was generated by the generate_boot_screen Haiku build "
300 		"tool.\n\n", sOutput);
301 
302 	fprintf(sOutput, "static const int32 kSplashLogoPlacementX = %d;\n",
303 		logoPlacementX);
304 	fprintf(sOutput, "static const int32 kSplashLogoPlacementY = %d;\n",
305 		logoPlacementY);
306 	fprintf(sOutput, "static const int32 kSplashIconsPlacementX = %d;\n",
307 		iconPlacementX);
308 	fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n",
309 		iconPlacementY);
310 
311 	parse_image(argv[1], "kSplashLogo");
312 	parse_image(argv[4], "kSplashIcons");
313 
314 	fclose(sOutput);
315 	return 0;
316 }
317 
318