mandulia-0.4: src/rjulia.c
/*
Mandulia -- Mandelbrot/Julia explorer
Copyright (C) 2010 Claude Heiland-Allen <claudiusmaximus@goto10.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rjulia.h"
#ifndef PRECISION
#define PRECISION 1
#endif
#if PRECISION == 1
typedef float R;
#define cos cosf
#define sqrt sqrtf
#define log logf
#define log2 log2f
#define fmin fminf
#define fmax fmaxf
#define SCAN "%f"
#else
typedef double R;
#define SCAN "%lf"
#endif
static const int channels = 4;
static const R escapeRadius = 65536.0;
static const R escapeRadius2 = 65536.0 * 65536.0;
static const int escapeLimit = 64;
static const R sqrt5 = 2.23606797749979;
static const R sqrt6 = 2.449489742783178;
static const R sqrt7 = 2.6457513110645907;
struct point {
unsigned char *pixel;
R x;
R y;
};
struct context {
struct point *points;
int width;
int height;
};
struct context *julia_new(int width, int height) {
if (width > 0 && height > 0) {
struct context *ctx = calloc(1, sizeof(struct context));
if (ctx) {
ctx->points = calloc(width * height, sizeof(struct point));
if (ctx->points) {
ctx->width = width;
ctx->height = height;
return ctx;
}
free(ctx);
}
}
return 0;
}
/*
void julia_delete(struct context *ctx) {
if (ctx) {
if (ctx->points) {
free(ctx->points);
}
free(ctx);
}
}
*/
static inline int min(int x, int y) {
return x < y ? x : y;
}
static inline int max(int x, int y) {
return x > y ? x : y;
}
static inline int colour(R a) {
int channel = 128 * cos(a) + 128;
return min(max(channel, 0), 255);
}
void julia(struct context *ctx, unsigned char *image, double dcx, double dcy) {
if (! ctx || ! image) {
return;
}
struct point *points = ctx->points;
const int width = ctx->width;
const int height = ctx->height;
const int stride = width * channels;
const R cx = dcx;
const R cy = dcy;
const R hw = height * 3.0 / width;
const R wh = width * 3.0 / height;
const R sx = width < height ? 3 : wh;
const R sy = width < height ? hw : 3;
const int ntotal = width * height;
int npoints;
{ // initialize
npoints = ntotal;
struct point *p = points;
for (int j = 0; j < height; ++j) {
R y = j * sy / height - sy/2;
for (int i = 0; i < width; ++i) {
R x = i * sx / width - sx/2;
p->pixel = image + j * stride + i * channels;
p->x = x;
p->y = y;
p->pixel[0] = 0;
p->pixel[1] = 0;
p->pixel[2] = 0;
p->pixel[3] = 255;
++p;
}
}
}
int ndone = 0;
int n = 0;
int escapes;
do {
escapes = 0;
{ // iterate
struct point *p = points;
for (int k = 0; k < npoints; ++k) {
R x = p->x;
R y = p->y;
int ok = 1;
for (int e = 0; ok && (e < escapeLimit); ++e) {
R zx2 = x * x - y * y;
R zy2 = 2 * x * y;
R zx = zx2 + cx;
R zy = zy2 + cy;
R zd2 = zx * zx + zy * zy;
if (zd2 >= escapeRadius2) {
R d = sqrt(zd2);
// colourify pixel
unsigned char *pixel = p->pixel;
R v = 0.025 * ((n+e) - log2(log2(d)/log2(escapeRadius)));
R a = fmin(fmax(8 * (v - 0.1) * v, 0), 1);
pixel[0] = colour(sqrt5 * v );
pixel[1] = colour(sqrt6 * v + 1);
pixel[2] = colour(sqrt7 * v + 2);
pixel[3] = a * 255;
p->pixel = 0;
++escapes;
ok = 0;
} else {
x = zx;
y = zy;
}
}
p->x = x;
p->y = y;
++p;
}
n += escapeLimit;
}
{ // compact memory
struct point *src = points;
struct point *dst = points;
int ncopied = 0;
for (int k = 0; k < npoints; ++k) {
if (! (src->pixel)) {
++src;
++ndone;
} else {
unsigned char *pixel = src->pixel;
R x = src->x;
R y = src->y;
dst->pixel = pixel;
dst->x = x;
dst->y = y;
++src;
++dst;
++ncopied;
}
}
npoints = ncopied;
}
} while(escapes);
}