main.c (5928B)
1 /** 2 * Based on the libwebp example program "anim_dump" 3 */ 4 5 #define _GNU_SOURCE 6 7 #include <stdio.h> 8 #include "webp/decode.h" 9 #include "webp/encode.h" 10 #include "webp/demux.h" 11 #include <stdbool.h> 12 #include <stdlib.h> 13 #include <libgen.h> 14 #include <string.h> 15 #include <strings.h> 16 #include <assert.h> 17 18 #ifdef __OpenBSD__ 19 #include <sys/syslimits.h> 20 #endif 21 22 #ifdef __linux__ 23 #include <linux/limits.h> 24 #endif 25 26 #define NUM_CHANNELS 4 27 28 static void print_webp_version() { 29 int dec_ver = WebPGetDecoderVersion(); 30 int demux_ver = WebPGetDemuxVersion(); 31 printf("---------------------------\n"); 32 printf("webp decoder version: %d.%d.%d\n", (dec_ver>>16)&0xff, (dec_ver>>8)&0xff, dec_ver&0xff); 33 printf("webp demuxer version: %d.%d.%d\n", (demux_ver>>16)&0xff, (demux_ver>>8)&0xff, demux_ver&0xff); 34 } 35 36 typedef struct { 37 uint8_t *rgba; 38 int duration; 39 bool is_key; 40 } DecodedFrame; 41 42 typedef struct { 43 int width, height, bgcolor, loop_count; 44 DecodedFrame *frames; 45 uint32_t frame_count; 46 void *raw_mem; 47 } AnimatedImage; 48 49 void alloc_image(AnimatedImage *img, uint32_t frame_count) { 50 assert(frame_count > 0); 51 uint8_t *mem = NULL; 52 DecodedFrame *frames = NULL; 53 const uint64_t rgba_size = img->width * img->height * NUM_CHANNELS; 54 const uint64_t img_size = frame_count * rgba_size * sizeof(uint8_t); 55 const uint64_t frames_size = frame_count * sizeof(DecodedFrame); 56 57 assert(img_size == (size_t)img_size); 58 assert(frames_size == (size_t)frames_size); 59 60 printf("img mem: %" PRIu64 "\nframes mem: %" PRIu64 "\n", img_size, frames_size); 61 62 mem = malloc(img_size); 63 frames = malloc(frames_size); 64 assert(mem != NULL); 65 assert(frames != NULL); 66 67 for (uint32_t i=0; i<frame_count; ++i) { 68 frames[i].rgba = mem+i*rgba_size; 69 frames[i].duration = 0; 70 frames[i].is_key = false; 71 } 72 img->frame_count = frame_count; 73 img->frames = frames; 74 img->raw_mem = mem; 75 } 76 77 int read_file(const char *fname, const uint8_t **data, size_t *size) { 78 assert(data != NULL); 79 assert(size != NULL); 80 81 *data = NULL; 82 *size = 0; 83 FILE *infile = fopen(fname, "rb"); 84 assert(infile != NULL); 85 fseek(infile, 0, SEEK_END); 86 size_t fsize = ftell(infile); 87 printf("%s: %zu bytes\n", fname, fsize); 88 fseek(infile, 0, SEEK_SET); 89 90 uint8_t *fdata = malloc(fsize+1); 91 assert(fdata != NULL); 92 fdata[fsize] = '\0'; 93 int ok = (fread(fdata, fsize, 1, infile)==1); 94 fclose(infile); 95 96 if (!ok) { 97 fprintf(stderr, "cannot read file %s (%d)\n", fname, ok); 98 free(fdata); 99 return -1; 100 } 101 *data = fdata; 102 *size = fsize; 103 return 0; 104 } 105 106 int read_webp(const char *fname, AnimatedImage *anim) { 107 printf("read_webp(%s)\n", fname); 108 109 WebPData webp_data; 110 WebPDataInit(&webp_data); 111 112 if (read_file(fname, &webp_data.bytes, &webp_data.size) == -1) { 113 fprintf(stderr, "read_file error\n"); 114 return -1; 115 } 116 117 if (!WebPGetInfo(webp_data.bytes, webp_data.size, NULL, NULL)) { 118 fprintf(stderr, "invalid webp\n"); 119 WebPDataClear(&webp_data); 120 return -1; 121 } 122 123 WebPAnimDecoder *dec = WebPAnimDecoderNew(&webp_data, NULL); 124 assert(dec != NULL); 125 126 WebPAnimInfo anim_info; 127 if (!WebPAnimDecoderGetInfo(dec, &anim_info)) { 128 fprintf(stderr, "error decoding animation info\n"); 129 // @todo cleanup 130 return -1; 131 } 132 133 anim->width = anim_info.canvas_width; 134 anim->height = anim_info.canvas_height; 135 anim->loop_count = anim_info.loop_count; 136 anim->bgcolor = anim_info.bgcolor; 137 alloc_image(anim, anim_info.frame_count); 138 139 uint32_t frame_index = 0; 140 int prev_ts = 0; 141 while (WebPAnimDecoderHasMoreFrames(dec)) { 142 DecodedFrame *frame; 143 uint8_t *curr_rgba, *frame_rgba; 144 int ts; 145 if (!WebPAnimDecoderGetNext(dec, &frame_rgba, &ts)) { 146 fprintf(stderr, "error decoding frame %d\n", frame_index); 147 return -1; 148 } 149 assert(frame_index < anim_info.frame_count); 150 frame = &anim->frames[frame_index]; 151 curr_rgba = frame->rgba; 152 frame->duration = ts - prev_ts; 153 memcpy(curr_rgba, frame_rgba, anim->width * anim->height * NUM_CHANNELS); 154 // ... <- nani kore? 155 ++frame_index; 156 prev_ts = ts; 157 } 158 159 WebPDataClear(&webp_data); 160 WebPAnimDecoderDelete(dec); 161 return 0; 162 } 163 164 void write_webp(const char *fname, AnimatedImage *img, int cols) { 165 int rows = (int)img->frame_count / cols; 166 if ((int)img->frame_count % cols > 0) { 167 ++rows; 168 } 169 FILE *fp = fopen(fname, "wb"); 170 assert(fp!=NULL); 171 uint8_t *out; 172 size_t frame_size = img->width * img->height * sizeof(uint32_t); 173 size_t line_size = img->width * sizeof(uint32_t); 174 size_t full_line = line_size * cols; 175 uint8_t *merged = calloc(rows * cols, frame_size); 176 assert(merged!=NULL); 177 uint8_t *merged_orig = merged; 178 for (int row = 0; row < rows; ++row) { 179 for (int y = 0; y < img->height; ++y) { 180 for (int col = 0; col < cols; ++col) { 181 uint32_t offset = row*cols+col; 182 if (offset < img->frame_count) { 183 memcpy(merged, img->frames[offset].rgba+y*line_size, line_size); 184 } 185 merged += line_size; 186 } 187 } 188 } 189 int stride = full_line; 190 printf("stride: %d\n", stride); 191 size_t encoded = WebPEncodeLosslessRGBA(merged_orig, img->width * cols, img->height * rows, stride, &out); 192 printf("size: %zu, encoded: %zu\n", img->width*img->height*sizeof(uint32_t), encoded); 193 assert(encoded!=0); 194 size_t written = fwrite(out, sizeof(uint8_t), encoded, fp); 195 assert(written==encoded); 196 WebPFree(out); 197 free(merged_orig); 198 fclose(fp); 199 } 200 201 int main(int argc, const char **argv) { 202 atexit(print_webp_version); 203 204 if (argc < 3) { 205 printf("Usage: %s anim_file.webp COLUMNS\n", argc>0?argv[0]:"emote2ss"); 206 return 0; 207 } 208 209 AnimatedImage img = {0}; 210 char out_name[PATH_MAX]; 211 char *in_path = strndup(argv[1], PATH_MAX-1); 212 assert(in_path!=NULL); 213 int cols = atoi(argv[2]); 214 int r = read_webp(in_path, &img); 215 assert(r==0); 216 char *in_name = basename((char *)in_path); 217 char *in_dir = dirname((char *)in_path); 218 int n = snprintf(out_name, NAME_MAX, "atlas_%s", in_name); 219 assert(n>0); 220 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); 221 write_webp(out_name, &img, cols); 222 223 return 0; 224 }