/* This file is an image processing operation for GEGL.
*
* This GEGL operation is a port of the main part of the Fractal
* Explorer plug-in from GIMP. Fractal Explorer (Version 2) was
* originally written by Daniel Cotting (cotting@multimania.com).
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2006 Kevin Cozens <kcozens@cvs.gnome.org>
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#define MAXNCOLORS 8192
#ifdef GEGL_CHANT_PROPERTIES
gegl_chant_int_ui (width, _("Width"), 1, 10000000, 400, 1, 2000, 1.5,
_("Width"))
gegl_chant_int_ui (height, _("Height"), 1, 10000000, 400, 1, 2000, 1.5,
_("Height"))
gegl_chant_int (fractaltype, _("Fractal type"), 0, 8, 0, _("Type of a fractal"))
gegl_chant_double (xmin, _("Left"), -3.0, 3.0, -2.0, _("Left"))
gegl_chant_double (xmax, _("Right"), -3.0, 3.0, 2.0, _("Right"))
gegl_chant_double (ymin, _("Top"), -3.0, 3.0, -2.0, _("Top"))
gegl_chant_double (ymax, _("Bottom"), -3.0, 3.0, 2.0, _("Bottom"))
gegl_chant_int (iter, _("Iterations"), 1, 1000, 50, _("Iterations"))
gegl_chant_double (cx, _("CX"), -2.5, 2.5, -0.75, _("CX (only Julia)"))
gegl_chant_double (cy, _("CY"), -2.5, 2.5, 0.2, _("CY (only Julia)"))
gegl_chant_double (redstretch, _("Red stretch"), 0.0, 1.0, 1.0,
_("Red stretching factor"))
gegl_chant_double (greenstretch, _("Green stretch"), 0.0, 1.0, 1.0,
_("Green stretching factor"))
gegl_chant_double (bluestretch, _("Blue stretch"), 0.0, 1.0, 1.0,
_("Blue stretching factor"))
gegl_chant_int (redmode, _("Red mode"), 0, 2, 1,
_("Red application mode (0:SIN; 1:COS; 2:NONE)"))
gegl_chant_int (greenmode, _("Green mode"), 0, 2, 1,
_("Green application mode (0:SIN; 1:COS; 2:NONE)"))
gegl_chant_int (bluemode, _("Blue mode"), 0, 2, 0,
_("Blue application mode (0:SIN; 1:COS; 2:NONE)"))
gegl_chant_boolean (redinvert, _("Red inversion"), FALSE,
_("Red inversion"))
gegl_chant_boolean (greeninvert, _("Green inversion"), FALSE,
_("Green inversion"))
gegl_chant_boolean (blueinvert, _("Blue inversion"), FALSE,
_("Blue inversion"))
gegl_chant_int (ncolors, _("Colors"), 2, MAXNCOLORS, 256,
_("Number of colors"))
gegl_chant_boolean (useloglog, _("Loglog smoothing"), FALSE,
_("Use loglog smoothing"))
#else
#define GEGL_CHANT_TYPE_SOURCE
#define GEGL_CHANT_C_FILE "fractal-explorer.c"
#include "gegl-chant.h"
#include <math.h>
#include <stdio.h>
enum
{
SINUS,
COSINUS,
NONE
};
enum
{
TYPE_MANDELBROT,
TYPE_JULIA,
TYPE_BARNSLEY_1,
TYPE_BARNSLEY_2,
TYPE_BARNSLEY_3,
TYPE_SPIDER,
TYPE_MAN_O_WAR,
TYPE_LAMBDA,
TYPE_SIERPINSKI,
NUM_TYPES
};
typedef struct
{
guchar r, g, b;
} gucharRGB;
typedef gucharRGB clrmap[MAXNCOLORS];
static void
explorer_render_row (GeglChantO *o,
gint col_start,
gint col_end,
gint row,
clrmap colormap,
guchar **dest_row)
{
gint fractaltype;
gint col;
gdouble xmin;
gdouble ymin;
gdouble a;
gdouble b;
gdouble x;
gdouble y;
gdouble oldx;
gdouble oldy;
gdouble tempsqrx;
gdouble tempsqry;
gdouble tmpx = 0;
gdouble tmpy = 0;
gdouble foldxinitx;
gdouble foldxinity;
gdouble foldyinitx;
gdouble foldyinity;
gdouble xx = 0;
gdouble adjust;
gdouble cx;
gdouble cy;
gint counter;
gint color;
gint iteration;
gint ncolors;
gint useloglog;
gdouble xdiff;
gdouble ydiff;
gdouble log2;
fractaltype = o->fractaltype;
xmin = o->xmin;
ymin = o->ymin;
cx = o->cx;
cy = o->cy;
iteration = o->iter;
ncolors = o->ncolors;
useloglog = o->useloglog;
log2 = log (2.0);
xdiff = (o->xmax - xmin) / o->width;
ydiff = (o->ymax - ymin) / o->height;
for (col = col_start; col < col_end; col++)
{
a = xmin + (gdouble) col * xdiff;
b = ymin + (gdouble) row * ydiff;
if (fractaltype != 0)
{
tmpx = x = a;
tmpy = y = b;
}
else
{
x = 0;
y = 0;
}
for (counter = 0; counter < iteration; counter++)
{
oldx=x;
oldy=y;
switch (fractaltype)
{
case TYPE_MANDELBROT:
xx = x * x - y * y + a;
y = 2.0 * x * y + b;
break;
case TYPE_JULIA:
xx = x * x - y * y + cx;
y = 2.0 * x * y + cy;
break;
case TYPE_BARNSLEY_1:
foldxinitx = oldx * cx;
foldyinity = oldy * cy;
foldxinity = oldx * cy;
foldyinitx = oldy * cx;
/* orbit calculation */
if (oldx >= 0)
{
xx = (foldxinitx - cx - foldyinity);
y = (foldyinitx - cy + foldxinity);
}
else
{
xx = (foldxinitx + cx - foldyinity);
y = (foldyinitx + cy + foldxinity);
}
break;
case TYPE_BARNSLEY_2:
foldxinitx = oldx * cx;
foldyinity = oldy * cy;
foldxinity = oldx * cy;
foldyinitx = oldy * cx;
/* orbit calculation */
if (foldxinity + foldyinitx >= 0)
{
xx = foldxinitx - cx - foldyinity;
y = foldyinitx - cy + foldxinity;
}
else
{
xx = foldxinitx + cx - foldyinity;
y = foldyinitx + cy + foldxinity;
}
break;
case TYPE_BARNSLEY_3:
foldxinitx = oldx * oldx;
foldyinity = oldy * oldy;
foldxinity = oldx * oldy;
/* orbit calculation */
if (oldx > 0)
{
xx = foldxinitx - foldyinity - 1.0;
y = foldxinity * 2;
}
else
{
xx = foldxinitx - foldyinity -1.0 + cx * oldx;
y = foldxinity * 2;
y += cy * oldx;
}
break;
case TYPE_SPIDER:
/* { c=z=pixel: z=z*z+c; c=c/2+z, |z|<=4 } */
xx = x*x - y*y + tmpx + cx;
y = 2 * oldx * oldy + tmpy +cy;
tmpx = tmpx/2 + xx;
tmpy = tmpy/2 + y;
break;
case TYPE_MAN_O_WAR:
xx = x*x - y*y + tmpx + cx;
y = 2.0 * x * y + tmpy + cy;
tmpx = oldx;
tmpy = oldy;
break;
case TYPE_LAMBDA:
tempsqrx = x * x;
tempsqry = y * y;
tempsqrx = oldx - tempsqrx + tempsqry;
tempsqry = -(oldy * oldx);
tempsqry += tempsqry + oldy;
xx = cx * tempsqrx - cy * tempsqry;
y = cx * tempsqry + cy * tempsqrx;
break;
case TYPE_SIERPINSKI:
xx = oldx + oldx;
y = oldy + oldy;
if (oldy > .5)
y = y - 1;
else if (oldx > .5)
xx = xx - 1;
break;
default:
break;
}
x = xx;
if (((x * x) + (y * y)) >= 4.0)
break;
}
if (useloglog)
{
gdouble modulus_square = (x * x) + (y * y);
if (modulus_square > (G_E * G_E))
adjust = log (log (modulus_square) / 2.0) / log2;
else
adjust = 0.0;
}
else
{
adjust = 0.0;
}
color = (gint) (((counter - adjust) * (ncolors - 1)) / iteration);
(*dest_row)[0] = colormap[color].r;
(*dest_row)[1] = colormap[color].g;
(*dest_row)[2] = colormap[color].b;
(*dest_row) += 3;
}
}
static void
make_color_map (GeglChantO *o, clrmap colormap)
{
gint i;
gint r;
gint gr;
gint bl;
gdouble redstretch;
gdouble greenstretch;
gdouble bluestretch;
gdouble pi = atan (1) * 4;
redstretch = o->redstretch * 127.5;
greenstretch = o->greenstretch * 127.5;
bluestretch = o->bluestretch * 127.5;
for (i = 0; i < o->ncolors; i++)
{
double x = (i*2.0) / o->ncolors;
r = gr = bl = 0;
switch (o->redmode)
{
case SINUS:
r = (int) redstretch *(1.0 + sin((x - 1) * pi));
break;
case COSINUS:
r = (int) redstretch *(1.0 + cos((x - 1) * pi));
break;
case NONE:
r = (int)(redstretch *(x));
break;
default:
break;
}
switch (o->greenmode)
{
case SINUS:
gr = (int) greenstretch *(1.0 + sin((x - 1) * pi));
break;
case COSINUS:
gr = (int) greenstretch *(1.0 + cos((x - 1) * pi));
break;
case NONE:
gr = (int)(greenstretch *(x));
break;
default:
break;
}
switch (o->bluemode)
{
case SINUS:
bl = (int) bluestretch * (1.0 + sin ((x - 1) * pi));
break;
case COSINUS:
bl = (int) bluestretch * (1.0 + cos ((x - 1) * pi));
break;
case NONE:
bl = (int) (bluestretch * x);
break;
default:
break;
}
r = MIN (r, 255);
gr = MIN (gr, 255);
bl = MIN (bl, 255);
if (o->redinvert)
r = 255 - r;
if (o->greeninvert)
gr = 255 - gr;
if (o->blueinvert)
bl = 255 - bl;
colormap[i].r = r;
colormap[i].g = gr;
colormap[i].b = bl;
}
}
static void
prepare (GeglOperation *operation)
{
gegl_operation_set_format (operation, "output", babl_format ("R'G'B' u8"));
}
static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
GeglRectangle result = {0,0,0,0};
result.width = o->width;
result.height = o->height;
return result;
}
static gboolean
process (GeglOperation *operation,
GeglBuffer *output,
const GeglRectangle *result,
gint level)
{
GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
clrmap colormap;
guchar *buf;
gint pxsize;
make_color_map (o, colormap);
g_object_get (output, "px-size", &pxsize, NULL);
buf = g_new (guchar, result->width * result->height * pxsize);
{
guchar *dst=buf;
gint y;
for (y=0; y < result->height; y++)
{
explorer_render_row (o,
result->x,
result->x + result->width ,
result->y + y,
colormap,
&dst);
}
}
gegl_buffer_set (output, NULL, 0, babl_format ("R'G'B' u8"), buf,
GEGL_AUTO_ROWSTRIDE);
g_free (buf);
return TRUE;
}
static void
gegl_chant_class_init (GeglChantClass *klass)
{
GeglOperationClass *operation_class;
GeglOperationSourceClass *source_class;
operation_class = GEGL_OPERATION_CLASS (klass);
source_class = GEGL_OPERATION_SOURCE_CLASS (klass);
source_class->process = process;
operation_class->get_bounding_box = get_bounding_box;
operation_class->prepare = prepare;
gegl_operation_class_set_keys (operation_class,
"name" , "gegl:fractal-explorer",
"categories" , "render",
"description", _("Fractal Explorer"),
NULL);
operation_class->no_cache = TRUE;
operation_class->get_cached_region = NULL;
}
#endif