2 * Copyright (c) 2008-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
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>
13 //! Haiku boot splash image generator/converter
24 #include <ColorQuantizer.h>
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.
36 error(const char *s
, ...)
40 vfprintf(stderr
, s
, args
);
41 fprintf(stderr
, "\n");
48 new_line_if_required()
51 if (sOffset
% 12 == 0)
52 fprintf(sOutput
, "\n\t");
59 class AutoFileCloser
{
61 AutoFileCloser(FILE* file
)
76 class ZlibCompressor
{
78 ZlibCompressor(FILE* output
);
80 int Compress(const void* data
, int dataSize
, int flush
= Z_NO_FLUSH
);
89 ZlibCompressor::ZlibCompressor(FILE* output
)
92 // prepare zlib stream
94 fStream
.next_in
= NULL
;
97 fStream
.next_out
= NULL
;
98 fStream
.avail_out
= 0;
99 fStream
.total_out
= 0;
102 fStream
.zalloc
= Z_NULL
;
103 fStream
.zfree
= Z_NULL
;
104 fStream
.opaque
= Z_NULL
;
105 fStream
.data_type
= 0;
107 fStream
.reserved
= 0;
109 int zlibError
= deflateInit(&fStream
, Z_BEST_COMPRESSION
);
110 if (zlibError
!= Z_OK
)
111 return; // TODO: translate zlibError
116 ZlibCompressor::Compress(const void* data
, int dataSize
, int flush
)
120 fStream
.next_in
= (Bytef
*)data
;
121 fStream
.avail_in
= dataSize
;
123 int zlibError
= Z_OK
;
126 fStream
.next_out
= (Bytef
*)buffer
;
127 fStream
.avail_out
= sizeof(buffer
);
129 zlibError
= deflate(&fStream
, flush
);
130 if (zlibError
!= Z_OK
&&
131 (flush
== Z_FINISH
&& zlibError
!= Z_STREAM_END
))
134 unsigned int outputSize
= sizeof(buffer
) - fStream
.avail_out
;
135 for (unsigned int i
= 0; i
< outputSize
; i
++) {
136 fprintf(fOutput
, "%d, ", buffer
[i
]);
137 new_line_if_required();
140 if (zlibError
== Z_STREAM_END
)
143 } while (fStream
.avail_in
> 0 || flush
== Z_FINISH
);
150 ZlibCompressor::Finish()
152 Compress(NULL
, 0, Z_FINISH
);
153 return deflateEnd(&fStream
);
161 read_png(const char* filename
, int& width
, int& height
, png_bytep
*& rowPtrs
,
162 png_structp
& pngPtr
, png_infop
& infoPtr
)
165 FILE* input
= fopen(filename
, "rb");
167 error("[read_png] File %s could not be opened for reading", filename
);
169 AutoFileCloser
_(input
);
171 fread(header
, 1, 8, input
);
172 if (png_sig_cmp((png_byte
*)header
, 0, 8 ))
173 error("[read_png] File %s is not recognized as a PNG file", filename
);
175 pngPtr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
,
178 error("[read_png] png_create_read_struct failed");
180 infoPtr
= png_create_info_struct(pngPtr
);
182 error("[read_png] png_create_info_struct failed");
184 // TODO: I don't know which version of libpng introduced this feature:
185 #if PNG_LIBPNG_VER > 10005
186 if (setjmp(png_jmpbuf(pngPtr
)))
187 error("[read_png] Error during init_io");
190 png_init_io(pngPtr
, input
);
191 png_set_sig_bytes(pngPtr
, 8);
193 // make sure we automatically get RGB data with 8 bits per channel
194 // also make sure the alpha channel is stripped, in the end, we
195 // expect 24 bits BGR data
196 png_set_expand(pngPtr
);
197 png_set_expand_gray_1_2_4_to_8(pngPtr
);
198 png_set_palette_to_rgb(pngPtr
);
199 png_set_gray_to_rgb(pngPtr
);
200 png_set_strip_alpha(pngPtr
);
203 png_read_info(pngPtr
, infoPtr
);
204 width
= (int)png_get_image_width(pngPtr
, infoPtr
);
205 height
= (int)png_get_image_height(pngPtr
, infoPtr
);
206 if (png_get_bit_depth(pngPtr
, infoPtr
) != 8)
207 error("[read_png] File %s has wrong bit depth\n", filename
);
208 if ((int)png_get_rowbytes(pngPtr
, infoPtr
) < width
* 3) {
209 error("[read_png] File %s has wrong color type (RGB required)\n",
214 png_set_interlace_handling(pngPtr
);
215 png_read_update_info(pngPtr
, infoPtr
);
217 #if PNG_LIBPNG_VER > 10005
218 if (setjmp(png_jmpbuf(pngPtr
)))
219 error("[read_png] Error during read_image");
222 rowPtrs
= (png_bytep
*)malloc(sizeof(png_bytep
) * height
);
223 for (int y
= 0; y
< height
; y
++)
224 rowPtrs
[y
] = (png_byte
*)malloc(png_get_rowbytes(pngPtr
, infoPtr
));
226 png_read_image(pngPtr
, rowPtrs
);
231 write_24bit_image(const char* baseName
, int width
, int height
, png_bytep
* rowPtrs
)
233 fprintf(sOutput
, "static const uint16 %sWidth = %d;\n", baseName
, width
);
234 fprintf(sOutput
, "static const uint16 %sHeight = %d;\n", baseName
, height
);
235 fprintf(sOutput
, "#ifndef __BOOTSPLASH_KERNEL__\n");
237 fprintf(sOutput
, "static uint8 %s24BitCompressedImage[] = {\n\t",
240 ZlibCompressor
zlib(sOutput
);
242 for (int y
= 0; y
< height
; y
++)
243 zlib
.Compress(rowPtrs
[y
], width
* 3);
247 fprintf(sOutput
, "\n};\n");
248 fprintf(sOutput
, "#endif\n\n");
253 write_8bit_image(const char* baseName
, int width
, int height
, unsigned char** rowPtrs
)
256 // buffer[0] stores count, buffer[1..127] holds the actual values
258 fprintf(sOutput
, "static uint8 %s8BitCompressedImage[] = {\n\t",
262 ZlibCompressor
zlib(sOutput
);
264 for (int y
= 0; y
< height
; y
++)
265 zlib
.Compress(rowPtrs
[y
], width
);
269 fprintf(sOutput
, "\n};\n\n");
274 nearest_color(unsigned char* color
, RGBA palette
[256])
276 int i
, dist
, minDist
, index
= 0;
277 minDist
= 255 * 255 + 255 * 255 + 255 * 255 + 1;
278 for (i
= 0; i
< 256; i
++) {
279 int dr
= ((int)color
[2]) - palette
[i
].r
;
280 int dg
= ((int)color
[1]) - palette
[i
].g
;
281 int db
= ((int)color
[0]) - palette
[i
].b
;
282 dist
= dr
* dr
+ dg
* dg
+ db
* db
;
283 if (dist
< minDist
) {
293 create_8bit_images(const char* logoBaseName
, int logoWidth
, int logoHeight
,
294 png_bytep
* logoRowPtrs
, const char* iconsBaseName
, int iconsWidth
,
295 int iconsHeight
, png_bytep
* iconsRowPtrs
)
297 // Generate 8-bit palette
298 BColorQuantizer
quantizer(256, 8);
299 quantizer
.ProcessImage(logoRowPtrs
, logoWidth
, logoHeight
);
300 quantizer
.ProcessImage(iconsRowPtrs
, iconsWidth
, iconsHeight
);
303 quantizer
.GetColorTable(palette
);
305 // convert 24-bit logo image to 8-bit indexed color
306 uint8
* logoIndexedImageRows
[logoHeight
];
307 for (int y
= 0; y
< logoHeight
; y
++) {
308 logoIndexedImageRows
[y
] = new uint8
[logoWidth
];
309 for (int x
= 0; x
< logoWidth
; x
++) {
310 logoIndexedImageRows
[y
][x
] = nearest_color(&logoRowPtrs
[y
][x
*3],
315 // convert 24-bit icons image to 8-bit indexed color
316 uint8
* iconsIndexedImageRows
[iconsHeight
];
317 for (int y
= 0; y
< iconsHeight
; y
++) {
318 iconsIndexedImageRows
[y
] = new uint8
[iconsWidth
];
319 for (int x
= 0; x
< iconsWidth
; x
++) {
320 iconsIndexedImageRows
[y
][x
] = nearest_color(&iconsRowPtrs
[y
][x
*3],
326 fprintf(sOutput
, "#ifndef __BOOTSPLASH_KERNEL__\n");
328 // write the 8-bit color palette
329 fprintf(sOutput
, "static const uint8 k8BitPalette[] = {\n");
330 for (int c
= 0; c
< 256; c
++) {
331 fprintf(sOutput
, "\t0x%x, 0x%x, 0x%x,\n",
332 palette
[c
].r
, palette
[c
].g
, palette
[c
].b
);
334 fprintf(sOutput
, "\t};\n\n");
336 // write the 8-bit images
337 write_8bit_image(logoBaseName
, logoWidth
, logoHeight
, logoIndexedImageRows
);
338 write_8bit_image(iconsBaseName
, iconsWidth
, iconsHeight
,
339 iconsIndexedImageRows
);
341 fprintf(sOutput
, "#endif\n\n");
344 for (int y
= 0; y
< logoHeight
; y
++)
345 delete[] logoIndexedImageRows
[y
];
347 for (int y
= 0; y
< iconsHeight
; y
++)
348 delete[] iconsIndexedImageRows
[y
];
353 parse_images(const char* logoFilename
, const char* logoBaseName
,
354 const char* iconsFilename
, const char* iconsBaseName
)
358 png_bytep
* logoRowPtrs
= NULL
;
359 png_structp logoPngPtr
;
360 png_infop logoInfoPtr
;
364 png_bytep
* iconsRowPtrs
= NULL
;
365 png_structp iconsPngPtr
;
366 png_infop iconsInfoPtr
;
368 read_png(logoFilename
, logoWidth
, logoHeight
, logoRowPtrs
, logoPngPtr
,
370 read_png(iconsFilename
, iconsWidth
, iconsHeight
, iconsRowPtrs
, iconsPngPtr
,
373 // write 24-bit images
374 write_24bit_image(logoBaseName
, logoWidth
, logoHeight
, logoRowPtrs
);
375 write_24bit_image(iconsBaseName
, iconsWidth
, iconsHeight
, iconsRowPtrs
);
377 // write 8-bit index color images
378 create_8bit_images(logoBaseName
, logoWidth
, logoHeight
, logoRowPtrs
,
379 iconsBaseName
, iconsWidth
, iconsHeight
, iconsRowPtrs
);
382 png_destroy_read_struct(&logoPngPtr
, &logoInfoPtr
, NULL
);
383 for (int y
= 0; y
< logoHeight
; y
++)
384 free(logoRowPtrs
[y
]);
387 png_destroy_read_struct(&iconsPngPtr
, &iconsInfoPtr
, NULL
);
388 for (int y
= 0; y
< iconsHeight
; y
++)
389 free(iconsRowPtrs
[y
]);
395 main(int argc
, char* argv
[])
399 printf("\t%s <splash.png> <x placement in %%> <y placement in %%> "
400 "<icons.png> <x placement in %%> <y placement in %%> <images.h>\n",
405 int logoPlacementX
= atoi(argv
[2]);
406 int logoPlacementY
= atoi(argv
[3]);
407 int iconPlacementX
= atoi(argv
[5]);
408 int iconPlacementY
= atoi(argv
[6]);
409 if (logoPlacementX
< 0 || logoPlacementX
> 100) {
410 printf("Bad X placement for logo: %d%%\n", logoPlacementX
);
413 if (logoPlacementY
< 0 || logoPlacementY
> 100) {
414 printf("Bad Y placement for logo: %d%%\n\n", logoPlacementY
);
417 if (iconPlacementX
< 0 || iconPlacementX
> 100) {
418 printf("Bad X placement for icons: %d%%\n", iconPlacementX
);
421 if (iconPlacementY
< 0 || iconPlacementY
> 100) {
422 printf("Bad Y placement for icons: %d%%\n", iconPlacementY
);
426 const char* headerFileName
= argv
[7];
427 sOutput
= fopen(headerFileName
, "wb");
429 error("Could not open file \"%s\" for writing", headerFileName
);
431 fputs("// This file was generated by the generate_boot_screen Haiku build "
432 "tool.\n\n", sOutput
);
434 fprintf(sOutput
, "static const int32 kSplashLogoPlacementX = %d;\n",
436 fprintf(sOutput
, "static const int32 kSplashLogoPlacementY = %d;\n",
438 fprintf(sOutput
, "static const int32 kSplashIconsPlacementX = %d;\n",
440 fprintf(sOutput
, "static const int32 kSplashIconsPlacementY = %d;\n\n",
443 parse_images(argv
[1], "kSplashLogo", argv
[4], "kSplashIcons");