[gegl] pixel-duster: make more configurable with ifdefs



commit 1a25b6a307b8ec7c9a9ca43a153c3e3f93352881
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Feb 25 22:17:51 2018 +0100

    pixel-duster: make more configurable with ifdefs
    
    Adds support for more symmetries, unused experimental relative neighborhood
    code and more.

 operations/workshop/Makefile.am    |    1 +
 operations/workshop/enlarge.c      |   55 +++++--
 operations/workshop/enlarge2.c     |  239 +++++++++++++++++++++++++++
 operations/workshop/inpaint.c      |    4 +-
 operations/workshop/pixel-duster.h |  310 +++++++++++++++++++++++++++---------
 5 files changed, 518 insertions(+), 91 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index f6b4f8d..bc13f77 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -20,6 +20,7 @@ op_LTLIBRARIES = \
        gradient-map.la \
        hstack.la \
        enlarge.la \
+       enlarge2.la \
        inpaint.la \
        integral-image.la \
        linear-sinusoid.la \
diff --git a/operations/workshop/enlarge.c b/operations/workshop/enlarge.c
index f8a3f89..c7eb59f 100644
--- a/operations/workshop/enlarge.c
+++ b/operations/workshop/enlarge.c
@@ -77,19 +77,18 @@ static void scaled_copy (GeglBuffer *in,
         GeglRectangle r = {x, y, 1, 1};
         gegl_buffer_sample (in, x / scale, y / scale, NULL,
                             &rgba[0], format,
-                            GEGL_SAMPLER_NOHALO, 0);
+                            GEGL_SAMPLER_NEAREST, 0);
         gegl_buffer_set (out, &r, 0, format, &rgba[0], 0);
       }
 }
 
-
 static void improve (PixelDuster *duster,
                      GeglBuffer *in,
                      GeglBuffer *out,
                      gfloat      scale)
 {
   GeglRectangle rect;
-  const Babl *format = babl_format ("RGBA float");
+  const Babl *format = babl_format ("R'G'B'A float");
   gint x, y;
 
   rect = *gegl_buffer_get_extent (out);
@@ -110,16 +109,52 @@ static void improve (PixelDuster *duster,
       {
         Probe *probe;
         probe = add_probe (duster, x, y);
-#if 1
         if (probe_improve (duster, probe) == 0)
         {
-          gfloat rgba[4];
-          gegl_buffer_sample (duster->input, probe->source_x, probe->source_y,  NULL, &rgba[0], format, 
GEGL_SAMPLER_NEAREST, 0);
+#if PIXDUST_REL_DIGEST==0
+          gfloat rgba[4*MAX_K];
+
+          for (int j = 0; j < MAX_K; j++)
+            gegl_buffer_sample (duster->input, probe->source_x[j], probe->source_y[j],  NULL, &rgba[j*4], 
format, GEGL_SAMPLER_NEAREST, 0);
+
+
+          for (int j = 1; j < probe->k; j++)
+            for (int c = 0; c < 4; c++)
+            {
+              rgba[0+c] += rgba[j*4+c];
+            }
+          for (int c = 0; c < 4; c++)
+            {
+              rgba[c] /= probe->k;
+            }
+
           if (rgba[3] <= 0.01)
-            fprintf (stderr, "eek %i,%i %f %f %f %f\n", probe->source_x, probe->source_y, rgba[0], rgba[1], 
rgba[2], rgba[3]);
+            fprintf (stderr, "eek %i,%i %f %f %f %f\n", probe->source_x[MAX_K/2], probe->source_y[MAX_K/2], 
rgba[0], rgba[1], rgba[2], rgba[3]);
+
           gegl_buffer_set (duster->output, GEGL_RECTANGLE(probe->target_x, probe->target_y, 1, 1), 0, 
format, &rgba[0], 0);
-        }
+#else
+          gfloat rgba[4];
+          gfloat delta[4]={0,0,0,0};
+          {
+            int dx = 0, dy = 0;
+            duster_idx_to_x_y (duster, 1, probe->hay[0][0], &dx, &dy);
+            gegl_buffer_sample (duster->output, probe->target_x + dx, probe->target_y + dy,  NULL, &rgba[0], 
format, GEGL_SAMPLER_NEAREST, 0);
+          }
+
+          for (int k = 0; k < probe->k; k++)
+          {
+            for (int c = 0; c < 3; c ++)
+              delta[c] += ((probe->hay[k][4+c]-127.0) / 128);
+          }
+
+          for (int c = 0; c < 3; c ++)
+            rgba[c] = rgba[c] - delta[c] / probe->k;
+          rgba[3]=1.0;
+
+          gegl_buffer_set (duster->output, GEGL_RECTANGLE(probe->target_x, probe->target_y, 1, 1), 0, 
format, &rgba[0], 0);
+
 #endif
+        }
         g_hash_table_remove (duster->probes_ht, xy2offset(probe->target_x, probe->target_y));
       }
    fprintf (stderr, "\r%2.2f   ", y * 100.0 / rect.height);
@@ -127,7 +162,6 @@ static void improve (PixelDuster *duster,
    fprintf (stderr, "\n");
 }
 
-
 static gboolean
 process (GeglOperation       *operation,
          GeglBuffer          *input,
@@ -143,7 +177,7 @@ process (GeglOperation       *operation,
   duster  = pixel_duster_new (input, output,
                               &in_rect, &out_rect,
                               o->seek_distance,
-                              1, 1, 1.0, 1.0,
+                              1, 1, 1.0, 0.3,
                               o->scale,
                               o->scale,
                               NULL);
@@ -170,7 +204,6 @@ get_bounding_box (GeglOperation *operation)
   return result;
 }
 
-
 static GeglRectangle
 get_cached_region (GeglOperation       *operation,
                    const GeglRectangle *roi)
diff --git a/operations/workshop/enlarge2.c b/operations/workshop/enlarge2.c
new file mode 100644
index 0000000..a8d9318
--- /dev/null
+++ b/operations/workshop/enlarge2.c
@@ -0,0 +1,239 @@
+/* This file is an image processing operation for GEGL
+ *
+ * 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 2018 Øyvind Kolås <pippin gimp org>
+ *
+ */
+
+#include <stdio.h>
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_int (seek_distance, "seek radius", 8)
+  value_range (4, 512)
+
+property_double (scale, "scale", 2.0)
+  value_range (0.1, 20.0)
+
+property_int (min_neigh, "min neigh", 1)
+  value_range (1, 10)
+
+property_int (min_iter, "min iter", 512)
+  value_range (1, 512)
+
+property_double (chance_try, "try chance", 0.1)
+  value_range (0.0, 1.0)
+
+property_double (chance_retry, "retry chance", 0.0)
+  value_range (0.0, 1.0)
+
+#else
+
+#define GEGL_OP_FILTER
+#define GEGL_OP_NAME      enlarge2
+#define GEGL_OP_C_SOURCE  enlarge2.c
+
+#include "gegl-op.h"
+#include <stdio.h>
+#include "pixel-duster.h"
+
+static GeglRectangle
+get_required_for_output (GeglOperation       *operation,
+                         const gchar         *input_pad,
+                         const GeglRectangle *roi)
+{
+  GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+  if (gegl_rectangle_is_infinite_plane (&result))
+    return *roi;
+  return result;
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  const Babl *format = babl_format ("RGBA float");
+
+  gegl_operation_set_format (operation, "input",  format);
+  gegl_operation_set_format (operation, "output", format);
+}
+
+static void scaled_copy (GeglBuffer *in,
+                         GeglBuffer *out,
+                         gfloat      scale)
+{
+  GeglRectangle rect;
+  const Babl *format = babl_format ("RGBA float");
+  gint x, y;
+
+  rect = *gegl_buffer_get_extent (out);
+  for (y = 0; y < rect.height; y++)
+    for (x = 0; x < rect.width; x++)
+      {
+        GeglRectangle r = {x, y, 1, 1};
+        gfloat rgba[4] = {0,0,0,0};
+        gegl_buffer_set (out, &r, 0, format, &rgba[0], 0);
+      }
+
+  rect = *gegl_buffer_get_extent (in);
+  for (y = 0; y < rect.height; y++)
+    for (x = 0; x < rect.width; x++)
+      {
+        GeglRectangle r = {x * scale , y * scale, 1, 1};
+        gfloat rgba[4];
+        gegl_buffer_sample (in, x, y, NULL, &rgba[0], format, GEGL_SAMPLER_NEAREST, 0);
+        gegl_buffer_set (out, &r, 0, format, &rgba[0], 0);
+      }
+}
+
+static void remove_grid (GeglBuffer *in,
+                         GeglBuffer *out,
+                         gfloat      scale)
+{
+  GeglRectangle rect;
+  const Babl *format = babl_format ("RGBA float");
+  gint x, y;
+  rect = *gegl_buffer_get_extent (in);
+  for (y = 0; y < rect.height; y++)
+    for (x = 0; x < rect.width; x++)
+      {
+        GeglRectangle r = {x * scale , y * scale, 1, 1};
+        gfloat rgba[4] = {0,0,0,0};
+        gegl_buffer_set (out, &r, 0, format, &rgba[0], 0);
+      }
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglProperties *o      = GEGL_PROPERTIES (operation);
+  GeglRectangle in_rect = *gegl_buffer_get_extent (input);
+  GeglRectangle out_rect = *gegl_buffer_get_extent (output);
+  PixelDuster    *duster;
+
+  scaled_copy (input, output, o->scale);
+  duster  = pixel_duster_new (input, output,
+                              &in_rect, &out_rect,
+                              o->seek_distance,
+                              o->min_neigh,
+                              o->min_iter,
+                              o->chance_try,
+                              o->chance_retry,
+                              o->scale,
+                              o->scale,
+                              NULL);
+  seed_db (duster);
+  pixel_duster_add_probes_for_transparent (duster);
+  pixel_duster_fill (duster);
+#if 1
+  remove_grid (input, output, o->scale);
+  pixel_duster_remove_probes (duster);
+  pixel_duster_add_probes_for_transparent (duster);
+  pixel_duster_fill (duster);
+#endif
+  pixel_duster_destroy (duster);
+
+  return TRUE;
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+  GeglProperties *o      = GEGL_PROPERTIES (operation);
+  result.x = 0;
+  result.y = 0;
+  result.width  *= o->scale;
+  result.height *= o->scale;
+
+  return result;
+}
+
+
+static GeglRectangle
+get_cached_region (GeglOperation       *operation,
+                   const GeglRectangle *roi)
+{
+  GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+  GeglProperties *o      = GEGL_PROPERTIES (operation);
+
+  if (gegl_rectangle_is_infinite_plane (&result))
+    return *roi;
+  result.x = 0;
+  result.y = 0;
+  result.width  *= o->scale;
+  result.height *= o->scale;
+
+  return result;
+}
+
+static gboolean
+operation_process (GeglOperation        *operation,
+                   GeglOperationContext *context,
+                   const gchar          *output_prop,
+                   const GeglRectangle  *result,
+                   gint                  level)
+{
+  GeglOperationClass  *operation_class;
+
+  const GeglRectangle *in_rect =
+    gegl_operation_source_get_bounding_box (operation, "input");
+
+  operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class);
+
+  if (in_rect && gegl_rectangle_is_infinite_plane (in_rect))
+    {
+      gpointer in = gegl_operation_context_get_object (context, "input");
+      gegl_operation_context_take_object (context, "output",
+                                          g_object_ref (G_OBJECT (in)));
+      return TRUE;
+    }
+
+  return operation_class->process (operation, context, output_prop, result,
+                                   gegl_operation_context_get_level (context));
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationFilterClass *filter_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  filter_class->process                    = process;
+  operation_class->prepare                 = prepare;
+  operation_class->process                 = operation_process;
+  operation_class->get_bounding_box        = get_bounding_box;
+  operation_class->get_required_for_output = get_required_for_output;
+  operation_class->get_cached_region       = get_cached_region;
+  operation_class->opencl_support          = FALSE;
+  operation_class->threaded                = FALSE;
+
+  gegl_operation_class_set_keys (operation_class,
+      "name",        "gegl:enlarge2",
+      "title",       "Smart enlarge",
+      "categories",  "heal",
+      "description", "Enlarges an images based on pixel contents",
+      NULL);
+}
+
+#endif
diff --git a/operations/workshop/inpaint.c b/operations/workshop/inpaint.c
index 84d721b..250eb01 100644
--- a/operations/workshop/inpaint.c
+++ b/operations/workshop/inpaint.c
@@ -32,10 +32,10 @@ property_int (min_neigh, "min neigh", 2)
 property_int (min_iter, "min iter", 4)
   value_range (1, 512)
 
-property_double (chance_try, "try chance", 0.55)
+property_double (chance_try, "try chance", 0.88)
   value_range (0.0, 1.0)
 
-property_double (chance_retry, "retry chance", 0.2)
+property_double (chance_retry, "retry chance", 0.5)
   value_range (0.0, 1.0)
 
 #else
diff --git a/operations/workshop/pixel-duster.h b/operations/workshop/pixel-duster.h
index 403c806..1e7293a 100644
--- a/operations/workshop/pixel-duster.h
+++ b/operations/workshop/pixel-duster.h
@@ -1,12 +1,30 @@
 /* pixel-duster
+ *
+ * the pixel duster data structures and functions are used by multiple ops,
+ * but kept in one place since they share so much implementation
+ *
  * a context aware pixel inpainting algorithm
  * 2018 (c) Øyvind Kolås pippin gimp org
  */
 
 /*
     todo:
-         median/mean for noise redux
          exclude identicals - when it is obvious
+
+         threading
+           create list of hashtables and to hashtable list per thread
+
+         adjust precision of matching
+
+         replace hashtables with just lists - and include coords in element - perhaps with count..
+         for identical entries - thus not losing accurate median computation capabilitiy..
+         do median instead of mean for matched pixel components
+
+         store values relative to center pixel instead of center pixel - thus
+         permitting a wider range of neighborhoods to produce valid data - thus will be
+         good at least for superresolution
+
+         add mirroring - thus doubling data
  */
 
 #define POW2(x) ((x)*(x))
@@ -35,19 +53,40 @@ typedef struct
   int            order[512][3];
 } PixelDuster;
 
+
+/* todo: create neighbor color histogram - that can
+         be used as neighborhood separation criteria
+         ideally low-enough bits that it can be used
+         as selector.
+         gray axis as separate entity.
+
+         4x4x4
+
+*/
+
+#define MAX_K               4
+#define PIXDUST_REL_DIGEST  0
+#define NEIGHBORHOOD        32
+#define PIXDUST_ORDERED     1
+#define MAX_DIR             4
+
+//#define ONLY_DIR 0
+
 typedef struct Probe {
   int            target_x;
   int            target_y;
-  int            score;
   int            age;
-  int            source_x;
-  int            source_y;
+  int            k;
+  int            score;
+  int            k_score[MAX_K];
+  int            source_x[MAX_K];
+  int            source_y[MAX_K];
+  guchar        *hay[MAX_K];
 } Probe;
 
 /* used for hash-table keys */
 #define xy2offset(x,y)   GINT_TO_POINTER(((y) * 65536 + (x)))
 
-#define NEIGHBORHOOD 23
 
 /* when going through the image preparing the index - only look at the subset
  * needed pixels - and later when fetching out hashed pixels - investigate
@@ -58,7 +97,7 @@ typedef struct Probe {
 static void init_order(PixelDuster *duster)
 {
   int i, x, y;
-#if 0
+#if PIXDUST_ORDERED
   int order_2d[][15]={
                  {  0,  0,  0,  0,  0,142,111,112,126,128,  0,  0,  0,  0,  0},
                  {  0,  0,  0,  0,124,110, 86, 87, 88,113,129,143,  0,  0,  0},
@@ -104,15 +143,40 @@ static void init_order(PixelDuster *duster)
   for (y = -7; y <= 7; y ++)
     for (x = -7; x <= 7; x ++)
       {
-        if (order_2d[x+7][y+7] == i)
+        if (order_2d[y+7][x+7] == i)
         {
-          duster->order[i][0] = x;
-          duster->order[i][1] = y;
+          duster->order[i][0] = y;
+          duster->order[i][1] = x;
           duster->order[i][2] = POW2(x)+POW2(y);
         }
       }
 }
 
+static void duster_idx_to_x_y (PixelDuster *duster, int index, int dir, int *x, int *y)
+{
+  switch (dir)
+  {
+    default:
+    case 0: /* right */
+      *x =  -duster->order[index][0];
+      *y =  -duster->order[index][1];
+      break;
+    case 1: /* left */
+      *x =  duster->order[index][0];
+      *y =  duster->order[index][1];
+      break;
+    case 2: /* down */
+      *x =  -duster->order[index][1];
+      *y =  -duster->order[index][0];
+      break;
+    case 3: /* up */
+      *x =  duster->order[index][1];
+      *y =  duster->order[index][0];
+      break;
+  }
+}
+
+
 static PixelDuster * pixel_duster_new (GeglBuffer *input,
                                        GeglBuffer *output,
                                        const GeglRectangle *in_rect,
@@ -127,18 +191,18 @@ static PixelDuster * pixel_duster_new (GeglBuffer *input,
                                        GeglOperation *op)
 {
   PixelDuster *ret = g_malloc0 (sizeof (PixelDuster));
-  ret->input = input;
-  ret->output = output;
+  ret->input       = input;
+  ret->output      = output;
   ret->seek_radius = seek_radius;
-  ret->minimum_neighbors = minimum_neighbors;
+  ret->minimum_neighbors  = minimum_neighbors;
   ret->minimum_iterations = minimum_iterations;
-  ret->try_chance = try_chance;
+  ret->try_chance   = try_chance;
   ret->retry_chance = retry_chance;
   ret->op = op;
-  ret->in_rect = *in_rect;
+  ret->in_rect  = *in_rect;
   ret->out_rect = *out_rect;
-  ret->scale_x = scale_x;
-  ret->scale_y = scale_y;
+  ret->scale_x  = scale_x;
+  ret->scale_y  = scale_y;
   for (int i = 0; i < 4096; i++)
     ret->ht[i] = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
   ret->probes_ht = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
@@ -160,80 +224,100 @@ static void pixel_duster_destroy (PixelDuster *duster)
   pixel_duster_remove_probes (duster);
   for (int i = 0; i < 4096; i++)
   {
+#if 0
     if (g_hash_table_size (duster->ht[i]))
       fprintf (stderr, "%i:%i ", i, g_hash_table_size (duster->ht[i]));
+#endif
     g_hash_table_destroy (duster->ht[i]);
   }
   fprintf (stderr, "\n");
   g_free (duster);
 }
 
+
 static void extract_site (PixelDuster *duster, GeglBuffer *input, int x, int y, guchar *dst)
 {
   static const Babl *format = NULL;
   static const Babl *yformat = NULL;
   guchar lum[8];
-  int bdir = 0;
+  int bdir, maxlum;
+
   if (!format){
     format = babl_format ("R'G'B'A u8");
     yformat = babl_format ("Y'aA u8");
   }
-#if 1
+#define PIXDUST_DIR_INVARIANT 1
+#if PIXDUST_DIR_INVARIANT==1
   /* figure out which of the up/down/left/right pixels are brightest,
      using premultiplied alpha - do punish blank spots  */
 
-  gegl_buffer_sample (input,  x + 1, y + 0, NULL, &lum[0], yformat, GEGL_SAMPLER_NEAREST, 0);
-  gegl_buffer_sample (input,  x - 1, y + 0, NULL, &lum[2], yformat, GEGL_SAMPLER_NEAREST, 0);
-  gegl_buffer_sample (input,  x + 0, y + 1, NULL, &lum[3], yformat, GEGL_SAMPLER_NEAREST, 0);
-  gegl_buffer_sample (input,  x + 0, y - 1, NULL, &lum[4], yformat, GEGL_SAMPLER_NEAREST, 0);
+  gegl_buffer_sample (input, x + 1, y + 0, NULL, &lum[0], yformat, GEGL_SAMPLER_NEAREST, 0);
+  gegl_buffer_sample (input, x - 1, y + 0, NULL, &lum[2], yformat, GEGL_SAMPLER_NEAREST, 0);
+  gegl_buffer_sample (input, x + 0, y + 1, NULL, &lum[4], yformat, GEGL_SAMPLER_NEAREST, 0);
+  gegl_buffer_sample (input, x + 0, y - 1, NULL, &lum[6], yformat, GEGL_SAMPLER_NEAREST, 0);
+
+ bdir = 0;
 
-  for (int i = 1; i < 4; i++)
-    if (lum[i*2] > bdir)
-      bdir = i;
+ maxlum = lum[0*2];
+ for (int i = 1; i < MAX_DIR; i++)
+   if (lum[i*2] > maxlum)
+     {
+       bdir = i;
+       maxlum = lum[i*2];
+     }
+
+#ifdef ONLY_DIR
+  bdir = ONLY_DIR;
+#endif
 #endif
-  /* and orient our neighborhood fetching based on that */
-  switch (bdir)
-  {
-    case 0:
-      for (int i = 0; i <= NEIGHBORHOOD; i++)
-        gegl_buffer_sample (input,
-                            x + duster->order[i][0],
-                            y + duster->order[i][1],
-                            NULL, &dst[i*4], format,
-                            GEGL_SAMPLER_NEAREST, 0);
-      break;
-    case 1:
-      for (int i = 0; i <= NEIGHBORHOOD; i++)
-        gegl_buffer_sample (input,
-                            x - duster->order[i][0],
-                            y - duster->order[i][1],
-                            NULL, &dst[i*4], format,
-                            GEGL_SAMPLER_NEAREST, 0);
-      break;
-    case 2:
-      for (int i = 0; i <= NEIGHBORHOOD; i++)
-        gegl_buffer_sample (input,
-                            x + duster->order[i][1],
-                            y + duster->order[i][0],
-                            NULL, &dst[i*4], format,
-                            GEGL_SAMPLER_NEAREST, 0);
-      break;
-    case 3:
-      for (int i = 0; i <= NEIGHBORHOOD; i++)
-        gegl_buffer_sample (input,
-                            x - duster->order[i][1],
-                            y - duster->order[i][0],
-                            NULL, &dst[i*4], format,
-                            GEGL_SAMPLER_NEAREST, 0);
-      break;
 
-    break;
+#if PIXDUST_REL_DIGEST==0
+
+  for (int i = 0; i <= NEIGHBORHOOD; i++)
+  {
+    int dx, dy;
+    duster_idx_to_x_y (duster, i, bdir, &dx, &dy);
+    gegl_buffer_sample (input,
+                        x + dx,
+                        y + dy,
+                        NULL, &dst[i*4], format,
+                        GEGL_SAMPLER_NEAREST, 0);
+  }
+#else
+  for (int i = 0; i <= NEIGHBORHOOD; i++)
+  {
+  guchar tmp[4];
+  gegl_buffer_sample (input,
+                      x,
+                      y,
+                      NULL, &tmp[0], format,
+                      GEGL_SAMPLER_NEAREST, 0);
+  for (int i = 0; i <= NEIGHBORHOOD; i++)
+  {
+    int dx, dy;
+    duster_idx_to_x_y (duster, i, bdir, &dx, &dy);
+    gegl_buffer_sample (input,
+                        x + dx,
+                        y + dy,
+                        NULL, &dst[i*4], format,
+                        GEGL_SAMPLER_NEAREST, 0);
+
+    if (i==0)
+      for (int j = 0; j < 3; j++)
+        dst[i*4+j]=tmp[j];
+      else
+        for (int j = 0; j < 3; j++)
+          dst[i*4+j]= (dst[i*4+j] - tmp[j])/2 + 127;
+    dst[i*4+3]= dst[3];
   }
+ }
+#endif
+ dst[0] = bdir;
 }
 
 static inline int u8_rgb_diff (guchar *a, guchar *b)
 {
-  return POW2(a[0]-b[0]) + POW2(a[1]-b[1]) * 2 + POW2(a[2]-b[2]);
+  return POW2(a[0]-b[0]) * 2 + POW2(a[1]-b[1]) * 3 + POW2(a[2]-b[2]);
 }
 
 static int inline
@@ -260,7 +344,11 @@ score_site (PixelDuster *duster,
     else
     {
       /* we score missing cells as if it is a big diff */
-      score += ((POW2(36))*3) * 40 / duster->order[i][2];
+#if PIXDUST_REL_DIGEST==0
+      score += ((POW2(1))*3) * 40 / duster->order[i][2];
+#else
+      score += ((POW2(3))*3) * 40 / duster->order[i][2];
+#endif
     }
   }
   return score;
@@ -272,9 +360,11 @@ add_probe (PixelDuster *duster, int target_x, int target_y)
   Probe *probe    = g_malloc0 (sizeof (Probe));
   probe->target_x = target_x;
   probe->target_y = target_y;
-  probe->source_x = 0;//target_x / duster->scale_x;
-  probe->source_y = 0;//target_y / duster->scale_y;
-  probe->score    = INITIAL_SCORE;
+  probe->source_x[0] = 0;//target_x / duster->scale_x;
+  probe->source_y[0] = 0;//target_y / duster->scale_y;
+  probe->k_score[0]   = INITIAL_SCORE;
+  probe->k = 0;
+  probe->score   = INITIAL_SCORE;
   g_hash_table_insert (duster->probes_ht,
                        xy2offset(target_x, target_y), probe);
   return probe;
@@ -310,6 +400,7 @@ probe_neighbors (PixelDuster *duster, GeglBuffer *output, Probe *probe, int min)
   if (found >=min) return found;
   if (probe_rel_is_set (duster, output, probe,  0, -1)) found ++;
   if (found >=min) return found;
+#if 0
   if (probe_rel_is_set (duster, output, probe,  1, 1)) found ++;
   if (found >=min) return found;
   if (probe_rel_is_set (duster, output, probe, -1,-1)) found ++;
@@ -318,7 +409,6 @@ probe_neighbors (PixelDuster *duster, GeglBuffer *output, Probe *probe, int min)
   if (found >=min) return found;
   if (probe_rel_is_set (duster, output, probe, -1, 1)) found ++;
   if (found >=min) return found;
-#if 0
   if (probe_rel_is_set (duster, output, probe,  2, 0)) found ++;
   if (found >=min) return found;
   if (probe_rel_is_set (duster, output, probe,  0, 2)) found ++;
@@ -338,6 +428,7 @@ probe_neighbors (PixelDuster *duster, GeglBuffer *output, Probe *probe, int min)
   return found;
 }
 
+#if 0
 static inline int
 spread_relative (PixelDuster *duster, Probe *probe, int dx, int dy)
 {
@@ -370,6 +461,7 @@ spread_relative (PixelDuster *duster, Probe *probe, int dx, int dy)
       }
   return 0;
 }
+#endif
 
 static int site_subset (guchar *site)
 {
@@ -407,6 +499,27 @@ static void site_subset2 (guchar *site, gint *min, gint *max)
   }
 }
 
+static void inline compare_needle_exact (gpointer key, gpointer value, gpointer data)
+{
+  void **ptr = data;
+  //PixelDuster *duster = ptr[0];
+  guchar *new_hay;
+  guchar *val_hay;
+  gint   *found_count = ptr[2];
+  if (*found_count)
+    return;
+
+  new_hay = ptr[1];
+  val_hay = value;
+
+  for (int i = 4; i < 4 * NEIGHBORHOOD; i++)
+    {
+      int diff = ((int)(new_hay[i])) - ((int)(val_hay[i]));
+      if ( (diff <= -2) || (diff >= 2))
+        return;
+    }
+ (*found_count)++;
+}
 
 static guchar *ensure_hay (PixelDuster *duster, int x, int y, int subset)
 {
@@ -428,12 +541,27 @@ static guchar *ensure_hay (PixelDuster *duster, int x, int y, int subset)
           return NULL;
         }
       }
-      g_hash_table_insert (duster->ht[subset], xy2offset(x, y), hay);
+      {
+#if 0
+        gint found_count = 0;
+        void *ptr[3] = {duster, &hay[0], &found_count};
+        g_hash_table_foreach (duster->ht[subset], compare_needle_exact, ptr);
+        if (found_count)
+          {
+            g_free (hay);
+          }
+        else
+#endif
+          {
+            g_hash_table_insert (duster->ht[subset], xy2offset(x, y), hay);
+          }
+      }
     }
 
   return hay;
 }
 
+
 static void compare_needle (gpointer key, gpointer value, gpointer data)
 {
   void **ptr = data;
@@ -446,17 +574,31 @@ static void compare_needle (gpointer key, gpointer value, gpointer data)
   gint y = offset / 65536;
   int score;
 
+#if 1
   if (duster->scale_x == 1.0 && x == probe->target_x &&
 -     duster->scale_y == 1.0 && y == probe->target_y )
    return;
+#endif
 
   score = score_site (duster, &needle[0], hay, probe->score);
 
-  if (score < probe->score)
+  if (score <= probe->score)
     {
-      probe->source_x = x;
-      probe->source_y = y;
-      probe->score = score;
+      int j;
+      for (j = MAX_K-1; j >= 1; j --)
+      {
+        probe->source_x[j] = probe->source_x[j-1];
+        probe->source_y[j] = probe->source_y[j-1];
+        probe->hay[j] = probe->hay[j-1];
+        probe->k_score[j] = probe->k_score[j-1];
+      }
+      probe->k++;
+      if (probe->k > MAX_K)
+        probe->k = MAX_K;
+      probe->source_x[0] = x;
+      probe->source_y[0] = y;
+      probe->hay[0] = hay;
+      probe->score = probe->k_score[0] = score;
     }
 }
 
@@ -541,7 +683,10 @@ static inline void pixel_duster_add_probes_for_transparent (PixelDuster *duster)
     float *out_pix = i->data[0];
     while (n_pixels--)
     {
-      if (out_pix[3] <= 0.001)
+      if (out_pix[3] <= 0.001 ||
+          (out_pix[0] <= 0.01 &&
+           out_pix[1] <= 0.01 &&
+           out_pix[2] <= 0.01))
       {
         add_probe (duster, x, y);
       }
@@ -588,8 +733,8 @@ static inline void pixel_duster_fill (PixelDuster *duster)
 
     if (probe->score == INITIAL_SCORE || try_replace)
     {
-       if ((probe->source_x == probe->target_x &&
-            probe->source_y == probe->target_y))
+       if ((probe->source_x[0] == probe->target_x &&
+            probe->source_y[0] == probe->target_y))
          try_replace = 0;
 
       if ((rand()%100)/100.0 < duster->try_chance &&
@@ -600,11 +745,20 @@ static inline void pixel_duster_fill (PixelDuster *duster)
           probe->score = INITIAL_SCORE;
         if (probe_improve (duster, probe) == 0)
         {
+          gfloat sum_rgba[4]={0.0,0.0,0.0,0.0};
           gfloat rgba[4];
-          gegl_buffer_sample (duster->input, probe->source_x, probe->source_y,
-                              NULL, &rgba[0], format, GEGL_SAMPLER_NEAREST, 0);
+
+          for (gint i = 0; i < probe->k; i++)
+          {
+            gegl_buffer_sample (duster->input, probe->source_x[i], probe->source_y[i],
+                                NULL, &rgba[0], format, GEGL_SAMPLER_NEAREST, 0);
+            for (gint c = 0; c < 4; c++)
+              sum_rgba[c] += rgba[c];
+          }
+          for (gint c = 0; c < 4; c++)
+            rgba[c] = sum_rgba[c] / probe->k;
           if (rgba[3] <= 0.01)
-            fprintf (stderr, "eek %i,%i %f %f %f %f\n", probe->source_x, probe->source_y, rgba[0], rgba[1], 
rgba[2], rgba[3]);
+            fprintf (stderr, "eek %i,%i %f %f %f %f\n", probe->source_x[MAX_K/2], probe->source_y[MAX_K/2], 
rgba[0], rgba[1], rgba[2], rgba[3]);
           gegl_buffer_set (duster->output, GEGL_RECTANGLE(probe->target_x, probe->target_y, 1, 1), 0, 
format, &rgba[0], 0);
          }
       }


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