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 */ 10 11 //! Haiku boot splash image generator/converter 12 13 #include <iostream> 14 #include <png.h> 15 #include <string> 16 #include <stdarg.h> 17 #include <stdint.h> 18 19 // TODO: Generate the single optimal palette for all three images, 20 // store palette versions of these images as well, so that they are 21 // ready to be used during boot in case we need to run in palette 22 // VGA mode. 23 24 25 FILE* sOutput = NULL; 26 int sOffset = 0; 27 28 static void 29 error(const char *s, ...) 30 { 31 va_list args; 32 va_start(args, s); 33 vfprintf(stderr, s, args); 34 fprintf(stderr, "\n"); 35 va_end(args); 36 exit(-1); 37 } 38 39 40 class AutoFileCloser { 41 public: 42 AutoFileCloser(FILE* file) 43 : fFile(file) 44 {} 45 ~AutoFileCloser() 46 { 47 fclose(fFile); 48 } 49 private: 50 FILE* fFile; 51 }; 52 53 54 static void 55 read_png(const char* filename, int& width, int& height, png_bytep*& rowPtrs, 56 png_structp& pngPtr, png_infop& infoPtr) 57 { 58 char header[8]; 59 FILE* input = fopen(filename, "rb"); 60 if (!input) 61 error("[read_png] File %s could not be opened for reading", filename); 62 63 AutoFileCloser _(input); 64 65 fread(header, 1, 8, input); 66 if (png_sig_cmp((png_byte *)header, 0, 8 )) 67 error("[read_png] File %s is not recognized as a PNG file", filename); 68 69 pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 70 NULL, NULL, NULL); 71 if (!pngPtr) 72 error("[read_png] png_create_read_struct failed"); 73 74 infoPtr = png_create_info_struct(pngPtr); 75 if (!infoPtr) 76 error("[read_png] png_create_info_struct failed"); 77 78 // TODO: I don't know which version of libpng introduced this feature: 79 #if PNG_LIBPNG_VER > 10005 80 if (setjmp(png_jmpbuf(pngPtr))) 81 error("[read_png] Error during init_io"); 82 #endif 83 84 png_init_io(pngPtr, input); 85 png_set_sig_bytes(pngPtr, 8); 86 87 // make sure we automatically get RGB data with 8 bits per channel 88 // also make sure the alpha channel is stripped, in the end, we 89 // expect 24 bits BGR data 90 png_set_expand(pngPtr); 91 png_set_gray_1_2_4_to_8(pngPtr); 92 png_set_palette_to_rgb(pngPtr); 93 png_set_gray_to_rgb(pngPtr); 94 png_set_strip_alpha(pngPtr); 95 png_set_bgr(pngPtr); 96 97 png_read_info(pngPtr, infoPtr); 98 width = infoPtr->width; 99 height = infoPtr->height; 100 if (infoPtr->bit_depth != 8) 101 error("[read_png] File %s has wrong bit depth\n", filename); 102 if ((int)infoPtr->rowbytes < width * 3) { 103 error("[read_png] File %s has wrong color type (RGB required)\n", 104 filename); 105 } 106 107 108 png_set_interlace_handling(pngPtr); 109 png_read_update_info(pngPtr, infoPtr); 110 111 #if PNG_LIBPNG_VER > 10005 112 if (setjmp(png_jmpbuf(pngPtr))) 113 error("[read_png] Error during read_image"); 114 #endif 115 116 rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height); 117 for (int y = 0; y < height; y++) 118 rowPtrs[y] = (png_byte*)malloc(infoPtr->rowbytes); 119 120 png_read_image(pngPtr, rowPtrs); 121 } 122 123 124 static void 125 new_line_if_required() 126 { 127 sOffset++; 128 if (sOffset % 12 == 0) 129 fprintf(sOutput, "\n\t"); 130 } 131 132 133 static void 134 write_image(const char* baseName, int width, int height, png_bytep* rowPtrs) 135 { 136 fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width); 137 fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height); 138 fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n"); 139 140 int buffer[128]; 141 // buffer[0] stores count, buffer[1..127] holds the actual values 142 143 fprintf(sOutput, "static uint8 %sCompressedImage[] = {\n\t", 144 baseName); 145 146 for (int c = 0; c < 3; c++) { 147 // for each component i.e. R, G, B ... 148 // NOTE : I don't care much about performance at this step, 149 // decoding however... 150 int currentValue = rowPtrs[0][c]; 151 int count = 0; 152 153 // When bufferActive == true, we store the number rather than writing 154 // them directly; we use this to store numbers until we find a pair.. 155 bool bufferActive = false; 156 157 sOffset = 0; 158 159 for (int y = 0; y < height; y++) { 160 png_byte* row = rowPtrs[y]; 161 for (int x = c; x < width * 3; x += 3) { 162 if (row[x] == currentValue) { 163 if (bufferActive) { 164 bufferActive = false; 165 count = 2; 166 if (buffer[0] > 1) { 167 fprintf (sOutput, "%d, ", 168 128 + buffer[0] - 1); 169 new_line_if_required(); 170 for (int i = 1; i < buffer[0] ; i++) { 171 fprintf( sOutput, "%d, ", 172 buffer[i] ); 173 new_line_if_required(); 174 } 175 } 176 } else { 177 count++; 178 if (count == 127) { 179 fprintf(sOutput, "127, "); 180 new_line_if_required(); 181 fprintf(sOutput, "%d, ", currentValue); 182 new_line_if_required(); 183 count = 0; 184 } 185 } 186 } else { 187 if (bufferActive) { 188 if (buffer[0] == 127) { 189 // we don't have enough room, 190 // flush the buffer 191 fprintf(sOutput, "%d, ", 192 128 + buffer[0] - 1); 193 new_line_if_required(); 194 for (int i = 1; i < buffer[0]; i++) { 195 fprintf(sOutput, "%d, ", buffer[i]); 196 new_line_if_required(); 197 } 198 buffer[0] = 0; 199 } 200 buffer[0]++; 201 buffer[buffer[0]] = row[x]; 202 } else if (count > 0) { 203 buffer[0] = 1; 204 buffer[1] = row[x]; 205 bufferActive = true; 206 if (count > 1) { 207 fprintf(sOutput, "%d, ", count); 208 new_line_if_required(); 209 fprintf(sOutput, "%d, ", currentValue); 210 new_line_if_required(); 211 } 212 } 213 currentValue = row[x]; 214 } 215 } 216 } 217 if (bufferActive) { 218 // I could have written 127 + buffer[0], 219 // but I think this is more readable... 220 fprintf(sOutput, "%d, ", 128 + buffer[0] - 1); 221 new_line_if_required(); 222 for (int i = 1; i < buffer[0] ; i++) { 223 fprintf(sOutput, "%d, ", buffer[i]); 224 new_line_if_required(); 225 } 226 } else { 227 fprintf(sOutput, "%d, %d, ", count, currentValue); 228 new_line_if_required(); 229 } 230 // we put a terminating zero for the next byte that indicates 231 // a "count", just to indicate the end of the channel 232 fprintf(sOutput, "0"); 233 if (c != 2) 234 fprintf(sOutput, ","); 235 236 fprintf(sOutput, "\n\t"); 237 } 238 fprintf(sOutput, "};\n"); 239 fprintf(sOutput, "#endif\n\n"); 240 } 241 242 243 static void 244 parse_image(const char* filename, const char* baseName) 245 { 246 int width; 247 int height; 248 png_bytep* rowPtrs = NULL; 249 png_structp pngPtr; 250 png_infop infoPtr; 251 read_png(filename, width, height, rowPtrs, pngPtr, infoPtr); 252 write_image(baseName, width, height, rowPtrs); 253 254 // free resources 255 png_destroy_read_struct(&pngPtr, &infoPtr, NULL); 256 for (int y = 0; y < height; y++) 257 free(rowPtrs[y]); 258 free(rowPtrs); 259 } 260 261 262 int 263 main(int argc, char* argv[]) 264 { 265 if (argc < 8) { 266 printf("Usage:\n"); 267 printf("\t%s <splash.png> <x placement in %%> <y placement in %%> " 268 "<icons.png> <x placement in %%> <y placement in %%> <images.h>\n", 269 argv[0]); 270 return 0; 271 } 272 273 int logoPlacementX = atoi(argv[2]); 274 int logoPlacementY = atoi(argv[3]); 275 int iconPlacementX = atoi(argv[5]); 276 int iconPlacementY = atoi(argv[6]); 277 if (logoPlacementX < 0 || logoPlacementX > 100) { 278 printf("Bad X placement for logo: %d%%\n", logoPlacementX); 279 return 1; 280 } 281 if (logoPlacementY < 0 || logoPlacementY > 100) { 282 printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY); 283 return 1; 284 } 285 if (iconPlacementX < 0 || iconPlacementX > 100) { 286 printf("Bad X placement for icons: %d%%\n", iconPlacementX); 287 return 1; 288 } 289 if (iconPlacementY < 0 || iconPlacementY > 100) { 290 printf("Bad Y placement for icons: %d%%\n", iconPlacementY); 291 return 1; 292 } 293 294 const char* headerFileName = argv[7]; 295 sOutput = fopen(headerFileName, "wb"); 296 if (!sOutput) 297 error("Could not open file \"%s\" for writing", headerFileName); 298 299 fputs("// This file was generated by the generate_boot_screen Haiku build " 300 "tool.\n\n", sOutput); 301 302 fprintf(sOutput, "static const int32 kSplashLogoPlacementX = %d;\n", 303 logoPlacementX); 304 fprintf(sOutput, "static const int32 kSplashLogoPlacementY = %d;\n", 305 logoPlacementY); 306 fprintf(sOutput, "static const int32 kSplashIconsPlacementX = %d;\n", 307 iconPlacementX); 308 fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n", 309 iconPlacementY); 310 311 parse_image(argv[1], "kSplashLogo"); 312 parse_image(argv[4], "kSplashIcons"); 313 314 fclose(sOutput); 315 return 0; 316 } 317 318