emote2ss

Animated webp to spritesheets converting tool
git clone git://bsandro.tech/emote2ss
Log | Files | Refs | README | LICENSE

commit b2924cd50878cb9957b7820e4842478ddb566127
parent 0dfb16426a0885bedaa1cddf91ab4d11e71f025a
Author: bsandro <email@bsandro.tech>
Date:   Wed, 31 Dec 2025 01:00:28 +0200

Basic cleanup and argv parsing for the CLI version

Diffstat:
Mcli/Makefile | 5++++-
Acli/arg.h | 37+++++++++++++++++++++++++++++++++++++
Mcli/main.c | 127++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
3 files changed, 132 insertions(+), 37 deletions(-)

diff --git a/cli/Makefile b/cli/Makefile @@ -19,5 +19,8 @@ clean: $(NAME): $(OBJ) $(CC) $(OBJ) -o ../$@ $(LDFLAGS) $(CFLAGS) -run: $(NAME) +strip: $(NAME) + strip -S --strip-unneeded --remove-section=.note.gnu.gold-version --remove-section=.comment --remove-section=.note --remove-section=.note.gnu.build-id --remove-section=.note.ABI-tag ../$(NAME) + +run: strip ../$(NAME) diff --git a/cli/arg.h b/cli/arg.h @@ -0,0 +1,37 @@ +#ifndef ARG_H +#define ARG_H + +#define USED(x) ((void)(x)) + +extern char *argv0; + +#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char _argc;\ + char **_argv;\ + if(argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + int i_;\ + for(i_ = 1, _argv = argv; argv[0][i_];\ + i_++) {\ + if(_argv != argv)\ + break;\ + _argc = argv[0][i_];\ + switch(_argc) + +#define ARGEND }\ + USED(_argc);\ + }\ + USED(argv);\ + USED(argc); + +#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + (argc--, argv++, argv[0])) + +#endif + diff --git a/cli/main.c b/cli/main.c @@ -2,6 +2,7 @@ * Based on the libwebp example program "anim_dump" */ +// to use the GNU version of basename() that doesn't modify its argument #define _GNU_SOURCE #include <stdio.h> @@ -14,6 +15,7 @@ #include <string.h> #include <strings.h> #include <assert.h> +#include "arg.h" #ifdef __OpenBSD__ #include <sys/syslimits.h> @@ -33,7 +35,9 @@ #define NUM_CHANNELS 4 -static void print_webp_version() { +char *argv0; + +static void print_webp_version(void) { int dec_ver = WebPGetDecoderVersion(); int demux_ver = WebPGetDemuxVersion(); printf("---------------------------\n"); @@ -48,6 +52,7 @@ typedef struct { } DecodedFrame; typedef struct { + char *path; int width, height, bgcolor, loop_count; DecodedFrame *frames; uint32_t frame_count; @@ -82,6 +87,11 @@ void alloc_image(AnimatedImage *img, uint32_t frame_count) { img->raw_mem = mem; } +void free_image(AnimatedImage *img) { + free(img->frames); + free(img->raw_mem); +} + int read_file(const char *fname, const uint8_t **data, size_t *size) { assert(data != NULL); assert(size != NULL); @@ -104,7 +114,7 @@ int read_file(const char *fname, const uint8_t **data, size_t *size) { if (!ok) { fprintf(stderr, "cannot read file %s (%d)\n", fname, ok); free(fdata); - return -1; + return 1; } *data = fdata; *size = fsize; @@ -117,25 +127,27 @@ int read_webp(const char *fname, AnimatedImage *anim) { WebPData webp_data; WebPDataInit(&webp_data); - if (read_file(fname, &webp_data.bytes, &webp_data.size) == -1) { - fprintf(stderr, "read_file error\n"); - return -1; + if (read_file(fname, &webp_data.bytes, &webp_data.size)!=0) { + fputs("Error reading file", stderr); + return 1; } if (!WebPGetInfo(webp_data.bytes, webp_data.size, NULL, NULL)) { - fprintf(stderr, "invalid webp\n"); + fputs("Error: invalid webp", stderr); WebPDataClear(&webp_data); - return -1; + return 1; } WebPAnimDecoder *dec = WebPAnimDecoderNew(&webp_data, NULL); - assert(dec != NULL); + if (dec==NULL) { + return 1; + } WebPAnimInfo anim_info; if (!WebPAnimDecoderGetInfo(dec, &anim_info)) { - fprintf(stderr, "error decoding animation info\n"); - // @todo cleanup - return -1; + fputs("Error decoding animation info", stderr); + WebPAnimDecoderDelete(dec); + return 1; } anim->width = anim_info.canvas_width; @@ -151,10 +163,10 @@ int read_webp(const char *fname, AnimatedImage *anim) { uint8_t *curr_rgba, *frame_rgba; int ts; if (!WebPAnimDecoderGetNext(dec, &frame_rgba, &ts)) { - fprintf(stderr, "error decoding frame %d\n", frame_index); - return -1; + fprintf(stderr, "Error decoding frame %d\n", frame_index); + return 1; } - assert(frame_index < anim_info.frame_count); + assert(frame_index<anim_info.frame_count); frame = &anim->frames[frame_index]; curr_rgba = frame->rgba; frame->duration = ts - prev_ts; @@ -163,13 +175,13 @@ int read_webp(const char *fname, AnimatedImage *anim) { ++frame_index; prev_ts = ts; } - + WebPAnimDecoderReset(dec); WebPDataClear(&webp_data); WebPAnimDecoderDelete(dec); return 0; } -void write_webp(const char *fname, AnimatedImage *img, int cols) { +void write_webp(AnimatedImage *img, const char *fname, int cols) { int rows = (int)img->frame_count / cols; if ((int)img->frame_count % cols > 0) { ++rows; @@ -180,9 +192,9 @@ void write_webp(const char *fname, AnimatedImage *img, int cols) { size_t frame_size = img->width * img->height * sizeof(uint32_t); size_t line_size = img->width * sizeof(uint32_t); size_t full_line = line_size * cols; - uint8_t *merged = calloc(rows * cols, frame_size); - assert(merged!=NULL); - uint8_t *merged_orig = merged; + uint8_t *merged_orig = calloc(rows * cols, frame_size); + assert(merged_orig!=NULL); + uint8_t *merged = merged_orig; for (int row = 0; row < rows; ++row) { for (int y = 0; y < img->height; ++y) { for (int col = 0; col < cols; ++col) { @@ -206,27 +218,70 @@ void write_webp(const char *fname, AnimatedImage *img, int cols) { fclose(fp); } -int main(int argc, const char **argv) { +int save_file(AnimatedImage *img, char *out, int cols, int width) { + char out_name[PATH_MAX]; + char *in_name = basename(img->path); + char *in_dir = dirname(img->path); + int n = snprintf(out_name, NAME_MAX, "atlas_%s", in_name); + if (n<=0) return 1; + printf("[path:%s][%s -> %s(%d)]\ndimensions: %dx%d\nframes: %d\n", in_dir, in_name, out_name, cols, img->width, img->height, img->frame_count); + write_webp(img, out_name, cols); + + return 0; +} + +void print_usage(void) { + printf("Usage: %s [-w MAX_WIDTH_PIXELS] [-c COLUMNS] [-o OUTPUT_FILE] input_file.webp\n", argv0); + exit(1); +} + +int main(int argc, char **argv) { atexit(print_webp_version); - if (argc < 3) { - printf("Usage: %s anim_file.webp COLUMNS\n", argc>0?argv[0]:"emote2ss"); - return 0; + char *in_path = argv[argc-1]; + char *out_path = NULL; + int cols = 0; + int max_width = 0; + printf("argc:%d\n", argc); + + ARGBEGIN { + case 'o': + out_path = EARGF(print_usage()); + break; + case 'c': + cols = atoi(EARGF(print_usage())); + break; + case 'w': + max_width = atoi(EARGF(print_usage())); + break; + default: + print_usage(); + } ARGEND; + + printf("in_path: %s\n", in_path); + printf("out_path: %s\n", out_path); + printf("cols: %d\n", cols); + printf("max_width: %d\n", max_width); + + if (in_path==NULL||(cols<=0 && max_width<=0)) { + puts("invalid arguments"); + print_usage(); + return 1; } - AnimatedImage img = {0}; - char out_name[PATH_MAX]; - char *in_path = strndup(argv[1], PATH_MAX-1); - assert(in_path!=NULL); - int cols = atoi(argv[2]); - int r = read_webp(in_path, &img); - assert(r==0); - char *in_name = basename((char *)in_path); - char *in_dir = dirname((char *)in_path); - int n = snprintf(out_name, NAME_MAX, "atlas_%s", in_name); - assert(n>0); - printf("[path:%s][%s -> %s(%d)]\ndimensions: %dx%d\nframes: %d\n", in_dir, in_name, out_name, cols, img.width, img.height, img.frame_count); - write_webp(out_name, &img, cols); + AnimatedImage img = { .path=in_path }; + + if (read_webp(in_path, &img)!=0) { + fputs("Error parsing WEBP", stderr); + return 1; + } + + if (save_file(&img, out_path, cols, max_width)!=0) { + fputs("Error saving file", stderr); + return 1; + } + + free_image(&img); return 0; }