To preface this: I'm currently a first-year student who was allowed to enroll in some second-year classes. Because of this, I'm currently wrangling a language (C++) that I haven't really had the time to learn (First-years mostly learn C#), so this code might not be pretty.
Our assignment is twofold. First, we need to write a program that outputs a Mandelbrot image in a PPM. To achieve this, I've followed a Youtube tutorial here.
The second part of the assignment is to make the program multithreaded. Essentially, the program is supposed to use 4 threads that each draw a quarter of the image.
To this end I have altered the code from the video tutorial, and converted the main to a method. Now, I'm trying to properly make the first quarter of the image. I figured the way to do this was to adjust
for (int y = 0; y < imageHeight; y++) //Rows!
{
for (int x = 0; x < imageWidth; x++) //Columns! (pixels in every row)
{
to
for (int y = 0; y < halfHeight; y++) //Rows!
{
for (int x = 0; x < halfWidth; x++) //Columns! (pixels in every row)
{
However, instead of drawing the top left quarter as I suspected, the program drew along the full width, repeating itself after the halfway mark of the image width was reached, and only drew along a quarter of the height (see image)
As I like to learn from my mistakes, I'd love to know what exactly is going wrong here.
Thank you for helping a programming greenhorn out :)
Full program code below.
#include "stdafx.h"
#include <fstream>
#include <iostream>
int imageWidth = 512, imageHeight = 512, maxN = 255, halfWidth = 256, halfHeight = 256;
double minR = -1.5, maxR = 0.7, minI = -1.0, maxI = 1.0;
std::ofstream f_out("output_image.ppm");
int findMandelbrot(double cr, double ci, int max_iterations)
{
int i = 0;
double zr = 0.0, zi = 0.0;
while (i < max_iterations && zr * zr + zi * zi < 4.0)
{
double temp = zr * zr - zi * zi + cr;
zi = 2.0 * zr * zi + ci;
zr = temp;
i++;
}
return i;
}
double mapToReal(int x, int imageWidth, double minR, double maxR)
{
double range = maxR - minR;
return x * (range / imageWidth) + minR;
}
double mapToImaginary(int y, int imageHeight, double minI, double maxI)
{
double range = maxI - minI;
return y * (range / imageHeight) + minI;
}
void threadedMandelbrot()
{
for (int y = 0; y < halfHeight; y++) //Rows!
{
for (int x = 0; x < halfWidth; x++) //Columns! (pixels in every row)
{
//... Find the real and imaginary values of c, corresponding
// to that x,y pixel in the image
double cr = mapToReal(x, imageWidth, minR, maxR);
double ci = mapToImaginary(y, imageHeight, minI, maxI);
//... Find the number of iterations in the Mandelbrot formula
// using said c.
int n = findMandelbrot(cr, ci, maxN);
//... Map the resulting number to an RGB value.
int r = (n % 256);
int g = (n % 256);
int b = (n % 256);
//... Output it to the image
f_out << r << " " << g << " " << b << " ";
}
f_out << std::endl;
}
}
int main()
{
//Initializes file
f_out << "P3" << std::endl;
f_out << imageWidth << " " << imageHeight << std::endl;
f_out << "256" << std::endl;
//For every pixel...
threadedMandelbrot();
f_out.close();
std::cout << "Helemaal klaar!" << std::endl;
return 0;
}
Your are calculating only a quarter of the image, so you have to set the dimension of that to halfHeight
, halfWidth
or fill the file with zeroes. When the image viewer reads the file, it shows two lines of it in a single line of pixels untill it reaches the end of the file, at a quarter of the picture height.
To fix the problem you just have to calculate the other three quarters of the image, but I suggest you to seperate the calc function from the file writing function: do the threaded calcs putting the result in an array (std::array
or std::vector
), look up the right color and then write to file.