[gegl] ink-simulator: added stochastic ink separator



commit 56272dc5bd3b0c773724d84152bbe1dbd26acf5c
Author: Øyvind Kolås <pippin gimp org>
Date:   Wed Mar 19 06:22:13 2014 +0100

    ink-simulator: added stochastic ink separator

 operations/workshop/ink-simulator.c |  300 +++++++++++++++++++++++-----------
 1 files changed, 203 insertions(+), 97 deletions(-)
---
diff --git a/operations/workshop/ink-simulator.c b/operations/workshop/ink-simulator.c
index 8155a1f..784ebbf 100644
--- a/operations/workshop/ink-simulator.c
+++ b/operations/workshop/ink-simulator.c
@@ -22,16 +22,25 @@
 #ifdef GEGL_CHANT_PROPERTIES
 
 #define DEFAULT_INKS \
-"illuminant = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n" \
-"substrate  = white\n" \
-"ink1 = cyan s=3\n"\
-"ink2 = magenta s=3\n"\
-"ink3 = yellow s=2\n"\
-"ink4 = black s=1\n"
+"illuminant=1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n" \
+"substrate= rgb 0.98 0.98 0.98 \n" \
+"ink1=cyan    s=1.5\n"\
+"ink2=magenta s=1.5\n"\
+"ink3=yellow  s=1.5\n"\
+"ink4=black   s=1\n"\
+"inklimit=150\n"
+
+gegl_chant_register_enum (ink_sim_mode)
+  enum_value (GEGL_INK_SIMULATOR_PROOF, "simulate printing of inks")
+  enum_value (GEGL_INK_SIMULATOR_SEPARATE, "separate to inks")
+  enum_value (GEGL_INK_SIMULATOR_SEPARATE_PROOF, "separate and simulate")
+gegl_chant_register_enum_end (GeglInkSimMode)
 
 gegl_chant_multiline (config, _("ink configuration"), DEFAULT_INKS,
          _("Textual desciption of inks passed in"))
 
+gegl_chant_enum (mode, _("mode"), GeglInkSimMode, ink_sim_mode, GEGL_INK_SIMULATOR_SEPARATE_PROOF, _("how 
the ink simulator is used"))
+
 #else
 
 #define GEGL_CHANT_TYPE_POINT_FILTER
@@ -41,7 +50,11 @@ gegl_chant_multiline (config, _("ink configuration"), DEFAULT_INKS,
 
 #define SPECTRUM_BANDS 20
 
+typedef struct _Config Config;
+typedef struct _Ink Ink;
 typedef struct _Spectrum Spectrum;
+
+
 struct _Spectrum {
   float bands[SPECTRUM_BANDS];
   /* 20 bands, spaced as follows :
@@ -52,6 +65,22 @@ struct _Spectrum {
    */
 };
 
+struct _Ink {
+  Spectrum transmittance; /* spectral energy of this ink; as reflected of a fully reflective background */
+  float    opacity;       /* how much this ink covers up instead of mixes with background (substrate+inks) */
+  float    scale;         /* scale factor; increasing the amount of spectral contribution */
+};
+
+struct _Config {
+  Spectrum illuminant;
+  Spectrum substrate;
+  Ink      ink_def[4];
+  float    ink_limit;
+  int      inks;
+};
+
+static Config config;
+
 /* 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
@@ -80,20 +109,11 @@ static Spectrum STANDARD_OBSERVER_S   = {{
 #define ILLUMINANT_E         TRANSMITTANCE_WHITE
 
 /* the even illuminant; which is easy and almost cheating */
-static Spectrum ILLUMINANT            = ILLUMINANT_E;
-static Spectrum SUBSTRATE_TRANSMITTANCE = TRANSMITTANCE_WHITE;
-
-static int inks = 3;
-static Spectrum INK_TRANSMITTANCE_INK1 = TRANSMITTANCE_GREEN;
-static Spectrum INK_TRANSMITTANCE_INK2 = TRANSMITTANCE_BLACK;
-static Spectrum INK_TRANSMITTANCE_INK3 = TRANSMITTANCE_YELLOW;
-static Spectrum INK_TRANSMITTANCE_INK4 = TRANSMITTANCE_WHITE;
-static Spectrum INK_TRANSMITTANCE_INK5 = TRANSMITTANCE_WHITE;
-static float ink1_opacity = 0;
-static float ink2_opacity = 0;
-static float ink3_opacity = 0;
-static float ink4_opacity = 0;
-static float ink5_opacity = 0;
+
+//static int inks = 3;
+//static Ink ink_defs[4] = {{{{0}}}};
+//static Spectrum ILLUMINANT              = ILLUMINANT_E;
+//static Spectrum SUBSTRATE_TRANSMITTANCE = TRANSMITTANCE_WHITE;
 
 static inline Spectrum add_ink (Spectrum s, Spectrum ink, float coverage, float opacity)
 {
@@ -109,7 +129,12 @@ static inline Spectrum add_ink (Spectrum s, Spectrum ink, float coverage, float
 
 static Spectrum spectrum_remove_light (Spectrum s, Spectrum ink, float coverage)
 {
-  return add_ink (s, ink, coverage, 0.0);
+  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;
+  }
+  return s;
 }
 
 static float spectrum_integrate (Spectrum s, Spectrum is, float scale)
@@ -126,16 +151,93 @@ static void parse_config (GeglOperation *operation);
 static void
 prepare (GeglOperation *operation)
 {
-
   /* 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 ("RGB float"));
-
+  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
 
   parse_config (operation);
 }
 
-static float ink_scale[4] = {1,1,1,1};
+static inline void spectrum_to_rgba (Spectrum spec, float *rgba)
+{
+  float l, m, s;
+  l = spectrum_integrate (spec, STANDARD_OBSERVER_L, 0.4);
+  m = spectrum_integrate (spec, STANDARD_OBSERVER_M, 0.4);
+  s = spectrum_integrate (spec, STANDARD_OBSERVER_S, 0.4);
+  /* this LMS to linear RGB -- likely not 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;
+}
+
+static inline Spectrum inks_to_spectrum (Config *config, float *ink_levels)
+{
+  int i;
+  Spectrum spec = config->illuminant;
+
+  spec = spectrum_remove_light (spec, config->substrate, 1.0);
+  for (i = 0; i < config->inks; i++)
+    spec = add_ink (spec,
+                    config->ink_def[i].transmittance,
+                    ink_levels[i] * config->ink_def[i].scale,
+                    config->ink_def[i].opacity);
+  return spec;
+}
+
+static inline void spectral_proof (Config *config, float *ink_levels, float *rgba)
+{
+  spectrum_to_rgba (inks_to_spectrum (config, ink_levels), rgba);
+}
+
+static inline double colordiff (float *cola, float *colb)
+{
+  return 
+    (cola[0]-colb[0])*(cola[0]-colb[0])+
+    (cola[1]-colb[1])*(cola[1]-colb[1])+
+    (cola[2]-colb[2])*(cola[2]-colb[2]);
+}
+
+static inline void rgb_to_inks (Config *config, float *rgb, float *ink_levels)
+{
+  float best[8] = {0.5,0.5,0.5,0.5,0.5};
+  float bestdiff = 3;
+  int i;
+
+  float rrange = 0.5;
+  for (i = 0; i < 64; i++)
+  {
+    int j;
+    float attempt[8];
+    float softrgb[4];
+    float diff;
+    float inksum = 0;
+    do{
+      inksum = 0.0;
+      for (j = 0; j < config->inks; j++)
+      {
+        attempt[j] = best[j] + g_random_double_range(-rrange, rrange);
+        if (attempt[j] < 0) attempt[j] = 0;
+        if (attempt[j] > 1) attempt[j] = 1;
+        inksum += attempt[j];
+      }
+    } while (inksum > config->ink_limit);
+
+    spectral_proof (config, attempt, softrgb);
+    diff = colordiff (rgb, softrgb);
+    if (diff < bestdiff)
+    {
+      bestdiff = diff;
+      for (j = 0; j < config->inks; j++)
+        best[j] = attempt[j];
+      rrange *= 0.8;
+      //if (diff < 0.001) /* bail early */
+      //  i = 1000;
+    }
+  }
+  for (i = 0; i < config->inks; i++)
+    ink_levels[i] = best[i];
+}
 
 static gboolean
 process (GeglOperation       *op,
@@ -145,38 +247,49 @@ process (GeglOperation       *op,
          const GeglRectangle *roi,
          gint                 level)
 {
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (op);
   gfloat *in  = in_buf;
   gfloat *out = out_buf;
 
-  while (samples--)
-    {
-      Spectrum spec = ILLUMINANT;
-      float l, m, s;
-
-      spec = spectrum_remove_light (spec, SUBSTRATE_TRANSMITTANCE, 1.0);
-      if (inks >=1) spec = add_ink (spec, INK_TRANSMITTANCE_INK1, in[0] * ink_scale[0], ink1_opacity);
-      if (inks >=2) spec = add_ink (spec, INK_TRANSMITTANCE_INK2, in[1] * ink_scale[1], ink2_opacity);
-      if (inks >=3) spec = add_ink (spec, INK_TRANSMITTANCE_INK3, in[2] * ink_scale[2], ink3_opacity);
-      if (inks >=4) spec = add_ink (spec, INK_TRANSMITTANCE_INK4, in[3] * ink_scale[3], ink4_opacity);
-
-      l = spectrum_integrate (spec, STANDARD_OBSERVER_L, 0.4);
-      m = spectrum_integrate (spec, STANDARD_OBSERVER_M, 0.4);
-      s = spectrum_integrate (spec, STANDARD_OBSERVER_S, 0.4);
-
-      /* this LMS to linear RGB -- likely no right..  */
-      
-      out[0] = ((30.83) * l + (-29.83) * m + (1.61) * s);
-      out[1] = ((-6.481) * l + (17.7155) * m + (-2.532) * s);
-      out[2] = ((-0.375) * l + (-1.19906) * m + (14.27) * s);
-
+  switch (o->mode)
+  {
+    case GEGL_INK_SIMULATOR_PROOF:
+      while (samples--)
+        {
+          spectral_proof    (&config, in, out);
+          in  += 4;
+          out += 4;
+        }
+      break;
+    case GEGL_INK_SIMULATOR_SEPARATE:
+    while (samples--)
+      {
+        rgb_to_inks (&config, in, out);
+        if (config.inks < 4)
+          out[3] = 1.0;
+        if (config.inks < 3)
+          out[2] = 0.0;
+        if (config.inks < 2)
+          out[1] = 0.0;
+        in  += 4;
+        out += 4;
+      }
+      break;
+    case GEGL_INK_SIMULATOR_SEPARATE_PROOF:
+    while (samples--)
+      {
+        float temp[4];
+        rgb_to_inks (&config, in, temp);
+        spectral_proof    (&config, temp, out);
+        in  += 4;
+        out += 4;
+      }
+      break;
+  }
 
-      in  += 4;
-      out += 3;
-    }
   return TRUE;
 }
 
-
 static Spectrum parse_spectrum (gchar *spectrum)
 {
   Spectrum s = TRANSMITTANCE_WHITE;
@@ -184,7 +297,23 @@ static Spectrum parse_spectrum (gchar *spectrum)
     return s;
   while (*spectrum == ' ') spectrum ++;
 
-  if (g_str_has_prefix (spectrum, "red"))
+  if (g_str_has_prefix (spectrum, "rgb"))
+  {
+    Spectrum red = TRANSMITTANCE_RED;
+    Spectrum green = TRANSMITTANCE_GREEN;
+    Spectrum blue = TRANSMITTANCE_BLUE;
+    int i;
+    float 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;
@@ -218,50 +347,36 @@ static void parse_config_line (GeglOperation *operation,
                                const char *line)
 {
   Spectrum s;
+  int i;
   if (!line)
     return;
-
   if (!strchr (line, '='))
     return;
-
   while (*line == ' ') line++;
-  
-  inks = 0;
-
   s = parse_spectrum (strchr (line, '=') +1);
-
   if (g_str_has_prefix (line, "illuminant"))
-    ILLUMINANT = s;
+    config.illuminant = s;
   else if (g_str_has_prefix (line, "substrate"))
-    SUBSTRATE_TRANSMITTANCE = s;
-  else if (g_str_has_prefix (line, "ink1"))
+    config.substrate = s;
+  else if (g_str_has_prefix (line, "inklimit"))
     {
-      INK_TRANSMITTANCE_INK1 = s;
-      if (strstr(line, "o=")) ink1_opacity = g_strtod (strstr(line, "o=") + 2, NULL);
-      if (strstr(line, "s=")) ink_scale[0] = g_strtod (strstr(line, "s=") + 2, NULL);
-      inks = MAX(inks, 1);
-    }
-  else if (g_str_has_prefix (line, "ink2"))
-    {
-      INK_TRANSMITTANCE_INK2 = s;
-      inks = MAX(inks, 2);
-      if (strstr(line, "o=")) ink2_opacity = g_strtod (strstr(line, "o=") + 2, NULL);
-      if (strstr(line, "s=")) ink_scale[1] = g_strtod (strstr(line, "s=") + 2, NULL);
-    }
-  else if (g_str_has_prefix (line, "ink3"))
-    {
-      INK_TRANSMITTANCE_INK3 = s;
-      inks = MAX(inks, 3);
-      if (strstr(line, "o=")) ink3_opacity = g_strtod (strstr(line, "o=") + 2, NULL);
-      if (strstr(line, "s=")) ink_scale[2] = g_strtod (strstr(line, "s=") + 2, NULL);
-    }
-  else if (g_str_has_prefix (line, "ink4"))
-    {
-      INK_TRANSMITTANCE_INK4 = s;
-      inks = MAX(inks, 4);
-      if (strstr(line, "o=")) ink4_opacity = g_strtod (strstr(line, "o=") + 2, NULL);
-      if (strstr(line, "s=")) ink_scale[3] = g_strtod (strstr(line, "s=") + 2, NULL);
+      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
+  for (i = 0; i < 4; 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)
@@ -274,18 +389,9 @@ static void parse_config (GeglOperation *operation)
   if (!p)
     return;
 
-  memset (&INK_TRANSMITTANCE_INK1, 0, sizeof (INK_TRANSMITTANCE_INK1));
-  memset (&INK_TRANSMITTANCE_INK2, 0, sizeof (INK_TRANSMITTANCE_INK2));
-  memset (&INK_TRANSMITTANCE_INK3, 0, sizeof (INK_TRANSMITTANCE_INK3));
-  memset (&INK_TRANSMITTANCE_INK4, 0, sizeof (INK_TRANSMITTANCE_INK4));
-  memset (&INK_TRANSMITTANCE_INK5, 0, sizeof (INK_TRANSMITTANCE_INK5));
-
-  for (i = 0; i < 4; i++) ink_scale[i] = 2.0;
-  ink1_opacity = 0;
-  ink2_opacity = 0;
-  ink3_opacity = 0;
-  ink4_opacity = 0;
-  ink5_opacity = 0;
+  memset (&config, 0, sizeof (config));
+  for (i = 0; i < 4; i++) config.ink_def[i].scale = 1.0;
+  config.ink_limit = 3.0;
 
   str = g_string_new ("");
   while (*p)


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