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 buffer[0]++; 205 buffer[buffer[0]] = row[x]; 206 } else if (count > 0) { 207 buffer[0] = 1; 208 buffer[1] = row[x]; 209 bufferActive = true; 210 if (count > 1) { 211 fprintf(sOutput, "%d, ", count); 212 new_line_if_required(); 213 fprintf(sOutput, "%d, ", currentValue); 214 new_line_if_required(); 215 } 216 } 217 currentValue = row[x]; 218 } 219 } 220 } 221 if (bufferActive) { 222 // I could have written 127 + buffer[0], 223 // but I think this is more readable... 224 fprintf(sOutput, "%d, ", 128 + buffer[0] - 1); 225 new_line_if_required(); 226 for (int i = 1; i < buffer[0] ; i++) { 227 fprintf(sOutput, "%d, ", buffer[i]); 228 new_line_if_required(); 229 } 230 } else { 231 fprintf(sOutput, "%d, %d, ", count, currentValue); 232 new_line_if_required(); 233 } 234 // we put a terminating zero for the next byte that indicates 235 // a "count", just to indicate the end of the channel 236 fprintf(sOutput, "0"); 237 if (c != 2) 238 fprintf(sOutput, ","); 239 240 fprintf(sOutput, "\n\t"); 241 } 242 fprintf(sOutput, "};\n"); 243 fprintf(sOutput, "#endif\n\n"); 244 } 245 246 247 static void 248 write_8bit_image(const char* baseName, int width, int height, unsigned char** rowPtrs) 249 { 250 int buffer[128]; 251 // buffer[0] stores count, buffer[1..127] holds the actual values 252 253 fprintf(sOutput, "static uint8 %s8BitCompressedImage[] = {\n\t", 254 baseName); 255 256 // NOTE: I don't care much about performance at this step, 257 // decoding however... 258 unsigned char currentValue = rowPtrs[0][0]; 259 int count = 0; 260 261 // When bufferActive == true, we store the number rather than writing 262 // them directly; we use this to store numbers until we find a pair.. 263 bool bufferActive = false; 264 265 sOffset = 0; 266 for (int y = 0; y < height; y++) { 267 unsigned char* row = rowPtrs[y]; 268 for (int x = 0; x < width; x++) { 269 if (row[x] == currentValue) { 270 if (bufferActive) { 271 bufferActive = false; 272 count = 2; 273 if (buffer[0] > 1) { 274 fprintf(sOutput, "%d, ", 275 128 + buffer[0] - 1); 276 new_line_if_required(); 277 for (int i = 1; i < buffer[0] ; i++) { 278 fprintf(sOutput, "%d, ", 279 buffer[i]); 280 new_line_if_required(); 281 } 282 } 283 } else { 284 count++; 285 if (count == 127) { 286 fprintf(sOutput, "127, "); 287 new_line_if_required(); 288 fprintf(sOutput, "%d, ", currentValue); 289 new_line_if_required(); 290 count = 0; 291 } 292 } 293 } else { 294 if (bufferActive) { 295 if (buffer[0] == 127) { 296 // we don't have enough room, 297 // flush the buffer 298 fprintf(sOutput, "%d, ", 299 128 + buffer[0] - 1); 300 new_line_if_required(); 301 for (int i = 1; i < buffer[0]; i++) { 302 fprintf(sOutput, "%d, ", buffer[i]); 303 new_line_if_required(); 304 } 305 buffer[0] = 0; 306 } 307 buffer[0]++; 308 buffer[buffer[0]] = row[x]; 309 } else if (count > 0) { 310 buffer[0] = 1; 311 buffer[1] = row[x]; 312 bufferActive = true; 313 if (count > 1) { 314 fprintf(sOutput, "%d, ", count); 315 new_line_if_required(); 316 fprintf(sOutput, "%d, ", currentValue); 317 new_line_if_required(); 318 } 319 } 320 currentValue = row[x]; 321 } 322 } 323 } 324 if (bufferActive) { 325 // I could have written 127 + buffer[0], 326 // but I think this is more readable... 327 fprintf(sOutput, "%d, ", 128 + buffer[0] - 1); 328 new_line_if_required(); 329 for (int i = 1; i < buffer[0] ; i++) { 330 fprintf(sOutput, "%d, ", buffer[i]); 331 new_line_if_required(); 332 } 333 } else { 334 fprintf(sOutput, "%d, %d, ", count, currentValue); 335 new_line_if_required(); 336 } 337 // we put a terminating zero for the next byte that indicates 338 // a "count", to indicate the end 339 fprintf(sOutput, "0"); 340 341 fprintf(sOutput, "\n\t"); 342 fprintf(sOutput, "};\n\n"); 343 } 344 345 346 unsigned char 347 nearest_color(unsigned char* color, RGBA palette[256]) 348 { 349 int i, dist, minDist, index = 0; 350 minDist = 255 * 255 + 255 * 255 + 255 * 255 + 1; 351 for (i = 0; i < 256; i++) { 352 int dr = ((int)color[2]) - palette[i].r; 353 int dg = ((int)color[1]) - palette[i].g; 354 int db = ((int)color[0]) - palette[i].b; 355 dist = dr * dr + dg * dg + db * db; 356 if (dist < minDist) { 357 minDist = dist; 358 index = i; 359 } 360 } 361 return index; 362 } 363 364 365 static void 366 create_8bit_images(const char* logoBaseName, int logoWidth, int logoHeight, 367 png_bytep* logoRowPtrs, const char* iconsBaseName, int iconsWidth, 368 int iconsHeight, png_bytep* iconsRowPtrs) 369 { 370 // Generate 8-bit palette 371 BColorQuantizer quantizer(256, 8); 372 quantizer.ProcessImage(logoRowPtrs, logoWidth, logoHeight); 373 quantizer.ProcessImage(iconsRowPtrs, iconsWidth, iconsHeight); 374 375 RGBA palette[256]; 376 quantizer.GetColorTable(palette); 377 378 // convert 24-bit logo image to 8-bit indexed color 379 uint8* logoIndexedImageRows[logoHeight]; 380 for (int y = 0; y < logoHeight; y++) { 381 logoIndexedImageRows[y] = new uint8[logoWidth]; 382 for (int x = 0; x < logoWidth; x++) { 383 logoIndexedImageRows[y][x] = nearest_color(&logoRowPtrs[y][x*3], 384 palette); 385 } 386 } 387 388 // convert 24-bit icons image to 8-bit indexed color 389 uint8* iconsIndexedImageRows[iconsHeight]; 390 for (int y = 0; y < iconsHeight; y++) { 391 iconsIndexedImageRows[y] = new uint8[iconsWidth]; 392 for (int x = 0; x < iconsWidth; x++) { 393 iconsIndexedImageRows[y][x] = nearest_color(&iconsRowPtrs[y][x*3], 394 palette); 395 } 396 } 397 398 399 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n"); 400 401 // write the 8-bit color palette 402 fprintf(sOutput, "static const uint8 k8BitPalette[] = {\n"); 403 for (int c = 0; c < 256; c++) { 404 fprintf(sOutput, "\t0x%x, 0x%x, 0x%x,\n", 405 palette[c].r, palette[c].g, palette[c].b); 406 } 407 fprintf(sOutput, "\t};\n\n"); 408 409 // write the 8-bit images 410 write_8bit_image(logoBaseName, logoWidth, logoHeight, logoIndexedImageRows); 411 write_8bit_image(iconsBaseName, iconsWidth, iconsHeight, 412 iconsIndexedImageRows); 413 414 fprintf(sOutput, "#endif\n\n"); 415 416 // free memory 417 for (int y = 0; y < logoHeight; y++) 418 delete[] logoIndexedImageRows[y]; 419 420 for (int y = 0; y < iconsHeight; y++) 421 delete[] iconsIndexedImageRows[y]; 422 } 423 424 425 static void 426 parse_images(const char* logoFilename, const char* logoBaseName, 427 const char* iconsFilename, const char* iconsBaseName) 428 { 429 int logoWidth; 430 int logoHeight; 431 png_bytep* logoRowPtrs = NULL; 432 png_structp logoPngPtr; 433 png_infop logoInfoPtr; 434 435 int iconsWidth; 436 int iconsHeight; 437 png_bytep* iconsRowPtrs = NULL; 438 png_structp iconsPngPtr; 439 png_infop iconsInfoPtr; 440 441 read_png(logoFilename, logoWidth, logoHeight, logoRowPtrs, logoPngPtr, 442 logoInfoPtr); 443 read_png(iconsFilename, iconsWidth, iconsHeight, iconsRowPtrs, iconsPngPtr, 444 iconsInfoPtr); 445 446 // write 24-bit images 447 write_24bit_image(logoBaseName, logoWidth, logoHeight, logoRowPtrs); 448 write_24bit_image(iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs); 449 450 // write 8-bit index color images 451 create_8bit_images(logoBaseName, logoWidth, logoHeight, logoRowPtrs, 452 iconsBaseName, iconsWidth, iconsHeight, iconsRowPtrs); 453 454 // free resources 455 png_destroy_read_struct(&logoPngPtr, &logoInfoPtr, NULL); 456 for (int y = 0; y < logoHeight; y++) 457 free(logoRowPtrs[y]); 458 free(logoRowPtrs); 459 460 png_destroy_read_struct(&iconsPngPtr, &iconsInfoPtr, NULL); 461 for (int y = 0; y < iconsHeight; y++) 462 free(iconsRowPtrs[y]); 463 free(iconsRowPtrs); 464 } 465 466 467 int 468 main(int argc, char* argv[]) 469 { 470 if (argc < 8) { 471 printf("Usage:\n"); 472 printf("\t%s <splash.png> <x placement in %%> <y placement in %%> " 473 "<icons.png> <x placement in %%> <y placement in %%> <images.h>\n", 474 argv[0]); 475 return 0; 476 } 477 478 int logoPlacementX = atoi(argv[2]); 479 int logoPlacementY = atoi(argv[3]); 480 int iconPlacementX = atoi(argv[5]); 481 int iconPlacementY = atoi(argv[6]); 482 if (logoPlacementX < 0 || logoPlacementX > 100) { 483 printf("Bad X placement for logo: %d%%\n", logoPlacementX); 484 return 1; 485 } 486 if (logoPlacementY < 0 || logoPlacementY > 100) { 487 printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY); 488 return 1; 489 } 490 if (iconPlacementX < 0 || iconPlacementX > 100) { 491 printf("Bad X placement for icons: %d%%\n", iconPlacementX); 492 return 1; 493 } 494 if (iconPlacementY < 0 || iconPlacementY > 100) { 495 printf("Bad Y placement for icons: %d%%\n", iconPlacementY); 496 return 1; 497 } 498 499 const char* headerFileName = argv[7]; 500 sOutput = fopen(headerFileName, "wb"); 501 if (!sOutput) 502 error("Could not open file \"%s\" for writing", headerFileName); 503 504 fputs("// This file was generated by the generate_boot_screen Haiku build " 505 "tool.\n\n", sOutput); 506 507 fprintf(sOutput, "static const int32 kSplashLogoPlacementX = %d;\n", 508 logoPlacementX); 509 fprintf(sOutput, "static const int32 kSplashLogoPlacementY = %d;\n", 510 logoPlacementY); 511 fprintf(sOutput, "static const int32 kSplashIconsPlacementX = %d;\n", 512 iconPlacementX); 513 fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n", 514 iconPlacementY); 515 516 parse_images(argv[1], "kSplashLogo", argv[4], "kSplashIcons"); 517 518 fclose(sOutput); 519 return 0; 520 } 521