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