2026-07-04 23:09:58 +02:00
#define _POSIX_C_SOURCE 199309L // for CLOCK_MONOTONIC
2026-06-13 21:27:54 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
2026-06-16 14:48:03 +02:00
#include <sys/types.h>
2026-06-18 18:07:03 +02:00
#include <unistd.h> // for getopt
#include <getopt.h> // for getopt_long
#include <string.h> // for strcpy
2026-07-04 23:09:58 +02:00
#include <time.h> // for benchmarking
2026-07-04 20:10:51 +02:00
// All implementations
#include "tricorn.h" // Final implmementation
2026-07-03 15:03:55 +02:00
#include "tricorn_V1.h"
#include "tricorn_V2.h"
2026-07-03 16:31:48 +02:00
#include "tricorn_V3.h"
2026-07-13 23:33:26 +02:00
2026-06-13 21:27:54 +02:00
2026-07-19 15:41:25 +02:00
const uint16_t TESTING_AMOUNT = 100 ;
2026-07-15 19:58:39 +02:00
const uint16_t NUMBER_OF_VERSIONS = 4 ;
2026-07-15 16:21:25 +02:00
2026-07-04 20:10:51 +02:00
2026-06-26 19:03:39 +02:00
void save_bmp ( const char * filename , unsigned char * img , size_t width , ssize_t height , _Bool color );
2026-07-17 12:22:23 +02:00
void benchmarking ( size_t benchmarking_amount , int version_to_use , float complex complexA , size_t width , ssize_t height , float resolution , unsigned n , _Bool colored );
unsigned tests ( void );
void checkInputs ( _Bool n , _Bool r , _Bool o , _Bool d , _Bool s );
2026-06-13 21:27:54 +02:00
2026-07-05 00:29:15 +02:00
2026-07-15 21:09:14 +02:00
// Example test command
2026-07-15 21:10:44 +02:00
// ./project -s -2.0,-1.5 -d 1200,900 -r 0.003 -n 256 -o tricorn.bmp -C
2026-06-20 18:22:02 +02:00
const char * help_messsage = "Help Message \n\n "
"Usage: \n "
" ./tricorn [OPTIONS] \n\n "
"Options: \n "
"-V <number> \n "
" Selects the implementation to use. \n "
" -V 0 uses the main implementation. \n "
2026-06-21 17:59:02 +02:00
" If no argument is given, -V 0 is used. \n "
2026-06-20 18:22:02 +02:00
" Example: -V 1 \n\n "
"-B <number> \n "
" Benchmarks the selected implementation. \n "
" The argument chooses how many times the function is called. \n "
" Example: -B 5 \n\n "
"-s <real>,<imaginary> \n "
" Sets the starting point of the function. \n "
" Example: -s -2.0,1.5 \n\n "
"-d <width>,<height> \n "
" Sets the width and the height of the generated image. \n "
" Example: -d 800,600 \n\n "
"-n <number> \n "
" Sets the maximum number of iterations per pixel. \n "
" Example: -n 255 \n\n "
"-r <floating-point number> \n "
" Sets the resolution per pixel. \n "
" Example: -r 0.01 \n\n "
"-o <filename> \n "
" Sets the output file name. \n "
" The file name must end with .bmp \n "
" Example: -o image.bmp \n\n "
"-C, --color \n "
" Enables colored output. \n\n "
"-h, --help \n "
" Displays the help message and exits. \n\n "
"-t, --test \n "
" Runs the tests, prints the results, and exists. \n " ;
2026-06-18 18:07:03 +02:00
int main ( int argc , char ** argv )
2026-06-11 14:40:16 +02:00
{
2026-06-18 18:07:03 +02:00
int opt_long ;
// will be the returned optional character,
// example: ./main -c -> opt_long here will be the optional 'c'
const char * optstring = "V:B:s:d:n:r:o:Cht" ;
// valid options that can be given to the programme. options with ":" after them expect an argument, which we'll later store in <char *optarg>
// example: B: <number> -> optional B expects an argument after it, and is valid -> ./main -B 67 (optarg is now "67")
// example: h -> optional h doesn't expect an argument -> ./main -h
// optind -> keeps track of the next index. initialised as 1 in the beginning. calling opt_long updates it every time
// optarg -> is a charptr. stores the arguments given to the optionals
static struct option long_options [] = {
{ "color" , 0 , 0 , 'C' },
{ "help" , 0 , 0 , 'h' },
{ "test" , 0 , 0 , 't' },
{ 0 , 0 , 0 , 0 }
};
// these are the long optionals, which we can call with double "--". "--help" or "--test". they are converted to the optionals "h" and "t" respectively
// first argument is "name" -> name of the long. "--<name>"
// second is "has_arg" -> 0 if no arg, 1 if arg, 2 if optional arg
// third is "*flag" -> sets a flag so that "--help" simply returns "h" and does nothing else
// fourth is "val" -> what to return when "--help" is given, returns "h"
2026-06-20 18:22:02 +02:00
int version_to_use = 0 ; // actual initial value
_Bool colored = 0 ; // actual initial value
2026-07-05 00:03:40 +02:00
_Bool will_benchmark = false ; // actual initial value
2026-07-05 00:29:15 +02:00
_Bool will_test = false ;
2026-06-20 18:22:02 +02:00
2026-07-08 00:00:30 +02:00
_Bool nGiven = false ;
_Bool rGiven = false ;
_Bool oGiven = false ;
_Bool dGiven = false ;
_Bool sGiven = false ;
2026-07-08 00:52:49 +02:00
size_t amount_function_called = 0 ; // initial values, so that the compiler doesn't complain
2026-06-20 18:22:02 +02:00
unsigned n = 0 ;
2026-07-07 19:02:23 +02:00
float starting_point_real = - 1 ;
float starting_point_imaginary = - 1 ;
2026-06-20 18:22:02 +02:00
size_t width = 0 ;
2026-07-07 19:02:23 +02:00
ssize_t height = - 1 ;
float resolution = - 1 ;
char * output_data = NULL ;
2026-06-18 18:07:03 +02:00
opt_long = getopt_long ( argc , argv , optstring , long_options , NULL );
// first four arguments are self-explanatory, fifth argument is the index of which long_option we return in the struct "long_options", but it's irrelevant so we set it to NULL
while ( opt_long != - 1 )
{
switch ( opt_long )
{
2026-07-15 20:46:50 +02:00
case 'V' : {
2026-07-08 00:25:20 +02:00
char * end ;
version_to_use = strtol ( optarg , & end , 10 );
if ( version_to_use < 0 || version_to_use > 5 || end == optarg || * end != '\0' )
2026-07-08 00:00:30 +02:00
{
2026-07-14 00:15:07 +02:00
fprintf ( stderr , "Invalid version to use: V%s. Available versions are : V0,V1,V2,V3 \n " , optarg );
2026-07-08 00:00:30 +02:00
return EXIT_FAILURE ;
}
2026-06-18 18:07:03 +02:00
break ;
2026-07-08 00:52:49 +02:00
}
2026-06-18 18:07:03 +02:00
2026-07-15 20:46:50 +02:00
case 'B' : {
2026-07-08 00:25:20 +02:00
char * end ;
2026-07-08 00:52:49 +02:00
long value = strtol ( optarg , & end , 10 );
2026-07-08 00:25:20 +02:00
2026-07-08 00:52:49 +02:00
if ( value <= 0 || end == optarg || * end != '\0' ) {
fprintf ( stderr , "Invalid benchmarking input: %s \n " , optarg );
2026-07-08 00:25:20 +02:00
return EXIT_FAILURE ;
2026-07-04 23:09:58 +02:00
}
2026-07-08 00:52:49 +02:00
amount_function_called = ( size_t ) value ;
2026-07-04 23:09:58 +02:00
will_benchmark = true ;
break ;
2026-07-08 00:25:20 +02:00
}
2026-06-18 18:07:03 +02:00
2026-07-05 00:03:40 +02:00
case 's' : {
2026-07-08 00:00:30 +02:00
sGiven = true ;
2026-07-05 00:03:40 +02:00
char * imaginary_string ;
char * end ;
2026-06-18 18:07:03 +02:00
starting_point_real = strtof ( optarg , & imaginary_string ); // split into two
2026-07-05 00:03:40 +02:00
if ( imaginary_string == optarg || * imaginary_string != ',' )
{
fprintf ( stderr , "Invalid first number or missing comma: %s \n " , optarg );
return EXIT_FAILURE ;
}
starting_point_imaginary = strtof ( imaginary_string + 1 , & end ); // second part, the imaginary part
if ( end == imaginary_string + 1 || * end != '\0' )
{
fprintf ( stderr , "Invalid second number or extra characters: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-06-18 18:07:03 +02:00
break ;
2026-07-05 00:03:40 +02:00
}
2026-06-18 18:07:03 +02:00
2026-07-05 00:03:40 +02:00
case 'd' : {
2026-07-08 00:00:30 +02:00
dGiven = true ;
2026-06-18 18:07:03 +02:00
char * height_string ;
2026-07-05 00:03:40 +02:00
char * end ;
2026-07-08 00:52:49 +02:00
long widthValue = strtol ( optarg , & height_string , 10 );
2026-07-05 00:03:40 +02:00
if ( height_string == optarg || * height_string != ',' )
{
fprintf ( stderr , "Invalid width or missing comma: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-07-08 00:52:49 +02:00
long heightValue = strtol ( height_string + 1 , & end , 10 );
2026-07-05 00:03:40 +02:00
if ( end == height_string + 1 || * end != '\0' )
{
fprintf ( stderr , "Invalid height or extra characters: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-07-08 00:52:49 +02:00
if ( widthValue <= 0 || heightValue == 0 )
{
fprintf ( stderr , "Width must be > 0 and height must not be 0: %s \n " , optarg );
return EXIT_FAILURE ;
}
width = ( size_t ) widthValue ;
height = ( ssize_t ) heightValue ;
2026-06-18 18:07:03 +02:00
break ;
2026-07-05 00:03:40 +02:00
}
2026-06-18 18:07:03 +02:00
2026-07-08 00:25:20 +02:00
case 'n' : {
2026-07-08 00:00:30 +02:00
nGiven = true ;
2026-07-08 00:25:20 +02:00
char * end ;
2026-07-08 00:52:49 +02:00
long nValue = strtol ( optarg , & end , 10 );
2026-07-08 00:25:20 +02:00
2026-07-08 00:52:49 +02:00
if ( end == optarg || * end != '\0' || nValue <= 0 )
2026-07-08 00:25:20 +02:00
{
fprintf ( stderr , "Invalid n value: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-07-08 00:52:49 +02:00
n = ( unsigned ) nValue ;
2026-06-18 18:07:03 +02:00
break ;
2026-07-08 00:25:20 +02:00
}
2026-06-18 18:07:03 +02:00
2026-07-05 00:03:40 +02:00
case 'r' : {
2026-07-08 00:00:30 +02:00
rGiven = true ;
2026-07-05 00:03:40 +02:00
char * end ;
resolution = strtof ( optarg , & end );
2026-07-08 00:25:20 +02:00
if ( end == optarg || * end != '\0' || resolution <= 0.0f )
2026-07-05 00:03:40 +02:00
{
fprintf ( stderr , "Invalid floating point number: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-06-18 18:07:03 +02:00
break ;
2026-07-05 00:03:40 +02:00
}
2026-06-18 18:07:03 +02:00
case 'o' :
2026-07-08 00:00:30 +02:00
oGiven = true ;
2026-07-08 00:25:20 +02:00
if ( optarg [ 0 ] == '-' )
{
fprintf ( stderr , "Invalid output filename: %s \n " , optarg );
return EXIT_FAILURE ;
}
2026-09-01 19:10:07 +03:00
int output_data_length = strlen ( optarg ) + 1 ;
output_data = malloc ( output_data_length );
2026-07-08 00:25:20 +02:00
if ( output_data == NULL )
{
fprintf ( stderr , "Memory allocation failed. \n " );
return EXIT_FAILURE ;
}
2026-09-01 19:10:07 +03:00
strncpy ( output_data , optarg , output_data_length );
output_data [ output_data_length - 1 ] = 0 ;
2026-06-18 18:07:03 +02:00
break ;
2026-07-15 20:46:50 +02:00
case 'C' :
2026-07-05 00:03:40 +02:00
colored = true ;
2026-06-18 18:07:03 +02:00
break ;
2026-07-15 20:46:50 +02:00
case 'h' :
2026-06-20 18:22:02 +02:00
printf ( "%s" , help_messsage );
return EXIT_SUCCESS ;
2026-06-18 18:07:03 +02:00
2026-07-15 20:46:50 +02:00
case 't' :
2026-07-05 00:29:15 +02:00
will_test = true ;
break ;
2026-06-18 18:07:03 +02:00
2026-07-15 20:46:50 +02:00
default :
2026-06-20 18:22:02 +02:00
// case '?' -> when the argument is not valid
fprintf ( stderr , "ERROR: Invalid argument -%c: \n " , optopt );
return EXIT_FAILURE ;
2026-06-18 18:07:03 +02:00
}
opt_long = getopt_long ( argc , argv , optstring , long_options , NULL );
}
2026-07-17 12:22:23 +02:00
// Run
float complex start = ( starting_point_real + starting_point_imaginary * I );
2026-06-28 21:38:55 +02:00
int bytesPerColour = colored ? 3 : 1 ;
2026-06-28 20:49:00 +02:00
ssize_t absolute_height = height >= 0 ? height : height * - 1 ;
2026-09-01 19:10:07 +03:00
if ( will_test )
return tests () ? EXIT_FAILURE : EXIT_SUCCESS ;
2026-07-17 12:22:23 +02:00
unsigned char * imageArr = malloc ( width * absolute_height * bytesPerColour ); // allocate image array on the heap to support large images as the stack is usually only 8 MB big
if ( imageArr == NULL )
2026-06-26 23:07:45 +02:00
exit ( EXIT_FAILURE );
2026-09-01 19:10:07 +03:00
2026-07-08 00:00:30 +02:00
if ( ! ( nGiven && rGiven && oGiven && dGiven && sGiven ))
{
checkInputs ( nGiven , rGiven , oGiven , dGiven , sGiven );
2026-07-17 12:22:23 +02:00
free ( imageArr );
2026-07-08 00:25:20 +02:00
free ( output_data );
2026-07-08 00:00:30 +02:00
return EXIT_FAILURE ;
}
2026-07-04 23:09:58 +02:00
if ( will_benchmark )
{
2026-07-05 01:12:44 +02:00
printf ( " \n Doing benchmarking. No images will be created \n " );
2026-07-17 12:22:23 +02:00
benchmarking ( amount_function_called , version_to_use , start , width , height , resolution , n , colored );
free ( imageArr );
2026-07-05 01:29:04 +02:00
free ( output_data );
2026-07-08 00:25:20 +02:00
return EXIT_SUCCESS ;
2026-07-04 23:09:58 +02:00
}
2026-07-03 15:03:55 +02:00
switch ( version_to_use )
{
case 0 :
2026-07-13 23:33:26 +02:00
// Uses highly optimized SIMD - main implementation
2026-07-17 12:22:23 +02:00
tricorn ( start , width , height , resolution , n , colored , imageArr );
2026-07-03 15:03:55 +02:00
break ;
2026-07-03 16:31:48 +02:00
case 1 :
2026-07-13 23:33:26 +02:00
// Uses scalar comparison implementation
2026-07-17 12:22:23 +02:00
tricorn_V1 ( start , width , height , resolution , n , colored , imageArr );
2026-07-03 15:03:55 +02:00
break ;
case 2 :
2026-07-13 23:33:26 +02:00
// Uses unoptimized SIMD implementation
2026-07-17 12:22:23 +02:00
tricorn_V2 ( start , width , height , resolution , n , colored , imageArr );
2026-07-03 16:31:48 +02:00
break ;
case 3 :
2026-07-13 23:33:26 +02:00
// Uses highly optimized SIMD + Threading implementation
2026-07-17 12:22:23 +02:00
tricorn_V3 ( start , width , height , resolution , n , colored , imageArr );
2026-07-04 15:28:51 +02:00
break ;
2026-07-03 15:03:55 +02:00
default :
break ;
2026-06-28 20:49:00 +02:00
}
2026-07-05 00:29:15 +02:00
2026-07-17 12:22:23 +02:00
save_bmp ( output_data , imageArr , width , height , colored );
2026-06-18 18:07:03 +02:00
2026-06-26 23:07:45 +02:00
free ( output_data );
2026-07-17 12:22:23 +02:00
free ( imageArr );
2026-06-11 14:40:16 +02:00
return 0 ;
}
2026-06-13 21:27:54 +02:00
2026-07-17 12:22:23 +02:00
void benchmarking ( size_t benchmarking_amount , int version_to_use , float complex complexA , size_t width , ssize_t height , float resolution , unsigned n , _Bool colored ) {
struct timespec start ;
struct timespec end ;
void ( * tricorn_implementation )( float complex , size_t , ssize_t , float , unsigned , _Bool , unsigned char * );
unsigned int bytePerColor = colored ? 3 : 1 ;
unsigned char * tempArr = malloc ( width * height * bytePerColor );
if ( tempArr == NULL ) {
fprintf ( stderr , "Could not allocate memory for buffer" );
exit ( EXIT_FAILURE );
}
switch ( version_to_use )
{
case 0 : // Uses highly optimized SIMD and threaded version / main implementation
printf ( "Benchmarking V0 - highly optimized SIMD \n " );
tricorn_implementation = tricorn ;
break ;
case 1 : // Uses unoptimised scalar comparison implementation
printf ( "Benchmarking V1 - scalar comparison implementation \n " );
tricorn_implementation = tricorn_V1 ;
break ;
case 2 : // Uses optimized SISD implementation
printf ( "Benchmarking V2 - unoptimized SIMD implementation \n " );
tricorn_implementation = tricorn_V2 ;
break ;
case 3 : // Uses unoptimized SIMD implementation
printf ( "Benchmarking V3 - highly optimized SIMD + Threading implementation \n " );
tricorn_implementation = tricorn_V3 ;
break ;
default :
fprintf ( stderr , "Invalid version to use: V%d. Available versions are : V0,V1,V2,V3,V4,V5 \n " , version_to_use );
return ;
}
printf ( " \n " );
clock_gettime ( CLOCK_MONOTONIC , & start );
for ( size_t i = 0 ; i < benchmarking_amount ; i ++ )
{
tricorn_implementation ( complexA , width , height , resolution , n , colored , tempArr );
}
clock_gettime ( CLOCK_MONOTONIC , & end );
double time = end . tv_sec - start . tv_sec + 1e-9 * ( end . tv_nsec - start . tv_nsec );
printf ( "The total amount of time spent for %zu function calls: %lf seconds \n " , benchmarking_amount , time );
double average = time / benchmarking_amount ;
printf ( "Average time per function: %lf seconds \n " , average );
free ( tempArr );
}
void run_benchmarks ( float complex start , size_t width , size_t height , float res , int n , _Bool colored );
unsigned tests ( void ) {
printf ( "Doing testing \n\n " );
printf ( " \n\n\n Test 1: Default image, coloured \n " );
float complex start = - 2.0f - 0.5f * I ;
size_t width = 1920 ;
size_t height = 1080 ;
float res = 0.001f ;
int n = 200 ;
_Bool colored = true ;
run_benchmarks ( start , width , height , res , n , colored );
printf ( " \n\n\n Test 2: Ultra high resolution low iterations \n " );
start = - 2.0f - 0.5f * I ;
width = 7680 ;
height = 4320 ;
res = 0.001f ;
n = 5 ;
run_benchmarks ( start , width , height , res , n , colored );
printf ( " \n\n\n Test 3: Zoomed on fractal edge \n " );
start = - 1.431f - 0.250f * I ;
width = 1024 ;
height = 1024 ;
res = 0.0005f ;
2026-08-23 18:43:43 +03:00
n = 1000 ;
2026-07-17 12:22:23 +02:00
run_benchmarks ( start , width , height , res , n , colored );
printf ( " \n Test 4: Pure Black \n " );
start = 0 ;
width = 1920 ;
height = 1080 ;
res = 0.00001f ;
n = 1000 ;
run_benchmarks ( start , width , height , res , n , colored );
return 0 ;
}
void run_benchmarks ( float complex start , size_t width , size_t height , float res , int n , _Bool colored ){
printf ( "Start: %f%+fi \n " , crealf ( start ), cimagf ( start ));
printf ( "Width: %li \n " , width );
printf ( "Height: %li \n " , height );
printf ( "Resolution: %f \n " , res );
printf ( "Iterations: %i \n " , n );
printf ( "Colored: %s \n " , colored ? "true" : "false" );
for ( int i = 0 ; i < NUMBER_OF_VERSIONS ; i ++ ) {
printf ( " \n\n " );
benchmarking ( TESTING_AMOUNT , i , start , width , height , res , n , colored );
}
}
void checkInputs ( _Bool n , _Bool r , _Bool o , _Bool d , _Bool s ) {
if ( ! n )
{
fprintf ( stderr , "ERROR: Input n not given. \n " );
}
if ( ! r )
{
fprintf ( stderr , "ERROR: Input r not given. \n " );
}
if ( ! o )
{
fprintf ( stderr , "ERROR: Input o not given. \n " );
}
if ( ! d )
{
fprintf ( stderr , "ERROR: Input d not given. \n " );
}
if ( ! s )
{
fprintf ( stderr , "ERROR: Input s not given. \n " );
}
}
2026-06-13 21:27:54 +02:00
2026-06-26 19:03:39 +02:00
void save_bmp ( const char * filename , unsigned char * img , size_t width , ssize_t height , _Bool color ) {
2026-06-21 17:16:55 +02:00
// Open a file at the filepath given as a stream in the program
2026-06-13 21:27:54 +02:00
FILE * fptr = fopen ( filename , "wb" );
2026-06-21 17:16:55 +02:00
if ( fptr == NULL ) { // Chech for errors with opening the file stream
2026-06-13 21:27:54 +02:00
fprintf ( stderr , "ERROR: Could not open output file at %s \n ." , filename );
exit ( EXIT_FAILURE );
}
2026-06-21 17:16:55 +02:00
2026-06-26 22:38:55 +02:00
size_t bytesPerColor = color ? 3 : 1 ;
2026-06-21 17:16:55 +02:00
int actual_height = ( height < 0 ) ? - height : height ; // Calculate absolute height for iteration and calculation purposes
2026-06-26 22:38:55 +02:00
size_t row_padding = ( 4 - ( width * bytesPerColor ) % 4 ) % 4 ; // Calculate padding for each row as .bmp is 4 bytes aligned
size_t row_width = width * bytesPerColor + row_padding ; // Calculate row width with padding
2026-06-13 21:27:54 +02:00
size_t image_size = actual_height * row_width ;
2026-06-28 21:38:55 +02:00
size_t header_offset = color ? 54 : 1078 ; // If colored then offset = 54 if not we add offset = 54 + 1024; 1024 Bytes for color pallete
2026-07-15 19:58:39 +02:00
size_t file_size = image_size + header_offset ; // File size = Image Size + Headers
2026-06-13 21:27:54 +02:00
2026-06-26 19:03:39 +02:00
2026-06-21 17:16:55 +02:00
// File header - contains information about the file
unsigned char file_header [ 14 ] = {
'B' , 'M' , // BM - identifies file as a .bmp file
0 , 0 , 0 , 0 , // 4 bytes for file size - will be set later
0 , 0 , 0 , 0 , // Reserved
2026-06-28 21:38:55 +02:00
0 , 0 , 0 , 0 // 4 bytes that specify the offset where the image array begins
2026-06-13 21:27:54 +02:00
};
2026-06-22 19:12:09 +02:00
// Serialize the size into 4 bytes - (unsigned char) takes only bottom 4 bytes 1 2 3 4
2026-06-21 17:16:55 +02:00
file_header [ 2 ] = ( unsigned char )( file_size );
2026-06-13 21:27:54 +02:00
file_header [ 3 ] = ( unsigned char )( file_size >> 8 );
file_header [ 4 ] = ( unsigned char )( file_size >> 16 );
file_header [ 5 ] = ( unsigned char )( file_size >> 24 );
2026-06-28 21:38:55 +02:00
file_header [ 10 ] = ( unsigned char )( header_offset );
file_header [ 11 ] = ( unsigned char )( header_offset >> 8 );
2026-06-21 17:16:55 +02:00
// Info header - contains information about the image itself
unsigned char info_header [ 40 ] = {
40 , 0 , 0 , 0 , // Size of this header
0 , 0 , 0 , 0 , // Width in pixels - set later
0 , 0 , 0 , 0 , // Height in pixels - set later
1 , 0 , // Number of color planes - always 1
2026-06-26 19:03:39 +02:00
bytesPerColor * 8 , 0 , // Number of bits per pixel or color depth
2026-06-21 17:16:55 +02:00
0 , 0 , 0 , 0 , // Compression method - 0 for no compression
0 , 0 , 0 , 0 , // Image size - set later
0x13 , 0x0B , 0 , 0 , // Horizontal resolution in pixels per metre - Set to standart 72 pixels per inch
0x13 , 0x0B , 0 , 0 , // Vertical resolution in pixels per metre - Set to standart 72 pixels per inch
0 , 0 , 0 , 0 , // Number of colors in the color pallete - 0 for default of 2^n where n is bits per pixel
0 , 0 , 0 , 0 // Number of important colors - 0 for all colors important
2026-06-13 21:27:54 +02:00
};
2026-06-21 17:16:55 +02:00
// Setting width
2026-06-13 21:27:54 +02:00
info_header [ 4 ] = ( unsigned char )( width );
info_header [ 5 ] = ( unsigned char )( width >> 8 );
info_header [ 6 ] = ( unsigned char )( width >> 16 );
info_header [ 7 ] = ( unsigned char )( width >> 24 );
2026-06-21 17:16:55 +02:00
// Setting height
2026-06-13 21:27:54 +02:00
uint32_t h = ( uint32_t ) height ;
info_header [ 8 ] = ( unsigned char )( h );
info_header [ 9 ] = ( unsigned char )( h >> 8 );
info_header [ 10 ] = ( unsigned char )( h >> 16 );
info_header [ 11 ] = ( unsigned char )( h >> 24 );
2026-06-21 17:16:55 +02:00
// Setting image size
2026-06-13 21:27:54 +02:00
info_header [ 20 ] = ( unsigned char )( image_size );
info_header [ 21 ] = ( unsigned char )( image_size >> 8 );
info_header [ 22 ] = ( unsigned char )( image_size >> 16 );
info_header [ 23 ] = ( unsigned char )( image_size >> 24 );
2026-06-21 17:16:55 +02:00
// Write headers into file
//size_t fwrite(const void ptr[restrict .size * .n], size_t size, size_t n, FILE *restrict stream);
fwrite ( file_header , 1 , 14 , fptr );
2026-06-13 21:27:54 +02:00
fwrite ( info_header , 1 , 40 , fptr );
2026-06-28 21:38:55 +02:00
//If image is grayscale write color dictionary to tell reader what color to visualise for each value
// Row 0 - Value 0: [R=0;B=0;G=0]
// ...
//Row i - Value i: [R=i;B=i;G=i]
if ( ! color ) {
unsigned char palette [ 1024 ];
for ( int i = 0 ; i < 256 ; i ++ ) {
palette [ i * 4 + 0 ] = i ; // B
palette [ i * 4 + 1 ] = i ; // G
palette [ i * 4 + 2 ] = i ; // R
palette [ i * 4 + 3 ] = 0 ; // Reserved (Padding)
}
fwrite ( palette , 1 , 1024 , fptr );
}
2026-06-21 17:16:55 +02:00
// Save padding bytes as array so we can write from it easily to the file later
unsigned char padding_bytes [ 3 ] = { 0 , 0 , 0 };
2026-06-26 19:03:39 +02:00
2026-06-13 21:27:54 +02:00
for ( int i = 0 ; i < actual_height ; i ++ ) {
2026-06-21 17:16:55 +02:00
// Write whole row of pixels - img + (i * width * 3) is the position of the row in the image array
// size = 3 as we are interpreting every pixel as an object
2026-06-26 19:03:39 +02:00
fwrite ( img + ( i * width * bytesPerColor ), bytesPerColor , width , fptr );
2026-06-13 21:27:54 +02:00
if ( row_padding > 0 ) {
2026-06-21 17:16:55 +02:00
fwrite ( padding_bytes , 1 , row_padding , fptr ); // Write row_padding number of padding bytes to the file stream
2026-06-13 21:27:54 +02:00
}
}
fclose ( fptr );
}