Created
October 16, 2017 04:51
-
-
Save ohga/3df1313d7d396fe0280d5eb7fd06ab02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 捨てコード。 | |
// 某手書きタブレットが吐く筆跡のベクター情報の入った json ファイルを svg にして、筆跡を辿って細々と連番画像ファイル化 | |
// 吐かれた画像を ffmpeg コマンドで乱暴に動画化する | |
#include <stdbool.h> | |
#include <unistd.h> | |
#include <getopt.h> | |
#include <string.h> | |
#include <limits.h> | |
#include <dirent.h> | |
#include <sys/stat.h> | |
#include <openssl/md5.h> | |
#include <wand/magick-wand.h> | |
#include "parson.h" | |
#define DIRBASE "/tmp" | |
#define BUFMAX 256 | |
#define TEXTMAX 512 * 1024 * 1024 | |
#define FPS 23.97 | |
#define WIDTH 384 | |
#define HEIGHT 512 | |
static int s_init(int argc, char **argv); | |
static int s_main(); | |
static void s_term(bool flg); | |
// TODO.. | |
static int validate_num(const char *str); | |
static int validate_num2(const char *str); | |
static int validate_rgb(const char *str); | |
static int validate_alnumex2(const char *str); | |
static int validate_alnumex3(const char *str); | |
static int max_sec = 0; | |
static char infile[BUFMAX]; | |
static char rgb_b[BUFMAX]; | |
static char rgb_f[BUFMAX]; | |
static double pen_ex; | |
static char pass[BUFMAX]; | |
static char outdir[BUFMAX]; | |
static JSON_Value *json_val; | |
int main(int argc, char **argv)/* {{{ */ | |
{ | |
int rtn; | |
rtn = s_init(argc, argv); | |
if(rtn != 0) return 1; | |
rtn = s_main(); | |
if(rtn != 0){ | |
s_term(false); | |
return 1; | |
} | |
s_term(true); | |
return 0; | |
}/* }}} */ | |
static int s_init(int argc, char **argv)/* {{{ */ | |
{ | |
int rtn, opt; | |
FILE *fp; | |
char fn[BUFMAX], buf[BUFMAX]; | |
char *res; | |
MD5_CTX ctx; | |
unsigned char md5sum[16]; | |
max_sec = 5; | |
memset(infile,'\0',BUFMAX); | |
memset(rgb_b,'\0',BUFMAX); | |
memset(rgb_f,'\0',BUFMAX); | |
pen_ex = 1.0; | |
memset(pass,'\0',BUFMAX); | |
memset(outdir,'\0',BUFMAX); | |
json_val = NULL; | |
while((opt = getopt(argc, argv, "s:i:B:F:p:P:")) != -1){/* {{{ */ | |
switch(opt){ | |
case 's':/* {{{ */ | |
rtn = validate_num(optarg); | |
if(rtn != 0) return -1; | |
max_sec = atoi(optarg); | |
break;/* }}} */ | |
case 'i':/* {{{ */ | |
rtn = validate_alnumex2(optarg); | |
if(rtn != 0) return -1; | |
snprintf(infile,BUFMAX,"%s",optarg); | |
break;/* }}} */ | |
case 'B':/* {{{ */ | |
rtn = validate_rgb(optarg); | |
if(rtn != 0) return -1; | |
snprintf(rgb_b,BUFMAX,"%s",optarg); | |
break;/* }}} */ | |
case 'F':/* {{{ */ | |
rtn = validate_rgb(optarg); | |
if(rtn != 0) return -1; | |
snprintf(rgb_f,BUFMAX,"%s",optarg); | |
break;/* }}} */ | |
case 'p':/* {{{ */ | |
rtn = validate_num2(optarg); | |
if(rtn != 0) return -1; | |
pen_ex = atof(optarg); | |
break;/* }}} */ | |
case 'P':/* {{{ */ | |
rtn = validate_alnumex3(optarg); | |
if(rtn != 0) return -1; | |
snprintf(pass,BUFMAX,"%s",optarg); | |
break;/* }}} */ | |
case '?': default: return -1; | |
} | |
}/* }}} */ | |
if(strlen(infile) == 0) return -1; | |
fp = fopen(infile, "r"); | |
if(fp == NULL) return -1; | |
res = (char*)malloc(TEXTMAX); | |
memset(res, '\0', TEXTMAX); | |
fgets(res, TEXTMAX, fp); | |
fclose(fp); | |
MD5_Init(&ctx); | |
MD5_Update(&ctx, res, strlen(res)); | |
MD5_Final(md5sum, &ctx); | |
snprintf(outdir, BUFMAX, | |
"%02x%02x" | |
"/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" | |
"/%dx%d_%d_%s_%s_%lf", | |
md5sum[0], md5sum[1], | |
md5sum[2], md5sum[3], md5sum[4], md5sum[5], md5sum[6], | |
md5sum[7], md5sum[8], md5sum[9], md5sum[10], md5sum[11], | |
md5sum[12], md5sum[13], md5sum[14], md5sum[15], | |
WIDTH, HEIGHT, max_sec, rgb_b, rgb_f, pen_ex | |
); | |
snprintf(fn, BUFMAX, DIRBASE "/%s/out.mp4", outdir); | |
fp = fopen(fn, "r"); | |
if(fp != NULL) { | |
fclose(fp); | |
free(res); | |
printf(DIRBASE "/%s/pack.zip\n", outdir); | |
return -1; | |
} | |
json_val = json_parse_string(res); | |
if(json_val == NULL) return -1; | |
free(res); | |
chdir(DIRBASE); | |
snprintf(buf, BUFMAX, "%02x%02x/", md5sum[0], md5sum[1]); | |
if(mkdir(buf, 0755) != 0) return -1; | |
chdir(buf); | |
snprintf(buf, BUFMAX, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x/", | |
md5sum[2], md5sum[3], md5sum[4], md5sum[5], md5sum[6], | |
md5sum[7], md5sum[8], md5sum[9], md5sum[10], md5sum[11], | |
md5sum[12], md5sum[13], md5sum[14], md5sum[15]); | |
if(mkdir(buf, 0755) != 0) return -1; | |
chdir(buf); | |
snprintf(buf, BUFMAX, "%dx%d_%d_%s_%s_%lf/", | |
WIDTH, HEIGHT, max_sec, rgb_b, rgb_f, pen_ex); | |
if(mkdir(buf, 0755) != 0) return -1; | |
chdir(buf); | |
if(mkdir("jpg/", 0755) != 0) return -1; | |
return 0; | |
}/* }}} */ | |
static int s_main()/* {{{ */ | |
{ | |
char fn[BUFMAX], cmd[BUFMAX]; | |
JSON_Object *json_obj; | |
JSON_Array *strokes; | |
double width, height, color; | |
MagickWand *mw, *mw_all; | |
DrawingWand *dw, *dw_all; | |
PixelWand *pw; | |
char pw_tmp[32]; | |
long frames_cnt = 1; | |
long skip = INT_MAX; | |
long skip_tmp; | |
long jpg_cnt = 0; | |
json_obj = json_value_get_object(json_val); | |
width = json_object_dotget_number(json_obj, "width"); | |
height = json_object_dotget_number(json_obj, "height"); | |
color = json_object_dotget_number(json_obj, "color"); | |
strokes = json_object_get_array(json_obj, "strokes"); | |
if(json_obj == NULL || strokes == NULL) return -1; | |
for(int ii = 0; ii < json_array_get_count(strokes); ii++) { | |
JSON_Object *stroke_obj = json_array_get_object(strokes, ii); | |
if(stroke_obj == NULL) continue; | |
JSON_Array *data_arr = json_object_get_array(stroke_obj, "data"); | |
if(data_arr == NULL) continue; | |
frames_cnt += json_array_get_count(data_arr) / 3; | |
} | |
if(frames_cnt > (max_sec * FPS)) | |
skip = frames_cnt / (max_sec * FPS); | |
skip_tmp = skip; | |
MagickWandGenesis(); | |
mw = NewMagickWand(); | |
mw_all = NewMagickWand(); | |
pw = NewPixelWand(); | |
if(strlen(rgb_b) != 0) | |
snprintf(pw_tmp, 32, "#%s", rgb_b); | |
else { | |
long ll = (long)color; | |
snprintf(pw_tmp, 32, "rgb(%d,%d,%d)", | |
(int)((ll >> 16)&0xff), | |
(int)((ll >> 8 )&0xff), | |
(int)((ll >> 0 )&0xff) | |
); | |
PixelSetAlpha(pw, (((ll >> 24)&0xff) / 255.0)); | |
} | |
PixelSetColor(pw, pw_tmp); | |
MagickNewImage(mw, width, height, pw); | |
MagickNewImage(mw_all, width, height, pw); | |
ClearPixelWand(pw); | |
dw = NewDrawingWand(); | |
MagickDrawImage(mw, dw); | |
snprintf(fn,BUFMAX, "jpg/%09ld.jpg", jpg_cnt++); | |
MagickWriteImage(mw, fn); | |
ClearDrawingWand(dw); | |
skip_tmp--; | |
dw_all = NewDrawingWand(); | |
for(int ii = 0; ii < json_array_get_count(strokes); ii++) {/* {{{ */ | |
JSON_Object *stroke_obj = json_array_get_object(strokes, ii); | |
double stroke_width = json_object_dotget_number(stroke_obj, "width"); | |
double stroke_color = json_object_dotget_number(stroke_obj, "color"); | |
stroke_width = stroke_width * pen_ex; | |
dw = NewDrawingWand(); | |
if(strlen(rgb_f) != 0) | |
snprintf(pw_tmp, 32, "#%s", rgb_f); | |
else { | |
long ll = (long)stroke_color; | |
snprintf(pw_tmp, 32, "rgb(%d,%d,%d)", | |
(int)((ll >> 16)&0xff), | |
(int)((ll >> 8 )&0xff), | |
(int)((ll >> 0 )&0xff) | |
); | |
PixelSetAlpha(pw, (((ll >> 24)&0xff) / 255.0)); | |
} | |
PixelSetColor(pw, pw_tmp); | |
DrawSetStrokeColor(dw, pw); | |
DrawSetStrokeColor(dw_all, pw); | |
double old_xx, old_yy; | |
JSON_Array *data_arr = json_object_get_array(stroke_obj, "data"); | |
for(int jj = 0;jj < json_array_get_count(data_arr); jj+=3) {/* {{{ */ | |
double xx = json_array_get_number(data_arr, jj); | |
double yy = json_array_get_number(data_arr, jj + 1); | |
double ww = json_array_get_number(data_arr, jj + 2); | |
if(jj == 0){ | |
old_xx = xx; | |
old_yy = yy; | |
continue; | |
} | |
DrawSetStrokeWidth(dw, stroke_width * ww); | |
DrawSetStrokeWidth(dw_all, stroke_width * ww); | |
DrawLine(dw, old_xx, old_yy, xx, yy); | |
DrawLine(dw_all, old_xx, old_yy, xx, yy); | |
if(skip_tmp <= 0){ | |
MagickDrawImage(mw, dw); | |
snprintf(fn, BUFMAX, "jpg/%09ld.jpg", jpg_cnt++); | |
MagickWriteImage(mw, fn); | |
skip_tmp = skip; | |
} else | |
skip_tmp--; | |
old_xx = xx; | |
old_yy = yy; | |
}/* }}} */ | |
MagickDrawImage(mw, dw); | |
ClearPixelWand(pw); | |
ClearDrawingWand(dw); | |
DestroyDrawingWand(dw); | |
}/* }}} */ | |
DestroyPixelWand(pw); | |
snprintf(fn, BUFMAX, "jpg/%09ld.jpg", jpg_cnt++); | |
MagickWriteImage(mw, fn); | |
DestroyMagickWand(mw); | |
link(fn, "out.jpg"); | |
MagickDrawImage(mw_all, dw_all); | |
MagickWriteImage(mw_all, "out.svg"); | |
MagickWriteImage(mw_all, "out.ai"); | |
MagickWriteImage(mw_all, "out.psd"); | |
MagickWriteImage(mw_all, "out.tiff"); | |
DestroyDrawingWand(dw_all); | |
DestroyMagickWand(mw_all); | |
MagickCoreTerminus(); | |
snprintf(cmd, BUFMAX, | |
"ffmpeg -i 'jpg/%%09d.jpg' -r %lf -vcodec libx264 -profile baseline " | |
"-s %dx%d -coder 0 -bf 0 out.mp4 2> ffmpeg.log", FPS, WIDTH, HEIGHT); | |
system(cmd); | |
snprintf(cmd, BUFMAX, | |
"ffmpeg -i 'jpg/%%09d.jpg' -r %lf " | |
"-s %dx%d -coder 0 -bf 0 out.ogv 2>> ffmpeg.log", FPS, WIDTH, HEIGHT); | |
system(cmd); | |
snprintf(cmd, BUFMAX, | |
"ffmpeg -i 'jpg/%%09d.jpg' -r %lf " | |
"-s %dx%d -coder 0 -bf 0 out.webm 2>> ffmpeg.log", FPS, WIDTH, HEIGHT); | |
system(cmd); | |
return 0; | |
}/* }}} */ | |
static void s_term(bool flg)/* {{{ */ | |
{ | |
char cmd[BUFMAX]; | |
if(json_val != NULL) json_value_free(json_val); | |
if(flg == false) return; | |
if(strlen(outdir) != 0){ | |
DIR *dp; | |
struct dirent *ent; | |
if(strlen(pass) != 0) | |
snprintf(cmd, BUFMAX, | |
"zip -e -P '%s' -r pack.zip out.* >> ffmpeg.log", pass); | |
else | |
snprintf(cmd, BUFMAX, | |
"zip -r pack.zip out.* >> ffmpeg.log"); | |
system(cmd); | |
if((dp = opendir("jpg")) != NULL){ | |
chdir("jpg"); | |
while((ent = readdir(dp)) != NULL) unlink(ent->d_name); | |
chdir(".."); | |
closedir(dp); | |
} | |
rmdir("jpg"); | |
unlink("info.json"); | |
unlink("out.psd"); | |
unlink("out.svg"); | |
unlink("out.ai"); | |
unlink("out.tiff"); | |
printf(DIRBASE "/%s/pack.zip\n",outdir); | |
} | |
return; | |
}/* }}} */ | |
// TODO.. | |
static int validate_num(const char *str)/* {{{ */ | |
{ | |
int len; | |
int ii; | |
if(str == NULL) return -1; | |
len = strlen(str); | |
for(ii = 0; ii< len;ii++){ | |
if(str[ii] >= '0' && str[ii] <= '9') continue; | |
return -1; | |
} | |
return 0; | |
}/* }}} */ | |
static int validate_num2(const char *str)/* {{{ */ | |
{ | |
int len; | |
int ii; | |
int flg = 0; | |
if(str == NULL) return -1; | |
len = strlen(str); | |
for(ii = 0; ii< len;ii++){ | |
if(str[ii] >= '0' && str[ii] <= '9') continue; | |
if(str[ii] == '.'){ | |
if(flg != 0) return -1; | |
flg = 1; | |
continue; | |
} | |
return -1; | |
} | |
return 0; | |
}/* }}} */ | |
static int validate_rgb(const char *str)/* {{{ */ | |
{ | |
int len; | |
int ii; | |
if(str == NULL) return -1; | |
len = strlen(str); | |
if(len != 6) return -1; | |
for(ii = 0; ii< len;ii++){ | |
if(str[ii] >= '0' && str[ii] <= '9') continue; | |
if(str[ii] >= 'A' && str[ii] <= 'F') continue; | |
if(str[ii] >= 'a' && str[ii] <= 'f') continue; | |
return -1; | |
} | |
return 0; | |
}/* }}} */ | |
static int validate_alnumex2(const char *str)/* {{{ */ | |
{ | |
int len; | |
int ii; | |
if(str == NULL) return -1; | |
len = strlen(str); | |
for(ii = 0; ii< len;ii++){ | |
if(str[ii] >= 'A' && str[ii] <= 'Z') continue; | |
if(str[ii] >= '0' && str[ii] <= '9') continue; | |
if(str[ii] >= 'a' && str[ii] <= 'z') continue; | |
if(str[ii] == '_' || str[ii] == '.' || str[ii] == '-' || str[ii] == '(' | |
|| str[ii] == ')' || str[ii] == ',' || str[ii] == '/') | |
continue; | |
return -1; | |
} | |
return 0; | |
}/* }}} */ | |
static int validate_alnumex3(const char *str)/* {{{ */ | |
{ | |
int len; | |
int ii; | |
if(str == NULL) return -1; | |
len = strlen(str); | |
for(ii = 0; ii< len;ii++){ | |
if(str[ii] >= 'A' && str[ii] <= 'Z') continue; | |
if(str[ii] >= '0' && str[ii] <= '9') continue; | |
if(str[ii] >= 'a' && str[ii] <= 'z') continue; | |
if(str[ii] == '!' || str[ii] == '@' || str[ii] == '#' || str[ii] == '$' | |
|| str[ii] == '%' || str[ii] == '^' || str[ii] == '&' || str[ii] == '*' | |
|| str[ii] == '(' || str[ii] == ')' || str[ii] == '_' || str[ii] == '-' | |
|| str[ii] == '+' || str[ii] == '=' || str[ii] == '[' || str[ii] == '{' | |
|| str[ii] == ']' || str[ii] == '}' || str[ii] == '|' || str[ii] == ';' | |
|| str[ii] == ':' || str[ii] == '>' || str[ii] == '<' || str[ii] == ',' | |
|| str[ii] == '.' || str[ii] == '/' || str[ii] == ' ') | |
continue; | |
return -1; | |
} | |
return 0; | |
}/* }}} */ | |
// vim: et ts=4 sw=4 sts=4 fenc=utf8 ff=unix noexpandtab: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment