GeglBuffer *
gimp_image_contiguous_region_by_color (GimpImage            *image,
                                       GimpDrawable         *drawable,
                                       gboolean              sample_merged,
                                       gboolean              antialias,
                                       gfloat                threshold,
                                       gboolean              select_transparent,
                                       GimpSelectCriterion   select_criterion,
                                       const GimpRGB        *color)
{
  /*  Scan over the image's active layer, finding pixels within the
   *  specified threshold from the given R, G, & B values.  If
   *  antialiasing is on, use the same antialiasing scheme as in
   *  fuzzy_select.  Modify the image's mask to reflect the
   *  additional selection
   */
  GeglBufferIterator *iter;
  GimpPickable       *pickable;
  GeglBuffer         *src_buffer;
  GeglBuffer         *mask_buffer;
  const Babl         *format;
  gint                n_components;
  gboolean            has_alpha;
  gfloat              start_col[MAX_CHANNELS];

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (color != NULL, NULL);

  if (sample_merged)
    pickable = GIMP_PICKABLE (image);
  else
    pickable = GIMP_PICKABLE (drawable);

  gimp_pickable_flush (pickable);

  src_buffer = gimp_pickable_get_buffer (pickable);

  format = choose_format (src_buffer, select_criterion,
                          &n_components, &has_alpha);

  gimp_rgba_get_pixel (color, format, start_col);

  if (has_alpha)
    {
      if (select_transparent)
        {
          /*  don't select transparancy if "color" isn't fully transparent
           */
          if (start_col[n_components - 1] > 0.0)
            select_transparent = FALSE;
        }
    }
  else
    {
      select_transparent = FALSE;
    }

  mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer),
                                 babl_format ("Y float"));

  iter = gegl_buffer_iterator_new (src_buffer,
                                   NULL, 0, format,
                                   GEGL_BUFFER_READ, GEGL_ABYSS_NONE);

  gegl_buffer_iterator_add (iter, mask_buffer,
                            NULL, 0, babl_format ("Y float"),
                            GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);

  while (gegl_buffer_iterator_next (iter))
    {
      const gfloat *src   = iter->data[0];
      gfloat       *dest  = iter->data[1];
      gint          count = iter->length;

      while (count--)
        {
          /*  Find how closely the colors match  */
          *dest = pixel_difference (start_col, src,
                                    antialias,
                                    threshold,
                                    n_components,
                                    has_alpha,
                                    select_transparent,
                                    select_criterion);

          src  += n_components;
          dest += 1;
        }
    }

  return mask_buffer;
}
static gboolean
find_contiguous_segment (const gfloat        *col,
                         GeglBuffer          *src_buffer,
                         GeglBuffer          *mask_buffer,
                         const Babl          *format,
                         gint                 n_components,
                         gboolean             has_alpha,
                         gint                 width,
                         gboolean             select_transparent,
                         GimpSelectCriterion  select_criterion,
                         gboolean             antialias,
                         gfloat               threshold,
                         gint                 initial_x,
                         gint                 initial_y,
                         gint                *start,
                         gint                *end)
{
  gfloat s[MAX_CHANNELS];
  gfloat mask_row[width];
  gfloat diff;

  gegl_buffer_sample (src_buffer, initial_x, initial_y, NULL, s, format,
                      GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

  diff = pixel_difference (col, s, antialias, threshold,
                           n_components, has_alpha, select_transparent,
                           select_criterion);

  /* check the starting pixel */
  if (! diff)
    return FALSE;

  mask_row[initial_x] = diff;

  *start = initial_x - 1;

  while (*start >= 0 && diff)
    {
      gegl_buffer_sample (src_buffer, *start, initial_y, NULL, s, format,
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      diff = pixel_difference (col, s, antialias, threshold,
                               n_components, has_alpha, select_transparent,
                               select_criterion);

      mask_row[*start] = diff;

      if (diff)
        (*start)--;
    }

  diff = 1;
  *end = initial_x + 1;

  while (*end < width && diff)
    {
      gegl_buffer_sample (src_buffer, *end, initial_y, NULL, s, format,
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      diff = pixel_difference (col, s, antialias, threshold,
                               n_components, has_alpha, select_transparent,
                               select_criterion);

      mask_row[*end] = diff;

      if (diff)
        (*end)++;
    }

  gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start, initial_y,
                                                *end - *start, 1),
                   0, babl_format ("Y float"), &mask_row[*start],
                   GEGL_AUTO_ROWSTRIDE);

  return TRUE;
}
static gboolean
find_contiguous_segment (const gfloat        *col,
                         GeglBuffer          *src_buffer,
                         GeglBuffer          *mask_buffer,
                         const Babl          *format,
                         gint                 n_components,
                         gboolean             has_alpha,
                         gint                 width,
                         gboolean             select_transparent,
                         GimpSelectCriterion  select_criterion,
                         gboolean             antialias,
                         gfloat               threshold,
                         gint                 initial_x,
                         gint                 initial_y,
                         gint                *start,
                         gint                *end,
                         gfloat              *row)
{
  gfloat *s;
  gfloat  mask_row[width];
  gfloat  diff;

#ifdef FETCH_ROW
  gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, initial_y, width, 1), 1.0,
                   format,
                   row, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
  s = row + initial_x * n_components;
#else
  s = g_alloca (n_components * sizeof (gfloat));

  gegl_buffer_sample (src_buffer, initial_x, initial_y, NULL, s, format,
                      GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
#endif

  diff = pixel_difference (col, s, antialias, threshold,
                           n_components, has_alpha, select_transparent,
                           select_criterion);

  /* check the starting pixel */
  if (! diff)
    return FALSE;

  mask_row[initial_x] = diff;

  *start = initial_x - 1;
#ifdef FETCH_ROW
  s = row + *start * n_components;
#endif

  while (*start >= 0 && diff)
    {
#ifndef FETCH_ROW
      gegl_buffer_sample (src_buffer, *start, initial_y, NULL, s, format,
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
#endif

      diff = pixel_difference (col, s, antialias, threshold,
                               n_components, has_alpha, select_transparent,
                               select_criterion);

      mask_row[*start] = diff;

      if (diff)
        {
          (*start)--;
#ifdef FETCH_ROW
          s -= n_components;
#endif
        }
    }

  diff = 1;
  *end = initial_x + 1;
#ifdef FETCH_ROW
  s = row + *end * n_components;
#endif

  while (*end < width && diff)
    {
#ifndef FETCH_ROW
      gegl_buffer_sample (src_buffer, *end, initial_y, NULL, s, format,
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
#endif

      diff = pixel_difference (col, s, antialias, threshold,
                               n_components, has_alpha, select_transparent,
                               select_criterion);

      mask_row[*end] = diff;

      if (diff)
        {
          (*end)++;
#ifdef FETCH_ROW
          s += n_components;
#endif
        }
    }

  gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start, initial_y,
                                                *end - *start, 1),
                   0, babl_format ("Y float"), &mask_row[*start],
                   GEGL_AUTO_ROWSTRIDE);

  return TRUE;
}