Patch by Philippe Saint-Pierre:

* Revised the RLE algorithm used for compressing the boot splash artwork to
  handle uncompressed runs.
* Compress the RGB channels separately for improved efficiency.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24844 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-04-06 20:10:50 +00:00
parent 5c21588c29
commit 636bff2fd2
3 changed files with 8947 additions and 5510 deletions
File diff suppressed because it is too large Load Diff
+38 -11
View File
@@ -1,5 +1,7 @@
/* /*
* Copyright 2004-2008, Axel Dörfler, [email protected]. * Copyright 2004-2008, Axel Dörfler, [email protected].
* Copyright 2008, Stephan Aßmus <[email protected]>
* Copyright 2008, Philippe Saint-Pierre <[email protected]>
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -796,14 +798,41 @@ blit_image(const uint8 *data, const uint8* indexedData, uint16 width,
static void static void
uncompress_RLE(const RLE_element *compressed, uint8 *uncompressed, uint32 size) uncompress_RLE(const uint8 compressed[], uint8 *uncompressed)
{ {
uint32 cursor = 0; uint32 cursorUncompressed = 0;
for (uint32 i = 0; i < size; i++) { uint32 cursorCompressed = 0;
memset(uncompressed + cursor, compressed[i].colorComponent, uint8 count = 0;
compressed[i].count); uint8 item = 0;
cursor += compressed[i].count; int i = 0;
} for (uint8 c = 0; c < 3; c++) {
// for Red channel, then Green, then finally Blue...
cursorUncompressed = c;
while (compressed[cursorCompressed]) {
// at the end of the channel there is a terminating 0,
// so the loop will end... (ref: generate_boot_screen.cpp)
count = compressed[cursorCompressed++];
if (count < 128) {
// regular run, repeat "item" "count" times...
item = compressed[cursorCompressed++];
for (i = count - 1; i >= 0; --i) {
uncompressed[cursorUncompressed] = item;
cursorUncompressed += 3;
}
} else {
// enumeration, just write the next "count" items as is...
count = count - 128;
for (i = count - 1; i >= 0; --i) {
uncompressed[cursorUncompressed]
= compressed[cursorCompressed++];
cursorUncompressed += 3;
}
}
}
// the current position of compressed[cursor] is the end of channel,
// we skip it...
cursorCompressed++;
}
} }
// #pragma mark - // #pragma mark -
@@ -884,8 +913,7 @@ fallback:
* kSplashLogoHeight * 3); * kSplashLogoHeight * 3);
if (uncompressedLogo == NULL) if (uncompressedLogo == NULL)
return; return;
uncompress_RLE(kSplashLogoCompressedImage, uncompressedLogo, uncompress_RLE(kSplashLogoCompressedImage, uncompressedLogo);
kSplashLogoSize);
// TODO: support indexed versions of the images! // TODO: support indexed versions of the images!
@@ -911,8 +939,7 @@ fallback:
* kSplashIconsHeight * 3); * kSplashIconsHeight * 3);
if (gKernelArgs.boot_splash == NULL) if (gKernelArgs.boot_splash == NULL)
return; return;
uncompress_RLE(kSplashIconsCompressedImage, gKernelArgs.boot_splash, uncompress_RLE(kSplashIconsCompressedImage, gKernelArgs.boot_splash );
kSplashIconsSize);
// render initial (grayed out) icons // render initial (grayed out) icons
// the grayed out version is the lower half of the icons image // the grayed out version is the lower half of the icons image
+121 -48
View File
@@ -5,6 +5,7 @@
* Authors: * Authors:
* Artur Wyszynski <[email protected]> * Artur Wyszynski <[email protected]>
* Stephan Aßmus <[email protected]> * Stephan Aßmus <[email protected]>
* Philippe Saint-Pierre <[email protected]>
*/ */
//! Haiku boot splash image generator/converter //! Haiku boot splash image generator/converter
@@ -13,6 +14,7 @@
#include <png.h> #include <png.h>
#include <string> #include <string>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
// TODO: Generate the single optimal palette for all three images, // TODO: Generate the single optimal palette for all three images,
// store palette versions of these images as well, so that they are // store palette versions of these images as well, so that they are
@@ -21,7 +23,7 @@
FILE* sOutput = NULL; FILE* sOutput = NULL;
int sOffset = 0;
static void static void
error(const char *s, ...) error(const char *s, ...)
@@ -50,33 +52,33 @@ private:
static void static void
readPNG(const char* filename, int& width, int& height, png_bytep*& rowPtrs, read_png(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
png_structp& pngPtr, png_infop& infoPtr) png_structp& pngPtr, png_infop& infoPtr)
{ {
char header[8]; char header[8];
FILE* input = fopen(filename, "rb"); FILE* input = fopen(filename, "rb");
if (!input) if (!input)
error("[readPNG] File %s could not be opened for reading", filename); error("[read_png] File %s could not be opened for reading", filename);
AutoFileCloser _(input); AutoFileCloser _(input);
fread(header, 1, 8, input); fread(header, 1, 8, input);
if (png_sig_cmp((png_byte *)header, 0, 8 )) if (png_sig_cmp((png_byte *)header, 0, 8 ))
error("[readPNG] File %s is not recognized as a PNG file", filename); error("[read_png] File %s is not recognized as a PNG file", filename);
pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL); NULL, NULL, NULL);
if (!pngPtr) if (!pngPtr)
error("[readPNG] png_create_read_struct failed"); error("[read_png] png_create_read_struct failed");
infoPtr = png_create_info_struct(pngPtr); infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) if (!infoPtr)
error("[readPNG] png_create_info_struct failed"); error("[read_png] png_create_info_struct failed");
// TODO: I don't know which version of libpng introduced this feature: // TODO: I don't know which version of libpng introduced this feature:
#if PNG_LIBPNG_VER > 10005 #if PNG_LIBPNG_VER > 10005
if (setjmp(png_jmpbuf(pngPtr))) if (setjmp(png_jmpbuf(pngPtr)))
error("[readPNG] Error during init_io"); error("[read_png] Error during init_io");
#endif #endif
png_init_io(pngPtr, input); png_init_io(pngPtr, input);
@@ -96,9 +98,9 @@ readPNG(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
width = infoPtr->width; width = infoPtr->width;
height = infoPtr->height; height = infoPtr->height;
if (infoPtr->bit_depth != 8) if (infoPtr->bit_depth != 8)
error("[readPNG] File %s has wrong bit depth\n", filename); error("[read_png] File %s has wrong bit depth\n", filename);
if ((int)infoPtr->rowbytes < width * 3) { if ((int)infoPtr->rowbytes < width * 3) {
error("[readPNG] File %s has wrong color type (RGB required)\n", error("[read_png] File %s has wrong color type (RGB required)\n",
filename); filename);
} }
@@ -108,7 +110,7 @@ readPNG(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
#if PNG_LIBPNG_VER > 10005 #if PNG_LIBPNG_VER > 10005
if (setjmp(png_jmpbuf(pngPtr))) if (setjmp(png_jmpbuf(pngPtr)))
error("[readPNG] Error during read_image"); error("[read_png] Error during read_image");
#endif #endif
rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height); rowPtrs = (png_bytep*)malloc(sizeof(png_bytep) * height);
@@ -120,60 +122,134 @@ readPNG(const char* filename, int& width, int& height, png_bytep*& rowPtrs,
static void static void
writeHeader(const char* baseName, int width, int height, png_bytep* rowPtrs) new_line_if_required()
{
sOffset++;
if (sOffset % 12 == 0)
fprintf(sOutput, "\n\t");
}
static void
write_image(const char* baseName, int width, int height, png_bytep* rowPtrs)
{ {
fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width); fprintf(sOutput, "static const uint16 %sWidth = %d;\n", baseName, width);
fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height); fprintf(sOutput, "static const uint16 %sHeight = %d;\n", baseName, height);
fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n"); fprintf(sOutput, "#ifndef __BOOTSPLASH_KERNEL__\n");
fprintf(sOutput, "static const RLE_element %sCompressedImage[] = {\n\t",
int buffer[128];
// buffer[0] stores count, buffer[1..127] holds the actual values
fprintf(sOutput, "static uint8 %sCompressedImage[] = {\n\t",
baseName); baseName);
int offset = 0; for (int c = 0; c < 3; c++) {
int lastColor = -1; // for each component i.e. R, G, B ...
int counter = 0; // NOTE : I don't care much about performance at this step,
for (int y = 0; y < height; y++) { // decoding however...
png_byte* row = rowPtrs[y]; int currentValue = rowPtrs[0][c];
for (int x = 0; x < width * 3; x++) { int count = 0;
if (lastColor == row[x]) {
// if the currentColor is already being counted...
counter++;
} else {
// otherwise display what we had in memory, record that color
// and reset the counter...
if (lastColor != -1) {
offset++;
fprintf(sOutput, "{%d, 0x%02x}, ", counter, lastColor);
if (offset % 6 == 0) {
fprintf(sOutput, "\n\t");
}
}
lastColor = row[x];
counter = 1;
}
if (x == width * 3 - 1 && y == height - 1) { // When bufferActive == true, we store the number rather than writing
// we have reach the end // them directly; we use this to store numbers until we find a pair..
fprintf(sOutput, "{%d, 0x%02x}\n};\n\n", counter, row[x]); bool bufferActive = false;
offset++;
break; sOffset = 0;
for (int y = 0; y < height; y++) {
png_byte* row = rowPtrs[y];
for (int x = c; x < width * 3; x += 3) {
if (row[x] == currentValue) {
if (bufferActive) {
bufferActive = false;
count = 2;
if (buffer[0] > 1) {
fprintf (sOutput, "%d, ",
128 + buffer[0] - 1);
new_line_if_required();
for (int i = 1; i < buffer[0] ; i++) {
fprintf( sOutput, "%d, ",
buffer[i] );
new_line_if_required();
}
}
} else {
count++;
if (count == 127) {
fprintf(sOutput, "127, ");
new_line_if_required();
fprintf(sOutput, "%d, ", currentValue);
new_line_if_required();
count = 0;
}
}
} else {
if (bufferActive) {
if (buffer[0] == 127) {
// we don't have enough room,
// flush the buffer
fprintf(sOutput, "%d, ",
128 + buffer[0] - 1);
new_line_if_required();
for (int i = 1; i < buffer[0]; i++) {
fprintf(sOutput, "%d, ", buffer[i]);
new_line_if_required();
}
buffer[0] = 0;
}
buffer[0]++;
buffer[buffer[0]] = row[x];
} else if (count > 0) {
buffer[0] = 1;
buffer[1] = row[x];
bufferActive = true;
if (count > 1) {
fprintf(sOutput, "%d, ", count);
new_line_if_required();
fprintf(sOutput, "%d, ", currentValue);
new_line_if_required();
}
}
currentValue = row[x];
}
} }
} }
if (bufferActive) {
// I could have written 127 + buffer[0],
// but I think this is more readable...
fprintf(sOutput, "%d, ", 128 + buffer[0] - 1);
new_line_if_required();
for (int i = 1; i < buffer[0] ; i++) {
fprintf(sOutput, "%d, ", buffer[i]);
new_line_if_required();
}
} else {
fprintf(sOutput, "%d, %d, ", count, currentValue);
new_line_if_required();
}
// we put a terminating zero for the next byte that indicates
// a "count", just to indicate the end of the channel
fprintf(sOutput, "0");
if (c != 2)
fprintf(sOutput, ",");
fprintf(sOutput, "\n\t");
} }
fprintf(sOutput, "static const uint32 %sSize = %d;\n", baseName, offset); fprintf(sOutput, "};\n");
fprintf(sOutput, "#endif\n\n"); fprintf(sOutput, "#endif\n\n");
} }
static void static void
parseImage(const char* filename, const char* baseName) parse_image(const char* filename, const char* baseName)
{ {
int width; int width;
int height; int height;
png_bytep* rowPtrs = NULL; png_bytep* rowPtrs = NULL;
png_structp pngPtr; png_structp pngPtr;
png_infop infoPtr; png_infop infoPtr;
readPNG(filename, width, height, rowPtrs, pngPtr, infoPtr); read_png(filename, width, height, rowPtrs, pngPtr, infoPtr);
writeHeader(baseName, width, height, rowPtrs); write_image(baseName, width, height, rowPtrs);
// free resources // free resources
png_destroy_read_struct(&pngPtr, &infoPtr, NULL); png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
@@ -232,11 +308,8 @@ main(int argc, char* argv[])
fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n", fprintf(sOutput, "static const int32 kSplashIconsPlacementY = %d;\n\n",
iconPlacementY); iconPlacementY);
fprintf(sOutput, "struct RLE_element \n{\n\tuint16 count; \n\tuint8 " parse_image(argv[1], "kSplashLogo");
"colorComponent;\n};\n\n"); parse_image(argv[4], "kSplashIcons");
parseImage(argv[1], "kSplashLogo");
parseImage(argv[4], "kSplashIcons");
fclose(sOutput); fclose(sOutput);
return 0; return 0;