1 /*
2 * Copyright (c) 2008-2011, 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 * David Powell <david@mad.scientist.com>
10 * Philippe Houdoin
11 */
12
13 //! Haiku boot splash image generator/converter
14
15 #include <iostream>
16 #include <png.h>
17 #include <string>
18 #include <stdarg.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21
22 #include <zlib.h>
23
24 #include <AutoDeleterPosix.h>
25 #include <ColorQuantizer.h>
26
27 // TODO: Create 4 bit palette version of these
28 // images as well, so that they are ready to be
29 // used during boot in case we need to run in
30 // palette 4 bit VGA mode.
31
32
33 FILE* sOutput = NULL;
34 int sOffset = 0;
35
36 static void
error(const char * s,...)37 error(const char *s, ...)
38 {
39 va_list args;
40 va_start(args, s);
41 vfprintf(stderr, s, args);
42 fprintf(stderr, "\n");
43 va_end(args);
44 exit(-1);
45 }
46
47
48 static void
new_line_if_required()49 new_line_if_required()
50 {
51 sOffset++;
52 if (sOffset % 12 == 0)
53 fprintf(sOutput, "\n\t");
54 }
55
56
57 // #pragma mark -
58
59
60 class ZlibCompressor {
61 public:
62 ZlibCompressor(FILE* output);
63
64 int Compress(const void* data, int dataSize, int flush = Z_NO_FLUSH);
65 int Finish();
66
67 private:
68 FILE* fOutput;
69 z_stream fStream;
70 };
71
72
ZlibCompressor(FILE * output)73 ZlibCompressor::ZlibCompressor(FILE* output)
74 : fOutput(output)
75 {
76 // prepare zlib stream
77
78 fStream.next_in = NULL;
79 fStream.avail_in = 0;
80 fStream.total_in = 0;
81 fStream.next_out = NULL;
82 fStream.avail_out = 0;
83 fStream.total_out = 0;
84 fStream.msg = 0;
85 fStream.state = 0;
86 fStream.zalloc = Z_NULL;
87 fStream.zfree = Z_NULL;
88 fStream.opaque = Z_NULL;
89 fStream.data_type = 0;
90 fStream.adler = 0;
91 fStream.reserved = 0;
92
93 int zlibError = deflateInit(&fStream, Z_BEST_COMPRESSION);
94 if (zlibError != Z_OK)
95 return; // TODO: translate zlibError
96 }
97
98
99 int
Compress(const void * data,int dataSize,int flush)100 ZlibCompressor::Compress(const void* data, int dataSize, int flush)
101 {
102 uint8 buffer[1024];
103
104 fStream.next_in = (Bytef*)data;
105 fStream.avail_in = dataSize;
106
107 int zlibError = Z_OK;
108
109 do {
110 fStream.next_out = (Bytef*)buffer;
111 fStream.avail_out = sizeof(buffer);
112
113 zlibError = deflate(&fStream, flush);
114 if (zlibError != Z_OK &&
115 (flush == Z_FINISH && zlibError != Z_STREAM_END))
116 return zlibError;
117
118 unsigned int outputSize = sizeof(buffer) - fStream.avail_out;
119 for (unsigned int i = 0; i < outputSize; i++) {
120 fprintf(fOutput, "%d, ", buffer[i]);
121 new_line_if_required();
122 }
123
124 if (zlibError == Z_STREAM_END)
125 break;
126
127 } while (fStream.avail_in > 0 || flush == Z_FINISH);
128
129 return zlibError;
130 }
131
132
133 int
Finish()134 ZlibCompressor::Finish()
135 {
136 Compress(NULL, 0, Z_FINISH);
137 return deflateEnd(&fStream);
138 }
139
140
141 // #pragma mark -
142
143
144 static void
read_png(const char * filename,int & width,int & height,png_bytep * & rowPtrs,png_structp & pngPtr,png_infop & infoPtr)145 read_png(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
146 png_structp& pngPtr, png_infop& infoPtr)
147 {
148 char header[8];
149 FileCloser input(fopen(filename, "rb"));
150 if (!input.IsSet())
151 error("[read_png] File %s could not be opened for reading", filename);
152
153 fread(header, 1, 8, input.Get());
154 if (png_sig_cmp((png_byte *)header, 0, 8 ))
155 error("[read_png] File %s is not recognized as a PNG file", filename);
156
157 pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
158 NULL, NULL, NULL);
159 if (!pngPtr)
160 error("[read_png] png_create_read_struct failed");
161
162 infoPtr = png_create_info_struct(pngPtr);
163 if (!infoPtr)
164 error("[read_png] png_create_info_struct failed");
165
166 // TODO: I don't know which version of libpng introduced this feature:
167 #if PNG_LIBPNG_VER > 10005
168 if (setjmp(png_jmpbuf(pngPtr)))
169 error("[read_png] Error during init_io");
170 #endif
171
172 png_init_io(pngPtr, input.Get());
173 png_set_sig_bytes(pngPtr, 8);
174
175 // make sure we automatically get RGB data with 8 bits per channel
176 // also make sure the alpha channel is stripped, in the end, we
177 // expect 24 bits BGR data
178 png_set_expand(pngPtr);
179 png_set_expand_gray_1_2_4_to_8(pngPtr);
180 png_set_palette_to_rgb(pngPtr);
181 png_set_gray_to_rgb(pngPtr);
182 png_set_strip_alpha(pngPtr);
183 png_set_bgr(pngPtr);
184
185 png_read_info(pngPtr, infoPtr);
186 width = (int)png_get_image_width(pngPtr, infoPtr);
187 height = (int)png_get_image_height(pngPtr, infoPtr);
188 if (png_get_bit_depth(pngPtr, infoPtr) != 8)
189 error("[read_png] File %s has wrong bit depth\n", filename);
190 if ((int)png_get_rowbytes(pngPtr, infoPtr) < width * 3) {
191 error("[read_png] File %s has wrong color type (RGB required)\n",
192 filename);
193 }
194
195
196 png_set_interlace_handling(pngPtr);
197 png_read_update_info(pngPtr, infoPtr);
198
199 #if PNG_LIBPNG_VER > 10005
200 if (setjmp(png_jmpbuf(pngPtr)))
201 error("[read_png] Error during read_image");
202 #endif
203
204 rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height);
205 for (int y = 0; y < height; y++)
206 rowPtrs[y] = (png_byte*)malloc(png_get_rowbytes(pngPtr, infoPtr));
207
208 png_read_image(pngPtr, rowPtrs);
209 }
210
211
212 static void
write_24bit_image(const char * baseName,int width,int height,png_bytep * rowPtrs)213 write_24bit_image(const char* baseName, int width, int height, png_bytep* rowPtrs)
214 {
215 fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width);
216 fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height);
217 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n");
218
219 fprintf(sOutput, "static uint8 %s24BitCompressedImage[] = {\n\t",
220 baseName);
221
222 ZlibCompressor zlib(sOutput);
223
224 for (int y = 0; y < height; y++)
225 zlib.Compress(rowPtrs[y], width * 3);
226
227 zlib.Finish();
228
229 fprintf(sOutput, "\n};\n");
230 fprintf(sOutput, "#endif\n\n");
231 }
232
233
234 static void
write_8bit_image(const char * baseName,int width,int height,unsigned char ** rowPtrs)235 write_8bit_image(const char* baseName, int width, int height, unsigned char** rowPtrs)
236 {
237 //int buffer[128];
238 // buffer[0] stores count, buffer[1..127] holds the actual values
239
240 fprintf(sOutput, "static uint8 %s8BitCompressedImage[] = {\n\t",
241 baseName);
242
243
244 ZlibCompressor zlib(sOutput);
245
246 for (int y = 0; y < height; y++)
247 zlib.Compress(rowPtrs[y], width);
248
249 zlib.Finish();
250
251 fprintf(sOutput, "\n};\n\n");
252 }
253
254
255 unsigned char
nearest_color(unsigned char * color,RGBA palette[256])256 nearest_color(unsigned char* color, RGBA palette[256])
257 {
258 int i, dist, minDist, index = 0;
259 minDist = 255 * 255 + 255 * 255 + 255 * 255 + 1;
260 for (i = 0; i < 256; i++) {
261 int dr = ((int)color[2]) - palette[i].r;
262 int dg = ((int)color[1]) - palette[i].g;
263 int db = ((int)color[0]) - palette[i].b;
264 dist = dr * dr + dg * dg + db * db;
265 if (dist < minDist) {
266 minDist = dist;
267 index = i;
268 }
269 }
270 return index;
271 }
272
273
274 static void
create_8bit_images(const char * logoBaseName,int logoWidth,int logoHeight,png_bytep * logoRowPtrs,const char * iconsBaseName,int iconsWidth,int iconsHeight,png_bytep * iconsRowPtrs)275 create_8bit_images(const char* logoBaseName, int logoWidth, int logoHeight,
276 png_bytep* logoRowPtrs, const char* iconsBaseName, int iconsWidth,
277 int iconsHeight, png_bytep* iconsRowPtrs)
278 {
279 // Generate 8-bit palette
280 BColorQuantizer quantizer(256, 8);
281 quantizer.ProcessImage(logoRowPtrs, logoWidth, logoHeight);
282 quantizer.ProcessImage(iconsRowPtrs, iconsWidth, iconsHeight);
283
284 RGBA palette[256];
285 quantizer.GetColorTable(palette);
286
287 // convert 24-bit logo image to 8-bit indexed color
288 uint8* logoIndexedImageRows[logoHeight];
289 for (int y = 0; y < logoHeight; y++) {
290 logoIndexedImageRows[y] = new uint8[logoWidth];
291 for (int x = 0; x < logoWidth; x++) {
292 logoIndexedImageRows[y][x] = nearest_color(&logoRowPtrs[y][x*3],
293 palette);
294 }
295 }
296
297 // convert 24-bit icons image to 8-bit indexed color
298 uint8* iconsIndexedImageRows[iconsHeight];
299 for (int y = 0; y < iconsHeight; y++) {
300 iconsIndexedImageRows[y] = new uint8[iconsWidth];
301 for (int x = 0; x < iconsWidth; x++) {
302 iconsIndexedImageRows[y][x] = nearest_color(&iconsRowPtrs[y][x*3],
303 palette);
304 }
305 }
306
307
308 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n");
309
310 // write the 8-bit color palette
311 fprintf(sOutput, "static const uint8 k8BitPalette[] = {\n");
312 for (int c = 0; c < 256; c++) {
313 fprintf(sOutput, "\t0x%x, 0x%x, 0x%x,\n",
314 palette[c].r, palette[c].g, palette[c].b);
315 }
316 fprintf(sOutput, "\t};\n\n");
317
318 // write the 8-bit images
319 write_8bit_image(logoBaseName, logoWidth, logoHeight, logoIndexedImageRows);
320 write_8bit_image(iconsBaseName, iconsWidth, iconsHeight,
321 iconsIndexedImageRows);
322
323 fprintf(sOutput, "#endif\n\n");
324
325 // free memory
326 for (int y = 0; y < logoHeight; y++)
327 delete[] logoIndexedImageRows[y];
328
329 for (int y = 0; y < iconsHeight; y++)
330 delete[] iconsIndexedImageRows[y];
331 }
332
333
334 static void
parse_images(const char * logoFilename,const char * logoBaseName,const char * iconsFilename,const char * iconsBaseName)335 parse_images(const char* logoFilename, const char* logoBaseName,
336 const char* iconsFilename, const char* iconsBaseName)
337 {
338 int logoWidth;
339 int logoHeight;
340 png_bytep* logoRowPtrs = NULL;
341 png_structp logoPngPtr;
342 png_infop logoInfoPtr;
343
344 int iconsWidth;
345 int iconsHeight;
346 png_bytep* iconsRowPtrs = NULL;
347 png_structp iconsPngPtr;
348 png_infop iconsInfoPtr;
349
350 read_png(logoFilename, logoWidth, logoHeight, logoRowPtrs, logoPngPtr,
351 logoInfoPtr);
352 read_png(iconsFilename, iconsWidth, iconsHeight, iconsRowPtrs, iconsPngPtr,
353 iconsInfoPtr);
354
355 // write 24-bit images
356 write_24bit_image(logoBaseName, logoWidth, logoHeight, logoRowPtrs);
357 write_24bit_image(iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs);
358
359 // write 8-bit index color images
360 create_8bit_images(logoBaseName, logoWidth, logoHeight, logoRowPtrs,
361 iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs);
362
363 // free resources
364 png_destroy_read_struct(&logoPngPtr, &logoInfoPtr, NULL);
365 for (int y = 0; y < logoHeight; y++)
366 free(logoRowPtrs[y]);
367 free(logoRowPtrs);
368
369 png_destroy_read_struct(&iconsPngPtr, &iconsInfoPtr, NULL);
370 for (int y = 0; y < iconsHeight; y++)
371 free(iconsRowPtrs[y]);
372 free(iconsRowPtrs);
373 }
374
375
376 int
main(int argc,char * argv[])377 main(int argc, char* argv[])
378 {
379 if (argc < 8) {
380 printf("Usage:\n");
381 printf("\t%s <splash.png> <x placement in %%> <y placement in %%> "
382 "<icons.png> <x placement in %%> <y placement in %%> <images.h>\n",
383 argv[0]);
384 return 0;
385 }
386
387 int logoPlacementX = atoi(argv[2]);
388 int logoPlacementY = atoi(argv[3]);
389 int iconPlacementX = atoi(argv[5]);
390 int iconPlacementY = atoi(argv[6]);
391 if (logoPlacementX < 0 || logoPlacementX > 100) {
392 printf("Bad X placement for logo: %d%%\n", logoPlacementX);
393 return 1;
394 }
395 if (logoPlacementY < 0 || logoPlacementY > 100) {
396 printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY);
397 return 1;
398 }
399 if (iconPlacementX < 0 || iconPlacementX > 100) {
400 printf("Bad X placement for icons: %d%%\n", iconPlacementX);
401 return 1;
402 }
403 if (iconPlacementY < 0 || iconPlacementY > 100) {
404 printf("Bad Y placement for icons: %d%%\n", iconPlacementY);
405 return 1;
406 }
407
408 const char* headerFileName = argv[7];
409 sOutput = fopen(headerFileName, "wb");
410 if (!sOutput)
411 error("Could not open file \"%s\" for writing", headerFileName);
412
413 fputs("// This file was generated by the generate_boot_screen Haiku build "
414 "tool.\n\n", sOutput);
415
416 fprintf(sOutput, "static const int32 kSplashLogoPlacementX = %d;\n",
417 logoPlacementX);
418 fprintf(sOutput, "static const int32 kSplashLogoPlacementY = %d;\n",
419 logoPlacementY);
420 fprintf(sOutput, "static const int32 kSplashIconsPlacementX = %d;\n",
421 iconPlacementX);
422 fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n",
423 iconPlacementY);
424
425 parse_images(argv[1], "kSplashLogo", argv[4], "kSplashIcons");
426
427 fclose(sOutput);
428 return 0;
429 }
430