mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[escher] Compress images
This commit is contained in:
committed by
LeaNumworks
parent
fc7fa4b152
commit
f4f567814e
@@ -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 $@"
|
||||
|
||||
@@ -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 = ℑ\n", variable);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -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
|
||||
|
||||
8
ion/src/shared/decompress.cpp
Normal file
8
ion/src/shared/decompress.cpp
Normal 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);
|
||||
}
|
||||
@@ -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 $@
|
||||
|
||||
|
||||
@@ -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);}}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user