fractal-explorer.c 12.2 KB
Newer Older
Kevin Cozens's avatar
Kevin Cozens committed
1 2 3 4 5 6 7 8 9
/* 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
10
 * version 3 of the License, or (at your option) any later version.
Kevin Cozens's avatar
Kevin Cozens committed
11 12 13 14 15 16 17
 *
 * 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
18
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Kevin Cozens's avatar
Kevin Cozens committed
19 20 21 22
 *
 * Copyright 2006 Kevin Cozens <kcozens@cvs.gnome.org>
 */

23 24 25
#include "config.h"
#include <glib/gi18n-lib.h>

Kevin Cozens's avatar
Kevin Cozens committed
26 27
#define MAXNCOLORS 8192

28
#ifdef GEGL_CHANT_PROPERTIES
Kevin Cozens's avatar
Kevin Cozens committed
29

30 31
gegl_chant_int (width,  _("Width"),  10, 10000000, 400, _("Width"))
gegl_chant_int (height, _("Height"), 10, 10000000, 400, _("Height"))
32

33
gegl_chant_int (fractaltype, _("Fractal type"), 0, 8, 0, _("Fractal Type"))
34

35 36 37 38
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"))
39

40
gegl_chant_int (iter, _("Iterations"), 1, 1000, 50, _("Iterations"))
41

42 43
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)"))
44

45 46 47 48 49 50
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"))
51

52 53 54 55 56 57
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)"))
58

59 60 61 62 63 64
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"))
65

66 67
gegl_chant_int (ncolors, _("Colors"), 2, MAXNCOLORS, 256,
                _("Number of colors"))
68

69 70
gegl_chant_boolean (useloglog, _("Loglog smoothing"), FALSE,
                    _("Use loglog smoothing"))
Kevin Cozens's avatar
Kevin Cozens committed
71 72 73

#else

74
#define GEGL_CHANT_TYPE_SOURCE
75
#define GEGL_CHANT_C_FILE       "fractal-explorer.c"
Kevin Cozens's avatar
Kevin Cozens committed
76

77
#include "gegl-chant.h"
Kevin Cozens's avatar
Kevin Cozens committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
#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
103 104 105
{
  guchar r, g, b;
} gucharRGB;
Kevin Cozens's avatar
Kevin Cozens committed
106 107 108 109 110

typedef gucharRGB  clrmap[MAXNCOLORS];


static void
111 112 113 114 115 116
explorer_render_row (GeglChantO *o,
                     gint        col_start,
                     gint        col_end,
                     gint        row,
                     clrmap      colormap,
                     guchar    **dest_row)
Kevin Cozens's avatar
Kevin Cozens committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
  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;

149 150 151 152 153 154 155 156
  fractaltype = o->fractaltype;
  xmin = o->xmin;
  ymin = o->ymin;
  cx = o->cx;
  cy = o->cy;
  iteration = o->iter;
  ncolors = o->ncolors;
  useloglog = o->useloglog;
Kevin Cozens's avatar
Kevin Cozens committed
157 158
  log2 = log (2.0);

159 160
  xdiff = (o->xmax - xmin) / o->width;
  ydiff = (o->ymax - ymin) / o->height;
Kevin Cozens's avatar
Kevin Cozens committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

  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;
    }
}

315
static void
316
make_color_map (GeglChantO *o, clrmap colormap)
Kevin Cozens's avatar
Kevin Cozens committed
317 318 319 320 321 322 323 324 325 326
{
  gint     i;
  gint     r;
  gint     gr;
  gint     bl;
  gdouble  redstretch;
  gdouble  greenstretch;
  gdouble  bluestretch;
  gdouble  pi = atan (1) * 4;

327 328 329
  redstretch   = o->redstretch * 127.5;
  greenstretch = o->greenstretch * 127.5;
  bluestretch  = o->bluestretch * 127.5;
Kevin Cozens's avatar
Kevin Cozens committed
330

331
  for (i = 0; i < o->ncolors; i++)
Kevin Cozens's avatar
Kevin Cozens committed
332
    {
333
      double x = (i*2.0) / o->ncolors;
Kevin Cozens's avatar
Kevin Cozens committed
334 335
      r = gr = bl = 0;

336
      switch (o->redmode)
Kevin Cozens's avatar
Kevin Cozens committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350
        {
        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;
        }

351
      switch (o->greenmode)
Kevin Cozens's avatar
Kevin Cozens committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365
        {
        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;
        }

366
      switch (o->bluemode)
Kevin Cozens's avatar
Kevin Cozens committed
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
        {
        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);

385
      if (o->redinvert)
Kevin Cozens's avatar
Kevin Cozens committed
386 387
        r = 255 - r;

388
      if (o->greeninvert)
Kevin Cozens's avatar
Kevin Cozens committed
389 390
        gr = 255 - gr;

391
      if (o->blueinvert)
Kevin Cozens's avatar
Kevin Cozens committed
392 393 394 395 396 397 398 399
        bl = 255 - bl;

      colormap[i].r = r;
      colormap[i].g = gr;
      colormap[i].b = bl;
    }
}

400
static void
401
prepare (GeglOperation *operation)
402
{
403
  gegl_operation_set_format (operation, "output", babl_format ("R'G'B' u8"));
404 405
}

406
static GeglRectangle
407
get_bounding_box (GeglOperation *operation)
Kevin Cozens's avatar
Kevin Cozens committed
408
{
409 410
  GeglChantO    *o = GEGL_CHANT_PROPERTIES (operation);
  GeglRectangle  result = {0,0,0,0};
Kevin Cozens's avatar
Kevin Cozens committed
411

412 413
  result.width  = o->width;
  result.height = o->height;
Kevin Cozens's avatar
Kevin Cozens committed
414 415 416 417

  return result;
}

418 419 420
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *output,
421 422
         const GeglRectangle *result,
         gint                 level)
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
{
  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);
        }
    }

448
  gegl_buffer_set (output, NULL, 0, babl_format ("R'G'B' u8"), buf,
449 450 451 452 453 454 455 456
                   GEGL_AUTO_ROWSTRIDE);
  g_free (buf);

  return TRUE;
}


static void
457
gegl_chant_class_init (GeglChantClass *klass)
458
{
459 460 461 462 463 464 465
  GeglOperationClass       *operation_class;
  GeglOperationSourceClass *source_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);

  source_class->process = process;
466
  operation_class->get_bounding_box = get_bounding_box;
467 468
  operation_class->prepare = prepare;

469 470 471 472 473
  gegl_operation_class_set_keys (operation_class,
    "name"       , "gegl:fractal-explorer",
    "categories" , "render",
    "description", _("Fractal Explorer"),
    NULL);
474 475

  operation_class->no_cache = TRUE;
476
  operation_class->get_cached_region = NULL;
477 478
}

Kevin Cozens's avatar
Kevin Cozens committed
479
#endif