I have written a C program to generate a PPM (Portable Pixmap) image of the Mandelbrot set. The program implements many things in C I am fairly unfamiliar with (Structures, error handling, use of new libraries (complex.h), and files (I'm completely unfamiliar with image files)) and the purpose of writing this program is primarily to test my knowledge.
If possible, could someone tell me things I've done well here, and things I've done badly, as well as a brief summary of how to do things better in the future? I'd also quite like to know any ways I could reduce execution time. All feedback is greatly appreciated.
I've added comments to give a brief summary of each function and their purposes, as well as make some possibly unclear areas clearer.
//mandelbrot.c - generates a .PPM (Portable Pixmap format) file of the Mandelbrot set with shading
//Still to add: Implement a better file format, optimise to reduce time,
#include "stdio.h"
#include "complex.h"
#include "math.h"
#define MAX_TESTS 650
int error(const char *message);
struct colour mandelbrot_test(double complex c);
struct colour rgb_gen(int iterations, double complex num);
struct dimensions dim_gen(int height);
struct dimensions
{
double hinc;
double winc;
unsigned int height;
unsigned int width;
};
struct colour
{
unsigned int red;
unsigned int green;
unsigned int blue;
};
int main(int argc, char *argv[])
{
if (argc < 3)
{
return error("Too few args!\nCorrect usage: program_name image_name image_height");
}
char *file_name = argv[1];
FILE *file;
double i, j;
double complex num;
struct colour rgb;
struct dimensions dim;
dim.height = atoi(argv[2]);
file = fopen(file_name, "w");
if (!file)
{
return error("Unable to access file!\n");
}
else if (dim.height < 1000) //values under ~1000px cause scaling issues with the file
{
return error("Image cannot be less than 1000 px in height");
}
dim = dim_gen(dim.height);
fprintf(file, "P3\n%d %d\n255\n", dim.width + 1, dim.height); //Magic number, image dimensions and max RGB value
for (j = -1.0; j <= 1.0; j += dim.hinc)
{
for (i = -2.0; i <= 0.5; i += dim.winc)
{
num = i + j * I; //Generate the complex number
rgb = mandelbrot_test(num); //Generate an RGB value for that number
fprintf(file, "%d %d %d ", rgb.red, rgb.green, rgb.blue); //Print it to the file
}
fprintf(file, "\n");
}
fclose(file);
return 0;
}
struct colour mandelbrot_test(double complex c) //Function to test for divergence of a given number
{
double complex x = 0;
int i;
double r_squared = cimag(c) * cimag(c) + creal(c) * creal(c);
if (r_squared * (8.0 * r_squared - 3.0) < 3.0/32.0 - creal(c)) //Quick test to see if we can bail out early
{
return rgb_gen(MAX_TESTS, x);
}
for (i = 1; i < MAX_TESTS; i++) //Actual testing loop
{
x *= x;
x += c;
if (cimag(x) * cimag(x) + creal(x) * creal(x) >= 4)
{
return rgb_gen(i, x);
}
}
return rgb_gen(MAX_TESTS, x);
}
struct colour rgb_gen(int iterations, double complex num) //Function to generate the RGB values for a given complex number
{
struct colour rgb;
if (iterations == MAX_TESTS)
{
rgb.red = 0;
rgb.green = 0;
rgb.blue = 0;
}
else
{
double z = sqrt(creal(num) * creal(num) + cimag(num) * cimag(num));
int brightness = 256.0 * log2(2.75 + iterations - log2(log2(z))) / log2((double)MAX_TESTS); //this generates shading based on how fast the number diverges
rgb.red = brightness;
rgb.green = brightness;
rgb.blue = 255;
}
return rgb;
}
struct dimensions dim_gen(int height) //Function to generate 5:2 scale dimensions and width/height increments based on the user given height
{
struct dimensions dim;
dim.height = height;
dim.width = 5.0 * (float)height / 2.0;
dim.hinc = 1.0 / (0.5 * (float)dim.height);
dim.winc = dim.hinc / 2.0;
return dim;
}
int error(const char *message) //Rudimentary error handling
{
printf("%s", message);
return 1;
}
vsrc_mandelbrot.c
in the FFmpeg source. Example cli usage:ffmpeg -f lavfi -i mandelbrot,format=yuv420p -t 30 output.mp4
and docs. \$\endgroup\$