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:
| M | cli/Makefile | | | 5 | ++++- |
| A | cli/arg.h | | | 37 | +++++++++++++++++++++++++++++++++++++ |
| M | cli/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;
}