[gegl] ink-simulator: update engine of ink-simulator



commit c1f227943493a6f1ac3175523c8513ad09ce5505
Author: Øyvind Kolås <pippin gimp org>
Date:   Thu Apr 24 13:32:54 2014 +0200

    ink-simulator: update engine of ink-simulator
    
    The core spectral simulation has been split out into a tiny non-glib
    dependant core; the header and cfile for this core is embedded inline
    in the op source for now.
    
    Printer specifications now have an additional inkN.black spectral/color
    sample. This permits determining the scattering/opacity of the pigments in the
    ink, it is currently used as a wavelength dependent opacity similar to the
    previous; now deprecated o= global per ink opacity. Such on black substrate
    measurements are also the data that would be needed to drive kubelka munk
    based simulation model.

 operations/workshop/ink-simulator.c | 1529 +++++++++++++++++++++++++---------
 1 files changed, 1124 insertions(+), 405 deletions(-)
---
diff --git a/operations/workshop/ink-simulator.c b/operations/workshop/ink-simulator.c
index 4729ebc..06dce3e 100644
--- a/operations/workshop/ink-simulator.c
+++ b/operations/workshop/ink-simulator.c
@@ -18,30 +18,47 @@
 
 #include "config.h"
 #include <glib/gi18n-lib.h>
+#include <stdio.h>
+
+//#define USE_UI
 
 #ifdef GEGL_CHANT_PROPERTIES
 
 #define DEFAULT_CONFIG \
-"illuminant=D65\n" \
-"substrate=white\n" \
 "\n"\
-"ink1=cyan\n"\
-"ink2=magenta\n"\
-"ink3=yellow\n"\
-"ink4=black\n"\
+"substrate=white\n"\
+"black=rgb 0 0 0\n"\
+"ink1=black\n"\
+"ink2=red\n"\
+"ink2.black=black\n"\
 "\n"\
-"inklimit=3.0\n"
 
 gegl_chant_register_enum (ink_sim_mode)
-  enum_value (GEGL_INK_SIMULATOR_PROOF, "proof")
-  enum_value (GEGL_INK_SIMULATOR_SEPARATE, "separate")
-  enum_value (GEGL_INK_SIMULATOR_SEPARATE_PROOF, "separate-proof")
+  enum_value (GEGL_SSIM_PROOF, "proof")
+  enum_value (GEGL_SSIM_SEPARATE, "separate")
+  enum_value (GEGL_SSIM_SEPARATE_PROOF, "separate-proof")
 gegl_chant_register_enum_end (GeglInkSimMode)
 
 gegl_chant_multiline (config, _("ink configuration"), DEFAULT_CONFIG,
          _("Textual desciption of inks used for simulated print-job"))
 gegl_chant_enum (mode, _("mode"), GeglInkSimMode, ink_sim_mode,
-                 GEGL_INK_SIMULATOR_SEPARATE_PROOF, _("how the ink simulator is used"))
+                 GEGL_SSIM_SEPARATE_PROOF, _("how the ink simulator is used"))
+
+#ifdef USE_UI
+
+gegl_chant_color (substrate_color, _("Substrate color"), "#ffffff", _("paper/fabric/material color"))
+gegl_chant_color (ink1_color, _("Ink1 color"), "#00ffff00", _("ink color"))
+gegl_chant_color (ink2_color, _("Ink2 color"), "#ff00ff00", _("ink color"))
+gegl_chant_color (ink3_color, _("Ink3 color"), "#ffff0000", _("ink color"))
+gegl_chant_color (ink4_color, _("Ink4 color"), "#00000000", _("ink color"))
+gegl_chant_color (ink5_color, _("Ink5 color"), "#ff000000", _("ink color"))
+
+gegl_chant_double (ink_limit, _("Ink limit"), 0.0, 5.0, 3.0,
+                   _("maximum amount of ink for one pixel, 2.5 = 250% coverage"))
+
+#endif
+
+gegl_chant_int (debug_width, _("debug width"), 0, 150, 0, _("how wide peel off bands for ink order vis"))
 
 #else
 
@@ -51,118 +68,266 @@ gegl_chant_enum (mode, _("mode"), GeglInkSimMode, ink_sim_mode,
 #include "gegl-chant.h"
 #include <math.h>
 
-#define SPECTRUM_BANDS 20
-#define LUT_DIM        32
-#define LUT_ITERATIONS 256
-#define MAX_INKS       7
+/*********************/
+
+  /* the code is seld contained; but shared inside this gegl-op for now */
+
+
+#ifndef SSIM_H_
+#define SSIM_H_
+
+#include <stdint.h>
+
+typedef struct _SSim SSim;
+#define SSIM_MAX_INKS 16
+
+SSim *ssim_new              (const char *config);
+void  ssim_destroy          (SSim       *ssim);
+void  ssim_proof            (SSim       *ssim,
+                             float      *ink_levels,
+                             float      *rgba);
+void  ssim_separate         (SSim       *ssim,
+                             float      *rgb,
+                             float      *ink_levels);
+int   ssim_get_ink_count    (SSim       *ssim);
+/* this allows proofing with a lower amount of inks,
+ * without writing a full new config for doing that.
+ */
+void  ssim_set_ink_count    (SSim       *ssim,
+                             int         count);
+float ssim_get_ink_limit    (SSim *ssim);
+void  ssim_set_ink_limit    (SSim *ssim, float limit);
+void  ssim_set_halftone_sim (SSim *ssim,
+                             int   enabled);
+int   ssim_get_halftone_sim (SSim *ssim);
+
+//const Spectrum *ssim_get_spectrum (SSim *ssim, const char *name)
+//void ssim_set_spectrum (SSim *ssim, const char *name, Spectrum *spectrum);
+
+/* 1 0.0                1.0
+ * 2 0.0      0.50      1.0
+ * 3 0.0   0.33  0.66   1.0
+ * 4 0.0 0.25 0.50 0.75 1.0
+ */
+
+
+#endif
+
+/* Spectral Color Simulator
+ *
+ * ssim 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.
+ *
+ * ssim 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 ssim; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2014 Øyvind Kolås <pippin gimp org>
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* the kubelka munk method is more broken than the naive internal one */
+//#define KUBELKA_MUNK 1
+
+static const char *config_internal =
+
+"observer_l= 390 20 1 0.002177 0.0158711 0.03687185 0.06265 0.13059 0.26828 0.57836 0.849104 0.9704655 
0.9793335 0.85568175 0.59266275 0.30191075 0.11285 0.032197 0.007657 0.0017424 0.0004039 0.000009 0.000002\n"
+
+"observer_m= 390 20 1 0.00207 0.01863 0.057983 0.11151 0.221277 0.4010105 0.75920 0.97423 0.93118 0.694917 
0.37578 0.14093 0.03995 0.0096 0.002 0.0004 0.0001095 0.0000026 0\n"
+
+"observer_s= 390 20 1 0.05312 0.45811575 0.9226575 0.83515125 0.46085625 0.146032125 0.03805695 0.00723844 
0.0010894 0.000158823 0.0000025 0.000000336 0\n"
+
+"observer_l= 390 5 1 4.07619E-04 1.06921E-03 2.54073E-03 5.31546E-03 9.98835E-03 1.60130E-02 2.33957E-02 
3.09104E-02 3.97810E-02 4.94172E-02 5.94619E-02 6.86538E-02 7.95647E-02 9.07704E-02 1.06663E-01 1.28336E-01 
1.51651E-01 1.77116E-01 2.07940E-01 2.44046E-01 2.82752E-01 3.34786E-01 3.91705E-01 4.56252E-01 5.26538E-01 
5.99867E-01 6.75313E-01 7.37108E-01 7.88900E-01 8.37403E-01 8.90871E-01 9.26660E-01 9.44527E-01 9.70703E-01 
9.85636E-01 9.96979E-01 9.99543E-01 9.87057E-01 9.57841E-01 9.39781E-01 9.06693E-01 8.59605E-01 8.03173E-01 
7.40680E-01 6.68991E-01 5.93248E-01 5.17449E-01 4.45125E-01 3.69168E-01 3.00316E-01 2.42316E-01 1.93730E-01 
1.49509E-01 1.12638E-01 8.38077E-02 6.16384E-02 4.48132E-02 3.21660E-02 2.27738E-02 1.58939E-02 1.09123E-02 
7.59453E-03 5.28607E-03 3.66675E-03 2.51327E-03 1.72108E-03 1.18900E-03 8.22396E-04 5.72917E-04 3.99670E-04 
2.78553E-04 1.96528E-04 1.38482E-04 9.81226E-05 6.98827E-05 4.98430E-05 3.57781E-05 2.56411E-05 1.85766E-05 
1.34792E-05 9.80671E
 -06 7.14808E-06 5.24229E-06 3.86280E-06 2.84049E-06 2.10091E-06 1.56506E-06 1.16700E-06 8.73008E-07\n"
+
+"observer_m= 390 5 1 3.58227E-04 9.64828E-04 2.37208E-03 5.12316E-03 9.98841E-03 1.72596E-02 2.73163E-02 
3.96928E-02 5.55384E-02 7.50299E-02 9.57612E-02 1.16220E-01 1.39493E-01 1.62006E-01 1.93202E-01 2.32275E-01 
2.71441E-01 3.10372E-01 3.55066E-01 4.05688E-01 4.56137E-01 5.22970E-01 5.91003E-01 6.66404E-01 7.43612E-01 
8.16808E-01 8.89214E-01 9.34977E-01 9.61962E-01 9.81481E-01 9.98931E-01 9.91383E-01 9.61876E-01 9.35829E-01 
8.90949E-01 8.40969E-01 7.76526E-01 7.00013E-01 6.11728E-01 5.31825E-01 4.54142E-01 3.76527E-01 3.04378E-01 
2.39837E-01 1.85104E-01 1.40431E-01 1.04573E-01 7.65841E-02 5.54990E-02 3.97097E-02 2.80314E-02 1.94366E-02 
1.37660E-02 9.54315E-03 6.50455E-03 4.42794E-03 3.06050E-03 2.11596E-03 1.45798E-03 9.98424E-04 6.77653E-04 
4.67870E-04 3.25278E-04 2.25641E-04 1.55286E-04 1.07388E-04 7.49453E-05 5.24748E-05 3.70443E-05 2.62088E-05 
1.85965E-05 1.33965E-05 9.63397E-06 6.96522E-06 5.06711E-06 3.68617E-06 2.69504E-06 1.96864E-06 1.45518E-06 
1.07784E-06 8.00606E
 -07 5.96195E-07 4.48030E-07 3.38387E-07 2.54942E-07 1.93105E-07 1.47054E-07 1.11751E-07 8.48902E-08\n"
+
+"observer_s= 390 5 1 6.14265E-03 1.59515E-02 3.96308E-02 8.97612E-02 1.78530E-01 3.05941E-01 4.62692E-01 
6.09570E-01 7.56885E-01 8.69984E-01 9.66960E-01 9.93336E-01 9.91329E-01 9.06735E-01 8.23726E-01 7.37043E-01 
6.10456E-01 4.70894E-01 3.50108E-01 2.58497E-01 1.85297E-01 1.35351E-01 9.67990E-02 6.49614E-02 4.12337E-02 
2.71300E-02 1.76298E-02 1.13252E-02 7.17089E-03 4.54287E-03 2.83352E-03 1.75573E-03 1.08230E-03 6.64512E-04 
4.08931E-04 2.51918E-04 1.55688E-04 9.67045E-05 6.04705E-05 3.81202E-05 2.42549E-05 1.55924E-05 1.01356E-05 
6.66657E-06 4.43906E-06 2.99354E-06 0.0\n"
+
+"cyan= 390 20 1 0.8 0.8 0.9 0.95 0.95 0.95 0.9 0.9 0.82 0.75 0.6 0.55 0.5 0.4 0.3 0.22 0.3 0.37 0.57 0.72\n"
+"magenta= 390 20 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0\n"
+"yellow= 390 20 1 0.42 0.48 0.53 0.58 0.64 0.68 0.77 0.86 0.9  1 1 1 1 0 0 0 0 0 0 0\n"
+
+"black= 300 20 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \n"
+"blue=  390 20 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
+"green= 390 20 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0\n"
+"red =  390 20 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1\n"
+"white= 390 20 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n"
+
+"paper = 400 10 0.11 7.5404286 7.8298571 8.4084286 8.7702857 8.8400000 8.8737143 8.9232857 8.9704286 
9.0148571 9.0687143 9.1084286 9.1482857 9.1900000 9.2054286 9.2240000 9.2302857 9.2060000 9.2301429 9.2268571 
9.2401429 9.2400000 9.2524286 9.2640000 9.2732857 9.3015714 9.3287143 9.3427143 9.3327143 9.3237143 9.3204286 
9.3301429 # from jeremie gerhardt, assumed neutral substrate measurement \n"
+
+"cyan = 400 10 0.11 9.8113888 1.0670272 1.2382906 1.4463066 1.9420151 2.4855488 2.7483403 2.9397748 
2.9864173 2.8038144 2.4006503 1.8511818 1.2758176 8.0055737 4.8084866 2.7895001 1.6998021 1.2898931 1.2199036 
1.2299055 1.3098927 1.4198782 1.5898522 1.6498449 1.7198448 1.7798469 1.7498584 1.7598522 1.7398515 1.7498482 
1.8798300\n"
+
+"magenta = 400 10 0.11 1.2863557 1.2808865 1.2690493 1.2432869 1.1399922 1.0005523 8.3708221 6.7146438 
5.0772161 3.7486537 2.9692297 2.3795507 1.8397582 1.5498352 1.5698391 1.6698210 1.6798067 1.8997681 3.0094126 
6.9169947 1.5584424 2.7888956 3.9888990 4.8850636 5.4932014 5.9286392 6.2264940 6.3711174 6.3289353 6.2883122 
6.3226963 \n"
+
+"yellow = 400 10 0.11 3.4901877 3.4328886 3.3266590 2.5688609 2.3791376 2.3691969 2.4292326 2.6091947 
3.1389379 5.1374679 1.1957328 2.7395495 4.7471132 6.3588601 7.3552370 7.8975594 8.1510866 8.3363665 8.4318689 
8.5226294 8.5732716 8.6260495 8.6731905 8.7107605 8.7727353 8.8243792 8.8577589 8.8516172 8.8355047 8.8305203 
8.8606813 # from Jeremie Gerhardt \n"
+
+"dark_magenta = 400 10 0.11 1.5181483 1.5186113 1.5193041 1.4096572 1.3497226 1.2997584 1.2897838 1.2898034 
1.2398345 1.2298553 1.2098722 1.1898877 1.1599039 1.1399109 1.1099196 1.0799251 1.0499245 1.0399305 1.0199326 
1.0099363 9.9993749 1.0099384 1.0099403 1.0299396 1.0499422 1.0899426 1.1899345 1.3199169 1.4398983 1.5698778 
1.6698658 # from Jeremie Gerhardt \n"
+
+"blue = 400 10 0.11 1.2677560 1.3045020 1.3761595 1.4124810 1.3252730 1.1739987 9.9769206 8.2918111 
6.5253930 4.9276710 3.8886779 3.0892423 2.1896575 1.6398155 1.5998328 1.7298079 1.6798067 1.7797965 2.2596690 
2.9394595 3.2793269 3.2393654 3.2393856 3.2593942 3.4293824 3.7093343 4.0792291 4.2691287 4.0591902 3.7692949 
3.4694203 # from Jeremie Gerhardt \n"
+
+"light_magenta = 400 10 0.11 5.4856769 5.4619427 5.4111386 5.1254544 4.9862032 4.9564770 4.8769006 4.8372269 
4.8774315 4.6379375 4.1484949 3.4590498 2.5795246 2.0197201 2.0097362 2.2296807 2.2796439 2.5895690 4.2488281 
9.2046574 1.9255898 3.3553174 4.8132837 5.9475725 6.7066222 7.1778868 7.4608931 7.6137962 7.7141798 7.7933523 
7.8914000 # from Jeremie Gerhardt \n"
+
+"gray= 390 20 1 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5\n"
+"D65= 390 20 0.54648200 0.91486000 0.86682300 1.17008000 1.14861000 1.08811000 1.07802000 1.07689000 
1.04046000 0.96334200 0.88685600 0.89599100 0.83288600 0.80026800 0.82277800 0.69721300 0.74349000 0.69885600 
0.63592700 0.66805400\n"
+
+"D65= 300 5 1 0.000341 0.016643 0.032945 0.117652 0.20236 0.286447 0.370535 0.385011 0.399488 0.424302 
0.449117 0.45775 0.466383 0.493637 0.520891 0.510323 0.499755 0.523118 0.546482 0.687015 0.827549 0.871204 
0.91486 0.924589 0.934318 0.90057 0.866823 0.957736 1.04865 1.10936 1.17008 1.1741 1.17812 1.16336 1.14861 
1.15392 1.15923 1.12367 1.08811 1.09082 1.09.354 1.08578 1.07802 1.06296 1.0479 1.06239 1.07689 1.06047 
1.04405 1.04225 1.04046 1.02023 1.00 0.981671 0.963342 0.960611 0.95788 0.922368 0.886856 0.893459 0.900062 
0.898026 0.895991 0.886489 0.876987 0.854936 0.832886 0.834939 0.836992 0.81863 0.800268 0.801207 0.802146 
0.812462 0.822778 0.80281 0.782842 0.740027 0.697213 0.706652 0.716091 0.72979 0.74349 0.679765 0.61604 
0.657448 0.698856 0.724863 0.75087 0.693398 0.635927 0.550054 0.464182 0.566118 0.668054 0.650941 0.633828 
0.638434 0.64304 0.618779 0.594519 0.557054 0.51959 0.546998 0.574406 0.588765 0.603125\n"
+
+"D55= 390 20 1 0.3809 0.6855 0.6791 0.9799 0.9991 0.9808 1.0070 1.0421 1.0297 0.9722 0.9143 0.9514 0.9045 
0.8885 0.9395 0.7968 0.8484 0.7930 0.7188 0.7593\n"
+"D50= 390 20 1 0.4980 0.5850 0.5780 0.8720 0.9140 0.9200 0.9660 1.0210 1.0230 0.9770 0.9350 0.9930 0.9570 
0.9570 1.0300 0.8740 0.9290 0.8660 0.7820 0.8290\n"
+"E=white\n"
+
+"illuminant=D65\n"
+"substrate=paper\n"
+"inklimit=3.0\n"
+
+;
+
+#define SPECTRUM_BANDS   22
+#define SPECTRUM_GAP     13
+
+//#define SPECTRUM_BANDS   11
+//#define SPECTRUM_GAP     26
+
+#define SPECTRUM_START   400
+#define SPECTRUM_DB_SIZE 512
+#define LUT_DIM          48
+
+//static int STOCHASTIC_ITERATIONS = 384;
+//static float STOCHASTIC_DIFFUSION = 0.03;
+static int STOCHASTIC_ITERATIONS = 256;
+static float STOCHASTIC_DIFFUSION = 0.5;
+
+#ifndef CLAMP
+#define CLAMP(a,b,c) ((a)<(b)?(b):(a)>(c)?(c):(c))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#define INK_FLAG_DISABLED   0
+#define INK_FLAG_SEPARATE   1
+#define INK_FLAG_PROOF      2
+#define INK_FLAG_ENABLED   (INK_FLAG_SEPARATE | INK_FLAG_PROOF)
 
-typedef struct _Config Config;
 typedef struct _Ink Ink;
 typedef struct _Spectrum Spectrum;
 
+enum {
+  SSIM_COLOR_SUBSTRATE = -1,
+  SSIM_COLOR_ILLUMINANT = 0,
+  SSIM_COLOR_INK1 = 1,
+  SSIM_COLOR_INK2 = 2,
+  SSIM_COLOR_INK3 = 3,
+  SSIM_COLOR_INK4 = 4,
+  SSIM_COLOR_INK5 = 5,
+  SSIM_COLOR_INK6 = 6,
+  SSIM_COLOR_INK7 = 7,
+  SSIM_COLOR_INK8 = 8
+};
+
 struct _Spectrum {
-  gfloat bands[SPECTRUM_BANDS];
-  /* 20 bands, spaced as follows :
-   *
-   * 0 = 390 - 409nm
-   * 1 = 410 - 429nm
-   * ....
-   */
+  float bands[SPECTRUM_BANDS];
 };
 
 struct _Ink {
-  Spectrum transmittance; /* spectral energy of this ink; as reflected of a fully reflective background */
-  gfloat    opacity;  /* how much this ink covers up instead of mixes with background (substrate+inks) */
-  gfloat    scale;   /* scale factor; increasing the amount of spectral contribution */
+  Spectrum on_white; /* spectral energy of this ink; as reflected of a fully reflective background */
+  Spectrum on_black; /* spectral energy of this ink; as reflected of a fully reflective background */
+  Spectrum k;
+  Spectrum s;
+  float   scale;         /* scale factor; increasing the amount of spectral contribution */
 
-  /* per ink inklimit as well? */
+  float   dot_gain;
+  int32_t flags;
 };
 
 typedef struct _InkMix InkMix;
-struct _InkMix {
-  gint   defined;
-  gfloat level[MAX_INKS];
+
+struct _InkMix
+{
+  int32_t defined;
+  float   level[SSIM_MAX_INKS];
+};
+
+typedef struct _SpectrumDb SpectrumDb;
+
+struct _SpectrumDb
+{
+  Spectrum spectrum[SPECTRUM_DB_SIZE];
+  char     name[SPECTRUM_DB_SIZE][32];
+  int      count;
 };
 
-struct _Config {
+struct _SSim
+{
+  SpectrumDb db;
+
   Spectrum illuminant;
   Spectrum substrate;
-  Ink      ink_def[MAX_INKS];
-  gint     inks;
-  gfloat   ink_limit;
+  Spectrum mixtures[1 << SSIM_MAX_INKS];
+
+  Ink      ink_def[SSIM_MAX_INKS];
+  int32_t  inks;
+  float    ink_limit;
   InkMix   lut[LUT_DIM*LUT_DIM*LUT_DIM]; 
-  gint     debug_width;
-  gchar   *src;
+  int32_t  debug_width;
+  int32_t  halftone_sim;
+  char    *src; /* cached version of the source resulting in a configuration */
 };
 
+/* need a spectral resampler,.. 
+ *
+ * setting data structure directly, a
+ *
+ */
+
+const Spectrum *ssim_get_spectrum (SSim *ssim, const char *name);
 
-static inline gint
-lut_indice (gfloat  val,
-            gfloat *delta)
+static inline int lut_indice (float  val, float *delta)
 {
   /* LUT_DIM-1 to have both 0.0 and 1.0 values to interpolate from */
-  gint v = floor (val * (LUT_DIM - 1));
-  if (v < 0) v = 0; if (v >= (LUT_DIM-1)) v = LUT_DIM - 2;
-  if (delta)
-    {
-       *delta = ((val * (LUT_DIM - 1)) - v);
-    }
+  int v = floor (val * (LUT_DIM - 1));
+  if (v < 0)
+    v = 0;
+  if (v >= (LUT_DIM-1))
+    v = LUT_DIM - 2;
+  //if (delta)
+     *delta = ((val * (LUT_DIM - 1)) - v);
   return v;
 }
 
-static inline gint
-lut_index (gint ri,
-           gint gi,
-           gint bi)
+static inline int
+lut_index (int ri,
+           int gi,
+           int bi)
 {
   return ri * LUT_DIM * LUT_DIM + gi * LUT_DIM + bi;
 }
 
 /* from http://cvrl.ucl.ac.uk/cones.htm */
-static Spectrum STANDARD_OBSERVER_L   = {{
-0.002177, 0.0158711, 0.03687185, 0.06265, 0.13059, 0.26828, 0.57836, 0.849104, 0.9704655, 0.9793335, 
0.85568175, 0.59266275, 0.30191075, 0.11285, 0.032197, 0.007657, 0.0017424, 0.0004039, 0.000009, 0.000002
-}};
-static Spectrum STANDARD_OBSERVER_M   = {{
-0.00207, 0.01863, 0.057983, 0.11151, 0.221277, 0.4010105, 0.75920, 0.97423, 0.93118, 0.694917, 0.37578, 
0.14093, 0.03995, 0.0096, 0.002, 0.0004, 0.0001095,
-  0.0000026, 0
-}};
-static Spectrum STANDARD_OBSERVER_S   = {{
-  0.05312, 0.45811575, 0.9226575, 0.83515125, 0.46085625, 0.146032125, 0.03805695, 0.00723844, 0.0010894, 
0.000158823, 0.0000025, 0.000000336, 0
-}};
-
-#define TRANSMITTANCE_CYAN    
{{0.8,0.8,0.9,0.95,0.95,0.95,0.9,0.9,0.82,0.75,0.6,0.55,0.5,0.4,0.3,0.22,0.3,0.37,0.57,0.72}}
-#define TRANSMITTANCE_MAGENTA {{1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0}}
-#define TRANSMITTANCE_YELLOW  {{0.42,0.48,0.53,0.58,0.64,0.68,0.77,0.86,0.9, 1,1,1,1,0,0,0,0,0,0,0}}
-
-// this transmittance looks more blue than magenta:
-//#define TRANSMITTANCE_MAGENTA 
{{1.0,0.7,0.9,0.85,0.8,0.7,0.64,0.54,0.5,0.38,0.6,0.8,0.9,1.0,1.0,1.0,1.0,0.9,0.9,0.9}}
-
-#define TRANSMITTANCE_RED     {{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0}}
-#define TRANSMITTANCE_GREEN   {{0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}}
-#define TRANSMITTANCE_BLUE    {{0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
-#define TRANSMITTANCE_BLACK   {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
-#define TRANSMITTANCE_GRAY    
{{0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5}}
-#define TRANSMITTANCE_WHITE   {{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}}
-
-/* the even illuminant; which is easy and almost cheating */
-#define ILLUMINANT_E         TRANSMITTANCE_WHITE
-
-#define ILLUMINANT_D65 {{\
-0.54648200, 0.91486000, 0.86682300, 1.17008000, 1.14861000, 1.08811000, 1.07802000, 1.07689000, 1.04046000, 
0.96334200, 0.88685600, 0.89599100, 0.83288600, 0.80026800, 0.82277800, 0.69721300, 0.74349000, 0.69885600, 
0.63592700, 0.66805400 }} 
-
-#define ILLUMINANT_D55 {{\
-0.3809, 0.6855, 0.6791, 0.9799, 0.9991, 0.9808, 1.0070, 1.0421, 1.0297, 0.9722, 0.9143, 0.9514, 0.9045, 
0.8885, 0.9395, 0.7968, 0.8484, 0.7930, 0.7188, 0.7593 }}
-
-#define ILLUMINANT_D50 {{\
-0.4980, 0.5850, 0.5780, 0.8720, 0.9140, 0.9200, 0.9660, 1.0210, 1.0230, 0.9770, 0.9350, 0.9930, 0.9570, 
0.9570, 1.0300, 0.8740, 0.9290, 0.8660, 0.7820, 0.8290 }}
+static Spectrum STANDARD_OBSERVER_L;
+static Spectrum STANDARD_OBSERVER_M;
+static Spectrum STANDARD_OBSERVER_S;
 
 
 static inline void
 spectrum_remove_light (Spectrum       *s,
                        const Spectrum *ink,
-                       gfloat          coverage)
+                       float          coverage)
 {
-  gint i;
+  int i;
   for (i = 0; i < SPECTRUM_BANDS; i++)
     s->bands[i] = s->bands[i] * (1.0 - coverage) +
                   s->bands[i] * ink->bands[i] * coverage;
@@ -172,9 +337,9 @@ static inline void
 spectrum_add_opaque_ink (Spectrum       *s,
                          const Spectrum *ink,
                          const Spectrum *illuminant,
-                         gfloat          coverage)
+                         float          coverage)
 {
-  gint i;
+  int i;
   for (i = 0; i < SPECTRUM_BANDS; i++)
     s->bands[i] = s->bands[i] * (1.0 - coverage) +
                   ink->bands[i] * illuminant->bands[i] * coverage;
@@ -184,106 +349,361 @@ static inline void
 spectrum_lerp (Spectrum       *s,
                const Spectrum *a,
                const Spectrum *b,
-               gfloat          delta)
+               float          delta)
 {
-  gint i;
+  int i;
   for (i = 0; i < SPECTRUM_BANDS; i++)
     s->bands[i] = a->bands[i] * (1.0 - delta) + b->bands[i] * delta;
 }
 
 static inline void
-add_ink (Spectrum       *s,
-         const Spectrum *illuminant,
-         const Spectrum *ink,
-         gfloat          coverage,
-         gfloat          opacity)
+spectrum_lerp2 (Spectrum       *s,
+                const Spectrum *a,
+                const Spectrum *b,
+                const Spectrum *delta)
 {
-  if (fabs (opacity - 1.0) < 0.01)
-  {
-    spectrum_add_opaque_ink (s, ink, illuminant, coverage);
-  }
-  else if (fabs (opacity) < 0.01)
-  {
-    spectrum_remove_light (s, ink, coverage);
-  }
-  else
-  {
-    Spectrum opaque = *s;
-    spectrum_add_opaque_ink (&opaque, ink, illuminant, coverage);
-    spectrum_remove_light (s, ink, coverage);
-  
-    /* linear interpolation of the two simpler cases */
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    s->bands[i] = a->bands[i] * (1.0 - delta->bands[i]) + b->bands[i] * delta->bands[i];
+}
 
-    spectrum_lerp (s, s, &opaque, opacity);
-  }
+static inline void
+spectrum_add (Spectrum       *s,
+              const Spectrum *a,
+              const Spectrum *b)
+{
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    s->bands[i] = a->bands[i] + b->bands[i];
 }
 
-static gfloat spectrum_integrate (const Spectrum *s,
-                                  const Spectrum *is,
-                                  gfloat          scale)
+static inline void
+spectrum_add_factor (Spectrum       *s,
+                     const Spectrum *a,
+                     const Spectrum *b,
+                     float          factor)
 {
-  gfloat result = 0.0;
-  gint i;
+  int i;
   for (i = 0; i < SPECTRUM_BANDS; i++)
-    result += s->bands[i] * is->bands[i];
-  return (result / SPECTRUM_BANDS) * scale;
+    s->bands[i] = a->bands[i] + b->bands[i] * factor;
 }
 
-static void parse_config (GeglOperation *operation);
+static inline void
+spectrum_set (Spectrum       *s,
+              const Spectrum *a)
+{
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    s->bands[i] = a->bands[i];
+}
 
-static void
-prepare (GeglOperation *operation)
+
+static inline void
+add_ink (Spectrum       *s,
+         const Spectrum *illuminant,
+         const Spectrum *ink,
+         const Spectrum *opaqueness,
+         float          coverage,
+         float          dot_gain)
 {
-  /* this RGBA float is interpreted as CMYK ! */
-  gegl_operation_set_format (operation, "input", babl_format ("RGBA float"));
-  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+  Spectrum opaque = *s;
+  coverage = pow (coverage, dot_gain);
+
+  /* XXX: could have shortcuts for non-opaque/fully opaque inks */
+  spectrum_add_opaque_ink (&opaque, ink, illuminant, coverage);
+  /* additive mixing */
+
+  spectrum_remove_light (s, ink, coverage);
+  /* subtractive mixing  */
+  
+  /* linear interpolation between additive and subtractive mixing;
+   * this simplification is a gap in reality; how it compares to
+   * actual prints remains to be seen.
+   */
+  //spectrum_lerp (s, s, &opaque, opacity);
+  spectrum_lerp2 (s, s, &opaque, opaqueness);
+}
 
-  parse_config (operation);
+static float spectrum_integrate (const Spectrum *s,
+                                 const Spectrum *is,
+                                 float           scale)
+{
+  float result = 0.0;
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    result += s->bands[i] * is->bands[i];
+  return (result / SPECTRUM_BANDS) * scale;
 }
 
 static inline void
 spectrum_to_rgba (const Spectrum *spec,
-                  gfloat         *rgba)
+                  float         *rgba)
 {
-  gfloat l, m, s;
-  l = spectrum_integrate (spec, &STANDARD_OBSERVER_L, 0.5);
-  m = spectrum_integrate (spec, &STANDARD_OBSERVER_M, 0.5);
-  s = spectrum_integrate (spec, &STANDARD_OBSERVER_S, 0.5);
-  /* this LMS to linear RGB -- likely not right..  */
+  float l, m, s;
+  l = spectrum_integrate (spec, &STANDARD_OBSERVER_L, 0.62);
+  m = spectrum_integrate (spec, &STANDARD_OBSERVER_M, 0.59);
+  s = spectrum_integrate (spec, &STANDARD_OBSERVER_S, 0.66);
+  /* this LMS to linear RGB -- likely not quite right..  */
   rgba[0] = ((30.83) * l + (-29.83) * m + (1.61) * s);
   rgba[1] = ((-6.481) * l + (17.7155) * m + (-2.532) * s);
   rgba[2] = ((-0.375) * l + (-1.19906) * m + (14.27) * s);
   rgba[3] = 1.0;
 }
 
+#ifdef KUBELKA_MUNK
+
+static float inv_coth (float x)
+{
+  return 0.5 * log ( ( x  + 1 ) / (x-1));
+}
+
+inline static void ssim_compute_ks (float on_white,
+                                   float on_black,
+                                   float *k,
+                                   float *s)
+{
+  float a, b;
+  if (on_black <= 0.0)
+    on_black = 0.0001;
+  if (on_white <= on_black)
+    on_white = on_black + 0.001;
+  if (on_white >= 1.0)
+  {
+    on_white = 0.9999;
+  }
+  if (on_black >= on_white)
+  {
+    on_black = on_white - 0.0001;
+  }
+  a= 0.5 * (on_white + (on_black - on_white + 1) / (on_black));
+  b = sqrt (a * a - 1);
+  *s = 1/b * inv_coth ((b * b - (a - on_white) * (a - 1)) / (b * (1 - on_white)));
+  *k = *s * (a - 1);
+}
+
+static inline float ssim_ks_to_reflectance (float k, float s)
+{
+  float ks = (k/s);
+  return sqrt(1 + (ks) - sqrt((ks)*(ks)+2*ks));
+}
+
+#endif
+
+static inline void ssim_compute_opacity (float white, float black,
+                                         float *k, float *s)
+{
+  //black = 0 * (1-opacity) + white * opacity;
+  //black = white * opacity;
+  if (white == 0.0)
+    white = 0.001;
+  //opacity = black / white;
+  *k = black / white;
+}
+
+/* compute kubelka munks constants
+ */
+static void color_recompute (SSim *ssim, Ink *ink)
+{
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    {
+      float white = ink->on_white.bands[i];
+      float black = ink->on_black.bands[i];
+      float s, k;
+
+
+#ifdef KUBELKA_MUNK
+      ssim_compute_ks (white, black, &k, &s);
+#else
+      ssim_compute_opacity (white, black, &k, &s);
+      s = 0;
+#endif
+
+      ink->k.bands[i] = k;
+      ink->s.bands[i] = s;
+    }
+}
+
 static inline Spectrum
-inks_to_spectrum (Config *config,
-                  gfloat *ink_levels)
-{
-  gint i;
-  Spectrum spec = config->illuminant;
-  spectrum_remove_light (&spec, &config->substrate, 1.0);
-
-  for (i = 0; i < config->inks; i++) 
-    add_ink (&spec, &config->illuminant,
-                    &config->ink_def[i].transmittance,
-                    ink_levels[i] * config->ink_def[i].scale,
-                    config->ink_def[i].opacity);
+inks_to_spectrum_continous (SSim  *ssim,
+                            float *ink_levels)
+{
+  int i;
+  Spectrum spec = ssim->illuminant;
+#ifdef KUBELKA_MUNK
+  Spectrum k, s;
+  int b;
+/*
+  for (b = 0; b < SPECTRUM_BANDS; b++)
+  {
+    float k,s;
+    k = 0.001;//001027;// * 0.01;
+    s = 0.01;//0.009110460; // * 0.01;
+
+    for (i = 0; i < ssim->inks; i++) 
+      if (ink_levels[i] > 0.001)
+      {
+        k += ssim->ink_def[i].k.bands[b] *
+           ink_levels[i] * ssim->ink_def[i].scale;
+        s += ssim->ink_def[i].s.bands[b] *
+           ink_levels[i] * ssim->ink_def[i].scale;
+      }
+    spec.bands[b] = ssim_ks_to_reflectance (k, s) * ssim->illuminant.bands[b];
+  }
+*/
+  for (b = 0; b < SPECTRUM_BANDS; b++)
+  {
+    k.bands[b] = 0.001;
+    s.bands[b] = 0.01;
+  }
+  for (i = 0; i < ssim->inks; i++) 
+  {
+    float level = ink_levels[i] * ssim->ink_def[i].scale;
+
+    if (level > 0.00001)
+    {
+      level = pow (level, ssim->ink_def[i].dot_gain);
+      for (b = 0; b < SPECTRUM_BANDS; b++)
+      {
+        k.bands[b] += ssim->ink_def[i].k.bands[b] * level;
+        s.bands[b] += ssim->ink_def[i].s.bands[b] * level;
+      }
+    }
+  }
+  for (b = 0; b < SPECTRUM_BANDS; b++)
+    spec.bands[b] = ssim_ks_to_reflectance (k.bands[b], s.bands[b]) * ssim->illuminant.bands[b];
+#else
+  spectrum_remove_light (&spec, &ssim->substrate, 1.0);
+
+  for (i = 0; i < ssim->inks; i++) 
+    add_ink (&spec, &ssim->illuminant,
+                    &ssim->ink_def[i].on_white,
+                    &ssim->ink_def[i].k,
+                    ink_levels[i] * ssim->ink_def[i].scale,
+                    ssim->ink_def[i].dot_gain);
+
+#endif
   return spec;
 }
 
-static inline void
-spectral_proof (Config *config,
-                gfloat *ink_levels,
-                gfloat *rgba)
+/* Compute coverages:
+ *
+ * This implements the average amount of each combination of binary
+ * coverage combinations of inks.
+ *
+ * 2: inks
+ *
+ * ab_coverage = ink_a * ink_b
+ * a_coverage  = ink_a - ab_coverage
+ * b_coverage  = ink_b - ab_coverage
+ * 0_coverage  = 1 - full_coverage - a_coverage - b_coverage;
+ *
+ * 3: inks
+ *
+ * abc_coverage  = ink_a * ink_b * ink_c
+ * ab_coverage   = ink_a * ink_b - abc_coverage;
+ * ac_coverage   = ink_a * ink_c - abc_coverage;
+ * bc_coverage   = ink_b * ink_c - abc_coverage;
+ * a_coverage    = ink_a - abc_coverage - ab_coverage - ac_coverage;
+ * b_coverage    = ink_b - abc_coverage - ab_coverage - bc_coverage;
+ * c_coverage    = ink_c - abc_coverage - ac_coverage - bc_coverage;
+ * 0_coverage    = 1 - full_coverage - 
+ *                     a_coverage - b_coverage - c_coverage -
+ *                     ab_coverage - ac_coverage - bc_coverage;
+ *
+ *
+ * 4: inks
+ *
+ */
+
+#define BITSET(var,pos) ((var) & (1<<(pos)))
+static inline void compute_coverages (int          inks,
+                                      const float *ink_levels,
+                                      float       *coverages)
+{
+  int i;
+  float tsum = 0;
+  for (i = (1 << inks)-1; i > 0; i--)
+  {
+    int j;
+    float sum = 0;
+    for (j = 0; j < inks; j++)
+    {
+      if (BITSET (i, j))
+      {
+        if (sum == 0)
+          sum = ink_levels[j];
+        else
+          sum *= ink_levels[j];
+      }
+    }
+    for (j = (1 <<inks)-1; j > i; j --)
+    {
+      if (i &&  (  (j & i) == i))
+        sum -= coverages[j];
+    }
+    coverages[i] = sum;
+    tsum += sum;
+  }
+  coverages[0] = 1.0 - tsum;
+}
+
+static void compute_neugebauer_primaries (SSim *ssim)
+{
+  int i;
+  for (i = (1 << ssim->inks)-1; i >= 0; i--)
+  {
+    float ink_levels[SSIM_MAX_INKS];
+    int j;
+    for (j = 0; j < ssim->inks; j++)
+    {
+      if (BITSET (i, j))
+        ink_levels[j] = 1.0;
+      else
+        ink_levels[j] = 0.0;
+    }
+    {
+    Spectrum spec = inks_to_spectrum_continous (ssim, ink_levels);
+    spectrum_set (&ssim->mixtures[i], &spec);
+    }
+  }
+}
+
+
+static inline Spectrum
+inks_to_spectrum (SSim *ssim,
+                  float    *ink_levels)
 {
-  Spectrum spec = inks_to_spectrum (config, ink_levels);
+  if (ssim->halftone_sim)
+  {
+    int i;
+    Spectrum sum = {{0,}};
+    float   coverages[1 << ssim->inks];
+    compute_coverages (ssim->inks, ink_levels, coverages);
+
+    for (i = 0; i < (1 << ssim->inks); i++)
+    {
+      spectrum_add_factor (&sum, &sum, &ssim->mixtures[i], coverages[i]);
+    }
+    return sum;
+  }
+  return inks_to_spectrum_continous (ssim, ink_levels);
+}
+
+void
+ssim_proof (SSim *ssim,
+            float *ink_levels,
+            float *rgba)
+{
+  Spectrum spec = inks_to_spectrum (ssim, ink_levels);
   spectrum_to_rgba (&spec, rgba);
+
+  /* XXX: todo : try to add LUT here as well */
 }
 
-static inline gfloat
-colordiff (gfloat *rgb_a,
-           gfloat *rgb_b)
+static inline float
+colordiff (float *rgb_a,
+           float *rgb_b)
 {
   return 
     (rgb_a[0]-rgb_b[0])*(rgb_a[0]-rgb_b[0])+
@@ -292,99 +712,106 @@ colordiff (gfloat *rgb_a,
 }
 
 static inline void
-rgb_to_inks_stochastic (Config *config,
-                        gfloat  *rgb,
-                        gfloat  *ink_levels,
-                        gint    iterations,
-                        gfloat  rrange)
-{
-  gfloat best[MAX_INKS] = {};
-  gfloat bestdiff = 1000.0;
-  gint i;
-  for (i = 0; i < config->inks; i++)
-    best[i] = 0.0; /* XXX: maybe 0.0 is better to limit inks,
-                           and metamers? */
+ssim_separate_stochastic (SSim   *ssim,
+                          float  *rgb,
+                          float  *ink_levels,
+                          int     iterations,
+                          float   rrange)
+{
+  float best[SSIM_MAX_INKS] = {};
+  float bestdiff = 1000.0;
+  int i;
+
+  for (i = 0; i < ssim->inks; i++)
+    best[i] = ink_levels[i];  /* start with no inks; seek towards right color/spectra
+                     * by adding - not removing
+                     *
+                     * starting with a known good neighbour - if available
+                     * from the LUT; should improve uniformity an decrease
+                     * warp in LUT.
+                     */
 
   for (i = 0; i < iterations; i++)
   {
-    gint j;
-    gfloat attempt[MAX_INKS];
-    gfloat softrgb[4];
-    gfloat diff;
-    gfloat inksum = 0;
+    int j;
+    float attempt[SSIM_MAX_INKS];
+    float softrgb[4];
+    float diff;
+    float inksum = 0;
 
     do {
       inksum = 0.0;
-      for (j = 0; j < config->inks; j++)
+      for (j = 0; j < ssim->inks; j++)
       {
-        attempt[j] = best[j] + g_random_double_range(-rrange, rrange);
+        attempt[j] = best[j] + ((random()%10000)/5000.0-1.0) * rrange;
         attempt[j] = CLAMP(attempt[j],0,1);
         inksum += attempt[j];
       }
-    } while (inksum > config->ink_limit);
+    } while (inksum > ssim->ink_limit);
 
-    spectral_proof (config, attempt, softrgb);
+    ssim_proof (ssim, attempt, softrgb);
     diff = colordiff (rgb, softrgb) + (inksum / 50.0) * (inksum / 50.0);
     if (diff < bestdiff)
     {
-      rrange *= 0.99;
       bestdiff = diff;
-      for (j = 0; j < config->inks; j++)
+      for (j = 0; j < ssim->inks; j++)
         best[j] = attempt[j];
       if (diff < 0.0001) /* close enough */
         break;
     }
   }
 
-  for (i = 0; i < config->inks; i++)
+  for (i = 0; i < ssim->inks; i++)
     ink_levels[i] = best[i];
 }
 
-static inline gfloat *
-ensure_lut (Config *config,
-            gint    ri,
-            gint    gi,
-            gint    bi)
+static inline float *
+ensure_lut (SSim *ssim,
+            int    ri,
+            int    gi,
+            int    bi)
 {
-  gint l_index;
+  int l_index;
   l_index = lut_index (ri, gi, bi);
-  if (!config->lut[l_index].defined)
+  if (!ssim->lut[l_index].defined)
   {
-    gfloat trgb[3] = {(float)ri / LUT_DIM,
+    float trgb[3] = {(float)ri / LUT_DIM,
                       (float)gi / LUT_DIM,
                       (float)bi / LUT_DIM };
 
-    rgb_to_inks_stochastic (config, trgb, &config->lut[l_index].level[0], LUT_ITERATIONS, 0.6);
-    config->lut[l_index].defined = 1;
+    /* feed in relevant neighbour if set */
+    ssim_separate_stochastic (ssim, trgb, &ssim->lut[l_index].level[0], STOCHASTIC_ITERATIONS, 
STOCHASTIC_DIFFUSION);
+    ssim->lut[l_index].defined = 1;
   }
-  return &config->lut[l_index].level[0];
+  return &ssim->lut[l_index].level[0];
 }
 
 static inline void
-lerp_inks (gint          inks,
-           gfloat       *ink_res,
-           const gfloat *inka,
-           const gfloat *inkb,
-           gfloat        delta)
+lerp_inks (int          inks,
+           float       *ink_res,
+           const float *inka,
+           const float *inkb,
+           float        delta)
 {
-  gint i;
+  int i;
   for (i = 0; i < inks; i++)
     ink_res[i] = inka[i]  * (1.0 - delta) + inkb[i] * delta;
 }
 
-static inline void rgb_to_inks (Config *config,
-                                gfloat *rgb,
-                                gfloat *ink_levels)
+
+void ssim_separate (SSim  *ssim,
+                    float *rgb,
+                    float *ink_levels)
 {
-  gfloat rdelta, gdelta, bdelta;
-  gint ri = lut_indice (rgb[0], &rdelta);
-  gint gi = lut_indice (rgb[1], &gdelta);
-  gint bi = lut_indice (rgb[2], &bdelta);
-  gfloat *ink_corner[8];
-  gfloat  temp1[MAX_INKS];
-  gfloat  temp2[MAX_INKS];
-  gfloat  temp3[MAX_INKS];
-  gfloat  temp4[MAX_INKS];
+  float rdelta, gdelta, bdelta;
+  int ri = lut_indice (rgb[0], &rdelta);
+  int gi = lut_indice (rgb[1], &gdelta);
+  int bi = lut_indice (rgb[2], &bdelta);
+  float *ink_corner[8];
+  float  temp1[SSIM_MAX_INKS];
+  float  temp2[SSIM_MAX_INKS];
+  float  temp3[SSIM_MAX_INKS];
+  float  temp4[SSIM_MAX_INKS];
 
 /* numbering of corners, and positions of R,G,B axes
       6
@@ -398,23 +825,462 @@ static inline void rgb_to_inks (Config *config,
      \|/R
       0       */
 
-  ink_corner[0] = ensure_lut (config, ri + 0, gi + 0, bi + 0);
-  ink_corner[1] = ensure_lut (config, ri + 1, gi + 0, bi + 0);
-  ink_corner[2] = ensure_lut (config, ri + 1, gi + 0, bi + 1);
-  ink_corner[3] = ensure_lut (config, ri + 0, gi + 0, bi + 1);
+  ink_corner[0] = ensure_lut (ssim, ri + 0, gi + 0, bi + 0);
+  ink_corner[1] = ensure_lut (ssim, ri + 1, gi + 0, bi + 0);
+  ink_corner[2] = ensure_lut (ssim, ri + 1, gi + 0, bi + 1);
+  ink_corner[3] = ensure_lut (ssim, ri + 0, gi + 0, bi + 1);
+
+  ink_corner[4] = ensure_lut (ssim, ri + 0, gi + 1, bi + 0);
+  ink_corner[5] = ensure_lut (ssim, ri + 1, gi + 1, bi + 0);
+  ink_corner[6] = ensure_lut (ssim, ri + 1, gi + 1, bi + 1);
+  ink_corner[7] = ensure_lut (ssim, ri + 0, gi + 1, bi + 1);
+
+  lerp_inks (ssim->inks, temp1, ink_corner[0], ink_corner[1], rdelta);
+  lerp_inks (ssim->inks, temp2, ink_corner[3], ink_corner[2], rdelta);
+  lerp_inks (ssim->inks, temp3, ink_corner[4], ink_corner[5], rdelta);
+  lerp_inks (ssim->inks, temp4, ink_corner[7], ink_corner[6], rdelta);
+  lerp_inks (ssim->inks, temp1, temp1, temp3, gdelta);
+  lerp_inks (ssim->inks, temp2, temp2, temp4, gdelta);
+  lerp_inks (ssim->inks, ink_levels, temp1, temp2, bdelta);
+}
+
+#if 0
+static int str_has_prefix (const char *str, const char *prefix)
+{
+  int i;
+  for (i = 0; prefix[i] && str[i]; i++)
+  {
+    if (prefix[i] != str[i])
+      return 0;
+  }
+  if (prefix[i] == 0)
+    return 1;
+  return 0;
+}
+#endif
 
-  ink_corner[4] = ensure_lut (config, ri + 0, gi + 1, bi + 0);
-  ink_corner[5] = ensure_lut (config, ri + 1, gi + 1, bi + 0);
-  ink_corner[6] = ensure_lut (config, ri + 1, gi + 1, bi + 1);
-  ink_corner[7] = ensure_lut (config, ri + 0, gi + 1, bi + 1);
+static Spectrum parse_spectrum (SSim *ssim, char *spectrum)
+{
+  Spectrum s;
+  char key[32];
+  const Spectrum *tmp;
+  int i;
+  for (i = 0; i < SPECTRUM_BANDS; i++)
+    s.bands[i] = 1;
+  
+  if (!spectrum)
+    return s;
+  while (*spectrum == ' ') spectrum ++;
+  for (i = 0; spectrum[i] && spectrum[i]!=' '; i++)
+    key[i] = spectrum[i];
+  key[i]=0;
 
-  lerp_inks (config->inks, temp1, ink_corner[0], ink_corner[1], rdelta);
-  lerp_inks (config->inks, temp2, ink_corner[3], ink_corner[2], rdelta);
-  lerp_inks (config->inks, temp3, ink_corner[4], ink_corner[5], rdelta);
-  lerp_inks (config->inks, temp4, ink_corner[7], ink_corner[6], rdelta);
-  lerp_inks (config->inks, temp1, temp1, temp3, gdelta);
-  lerp_inks (config->inks, temp2, temp2, temp4, gdelta);
-  lerp_inks (config->inks, ink_levels, temp1, temp2, bdelta);
+  if (!strcmp (key, "rgb"))
+  {
+    Spectrum red   = *ssim_get_spectrum (ssim, "red");
+    Spectrum green = *ssim_get_spectrum (ssim, "green");
+    Spectrum blue  = *ssim_get_spectrum (ssim, "blue");
+    int i;
+    float r = 0, g = 0, b = 0;
+    char *p = spectrum + 3;
+
+    r = strtod (p, &p);
+    if (p) g = strtod (p, &p);
+    if (p) b = strtod (p, &p);
+
+    for (i = 0; i<SPECTRUM_BANDS; i++)
+      s.bands[i] = red.bands[i] * r + green.bands[i] * g + blue.bands[i] * b;
+  }
+
+  tmp = ssim_get_spectrum (ssim, key);
+  if (tmp)
+  {
+    s = *tmp;
+  } 
+  else
+  {
+    float num_array [100];
+    int band;
+    band = 0;
+    do {
+      num_array[band++] = strtod (spectrum, &spectrum);
+    } while (spectrum && band <= 100);
+
+    if (band > 3)
+    {
+      float nm_start = num_array[0];
+      float nm_gap = num_array[1];
+      float nm_scale = num_array[2];
+
+      int i;
+      int j;
+      float nm = nm_start;
+      for (i = 3; i < band; i++)
+      {
+        j = (int) ( (nm - SPECTRUM_START) / SPECTRUM_GAP);
+        if (j >=0 && j < SPECTRUM_BANDS)
+          {
+            int k;
+            for (k = j; k < SPECTRUM_BANDS; k++)
+            s.bands[k] = num_array[i] * nm_scale;
+          }
+        nm += nm_gap;
+      }
+
+      j = (int) ( (nm - SPECTRUM_START) / SPECTRUM_GAP);
+      if (j >=0 && j < SPECTRUM_BANDS)
+       {
+         int k;
+         for (k = j; k < SPECTRUM_BANDS; k++)
+          s.bands[k] = 0.0;
+       }
+    }
+  }
+
+  return s;
+}
+
+const Spectrum *ssim_get_spectrum (SSim *ssim, const char *name)
+{
+  int i;
+  if (!strcmp (name, "illuminant")) return &ssim->illuminant;
+  if (!strcmp (name, "substrate")) return &ssim->substrate;
+  if (!strcmp (name, "observer_l")) { return &STANDARD_OBSERVER_L; }
+  if (!strcmp (name, "observer_m")) { return &STANDARD_OBSERVER_M; }
+  if (!strcmp (name, "observer_s")) { return &STANDARD_OBSERVER_S; }
+
+  for (i = 0; i < ssim->db.count; i++)
+  {
+    if (!strcmp (name, ssim->db.name[i]))
+    {
+      return &ssim->db.spectrum[i];
+    }
+  }
+  return NULL;
+}
+
+void ssim_set_spectrum (SSim *ssim, const char *name, Spectrum *spectrum);
+void ssim_set_spectrum (SSim *ssim, const char *name, Spectrum *spectrum)
+{
+  int i;
+
+  if (!strcmp (name, "illuminant")) { ssim->illuminant = *spectrum; return; }
+  if (!strcmp (name, "substrate"))  { ssim->substrate = *spectrum; return; }
+  if (!strcmp (name, "observer_l")) { STANDARD_OBSERVER_L = *spectrum; return; }
+  if (!strcmp (name, "observer_m")) { STANDARD_OBSERVER_M = *spectrum; return; }
+  if (!strcmp (name, "observer_s")) { STANDARD_OBSERVER_S = *spectrum; return; }
+
+  for (i = 0; i < ssim->db.count; i++)
+  {
+    if (!strcmp (name, ssim->db.name[i]))
+    {
+      ssim->db.spectrum[i] = *spectrum;
+      return;
+    }
+  }
+  if (ssim->db.count >= SPECTRUM_DB_SIZE-1)
+  {
+    /* eeek */
+    return;
+  }
+  i = ssim->db.count;
+  strncpy (ssim->db.name[i], name, 32);
+  ssim->db.spectrum[i] = *spectrum;
+  ssim->db.count++;
+}
+
+
+static void parse_config_line (SSim   *ssim,
+                               const char *line)
+{
+  char *key;
+  char *p;
+  Spectrum s;
+  int i;
+  if (!line)
+    return;
+
+  if (!strchr (line, '=')) /* lines without = are simply skipped */
+    return;
+  while (*line == ' ') line++;
+
+  key = strdup (line);
+  p = strchr (key, '=');
+
+  while (*p == '=' || *p == ' ')
+  {
+    *p = '\0';
+    p--;
+  }
+
+
+  if (!strcmp (key, "inklimit"))
+    {
+      ssim->ink_limit = strchr(line, '=') ? strtod (strchr (line, '=')+1, NULL) : 3.0;
+      if (ssim->ink_limit < 0.2)
+        ssim->ink_limit = 0.2;
+      free (key);
+      return;
+    }
+  else if (!strcmp (key, "debugwidth"))
+    {
+      ssim->debug_width= strchr(line, '=') ? strtod (strchr (line, '=')+1, NULL) : 25;
+      free (key);
+      return;
+    }
+  else if (!strcmp (key, "halftone"))
+    {
+      ssim->halftone_sim = strchr(line, '=') ? strtod (strchr (line, '=')+1, NULL) : 0;
+      free (key);
+      return;
+    }
+  else if (!strcmp (key, "iterations"))
+    {
+      STOCHASTIC_ITERATIONS = (strchr(line, '=') ? atoi (strchr (line, '=')+1) : 42);
+      free (key);
+      return;
+    }
+  else if (!strcmp (key, "diffusion"))
+    {
+      STOCHASTIC_DIFFUSION = strchr(line, '=') ?
+                 strtod (strchr (line, '=')+1, NULL) : 0;
+      free (key);
+      return;
+    }
+
+  s = parse_spectrum (ssim, strchr (line, '=') +1);
+
+  {
+    ssim_set_spectrum (ssim, key, &s);
+    for (i = 0; i < SSIM_MAX_INKS; i++)
+    {
+      Ink *ink = &ssim->ink_def[i];
+      char prefix[20];
+      sprintf (prefix, "ink%i", i+1);
+      if (!strcmp (key, prefix))
+        {
+          ink->on_white = s;
+          ink->on_black = s; /* default to making paints, not inks */
+          memset (&ink->on_black, 0, sizeof (Spectrum));
+          if (strstr(line, "g=")) ink->dot_gain = strtod (strstr(line, "g=") + 2, NULL);
+          if (strstr(line, "s=")) ink->scale = strtod (strstr(line, "s=") + 2, NULL);
+          ssim->inks = MAX(ssim->inks, i+1);
+          
+          free (key);
+
+          color_recompute (ssim, ink);
+          return;
+        }
+
+      sprintf (prefix, "ink%i.black", i+1);
+      if (!strcmp (key, prefix))
+        {
+          ink->on_black = s;
+          if (strstr(line, "g=")) ink->dot_gain = strtod (strstr(line, "g=") + 2, NULL);
+          if (strstr(line, "s=")) ink->scale = strtod (strstr(line, "s=") + 2, NULL);
+          ssim->inks = MAX(ssim->inks, i+1);
+          
+          free (key);
+          color_recompute (ssim, ink);
+          return;
+        }
+    }
+  }
+}
+
+
+static void
+ssim_reset (SSim *ssim)
+{
+  int i;
+  memset (ssim, 0, sizeof (SSim));
+  for (i = 0; i < SSIM_MAX_INKS; i++)
+    {
+      ssim->ink_def[i].scale = 1.0;
+      ssim->ink_def[i].dot_gain = 1.0;
+    }
+  ssim->ink_limit = SSIM_MAX_INKS;
+}
+
+static void
+ssim_parse_int (SSim *ssim, const char *p)
+{
+  char acc[4096]; /* XXX: not protected */
+  int acci = 0;
+  acci = 0;
+  while (*p)
+  {
+    switch (*p)
+    {
+      case '\n':
+        parse_config_line (ssim, acc);
+        acci = 0;
+        acc[acci] = 0;
+        break;
+      default:
+        acc[acci++] = *p;
+        acc[acci] = 0;
+        break;
+    }
+    p++;
+  }
+}
+
+static void
+ssim_parse_config (SSim       *ssim,
+                   const char *p)
+{
+  if (!p)
+    return;
+  
+  if (ssim->src)
+    {
+      if (!strcmp (ssim->src, p))
+        return;
+      free (ssim->src);
+      ssim->src = NULL;
+    }
+
+  ssim_reset (ssim);
+
+  ssim->src = strdup (p);
+
+  ssim_parse_int (ssim, config_internal);
+  ssim_parse_int (ssim, p);
+
+  compute_neugebauer_primaries (ssim);
+  if (STOCHASTIC_DIFFUSION < 0.1)
+    STOCHASTIC_DIFFUSION = 0.1;
+  else if (!(STOCHASTIC_DIFFUSION < 100.0))
+    STOCHASTIC_DIFFUSION = 100.0;
+  
+}
+
+SSim *
+ssim_new (const char *config)
+{
+  SSim *ssim = calloc (sizeof (SSim), 1);
+  ssim_parse_config (ssim, config);
+  return ssim;
+}
+
+void
+ssim_destroy (SSim *ssim)
+{
+  if (ssim->src)
+    {
+      free (ssim->src);
+      ssim->src = NULL;
+    }
+  free (ssim);
+}
+
+
+int   ssim_get_ink_count    (SSim *ssim)
+{
+  return ssim->inks;
+}
+
+void ssim_set_ink_count    (SSim *ssim, int count)
+{
+  ssim->inks = count;
+}
+
+float ssim_get_ink_limit (SSim *ssim)
+{
+  return ssim->inks;
+}
+
+void ssim_set_ink_limit (SSim *ssim, float limit)
+{
+  ssim->ink_limit = limit;
+}
+
+void  ssim_set_halftone_sim (SSim *ssim,
+                             int   enabled)
+{
+  ssim->halftone_sim = enabled;
+}
+
+int   ssim_get_halftone_sim (SSim *ssim)
+{
+  return ssim->halftone_sim;
+}
+
+/*********************/
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  const Babl *input_format = gegl_operation_get_source_format (operation, "input");
+  gint input_components = 1;
+  if (input_format)
+    input_components = babl_format_get_n_components (input_format);
+
+
+  switch (o->mode)
+  {
+    case GEGL_SSIM_PROOF:
+      gegl_operation_set_format (operation, "input",
+        babl_format_n (babl_type("float"), input_components));
+      gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+      break;
+    case GEGL_SSIM_SEPARATE:
+      //gegl_operation_set_format (operation, "output", 
+      //  babl_format_n (babl_type("float"), ssim_get_ink_count (o->chant_data)));
+      gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+
+      gegl_operation_set_format (operation, "input",
+          babl_format ("RGBA float"));
+      break;
+    default:
+    case GEGL_SSIM_SEPARATE_PROOF:
+      gegl_operation_set_format (operation, "output", babl_format ("RGB float"));
+
+      gegl_operation_set_format (operation, "input",
+          babl_format ("RGBA float"));
+      break;
+  }
+
+#ifdef USE_UI
+  float color[4];
+  GString *conf_str;
+  conf_str = g_string_new ("illuminant=D65\n");
+
+  gegl_color_get_pixel (o->substrate_color, babl_format ("RGBA float"), color);
+  g_string_append_printf (conf_str, "substrate = rgb %f %f %f\n",
+      color[0], color[1], color[2]);
+
+  gegl_color_get_pixel (o->ink1_color, babl_format ("RGBA float"), color);
+  g_string_append_printf (conf_str, "ink1 = rgb %f %f %f o=%f\n",
+      color[0], color[1], color[2], color[3]);
+
+  gegl_color_get_pixel (o->ink2_color, babl_format ("RGBA float"), color);
+  g_string_append_printf (conf_str, "ink2 = rgb %f %f %f o=%f\n",
+      color[0], color[1], color[2], color[3]);
+
+  gegl_color_get_pixel (o->ink3_color, babl_format ("RGBA float"), color);
+  g_string_append_printf (conf_str, "ink3 = rgb %f %f %f o=%f\n",
+      color[0], color[1], color[2], color[3]);
+
+  gegl_color_get_pixel (o->ink4_color, babl_format ("RGBA float"), color);
+  g_string_append_printf (conf_str, "ink4 = rgb %f %f %f o=%f\n",
+      color[0], color[1], color[2], color[3]);
+
+  g_string_append_printf (conf_str, "inklimit=%f\n", o->ink_limit);
+
+  if (o->chant_data)
+    ssim_destroy (o->chant_data);
+  o->chant_data = ssim_new (conf_str->str);
+
+  g_string_free (conf_str, TRUE);
+#else
+
+  if (o->chant_data)
+    ssim_destroy (o->chant_data);
+  o->chant_data = ssim_new (o->config);
+#endif
 }
 
 static gboolean
@@ -428,13 +1294,15 @@ process (GeglOperation       *op,
   GeglChantO *o = GEGL_CHANT_PROPERTIES (op);
   gfloat *in  = in_buf;
   gfloat *out = out_buf;
-  Config *config = o->chant_data;
+  SSim *ssim = o->chant_data;
+  int in_components = 
+    babl_format_get_n_components (gegl_operation_get_format (op, "input"));
 
   switch (o->mode)
   {
-    case GEGL_INK_SIMULATOR_PROOF:
+    case GEGL_SSIM_PROOF:
       {
-        if (config->inks > 4)
+        if (ssim_get_ink_count (ssim) > 3)
         {
           while (samples--)
             {
@@ -442,8 +1310,8 @@ process (GeglOperation       *op,
               int i;
               for (i = 0; i < 4; i++)
                 inks[i] = in[i];
-              spectral_proof  (config, inks, out);
-              in  += 4;
+              ssim_proof  (ssim, inks, out);
+              in  += in_components;
               out += 4;
             }
         }
@@ -451,48 +1319,50 @@ process (GeglOperation       *op,
         {
           while (samples--)
             {
-              spectral_proof  (config, in, out);
-              in  += 4;
+              ssim_proof  (ssim, in, out);
+              in  += in_components;
               out += 4;
             }
         }
       }
       break;
-    case GEGL_INK_SIMULATOR_SEPARATE:
+    case GEGL_SSIM_SEPARATE:
+      /* eeek hard coded for rgba output */
     while (samples--)
       {
         int i;
-        gfloat inks[MAX_INKS];
-        rgb_to_inks (config, in, inks);
-        for (i = 0; i < MIN(4, config->inks); i++)
+        int ink_count = ssim_get_ink_count (ssim);
+        gfloat inks[SSIM_MAX_INKS];
+        ssim_separate (ssim, in, inks);
+        for (i = 0; i < MIN(4, ink_count); i++)
           out[i] = inks[i];
-        if (config->inks < 4)
+        if (ink_count < 4)
           out[3] = 1.0;
-        if (config->inks < 3)
+        if (ink_count < 3)
           out[2] = 0.0;
-        if (config->inks < 2)
+        if (ink_count < 2)
           out[1] = 0.0;
-        in  += 4;
+        in  += in_components;
         out += 4;
       }
       break;
-    case GEGL_INK_SIMULATOR_SEPARATE_PROOF:
-      if (config->debug_width)
+    case GEGL_SSIM_SEPARATE_PROOF:
+      if (o->debug_width)
       {
         gint x = roi->x;
         gint y = roi->y;
         while (samples--)
           {
-            gfloat inks[MAX_INKS];
-            gint foo = ((x+y)/config->debug_width);
-            gint actual_inks = config->inks;
-            rgb_to_inks (config, in, inks);
-            config->inks = MIN(foo, actual_inks);
-            spectral_proof (config, inks, out);
-            config->inks = actual_inks;
+            gfloat inks[SSIM_MAX_INKS];
+            gint foo = ((x+y)/o->debug_width);
+            gint actual_inks = ssim_get_ink_count (ssim);
+            ssim_separate (ssim, in, inks);
+            ssim_set_ink_count (ssim, MIN(foo, actual_inks));
+            ssim_proof (ssim, inks, out);
+            ssim_set_ink_count (ssim, actual_inks);
 
-            in  += 4;
-            out += 4;
+            in  += in_components;
+            out += 3;
 
             x++; if (x>=roi->x+roi->width){x=roi->x;y++;}
           }
@@ -501,178 +1371,26 @@ process (GeglOperation       *op,
       {
         while (samples--)
           {
-            gfloat inks[MAX_INKS];
-            rgb_to_inks (config, in, inks);
-            spectral_proof (config, inks, out);
+            gfloat inks[SSIM_MAX_INKS];
+            ssim_separate (ssim, in, inks);
+            ssim_proof (ssim, inks, out);
 
-            in  += 4;
-            out += 4;
+            in  += in_components;
+            out += 3;
           }
       }
       break;
   }
-
   return TRUE;
 }
 
-static Spectrum parse_spectrum (gchar *spectrum)
-{
-  Spectrum s = TRANSMITTANCE_WHITE;
-  if (!spectrum)
-    return s;
-  while (*spectrum == ' ') spectrum ++;
-
-  if (g_str_has_prefix (spectrum, "rgb"))
-  {
-    Spectrum red = TRANSMITTANCE_RED;
-    Spectrum green = TRANSMITTANCE_GREEN;
-    Spectrum blue = TRANSMITTANCE_BLUE;
-    gint i;
-    gfloat r = 0, g = 0, b = 0;
-    gchar *p = spectrum + 3;
-
-    r = g_strtod (p, &p);
-    if (p) g = g_strtod (p, &p);
-    if (p) b = g_strtod (p, &p);
-
-    for (i = 0; i<SPECTRUM_BANDS; i++)
-      s.bands[i] = red.bands[i] * r + green.bands[i] * g + blue.bands[i] * b;
-
-  } else if (g_str_has_prefix (spectrum, "red"))
-  { Spectrum color = TRANSMITTANCE_RED; s = color;
-  } else if (g_str_has_prefix (spectrum, "green"))
-  { Spectrum color = TRANSMITTANCE_GREEN; s = color;
-  } else if (g_str_has_prefix (spectrum, "blue"))
-  { Spectrum color = TRANSMITTANCE_BLUE; s = color;
-  } else if (g_str_has_prefix (spectrum, "cyan"))
-  { Spectrum color = TRANSMITTANCE_CYAN; s = color;
-  } else if (g_str_has_prefix (spectrum, "yellow"))
-  { Spectrum color = TRANSMITTANCE_YELLOW; s = color;
-  } else if (g_str_has_prefix (spectrum, "magenta"))
-  { Spectrum color = TRANSMITTANCE_MAGENTA; s = color;
-  } else if (g_str_has_prefix (spectrum, "white"))
-  { Spectrum color = TRANSMITTANCE_WHITE; s = color;
-  } else if (g_str_has_prefix (spectrum, "D50"))
-  { Spectrum color = ILLUMINANT_D50; s = color;
-  } else if (g_str_has_prefix (spectrum, "D55"))
-  { Spectrum color = ILLUMINANT_D55; s = color;
-  } else if (g_str_has_prefix (spectrum, "D65"))
-  { Spectrum color = ILLUMINANT_D65; s = color;
-  } else if (g_str_has_prefix (spectrum, "E"))
-  { Spectrum color = ILLUMINANT_E; s = color;
-  } else if (g_str_has_prefix (spectrum, "black"))
-  { Spectrum color = TRANSMITTANCE_BLACK; s = color;
-  } else if (g_str_has_prefix (spectrum, "gray"))
-  { Spectrum color = TRANSMITTANCE_GRAY; s = color;
-  } else
-  {
-    gint band = 0;
-    band = 0;
-    do {
-    s.bands[band++] = g_strtod (spectrum, &spectrum);
-    } while (spectrum && band < SPECTRUM_BANDS);
-  }
-
-  return s;
-}
-
-static void parse_config_line (GeglOperation *operation,
-                               Config        *config,
-                               const gchar   *line)
-{
-  Spectrum s;
-  gint i;
-  if (!line)
-    return;
-  if (!strchr (line, '='))
-    return;
-  while (*line == ' ') line++;
-  s = parse_spectrum (strchr (line, '=') +1);
-  if (g_str_has_prefix (line, "illuminant"))
-    config->illuminant = s;
-  else if (g_str_has_prefix (line, "substrate"))
-    config->substrate = s;
-  else if (g_str_has_prefix (line, "inklimit"))
-    {
-      config->ink_limit = strchr(line, '=') ? g_strtod (strchr (line, '=')+1, NULL) : 3.0;
-      if (config->ink_limit < 0.2)
-        config->ink_limit = 0.2;
-    }
-  else if (g_str_has_prefix (line, "debugwidth"))
-    {
-      config->debug_width= strchr(line, '=') ? g_strtod (strchr (line, '=')+1, NULL) : 25;
-    }
-  else
-  for (i = 0; i < MAX_INKS; i++)
-  {
-    gchar prefix[] = "ink1";
-    prefix[3] = (i+1) + '0';
-    if (g_str_has_prefix (line, prefix))
-      {
-        config->ink_def[i].transmittance = s;
-        if (strstr(line, "o=")) config->ink_def[i].opacity = g_strtod (strstr(line, "o=") + 2, NULL);
-        if (strstr(line, "s=")) config->ink_def[i].scale = g_strtod (strstr(line, "s=") + 2, NULL);
-        config->inks = MAX(config->inks, i+1);
-      }
-  }
-}
-
-static void parse_config (GeglOperation *operation)
-{
-  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
-  const char *p = o->config;
-  GString *str;
-  Config *config = o->chant_data;
-  
-  gint i;
-  if (!p)
-    return;
-
-  if (!config)
-  {
-    config = g_new0 (Config, 1);
-    o->chant_data = config;
-  }
-  
-  if (config->src)
-    {
-      if (!strcmp (config->src, p))
-        return;
-      g_free (config->src);
-    }
-
-  memset (config, 0, sizeof (Config));
-  for (i = 0; i < MAX_INKS; i++) config->ink_def[i].scale = 1.0;
-  config->ink_limit = MAX_INKS;
-
-  str = g_string_new ("");
-  while (*p)
-  {
-    switch (*p)
-    {
-      case '\n':
-        parse_config_line (operation, config, str->str);
-        g_string_assign (str, "");
-        break;
-      default:
-        g_string_append_c (str, *p);
-        break;
-    }
-    p++;
-  }
-
-  g_string_free (str, TRUE);
-  config->src = g_strdup (o->config);
-}
-
-
 static void
 finalize (GObject *object)
 {
   GeglChantO *o = GEGL_CHANT_PROPERTIES (object);
   if (o->chant_data)
   {
-    g_free (o->chant_data);
+    ssim_destroy (o->chant_data);
     o->chant_data = NULL;
   }
   G_OBJECT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (object)))->finalize (object);
@@ -702,4 +1420,5 @@ gegl_chant_class_init (GeglChantClass *klass)
         NULL);
 }
 
+
 #endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]