summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2014-08-08 15:13:32 -0400
committerTavian Barnes <tavianator@tavianator.com>2014-08-08 15:13:32 -0400
commit0b7731ebb7c06fd73e945cec777960d450e8192c (patch)
treec9dfaf4f3f6a0335cbfe76d9e9db05267161236f
parentaf442be2fa586a98e8e4da1bb88a9236f5c191d2 (diff)
downloadkd-forest-0b7731ebb7c06fd73e945cec777960d450e8192c.tar.xz
Allow image size and initial position to be specified on the command line.
-rw-r--r--main.c77
-rw-r--r--options.c99
-rw-r--r--options.h5
3 files changed, 126 insertions, 55 deletions
diff --git a/main.c b/main.c
index d255515..7c0ab56 100644
--- a/main.c
+++ b/main.c
@@ -36,9 +36,6 @@ typedef struct {
// All-encompasing state struct
typedef struct {
const options_t *options;
- unsigned int width;
- unsigned int height;
- size_t size;
uint32_t *colors;
pixel_t *pixels;
png_byte **bitmap;
@@ -55,12 +52,12 @@ main(int argc, char *argv[])
options_t options;
if (!parse_options(&options, argc, argv)) {
fprintf(stderr, "\n");
- print_usage(stderr, argv[0]);
+ print_usage(stderr, argv[0], options.help);
return EXIT_FAILURE;
}
if (options.help) {
- print_usage(stdout, argv[0]);
+ print_usage(stdout, argv[0], true);
return EXIT_SUCCESS;
}
@@ -72,16 +69,16 @@ main(int argc, char *argv[])
}
static uint32_t *
-create_colors(const state_t *state)
+create_colors(const options_t *options)
{
- const unsigned int bit_depth = state->options->bit_depth;
+ const unsigned int bit_depth = options->bit_depth;
// From least to most perceptually important
const unsigned int bskip = 1U << (24 - bit_depth)/3;
const unsigned int rskip = 1U << (24 - bit_depth + 1)/3;
const unsigned int gskip = 1U << (24 - bit_depth + 2)/3;
- uint32_t *colors = xmalloc(state->size*sizeof(uint32_t));
+ uint32_t *colors = xmalloc(options->ncolors*sizeof(uint32_t));
for (unsigned int b = 0, i = 0; b < 0x100; b += bskip) {
for (unsigned int g = 0; g < 0x100; g += gskip) {
for (unsigned int r = 0; r < 0x100; r += rskip, ++i) {
@@ -90,14 +87,14 @@ create_colors(const state_t *state)
}
}
- switch (state->options->mode) {
+ switch (options->mode) {
case MODE_HUE_SORT:
- qsort(colors, state->size, sizeof(uint32_t), color_comparator);
+ qsort(colors, options->ncolors, sizeof(uint32_t), color_comparator);
break;
case MODE_RANDOM:
// Fisher-Yates shuffle
- for (unsigned int i = state->size; i-- > 0;) {
+ for (unsigned int i = options->ncolors; i-- > 0;) {
unsigned int j = xrand(i + 1);
uint32_t temp = colors[i];
colors[i] = colors[j];
@@ -110,11 +107,11 @@ create_colors(const state_t *state)
}
static pixel_t *
-create_pixels(const state_t *state)
+create_pixels(const options_t *options)
{
- pixel_t *pixels = xmalloc(state->size*sizeof(pixel_t));
- for (unsigned int y = 0, i = 0; y < state->height; ++y) {
- for (unsigned int x = 0; x < state->width; ++x, ++i) {
+ pixel_t *pixels = xmalloc(options->npixels*sizeof(pixel_t));
+ for (unsigned int y = 0, i = 0; y < options->height; ++y) {
+ for (unsigned int x = 0; x < options->width; ++x, ++i) {
pixel_t *pixel = pixels + i;
pixel->node = NULL;
pixel->x = x;
@@ -126,11 +123,11 @@ create_pixels(const state_t *state)
}
static png_byte **
-create_bitmap(const state_t *state)
+create_bitmap(const options_t *options)
{
- png_byte **rows = xmalloc(state->height*sizeof(png_byte *));
- const size_t row_size = 3*state->width*sizeof(png_byte);
- for (unsigned int i = 0; i < state->height; ++i) {
+ png_byte **rows = xmalloc(options->height*sizeof(png_byte *));
+ const size_t row_size = 4*options->width*sizeof(png_byte);
+ for (unsigned int i = 0; i < options->height; ++i) {
rows[i] = xmalloc(row_size);
memset(rows[i], 0, row_size);
}
@@ -140,19 +137,15 @@ create_bitmap(const state_t *state)
static void
init_state(state_t *state, const options_t *options)
{
+ printf("Generating a %u-bit, %ux%u image (%zu pixels)\n",
+ options->bit_depth, options->width, options->height, options->npixels);
+
xsrand(options->seed);
state->options = options;
- state->width = 1U << (options->bit_depth + 1)/2; // Round up
- state->height = 1U << options->bit_depth/2; // Round down
- state->size = (size_t)state->width*state->height;
-
- printf("Generating a %u-bit, %ux%u image (%zu pixels)\n",
- options->bit_depth, state->width, state->height, state->size);
-
- state->colors = create_colors(state);
- state->pixels = create_pixels(state);
- state->bitmap = create_bitmap(state);
+ state->colors = create_colors(options);
+ state->pixels = create_pixels(options);
+ state->bitmap = create_bitmap(options);
}
static void generate_bitmap(state_t *state);
@@ -171,7 +164,7 @@ generate_image(state_t *state)
static void
destroy_state(state_t *state)
{
- for (unsigned int i = 0; i < state->height; ++i) {
+ for (unsigned int i = 0; i < state->options->height; ++i) {
free(state->bitmap[i]);
}
free(state->bitmap);
@@ -182,7 +175,7 @@ destroy_state(state_t *state)
static pixel_t *
get_pixel(const state_t *state, unsigned int x, unsigned int y)
{
- return state->pixels + state->width*y + x;
+ return state->pixels + state->options->width*y + x;
}
static pixel_t *
@@ -190,15 +183,15 @@ try_neighbor(const state_t *state, pixel_t *pixel, int dx, int dy)
{
if (dx < 0 && pixel->x < -dx) {
return NULL;
- } else if (dx > 0 && pixel->x + dx >= state->width) {
+ } else if (dx > 0 && pixel->x + dx >= state->options->width) {
return NULL;
} else if (dy < 0 && pixel->y < -dy) {
return NULL;
- } else if (dy > 0 && pixel->y + dy >= state->height) {
+ } else if (dy > 0 && pixel->y + dy >= state->options->height) {
return NULL;
}
- return pixel + (int)state->width*dy + dx;
+ return pixel + (int)state->options->width*dy + dx;
}
static unsigned int
@@ -403,8 +396,8 @@ generate_bitmap(state_t *state)
for (unsigned int i = 1, progress = 0; i <= bit_depth + 1; ++i) {
unsigned int stripe = 1 << i;
- for (unsigned int j = stripe/2 - 1; j < state->size; j += stripe, ++progress) {
- if (progress%state->width == 0) {
+ for (unsigned int j = stripe/2 - 1; j < state->options->ncolors; j += stripe, ++progress) {
+ if (progress % state->options->width == 0) {
if (animate) {
sprintf(filename, "%s/%04u.png", state->options->filename, frame);
write_png(state, filename);
@@ -412,7 +405,7 @@ generate_bitmap(state_t *state)
}
print_progress("%.2f%%\t| boundary size: %zu\t| max boundary size: %zu",
- 100.0*progress/state->size, kdf.size, max_size);
+ 100.0*progress/state->options->ncolors, kdf.size, max_size);
}
uint32_t color = state->colors[j];
@@ -432,8 +425,7 @@ generate_bitmap(state_t *state)
pixel_t *pixel;
if (j == 0) {
- // First node goes in the center
- pixel = get_pixel(state, state->width/2, state->height/2);
+ pixel = get_pixel(state, state->options->x, state->options->y);
} else {
pixel = find_next_pixel(state, &kdf, target);
}
@@ -444,8 +436,9 @@ generate_bitmap(state_t *state)
max_size = kdf.size;
}
- png_byte *png_pixel = state->bitmap[pixel->y] + 3*pixel->x;
+ png_byte *png_pixel = state->bitmap[pixel->y] + 4*pixel->x;
color_unpack(png_pixel, color);
+ png_pixel[3] = 0xFF;
}
}
@@ -499,8 +492,8 @@ write_png(const state_t *state, const char *filename)
}
png_init_io(png_ptr, file);
- png_set_IHDR(png_ptr, info_ptr, state->width, state->height, 8,
- PNG_COLOR_TYPE_RGB, PNG_INTERLACE_ADAM7,
+ png_set_IHDR(png_ptr, info_ptr, state->options->width, state->options->height, 8,
+ PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_ADAM7,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
png_set_rows(png_ptr, info_ptr, state->bitmap);
diff --git a/options.c b/options.c
index 06deddb..5a5b446 100644
--- a/options.c
+++ b/options.c
@@ -24,19 +24,19 @@ parse_arg(int argc, char *argv[],
const char *short_form, const char *long_form,
const char **value, int *i, bool *error)
{
- size_t short_len = strlen(short_form);
- size_t long_len = strlen(long_form);
+ size_t short_len = short_form ? strlen(short_form) : 0;
+ size_t long_len = long_form ? strlen(long_form) : 0;
const char *actual_form;
const char *arg = argv[*i];
const char *candidate = NULL;
- if (strncmp(arg, short_form, short_len) == 0) {
+ if (short_form && strncmp(arg, short_form, short_len) == 0) {
actual_form = short_form;
if (strlen(arg) > short_len) {
candidate = arg + short_len;
}
- } else if (strncmp(arg, long_form, long_len) == 0) {
+ } else if (long_form && strncmp(arg, long_form, long_len) == 0) {
actual_form = long_form;
if (strlen(arg) > long_len) {
if (arg[long_len] == '=') {
@@ -209,7 +209,7 @@ print_colorized(FILE *file, bool tty, const char *format, ...)
}
void
-print_usage(FILE *file, const char *command)
+print_usage(FILE *file, const char *command, bool verbose)
{
#if __unix__
bool tty = isatty(fileno(file));
@@ -228,10 +228,17 @@ print_usage(FILE *file, const char *command)
usage(" %s [-s|--hue-sort] [-r|--random]\n", whitespace);
usage(" %s [-l|--selection @min@|@mean@]\n", whitespace);
usage(" %s [-c|--color-space @RGB@|@Lab@|@Luv@]\n", whitespace);
+ usage(" %s [-w|--width @WIDTH@] [-h|--height @HEIGHT@]\n", whitespace);
+ usage(" %s [-x @X@] [-y @Y@]\n", whitespace);
usage(" %s [-a|--animate]\n", whitespace);
usage(" %s [-o|--output @PATH@]\n", whitespace);
usage(" %s [-e|--seed @SEED@]\n", whitespace);
- usage(" %s [-h|--help]\n", whitespace);
+ usage(" %s [-?|--help]\n", whitespace);
+
+ if (!verbose) {
+ return;
+ }
+
usage("\n");
usage(" -b, --bit-depth @DEPTH@:\n");
usage(" Use all @DEPTH@\\-bit colors (!default!: @24@)\n\n");
@@ -245,6 +252,12 @@ print_usage(FILE *file, const char *command)
usage(" @mean@: Pick the pixel with the closest average of all its neighbors\n\n");
usage(" -c, --color-space @RGB@|@Lab@|@Luv@:\n");
usage(" Use the given color space (!default!: @Lab@)\n\n");
+ usage(" -w, --width @WIDTH@\n");
+ usage(" -h, --height @HEIGHT@:\n");
+ usage(" Generate a @WIDTH@x@HEIGHT@ image (!default!: @as small as possible@)\n\n");
+ usage(" -x @X@\n");
+ usage(" -y @Y@:\n");
+ usage(" Place the first pixel at (@X@, @Y@) (!default!: @center@)\n\n");
usage(" -a, --animate:\n");
usage(" Generate frames of an animation\n\n");
usage(" -o, --output @PATH@:\n");
@@ -253,7 +266,7 @@ print_usage(FILE *file, const char *command)
usage(" will hold many frames\n\n");
usage(" -e, --seed @SEED@:\n");
usage(" Seed the random number generator (!default!: @0@)\n\n");
- usage(" -h, --help:\n");
+ usage(" -?, --help:\n");
usage(" Show this message\n");
#undef usage
}
@@ -271,6 +284,8 @@ parse_options(options_t *options, int argc, char *argv[])
options->seed = 0;
options->help = false;
+ bool width_set = false, height_set = false;
+ bool x_set = false, y_set = false;
bool result = true;
for (int i = 1; i < argc; ++i) {
@@ -288,10 +303,6 @@ 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, "-l", "--selection", &value, &i, &error)) {
if (strcmp(value, "min") == 0) {
options->selection = SELECTION_MIN;
@@ -312,12 +323,44 @@ parse_options(options_t *options, int argc, char *argv[])
fprintf(stderr, "Invalid color space: `%s'\n", value);
error = true;
}
+ } else if (parse_arg(argc, argv, "-w", "--width", &value, &i, &error)) {
+ if (str_to_uint(value, &options->width)) {
+ width_set = true;
+ } else {
+ fprintf(stderr, "Invalid width: `%s'\n", value);
+ error = true;
+ }
+ } else if (parse_arg(argc, argv, "-h", "--height", &value, &i, &error)) {
+ if (str_to_uint(value, &options->height)) {
+ height_set = true;
+ } else {
+ fprintf(stderr, "Invalid height: `%s'\n", value);
+ error = true;
+ }
+ } else if (parse_arg(argc, argv, "-x", NULL, &value, &i, &error)) {
+ if (str_to_uint(value, &options->x)) {
+ x_set = true;
+ } else {
+ fprintf(stderr, "Invalid x coordinate: `%s'\n", value);
+ error = true;
+ }
+ } else if (parse_arg(argc, argv, "-y", NULL, &value, &i, &error)) {
+ if (str_to_uint(value, &options->y)) {
+ y_set = true;
+ } else {
+ fprintf(stderr, "Invalid y coordinate: `%s'\n", value);
+ error = true;
+ }
+ } 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, "-e", "--seed", &value, &i, &error)) {
if (!str_to_uint(value, &options->seed)) {
fprintf(stderr, "Invalid random seed: `%s'\n", value);
error = true;
}
- } else if (parse_arg(argc, argv, "-h", "--help", NULL, &i, &error)) {
+ } else if (parse_arg(argc, argv, "-?", "--help", NULL, &i, &error)) {
options->help = true;
} else if (!error) {
fprintf(stderr, "Unexpected argument `%s'\n", argv[i]);
@@ -329,6 +372,38 @@ parse_options(options_t *options, int argc, char *argv[])
}
}
+ options->ncolors = (size_t)1 << options->bit_depth;
+
+ if (!width_set && !height_set) {
+ // Round width up to make widescreen the default
+ options->width = 1U << (options->bit_depth + 1)/2;
+ options->height = 1U << options->bit_depth/2;
+ } else if (width_set && !height_set) {
+ options->height = (options->ncolors + options->width - 1)/options->width;
+ } else if (!width_set && height_set) {
+ options->width = (options->ncolors + options->height - 1)/options->height;
+ }
+
+ options->npixels = (size_t)options->width*options->height;
+ if (options->npixels < options->ncolors) {
+ fprintf(stderr, "Image too small (at least %zu pixels needed)\n", options->ncolors);
+ result = false;
+ }
+
+ if (!x_set) {
+ options->x = options->width/2;
+ } else if (options->x >= options->width) {
+ fprintf(stderr, "-x coordinate too big, should be less than %u\n", options->width);
+ result = false;
+ }
+
+ if (!y_set) {
+ options->y = options->height/2;
+ } else if (options->y >= options->height) {
+ fprintf(stderr, "-y coordinate too big, should be less than %u\n", options->height);
+ result = false;
+ }
+
// Default filename depends on -a flag
if (!options->filename) {
options->filename = options->animate ? "frames" : "kd-forest.png";
diff --git a/options.h b/options.h
index fe996f7..382f5fc 100644
--- a/options.h
+++ b/options.h
@@ -40,6 +40,9 @@ typedef struct {
mode_t mode;
selection_t selection;
color_space_t color_space;
+ unsigned int width, height;
+ size_t ncolors, npixels;
+ unsigned int x, y;
bool animate;
const char *filename;
unsigned int seed;
@@ -47,6 +50,6 @@ typedef struct {
} options_t;
bool parse_options(options_t *options, int argc, char *argv[]);
-void print_usage(FILE *file, const char *command);
+void print_usage(FILE *file, const char *command, bool verbose);
#endif // OPTIONS_H