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 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 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 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 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 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 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 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 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 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 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 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 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