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 * David Powell <david@mad.scientist.com> 10 */ 11 12 //! Haiku boot splash image generator/converter 13 14 #include <iostream> 15 #include <png.h> 16 #include <string> 17 #include <stdarg.h> 18 #include <stdint.h> 19 #include <stdlib.h> 20 21 #include <ColorQuantizer.h> 22 23 // TODO: Create 4 bit palette version of these 24 // images as well, so that they are ready to be 25 // used during boot in case we need to run in 26 // palette 4 bit VGA mode. 27 28 29 FILE* sOutput = NULL; 30 int sOffset = 0; 31 32 static void 33 error(const char *s, ...) 34 { 35 va_list args; 36 va_start(args, s); 37 vfprintf(stderr, s, args); 38 fprintf(stderr, "\n"); 39 va_end(args); 40 exit(-1); 41 } 42 43 44 class AutoFileCloser { 45 public: 46 AutoFileCloser(FILE* file) 47 : fFile(file) 48 {} 49 ~AutoFileCloser() 50 { 51 fclose(fFile); 52 } 53 private: 54 FILE* fFile; 55 }; 56 57 58 static void 59 read_png(const char* filename, int& width, int& height, png_bytep*& rowPtrs, 60 png_structp& pngPtr, png_infop& infoPtr) 61 { 62 char header[8]; 63 FILE* input = fopen(filename, "rb"); 64 if (!input) 65 error("[read_png] File %s could not be opened for reading", filename); 66 67 AutoFileCloser _(input); 68 69 fread(header, 1, 8, input); 70 if (png_sig_cmp((png_byte *)header, 0, 8 )) 71 error("[read_png] File %s is not recognized as a PNG file", filename); 72 73 pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 74 NULL, NULL, NULL); 75 if (!pngPtr) 76 error("[read_png] png_create_read_struct failed"); 77 78 infoPtr = png_create_info_struct(pngPtr); 79 if (!infoPtr) 80 error("[read_png] png_create_info_struct failed"); 81 82 // TODO: I don't know which version of libpng introduced this feature: 83 #if PNG_LIBPNG_VER > 10005 84 if (setjmp(png_jmpbuf(pngPtr))) 85 error("[read_png] Error during init_io"); 86 #endif 87 88 png_init_io(pngPtr, input); 89 png_set_sig_bytes(pngPtr, 8); 90 91 // make sure we automatically get RGB data with 8 bits per channel 92 // also make sure the alpha channel is stripped, in the end, we 93 // expect 24 bits BGR data 94 png_set_expand(pngPtr); 95 png_set_expand_gray_1_2_4_to_8(pngPtr); 96 png_set_palette_to_rgb(pngPtr); 97 png_set_gray_to_rgb(pngPtr); 98 png_set_strip_alpha(pngPtr); 99 png_set_bgr(pngPtr); 100 101 png_read_info(pngPtr, infoPtr); 102 width = infoPtr->width; 103 height = infoPtr->height; 104 if (infoPtr->bit_depth != 8) 105 error("[read_png] File %s has wrong bit depth\n", filename); 106 if ((int)infoPtr->rowbytes < width * 3) { 107 error("[read_png] File %s has wrong color type (RGB required)\n", 108 filename); 109 } 110 111 112 png_set_interlace_handling(pngPtr); 113 png_read_update_info(pngPtr, infoPtr); 114 115 #if PNG_LIBPNG_VER > 10005 116 if (setjmp(png_jmpbuf(pngPtr))) 117 error("[read_png] Error during read_image"); 118 #endif 119 120 rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height); 121 for (int y = 0; y < height; y++) 122 rowPtrs[y] = (png_byte*)malloc(infoPtr->rowbytes); 123 124 png_read_image(pngPtr, rowPtrs); 125 } 126 127 128 static void 129 new_line_if_required() 130 { 131 sOffset++; 132 if (sOffset % 12 == 0) 133 fprintf(sOutput, "\n\t"); 134 } 135 136 137 static void 138 write_24bit_image(const char* baseName, int width, int height, png_bytep* rowPtrs) 139 { 140 fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width); 141 fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height); 142 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n"); 143 144 int buffer[128]; 145 // buffer[0] stores count, buffer[1..127] holds the actual values 146 147 fprintf(sOutput, "static uint8 %s24BitCompressedImage[] = {\n\t", 148 baseName); 149 150 for (int c = 0; c < 3; c++) { 151 // for each component i.e. R, G, B ... 152 // NOTE : I don't care much about performance at this step, 153 // decoding however... 154 int currentValue = rowPtrs[0][c]; 155 int count = 0; 156 157 // When bufferActive == true, we store the number rather than writing 158 // them directly; we use this to store numbers until we find a pair.. 159 bool bufferActive = false; 160 161 sOffset = 0; 162 163 for (int y = 0; y < height; y++) { 164 png_byte* row = rowPtrs[y]; 165 for (int x = c; x < width * 3; x += 3) { 166 if (row[x] == currentValue) { 167 if (bufferActive) { 168 bufferActive = false; 169 count = 2; 170 if (buffer[0] > 1) { 171 fprintf(sOutput, "%d, ", 172 128 + buffer[0] - 1); 173 new_line_if_required(); 174 for (int i = 1; i < buffer[0] ; i++) { 175 fprintf(sOutput, "%d, ", 176 buffer[i]); 177 new_line_if_required(); 178 } 179 } 180 } else { 181 count++; 182 if (count == 127) { 183 fprintf(sOutput, "127, "); 184 new_line_if_required(); 185 fprintf(sOutput, "%d, ", currentValue); 186 new_line_if_required(); 187 count = 0; 188 } 189 } 190 } else { 191 if (bufferActive) { 192 if (buffer[0] == 127) { 193 // we don't have enough room, 194 // flush the buffer 195 fprintf(sOutput, "%d, ", 196 128 + buffer[0] - 1); 197 new_line_if_required(); 198 for (int i = 1; i < buffer[0]; i++) { 199 fprintf(sOutput, "%d, ", buffer[i]); 200 new_line_if_required(); 201 } 202 buffer[0] = 0; 203 } 204 } else { 205 if (count > 0) { 206 fprintf(sOutput, "%d, ", count); 207 new_line_if_required(); 208 fprintf(sOutput, "%d, ", currentValue); 209 new_line_if_required(); 210 } 211 buffer[0] = 0; 212 bufferActive = true; 213 } 214 buffer[0]++; 215 buffer[buffer[0]] = row[x]; 216 currentValue = row[x]; 217 } 218 } 219 } 220 if (bufferActive) { 221 // I could have written 127 + buffer[0], 222 // but I think this is more readable... 223 fprintf(sOutput, "%d, ", 128 + buffer[0] - 1); 224 new_line_if_required(); 225 for (int i = 1; i < buffer[0] ; i++) { 226 fprintf(sOutput, "%d, ", buffer[i]); 227 new_line_if_required(); 228 } 229 } else { 230 fprintf(sOutput, "%d, %d, ", count, currentValue); 231 new_line_if_required(); 232 } 233 // we put a terminating zero for the next byte that indicates 234 // a "count", just to indicate the end of the channel 235 fprintf(sOutput, "0"); 236 if (c != 2) 237 fprintf(sOutput, ","); 238 239 fprintf(sOutput, "\n\t"); 240 } 241 fprintf(sOutput, "};\n"); 242 fprintf(sOutput, "#endif\n\n"); 243 } 244 245 246 static void 247 write_8bit_image(const char* baseName, int width, int height, unsigned char** rowPtrs) 248 { 249 int buffer[128]; 250 // buffer[0] stores count, buffer[1..127] holds the actual values 251 252 fprintf(sOutput, "static uint8 %s8BitCompressedImage[] = {\n\t", 253 baseName); 254 255 // NOTE: I don't care much about performance at this step, 256 // decoding however... 257 unsigned char currentValue = rowPtrs[0][0]; 258 int count = 0; 259 260 // When bufferActive == true, we store the number rather than writing 261 // them directly; we use this to store numbers until we find a pair.. 262 bool bufferActive = false; 263 264 sOffset = 0; 265 for (int y = 0; y < height; y++) { 266 unsigned char* row = rowPtrs[y]; 267 for (int x = 0; x < width; x++) { 268 if (row[x] == currentValue) { 269 if (bufferActive) { 270 bufferActive = false; 271 count = 2; 272 if (buffer[0] > 1) { 273 fprintf(sOutput, "%d, ", 274 128 + buffer[0] - 1); 275 new_line_if_required(); 276 for (int i = 1; i < buffer[0] ; i++) { 277 fprintf(sOutput, "%d, ", 278 buffer[i]); 279 new_line_if_required(); 280 } 281 } 282 } else { 283 count++; 284 if (count == 127) { 285 fprintf(sOutput, "127, "); 286 new_line_if_required(); 287 fprintf(sOutput, "%d, ", currentValue); 288 new_line_if_required(); 289 count = 0; 290 } 291 } 292 } else { 293 if (bufferActive) { 294 if (buffer[0] == 127) { 295 // we don't have enough room, 296 // flush the buffer 297 fprintf(sOutput, "%d, ", 298 128 + buffer[0] - 1); 299 new_line_if_required(); 300 for (int i = 1; i < buffer[0]; i++) { 301 fprintf(sOutput, "%d, ", buffer[i]); 302 new_line_if_required(); 303 } 304 buffer[0] = 0; 305 } 306 } else { 307 if (count > 0) { 308 fprintf(sOutput, "%d, ", count); 309 new_line_if_required(); 310 fprintf(sOutput, "%d, ", currentValue); 311 new_line_if_required(); 312 } 313 buffer[0] = 0; 314 bufferActive = true; 315 } 316 buffer[0]++; 317 buffer[buffer[0]] = row[x]; 318 currentValue = row[x]; 319 } 320 } 321 } 322 if (bufferActive) { 323 // I could have written 127 + buffer[0], 324 // but I think this is more readable... 325 fprintf(sOutput, "%d, ", 128 + buffer[0] - 1); 326 new_line_if_required(); 327 for (int i = 1; i < buffer[0] ; i++) { 328 fprintf(sOutput, "%d, ", buffer[i]); 329 new_line_if_required(); 330 } 331 } else { 332 fprintf(sOutput, "%d, %d, ", count, currentValue); 333 new_line_if_required(); 334 } 335 // we put a terminating zero for the next byte that indicates 336 // a "count", to indicate the end 337 fprintf(sOutput, "0"); 338 339 fprintf(sOutput, "\n\t"); 340 fprintf(sOutput, "};\n\n"); 341 } 342 343 344 unsigned char 345 nearest_color(unsigned char* color, RGBA palette[256]) 346 { 347 int i, dist, minDist, index = 0; 348 minDist = 255 * 255 + 255 * 255 + 255 * 255 + 1; 349 for (i = 0; i < 256; i++) { 350 int dr = ((int)color[2]) - palette[i].r; 351 int dg = ((int)color[1]) - palette[i].g; 352 int db = ((int)color[0]) - palette[i].b; 353 dist = dr * dr + dg * dg + db * db; 354 if (dist < minDist) { 355 minDist = dist; 356 index = i; 357 } 358 } 359 return index; 360 } 361 362 363 static void 364 create_8bit_images(const char* logoBaseName, int logoWidth, int logoHeight, 365 png_bytep* logoRowPtrs, const char* iconsBaseName, int iconsWidth, 366 int iconsHeight, png_bytep* iconsRowPtrs) 367 { 368 // Generate 8-bit palette 369 BColorQuantizer quantizer(256, 8); 370 quantizer.ProcessImage(logoRowPtrs, logoWidth, logoHeight); 371 quantizer.ProcessImage(iconsRowPtrs, iconsWidth, iconsHeight); 372 373 RGBA palette[256]; 374 quantizer.GetColorTable(palette); 375 376 // convert 24-bit logo image to 8-bit indexed color 377 uint8* logoIndexedImageRows[logoHeight]; 378 for (int y = 0; y < logoHeight; y++) { 379 logoIndexedImageRows[y] = new uint8[logoWidth]; 380 for (int x = 0; x < logoWidth; x++) { 381 logoIndexedImageRows[y][x] = nearest_color(&logoRowPtrs[y][x*3], 382 palette); 383 } 384 } 385 386 // convert 24-bit icons image to 8-bit indexed color 387 uint8* iconsIndexedImageRows[iconsHeight]; 388 for (int y = 0; y < iconsHeight; y++) { 389 iconsIndexedImageRows[y] = new uint8[iconsWidth]; 390 for (int x = 0; x < iconsWidth; x++) { 391 iconsIndexedImageRows[y][x] = nearest_color(&iconsRowPtrs[y][x*3], 392 palette); 393 } 394 } 395 396 397 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n"); 398 399 // write the 8-bit color palette 400 fprintf(sOutput, "static const uint8 k8BitPalette[] = {\n"); 401 for (int c = 0; c < 256; c++) { 402 fprintf(sOutput, "\t0x%x, 0x%x, 0x%x,\n", 403 palette[c].r, palette[c].g, palette[c].b); 404 } 405 fprintf(sOutput, "\t};\n\n"); 406 407 // write the 8-bit images 408 write_8bit_image(logoBaseName, logoWidth, logoHeight, logoIndexedImageRows); 409 write_8bit_image(iconsBaseName, iconsWidth, iconsHeight, 410 iconsIndexedImageRows); 411 412 fprintf(sOutput, "#endif\n\n"); 413 414 // free memory 415 for (int y = 0; y < logoHeight; y++) 416 delete[] logoIndexedImageRows[y]; 417 418 for (int y = 0; y < iconsHeight; y++) 419 delete[] iconsIndexedImageRows[y]; 420 } 421 422 423 static void 424 parse_images(const char* logoFilename, const char* logoBaseName, 425 const char* iconsFilename, const char* iconsBaseName) 426 { 427 int logoWidth; 428 int logoHeight; 429 png_bytep* logoRowPtrs = NULL; 430 png_structp logoPngPtr; 431 png_infop logoInfoPtr; 432 433 int iconsWidth; 434 int iconsHeight; 435 png_bytep* iconsRowPtrs = NULL; 436 png_structp iconsPngPtr; 437 png_infop iconsInfoPtr; 438 439 read_png(logoFilename, logoWidth, logoHeight, logoRowPtrs, logoPngPtr, 440 logoInfoPtr); 441 read_png(iconsFilename, iconsWidth, iconsHeight, iconsRowPtrs, iconsPngPtr, 442 iconsInfoPtr); 443 444 // write 24-bit images 445 write_24bit_image(logoBaseName, logoWidth, logoHeight, logoRowPtrs); 446 write_24bit_image(iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs); 447 448 // write 8-bit index color images 449 create_8bit_images(logoBaseName, logoWidth, logoHeight, logoRowPtrs, 450 iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs); 451 452 // free resources 453 png_destroy_read_struct(&logoPngPtr, &logoInfoPtr, NULL); 454 for (int y = 0; y < logoHeight; y++) 455 free(logoRowPtrs[y]); 456 free(logoRowPtrs); 457 458 png_destroy_read_struct(&iconsPngPtr, &iconsInfoPtr, NULL); 459 for (int y = 0; y < iconsHeight; y++) 460 free(iconsRowPtrs[y]); 461 free(iconsRowPtrs); 462 } 463 464 465 int 466 main(int argc, char* argv[]) 467 { 468 if (argc < 8) { 469 printf("Usage:\n"); 470 printf("\t%s <splash.png> <x placement in %%> <y placement in %%> " 471 "<icons.png> <x placement in %%> <y placement in %%> <images.h>\n", 472 argv[0]); 473 return 0; 474 } 475 476 int logoPlacementX = atoi(argv[2]); 477 int logoPlacementY = atoi(argv[3]); 478 int iconPlacementX = atoi(argv[5]); 479 int iconPlacementY = atoi(argv[6]); 480 if (logoPlacementX < 0 || logoPlacementX > 100) { 481 printf("Bad X placement for logo: %d%%\n", logoPlacementX); 482 return 1; 483 } 484 if (logoPlacementY < 0 || logoPlacementY > 100) { 485 printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY); 486 return 1; 487 } 488 if (iconPlacementX < 0 || iconPlacementX > 100) { 489 printf("Bad X placement for icons: %d%%\n", iconPlacementX); 490 return 1; 491 } 492 if (iconPlacementY < 0 || iconPlacementY > 100) { 493 printf("Bad Y placement for icons: %d%%\n", iconPlacementY); 494 return 1; 495 } 496 497 const char* headerFileName = argv[7]; 498 sOutput = fopen(headerFileName, "wb"); 499 if (!sOutput) 500 error("Could not open file \"%s\" for writing", headerFileName); 501 502 fputs("// This file was generated by the generate_boot_screen Haiku build " 503 "tool.\n\n", sOutput); 504 505 fprintf(sOutput, "static const int32 kSplashLogoPlacementX = %d;\n", 506 logoPlacementX); 507 fprintf(sOutput, "static const int32 kSplashLogoPlacementY = %d;\n", 508 logoPlacementY); 509 fprintf(sOutput, "static const int32 kSplashIconsPlacementX = %d;\n", 510 iconPlacementX); 511 fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n", 512 iconPlacementY); 513 514 parse_images(argv[1], "kSplashLogo", argv[4], "kSplashIcons"); 515 516 fclose(sOutput); 517 return 0; 518 } 519