summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile12
-rw-r--r--main.c57
3 files changed, 64 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index f815932..4059c54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/kd-forest
*.png
+*.mkv
diff --git a/Makefile b/Makefile
index a4f8280..9cc99c6 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,17 @@ kd-forest.png: kd-forest
image: kd-forest.png
+kd-forest.mkv: kd-forest
+ $(RM) kd-forest.mkv
+ $(RM) -r frames
+ mkdir -p frames
+ ./kd-forest -b 20 -s -c Lab -a -o frames
+ ffmpeg -r 60 -i frames/%04d.png -c:v libx264 -preset veryslow -qp 0 kd-forest.mkv
+
+anim: kd-forest.mkv
+
clean:
$(RM) kd-forest
+ $(RM) -r frames
-.PHONY: image clean
+.PHONY: image anim clean
diff --git a/main.c b/main.c
index ca13889..f4912de 100644
--- a/main.c
+++ b/main.c
@@ -9,6 +9,8 @@
* the COPYING file or http://www.wtfpl.net/ for more details. *
*********************************************************************/
+#define _POSIX_C_SOURCE 200809L
+
#include "kd-forest.h"
#include "util.h"
#include "color.h"
@@ -43,6 +45,7 @@ typedef struct {
unsigned int bit_depth;
mode_t mode;
color_space_t color_space;
+ bool animate;
const char *filename;
bool help;
} options_t;
@@ -168,15 +171,19 @@ print_usage(FILE *file, const char *command)
fprintf(file, " $ %s [-b|--bit-depth DEPTH]\n", command);
fprintf(file, " %s [-s|--hue-sort] [-r|--random]\n", whitespace);
fprintf(file, " %s [-c|--color-space RGB|Lab|Luv]\n", whitespace);
+ fprintf(file, " %s [-a|--animate]\n", whitespace);
fprintf(file, " %s [-o|--output PATH]\n", whitespace);
fprintf(file, " %s [-h|--help]\n", whitespace);
fprintf(file, "\n");
- fprintf(file, " -b, --bit-depth DEPTH: Use all DEPTH-bit colors (default: 24)\n");
+ fprintf(file, " -b, --bit-depth DEPTH: Use all DEPTH-bit colors (default: 24)\n\n");
fprintf(file, " -s, --hue-sort: Sort colors by hue first (default)\n");
- fprintf(file, " -r, --random: Randomize colors first\n");
+ fprintf(file, " -r, --random: Randomize colors first\n\n");
fprintf(file, " -c, --color-space RGB|Lab|Luv:\n");
- fprintf(file, " Use the given color space (default: Lab)\n");
+ fprintf(file, " Use the given color space (default: Lab)\n\n");
+ fprintf(file, " -a, --animate: Generate frames of an animation\n\n");
fprintf(file, " -o, --output PATH: Output a PNG file at PATH (default: kd-forest.png)\n");
+ fprintf(file, " If -a/--animate is specified, this is treated as a\n");
+ fprintf(file, " directory which will hold many frames\n\n");
fprintf(file, " -h, --help: Show this message\n");
}
@@ -187,7 +194,8 @@ parse_options(options_t *options, int argc, char *argv[])
options->bit_depth = 24;
options->mode = MODE_HUE_SORT;
options->color_space = COLOR_SPACE_LAB;
- options->filename = "kd-forest.png";
+ options->animate = false;
+ options->filename = NULL;
options->help = false;
bool result = true;
@@ -207,6 +215,8 @@ parse_options(options_t *options, int argc, char *argv[])
options->mode = MODE_HUE_SORT;
} else if (parse_arg(argc, argv, "-r", "--random", NULL, &i, &error)) {
options->mode = MODE_RANDOM;
+ } else if (parse_arg(argc, argv, "-a", "--animate", NULL, &i, &error)) {
+ options->animate = true;
} else if (parse_arg(argc, argv, "-o", "--output", &value, &i, &error)) {
options->filename = value;
} else if (parse_arg(argc, argv, "-c", "--color-space", &value, &i, &error)) {
@@ -231,6 +241,11 @@ parse_options(options_t *options, int argc, char *argv[])
}
}
+ // Default filename depends on -a flag
+ if (!options->filename) {
+ options->filename = options->animate ? "frames" : "kd-forest.png";
+ }
+
return result;
}
@@ -318,7 +333,10 @@ static void
generate_image(const state_t *state)
{
generate_bitmap(state);
- write_png(state, state->options->filename);
+
+ if (!state->options->animate) {
+ write_png(state, state->options->filename);
+ }
}
static void
@@ -440,6 +458,10 @@ generate_bitmap(const state_t *state)
kd_forest_t kdf;
kdf_init(&kdf);
+ bool animate = state->options->animate;
+ unsigned int frame = 0;
+ char filename[strlen(state->options->filename) + 10];
+
size_t max_size = 0;
// Do multiple passes to get rid of artifacts in HUE_SORT mode
@@ -449,6 +471,12 @@ generate_bitmap(const state_t *state)
for (unsigned int j = stripe/2 - 1; j < state->size; j += stripe, ++progress) {
if (progress%state->width == 0) {
+ if (animate) {
+ sprintf(filename, "%s/%04u.png", state->options->filename, frame);
+ write_png(state, filename);
+ ++frame;
+ }
+
print_progress("%.2f%%\t| boundary size: %zu\t| max boundary size: %zu",
100.0*progress/state->size, kdf.size, max_size);
}
@@ -490,6 +518,25 @@ generate_bitmap(const state_t *state)
}
}
+ if (animate) {
+#if __unix__
+ sprintf(filename, "%s/last.png", state->options->filename);
+ write_png(state, filename);
+
+ for (int i = 0; i < 120; ++i) {
+ sprintf(filename, "%s/%04u.png", state->options->filename, frame + i);
+ if (symlink("last.png", filename) != 0) {
+ abort();
+ }
+ }
+#else
+ for (int i = 0; i < 120; ++i) {
+ sprintf(filename, "%s/%04u.png", state->options->filename, frame + i);
+ write_png(state, filename);
+ }
+#endif
+ }
+
print_progress("%.2f%%\t| boundary size: 0\t| max boundary size: %zu\n",
100.0, max_size);