#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mandelbrot.h" #include "mouse.h" #include "ui.h" /*TODO * 1. Store iterations instead of colors ? * 1.1 memory usage vs performance loss ??? (cant draw in parallel anyway) * 2. Look into SDL2 rendering * 2.1 texture tendering DONE * 2.2 fix colors ??? * 3. employ SSE2 * 3.1 x86intrin.h * 3.2 inline assembly */ int mandelbrot(Complex c) { // builb check //if ((c.real + 1.0) * (c.real + 1.0) + c.imag * c.imag <= 0.25) // return MAX_ITERATIONS; Complex z = {0.0, 0.0}; int i; for (i = 0; i < MAX_ITERATIONS; i++) { double temp_real = z.real * z.real - z.imag * z.imag + c.real; double temp_imag = 2 * z.real * z.imag + c.imag; z.real = temp_real; z.imag = temp_imag; if (__builtin_hypot(z.real, z.imag) > 2) return i; } return MAX_ITERATIONS; } typedef struct{ uint8_t r; uint8_t g; uint8_t b; uint8_t a; } Color; typedef struct { Color *pointer; int size; }Array; void calculate_set(Array *arr, int height, int width, ViewInfo view) { #pragma omp parallel for schedule(guided) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { double real = view.x_min + (x * (view.x_max - view.x_min)) / width; double imag = view.y_min + (y * (view.y_max - view.y_min)) / height; Complex c = {real, imag}; int iterations = mandelbrot(c); if (iterations == MAX_ITERATIONS){ arr->pointer[y * width + x].r = 0; arr->pointer[y * width + x].g = 0; arr->pointer[y * width + x].b = 0; arr->pointer[y * width + x].a = 0; } else { double t = (double)iterations / MAX_ITERATIONS; t = 0.5 + 0.5 * tan(log(t + 0.0001) * 3.0); arr->pointer[y * width + x].r = 255 * t * 0.5; arr->pointer[y * width + x].g = 255 * t * 0.4; arr->pointer[y * width + x].b = 255 * t; arr->pointer[y * width + x].a = 255; } } } } void update_texture(App *app, Array *arr) { void *pixels; int pitch; if (SDL_LockTexture(app->texture, NULL, &pixels, &pitch) != 0) { fprintf(stderr, "Failed to lock texture: %s\n", SDL_GetError()); return; } memcpy(pixels, arr->pointer, app->win_width * app->win_height * sizeof(Color)); SDL_UnlockTexture(app->texture); } void render_cl(Array *arr, App *app, ViewInfo view) { SDL_SetRenderDrawColor(app->renderer, 0, 0, 50, 255); SDL_RenderClear(app->renderer); if (arr->size < app->win_height * app->win_width * (int)sizeof(Color)){ arr->pointer = realloc(arr->pointer, app->win_height * app->win_width * sizeof(Color)); arr->size = app->win_height * app->win_width * sizeof(Color); } if (app->needs_recalc) calculate_set(arr, app->win_height, app->win_width, view); update_texture(app, arr); SDL_RenderCopy(app->renderer, app->texture, NULL, NULL); } int main(void) { if (SDL_Init(SDL_INIT_EVERYTHING) != 0){ printf("error initializing SDL: %s\n", SDL_GetError()); return 1; } App app = { SDL_CreateWindow("Mandelbrot set exploer", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL), SDL_CreateRenderer(app.window, -1, SDL_RENDERER_ACCELERATED), SDL_CreateTexture(app.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT), SCREEN_HEIGHT, SCREEN_WIDTH, 1, 1 }; ViewInfo view = { .x_min = -2.0, .x_max = 1.0, .y_min = -1.5, .y_max = 1.5, .zoom = 1.0 }; Mouse mouse = { .dragging = 0, .start_x = 0, .start_y = 0, .original_view = view }; Uint32 frame_start; UI ui; init_ui(&ui); Array screen = { malloc(app.win_height * app.win_width * sizeof(Color)), app.win_height * app.win_width * sizeof(Color) }; int running = 1; while (running) { frame_start = SDL_GetTicks(); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: running = 0; break; case SDL_WINDOWEVENT: if(event.window.event == SDL_WINDOWEVENT_RESIZED){ SDL_GetWindowSize(app.window, &app.win_width, &app.win_height); SDL_DestroyTexture(app.texture); app.texture = SDL_CreateTexture(app.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, app.win_width, app.win_height); app.needs_recalc = 1; } break; case SDL_KEYDOWN: switch (event.key.keysym.scancode) { case SDL_SCANCODE_Q: case SDL_SCANCODE_ESCAPE: running = 0; break; case SDL_SCANCODE_MINUS: view.x_min = view.x_min * 1.1; view.x_max = view.x_max * 1.1; view.y_min= view.y_min * 1.1; view.y_max = view.y_max * 1.1; view.zoom = view.zoom * 1.1; app.needs_recalc = 1; break; case SDL_SCANCODE_EQUALS: view.x_min = view.x_min * 0.9; view.x_max = view.x_max * 0.9; view.y_min= view.y_min * 0.9; view.y_max = view.y_max * 0.9; view.zoom = view.zoom * 0.9; app.needs_recalc = 1; break; case SDL_SCANCODE_R: view.x_min = -2.0; view.x_max = 1.0; view.y_min = -1.5; view.y_max = 1.5; view.zoom = 1.0; app.needs_recalc = 1; break; case SDL_SCANCODE_H: app.draw_ui = 1 - app.draw_ui; break; } default: //returns one if recalc needed app.needs_recalc = app.needs_recalc == 0 ? handle_mouse(event, &mouse, &view) : 1; break; } } //drawing happens here SDL_SetRenderDrawColor(app.renderer, 0xff, 0xff, 0xff, 0xff); SDL_RenderClear(app.renderer); render_cl(&screen, &app, view); // render(&app, // view, // 0, app.win_width, // 0,app.win_height); int msec = SDL_GetTicks() - frame_start; float fps = 0; if(msec > 0) fps = 1000.0 / (double) msec; if(app.draw_ui) render_ui(&ui, app.renderer, view, fps); SDL_RenderPresent(app.renderer); app.needs_recalc = 0; } //SDL_FreeSurface(app.surfaceA); free(screen.pointer); SDL_DestroyRenderer(app.renderer); SDL_DestroyWindow(app.window); SDL_Quit(); }