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