diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | docs/demo.mp4 | bin | 0 -> 40490910 bytes | |||
-rw-r--r-- | docs/demo_1.png | bin | 0 -> 2678925 bytes | |||
-rw-r--r-- | docs/demo_2.png | bin | 0 -> 2044428 bytes | |||
-rw-r--r-- | docs/demo_3.png | bin | 0 -> 1114088 bytes | |||
-rw-r--r-- | docs/dia/diagram.dia | bin | 0 -> 2942 bytes | |||
-rw-r--r-- | docs/dia/div_request.png | bin | 0 -> 158900 bytes | |||
-rw-r--r-- | docs/p4.pdf | bin | 0 -> 5197562 bytes | |||
-rw-r--r-- | gdbinit.gdb | 1 | ||||
-rw-r--r-- | libpng_wrapper.cpp | 46 | ||||
-rw-r--r-- | libpng_wrapper.hpp | 42 | ||||
-rw-r--r-- | main.cpp | 208 | ||||
-rw-r--r-- | makefile | 21 | ||||
-rw-r--r-- | mthread.cpp | 204 | ||||
-rw-r--r-- | mthread.hpp | 71 | ||||
-rw-r--r-- | notes | 109 | ||||
-rw-r--r-- | out.png | bin | 0 -> 2679921 bytes | |||
-rw-r--r-- | out1.png | bin | 0 -> 2679899 bytes | |||
-rw-r--r-- | test.cpp | 35 | ||||
-rw-r--r-- | test.hpp | 6 |
20 files changed, 745 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f7faa4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/* +mandelbrot diff --git a/docs/demo.mp4 b/docs/demo.mp4 Binary files differnew file mode 100644 index 0000000..0a40287 --- /dev/null +++ b/docs/demo.mp4 diff --git a/docs/demo_1.png b/docs/demo_1.png Binary files differnew file mode 100644 index 0000000..94f583e --- /dev/null +++ b/docs/demo_1.png diff --git a/docs/demo_2.png b/docs/demo_2.png Binary files differnew file mode 100644 index 0000000..cb26cba --- /dev/null +++ b/docs/demo_2.png diff --git a/docs/demo_3.png b/docs/demo_3.png Binary files differnew file mode 100644 index 0000000..60b35a2 --- /dev/null +++ b/docs/demo_3.png diff --git a/docs/dia/diagram.dia b/docs/dia/diagram.dia Binary files differnew file mode 100644 index 0000000..45cb6cc --- /dev/null +++ b/docs/dia/diagram.dia diff --git a/docs/dia/div_request.png b/docs/dia/div_request.png Binary files differnew file mode 100644 index 0000000..1812630 --- /dev/null +++ b/docs/dia/div_request.png diff --git a/docs/p4.pdf b/docs/p4.pdf Binary files differnew file mode 100644 index 0000000..858774b --- /dev/null +++ b/docs/p4.pdf diff --git a/gdbinit.gdb b/gdbinit.gdb new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/gdbinit.gdb @@ -0,0 +1 @@ + diff --git a/libpng_wrapper.cpp b/libpng_wrapper.cpp new file mode 100644 index 0000000..a0d48e3 --- /dev/null +++ b/libpng_wrapper.cpp @@ -0,0 +1,46 @@ +#include <string> +#include <stdio.h> +#include <string.h> +#include "libpng_wrapper.hpp" + +using namespace std; + +png::png(string filename, uint32_t width, uint32_t height) { + //have to use the old C way to access files for libPNGs sake + output_fp = fopen(filename.c_str(), "wb"); + if(!output_fp) throw PNG_FILE_ERROR; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(!png_ptr) throw PNG_LIBPNG_ERROR; + + png_info_ptr = png_create_info_struct(png_ptr); + if(!png_info_ptr) throw PNG_LIBPNG_ERROR; + + png_init_io(png_ptr, output_fp); + + png_set_IHDR(png_ptr, png_info_ptr, width, height, DEFAULT_BIT_DEPTH, DEFAULT_COLOR_TYPE, + (DEFAULT_INTERLACE) ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + row_pointers = new png_bytep[height * sizeof(png_bytep)]; + for(size_t row = 0; row < height; row++) + row_pointers[row] = new png_byte[png_get_rowbytes(png_ptr, png_info_ptr)]; +} + +png::~png() { + png_write_info(png_ptr, png_info_ptr); + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, NULL); + png_destroy_write_struct(&png_ptr, &png_info_ptr); + fclose(output_fp); +} + +void png::set_pixel(uint32_t x, uint32_t y, png_byte r, png_byte g, png_byte b) { + row_pointers[y][x * 3] = r; + row_pointers[y][(x * 3) + 1] = g; + row_pointers[y][(x * 3) + 2] = b; +} + + +uint32_t png::width() { return png_get_image_width(png_ptr, png_info_ptr); } +uint32_t png::height() { return png_get_image_height(png_ptr, png_info_ptr); } diff --git a/libpng_wrapper.hpp b/libpng_wrapper.hpp new file mode 100644 index 0000000..cf95dff --- /dev/null +++ b/libpng_wrapper.hpp @@ -0,0 +1,42 @@ +#ifndef PNGWRAP_H +#define PNGWRAP_H + +#include <string> +#include <array> +#include <png.h> + +#define PNG_FILE_ERROR 0 +#define PNG_LIBPNG_ERROR 1 + +struct rgb { + png_byte r; + png_byte g; + png_byte b; +}; + + +class png { + protected: + const int DEFAULT_BIT_DEPTH = 8; + const int DEFAULT_COLOR_TYPE = PNG_COLOR_TYPE_RGB; + const bool DEFAULT_INTERLACE = false; + const int DEFAULT_TRANSFORMS = PNG_TRANSFORM_IDENTITY; + FILE *output_fp; + png_structp png_ptr; + png_infop png_info_ptr; + png_byte **row_pointers; + + public: + //the user doesn't have a lot of control over png output settings. + //Will expand class to allow so if it's found nessesary. + ~png(); + png(std::string filename, uint32_t width, uint32_t height); + void save(); + + void set_pixel(uint32_t x, uint32_t y, png_byte r, png_byte g, png_byte b); + + uint32_t width(); + uint32_t height(); +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e801015 --- /dev/null +++ b/main.cpp @@ -0,0 +1,208 @@ +#include <thread> +#include <vector> +#include <iostream> +#include <chrono> +#include <complex> +#include <atomic> +#include <unistd.h> +#include <sstream> +#include "libpng_wrapper.hpp" +#include "mthread.hpp" +#include "test.hpp" + +using namespace std; + +//defaults + +//TODO remove temp_settings +const uint32_t DEFAULT_WIDTH = 1920; +const uint32_t DEFAULT_HEIGHT = 1080; +const int DEFAULT_JOBS = 1; +const string DEFAULT_IMG_PATH = "out.png"; + +//CONTENDOR_HI REPLACEMENT - 50000 iterations +const complex<double> DEFAULT_MIN_CORD (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000); +const complex<double> DEFAULT_MAX_CORD (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000); + +//CONTENDOR_ZOOM- 50000 iterations +//const complex<double> DEFAULT_MIN_CORD (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000); +//const complex<double> DEFAULT_MAX_CORD (-0.74364386269 + 0.00000001000, 0.13182590271 + 0.00000001000); + +const unsigned int DEFAULT_ITERS = 50000; +const unsigned int DEFAULT_BAILOUT = 256; + +void print_help(char *arg, bool error) { + stringstream help_text; + help_text.precision(numeric_limits<double>::max_digits10); + help_text << "Usage: " << arg << " [options]\n" + "Options:\n" + "\t-h\tthis cruft\n" + "\t-w\timage width \t\t\t\tdefault: " << DEFAULT_WIDTH << "\n" + "\t-H\timage height\t\t\t\tdefault: " << DEFAULT_HEIGHT << "\n" + "\t-o\timage output path\t\t\tdefault: " << DEFAULT_IMG_PATH << "\n" + "\t-j\tjobs -- set this to your corecount\tdefault: " << DEFAULT_JOBS << "\n" + "\t-c\tcomplex bottom border\t\t\tdefault: " << DEFAULT_MIN_CORD << "\n" + "\t-C\tcomplex top border\t\t\tdefualt: " << DEFAULT_MAX_CORD << " \n" + "\t-i\tfractal iterations\t\t\tdefault: " << DEFAULT_ITERS << "\n" + "\t-I\tbailout value\t\t\t\tdefault: " << DEFAULT_BAILOUT << "\n" + "\nFOR COMPLEX NUMBERS: if you want to input, say, 2-3i, your option argument will be \"(2,-3)\".\n"; + cout << help_text.str() << endl; + exit(error); +} + +template <class t> +bool getopt_int(t& number, char *optarg, char opt, char *arg) { + try { number = stoi(optarg); } + catch(invalid_argument const&) { + cout << "You must supply an integer for option -" << opt << "." << endl; + print_help(arg, true); + return true; + } + catch(out_of_range const&) { + cout << "You must supply an integer under " << numeric_limits<t>::max() << " for option -" << opt << "." << endl; + print_help(arg, true); + return true; + } + return false; +} + + + +int main(int argc, char **argv) { + + //argument options + uint32_t width = DEFAULT_WIDTH; + uint32_t height = DEFAULT_HEIGHT; + unsigned int jobs = DEFAULT_JOBS; + string img_path = DEFAULT_IMG_PATH; + complex<double> min_cord = DEFAULT_MIN_CORD; + complex<double> max_cord = DEFAULT_MAX_CORD; + unsigned int m_iters = DEFAULT_ITERS; + unsigned int bailout = DEFAULT_BAILOUT; + bool jobs_set = false; + + //I could not find a better way to turn a string into a complex variable + stringstream complex_str_buffer; + int arg; + while((arg = getopt(argc, argv, "hw:H:o:j:c:C:i:I:")) != -1) { + switch(arg) { + case 'h': + print_help(argv[0], false); + break; + case 'w': + getopt_int(width, optarg, 'w', argv[0]); + break; + case 'H': + getopt_int(height, optarg, 'H', argv[0]); + break; + case 'o': + img_path = optarg; + break; + case 'j': + getopt_int(jobs, optarg, 'j', argv[0]); + jobs_set = true; + break; + case 'c': + complex_str_buffer << optarg; + complex_str_buffer >> min_cord; + break; + case 'C': + complex_str_buffer << optarg; + complex_str_buffer >> max_cord; + break; + case 'i': + getopt_int(m_iters, optarg, 'i', argv[0]); + break; + case 'I': + getopt_int(bailout, optarg, 'I', argv[0]); + break; + default: + cout << "Invalid option." << endl; + print_help(argv[0], true); + exit(1); + break; + } + } + + if(!jobs_set) { + cout << "\nPERFORMANCE TIP: for best preformance, set jobs to the number of cores in your CPU.\n" + "See " << argv[0] << " -h for help.\n" << endl; + } + + double *vmap = new double[width * height]; + unsigned int *histogram = new unsigned int[m_iters](); + unsigned int histogram_sum = 0; + double *freq_hue = new double[m_iters](); + double current_hue = 0; + + unsigned int width_per_job = width / jobs; + atomic<uint32_t> progress(0); + + png image(img_path, width, height); + thread threads[jobs]; + + + //allocate worker threads, spawn workers + mthread** worker_objects = (mthread **)malloc(sizeof(mthread) * jobs); + + + for(unsigned int j = 0; j < jobs - 1; j++) { + worker_objects[j] = new mthread(j * width_per_job, (j + 1) * width_per_job, + min_cord, max_cord, bailout, m_iters, + image, vmap, histogram, worker_objects, j, jobs, progress); + } + + //last worker thread needs the width to go all the way to the edge of the screen, + //regardless of rounding issues + worker_objects[jobs - 1] = new mthread((jobs - 1) * width_per_job, width - 1, + min_cord, max_cord, bailout, m_iters, + image, vmap, histogram, worker_objects, jobs - 1, jobs, progress); + + + for(unsigned int j = 0; j < jobs; j++) worker_objects[j]->dispatch(); + + //the progress variables is simply how many pixels we have calculated + while(progress < (height * jobs)) { + cout << "\033[2K\033[0GCalculating pixel values... " << ((float)progress / (height * jobs)) * 100 << "\% complete" << flush; + this_thread::sleep_for(chrono::milliseconds(100)); + } + cout << endl; + + for(unsigned int j = 0; j < jobs; j++) worker_objects[j]->join(); + + //now to color the image + cout << "Coloring image... (this shouldn't take more then a few seconds)" << endl; + + //find the sum of all histogram values, we could ajust this to increase or decrease contrast + for(unsigned int p = 0; p < m_iters; p++) histogram_sum += histogram[p]; + + for(unsigned int i = 0; i < m_iters; i++) { + current_hue += histogram[i] / (double)histogram_sum; + freq_hue[i] = current_hue; + } + + //now to calculate the colors + { + double below, above, hue; + int c; + uint32_t x, y; + int rgb[3]; + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + below = freq_hue[(int)vmap[(y * width) + x]]; + above = freq_hue[(int)ceil(vmap[((y * width) + x) + 1])]; + hue = (((above - below) * fmod(vmap[(y * width) + x], 1.0)) + below); + rgb[0] = 255 * cos((M_PI * hue) - M_PI); + rgb[1] = 255 * cos((M_PI * hue) - ((M_PI) / 2.0)); + rgb[2] = 255 * cos(M_PI * hue); + for(c = 0; c < 3; c++) if(rgb[c] < 0) rgb[c] = 0; + image.set_pixel(x, y, (png_byte)rgb[0], (png_byte)rgb[1], (png_byte)rgb[2]); + } + } + } + + for(unsigned int j = 0; j < jobs; j++) delete worker_objects[j]; + + cout << "Image exported to " << img_path << "." << endl; + +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..fad6a86 --- /dev/null +++ b/makefile @@ -0,0 +1,21 @@ +CCOPTS=-ggdb -O0 -Wall -lpng -pthread +OUTFILE=mandelbrot + +SRCFILES := $(wildcard *.cpp) + +OBJFILES := $(patsubst %.cpp,build/%.o,$(SRCFILES)) + +all: $(OBJFILES) + g++ $(CCOPTS) -o $(OUTFILE) $(OBJFILES) + +build/%.o: %.cpp + if [ ! -d "build" ]; then mkdir -p build; fi + g++ $(CCOPTS) -c -o $@ $< + +clean: + rm -f build/*.o + rm -f *.out + rm -f compile_commands.json + +run: + ./$(OUTFILE) diff --git a/mthread.cpp b/mthread.cpp new file mode 100644 index 0000000..796055e --- /dev/null +++ b/mthread.cpp @@ -0,0 +1,204 @@ +#include "mthread.hpp" +#include <iostream> +#include <complex> +#include <unistd.h> +#include <thread> +#include <chrono> +#include <cmath> +#include <algorithm> +#include <atomic> +using namespace std; + +mthread::mthread( + unsigned int x_mn, unsigned int x_mx, complex<double> c_min, complex<double> c_max, + unsigned int inf_cutoff, unsigned int max_iter, png& image, double *g_vmap, unsigned int *g_histogram, + mthread **worker_list, unsigned int id, unsigned int jobs, atomic<uint32_t>& progress) + : x_min_orig(x_mn), x_max_orig(x_mx), + c_min(c_min), c_max(c_max), + inf_cutoff(inf_cutoff), max_iter(max_iter), image(image), id(id), worker_cnt(jobs), progress(progress){ + + workers = worker_list; + x_min = x_mn; + x_max = x_mx; + y_min = 0; + y_max = image.height(); + vmap = g_vmap; + histogram = g_histogram; + step = (c_max - c_min) / complex<double>(image.height(), image.width()); + my_thread = NULL; +} + +void mthread::dispatch() { + if((my_thread) && (my_thread->joinable())) delete my_thread; + my_thread = new thread([this] {render();}); +} + + +mthread::~mthread() { + if((my_thread) && (my_thread->joinable())) { + my_thread->join(); + delete my_thread; + } +} + + +void mthread::join() { + if((my_thread) && (my_thread->joinable())) my_thread->join(); +} + + +void mthread::render() { + uint32_t image_width = image.width(); + unsigned int iter; + unsigned int worker, workers_finished; + uint32_t loads[worker_cnt]; + double pixel_value; + complex<double> c, a; + struct mthread_status *peer_status; + struct mthread_divinfo divinfo; + + unique_lock<mutex> ack; + unique_lock<mutex> syn_ack; + + + status.status_lock.lock(); + status.searching = false; + status.share_finished = false; + status.div_syn = false; + status.div_error = false; + status.status_lock.unlock(); + + + y_min = 0; + y_max = image.height(); + + + + for(;;) { + //thread is actively rendering + for(on_y = y_min; on_y < y_max; on_y++) { + progress++; + status.status_lock.lock(); + status.row_load = y_max - on_y; + if(status.div_syn) { + + status.div_error = status.row_load <= min_lines; + + if(status.div_error) { + status.ack_lk.unlock(); + status.msg_notify.notify_all(); + } + else { + syn_ack = unique_lock<mutex>(status.syn_ack_lk); + status.ack_lk.unlock(); + status.msg_notify.notify_all(); + status.msg_notify.wait(syn_ack); + status.row_load = y_max - on_y; + syn_ack.unlock(); + //new x/y min/max is ajusted by other thread, we can continue as normal. + } + } + status.status_lock.unlock(); + + for(on_x = x_min; on_x < x_max; on_x++) { + c = (step * complex<double>(on_x,on_y)) + c_min; + a = 0; + for(iter = 0; iter < max_iter; iter++) { + if(abs(a) >= inf_cutoff) break; + a = a*a + c; + } + if(iter >= max_iter) { + iter = 0; + vmap[(on_y * image_width) + on_x] = 0; + } + else { + pixel_value = (iter + 1) - (log((log(pow(abs(a), 2.0)) / 2.0) / log(2.0))); + vmap[(on_y * image_width) + on_x] = pixel_value; + histogram[(int)pixel_value]++; + } + } + } + + + //thread is now searching for work + + /** 2022 comment: + * this state should have been moved to a seperate function to allow rendering methods to differ + * from inherited mthreads without needing to reimpliment searching **/ + status.status_lock.lock(); + status.searching = true; + status.share_finished = true; + status.status_lock.unlock(); + + while(status.searching) { + workers_finished = 0; + for(worker = 0; worker < worker_cnt; worker++) { + peer_status = &workers[worker]->status; + peer_status->status_lock.lock(); + + if((peer_status->share_finished) && (worker != id)) { + workers_finished++; + } + if((worker == id) || + (peer_status->searching) || (peer_status->div_syn) || (peer_status->row_load < min_lines)) { + loads[worker] = 0; + peer_status->status_lock.unlock(); + continue; + } + loads[worker] = peer_status->row_load; + peer_status->status_lock.unlock(); + } + if(workers_finished >= worker_cnt - 1) { + return; + } + for(;;) { + worker = distance(loads, max_element(loads, &loads[worker_cnt])); + if(!loads[worker]) break; + peer_status = &workers[worker]->status; + peer_status->status_lock.lock(); + if((peer_status->searching) || (peer_status->div_syn) || (peer_status->row_load < min_lines)) { + loads[worker] = 0; + peer_status->status_lock.unlock(); + continue; + } + ack = unique_lock<mutex>(peer_status->ack_lk); + peer_status->div_syn = true; + peer_status->status_lock.unlock(); + peer_status->msg_notify.wait(ack); + ack.unlock(); + if(peer_status->div_error) { + loads[worker] = 0; + peer_status->status_lock.lock(); + peer_status->div_error = false; + peer_status->div_syn = false; + peer_status->status_lock.unlock(); + continue; + } + + divinfo = workers[worker]->divide(); + peer_status->syn_ack_lk.unlock(); + peer_status->msg_notify.notify_all(); + y_min = divinfo.y_min; + y_max = divinfo.y_max; + x_min = divinfo.x_min; + x_max = divinfo.x_max; + status.status_lock.lock(); + status.searching = false; + status.status_lock.unlock(); + break; + } + } + } +} + +struct mthread_divinfo mthread::divide() { + struct mthread_divinfo ret; + ret.x_min = x_min; + ret.x_max = x_max; + ret.y_min = ((y_max - on_y) / 2) + on_y; + ret.y_max = y_max; + y_min = on_y; + y_max = ret.y_min; + status.div_syn = false; + return ret; +} diff --git a/mthread.hpp b/mthread.hpp new file mode 100644 index 0000000..c70647e --- /dev/null +++ b/mthread.hpp @@ -0,0 +1,71 @@ +#ifndef MTHREAD_H +#define MTHREAD_H + +#include <mutex> +#include <atomic> +#include <thread> +#include <complex> +#include <condition_variable> +#include "libpng_wrapper.hpp" + +struct mthread_status { + std::mutex status_lock; + unsigned int row_load; + bool share_finished; + bool searching; + + bool div_syn; + std::mutex ack_lk; + std::mutex syn_ack_lk; + bool div_error; + + std::condition_variable msg_notify; +}; + +struct mthread_divinfo { + uint32_t x_min, x_max; + uint32_t y_min, y_max; +}; + +class mthread { + public: + mthread(unsigned int x_mn, unsigned int x_mx, + std::complex<double> c_min, std::complex<double> c_max, + unsigned int inf_cutoff, unsigned int max_iter, png& image, double *g_vmap, unsigned int *g_histogram, + mthread **worker_list, unsigned int id, unsigned int jobs, std::atomic<uint32_t>& progress); + ~mthread(); + void join(); + void dispatch(); + + + struct mthread_status status; + unsigned int *histogram; + protected: + const unsigned int min_lines = 4; + const unsigned int x_min_orig, x_max_orig; + const std::complex<double> c_min; + const std::complex<double> c_max; + const unsigned int inf_cutoff; + const unsigned int max_iter; + png& image; + double *vmap; + const unsigned int id; + mthread **workers; + std::complex<double> c_current_min; + std::complex<double> c_current_max; + std::complex<double> step; + std::thread *my_thread; + const unsigned int worker_cnt; + std::atomic<uint32_t>& progress; + + uint32_t x_min, x_max, y_min, y_max; + uint32_t on_x, on_y; + unsigned int divisions; + void render(); + int state; + struct mthread_divinfo divide(); + + +}; + +#endif @@ -0,0 +1,109 @@ +NOTES TO SELF + +atomic struct mthread_status { + unsigned int area_covered + bool undevidable + bool searching + bool division_syn + bool division_ack + bool division_syn_ack +} + + +Thread finishes its job. +1: set "searching" local atomic struct to true +2: for each thread: + + if done=true; exit + + if searing, division_syn, division_ack, undevidable: go to next + if all seem to be searching, set global done=true + + if not: continue below + +3: record area_covered in own array, if more threads proceed to next, else continue below + +4: pick thread with largest area_covered: +5: if division_syn or division_ack or searching or undevidable, go to next + +5: set division_syn to true, wait for division_ack +6: divide(): copy over half of x/y, copy divisions, check and set undevidable, start to do work + +thread is working. +1: +for y: + update area_covered + check division_syn + if true: 2 inline here + for x: + do some math stuff here +searching = true, "if thread finishes its job" inline here. + + +2: +set division_ack +wait for division_syn_ack +check and set undevidable +// there should be no conflict with updating variable y is dependent on, +// as it's always more then current, and test will be accurate next loop. + + +thread is starting. +1: +for y: + update area_covered + check division_syn + if true: 2 inline here + for x: + do some math stuff here +searching = true, "if thread finishes its job" inline here. + + +PREFORMANCE: +without tasks helping eachother (commit c30fc7596810f7033dfd9a7452c808153bd2e14a): + +const uint32_t WIDTH = 1920; +const uint32_t HEIGHT = 1080; +const int JOBS = 6; //test uneven stuff +const std::complex<double> b_min (-0.7463-0.005, 0.1102-0.005); +const std::complex<double> b_max (-0.7463+0.005, 0.1102+0.005); +#define MAX_ITER 1000 +#define INF_CUTOFF 256 +#define COLOR_RAMP 100 + +/usr/bin/time -f '%p' -p ./mandelbrot + +real 36.41 +user 105.00 +sys 0.02 + +real 36.38 +user 104.98 +sys 0.02 + +real 36.32 +user 105.00 +sys 0.05 + +With tasks helping eachother: (commit 3663966b88681f44ec8939e39e33ef922227b7a7): + +real 19.21 +user 111.70 +sys 0.03 + +real 19.29 +user 111.61 +sys 0.02 + +real 19.30 +user 111.84 +sys 0.02 + + +//CONTENDOR_HI REPLACEMENT - 50000 iterations +//const complex<double> DEFAULT_B_MIN (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000); +//const complex<double> DEFAULT_B_MAX (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000); + +//CONTENDOR_ZOOM- 50000 iterations +//const complex<double> DEFAULT_B_MIN (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000); +//const complex<double> DEFAULT_B_MAX (-0.74364386269 + 0.00000001000, 0.13182590271 + 0.00000001000); Binary files differdiff --git a/out1.png b/out1.png Binary files differnew file mode 100644 index 0000000..f572131 --- /dev/null +++ b/out1.png diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..bc2366c --- /dev/null +++ b/test.cpp @@ -0,0 +1,35 @@ +#include <iostream> +#include <string> +#include "libpng_wrapper.hpp" +using namespace std; + +void test_png() { + png test_image("test_png.png", 500, 500); + uint32_t w = test_image.width(); + uint32_t h = test_image.height(); + + uint32_t square_w = w / 2; + uint32_t square_h = h / 2; + uint32_t square_x = square_w; + uint32_t square_y = square_h; + + for(uint32_t y = 0; y < h; y++) { + for(uint32_t x = 0; x < w; x++) { + if((x < (w / 2)) && (y < (h / 2))) { + test_image.set_pixel(x, y, 255, 0, 0); + } + else if((x > (w / 2)) && (y < (h / 2))) { + test_image.set_pixel(x, y, 0, 255, 0); + } + else if((x < (w / 2)) && (y > (h / 2))) { + test_image.set_pixel(x, y, 0, 0, 255); + } + else if((x > (w / 2)) && (y > (h / 2))) { + test_image.set_pixel(x, y, + (255 / square_w) * (x - square_x), + (255 / square_h) * (y - square_y), + (255 / square_w) * (square_x - x)); + } + } + } +} diff --git a/test.hpp b/test.hpp new file mode 100644 index 0000000..3347496 --- /dev/null +++ b/test.hpp @@ -0,0 +1,6 @@ +#ifndef TEST_H +#define TEST_H + +void test_png(); + +#endif |