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