aseprite importer
This commit is contained in:
parent
0256f4cd15
commit
d52b15e1be
12
Makefile
12
Makefile
|
@ -17,6 +17,9 @@ LD = $(CC)
|
||||||
ifeq ($(CC), clang)
|
ifeq ($(CC), clang)
|
||||||
AR = llvm-ar
|
AR = llvm-ar
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CC), x86_64-w64-mingw32-gcc)
|
||||||
|
AR = x86_64-w64-mingw32-ar
|
||||||
|
endif
|
||||||
|
|
||||||
ifdef NEDITOR
|
ifdef NEDITOR
|
||||||
CFLAGS += -DNO_EDITOR
|
CFLAGS += -DNO_EDITOR
|
||||||
|
@ -72,6 +75,7 @@ ifeq ($(OS), Windows_NT)
|
||||||
LDFLAGS += -mwin32 -static
|
LDFLAGS += -mwin32 -static
|
||||||
CFLAGS += -mwin32
|
CFLAGS += -mwin32
|
||||||
LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m
|
LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m
|
||||||
|
# LDLIBS += mingw32 kernel32 gdi32 user32 shell32 ws2_32 ole32 winmm setupapi m
|
||||||
EXT = .exe
|
EXT = .exe
|
||||||
PLAT = w64
|
PLAT = w64
|
||||||
PKGCMD = cd $(BIN); zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\*
|
PKGCMD = cd $(BIN); zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\*
|
||||||
|
@ -144,7 +148,7 @@ SHADERS := $(patsubst %.sglsl, %.sglsl.h, $(SHADERS))
|
||||||
install: $(BIN)/$(NAME)
|
install: $(BIN)/$(NAME)
|
||||||
cp -f $(BIN)/$(NAME) $(DESTDIR)
|
cp -f $(BIN)/$(NAME) $(DESTDIR)
|
||||||
|
|
||||||
$(BIN)/$(NAME): $(BIN)/libengine.a $(BIN)/libquickjs.a $(BIN)/libcdb.a
|
$(BIN)/$(NAME): $(BIN)/libengine.a $(BIN)/libquickjs.a
|
||||||
@echo Linking $(NAME)
|
@echo Linking $(NAME)
|
||||||
$(LD) $^ $(LDFLAGS) -L$(BIN) $(LDLIBS) -o $@
|
$(LD) $^ $(LDFLAGS) -L$(BIN) $(LDLIBS) -o $@
|
||||||
cp $(BIN)/$(NAME) .
|
cp $(BIN)/$(NAME) .
|
||||||
|
@ -155,13 +159,13 @@ $(DISTDIR)/$(DIST): $(BIN)/$(NAME)
|
||||||
@mkdir -p $(DISTDIR)
|
@mkdir -p $(DISTDIR)
|
||||||
@$(PKGCMD)
|
@$(PKGCMD)
|
||||||
|
|
||||||
$(BIN)/libengine.a: $(SHADERS) source/engine/core.cdb.h .WAIT $(OBJS)
|
$(BIN)/libengine.a: $(SHADERS) source/engine/core.cdb.h $(OBJS)
|
||||||
@$(AR) rcs $@ $(OBJS)
|
@$(AR) rcs $@ $(OBJS)
|
||||||
|
|
||||||
$(BIN)/libcdb.a:
|
$(BIN)/libcdb.a:
|
||||||
mkdir -p $(BIN)
|
mkdir -p $(BIN)
|
||||||
rm -f $(CDB)/libcdb.a
|
rm -f $(CDB)/libcdb.a
|
||||||
make -C $(CDB) libcdb.a
|
make -C $(CDB) CC=$(CC) AR=$(AR) libcdb.a
|
||||||
cp $(CDB)/libcdb.a $(BIN)
|
cp $(CDB)/libcdb.a $(BIN)
|
||||||
|
|
||||||
$(BIN)/libquickjs.a:
|
$(BIN)/libquickjs.a:
|
||||||
|
@ -211,7 +215,7 @@ jso: tools/jso.c $(BIN)/libquickjs.a
|
||||||
WINCC = x86_64-w64-mingw32-gcc
|
WINCC = x86_64-w64-mingw32-gcc
|
||||||
.PHONY: crosswin
|
.PHONY: crosswin
|
||||||
crosswin:
|
crosswin:
|
||||||
make CC=$(WINCC) OS=Windows_NT
|
gmake CC=$(WINCC) OS=Windows_NT
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@echo Cleaning project
|
@echo Cleaning project
|
||||||
|
|
|
@ -87,6 +87,52 @@ sprite.inputs.kp2 = function() { this.pos = [-0.5,-1]; };
|
||||||
sprite.inputs.kp1 = function() { this.pos = [-1,-1]; };
|
sprite.inputs.kp1 = function() { this.pos = [-1,-1]; };
|
||||||
Object.seal(sprite);
|
Object.seal(sprite);
|
||||||
|
|
||||||
|
var aseframeset2anim = function(frameset, meta)
|
||||||
|
{
|
||||||
|
var anim = {};
|
||||||
|
anim.frames = [];
|
||||||
|
anim.path = meta.image;
|
||||||
|
var dim = meta.size;
|
||||||
|
|
||||||
|
var ase_make_frame = function(ase_frame,i) {
|
||||||
|
var f = ase_frame.frame;
|
||||||
|
var frame = {};
|
||||||
|
frame.rect = {
|
||||||
|
s0: f.x/dim.w,
|
||||||
|
s1: (f.x+f.w)/dim.w,
|
||||||
|
t0: f.y/dim.h,
|
||||||
|
t1: (f.y+f.h)/dim.h
|
||||||
|
};
|
||||||
|
frame.time = ase_frame.duration / 1000;
|
||||||
|
anim.frames.push(frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
frameset.forEach(ase_make_frame);
|
||||||
|
|
||||||
|
anim.loop = true;
|
||||||
|
return anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ase2anim = function(ase)
|
||||||
|
{
|
||||||
|
var json = IO.slurp(ase);
|
||||||
|
json = JSON.parse(json);
|
||||||
|
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
|
||||||
|
return aseframeset2anim(json.frames, json.meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ase2anims = function(ase)
|
||||||
|
{
|
||||||
|
var json = IO.slurp(ase);
|
||||||
|
json = JSON.parse(json);
|
||||||
|
var anims = {};
|
||||||
|
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
|
||||||
|
for (var tag of json.meta.frameTags)
|
||||||
|
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to+1), json.meta);
|
||||||
|
|
||||||
|
return anims;
|
||||||
|
}
|
||||||
|
|
||||||
var gif2anim = function(gif)
|
var gif2anim = function(gif)
|
||||||
{
|
{
|
||||||
var anim = {};
|
var anim = {};
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#define SOKOL_TRACE_HOOKS
|
#define SOKOL_TRACE_HOOKS
|
||||||
#define SOKOL_IMPL
|
#define SOKOL_IMPL
|
||||||
#include "sokol/sokol_glue.h"
|
|
||||||
#include "sokol/sokol_audio.h"
|
#include "sokol/sokol_audio.h"
|
||||||
#include "sokol/sokol_time.h"
|
#include "sokol/sokol_time.h"
|
||||||
#include "sokol/sokol_args.h"
|
#include "sokol/sokol_args.h"
|
||||||
|
|
|
@ -93,5 +93,5 @@ void log_cat(FILE *f) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sg_logging(const char *tag, uint32_t lvl, uint32_t id, const char *msg, uint32_t line, const char *file, void *data) {
|
void sg_logging(const char *tag, uint32_t lvl, uint32_t id, const char *msg, uint32_t line, const char *file, void *data) {
|
||||||
mYughLog(lvl, 1, line, file, "tag: %d, msg: %s", tag, msg);
|
mYughLog(lvl, 1, line, file, "tag: %s, msg: %s", tag, msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,529 +0,0 @@
|
||||||
#include "gifdec.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <io.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
|
||||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
|
||||||
|
|
||||||
typedef struct Entry {
|
|
||||||
uint16_t length;
|
|
||||||
uint16_t prefix;
|
|
||||||
uint8_t suffix;
|
|
||||||
} Entry;
|
|
||||||
|
|
||||||
typedef struct Table {
|
|
||||||
int bulk;
|
|
||||||
int nentries;
|
|
||||||
Entry *entries;
|
|
||||||
} Table;
|
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
read_num(FILE *f)
|
|
||||||
{
|
|
||||||
uint8_t bytes[2];
|
|
||||||
|
|
||||||
fread(bytes, 2, 1, f);
|
|
||||||
return bytes[0] + (((uint16_t) bytes[1]) << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
gd_GIF *gd_open_gif_f(FILE *f)
|
|
||||||
{
|
|
||||||
uint8_t sigver[3];
|
|
||||||
uint16_t width, height, depth;
|
|
||||||
uint8_t fdsz, bgidx, aspect;
|
|
||||||
int i;
|
|
||||||
uint8_t *bgcolor;
|
|
||||||
int gct_sz;
|
|
||||||
gd_GIF *gif;
|
|
||||||
|
|
||||||
/* Header */
|
|
||||||
fread(sigver, 1, 3, f);
|
|
||||||
if (memcmp(sigver, "GIF", 3) != 0) {
|
|
||||||
fprintf(stderr, "invalid signature\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* Version */
|
|
||||||
fread(sigver, 1,3,f);
|
|
||||||
if (memcmp(sigver, "89a", 3) != 0) {
|
|
||||||
fprintf(stderr, "invalid version\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* Width x Height */
|
|
||||||
width = read_num(f);
|
|
||||||
height = read_num(f);
|
|
||||||
/* FDSZ */
|
|
||||||
fread(&fdsz, 1, 1, f);
|
|
||||||
/* Presence of GCT */
|
|
||||||
if (!(fdsz & 0x80)) {
|
|
||||||
fprintf(stderr, "no global color table\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* Color Space's Depth */
|
|
||||||
depth = ((fdsz >> 4) & 7) + 1;
|
|
||||||
/* Ignore Sort Flag. */
|
|
||||||
/* GCT Size */
|
|
||||||
gct_sz = 1 << ((fdsz & 0x07) + 1);
|
|
||||||
/* Background Color Index */
|
|
||||||
fread(&bgidx, 1, 1, f);
|
|
||||||
/* Aspect Ratio */
|
|
||||||
fread(&aspect, 1, 1, f);
|
|
||||||
/* Create gd_GIF Structure. */
|
|
||||||
gif = calloc(1, sizeof(*gif));
|
|
||||||
if (!gif) goto fail;
|
|
||||||
gif->fd = f;
|
|
||||||
gif->width = width;
|
|
||||||
gif->height = height;
|
|
||||||
gif->depth = depth;
|
|
||||||
/* Read GCT */
|
|
||||||
gif->gct.size = gct_sz;
|
|
||||||
fread(gif->gct.colors, gif->gct.size, 3, f);
|
|
||||||
gif->palette = &gif->gct;
|
|
||||||
gif->bgindex = bgidx;
|
|
||||||
gif->frame = calloc(4, width * height);
|
|
||||||
if (!gif->frame) {
|
|
||||||
free(gif);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
gif->canvas = &gif->frame[width * height];
|
|
||||||
if (gif->bgindex)
|
|
||||||
memset(gif->frame, gif->bgindex, gif->width * gif->height);
|
|
||||||
bgcolor = &gif->palette->colors[gif->bgindex*3];
|
|
||||||
if (bgcolor[0] || bgcolor[1] || bgcolor [2])
|
|
||||||
for (i = 0; i < gif->width * gif->height; i++)
|
|
||||||
memcpy(&gif->canvas[i*3], bgcolor, 3);
|
|
||||||
gif->anim_start = ftell(f);
|
|
||||||
goto ok;
|
|
||||||
fail:
|
|
||||||
return NULL;
|
|
||||||
ok:
|
|
||||||
return gif;
|
|
||||||
}
|
|
||||||
|
|
||||||
gd_GIF *
|
|
||||||
gd_open_gif(const char *fname)
|
|
||||||
{
|
|
||||||
FILE *f = fopen(fname, "rb");
|
|
||||||
if (!f) return NULL;
|
|
||||||
return gd_open_gif_f(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
discard_sub_blocks(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
uint8_t size;
|
|
||||||
|
|
||||||
do {
|
|
||||||
fread(&size, 1, 1, gif->fd);
|
|
||||||
fseek(gif->fd, size, SEEK_CUR);
|
|
||||||
} while (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_plain_text_ext(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
if (gif->plain_text) {
|
|
||||||
uint16_t tx, ty, tw, th;
|
|
||||||
uint8_t cw, ch, fg, bg;
|
|
||||||
off_t sub_block;
|
|
||||||
fseek(gif->fd, 1, SEEK_CUR); /* block size = 12 */
|
|
||||||
tx = read_num(gif->fd);
|
|
||||||
ty = read_num(gif->fd);
|
|
||||||
tw = read_num(gif->fd);
|
|
||||||
th = read_num(gif->fd);
|
|
||||||
fread(&cw, 1, 1, gif->fd);
|
|
||||||
fread(&ch, 1, 1, gif->fd);
|
|
||||||
fread(&fg, 1, 1, gif->fd);
|
|
||||||
fread(&bg, 1, 1, gif->fd);
|
|
||||||
sub_block = ftell(gif->fd);
|
|
||||||
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
|
|
||||||
fseek(gif->fd, sub_block, SEEK_SET);
|
|
||||||
} else {
|
|
||||||
/* Discard plain text metadata. */
|
|
||||||
fseek(gif->fd, 13, SEEK_CUR);
|
|
||||||
}
|
|
||||||
/* Discard plain text sub-blocks. */
|
|
||||||
discard_sub_blocks(gif);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_graphic_control_ext(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
uint8_t rdit;
|
|
||||||
|
|
||||||
/* Discard block size (always 0x04). */
|
|
||||||
fseek(gif->fd, 1, SEEK_CUR);
|
|
||||||
fread(&rdit, 1, 1, gif->fd);
|
|
||||||
gif->gce.disposal = (rdit >> 2) & 3;
|
|
||||||
gif->gce.input = rdit & 2;
|
|
||||||
gif->gce.transparency = rdit & 1;
|
|
||||||
gif->gce.delay = read_num(gif->fd);
|
|
||||||
fread(&gif->gce.tindex, 1, 1, gif->fd);
|
|
||||||
/* Skip block terminator. */
|
|
||||||
fseek(gif->fd, 1, SEEK_CUR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_comment_ext(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
if (gif->comment) {
|
|
||||||
off_t sub_block = ftell(gif->fd);
|
|
||||||
gif->comment(gif);
|
|
||||||
fseek(gif->fd, sub_block, SEEK_SET);
|
|
||||||
}
|
|
||||||
/* Discard comment sub-blocks. */
|
|
||||||
discard_sub_blocks(gif);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_application_ext(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
char app_id[8];
|
|
||||||
char app_auth_code[3];
|
|
||||||
|
|
||||||
/* Discard block size (always 0x0B). */
|
|
||||||
fseek(gif->fd, 1, SEEK_CUR);
|
|
||||||
/* Application Identifier. */
|
|
||||||
fread(app_id, 8, 1, gif->fd);
|
|
||||||
/* Application Authentication Code. */
|
|
||||||
fread(app_auth_code, 3, 1, gif->fd);
|
|
||||||
if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
|
|
||||||
/* Discard block size (0x03) and constant byte (0x01). */
|
|
||||||
fseek(gif->fd, 2, SEEK_CUR);
|
|
||||||
gif->loop_count = read_num(gif->fd);
|
|
||||||
/* Skip block terminator. */
|
|
||||||
fseek(gif->fd, 1, SEEK_CUR);
|
|
||||||
} else if (gif->application) {
|
|
||||||
off_t sub_block = ftell(gif->fd);
|
|
||||||
gif->application(gif, app_id, app_auth_code);
|
|
||||||
fseek(gif->fd, sub_block, SEEK_SET);
|
|
||||||
discard_sub_blocks(gif);
|
|
||||||
} else {
|
|
||||||
discard_sub_blocks(gif);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_ext(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
uint8_t label;
|
|
||||||
|
|
||||||
fread(&label, 1, 1, gif->fd);
|
|
||||||
switch (label) {
|
|
||||||
case 0x01:
|
|
||||||
read_plain_text_ext(gif);
|
|
||||||
break;
|
|
||||||
case 0xF9:
|
|
||||||
read_graphic_control_ext(gif);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
read_comment_ext(gif);
|
|
||||||
break;
|
|
||||||
case 0xFF:
|
|
||||||
read_application_ext(gif);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown extension: %02X\n", label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Table *
|
|
||||||
new_table(int key_size)
|
|
||||||
{
|
|
||||||
int key;
|
|
||||||
int init_bulk = MAX(1 << (key_size + 1), 0x100);
|
|
||||||
Table *table = malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
|
|
||||||
if (table) {
|
|
||||||
table->bulk = init_bulk;
|
|
||||||
table->nentries = (1 << key_size) + 2;
|
|
||||||
table->entries = (Entry *) &table[1];
|
|
||||||
for (key = 0; key < (1 << key_size); key++)
|
|
||||||
table->entries[key] = (Entry) {1, 0xFFF, key};
|
|
||||||
}
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add table entry. Return value:
|
|
||||||
* 0 on success
|
|
||||||
* +1 if key size must be incremented after this addition
|
|
||||||
* -1 if could not realloc table */
|
|
||||||
static int
|
|
||||||
add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
|
|
||||||
{
|
|
||||||
Table *table = *tablep;
|
|
||||||
if (table->nentries == table->bulk) {
|
|
||||||
table->bulk *= 2;
|
|
||||||
table = realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
|
|
||||||
if (!table) return -1;
|
|
||||||
table->entries = (Entry *) &table[1];
|
|
||||||
*tablep = table;
|
|
||||||
}
|
|
||||||
table->entries[table->nentries] = (Entry) {length, prefix, suffix};
|
|
||||||
table->nentries++;
|
|
||||||
if ((table->nentries & (table->nentries - 1)) == 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
|
|
||||||
{
|
|
||||||
int bits_read;
|
|
||||||
int rpad;
|
|
||||||
int frag_size;
|
|
||||||
uint16_t key;
|
|
||||||
|
|
||||||
key = 0;
|
|
||||||
for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
|
|
||||||
rpad = (*shift + bits_read) % 8;
|
|
||||||
if (rpad == 0) {
|
|
||||||
/* Update byte. */
|
|
||||||
if (*sub_len == 0) {
|
|
||||||
fread(sub_len, 1, 1, gif->fd); /* Must be nonzero! */
|
|
||||||
if (*sub_len == 0)
|
|
||||||
return 0x1000;
|
|
||||||
}
|
|
||||||
fread(byte, 1, 1, gif->fd);
|
|
||||||
(*sub_len)--;
|
|
||||||
}
|
|
||||||
frag_size = MIN(key_size - bits_read, 8 - rpad);
|
|
||||||
key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
|
|
||||||
}
|
|
||||||
/* Clear extra bits to the left. */
|
|
||||||
key &= (1 << key_size) - 1;
|
|
||||||
*shift = (*shift + key_size) % 8;
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute output index of y-th input line, in frame of height h. */
|
|
||||||
static int
|
|
||||||
interlaced_line_index(int h, int y)
|
|
||||||
{
|
|
||||||
int p; /* number of lines in current pass */
|
|
||||||
|
|
||||||
p = (h - 1) / 8 + 1;
|
|
||||||
if (y < p) /* pass 1 */
|
|
||||||
return y * 8;
|
|
||||||
y -= p;
|
|
||||||
p = (h - 5) / 8 + 1;
|
|
||||||
if (y < p) /* pass 2 */
|
|
||||||
return y * 8 + 4;
|
|
||||||
y -= p;
|
|
||||||
p = (h - 3) / 4 + 1;
|
|
||||||
if (y < p) /* pass 3 */
|
|
||||||
return y * 4 + 2;
|
|
||||||
y -= p;
|
|
||||||
/* pass 4 */
|
|
||||||
return y * 2 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decompress image pixels.
|
|
||||||
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
|
|
||||||
static int
|
|
||||||
read_image_data(gd_GIF *gif, int interlace)
|
|
||||||
{
|
|
||||||
uint8_t sub_len, shift, byte;
|
|
||||||
int init_key_size, key_size, table_is_full;
|
|
||||||
int frm_off, frm_size, str_len, i, p, x, y;
|
|
||||||
uint16_t key, clear, stop;
|
|
||||||
int ret;
|
|
||||||
Table *table;
|
|
||||||
Entry entry;
|
|
||||||
off_t start, end;
|
|
||||||
|
|
||||||
fread(&byte, 1, 1, gif->fd);
|
|
||||||
key_size = (int) byte;
|
|
||||||
if (key_size < 2 || key_size > 8)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
start = ftell(gif->fd);
|
|
||||||
discard_sub_blocks(gif);
|
|
||||||
end = ftell(gif->fd);
|
|
||||||
fseek(gif->fd, start, SEEK_SET);
|
|
||||||
clear = 1 << key_size;
|
|
||||||
stop = clear + 1;
|
|
||||||
table = new_table(key_size);
|
|
||||||
key_size++;
|
|
||||||
init_key_size = key_size;
|
|
||||||
sub_len = shift = 0;
|
|
||||||
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
|
|
||||||
frm_off = 0;
|
|
||||||
ret = 0;
|
|
||||||
frm_size = gif->fw*gif->fh;
|
|
||||||
while (frm_off < frm_size) {
|
|
||||||
if (key == clear) {
|
|
||||||
key_size = init_key_size;
|
|
||||||
table->nentries = (1 << (key_size - 1)) + 2;
|
|
||||||
table_is_full = 0;
|
|
||||||
} else if (!table_is_full) {
|
|
||||||
ret = add_entry(&table, str_len + 1, key, entry.suffix);
|
|
||||||
if (ret == -1) {
|
|
||||||
free(table);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (table->nentries == 0x1000) {
|
|
||||||
ret = 0;
|
|
||||||
table_is_full = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key = get_key(gif, key_size, &sub_len, &shift, &byte);
|
|
||||||
if (key == clear) continue;
|
|
||||||
if (key == stop || key == 0x1000) break;
|
|
||||||
if (ret == 1) key_size++;
|
|
||||||
entry = table->entries[key];
|
|
||||||
str_len = entry.length;
|
|
||||||
for (i = 0; i < str_len; i++) {
|
|
||||||
p = frm_off + entry.length - 1;
|
|
||||||
x = p % gif->fw;
|
|
||||||
y = p / gif->fw;
|
|
||||||
if (interlace)
|
|
||||||
y = interlaced_line_index((int) gif->fh, y);
|
|
||||||
gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
|
|
||||||
if (entry.prefix == 0xFFF)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
entry = table->entries[entry.prefix];
|
|
||||||
}
|
|
||||||
frm_off += str_len;
|
|
||||||
if (key < table->nentries - 1 && !table_is_full)
|
|
||||||
table->entries[table->nentries - 1].suffix = entry.suffix;
|
|
||||||
}
|
|
||||||
free(table);
|
|
||||||
if (key == stop)
|
|
||||||
fread(&sub_len, 1, 1, gif->fd); /* Must be zero! */
|
|
||||||
fseek(gif->fd, end, SEEK_SET);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read image.
|
|
||||||
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
|
|
||||||
static int
|
|
||||||
read_image(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
uint8_t fisrz;
|
|
||||||
int interlace;
|
|
||||||
|
|
||||||
/* Image Descriptor. */
|
|
||||||
gif->fx = read_num(gif->fd);
|
|
||||||
gif->fy = read_num(gif->fd);
|
|
||||||
|
|
||||||
if (gif->fx >= gif->width || gif->fy >= gif->height)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
gif->fw = read_num(gif->fd);
|
|
||||||
gif->fh = read_num(gif->fd);
|
|
||||||
|
|
||||||
gif->fw = MIN(gif->fw, gif->width - gif->fx);
|
|
||||||
gif->fh = MIN(gif->fh, gif->height - gif->fy);
|
|
||||||
|
|
||||||
fread(&fisrz, 1, 1, gif->fd);
|
|
||||||
interlace = fisrz & 0x40;
|
|
||||||
/* Ignore Sort Flag. */
|
|
||||||
/* Local Color Table? */
|
|
||||||
if (fisrz & 0x80) {
|
|
||||||
/* Read LCT */
|
|
||||||
gif->lct.size = 1 << ((fisrz & 0x07) + 1);
|
|
||||||
fread(gif->lct.colors, 3, 1, gif->fd);
|
|
||||||
gif->palette = &gif->lct;
|
|
||||||
} else
|
|
||||||
gif->palette = &gif->gct;
|
|
||||||
/* Image Data. */
|
|
||||||
return read_image_data(gif, interlace);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_frame_rect(gd_GIF *gif, uint8_t *buffer)
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
uint8_t index, *color;
|
|
||||||
i = gif->fy * gif->width + gif->fx;
|
|
||||||
for (j = 0; j < gif->fh; j++) {
|
|
||||||
for (k = 0; k < gif->fw; k++) {
|
|
||||||
index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
|
|
||||||
color = &gif->palette->colors[index*3];
|
|
||||||
if (!gif->gce.transparency || index != gif->gce.tindex)
|
|
||||||
memcpy(&buffer[(i+k)*3], color, 3);
|
|
||||||
}
|
|
||||||
i += gif->width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dispose(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
uint8_t *bgcolor;
|
|
||||||
switch (gif->gce.disposal) {
|
|
||||||
case 2: /* Restore to background color. */
|
|
||||||
bgcolor = &gif->palette->colors[gif->bgindex*3];
|
|
||||||
i = gif->fy * gif->width + gif->fx;
|
|
||||||
for (j = 0; j < gif->fh; j++) {
|
|
||||||
for (k = 0; k < gif->fw; k++)
|
|
||||||
memcpy(&gif->canvas[(i+k)*3], bgcolor, 3);
|
|
||||||
i += gif->width;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3: /* Restore to previous, i.e., don't update canvas.*/
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Add frame non-transparent pixels to canvas. */
|
|
||||||
render_frame_rect(gif, gif->canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
|
|
||||||
int
|
|
||||||
gd_get_frame(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
char sep;
|
|
||||||
|
|
||||||
dispose(gif);
|
|
||||||
fread(&sep, 1, 1, gif->fd);
|
|
||||||
while (sep != ',') {
|
|
||||||
if (sep == ';')
|
|
||||||
return 0;
|
|
||||||
if (sep == '!')
|
|
||||||
read_ext(gif);
|
|
||||||
else return -1;
|
|
||||||
fread(&sep, 1, 1, gif->fd);
|
|
||||||
}
|
|
||||||
if (read_image(gif) == -1)
|
|
||||||
return -1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gd_render_frame(gd_GIF *gif, uint8_t *buffer)
|
|
||||||
{
|
|
||||||
memcpy(buffer, gif->canvas, gif->width * gif->height * 3);
|
|
||||||
render_frame_rect(gif, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
gd_is_bgcolor(gd_GIF *gif, uint8_t color[3])
|
|
||||||
{
|
|
||||||
return !memcmp(&gif->palette->colors[gif->bgindex*3], color, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gd_rewind(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
fseek(gif->fd, gif->anim_start, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gd_close_gif(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
free(gif->frame);
|
|
||||||
free(gif);
|
|
||||||
}
|
|
|
@ -1,322 +0,0 @@
|
||||||
/*
|
|
||||||
GIF decoder
|
|
||||||
===========
|
|
||||||
|
|
||||||
This is a small C library that can be used to read GIF files.
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* support for all standard GIF features
|
|
||||||
* support for Netscape Application Extension (looping information)
|
|
||||||
* other extensions may be easily supported via user hooks
|
|
||||||
* small and portable: less than 500 lines of C99
|
|
||||||
* public domain
|
|
||||||
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
|
|
||||||
* no support for GIF files that don't have a global color table
|
|
||||||
* no direct support for the plain text extension (rarely used)
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
0. Essential GIF concepts
|
|
||||||
|
|
||||||
GIF animations are stored in files as a series of palette-based
|
|
||||||
compressed frames.
|
|
||||||
|
|
||||||
In order to display the animation, a program must lay the frames on top
|
|
||||||
of a fixed-size canvas, one after the other. Each frame has a size,
|
|
||||||
position and duration. Each frame can have its own palette or use a
|
|
||||||
global palette defined in the beginning of the file.
|
|
||||||
|
|
||||||
In order to properly use extension hooks, it's necessary to understand
|
|
||||||
how GIF files store variable-sized data. A GIF block of variable size is
|
|
||||||
a sequence of sub-blocks. The first byte in a sub-block indicates the
|
|
||||||
number of data bytes to follow. The end of the block is indicated by an
|
|
||||||
empty sub-block: one byte of value 0x00. For instance, a data block of
|
|
||||||
600 bytes is stored as 4 sub-blocks:
|
|
||||||
|
|
||||||
255, <255 data bytes>, 255, <255 data bytes>, 90, <90 data bytes>, 0
|
|
||||||
|
|
||||||
1. Opening and closing a GIF file
|
|
||||||
|
|
||||||
The function `gd_open_gif()` tries to open a GIF file for reading.
|
|
||||||
|
|
||||||
gd_GIF *gd_open_gif(const char *fname);
|
|
||||||
|
|
||||||
If this function fails, it returns NULL.
|
|
||||||
|
|
||||||
If `gd_open_gif()` succeeds, it returns a GIF handler (`gd_GIF *`). The
|
|
||||||
GIF handler can be passed to the other gifdec functions to decode GIF
|
|
||||||
metadata and frames.
|
|
||||||
|
|
||||||
To close the GIF file and free memory after it has been decoded, the
|
|
||||||
function `gd_close_gif()` must be called.
|
|
||||||
|
|
||||||
void gd_close_gif(gd_GIF *gif);
|
|
||||||
|
|
||||||
2. Reading GIF attributes
|
|
||||||
|
|
||||||
Once a GIF file has been successfully opened, some basic information can
|
|
||||||
be read directly from the GIF handler:
|
|
||||||
|
|
||||||
gd_GIF *gif = gd_open_gif("animation.gif");
|
|
||||||
printf("canvas size: %ux%u\n", gif->width, gif->height);
|
|
||||||
printf("number of colors: %d\n", gif->palette->size);
|
|
||||||
|
|
||||||
3. Reading frames
|
|
||||||
|
|
||||||
The function `gd_get_frame()` decodes one frame from the GIF file.
|
|
||||||
|
|
||||||
int gd_get_frame(gd_GIF *gif);
|
|
||||||
|
|
||||||
This function returns 0 if there are no more frames to read.
|
|
||||||
|
|
||||||
The decoded frame is stored in `gif->frame`, which is a buffer of size
|
|
||||||
`gif->width * gif->height`, in bytes. Each byte value is an index to the
|
|
||||||
palette at `gif->palette`.
|
|
||||||
|
|
||||||
Since GIF files often only store the rectangular region of a frame that
|
|
||||||
changed from the previous frame, this function will only update the
|
|
||||||
bytes in `gif->frame` that are in that region. For GIF files that only
|
|
||||||
use the global palette, the whole state of the canvas is stored in
|
|
||||||
`gif->frame` at all times, in the form of an indexed color image.
|
|
||||||
However, when local palettes are used, it's not enough to keep color
|
|
||||||
indices from previous frames. The color RGB values themselves need to be
|
|
||||||
stored.
|
|
||||||
|
|
||||||
For this reason, in order to get the whole state of the canvas after
|
|
||||||
a new frame has been read, it's necessary to call the function
|
|
||||||
`gd_render_frame()`, which writes all pixels to a given buffer.
|
|
||||||
|
|
||||||
void gd_render_frame(gd_GIF *gif, uint8_t *buffer);
|
|
||||||
|
|
||||||
The buffer size must be at least `gif->width * gif->height * 3`, in
|
|
||||||
bytes. The function `gd_render_frame()` writes the 24-bit RGB values of
|
|
||||||
all canvas pixels in it.
|
|
||||||
|
|
||||||
4. Frame duration
|
|
||||||
|
|
||||||
GIF animations are not required to have a constant frame rate. Each
|
|
||||||
frame can have a different duration, which is stored right before the
|
|
||||||
frame in a Graphic Control Extension (GCE) block. This type of block is
|
|
||||||
read by gifdec into a `gd_GCE` struct that is a member of the GIF
|
|
||||||
handler. Specifically, the unsigned integer `gif->gce.delay` holds the
|
|
||||||
current frame duration, in hundreths of a second. That means that, for
|
|
||||||
instance, if `gif->gce.delay` is `50`, then the current frame must be
|
|
||||||
displayed for half a second.
|
|
||||||
|
|
||||||
5. Looping
|
|
||||||
|
|
||||||
Most GIF animations are supposed to loop automatically, going back to
|
|
||||||
the first frame after the last one is displayed. GIF files may contain
|
|
||||||
looping instruction in the form of a non-negative number. If this number
|
|
||||||
is zero, the animation must loop forever. Otherwise, this number
|
|
||||||
indicates how many times the animation must be played. When `gifdec` is
|
|
||||||
decoding a GIF file, this number is stored in `gif->loop_count`.
|
|
||||||
|
|
||||||
The function `gd_rewind()` must be called to go back to the start of the
|
|
||||||
GIF file without closing and reopening it.
|
|
||||||
|
|
||||||
void gd_rewind(gd_GIF *gif);
|
|
||||||
|
|
||||||
6. Putting it all together
|
|
||||||
|
|
||||||
A simplified skeleton of a GIF viewer may look like this:
|
|
||||||
|
|
||||||
gd_GIF *gif = gd_open_gif("some_animation.gif");
|
|
||||||
char *buffer = malloc(gif->width * gif->height * 3);
|
|
||||||
for (unsigned looped = 1;; looped++) {
|
|
||||||
while (gd_get_frame(gif)) {
|
|
||||||
gd_render_frame(gif, buffer);
|
|
||||||
// insert code to render buffer to screen
|
|
||||||
// and wait for delay time to pass here
|
|
||||||
}
|
|
||||||
if (looped == gif->loop_count)
|
|
||||||
break;
|
|
||||||
gd_rewind(gif);
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
gd_close_gif(gif);
|
|
||||||
|
|
||||||
7. Transparent Background
|
|
||||||
|
|
||||||
GIFs can mark a certain color in the palette as the "Background Color".
|
|
||||||
Pixels having this color are usually treated as transparent pixels by
|
|
||||||
applications.
|
|
||||||
|
|
||||||
The function `gd_is_bgcolor()` can be used to check whether a pixel in
|
|
||||||
the canvas currently has background color.
|
|
||||||
|
|
||||||
int gd_is_bgcolor(gd_GIF *gif, uint8_t color[3]);
|
|
||||||
|
|
||||||
Here's an example of how to use it:
|
|
||||||
|
|
||||||
gd_render_frame(gif, buffer);
|
|
||||||
color = buffer;
|
|
||||||
for (y = 0; y < gif->height; y++) {
|
|
||||||
for (x = 0; x < gif->width; x++) {
|
|
||||||
if (gd_is_bgcolor(gif, color))
|
|
||||||
transparent_pixel(x, y);
|
|
||||||
else
|
|
||||||
opaque_pixel(x, y, color);
|
|
||||||
color += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
8. Reading streamed metadata with extension hooks
|
|
||||||
|
|
||||||
Some metadata blocks may occur any number of times in GIF files in
|
|
||||||
between frames. By default, gifdec ignore these blocks. However, it's
|
|
||||||
possible to setup callback functions to handle each type of extension
|
|
||||||
block, by changing some GIF handler members.
|
|
||||||
|
|
||||||
Whenever a Comment Extension block is found, `gif->comment()` is called.
|
|
||||||
|
|
||||||
void (*comment)(struct gd_GIF *gif);
|
|
||||||
|
|
||||||
As defined in the GIF specification, "[t]he Comment Extension contains
|
|
||||||
textual information which is not part of the actual graphics in the GIF
|
|
||||||
Data Stream." Encoders are recommended to only include "text using the
|
|
||||||
7-bit ASCII character set" in GIF comments.
|
|
||||||
|
|
||||||
The actual comment is stored as a variable-sized block and must be read
|
|
||||||
from the file (using the file descriptor `gif->fd`) by the callback
|
|
||||||
function. Here's an example, printing the comment to stdout:
|
|
||||||
|
|
||||||
void
|
|
||||||
comment(gd_GIF *gif)
|
|
||||||
{
|
|
||||||
uint8_t sub_len, byte, i;
|
|
||||||
do {
|
|
||||||
read(gif->fd, &sub_len, 1);
|
|
||||||
for (i = 0; i < sub_len; i++) {
|
|
||||||
read(gif->fd, &byte, 1);
|
|
||||||
printf("%c", byte);
|
|
||||||
}
|
|
||||||
} while (sub_len);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// Somewhere on the main path of execution.
|
|
||||||
gif->comment = comment;
|
|
||||||
|
|
||||||
|
|
||||||
Whenever a Plain Text Extension block is found, `gif->plain_text()` is
|
|
||||||
called.
|
|
||||||
|
|
||||||
void (*plain_text)(
|
|
||||||
struct gd_GIF *gif, uint16_t tx, uint16_t ty,
|
|
||||||
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
|
|
||||||
uint8_t fg, uint8_t bg
|
|
||||||
);
|
|
||||||
|
|
||||||
According to the GIF specification, "[t]he Plain Text Extension contains
|
|
||||||
textual data and the parameters necessary to render that data as a
|
|
||||||
graphic [...]". This is a rarely used extension that requires the
|
|
||||||
decoder to actually render text on the canvas. In order to support this,
|
|
||||||
one must read the relevant specification and implement a suitable
|
|
||||||
callback function to setup as `gif->plain_text`.
|
|
||||||
|
|
||||||
The actual plain text is stored as a variable-sized block and must be
|
|
||||||
read from the file by the callback function.
|
|
||||||
|
|
||||||
|
|
||||||
Whenever an unknown Application Extension block is found,
|
|
||||||
`gif->application()` is called.
|
|
||||||
|
|
||||||
void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
|
|
||||||
|
|
||||||
Application Extensions are used to extend GIF with extraofficial
|
|
||||||
features. Currently, gifdec only supports the so-called "Netscape
|
|
||||||
Application Extension", which is commonly used to specify looping
|
|
||||||
behavior. Other Application Extensions may be supported via this hook.
|
|
||||||
|
|
||||||
The application data is stored as a variable-sized block and must be
|
|
||||||
read from the file by the callback function.
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
The file "example.c" is a demo GIF player based on gifdec and SDL2. It
|
|
||||||
can be tested like this:
|
|
||||||
|
|
||||||
$ cc `pkg-config --cflags --libs sdl2` -o gifplay gifdec.c example.c
|
|
||||||
$ ./gifplay animation.gif
|
|
||||||
|
|
||||||
That should display the animation. Press SPACE to pause and Q to quit.
|
|
||||||
|
|
||||||
Copying
|
|
||||||
-------
|
|
||||||
|
|
||||||
All of the source code and documentation for gifdec is released into the
|
|
||||||
public domain and provided without warranty of any kind.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GIFDEC_H
|
|
||||||
#define GIFDEC_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct gd_Palette {
|
|
||||||
int size;
|
|
||||||
uint8_t colors[0x100 * 3];
|
|
||||||
} gd_Palette;
|
|
||||||
|
|
||||||
typedef struct gd_GCE {
|
|
||||||
uint16_t delay;
|
|
||||||
uint8_t tindex;
|
|
||||||
uint8_t disposal;
|
|
||||||
int input;
|
|
||||||
int transparency;
|
|
||||||
} gd_GCE;
|
|
||||||
|
|
||||||
typedef struct gd_GIF {
|
|
||||||
FILE *fd;
|
|
||||||
off_t anim_start;
|
|
||||||
uint16_t width, height;
|
|
||||||
uint16_t depth;
|
|
||||||
uint16_t loop_count;
|
|
||||||
gd_GCE gce;
|
|
||||||
gd_Palette *palette;
|
|
||||||
gd_Palette lct, gct;
|
|
||||||
void (*plain_text)(
|
|
||||||
struct gd_GIF *gif, uint16_t tx, uint16_t ty,
|
|
||||||
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
|
|
||||||
uint8_t fg, uint8_t bg
|
|
||||||
);
|
|
||||||
void (*comment)(struct gd_GIF *gif);
|
|
||||||
void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
|
|
||||||
uint16_t fx, fy, fw, fh;
|
|
||||||
uint8_t bgindex;
|
|
||||||
uint8_t *canvas, *frame;
|
|
||||||
} gd_GIF;
|
|
||||||
|
|
||||||
gd_GIF *gd_open_gif(const char *fname);
|
|
||||||
gd_GIF *gd_open_gif_f(FILE *f);
|
|
||||||
int gd_get_frame(gd_GIF *gif);
|
|
||||||
void gd_render_frame(gd_GIF *gif, uint8_t *buffer);
|
|
||||||
int gd_is_bgcolor(gd_GIF *gif, uint8_t color[3]);
|
|
||||||
void gd_rewind(gd_GIF *gif);
|
|
||||||
void gd_close_gif(gd_GIF *gif);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* GIFDEC_H */
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "yugine.h"
|
#include "yugine.h"
|
||||||
#include "sokol/sokol_app.h"
|
#include "sokol/sokol_app.h"
|
||||||
#include "sokol/sokol_glue.h"
|
|
||||||
|
|
||||||
#include "crt.sglsl.h"
|
#include "crt.sglsl.h"
|
||||||
#include "box.sglsl.h"
|
#include "box.sglsl.h"
|
||||||
|
@ -70,14 +69,16 @@ void gif_rec_start(int w, int h, int cpf, int bitdepth)
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = gif.w,
|
.width = gif.w,
|
||||||
.height = gif.h,
|
.height = gif.h,
|
||||||
.pixel_format = SG_PIXELFORMAT_RGBA8
|
// .pixel_format = SG_PIXELFORMAT_RGBA8
|
||||||
|
.label = "gif rt",
|
||||||
});
|
});
|
||||||
|
|
||||||
sg_gif.depth = sg_make_image(&(sg_image_desc){
|
sg_gif.depth = sg_make_image(&(sg_image_desc){
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = gif.w,
|
.width = gif.w,
|
||||||
.height = gif.h,
|
.height = gif.h,
|
||||||
.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL
|
.pixel_format = SG_PIXELFORMAT_DEPTH,
|
||||||
|
.label = "gif depth",
|
||||||
});
|
});
|
||||||
|
|
||||||
sg_gif.pass = sg_make_pass(&(sg_pass_desc){
|
sg_gif.pass = sg_make_pass(&(sg_pass_desc){
|
||||||
|
@ -196,6 +197,13 @@ void trace_destroy_shader(sg_shader shd, void *data)
|
||||||
YughWarn("DESTROYED SHADER");
|
YughWarn("DESTROYED SHADER");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void trace_fail_image(sg_image id, void *data)
|
||||||
|
{
|
||||||
|
sg_image_desc desc = sg_query_image_desc(id);
|
||||||
|
YughWarn("Failed to make image %s", desc.label);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static sg_trace_hooks hooks = {
|
static sg_trace_hooks hooks = {
|
||||||
.fail_shader = trace_fail_shader,
|
.fail_shader = trace_fail_shader,
|
||||||
.make_shader = trace_make_shader,
|
.make_shader = trace_make_shader,
|
||||||
|
@ -207,9 +215,22 @@ void render_init() {
|
||||||
mainwin.height = sapp_height();
|
mainwin.height = sapp_height();
|
||||||
|
|
||||||
sg_setup(&(sg_desc){
|
sg_setup(&(sg_desc){
|
||||||
.context = sapp_sgcontext(),
|
.context.d3d11.device = sapp_d3d11_get_device(),
|
||||||
|
.context.d3d11.device_context = sapp_d3d11_get_device_context(),
|
||||||
|
.context.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view,
|
||||||
|
.context.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view,
|
||||||
|
/* .context.metal.device = sapp_metal_get_device(),
|
||||||
|
.context.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor,
|
||||||
|
.context.metal.drawable_cb = sapp_metal_get_drawable,
|
||||||
|
.context.color_format = (sg_pixel_format) sapp_color_format(),
|
||||||
|
.context.depth_format = (sg_pixel_format) sapp_depth_format(),
|
||||||
|
.context.sample_count = sapp_sample_count(),
|
||||||
|
.context.wgpu.device = sapp_wgpu_get_device(),
|
||||||
|
.context.wgpu.render_view_cb = sapp_wgpu_get_render_view,
|
||||||
|
.context.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view,
|
||||||
|
.context.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view,
|
||||||
.mtl_force_managed_storage_mode = 1,
|
.mtl_force_managed_storage_mode = 1,
|
||||||
.logger = {
|
*/ .logger = {
|
||||||
.func = sg_logging,
|
.func = sg_logging,
|
||||||
.user_data = NULL,
|
.user_data = NULL,
|
||||||
},
|
},
|
||||||
|
@ -263,13 +284,15 @@ void render_init() {
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = mainwin.width,
|
.width = mainwin.width,
|
||||||
.height = mainwin.height,
|
.height = mainwin.height,
|
||||||
|
.label = "crt rt",
|
||||||
});
|
});
|
||||||
|
|
||||||
crt_post.depth_img = sg_make_image(&(sg_image_desc){
|
crt_post.depth_img = sg_make_image(&(sg_image_desc){
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = mainwin.width,
|
.width = mainwin.width,
|
||||||
.height = mainwin.height,
|
.height = mainwin.height,
|
||||||
.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL
|
.pixel_format = SG_PIXELFORMAT_DEPTH,
|
||||||
|
.label = "crt depth",
|
||||||
});
|
});
|
||||||
|
|
||||||
crt_post.pass = sg_make_pass(&(sg_pass_desc){
|
crt_post.pass = sg_make_pass(&(sg_pass_desc){
|
||||||
|
@ -370,14 +393,14 @@ void render_winsize()
|
||||||
crt_post.img = sg_make_image(&(sg_image_desc){
|
crt_post.img = sg_make_image(&(sg_image_desc){
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = mainwin.width,
|
.width = mainwin.width,
|
||||||
.height = mainwin.height
|
.height = mainwin.height,
|
||||||
});
|
});
|
||||||
|
|
||||||
crt_post.depth_img = sg_make_image(&(sg_image_desc){
|
crt_post.depth_img = sg_make_image(&(sg_image_desc){
|
||||||
.render_target = true,
|
.render_target = true,
|
||||||
.width = mainwin.width,
|
.width = mainwin.width,
|
||||||
.height = mainwin.height,
|
.height = mainwin.height,
|
||||||
.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL
|
.pixel_format = SG_PIXELFORMAT_DEPTH,
|
||||||
});
|
});
|
||||||
|
|
||||||
crt_post.pass = sg_make_pass(&(sg_pass_desc){
|
crt_post.pass = sg_make_pass(&(sg_pass_desc){
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stb_ds.h>
|
#include <stb_ds.h>
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include "gifdec.h"
|
|
||||||
|
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
|
|
||||||
|
@ -72,22 +71,8 @@ int mip_wh(int w, int h, int *mw, int *mh, int lvl)
|
||||||
|
|
||||||
int gif_nframes(const char *path)
|
int gif_nframes(const char *path)
|
||||||
{
|
{
|
||||||
long rawlen;
|
struct Texture *t = texture_pullfromfile(path);
|
||||||
unsigned char *raw = slurp_file(path, &rawlen);
|
return t->frames;
|
||||||
char *ext = strrchr(path,'.');
|
|
||||||
int frames = 0; /* default here is also the error code */
|
|
||||||
|
|
||||||
FILE *f = fmemopen(raw, rawlen, "r");
|
|
||||||
gd_GIF *gif = gd_open_gif_f(f);
|
|
||||||
|
|
||||||
if (!gif) goto fin;
|
|
||||||
|
|
||||||
while (gd_get_frame(gif)) frames++;
|
|
||||||
|
|
||||||
fin:
|
|
||||||
gd_close_gif(gif);
|
|
||||||
free(raw);
|
|
||||||
return frames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If an empty string or null is put for path, loads default texture */
|
/* If an empty string or null is put for path, loads default texture */
|
||||||
|
@ -122,43 +107,9 @@ struct Texture *texture_pullfromfile(const char *path) {
|
||||||
tex->height = qoi.height;
|
tex->height = qoi.height;
|
||||||
n = qoi.channels;
|
n = qoi.channels;
|
||||||
} else if (!strcmp(ext, ".gif")) {
|
} else if (!strcmp(ext, ".gif")) {
|
||||||
FILE *f = fmemopen(raw, rawlen, "r");
|
|
||||||
gd_GIF *gif = gd_open_gif_f(f);
|
|
||||||
int frames = 0;
|
|
||||||
while (gd_get_frame(gif))
|
|
||||||
frames++;
|
|
||||||
|
|
||||||
gd_rewind(gif);
|
|
||||||
|
|
||||||
int frame = 0;
|
|
||||||
uint8_t gifbuf[gif->width*gif->height*3];
|
|
||||||
data = malloc(gif->width*gif->height*4*frames);
|
|
||||||
|
|
||||||
while (gd_get_frame(gif)) {
|
|
||||||
gd_render_frame(gif, gifbuf);
|
|
||||||
uint8_t black[4] = {0,0,0,0};
|
|
||||||
for (int p = 0; p < gif->height*gif->width; p++) {
|
|
||||||
int offset = (p*4)+(frame*gif->width*gif->height*4);
|
|
||||||
if (gd_is_bgcolor(gif, gifbuf+(p*3)))
|
|
||||||
memcpy(data+offset, black, 4*sizeof(uint8_t));
|
|
||||||
else {
|
|
||||||
memcpy(data+offset, gifbuf+(p*3), 3*sizeof(uint8_t));
|
|
||||||
data[offset+3] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame++;
|
|
||||||
}
|
|
||||||
tex->width = gif->width;
|
|
||||||
tex->height = gif->height*frames;
|
|
||||||
gd_close_gif(gif);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
/*
|
|
||||||
int *delays;
|
int *delays;
|
||||||
int frames;
|
data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &tex->frames, &n, 4);
|
||||||
data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &frames, &n, 4);
|
tex->height *= tex->frames;
|
||||||
*/
|
|
||||||
} else {
|
} else {
|
||||||
data = stbi_load_from_memory(raw, rawlen, &tex->width, &tex->height, &n, 4);
|
data = stbi_load_from_memory(raw, rawlen, &tex->width, &tex->height, &n, 4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ struct TextureOptions {
|
||||||
int animation;
|
int animation;
|
||||||
int wrapx;
|
int wrapx;
|
||||||
int wrapy;
|
int wrapy;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Represents an actual texture on the GPU */
|
/* Represents an actual texture on the GPU */
|
||||||
|
@ -68,6 +69,7 @@ struct Texture {
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
struct TextureOptions opts;
|
struct TextureOptions opts;
|
||||||
struct TexAnim anim;
|
struct TexAnim anim;
|
||||||
|
int frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Image {
|
struct Image {
|
||||||
|
|
136
source/engine/thirdparty/sokol/sokol_glue.h
vendored
136
source/engine/thirdparty/sokol/sokol_glue.h
vendored
|
@ -1,136 +0,0 @@
|
||||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL)
|
|
||||||
#define SOKOL_GLUE_IMPL
|
|
||||||
#endif
|
|
||||||
#ifndef SOKOL_GLUE_INCLUDED
|
|
||||||
/*
|
|
||||||
sokol_glue.h -- glue helper functions for sokol headers
|
|
||||||
|
|
||||||
Project URL: https://github.com/floooh/sokol
|
|
||||||
|
|
||||||
Do this:
|
|
||||||
#define SOKOL_IMPL or
|
|
||||||
#define SOKOL_GLUE_IMPL
|
|
||||||
before you include this file in *one* C or C++ file to create the
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
...optionally provide the following macros to override defaults:
|
|
||||||
|
|
||||||
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
|
||||||
SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern)
|
|
||||||
SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL
|
|
||||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
|
||||||
|
|
||||||
If sokol_glue.h is compiled as a DLL, define the following before
|
|
||||||
including the declaration or implementation:
|
|
||||||
|
|
||||||
SOKOL_DLL
|
|
||||||
|
|
||||||
On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport)
|
|
||||||
or __declspec(dllimport) as needed.
|
|
||||||
|
|
||||||
OVERVIEW
|
|
||||||
========
|
|
||||||
The sokol core headers should not depend on each other, but sometimes
|
|
||||||
it's useful to have a set of helper functions as "glue" between
|
|
||||||
two or more sokol headers.
|
|
||||||
|
|
||||||
This is what sokol_glue.h is for. Simply include the header after other
|
|
||||||
sokol headers (both for the implementation and declaration), and
|
|
||||||
depending on what headers have been included before, sokol_glue.h
|
|
||||||
will make available "glue functions".
|
|
||||||
|
|
||||||
PROVIDED FUNCTIONS
|
|
||||||
==================
|
|
||||||
|
|
||||||
- if sokol_app.h and sokol_gfx.h is included:
|
|
||||||
|
|
||||||
sg_context_desc sapp_sgcontext(void):
|
|
||||||
|
|
||||||
Returns an initialized sg_context_desc function initialized
|
|
||||||
by calling sokol_app.h functions.
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
=======
|
|
||||||
zlib/libpng license
|
|
||||||
|
|
||||||
Copyright (c) 2018 Andre Weissflog
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty.
|
|
||||||
In no event will the authors be held liable for any damages arising from the
|
|
||||||
use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not
|
|
||||||
claim that you wrote the original software. If you use this software in a
|
|
||||||
product, an acknowledgment in the product documentation would be
|
|
||||||
appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not
|
|
||||||
be misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
*/
|
|
||||||
#define SOKOL_GLUE_INCLUDED
|
|
||||||
|
|
||||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL)
|
|
||||||
#define SOKOL_GLUE_API_DECL SOKOL_API_DECL
|
|
||||||
#endif
|
|
||||||
#ifndef SOKOL_GLUE_API_DECL
|
|
||||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL)
|
|
||||||
#define SOKOL_GLUE_API_DECL __declspec(dllexport)
|
|
||||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
|
||||||
#define SOKOL_GLUE_API_DECL __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define SOKOL_GLUE_API_DECL extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED)
|
|
||||||
SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
#endif /* SOKOL_GLUE_INCLUDED */
|
|
||||||
|
|
||||||
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
|
||||||
#ifdef SOKOL_GLUE_IMPL
|
|
||||||
#define SOKOL_GLUE_IMPL_INCLUDED (1)
|
|
||||||
#include <string.h> /* memset */
|
|
||||||
|
|
||||||
#ifndef SOKOL_API_IMPL
|
|
||||||
#define SOKOL_API_IMPL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED)
|
|
||||||
SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) {
|
|
||||||
sg_context_desc desc;
|
|
||||||
memset(&desc, 0, sizeof(desc));
|
|
||||||
desc.color_format = (sg_pixel_format) sapp_color_format();
|
|
||||||
desc.depth_format = (sg_pixel_format) sapp_depth_format();
|
|
||||||
desc.sample_count = sapp_sample_count();
|
|
||||||
desc.metal.device = sapp_metal_get_device();
|
|
||||||
desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor;
|
|
||||||
desc.metal.drawable_cb = sapp_metal_get_drawable;
|
|
||||||
desc.d3d11.device = sapp_d3d11_get_device();
|
|
||||||
desc.d3d11.device_context = sapp_d3d11_get_device_context();
|
|
||||||
desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view;
|
|
||||||
desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view;
|
|
||||||
desc.wgpu.device = sapp_wgpu_get_device();
|
|
||||||
desc.wgpu.render_view_cb = sapp_wgpu_get_render_view;
|
|
||||||
desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view;
|
|
||||||
desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view;
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* SOKOL_GLUE_IMPL */
|
|
Loading…
Reference in a new issue