diff --git a/Makefile b/Makefile index 2d9a244..5efb07a 100755 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ LD = $(CC) ifeq ($(CC), clang) AR = llvm-ar endif +ifeq ($(CC), x86_64-w64-mingw32-gcc) + AR = x86_64-w64-mingw32-ar +endif ifdef NEDITOR CFLAGS += -DNO_EDITOR @@ -72,6 +75,7 @@ ifeq ($(OS), Windows_NT) LDFLAGS += -mwin32 -static CFLAGS += -mwin32 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 PLAT = w64 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) 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) $(LD) $^ $(LDFLAGS) -L$(BIN) $(LDLIBS) -o $@ cp $(BIN)/$(NAME) . @@ -155,13 +159,13 @@ $(DISTDIR)/$(DIST): $(BIN)/$(NAME) @mkdir -p $(DISTDIR) @$(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) $(BIN)/libcdb.a: mkdir -p $(BIN) rm -f $(CDB)/libcdb.a - make -C $(CDB) libcdb.a + make -C $(CDB) CC=$(CC) AR=$(AR) libcdb.a cp $(CDB)/libcdb.a $(BIN) $(BIN)/libquickjs.a: @@ -211,7 +215,7 @@ jso: tools/jso.c $(BIN)/libquickjs.a WINCC = x86_64-w64-mingw32-gcc .PHONY: crosswin crosswin: - make CC=$(WINCC) OS=Windows_NT + gmake CC=$(WINCC) OS=Windows_NT clean: @echo Cleaning project diff --git a/scripts/components.js b/scripts/components.js index 72a9408..1a98ca8 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -87,6 +87,52 @@ sprite.inputs.kp2 = function() { this.pos = [-0.5,-1]; }; sprite.inputs.kp1 = function() { this.pos = [-1,-1]; }; 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 anim = {}; diff --git a/source/engine/config.c b/source/engine/config.c index 900cfcd..b01833f 100644 --- a/source/engine/config.c +++ b/source/engine/config.c @@ -3,7 +3,6 @@ #define SOKOL_TRACE_HOOKS #define SOKOL_IMPL -#include "sokol/sokol_glue.h" #include "sokol/sokol_audio.h" #include "sokol/sokol_time.h" #include "sokol/sokol_args.h" diff --git a/source/engine/debug/log.c b/source/engine/debug/log.c index 125f666..de7c19e 100644 --- a/source/engine/debug/log.c +++ b/source/engine/debug/log.c @@ -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) { - mYughLog(lvl, 1, line, file, "tag: %d, msg: %s", tag, msg); + mYughLog(lvl, 1, line, file, "tag: %s, msg: %s", tag, msg); } diff --git a/source/engine/gifdec.c b/source/engine/gifdec.c deleted file mode 100644 index 9af53e4..0000000 --- a/source/engine/gifdec.c +++ /dev/null @@ -1,529 +0,0 @@ -#include "gifdec.h" - -#include -#include -#include - -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#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); -} diff --git a/source/engine/gifdec.h b/source/engine/gifdec.h deleted file mode 100644 index 35e72be..0000000 --- a/source/engine/gifdec.h +++ /dev/null @@ -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 - -#include -#include - -#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 */ diff --git a/source/engine/render.c b/source/engine/render.c index 95393e1..26d00a7 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -16,7 +16,6 @@ #include "resources.h" #include "yugine.h" #include "sokol/sokol_app.h" -#include "sokol/sokol_glue.h" #include "crt.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, .width = gif.w, .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){ .render_target = true, .width = gif.w, .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){ @@ -196,6 +197,13 @@ void trace_destroy_shader(sg_shader shd, void *data) 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 = { .fail_shader = trace_fail_shader, .make_shader = trace_make_shader, @@ -207,9 +215,22 @@ void render_init() { mainwin.height = sapp_height(); 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, - .logger = { +*/ .logger = { .func = sg_logging, .user_data = NULL, }, @@ -263,13 +284,15 @@ void render_init() { .render_target = true, .width = mainwin.width, .height = mainwin.height, + .label = "crt rt", }); crt_post.depth_img = sg_make_image(&(sg_image_desc){ .render_target = true, .width = mainwin.width, .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){ @@ -370,14 +393,14 @@ void render_winsize() crt_post.img = sg_make_image(&(sg_image_desc){ .render_target = true, .width = mainwin.width, - .height = mainwin.height + .height = mainwin.height, }); crt_post.depth_img = sg_make_image(&(sg_image_desc){ .render_target = true, .width = mainwin.width, .height = mainwin.height, - .pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL + .pixel_format = SG_PIXELFORMAT_DEPTH, }); crt_post.pass = sg_make_pass(&(sg_pass_desc){ diff --git a/source/engine/texture.c b/source/engine/texture.c index 70a3808..8d81c47 100644 --- a/source/engine/texture.c +++ b/source/engine/texture.c @@ -6,7 +6,6 @@ #include #include #include -#include "gifdec.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) { - long rawlen; - unsigned char *raw = slurp_file(path, &rawlen); - 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; + struct Texture *t = texture_pullfromfile(path); + return t->frames; } /* 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; n = qoi.channels; } 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 frames; - data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &frames, &n, 4); - */ + data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &tex->frames, &n, 4); + tex->height *= tex->frames; } else { data = stbi_load_from_memory(raw, rawlen, &tex->width, &tex->height, &n, 4); } diff --git a/source/engine/texture.h b/source/engine/texture.h index dd000cb..ebfac01 100644 --- a/source/engine/texture.h +++ b/source/engine/texture.h @@ -58,6 +58,7 @@ struct TextureOptions { int animation; int wrapx; int wrapy; + }; /* Represents an actual texture on the GPU */ @@ -68,6 +69,7 @@ struct Texture { unsigned char *data; struct TextureOptions opts; struct TexAnim anim; + int frames; }; struct Image { diff --git a/source/engine/thirdparty/sokol/sokol_glue.h b/source/engine/thirdparty/sokol/sokol_glue.h deleted file mode 100644 index 7216f6a..0000000 --- a/source/engine/thirdparty/sokol/sokol_glue.h +++ /dev/null @@ -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 /* 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 */