Before we add more rooms, some rudimentary image compression - decreases executable size (therefore time to upload to the pinetab) about 10x
parent
785642a2c9
commit
a04abd101a
13
Makefile
13
Makefile
|
@ -1,6 +1,6 @@
|
||||||
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
OBJS = pinetab2_framework.o game.o textbox.o inventory.o
|
OBJS = pinetab2_framework.o game.o textbox.o inventory.o decompress_sprite.o
|
||||||
SPRITES = $(patsubst $(PROJECT_ROOT)sprites/%.xcf,%,$(wildcard $(PROJECT_ROOT)sprites/*.xcf))
|
SPRITES = $(patsubst $(PROJECT_ROOT)sprites/%.xcf,%,$(wildcard $(PROJECT_ROOT)sprites/*.xcf))
|
||||||
NAVMESHES = $(patsubst $(PROJECT_ROOT)navmesh/%.tmx,%,$(wildcard $(PROJECT_ROOT)navmesh/*.tmx))
|
NAVMESHES = $(patsubst $(PROJECT_ROOT)navmesh/%.tmx,%,$(wildcard $(PROJECT_ROOT)navmesh/*.tmx))
|
||||||
|
|
||||||
|
@ -44,15 +44,8 @@ pinetab2_framework: $(OBJS)
|
||||||
clean:
|
clean:
|
||||||
rm -fr pinetab2_framework $(OBJS) $(EXTRA_CLEAN)
|
rm -fr pinetab2_framework $(OBJS) $(EXTRA_CLEAN)
|
||||||
|
|
||||||
sprite_%.o: $(PROJECT_ROOT)sprites/%.xcf
|
sprite_%.o: %.png $(PROJECT_ROOT)compress_sprite.py
|
||||||
gimp -in -b '(let ((image (car (gimp-xcf-load 0 "$<" "$(notdir $<)")))) ;\
|
python3 "$(PROJECT_ROOT)compress_sprite.py" "$<" $(patsubst %.o,%.raw,$@)
|
||||||
;(gimp-image-scale image 1280 800) ;\
|
|
||||||
(gimp-image-rotate image ROTATE-90) ;\
|
|
||||||
(let ((layer (car (gimp-image-merge-visible-layers image CLIP-TO-IMAGE)))) ;\
|
|
||||||
(plug-in-colors-channel-mixer RUN-NONINTERACTIVE image layer 0 0 0 1 0 1 0 1 0 0) ; swap red and blue channels \
|
|
||||||
(file-raw-save 1 image layer "$(patsubst %.o,%.raw,$@)" "$(patsubst %.o,%.raw,$@)") ;\
|
|
||||||
) ;\
|
|
||||||
)' -b '(gimp-quit 0)'
|
|
||||||
$(OBJCOPY) -I binary --rename-section .data=.rodata,alloc,load,readonly,data,contents $(patsubst %.o,%.raw,$@) $@
|
$(OBJCOPY) -I binary --rename-section .data=.rodata,alloc,load,readonly,data,contents $(patsubst %.o,%.raw,$@) $@
|
||||||
rm $(patsubst %.o,%.raw,$@)
|
rm $(patsubst %.o,%.raw,$@)
|
||||||
@# symbols are _binary_sprite_%_raw_start / _end / _size
|
@# symbols are _binary_sprite_%_raw_start / _end / _size
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
import PIL.Image
|
||||||
|
_, inpath, outpath = sys.argv
|
||||||
|
|
||||||
|
image = PIL.Image.open(inpath)
|
||||||
|
assert image.mode == "RGBA", "image mode is "+image.mode
|
||||||
|
|
||||||
|
# We measure width and height prior to the rotation
|
||||||
|
width, height = image.width, image.height
|
||||||
|
|
||||||
|
image = image.transpose(PIL.Image.Transpose.ROTATE_270)
|
||||||
|
|
||||||
|
with open(outpath, "wb") as out:
|
||||||
|
out.write(struct.pack("=HH", width, height))
|
||||||
|
for channel in "BGRA":
|
||||||
|
pixelbytes = image.getchannel(channel).tobytes()
|
||||||
|
def rlegroups():
|
||||||
|
curpixel=None
|
||||||
|
count=0
|
||||||
|
litrun = b""
|
||||||
|
for pixel in pixelbytes:
|
||||||
|
if curpixel is None:
|
||||||
|
curpixel = pixel
|
||||||
|
count = 1
|
||||||
|
elif curpixel != pixel:
|
||||||
|
assert count > 0
|
||||||
|
if count <= 2:
|
||||||
|
if len(litrun)+count > 127:
|
||||||
|
assert len(litrun) > 0
|
||||||
|
yield litrun
|
||||||
|
litrun = b""
|
||||||
|
litrun += bytes([curpixel]) * count
|
||||||
|
else:
|
||||||
|
if litrun != b"":
|
||||||
|
assert len(litrun) > 0
|
||||||
|
yield litrun
|
||||||
|
litrun = b""
|
||||||
|
yield (curpixel, count)
|
||||||
|
curpixel = pixel
|
||||||
|
count = 1
|
||||||
|
else:
|
||||||
|
count += 1
|
||||||
|
if litrun != b"":
|
||||||
|
assert len(litrun) > 0
|
||||||
|
yield litrun
|
||||||
|
if count > 0:
|
||||||
|
yield (curpixel, count)
|
||||||
|
for group in rlegroups():
|
||||||
|
if type(group) == bytes:
|
||||||
|
out.write(struct.pack("=B", len(group)+128) + group)
|
||||||
|
else:
|
||||||
|
pixel, count = group
|
||||||
|
assert count > 0
|
||||||
|
if count >= 128:
|
||||||
|
out.write(struct.pack("=BIB", 0, count, pixel))
|
||||||
|
else:
|
||||||
|
out.write(struct.pack("=BB", count, pixel))
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <error.h>
|
||||||
|
#include "engine.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// TODO: even better compression using zlib or something - or vector graphics
|
||||||
|
|
||||||
|
#define MAX_SPRITES 5000
|
||||||
|
static struct sprite sprites[MAX_SPRITES];
|
||||||
|
static int numsprites;
|
||||||
|
|
||||||
|
struct sprite *get_decompressed_sprite(const unsigned char *spritedata) {
|
||||||
|
for(int i = 0; i < numsprites; i++) {
|
||||||
|
if(sprites[i].original_data_source == spritedata)
|
||||||
|
return &sprites[i];
|
||||||
|
}
|
||||||
|
struct sprite *s = &sprites[numsprites++];
|
||||||
|
s->width = *(uint16_t*)(spritedata + 0);
|
||||||
|
s->height = *(uint16_t*)(spritedata + 2);
|
||||||
|
s->original_data_source = spritedata;
|
||||||
|
spritedata += 4;
|
||||||
|
|
||||||
|
s->pixels = (uint32_t*)malloc(s->width*s->height*4);
|
||||||
|
if(!s->pixels) error(1, 0, "get_decompressed_sprite: memory allocation failed");
|
||||||
|
|
||||||
|
for(int channel = 0; channel < 4; channel++) {
|
||||||
|
unsigned char *pixels = (unsigned char*)s->pixels;
|
||||||
|
pixels += channel;
|
||||||
|
unsigned int pixelsleft = s->width * s->height;
|
||||||
|
while(pixelsleft > 0) {
|
||||||
|
unsigned char control = *spritedata++;
|
||||||
|
if(control & 0x80) {
|
||||||
|
control &= 0x7f;
|
||||||
|
if(control > pixelsleft) error(1, 0, "corrupted compressed sprite A %u > %u", (unsigned int)control, (unsigned int)pixelsleft);
|
||||||
|
for(int i = 0; i < control; i++) {
|
||||||
|
*pixels = *spritedata++;
|
||||||
|
pixels += 4;
|
||||||
|
}
|
||||||
|
pixelsleft -= control;
|
||||||
|
} else {
|
||||||
|
uint32_t count;
|
||||||
|
if(control == 0) {
|
||||||
|
count = *(uint32_t*)spritedata;
|
||||||
|
spritedata += 4;
|
||||||
|
} else {
|
||||||
|
count = control;
|
||||||
|
}
|
||||||
|
unsigned char value = *spritedata++;
|
||||||
|
if(count > pixelsleft) error(1, 0, "corrupted compressed sprite B %u > %u", (unsigned int)count, (unsigned int)pixelsleft);
|
||||||
|
for(uint32_t i = 0; i < count; i++) {
|
||||||
|
*pixels = value;
|
||||||
|
pixels += 4;
|
||||||
|
}
|
||||||
|
pixelsleft -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
7
engine.h
7
engine.h
|
@ -83,6 +83,13 @@ void push_scene_textbox(const char *text);
|
||||||
void create_standard_inventory(scene *s);
|
void create_standard_inventory(scene *s);
|
||||||
void standard_inventory_onclick(struct object *obj);
|
void standard_inventory_onclick(struct object *obj);
|
||||||
|
|
||||||
|
struct sprite {
|
||||||
|
int width, height;
|
||||||
|
uint32_t *pixels;
|
||||||
|
const unsigned char *original_data_source;
|
||||||
|
};
|
||||||
|
struct sprite *get_decompressed_sprite(const unsigned char *spritedata);
|
||||||
|
|
||||||
// used during rendering
|
// used during rendering
|
||||||
// pixel is 0xRRGGBB
|
// pixel is 0xRRGGBB
|
||||||
extern uint32_t *curfb;
|
extern uint32_t *curfb;
|
||||||
|
|
|
@ -221,7 +221,7 @@ struct object *scene_add_object(struct scene *sc, int id, int x, int y, int widt
|
||||||
objects[i].y = y;
|
objects[i].y = y;
|
||||||
objects[i].width = width;
|
objects[i].width = width;
|
||||||
objects[i].height = height;
|
objects[i].height = height;
|
||||||
objects[i].pixels = pixels;
|
objects[i].pixels = pixels ? (const char*)get_decompressed_sprite((const unsigned char*)pixels)->pixels : nullptr;
|
||||||
return &objects[i];
|
return &objects[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ static const char *textbox_text;
|
||||||
static int tb_width_chars, tb_height_chars;
|
static int tb_width_chars, tb_height_chars;
|
||||||
static int visible_chars = 0;
|
static int visible_chars = 0;
|
||||||
|
|
||||||
extern const char _binary_sprite_font_raw_start[];
|
extern const unsigned char _binary_sprite_font_raw_start[];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ extern const char _binary_sprite_font_raw_start[];
|
||||||
#define LINESPACE 8
|
#define LINESPACE 8
|
||||||
|
|
||||||
static void textbox_render_fn(struct scene *s) {
|
static void textbox_render_fn(struct scene *s) {
|
||||||
|
|
||||||
|
struct sprite *fontsprite = get_decompressed_sprite(_binary_sprite_font_raw_start);
|
||||||
|
|
||||||
int boxheight = tb_height_chars*(CHARSIZE + LINESPACE) - LINESPACE;
|
int boxheight = tb_height_chars*(CHARSIZE + LINESPACE) - LINESPACE;
|
||||||
int boxwidth = tb_width_chars*(CHARSIZE + HSPACE) - HSPACE;
|
int boxwidth = tb_width_chars*(CHARSIZE + HSPACE) - HSPACE;
|
||||||
int x = (1280 - tb_width_chars*CHARSIZE)/2;
|
int x = (1280 - tb_width_chars*CHARSIZE)/2;
|
||||||
|
@ -31,7 +34,7 @@ static void textbox_render_fn(struct scene *s) {
|
||||||
x = xleft;
|
x = xleft;
|
||||||
y += CHARSIZE + LINESPACE;
|
y += CHARSIZE + LINESPACE;
|
||||||
} else {
|
} else {
|
||||||
blit(x, y, CHARSIZE, CHARSIZE, (*str - 32)*(CHARSIZE*CHARSIZE) + (uint32_t*)_binary_sprite_font_raw_start);
|
blit(x, y, CHARSIZE, CHARSIZE, (*str - 32)*(CHARSIZE*CHARSIZE) + (uint32_t*)fontsprite->pixels);
|
||||||
x += CHARSIZE + HSPACE;
|
x += CHARSIZE + HSPACE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue