[escher] Compress images

This commit is contained in:
Romain Goyet
2018-10-10 09:57:18 +02:00
committed by LeaNumworks
parent fc7fa4b152
commit f4f567814e
14 changed files with 81 additions and 33 deletions

View File

@@ -86,9 +86,9 @@ objs += $(addprefix escher/src/,\
INLINER := escher/image/inliner
$(INLINER): escher/image/inliner.c
$(INLINER): escher/image/inliner.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c)
@echo "HOSTCC $@"
$(Q) $(HOSTCC) -std=c99 `libpng-config --cflags` $< `libpng-config --ldflags` -o $@
$(Q) $(HOSTCC) -std=c99 `libpng-config --cflags` $^ `libpng-config --ldflags` -o $@
%.h %.cpp : %.png $(INLINER)
@echo "INLINER $@"

View File

@@ -13,6 +13,7 @@
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include "../../ion/src/external/lz4/lz4hc.h"
#define ERROR_IF(cond, message) if (cond) { printf(message); return -1; };
#define MAX_FILENAME_LENGTH 255
@@ -146,18 +147,13 @@ void generateHeaderFromImage(FILE * file, const char * guardian, const char * va
}
void generateImplementationFromImage(FILE * file, const char * header, const char * variable, uint32_t width, uint32_t height, png_bytep * pixelsRowPointers) {
fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
fprintf(file, "#include \"%s.h\"\n\n", header);
fprintf(file, "#define P(c) KDColor::RGB24(c)\n\n");
fprintf(file, "const KDColor pixels[] = {");
int sizeOfPixelBuffer = width*height*sizeof(uint16_t);
uint16_t * pixelBuffer = (uint16_t *)malloc(sizeOfPixelBuffer);
for (int j=0; j<height; j++) {
png_bytep pixelRow = pixelsRowPointers[j];
for (int i=0; i<width; i++) {
if ((i+j*width) % 6 == 0) {
fprintf(file, "\n ");
} else {
fprintf(file, " ");
}
png_bytep pixel = &(pixelRow[i*4]);
double red = pixel[0]/255.0;
double green = pixel[1]/255.0;
@@ -167,13 +163,39 @@ void generateImplementationFromImage(FILE * file, const char * header, const cha
double blendedRed = red*alpha + 1.0*(1.0-alpha);
double blendedGreen = green*alpha + 1.0*(1.0-alpha);
double blendedBlue = blue*alpha + 1.0*(1.0-alpha);
fprintf(file, "P(0x%02X%02X%02X)", (int)(blendedRed*0xFF), (int)(blendedGreen*0xFF), (int)(blendedBlue*0xFF));
if (i!=width-1 || j!= height-1) {
fprintf(file, ",");
}
uint8_t intRed = blendedRed*0xFF;
uint8_t intGreen = blendedGreen*0xFF;
uint8_t intBlue = blendedBlue*0xFF;
uint16_t rgb565value = (intRed>>3)<<11 | (intGreen>>2) << 5 | (intBlue>>3);
pixelBuffer[j*width+i] = rgb565value;
}
}
uint8_t * compressedPixelBuffer = malloc(sizeOfPixelBuffer);
int sizeOfCompressedPixelBuffer = LZ4_compress_HC(
pixelBuffer,
compressedPixelBuffer,
sizeOfPixelBuffer,
sizeOfCompressedPixelBuffer,
LZ4HC_CLEVEL_MAX
);
fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
fprintf(file, "#include \"%s.h\"\n\n", header);
fprintf(file, "// Compressed %d pixels into %d bytes (%.2f%% compression ratio)/\n", width*height, sizeOfCompressedPixelBuffer, 100.0*sizeOfCompressedPixelBuffer/sizeOfPixelBuffer);
fprintf(file, "const uint8_t compressedPixelData[%d] = {", sizeOfCompressedPixelBuffer);
for (int i=0; i<sizeOfCompressedPixelBuffer; i++) {
fprintf(file, "0x%04x, ", compressedPixelBuffer[i]);
}
free(compressedPixelBuffer);
free(pixelBuffer);
fprintf(file, "\n};\n\n");
fprintf(file, "constexpr Image image = Image(%d, %d, pixels);\n\n", width, height);
fprintf(file, "constexpr Image image = Image(%d, %d, compressedPixelData, %d);\n\n", width, height, sizeOfCompressedPixelBuffer);
fprintf(file, "const Image * ImageStore::%s = &image;\n", variable);
}

View File

@@ -5,15 +5,17 @@
class Image {
public:
constexpr Image(KDCoordinate width, KDCoordinate height, const KDColor * pixels) :
m_width(width), m_height(height), m_pixels(pixels) {}
constexpr Image(KDCoordinate width, KDCoordinate height, const uint8_t * compressedPixelData, uint16_t compressedPixelDataSize) :
m_width(width), m_height(height), m_compressedPixelData(compressedPixelData), m_compressedPixelDataSize(compressedPixelDataSize) {}
KDCoordinate width() const { return m_width; }
KDCoordinate height() const { return m_height; }
const KDColor * pixels() const { return m_pixels; }
const uint8_t * compressedPixelData() const { return m_compressedPixelData; }
uint16_t compressedPixelDataSize() const { return m_compressedPixelDataSize; }
private:
KDCoordinate m_width;
KDCoordinate m_height;
const KDColor * m_pixels;
const uint8_t * m_compressedPixelData;
uint16_t m_compressedPixelDataSize;
};
#endif

View File

@@ -2,6 +2,7 @@
extern "C" {
#include <assert.h>
}
#include <ion.h>
ImageView::ImageView() :
View(),
@@ -9,16 +10,30 @@ ImageView::ImageView() :
{
}
constexpr static int maxPixelBufferSize = 4000;
// Icon file is 55 x 56 = 3080
// Boot logo file is 188 x 21 = 3948
void ImageView::drawRect(KDContext * ctx, KDRect rect) const {
if (m_image == nullptr) {
return;
}
assert(bounds().width() == m_image->width());
assert(bounds().height() == m_image->height());
ctx->fillRectWithPixels(bounds(), m_image->pixels(), nullptr);
// Image is 55*56 pixels 3K pixels = 6K bytes
KDColor pixelBuffer[maxPixelBufferSize];
int pixelBufferSize = m_image->width() * m_image->height();
// CAUTION: That's a VERY big buffer we're allocating on the stack
assert(pixelBufferSize <= maxPixelBufferSize);
Ion::decompress(
m_image->compressedPixelData(),
reinterpret_cast<uint8_t *>(pixelBuffer),
m_image->compressedPixelDataSize(),
pixelBufferSize * sizeof(KDColor)
);
ctx->fillRectWithPixels(bounds(), pixelBuffer, nullptr);
}
void ImageView::setImage(const Image * image) {

View File

@@ -20,8 +20,11 @@ objs += $(addprefix ion/src/shared/, \
events.o \
platform_info.o \
storage.o \
decompress.o \
)
objs += ion/src/external/lz4/lz4.o
tests += $(addprefix ion/test/,\
crc32.cpp\
events.cpp\

View File

@@ -40,6 +40,9 @@ uint32_t crc32(const uint32_t * data, size_t length);
// Provides a true random number
uint32_t random();
// Decompress data
void decompress(const uint8_t * src, uint8_t * dst, int srcSize, int dstSize);
}
#endif

View File

@@ -0,0 +1,8 @@
#include <ion.h>
#include "../external/lz4/lz4.h"
void Ion::decompress(const uint8_t * src, uint8_t * dst, int srcSize, int dstSize) {
int outputSize = LZ4_decompress_safe(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), srcSize, dstSize);
(void)outputSize; // Make the compiler happy if assertions are disabled
assert(outputSize == dstSize);
}

View File

@@ -16,8 +16,6 @@ objs += $(addprefix kandinsky/src/,\
rect.o\
)
objs += kandinsky/src/external/lz4/lz4.o
tests += $(addprefix kandinsky/test/,\
color.cpp\
rect.cpp\
@@ -51,7 +49,7 @@ kandinsky/src/font_large.cpp: kandinsky/fonts/rasterizer
@echo "RASTER $(large_font_files)"
$(Q) $< kandinsky/fonts/LargeSourcePixel.ttf 16 16 LargeFont $(large_font_files)
kandinsky/fonts/rasterizer: kandinsky/fonts/rasterizer.c kandinsky/fonts/unicode_for_symbol.c kandinsky/src/external/lz4/lz4.c kandinsky/src/external/lz4/lz4hc.c
kandinsky/fonts/rasterizer: kandinsky/fonts/rasterizer.c kandinsky/fonts/unicode_for_symbol.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c)
@echo "HOSTCC $@"
$(Q) $(HOSTCC) $(RASTERIZER_CFLAGS) $^ $(RASTERIZER_LDFLAGS) -o $@

View File

@@ -16,7 +16,7 @@
#include FT_FREETYPE_H
#include "unicode_for_symbol.h"
#include "../src/external/lz4/lz4hc.h"
#include "../../ion/src/external/lz4/lz4hc.h"
#define ENSURE(action, description...) { if (!(action)) { fprintf(stderr, "Error: "); fprintf(stderr, description); fprintf(stderr, "\n"); exit(-1);}}

View File

@@ -1,6 +1,6 @@
#include <assert.h>
#include <kandinsky/font.h>
#include "external/lz4/lz4.h"
#include <ion.h>
constexpr static int k_tabCharacterWidth = 4;
@@ -24,15 +24,12 @@ KDSize KDFont::stringSize(const char * text) const {
}
void KDFont::fetchGreyscaleGlyphForChar(char c, uint8_t * greyscaleBuffer) const {
//TODO: If debug, use LZ4_decompress_safe, otherwise LZ4_decompress_fast
int resultSize = LZ4_decompress_safe(
reinterpret_cast<const char *>(compressedGlyphData(c)),
reinterpret_cast<char *>(greyscaleBuffer),
Ion::decompress(
compressedGlyphData(c),
greyscaleBuffer,
compressedGlyphDataSize(c),
m_glyphSize.width() * m_glyphSize.height() * k_bitsPerPixel/8
);
(void)resultSize; // Silence the "variable unused" warning if assertions are not enabled
assert(resultSize == m_glyphSize.width() * m_glyphSize.height() * k_bitsPerPixel/8);
}
void KDFont::fetchGlyphForChar(char c, const KDFont::RenderPalette * renderPalette, KDColor * pixelBuffer) const {