Skip to content

Instantly share code, notes, and snippets.

@davawen
Created October 4, 2023 20:56
Show Gist options
  • Save davawen/19acca47250e687f51deb55f16145c67 to your computer and use it in GitHub Desktop.
Save davawen/19acca47250e687f51deb55f16145c67 to your computer and use it in GitHub Desktop.
#include <SDL2/SDL.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <pthread.h>
typedef struct {
double real;
double imag;
} complex_t;
uint16_t max_iter = 255;
uint16_t mandelbrot(complex_t c) {
complex_t z = { 0 };
for (uint16_t iterations = 0; iterations < max_iter; iterations++) {
if (z.real >= 2 || z.imag >= 2) return iterations;
// (a + bi)^2
// a^2 + 2abi - b^2
z = (complex_t) {
.real = z.real*z.real - z.imag*z.imag,
.imag = 2*z.real*z.imag
};
z.real += c.real;
z.imag += c.imag;
}
return max_iter;
}
typedef struct {
double x, y;
double zoom;
} Camera;
typedef struct {
SDL_Renderer *renderer;
const Camera *camera;
int w;
int h;
unsigned int id;
unsigned int num;
uint16_t *buf;
} ThreadData;
void *render(void *data) {
ThreadData d = *(ThreadData *)data;
const Camera *cam = d.camera;
int w = d.w, h = d.h;
for (int y = d.id; y < h; y += d.num) {
for (int x = 0; x < w; x++) {
complex_t c = {
.real = ((double)x / w - 0.5) * cam->zoom + cam->x,
.imag = ((double)y / w - ((double)h/w)/2.0) * cam->zoom + cam->y
};
d.buf[y*w + x] = mandelbrot(c);
}
}
return NULL;
}
int main() {
SDL_Window *window;
SDL_Renderer *renderer;
assert(SDL_Init(SDL_INIT_VIDEO) == 0);
SDL_CreateWindowAndRenderer(640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE, &window, &renderer);
assert(window != NULL);
assert(renderer != NULL);
SDL_RenderClear(renderer);
Camera camera = {
.x = -0.5, .y = 0.0,
.zoom = 2.5
};
struct {
uint16_t *data;
uint32_t len;
} buf = { NULL, 0 };
bool holding_shift = false;
bool window_open = true;
bool rerender = true;
while (window_open) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
window_open = false;
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_LSHIFT) holding_shift = true;
break;
case SDL_KEYUP:
if (event.key.keysym.sym == SDLK_LSHIFT) holding_shift = false;
break;
case SDL_MOUSEWHEEL:
if (holding_shift) {
if (event.wheel.y < 0 && max_iter > 1 || event.wheel.y > 0 && max_iter < 1000) {
max_iter += event.wheel.y;
printf("%ui\n", max_iter);
rerender = true;
}
} else {
double delta = (double)event.wheel.y / 10.0;
camera.x += camera.zoom*delta*((double)event.wheel.mouseX / w - 0.5);
camera.y += camera.zoom*delta*((double)event.wheel.mouseY / w - ((double)h/w/2.0));
camera.zoom *= 1.0 - delta;
rerender = true;
}
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
rerender = true;
break;
}
}
if (rerender) {
uint32_t len = w*h*sizeof(uint16_t);
if (buf.len < len) {
buf.data = malloc(len);
buf.len = len;
}
pthread_t t[6];
ThreadData d[6];
for (int i = 0; i < 6; i++) {
d[i] = (ThreadData) {
renderer, &camera, w, h, i, 6, buf.data
};
pthread_create(&t[i], NULL, render, &d[i]);
}
for (int i = 0; i < 6; i++) {
pthread_join(t[i], NULL);
}
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint16_t i = buf.data[y*w + x];
if (i == max_iter) SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
else SDL_SetRenderDrawColor(renderer, i*255 / max_iter, i*128/max_iter, 0, 255);
SDL_RenderDrawPoint(renderer, x, y);
}
}
rerender = false;
SDL_RenderPresent(renderer);
}
SDL_Delay(1000 / 60);
}
free(buf.data);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment