From 8db9e4cfba7de89b5492203ed1b225297be47f68 Mon Sep 17 00:00:00 2001 From: Brett Weiland Date: Sun, 22 Jan 2023 15:34:25 -0600 Subject: init --- .gitignore | 2 + docs/demo.mp4 | Bin 0 -> 40490910 bytes docs/demo_1.png | Bin 0 -> 2678925 bytes docs/demo_2.png | Bin 0 -> 2044428 bytes docs/demo_3.png | Bin 0 -> 1114088 bytes docs/dia/diagram.dia | Bin 0 -> 2942 bytes docs/dia/div_request.png | Bin 0 -> 158900 bytes docs/p4.pdf | Bin 0 -> 5197562 bytes gdbinit.gdb | 1 + libpng_wrapper.cpp | 46 +++++++++++ libpng_wrapper.hpp | 42 ++++++++++ main.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++++++ makefile | 21 +++++ mthread.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++++ mthread.hpp | 71 ++++++++++++++++ notes | 109 +++++++++++++++++++++++++ out.png | Bin 0 -> 2679921 bytes out1.png | Bin 0 -> 2679899 bytes test.cpp | 35 ++++++++ test.hpp | 6 ++ 20 files changed, 745 insertions(+) create mode 100644 .gitignore create mode 100644 docs/demo.mp4 create mode 100644 docs/demo_1.png create mode 100644 docs/demo_2.png create mode 100644 docs/demo_3.png create mode 100644 docs/dia/diagram.dia create mode 100644 docs/dia/div_request.png create mode 100644 docs/p4.pdf create mode 100644 gdbinit.gdb create mode 100644 libpng_wrapper.cpp create mode 100644 libpng_wrapper.hpp create mode 100644 main.cpp create mode 100644 makefile create mode 100644 mthread.cpp create mode 100644 mthread.hpp create mode 100644 notes create mode 100644 out.png create mode 100644 out1.png create mode 100644 test.cpp create mode 100644 test.hpp 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 new file mode 100644 index 0000000..0a40287 Binary files /dev/null and b/docs/demo.mp4 differ diff --git a/docs/demo_1.png b/docs/demo_1.png new file mode 100644 index 0000000..94f583e Binary files /dev/null and b/docs/demo_1.png differ diff --git a/docs/demo_2.png b/docs/demo_2.png new file mode 100644 index 0000000..cb26cba Binary files /dev/null and b/docs/demo_2.png differ diff --git a/docs/demo_3.png b/docs/demo_3.png new file mode 100644 index 0000000..60b35a2 Binary files /dev/null and b/docs/demo_3.png differ diff --git a/docs/dia/diagram.dia b/docs/dia/diagram.dia new file mode 100644 index 0000000..45cb6cc Binary files /dev/null and b/docs/dia/diagram.dia differ diff --git a/docs/dia/div_request.png b/docs/dia/div_request.png new file mode 100644 index 0000000..1812630 Binary files /dev/null and b/docs/dia/div_request.png differ diff --git a/docs/p4.pdf b/docs/p4.pdf new file mode 100644 index 0000000..858774b Binary files /dev/null and b/docs/p4.pdf differ 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 +#include +#include +#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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#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 DEFAULT_MIN_CORD (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000); +const complex DEFAULT_MAX_CORD (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000); + +//CONTENDOR_ZOOM- 50000 iterations +//const complex DEFAULT_MIN_CORD (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000); +//const complex 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::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 +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::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 min_cord = DEFAULT_MIN_CORD; + complex 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 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 +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +mthread::mthread( + unsigned int x_mn, unsigned int x_mx, complex c_min, complex 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& 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(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 c, a; + struct mthread_status *peer_status; + struct mthread_divinfo divinfo; + + unique_lock ack; + unique_lock 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(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(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(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 +#include +#include +#include +#include +#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 c_min, std::complex 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& 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 c_min; + const std::complex c_max; + const unsigned int inf_cutoff; + const unsigned int max_iter; + png& image; + double *vmap; + const unsigned int id; + mthread **workers; + std::complex c_current_min; + std::complex c_current_max; + std::complex step; + std::thread *my_thread; + const unsigned int worker_cnt; + std::atomic& 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 diff --git a/notes b/notes new file mode 100644 index 0000000..23b8396 --- /dev/null +++ b/notes @@ -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 b_min (-0.7463-0.005, 0.1102-0.005); +const std::complex 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 DEFAULT_B_MIN (-0.74364386269 - 0.00000003000, 0.13182590271 - 0.00000003000); +//const complex DEFAULT_B_MAX (-0.74364386269 + 0.00000003000, 0.13182590271 + 0.00000003000); + +//CONTENDOR_ZOOM- 50000 iterations +//const complex DEFAULT_B_MIN (-0.74364386269 - 0.00000001000, 0.13182590271 - 0.00000001000); +//const complex DEFAULT_B_MAX (-0.74364386269 + 0.00000001000, 0.13182590271 + 0.00000001000); diff --git a/out.png b/out.png new file mode 100644 index 0000000..c85eb9b Binary files /dev/null and b/out.png differ diff --git a/out1.png b/out1.png new file mode 100644 index 0000000..f572131 Binary files /dev/null and b/out1.png differ 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 +#include +#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 -- cgit v1.2.3