aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJP Appel <jeanpierre.appel01@gmail.com>2024-04-30 05:02:08 -0400
committerJP Appel <jeanpierre.appel01@gmail.com>2024-04-30 05:02:08 -0400
commit05ba9a3fc30878dcabbbe02929f24da189008293 (patch)
tree54d4e936822458380a0222fa0233006d9170a52f
parenta9760b31a2dfc672c72c4257a4e293fafb08c0b3 (diff)
gif support
-rw-r--r--makefile12
-rw-r--r--src/fractal_render.c96
-rw-r--r--src/fractal_render.h13
-rw-r--r--src/grids.c3
-rw-r--r--src/renderers.c98
-rw-r--r--src/renderers.h9
6 files changed, 210 insertions, 21 deletions
diff --git a/makefile b/makefile
index c90bf85..8fc8abd 100644
--- a/makefile
+++ b/makefile
@@ -1,4 +1,4 @@
-CC := gcc-13
+CC := gcc
CPPFLAGS := #-DEXTENDED_PRECISION
CFLAGS := -Wall -O3 -march=native
LDFLAGS := -lm
@@ -24,8 +24,8 @@ all: $(addprefix $(BUILD_DIR)/, $(TARGET))
# Programs #
##############
-$(BUILD_DIR)/fractal-render: $(OBJ_DIR)/grids.o $(OBJ_DIR)/fractal_render.o
- $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
+$(BUILD_DIR)/fractal-render: $(OBJ_DIR)/grids.o $(OBJ_DIR)/fractal_render.o $(OBJ_DIR)/renderers.o
+ $(CC) $(CFLAGS) $^ -o $@ -lgd
$(BUILD_DIR)/serial-fractals: $(OBJ_DIR)/serial-fractals.o $(OBJ_DIR)/grids.o $(OBJ_DIR)/fractals.o
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
@@ -52,6 +52,12 @@ $(OBJ_DIR):
# Tests #
###########
+examples/julia.png: examples/julia.grid $(BUILD_DIR)/fractal-render
+ $(BUILD_DIR)/fractal-render -i $< -r png -o $@
+
+examples/julia.grid: $(BUILD_DIR)/shared-fractals
+ $< -c 0.285+0.01i -i 255 -r 2 -x 8192 -y 8192 -o $@ -f julia
+
examples/mandelbrot_%.grid: $(BUILD_DIR)/%-fractals
$< -x 100 -y 100 -o $@
diff --git a/src/fractal_render.c b/src/fractal_render.c
index b9ffa09..721d3f2 100644
--- a/src/fractal_render.c
+++ b/src/fractal_render.c
@@ -7,6 +7,9 @@
#include "grids.h"
#include "precision.h"
#include "fractal_render.h"
+#include "renderers.h"
+
+#define BUFFER_SIZE 32
void print_usage(FILE* file, const char* program_name) {
fprintf(file, "Usage: %s -i input.grid [-r renderer] [-o output.ext]\n", program_name);
@@ -16,7 +19,8 @@ void print_help(){
printf("Options:\n"
" -i, --input <input grid> the grid to be rendered, if the file name is '-' reads from stdin\n"
" -r, --renderer <renderer> the renderer to use, defaults to the text renderer\n"
- " renderers: txt, png (TODO), gif (TODO, with additional features)\n"
+ " renderers: txt, png, gif (TODO, with additional features)\n"
+ " -d, --delay <delay> the delay between animation frames in 1/100 s\n"
" -o, --output <output file> the file to output the result of rendering, if not given defaults to output.<EXT>\n"
" where <EXT> is the renderer used\n"
" -v, --verbose verbose output\n"
@@ -49,16 +53,29 @@ void cleanup(FILE* output_file, FILE* input_file, grid_t* grid) {
if(grid) free_grid(grid);
}
+/*
+ * Wrapper for print_grid to meet renderer type
+ */
+void render_txt(FILE* output, const renderer_params* params){
+ print_grid(output, params->grid);
+}
+
+
int main(const int argc, char* argv[]){
+ // defaults
char* input_filename = "fractal.grid";
char* output_filename = "fractal.txt";
- renderer_func renderer = print_grid;
+ renderer_func renderer = render_txt;
+ int anim_delay = 30;
+ bool multigrid = false;
bool verbose = false;
+ renderer_params* params = malloc(sizeof(renderer_params));
static struct option long_options[] = {
{"input", required_argument, NULL, 'i'},
{"renderer", required_argument, NULL, 'r'},
+ {"delay", required_argument, NULL, 'd'},
{"output", required_argument, NULL, 'o'},
{"verbose", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
@@ -66,18 +83,37 @@ int main(const int argc, char* argv[]){
};
int opt;
- while((opt = getopt_long(argc, argv, "i:r:o:vh", long_options, NULL)) != -1){
+ while((opt = getopt_long(argc, argv, "i:r:o:d:vh", long_options, NULL)) != -1){
switch(opt){
case 'i':
input_filename = optarg;
break;
case 'r':
- //TODO: update with other renderers
- renderer = print_grid;
+ if(strcmp(optarg, "png") == 0){
+ renderer = render_png;
+ }
+ else if(strcmp(optarg, "txt") ==0 ){
+ renderer = render_txt;
+ }
+ else if(strcmp(optarg, "gif") == 0){
+ renderer = render_gif;
+ multigrid = true;
+ }
+ else {
+ fprintf(stderr, "Unrecognized renderer: %s, exitting", optarg);
+ exit(2);
+ }
break;
case 'o':
output_filename = optarg;
break;
+ case 'd':
+ anim_delay = strtol(optarg, NULL, 10);
+ if(anim_delay < 1){
+ fprintf(stderr, "Invalid frame delay: %d", anim_delay);
+ exit(2);
+ }
+ break;
case 'v':
verbose = true;
break;
@@ -92,7 +128,8 @@ int main(const int argc, char* argv[]){
}
}
- //TODO: logic to set output_filename if o flag is not used
+ //TODO: logic to set output_filename if o flag is not used
+
FILE* input_file = NULL;
FILE* output_file = NULL;
@@ -111,24 +148,53 @@ int main(const int argc, char* argv[]){
if (!output_file) { error_exit("Error opening output file", output_filename); }
}
- if(strcmp(input_filename, "-") == 0){
- grid = read_grid(stdin);
- if (!grid) { error_exit("Error reading from stdin", NULL); }
+ if(!multigrid){
+ if(strcmp(input_filename, "-") == 0){
+ grid = read_grid(stdin);
+ if (!grid) { error_exit("Error reading from stdin", NULL); }
+ }
+ else {
+ input_file = fopen(input_filename, "rb");
+ if(!input_file) { error_exit("Error opening input file", input_filename); }
+ grid = read_grid(input_file);
+ if(!grid) { error_exit("Error reading from file", input_filename); }
+ }
+ params->grid = grid;
}
else {
- input_file = fopen(input_filename, "rb");
- if (!input_file) { error_exit("Error opening input file", input_filename); }
- grid = read_grid(input_file);
- if (!grid) { error_exit("Error reading from file", input_filename); }
+ grid_t** grids = malloc(BUFFER_SIZE * sizeof(grid_t*));
+ char filename[256];
+ size_t size = 0;
+ FILE* file;
+ if(strcmp(input_filename, "-") == 0){
+ file = stdin;
+ }
+ else {
+ file = fopen(input_filename, "r");
+ }
+ while(fgets(filename, sizeof(filename), file) != NULL && size < BUFFER_SIZE){
+ //remove trailing newline from fgets
+ filename[strcspn(filename, "\n")] = 0;
+ input_file = fopen(filename, "rb");
+ if (!input_file) { error_exit("Error opening input file", filename); }
+ grids[size] = read_grid(input_file);
+ if (!grids[size]) { error_exit("Error reading from file", filename); }
+ size++;
+ }
+ //TODO: check grids to make sure they are have the same dimensions
+ params->grid_array.delay = anim_delay;
+ params->grid_array.size = size;
+ params->grid_array.grids = grids;
}
+
if(verbose){
print_grid_info(grid);
}
- renderer(output_file, grid);
+ renderer(output_file, params);
cleanup(output_file, input_file, grid);
-
+ free(params);
return 0;
}
diff --git a/src/fractal_render.h b/src/fractal_render.h
index a23acad..e527ce1 100644
--- a/src/fractal_render.h
+++ b/src/fractal_render.h
@@ -1,6 +1,17 @@
#pragma once
#include <stdio.h>
+#include <gd.h>
+
#include "grids.h"
-typedef void (*renderer_func)(FILE*, const grid_t*);
+typedef union {
+ grid_t* grid;
+ struct {
+ size_t size;
+ int delay;
+ grid_t** grids;
+ } grid_array;
+} renderer_params;
+typedef void (*renderer_func)(FILE*, const renderer_params*);
+typedef gdImagePtr (*grid_image_converter)(grid_t*);
diff --git a/src/grids.c b/src/grids.c
index 279fdb5..e73feb4 100644
--- a/src/grids.c
+++ b/src/grids.c
@@ -142,7 +142,6 @@ CBASE complex grid_to_complex(const grid_t* grid_p, const size_t index) {
* Resets all grid values to 0
*/
void zoom_grid(grid_t* restrict grid, const CBASE magnification){
- //FIXME: not impelemnted correctly
set_grid(grid, 0);
// const CBASE complex upper_right = grid->upper_right;
const complex_t upper_right = grid->upper_right;
@@ -153,7 +152,7 @@ void zoom_grid(grid_t* restrict grid, const CBASE magnification){
const CBASE inv_mag = 1 / magnification;
const complex_t center = {
.re = inv2 * (lower_left.re + upper_right.re),
- .im = inv2 * (lower_left.im + lower_left.im)
+ .im = inv2 * (lower_left.im + upper_right.im)
};
const complex_t offset = {
.re = inv_mag * (upper_right.re - lower_left.re),
diff --git a/src/renderers.c b/src/renderers.c
new file mode 100644
index 0000000..2955fb2
--- /dev/null
+++ b/src/renderers.c
@@ -0,0 +1,98 @@
+#include "renderers.h"
+#include <stdio.h>
+#include "fractal_render.h"
+#include <gd.h>
+
+static inline byte scale_iterations(const byte max_iterations, const byte iteration){
+ return (byte)((double)iteration / max_iterations * 255);
+}
+
+/*
+ * Convert a grid into a gd image with true color
+ * NOTE: modifying the size of colors will allow this function to use the
+ * millions of colors the true colors support
+ * As of now it is identical to converter, but should be changed
+ */
+gdImagePtr truecolor_converter(const grid_t* grid){
+ const size_t width = grid->x;
+ const size_t height = grid->y;
+ const byte* data = grid->data;
+ const byte max_iterations = grid->max_iterations;
+
+ gdImagePtr img = gdImageCreateTrueColor(width, height);
+ int colors[256];
+ for(size_t i = 0; i < 255; i++){
+ colors[i] = gdImageColorAllocate(img, 0, i, i/2);
+ }
+
+ colors[255] = gdImageColorAllocate(img, 0, 0, 0);
+
+ for(size_t y = 0; y < height; y++){
+ for(size_t x = 0; x < width; x++){
+ byte iteration = data[y * width + x];
+ byte scaled_iteration = scale_iterations(max_iterations, iteration);
+ int color = colors[scaled_iteration];
+ gdImageSetPixel(img, x, y, color);
+ }
+ }
+
+ return img;
+}
+
+/*
+ * Convert a grid into a gd image
+ */
+gdImagePtr converter(const grid_t* grid){
+ const size_t width = grid->x;
+ const size_t height = grid->y;
+ const byte* data = grid->data;
+ const byte max_iterations = grid->max_iterations;
+
+ gdImagePtr img = gdImageCreate(width, height);
+ int colors[256];
+ for(size_t i = 0; i < 255; i++){
+ colors[i] = gdImageColorAllocate(img, 0, i, i/2);
+ }
+
+ colors[255] = gdImageColorAllocate(img, 0, 0, 0);
+ for(size_t y = 0; y < height; y++){
+ for(size_t x = 0; x < width; x++){
+ byte iteration = data[y * width + x];
+ byte scaled_iteration = scale_iterations(max_iterations, iteration);
+ int color = colors[scaled_iteration];
+ gdImageSetPixel(img, x, y, color);
+ }
+ }
+
+ return img;
+}
+
+
+void render_png(FILE *output, const renderer_params* params){
+ gdImagePtr img = truecolor_converter(params->grid);
+ gdImagePng(img, output);
+ gdImageDestroy(img);
+}
+
+void render_gif(FILE* output, const renderer_params* params){
+ const size_t size = params->grid_array.size;
+ grid_t** grids = params->grid_array.grids;
+ const int delay = params->grid_array.delay;
+
+ gdImagePtr imgs[size];
+
+ imgs[0] = converter(grids[0]);
+ gdImageGifAnimBegin(imgs[0], output, 1, -1);
+ gdImageGifAnimAdd(imgs[0], output, 0, 0, 0, delay, 1, NULL);
+
+ for(size_t i = 1; i < size; i++){
+ imgs[i] = converter(grids[i]);
+ gdImagePaletteCopy(imgs[i], imgs[i-1]);
+ gdImageGifAnimAdd(imgs[i], output, 0, 0, 0, delay, 1, imgs[i-1]);
+ }
+
+ gdImageGifAnimEnd(output);
+ for(size_t i = 0; i < size; i++){
+ gdImageDestroy(imgs[i]);
+ }
+}
diff --git a/src/renderers.h b/src/renderers.h
new file mode 100644
index 0000000..add7594
--- /dev/null
+++ b/src/renderers.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <gd.h>
+#include <stdio.h>
+#include "fractal_render.h"
+#include "grids.h"
+
+void render_png(FILE* output, const renderer_params* params);
+void render_gif(FILE* output, const renderer_params* params);