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