[gegl] buffer: stack frame overhead optimizations



commit 07dc61e1a9ff994ea9b58ab0cf0cc8f7d557f82d
Author: Øyvind Kolås <pippin gimp org>
Date:   Wed Jun 18 18:27:30 2014 +0200

    buffer: stack frame overhead optimizations
    
    When using gegl_buffer_get / gegl_buffer_set and gegl_buffer_sample for
    small/1x1px sized rectangles the overhead of each involved function call
    amounts to more than 2% of time spend processing.
    
    This refactoring involves moving gegl_buffer_sample from gegl-buffer.c to
    gegl-sampler.c to make them live in the same compilation unit. Replacing
    internal nested chains of function calls with added complexity with an
    inlinable implementation using switches.

 gegl/buffer/gegl-buffer-access.c   |  332 ++++++++++++++++++++++++++----------
 gegl/buffer/gegl-buffer-iterator.c |    1 +
 gegl/buffer/gegl-buffer-private.h  |    7 +-
 gegl/buffer/gegl-buffer.c          |    5 +-
 gegl/buffer/gegl-buffer.h          |   11 --
 gegl/buffer/gegl-sampler-nearest.c |   52 ++++---
 gegl/buffer/gegl-sampler.c         |   51 ++++++-
 7 files changed, 328 insertions(+), 131 deletions(-)
---
diff --git a/gegl/buffer/gegl-buffer-access.c b/gegl/buffer/gegl-buffer-access.c
index aa1474c..5158254 100644
--- a/gegl/buffer/gegl-buffer-access.c
+++ b/gegl/buffer/gegl-buffer-access.c
@@ -56,7 +56,7 @@ static void gegl_buffer_iterate_read_dispatch (GeglBuffer          *buffer,
                                                gint                 level,
                                                GeglAbyssPolicy      repeat_mode);
 
-static void
+static void inline
 gegl_buffer_get_pixel (GeglBuffer     *buffer,
                        gint            x,
                        gint            y,
@@ -162,6 +162,130 @@ gegl_buffer_get_pixel (GeglBuffer     *buffer,
   }
 }
 
+static inline void
+__gegl_buffer_set_pixel (GeglBuffer     *buffer,
+                       gint            x,
+                       gint            y,
+                       const Babl     *format,
+                       gconstpointer   data)
+{
+  const GeglRectangle *abyss = &buffer->abyss;
+  const guchar        *buf   = data;
+
+  if (y <  abyss->y ||
+      x <  abyss->x ||
+      y >= abyss->y + abyss->height ||
+      x >= abyss->x + abyss->width)
+    return;
+
+  {
+    gint tile_width  = buffer->tile_width;
+    gint tile_height = buffer->tile_height;
+    gint tiledy      = y + buffer->shift_y;
+    gint tiledx      = x + buffer->shift_x;
+    gint indice_x    = gegl_tile_indice (tiledx, tile_width);
+    gint indice_y    = gegl_tile_indice (tiledy, tile_height);
+
+    GeglTile *tile = buffer->tile_storage->hot_tile;
+    const Babl *fish = NULL;
+    gint px_size;
+
+    if (format != buffer->soft_format)
+      {
+        fish    = babl_fish (format, buffer->soft_format);
+        px_size = babl_format_get_bytes_per_pixel (buffer->soft_format);
+      }
+    else
+      {
+        px_size = babl_format_get_bytes_per_pixel (buffer->soft_format);
+      }
+
+    if (!(tile &&
+          tile->x == indice_x &&
+          tile->y == indice_y))
+      {
+        _gegl_buffer_drop_hot_tile (buffer);
+        tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
+                                          indice_x, indice_y,
+                                          0);
+        buffer->tile_storage->hot_tile = tile;
+      }
+
+    if (tile)
+      {
+        gint tile_origin_x = indice_x * tile_width;
+        gint tile_origin_y = indice_y * tile_height;
+        gint       offsetx = tiledx - tile_origin_x;
+        gint       offsety = tiledy - tile_origin_y;
+
+        guchar *tp;
+        gegl_tile_lock (tile);
+        tp = gegl_tile_get_data (tile) + (offsety * tile_width + offsetx) * px_size;
+
+        if (fish)
+          babl_process (fish, buf, tp, 1);
+        else
+          memcpy (tp, buf, px_size);
+
+        gegl_tile_unlock (tile);
+      }
+  }
+}
+
+enum _GeglBufferSetFlag {
+  GEGL_BUFFER_SET_FLAG_FAST   = 0,
+  GEGL_BUFFER_SET_FLAG_LOCK   = 1<<0,
+  GEGL_BUFFER_SET_FLAG_NOTIFY = 1<<2,
+  GEGL_BUFFER_SET_FLAG_FULL   = GEGL_BUFFER_SET_FLAG_LOCK |
+                                GEGL_BUFFER_SET_FLAG_NOTIFY
+};
+
+typedef enum _GeglBufferSetFlag GeglBufferSetFlag;
+
+void
+gegl_buffer_set_with_flags (GeglBuffer          *buffer,
+                            const GeglRectangle *rect,
+                            gint                 level,
+                            const Babl          *format,
+                            const void          *src,
+                            gint                 rowstride,
+                            GeglBufferSetFlag    set_flags);
+
+
+static inline void
+_gegl_buffer_set_pixel (GeglBuffer       *buffer,
+                        gint              x,
+                        gint              y,
+                        const Babl       *format,
+                        gconstpointer     data,
+                        GeglBufferSetFlag flags)
+{
+  GeglRectangle rect = {x,y,1,1};
+  switch (flags)
+  {
+    case GEGL_BUFFER_SET_FLAG_FAST:
+      __gegl_buffer_set_pixel (buffer, x, y, format, data);
+    break;
+    case GEGL_BUFFER_SET_FLAG_LOCK:
+      gegl_buffer_lock (buffer);
+      __gegl_buffer_set_pixel (buffer, x, y, format, data);
+      gegl_buffer_unlock (buffer);
+      break;
+    case GEGL_BUFFER_SET_FLAG_NOTIFY:
+      __gegl_buffer_set_pixel (buffer, x, y, format, data);
+      if (flags & GEGL_BUFFER_SET_FLAG_NOTIFY)
+        gegl_buffer_emit_changed_signal(buffer, &rect);
+      break;
+    case GEGL_BUFFER_SET_FLAG_LOCK|GEGL_BUFFER_SET_FLAG_NOTIFY:
+    default:
+      gegl_buffer_lock (buffer);
+      __gegl_buffer_set_pixel (buffer, x, y, format, data);
+      gegl_buffer_unlock (buffer);
+      gegl_buffer_emit_changed_signal(buffer, &rect);
+      break;
+  }
+}
+
 /* flush any unwritten data (flushes the hot-cache of a single
  * tile used by gegl_buffer_set for 1x1 pixel sized rectangles
  */
@@ -182,8 +306,6 @@ gegl_buffer_flush (GeglBuffer *buffer)
                             GEGL_TILE_FLUSH, 0,0,0,NULL);
 }
 
-
-
 static inline void
 gegl_buffer_iterate_write (GeglBuffer          *buffer,
                            const GeglRectangle *roi,
@@ -346,6 +468,76 @@ gegl_buffer_iterate_write (GeglBuffer          *buffer,
     }
 }
 
+static inline void
+gegl_buffer_set_internal (GeglBuffer          *buffer,
+                          const GeglRectangle *rect,
+                          gint                 level,
+                          const Babl          *format,
+                          const void          *src,
+                          gint                 rowstride)
+{
+  if (gegl_cl_is_accelerated ())
+    {
+      gegl_buffer_cl_cache_flush (buffer, rect);
+    }
+
+  gegl_buffer_iterate_write (buffer, rect, (void *) src, rowstride, format, 0);
+
+  if (gegl_buffer_is_shared (buffer))
+    {
+      gegl_buffer_flush (buffer);
+    }
+}
+
+static void inline
+_gegl_buffer_set_with_flags (GeglBuffer       *buffer,
+                            const GeglRectangle *rect,
+                            gint                 level,
+                            const Babl          *format,
+                            const void          *src,
+                            gint                 rowstride,
+                            GeglBufferSetFlag    flags)
+{
+  switch (flags)
+  {
+    case GEGL_BUFFER_SET_FLAG_FAST:
+      gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
+    break;
+    case GEGL_BUFFER_SET_FLAG_LOCK:
+      gegl_buffer_lock (buffer);
+      gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
+      gegl_buffer_unlock (buffer);
+      break;
+    case GEGL_BUFFER_SET_FLAG_NOTIFY:
+      gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
+      if (flags & GEGL_BUFFER_SET_FLAG_NOTIFY)
+        gegl_buffer_emit_changed_signal(buffer, rect);
+      break;
+    case GEGL_BUFFER_SET_FLAG_LOCK|GEGL_BUFFER_SET_FLAG_NOTIFY:
+    default:
+      gegl_buffer_lock (buffer);
+      gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
+      gegl_buffer_unlock (buffer);
+      gegl_buffer_emit_changed_signal(buffer, rect);
+      break;
+  }
+}
+
+void
+gegl_buffer_set_with_flags (GeglBuffer       *buffer,
+                            const GeglRectangle *rect,
+                            gint                 level,
+                            const Babl          *format,
+                            const void          *src,
+                            gint                 rowstride,
+                            GeglBufferSetFlag    flags)
+{
+  g_return_if_fail (GEGL_IS_BUFFER (buffer));
+  if (format == NULL)
+    format = buffer->soft_format;
+  _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride, flags);
+}
+
 static void
 gegl_buffer_iterate_read_simple (GeglBuffer          *buffer,
                                  const GeglRectangle *roi,
@@ -1089,38 +1281,28 @@ gegl_buffer_iterate_read_dispatch (GeglBuffer          *buffer,
 void
 gegl_buffer_set_unlocked (GeglBuffer          *buffer,
                           const GeglRectangle *rect,
+                          gint                 level,
                           const Babl          *format,
                           const void          *src,
                           gint                 rowstride)
 {
-    gegl_buffer_set_unlocked_no_notify(buffer, rect, format, src, rowstride);
-    gegl_buffer_emit_changed_signal(buffer, rect);
+  _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
+                               GEGL_BUFFER_SET_FLAG_NOTIFY);
 }
 
-
 void
 gegl_buffer_set_unlocked_no_notify (GeglBuffer          *buffer,
                                     const GeglRectangle *rect,
+                                    gint                 level,
                                     const Babl          *format,
                                     const void          *src,
                                     gint                 rowstride)
 {
-  if (format == NULL)
-    format = buffer->soft_format;
-
-  if (gegl_cl_is_accelerated ())
-    {
-      gegl_buffer_cl_cache_flush (buffer, rect);
-    }
-
-  gegl_buffer_iterate_write (buffer, rect, (void *) src, rowstride, format, 0);
-
-  if (gegl_buffer_is_shared (buffer))
-    {
-      gegl_buffer_flush (buffer);
-    }
+  _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
+                               GEGL_BUFFER_SET_FLAG_FAST);
 }
 
+
 void
 gegl_buffer_set (GeglBuffer          *buffer,
                  const GeglRectangle *rect,
@@ -1130,10 +1312,16 @@ gegl_buffer_set (GeglBuffer          *buffer,
                  gint                 rowstride)
 {
   g_return_if_fail (GEGL_IS_BUFFER (buffer));
+  if (format == NULL)
+    format = buffer->soft_format;
 
-  gegl_buffer_lock (buffer);
-  gegl_buffer_set_unlocked (buffer, rect, format, src, rowstride);
-  gegl_buffer_unlock (buffer);
+  if (rect && (rect->width == 1 && rect->height == 1))
+      _gegl_buffer_set_pixel (buffer, rect->x, rect->y, format, src,
+                              GEGL_BUFFER_SET_FLAG_LOCK|GEGL_BUFFER_SET_FLAG_NOTIFY);
+  else
+    _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
+                                 GEGL_BUFFER_SET_FLAG_LOCK|
+                                 GEGL_BUFFER_SET_FLAG_NOTIFY);
 }
 
 /* Expand roi by scale so it uncludes all pixels needed
@@ -1172,36 +1360,33 @@ _gegl_get_required_for_scale (const Babl          *format,
       }
 }
 
-void
-gegl_buffer_get_unlocked (GeglBuffer          *buffer,
-                          gdouble              scale,
-                          const GeglRectangle *rect,
-                          const Babl          *format,
-                          gpointer             dest_buf,
-                          gint                 rowstride,
-                          GeglAbyssPolicy      repeat_mode)
+static inline void
+_gegl_buffer_get_unlocked (GeglBuffer          *buffer,
+                           gdouble              scale,
+                           const GeglRectangle *rect,
+                           const Babl          *format,
+                           gpointer             dest_buf,
+                           gint                 rowstride,
+                           GeglAbyssPolicy      repeat_mode)
 {
   g_return_if_fail (scale > 0.0);
 
   if (format == NULL)
     format = buffer->soft_format;
 
-#if 1
-  /* not thread-safe */
+  if (gegl_cl_is_accelerated ())
+    {
+      gegl_buffer_cl_cache_flush (buffer, rect);
+    }
+
   if (scale == 1.0 &&
       rect &&
       rect->width == 1 &&
-      rect->height == 1)  /* fast path */
+      rect->height == 1)
     {
       gegl_buffer_get_pixel (buffer, rect->x, rect->y, format, dest_buf, repeat_mode);
       return;
     }
-#endif
-
-  if (gegl_cl_is_accelerated ())
-    {
-      gegl_buffer_cl_cache_flush (buffer, rect);
-    }
 
   if (!rect && scale == 1.0)
     {
@@ -1314,6 +1499,18 @@ gegl_buffer_get_unlocked (GeglBuffer          *buffer,
 }
 
 void
+gegl_buffer_get_unlocked (GeglBuffer          *buffer,
+                          gdouble              scale,
+                          const GeglRectangle *rect,
+                          const Babl          *format,
+                          gpointer             dest_buf,
+                          gint                 rowstride,
+                          GeglAbyssPolicy      repeat_mode)
+{
+  return _gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride, repeat_mode);
+}
+
+void
 gegl_buffer_get (GeglBuffer          *buffer,
                  const GeglRectangle *rect,
                  gdouble              scale,
@@ -1323,58 +1520,9 @@ gegl_buffer_get (GeglBuffer          *buffer,
                  GeglAbyssPolicy      repeat_mode)
 {
   g_return_if_fail (GEGL_IS_BUFFER (buffer));
-  gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride, repeat_mode);
-}
-
-GType
-gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type);
-
-void
-gegl_buffer_sample (GeglBuffer       *buffer,
-                    gdouble           x,
-                    gdouble           y,
-                    GeglMatrix2      *scale,
-                    gpointer          dest,
-                    const Babl       *format,
-                    GeglSamplerType   sampler_type,
-                    GeglAbyssPolicy   repeat_mode)
-{
-  GType desired_type;
-
-  if (!format)
-    format = buffer->soft_format;
-
-  if (sampler_type == GEGL_SAMPLER_NEAREST)
-    {
-      /* XXX: not thread safe */
-      gegl_buffer_get_pixel (buffer, x, y, format, dest, repeat_mode);
-      return;
-    }
-
-  desired_type = gegl_sampler_gtype_from_enum (sampler_type);
-
-  /* unset the cached sampler if it dosn't match the needs */
-  if (buffer->sampler != NULL &&
-     (!G_TYPE_CHECK_INSTANCE_TYPE (buffer->sampler, desired_type) ||
-       buffer->sampler_format != format
-      ))
-    {
-      g_object_unref (buffer->sampler);
-      buffer->sampler = NULL;
-    }
-
-  /* look up appropriate sampler,. */
-  if (buffer->sampler == NULL)
-    {
-      buffer->sampler = g_object_new (desired_type,
-                                      "buffer", buffer,
-                                      "format", format,
-                                      NULL);
-      buffer->sampler_format = format;
-      gegl_sampler_prepare (buffer->sampler);
-    }
-
-  gegl_sampler_get (buffer->sampler, x, y, scale, dest, repeat_mode);
+  gegl_buffer_lock (buffer);
+  _gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride, repeat_mode);
+  gegl_buffer_unlock (buffer);
 }
 
 void
diff --git a/gegl/buffer/gegl-buffer-iterator.c b/gegl/buffer/gegl-buffer-iterator.c
index 6510d30..456e805 100644
--- a/gegl/buffer/gegl-buffer-iterator.c
+++ b/gegl/buffer/gegl-buffer-iterator.c
@@ -183,6 +183,7 @@ release_tile (GeglBufferIterator *iter,
         {
           gegl_buffer_set_unlocked_no_notify (sub->buffer,
                                               &sub->real_roi,
+                                              0, /* level */
                                               sub->format,
                                               sub->real_data,
                                               GEGL_AUTO_ROWSTRIDE);
diff --git a/gegl/buffer/gegl-buffer-private.h b/gegl/buffer/gegl-buffer-private.h
index b5dbd71..d51394d 100644
--- a/gegl/buffer/gegl-buffer-private.h
+++ b/gegl/buffer/gegl-buffer-private.h
@@ -71,9 +71,10 @@ struct _GeglBuffer
                                           useful for debugging */
   gint              alloc_stack_size;
 
-  GeglTileBackend  *backend;
+  gint              changed_signal_connections; /* to avoid firing changed signals
+                                                   with no listeners */
 
-  gint              changed_signal_connections;
+  GeglTileBackend  *backend;
 };
 
 struct _GeglBufferClass
@@ -105,12 +106,14 @@ gboolean          gegl_buffer_unlock      (GeglBuffer *buffer);
 
 void              gegl_buffer_set_unlocked (GeglBuffer          *buffer,
                                             const GeglRectangle *rect,
+                                            gint                 level,
                                             const Babl          *format,
                                             const void          *src,
                                             gint                 rowstride);
 
 void              gegl_buffer_set_unlocked_no_notify (GeglBuffer          *buffer,
                                                       const GeglRectangle *rect,
+                                                      gint                 level,
                                                       const Babl          *format,
                                                       const void          *src,
                                                       gint                 rowstride);
diff --git a/gegl/buffer/gegl-buffer.c b/gegl/buffer/gegl-buffer.c
index 4124816..5a3db18 100644
--- a/gegl/buffer/gegl-buffer.c
+++ b/gegl/buffer/gegl-buffer.c
@@ -91,7 +91,7 @@ enum
   LAST_SIGNAL
 };
 
-guint gegl_buffer_signals[LAST_SIGNAL] = { 0 };
+static guint gegl_buffer_signals[LAST_SIGNAL] = { 0 };
 
 static void
 gegl_buffer_get_property (GObject    *gobject,
@@ -1179,5 +1179,4 @@ glong gegl_buffer_signal_connect (GeglBuffer *buffer,
 {
   buffer->changed_signal_connections++;
   return g_signal_connect(buffer, detailed_signal, c_handler, data);
-}
-
+} 
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index 22f9f06..1d9ff47 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -431,17 +431,6 @@ void              gegl_buffer_sample          (GeglBuffer       *buffer,
  */
 void            gegl_buffer_sample_cleanup    (GeglBuffer *buffer);
 
-
-
-/**
- * gegl_sampler_type_from_string:
- * @string: the string to look up
- *
- * Looks up the GeglInterpolation corresponding to a string, if no matching
- * interpolation is found GEGL_SAMPLER_NEAREST is returned.
- */
-GeglSamplerType gegl_sampler_type_from_string (const gchar *string);
-
 typedef void (*GeglSamplerGetFun)  (GeglSampler     *self,
                                     gdouble          x,
                                     gdouble          y,
diff --git a/gegl/buffer/gegl-sampler-nearest.c b/gegl/buffer/gegl-sampler-nearest.c
index e40a495..44eb5c7 100644
--- a/gegl/buffer/gegl-sampler-nearest.c
+++ b/gegl/buffer/gegl-sampler-nearest.c
@@ -84,6 +84,8 @@ gegl_sampler_get_pixel (GeglSampler    *sampler,
   const GeglRectangle *abyss = &buffer->abyss;
   guchar              *buf   = data;
 
+  gegl_buffer_lock (sampler->buffer);
+
   if (y <  abyss->y ||
       x <  abyss->x ||
       y >= abyss->y + abyss->height ||
@@ -162,19 +164,22 @@ gegl_sampler_get_pixel (GeglSampler    *sampler,
         babl_process (sampler->fish, tp, buf, 1);
       }
   }
+  gegl_buffer_unlock (sampler->buffer);
 }
 
 static void
-gegl_sampler_nearest_prepare (GeglSampler* restrict sampler)
+gegl_sampler_nearest_get_same_format  (      GeglSampler*    restrict  sampler,
+                                       const gdouble                   absolute_x,
+                                       const gdouble                   absolute_y,
+                                             GeglMatrix2              *scale,
+                                             void*           restrict  output,
+                                             GeglAbyssPolicy           repeat_mode)
 {
-  if (!sampler->buffer) /* this happens when querying the extent of a sampler */
-    return;
-  GEGL_SAMPLER_NEAREST (sampler)->buffer_bpp = babl_format_get_bytes_per_pixel (sampler->buffer->format);
-
-  sampler->fish = babl_fish (sampler->buffer->soft_format, sampler->format);
+  GeglRectangle rectangle = {floorf(absolute_x), floorf(absolute_y), 1, 1};
+  gegl_buffer_get (sampler->buffer, &rectangle, 1.0, NULL, output, GEGL_AUTO_ROWSTRIDE, repeat_mode);
 }
 
-void
+static void
 gegl_sampler_nearest_get (      GeglSampler*    restrict  sampler,
                           const gdouble                   absolute_x,
                           const gdouble                   absolute_y,
@@ -182,24 +187,11 @@ gegl_sampler_nearest_get (      GeglSampler*    restrict  sampler,
                                 void*           restrict  output,
                                 GeglAbyssPolicy           repeat_mode)
 {
-  /*
-   * The reason why floor of the absolute position gives the nearest
-   * pixel (with ties resolved toward -infinity) is that the absolute
-   * position is corner-based (origin at the top left corner of the
-   * pixel labeled (0,0)) so that the center of the top left pixel is
-   * located at (.5,.5) (instead of (0,0) as it would be if absolute
-   * positions were center-based).
-   */
 #if 1
-  return gegl_sampler_get_pixel (sampler,
+  gegl_sampler_get_pixel (sampler,
            floorf(absolute_x), floorf(absolute_y),
            output, repeat_mode);
 #else
-
-  /* this left in here; to make it easy to manually verify that the
-   * sampler framework behaves correctly.
-   */
-
   const gfloat* restrict in_bptr =
     gegl_sampler_get_ptr (sampler,
                           (gint) floor ((double) absolute_x),
@@ -208,3 +200,21 @@ gegl_sampler_nearest_get (      GeglSampler*    restrict  sampler,
   babl_process (sampler->fish, in_bptr, output, 1);
 #endif
 }
+
+
+static void
+gegl_sampler_nearest_prepare (GeglSampler* restrict sampler)
+{
+  if (!sampler->buffer) /* this happens when querying the extent of a sampler */
+    return;
+  GEGL_SAMPLER_NEAREST (sampler)->buffer_bpp = babl_format_get_bytes_per_pixel (sampler->buffer->format);
+
+  if (sampler->format == sampler->buffer->soft_format)
+    {
+      sampler->get = gegl_sampler_nearest_get_same_format;
+    }
+  else
+    {
+      sampler->fish = babl_fish (sampler->buffer->soft_format, sampler->format);
+    }
+}
diff --git a/gegl/buffer/gegl-sampler.c b/gegl/buffer/gegl-sampler.c
index 7a117c4..d4e9aa5 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -72,7 +72,7 @@ static void buffer_contents_changed (GeglBuffer          *buffer,
                                      const GeglRectangle *changed_rect,
                                      gpointer             userdata);
 
-GType gegl_sampler_gtype_from_enum  (GeglSamplerType      sampler_type);
+static GType gegl_sampler_gtype_from_enum  (GeglSamplerType      sampler_type);
 
 G_DEFINE_TYPE (GeglSampler, gegl_sampler, G_TYPE_OBJECT)
 
@@ -455,7 +455,7 @@ gegl_sampler_type_from_string (const gchar *string)
   return GEGL_SAMPLER_LINEAR;
 }
 
-GType
+static inline GType
 gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type)
 {
   switch (sampler_type)
@@ -475,6 +475,53 @@ gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type)
     }
 }
 
+void
+gegl_buffer_sample (GeglBuffer       *buffer,
+                    gdouble           x,
+                    gdouble           y,
+                    GeglMatrix2      *scale,
+                    gpointer          dest,
+                    const Babl       *format,
+                    GeglSamplerType   sampler_type,
+                    GeglAbyssPolicy   repeat_mode)
+{
+  GType desired_type;
+  if (sampler_type == GEGL_SAMPLER_NEAREST && format == buffer->soft_format)
+  {
+    GeglRectangle rect = {floorf (x), floorf(y), 1, 1};
+    gegl_buffer_get (buffer, &rect, 1, NULL, dest, GEGL_AUTO_ROWSTRIDE, repeat_mode);
+    return;
+  }
+
+  if (!format)
+    format = buffer->soft_format;
+
+  desired_type = gegl_sampler_gtype_from_enum (sampler_type);
+
+  /* unset the cached sampler if it dosn't match the needs */
+  if (buffer->sampler != NULL &&
+     (!G_TYPE_CHECK_INSTANCE_TYPE (buffer->sampler, desired_type) ||
+       buffer->sampler_format != format
+      ))
+    {
+      g_object_unref (buffer->sampler);
+      buffer->sampler = NULL;
+    }
+
+  /* look up appropriate sampler,. */
+  if (buffer->sampler == NULL)
+    {
+      buffer->sampler = g_object_new (desired_type,
+                                      "buffer", buffer,
+                                      "format", format,
+                                      NULL);
+      buffer->sampler_format = format;
+      gegl_sampler_prepare (buffer->sampler);
+    }
+
+  buffer->sampler->get(buffer->sampler, x, y, scale, dest, repeat_mode);
+}
+
 GeglSampler *
 gegl_buffer_sampler_new (GeglBuffer      *buffer,
                          const Babl      *format,


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