Skip to content

Instantly share code, notes, and snippets.

@ohga
Created October 16, 2017 04:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ohga/3df1313d7d396fe0280d5eb7fd06ab02 to your computer and use it in GitHub Desktop.
Save ohga/3df1313d7d396fe0280d5eb7fd06ab02 to your computer and use it in GitHub Desktop.
// 捨てコード。
// 某手書きタブレットが吐く筆跡のベクター情報の入った 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