[gegl] Improved version of the Nohalobox samplers (downsize, downsharp, and downsmooth). A small improvemen



commit 3acebe93ee13f40c9c6c78250dd9aaac42fed504
Author: Adam Turcotte <adam turcotte gmail com>
Date:   Fri Aug 14 14:44:33 2009 -0400

    Improved version of the Nohalobox samplers (downsize, downsharp, and downsmooth). A small improvement for each sampler is being developed, and downsmooth will be re-written. This patch fixes some bugs that were re-instated by overriding a previous commit in the previous patch.

 gegl/buffer/Makefile.am               |    6 +
 gegl/buffer/gegl-buffer-access.c      |    3 +
 gegl/buffer/gegl-buffer.c             |    3 +
 gegl/buffer/gegl-buffer.h             |    3 +
 gegl/buffer/gegl-sampler-downsharp.c  |   26 +
 gegl/buffer/gegl-sampler-downsharp.h  |   48 ++
 gegl/buffer/gegl-sampler-downsize.c   |  943 +++++++++++++++++++++++++++++++++
 gegl/buffer/gegl-sampler-downsize.h   |   48 ++
 gegl/buffer/gegl-sampler-downsmooth.c |   26 +
 gegl/buffer/gegl-sampler-downsmooth.h |   48 ++
 gegl/buffer/gegl-sampler.c            |  310 +++++++-----
 gegl/buffer/gegl-sampler.h            |    1 +
 gegl/gegl-matrix.h                    |    1 +
 operations/affine/affine.c            |   10 +-
 14 files changed, 1351 insertions(+), 125 deletions(-)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 21c44a6..7b0eb22 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -33,6 +33,9 @@ libbuffer_la_SOURCES = \
     gegl-sampler-lanczos.c	\
     gegl-sampler-linear.c	\
     gegl-sampler-nearest.c	\
+    gegl-sampler-downsharp.c	\
+    gegl-sampler-downsize.c \
+    gegl-sampler-downsmooth.c	\
     gegl-sampler-sharp.c	\
     gegl-sampler-yafr.c		\
     gegl-region-generic.c	\
@@ -62,6 +65,9 @@ libbuffer_la_SOURCES = \
     gegl-sampler-lanczos.h	\
     gegl-sampler-linear.h	\
     gegl-sampler-nearest.h	\
+    gegl-sampler-downsharp.h	\
+    gegl-sampler-downsize.h \
+    gegl-sampler-downsmooth.h	\
     gegl-sampler-sharp.h	\
     gegl-sampler-yafr.h		\
     gegl-region.h		\
diff --git a/gegl/buffer/gegl-buffer-access.c b/gegl/buffer/gegl-buffer-access.c
index d8f4afb..b5d64f9 100644
--- a/gegl/buffer/gegl-buffer-access.c
+++ b/gegl/buffer/gegl-buffer-access.c
@@ -37,6 +37,9 @@
 #include "gegl-sampler-linear.h"
 #include "gegl-sampler-cubic.h"
 #include "gegl-sampler-lanczos.h"
+#include "gegl-sampler-downsharp.h"
+#include "gegl-sampler-downsize.h"
+#include "gegl-sampler-downsmooth.h"
 #include "gegl-sampler-sharp.h"
 #include "gegl-sampler-yafr.h"
 #include "gegl-buffer-index.h"
diff --git a/gegl/buffer/gegl-buffer.c b/gegl/buffer/gegl-buffer.c
index 3d9bbac..25b5aa6 100644
--- a/gegl/buffer/gegl-buffer.c
+++ b/gegl/buffer/gegl-buffer.c
@@ -63,6 +63,9 @@
 #include "gegl-sampler-linear.h"
 #include "gegl-sampler-cubic.h"
 #include "gegl-sampler-lanczos.h"
+#include "gegl-sampler-downsharp.h"
+#include "gegl-sampler-downsize.h"
+#include "gegl-sampler-downsmooth.h"
 #include "gegl-sampler-sharp.h"
 #include "gegl-sampler-yafr.h"
 #include "gegl-types-internal.h"
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index 3e1e620..1d0ff57 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -297,6 +297,9 @@ typedef enum {
   GEGL_INTERPOLATION_LINEAR,
   GEGL_INTERPOLATION_CUBIC,
   GEGL_INTERPOLATION_LANCZOS,
+  GEGL_INTERPOLATION_DOWNSHARP,
+  GEGL_INTERPOLATION_DOWNSIZE,
+  GEGL_INTERPOLATION_DOWNSMOOTH,
   GEGL_INTERPOLATION_SHARP,
   GEGL_INTERPOLATION_YAFR
 } GeglInterpolation;
diff --git a/gegl/buffer/gegl-sampler-downsharp.c b/gegl/buffer/gegl-sampler-downsharp.c
new file mode 100644
index 0000000..b766379
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsharp.c
@@ -0,0 +1,26 @@
+/* This file is part of 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/>.
+ *
+ * 2009 (c) Adam Turcotte and Nicolas Robidoux.
+ */
+
+/*
+ * The following #define sets the stage for the compilation of
+ * Downsharp from the gegl-sampler-downsize.c source code:
+ */
+#define __GEGL_SAMPLER_DOWNSHARP_C__
+#include "gegl-sampler-downsize.c"
+#undef __GEGL_SAMPLER_DOWNSHARP_C__
diff --git a/gegl/buffer/gegl-sampler-downsharp.h b/gegl/buffer/gegl-sampler-downsharp.h
new file mode 100644
index 0000000..d59060e
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsharp.h
@@ -0,0 +1,48 @@
+/* This file is part of 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/>.
+ *
+ */
+#ifndef __GEGL_SAMPLER_DOWNSHARP_H__
+#define __GEGL_SAMPLER_DOWNSHARP_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSHARP            (gegl_sampler_downsharp_get_type ())
+#define GEGL_SAMPLER_DOWNSHARP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSHARP, GeglSamplerDownsharp))
+#define GEGL_SAMPLER_DOWNSHARP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_SAMPLER_DOWNSHARP, GeglSamplerDownsharpClass))
+#define GEGL_IS_SAMPLER_DOWNSHARP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSHARP))
+#define GEGL_IS_SAMPLER_DOWNSHARP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_SAMPLER_DOWNSHARP))
+#define GEGL_SAMPLER_DOWNSHARP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_SAMPLER_DOWNSHARP, GeglSamplerDownsharpClass))
+
+typedef struct _GeglSamplerDownsharp      GeglSamplerDownsharp;
+typedef struct _GeglSamplerDownsharpClass GeglSamplerDownsharpClass;
+
+struct _GeglSamplerDownsharp
+{
+  GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsharpClass
+{
+  GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsharp_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-downsize.c b/gegl/buffer/gegl-sampler-downsize.c
new file mode 100644
index 0000000..4c54ff1
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsize.c
@@ -0,0 +1,943 @@
+/* This file is part of 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/>.
+ *
+ * 2009 (c) Adam Turcotte, Nicolas Robidoux and �yvind Kolås.
+ */
+
+/*
+ * Acknowledgements: Adam Turcotte's GEGL Nohalobox programming funded
+ * by Google Summer of Code 2009.  Nicolas Robidoux and Adam
+ * Turcotte's research on Nohalobox funded in part by NSERC (National
+ * Science and Engineering Research Council of Canada) Discovery and
+ * USRA grants.
+ *
+ * Nicolas Robidoux thanks Minglun Gong, Ralf Meyer, John Cupitt,
+ * Geert Jordaens and Sven Neumann for useful comments and code, with
+ * special thanks to Chantal Racette for exploring various ways of
+ * getting a rectangle with axes parallel to the axes from the affine
+ * preimage of a square with axes parallel to the axes.
+ *
+ */
+#include "config.h"
+#include <math.h>
+#include <glib-object.h>
+
+/*
+ * This file implements three versions of Nohalobox, an exact area
+ * resampler which consists of averaging the piecewise linear "minmod"
+ * intensity surface over a rectangle with sides parallel to the axes.
+ *
+ * All three methods are tuned for transformations in which
+ * downsampling is more typical than upsampling, although they give
+ * good results for general warps.
+ *
+ * If __GEGL_SAMPLER_DOWNSHARP_C__ is defined, the Downsharp method,
+ * in which the averaging rectangle has sides 1/sqrt(2) of those of
+ * the Downsmooth rectangle (again enforcing that the sides are never
+ * smaller than 1). This ensures that if all singular values of the
+ * Jacobian matrix of the transformation are at least 1 (upsampling,
+ * rotations, shears etc), the resampler is interpolatory.
+ *
+ * If __GEGL_SAMPLER_DOWNSMOOTH_C__ is defined (and
+ * __GEGL_SAMPLER_DOWNSHARP_C__ is undefined), the Downsmooth method
+ * is implemented. In this method, the averaging rectangle (the "box")
+ * has sides just large enough to contain the preimage of a pixel area
+ * in the output image (as approximated with the inverse Jacobian
+ * matrix of the applied transformation), with the caveat that the
+ * sides are never smaller than 1. This ensures that if downsampling,
+ * the scheme is interpolatory. The scheme, however, is not
+ * interpolatory if there is rotation involved, or if one or more of
+ * the singular values is larger than 1. This version of Downsmooth is
+ * transitional: see FUTURE IMPROVEMENTS below.
+ *
+ * If __GEGL_SAMPLER_DOWNSMOOTH_C__ and __GEGL_SAMPLER_DOWNSHARP_C__
+ * are undefined, the Downsize method is implemented. For this method,
+ * the size of the averaging rectangle is a compromise between the
+ * "sharp" and "smooth" versions.
+ *
+ * Downsmooth is smoother but more blurry and slower, Downsharp is
+ * sharper and faster but shows more aliasing, and Downsize is a
+ * halfway between them.
+ *
+ * ===================
+ * FUTURE IMPROVEMENTS
+ * ===================
+ *
+ * All three methods will be improved so that they better handle
+ * downsampling ratios larger than about 60 by branching and using
+ * multiple gegl_sampler_get_ptr calls if necessary.
+ *
+ * Downsmooth can be improved so that it is less blurry but equally
+ * antialiased (and faster) by averaging on a region shaped like a
+ * stubby plus sign instead of a rectangle. Also, some of the whiles
+ * could be changed to do whiles for this method.
+ */
+
+#include "gegl.h"
+#include "gegl-types-internal.h"
+#include "gegl-buffer-private.h"
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+#include "gegl-sampler-downsharp.h"
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+#include "gegl-sampler-downsmooth.h"
+#else
+#include "gegl-sampler-downsize.h"
+#endif
+#include "gegl-matrix.h"
+
+static inline gfloat
+fast_minmod( const gfloat first, const gfloat second, const gfloat third )
+{
+  const gfloat m1 = second - first;
+  const gfloat m2 = third - second;
+  const gfloat m1_m1 = m1 * m1;
+  const gfloat m1_m2 = m1 * m2;
+  return ( m1_m2>=0.f ? 1.f : 0.f ) * ( m1_m1<=m1_m2 ? m1 : m2 );
+}
+
+/*
+ * FAST_PSEUDO_FLOOR is a floor replacement which has been found to be
+ * faster. It returns the floor of its argument unless the argument is
+ * a negative integer, in which case it returns one less than the
+ * floor. For example:
+ *
+ * FAST_PSEUDO_FLOOR(0.5) = 0
+ *
+ * FAST_PSEUDO_FLOOR(0.) = 0
+ *
+ * FAST_PSEUDO_FLOOR(-.5) = -1
+ *
+ * as expected, but
+ *
+ * FAST_PSEUDO_FLOOR(-1.) = -2
+ *
+ * The discontinuities of FAST_PSEUDO_FLOOR are on the right of
+ * negative numbers instead of on the left as is the case for floor.
+ */
+#define FAST_PSEUDO_FLOOR(x) ( (int)(x) - ( (x) < 0. ) )
+
+/*
+ * Hack to get the restrict C99 keyword going at least some of the
+ * time:
+ */
+#ifndef restrict
+#ifdef __restrict
+#define restrict __restrict
+#else
+#ifdef __restrict__
+#define restrict __restrict__
+#else
+#define restrict
+#endif
+#endif
+#endif
+
+enum
+{
+  PROP_0,
+  PROP_LAST
+};
+
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+static void gegl_sampler_downsharp_get  (      GeglSampler* self,
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+static void gegl_sampler_downsmooth_get (      GeglSampler* self,
+#else
+static void gegl_sampler_downsize_get   (      GeglSampler* self,
+#endif
+                                         const gdouble      absolute_x,
+                                         const gdouble      absolute_y,
+                                               void*        output );
+
+static void set_property (      GObject*    object,
+                                guint       property_id,
+                          const GValue*     value,
+                                GParamSpec* pspec);
+
+static void get_property (GObject*    object,
+                          guint       property_id,
+                          GValue*     value,
+                          GParamSpec* pspec);
+
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+G_DEFINE_TYPE (GeglSamplerDownsharp,
+               gegl_sampler_downsharp,
+               GEGL_TYPE_SAMPLER)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+G_DEFINE_TYPE (GeglSamplerDownsmooth,
+               gegl_sampler_downsmooth,
+               GEGL_TYPE_SAMPLER)
+#else
+G_DEFINE_TYPE (GeglSamplerDownsize,
+               gegl_sampler_downsize,
+               GEGL_TYPE_SAMPLER)
+#endif
+
+static GObjectClass * parent_class = NULL;
+
+static void
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+gegl_sampler_downsharp_class_init  (GeglSamplerDownsharpClass  *klass)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+gegl_sampler_downsmooth_class_init (GeglSamplerDownsmoothClass *klass)
+#else
+gegl_sampler_downsize_class_init   (GeglSamplerDownsizeClass *klass)
+#endif
+{
+  GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  parent_class               = g_type_class_peek_parent (klass);
+  object_class->set_property = set_property;
+  object_class->get_property = get_property;
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+  sampler_class->get = gegl_sampler_downsharp_get;
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+  sampler_class->get = gegl_sampler_downsmooth_get;
+#else
+  sampler_class->get = gegl_sampler_downsize_get;
+#endif
+}
+
+static void
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+gegl_sampler_downsharp_init  (GeglSamplerDownsharp  *self)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+gegl_sampler_downsmooth_init (GeglSamplerDownsmooth *self)
+#else
+gegl_sampler_downsize_init   (GeglSamplerDownsize   *self)
+#endif
+{
+  /*
+   * ===================
+   * UNSATISFACTORY HACK
+   * ===================
+   *
+   * The stencil offsets context_rect.x and context.y are set to -2 even
+   * though -1 should be enough. When the GEGL affine code gets cleaned
+   * up, this hack may not be necessary: Set SAFETY_OFFSET to 0.
+   */
+  #define SAFETY_OFFSET 1
+
+  /*
+   * The width and height are very likely to be modified by
+   * downsharp/size/smooth at runtime.
+   *
+   * The offsets should be -1---because one more pixel is needed past
+   * the first (and last) intersected pixel area---but they are set to
+   * -2 for safety. For mysterious reasons (having to do with negative
+   * locations near zero?), -1 does not behave quite as expected. If
+   * and when GEGL has more robust variable context_rect handling, it
+   * could be brought back to -1.
+   */
+  GEGL_SAMPLER (self)->context_rect.x      = -1 - SAFETY_OFFSET;
+  GEGL_SAMPLER (self)->context_rect.y      = -1 - SAFETY_OFFSET;
+  GEGL_SAMPLER (self)->context_rect.width  =  4 + SAFETY_OFFSET;
+  GEGL_SAMPLER (self)->context_rect.height =  4 + SAFETY_OFFSET;
+  GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
+}
+
+static void
+#if   defined (__GEGL_SAMPLER_DOWNSHARP_C__)
+gegl_sampler_downsharp_get  (      GeglSampler* self,
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTH_C__)
+gegl_sampler_downsmooth_get (      GeglSampler* self,
+#else
+gegl_sampler_downsize_get   (      GeglSampler* self,
+#endif
+                             const gdouble      absolute_x,
+                             const gdouble      absolute_y,
+                                   void*        output)
+{
+  /*
+   * Needed constants related to the input pixel value pointer
+   * provided by gegl_sampler_get_ptr (self, ix,
+   * iy). pixels_per_fetched_row corresponds to fetch_rectangle.width
+   * in gegl_sampler_get_ptr.
+   *
+   * Note that the following code does loop unrolling. If channels is
+   * changed from 4, significant but trivial mods will be necessary.
+   */
+  const gint channels = 4;
+  const gint pixels_per_fetched_row = 64;
+
+  /*
+   * For affine operations, the following code, which computes the
+   * averaging box from the inverse Jacobian matrix, is kind of a
+   * waste because the Jacobian, and consequently the computed
+   * rectangle, are constant---that is, they are the same for every
+   * pixel---yet the width and height of the integration rectangle is
+   * recomputed for every sampled location.
+   *
+   * The code is written this way so that it work for perspective and
+   * more general warps, for which the rectangle is location
+   * dependent.
+   */
+  /*
+   * POSSIBLE FUTURE IMPROVEMENT: Avoid recomputation, for every
+   * pixel, of half_width and half_height from inverse_jacobian, when
+   * inverse_jacobian is constant.
+   */
+  GeglMatrix2* const inverse_jacobian = self->inverse_jacobian;
+
+  const gdouble Jinv_11 = *inverse_jacobian[0][0];
+  const gdouble Jinv_12 = *inverse_jacobian[0][1];
+  const gdouble Jinv_21 = *inverse_jacobian[1][0];
+  const gdouble Jinv_22 = *inverse_jacobian[1][1];
+
+  /*
+   * Preimages of (1,1) (top right corner) and (1,-1) (bottom right
+   * corner). By symmetry, it is not necessary to compute the
+   * preimages of the other two corners.
+   */
+  const gdouble top_preimage_x    = Jinv_11 + Jinv_12;
+  const gdouble bottom_preimage_x = Jinv_11 - Jinv_12;
+  const gdouble top_preimage_y    = Jinv_21 + Jinv_22;
+  const gdouble bottom_preimage_y = Jinv_21 - Jinv_22;
+
+  const gdouble far_x =
+    ( top_preimage_x * top_preimage_x >= bottom_preimage_x * bottom_preimage_x
+      ? top_preimage_x : bottom_preimage_x );
+  const gdouble far_y =
+    ( top_preimage_y * top_preimage_y >= bottom_preimage_y * bottom_preimage_y
+      ? top_preimage_y : bottom_preimage_y );
+
+  /*
+   * These lengths define the smallest rectangle with sides parallel
+   * to the axes which contains the preimage of an output pixel area,
+   * scaled by 1/sqrt(2) in the case of Downsharp, fixed so that the
+   * width and height are at least 1. Note that this implies that,
+   * when downsampling, Downsharp typically fetches only about half
+   * the number of pixels fetched by Downsize; Downsmooth, about four
+   * times as many.
+   */
+  const gdouble scaled_half_preimage_width  =
+    ( far_x >= 0. ? far_x : -far_x )
+#if   defined __GEGL_SAMPLER_DOWNSHARP_C__
+    * ( .25 * sqrt(2.) );
+#elif defined __GEGL_SAMPLER_DOWNSMOOTH_C__
+    ;
+#else
+    * .5;
+#endif
+
+  const gdouble scaled_half_preimage_height =
+    ( far_y >= 0. ? far_y : -far_y )
+#if   defined __GEGL_SAMPLER_DOWNSHARP_C__
+    * ( .25 * sqrt(2.) );
+#elif defined __GEGL_SAMPLER_DOWNSMOOTH_C__
+    ;
+#else
+    * .5;
+#endif
+
+  /*
+   * Make sure at least two pixels are overlapped (even if only a
+   * little bit) in each direction, by making the box's width and
+   * height at least a little bit more than 1:
+   */
+  const gdouble floored_half_preimage_width =
+    ( scaled_half_preimage_width  < .50000001
+      ? .50000001 : scaled_half_preimage_width );
+  const gdouble floored_half_preimage_height =
+    ( scaled_half_preimage_height < .50000001
+      ? .50000001 : scaled_half_preimage_height );
+  /*
+   * In order to be able to use a single gegl_sampler_get_ptr call to
+   * get the data we need, we limit the number of pixels needed in
+   * each direction to just a little less than the maximum number of
+   * pixels we can get in each direction.
+   *
+   * "- 1" is because the distance between the first and last pixel
+   * center is one less than their number. "- 2" accounts for the fact
+   * that we need one extra point at each end to compute slopes. The
+   * second "- 1" is because the use of FAST_PSEUDO_FLOOR may (?)
+   * stretch indices by 1 for negatives values. The "- .00000001" is
+   * for safety w.r.t. round off error.
+   */
+  const gdouble half_maximal_length =
+    ( pixels_per_fetched_row - 1 - 2 - 1 - SAFETY_OFFSET ) * .5 - .00000001;
+  const gdouble half_width =
+    ( floored_half_preimage_width  < half_maximal_length
+      ? floored_half_preimage_width  : half_maximal_length );
+  const gdouble half_height =
+    ( floored_half_preimage_height < half_maximal_length
+      ? floored_half_preimage_height : half_maximal_length );
+
+  /*
+   * Left/top limits of the integration box:
+   */
+  const gdouble left   = absolute_x - half_width;
+  const gdouble right  = absolute_x + half_width;
+  const gdouble top    = absolute_y - half_height;
+  const gdouble bottom = absolute_y + half_height;
+
+  /*
+   * An average is an integral divided by an area. Needed multiplier:
+   */
+  const gfloat one_over_box_area = .25 / ( half_width * half_height );
+
+  const gdouble right_plus_half  = right  + .5;
+  const gdouble bottom_plus_half = bottom + .5;
+
+  /*
+   * Index of the left/right/bottom/top-most overlapped pixels:
+   */
+  const gint absolute_left_j   = FAST_PSEUDO_FLOOR (left + .5);
+  const gint absolute_top_i    = FAST_PSEUDO_FLOOR (top  + .5);
+  const gint absolute_right_j  = FAST_PSEUDO_FLOOR (right_plus_half);
+  const gint absolute_bottom_i = FAST_PSEUDO_FLOOR (bottom_plus_half);
+
+  /*
+   * Relative indices:
+   */
+  const gint right_j  = absolute_right_j  - absolute_left_j;
+  const gint bottom_i = absolute_bottom_i - absolute_top_i;
+
+  /*
+   * "1" is because the number of pixels is one more than the index
+   * difference, "+ 2" is because we need one extra at each end.
+   */
+  const gint index_width  = right_j  + ( 1 + 2 + SAFETY_OFFSET );
+  const gint index_height = bottom_i + ( 1 + 2 + SAFETY_OFFSET );
+
+  /*
+   * Set context_rect to the appropriate values.
+   */
+  GEGL_SAMPLER (self)->context_rect.width  = index_width;
+  GEGL_SAMPLER (self)->context_rect.height = index_height;
+
+  {
+    /*
+     * Useful pointer shifts:
+     */
+    const gint skip     = channels;
+    const gint row_skip = pixels_per_fetched_row * channels;
+
+    /*
+     * Pointer offsets:
+     */
+    const gint to_topright    = right_j * skip;
+    const gint to_leftbottom  = bottom_i * row_skip;
+    const gint to_rightbottom = to_topright + to_leftbottom;
+
+    /*
+     * Widths/heights of the partially overlapped pixel areas:
+     */
+    const gfloat delta_x_left   = absolute_left_j + .5 - left;
+    const gfloat delta_x_right  = right_plus_half  - absolute_right_j;
+    const gfloat delta_y_top    = absolute_top_i  + .5 - top;
+    const gfloat delta_y_bottom = bottom_plus_half - absolute_bottom_i;
+
+    /*
+     * Coordinates of the overlap centers:
+     */
+    const gfloat left_x   =  .5f + -.5f * delta_x_left;
+    const gfloat right_x  = -.5f +  .5f * delta_x_right;
+    const gfloat top_y    =  .5f + -.5f * delta_y_top;
+    const gfloat bottom_y = -.5f +  .5f * delta_y_bottom;
+
+    /*
+     * Get a pointer to the first channel of the top left overlapped
+     * pixel. There is one left row/column before the
+     * (absolute_left_j,absolute_top_i) coordinate because the
+     * context_rect.x and context_rect.y offsets are set to -1.
+     */
+    const gfloat* restrict in_bptr =
+      gegl_sampler_get_ptr (self, absolute_left_j, absolute_top_i);
+
+    /*
+     * Computation of the integral of the piecewise linear "minmod
+     * surface" over the rectangle:
+     */
+    /*
+     * Accumulators for the various pieces:
+     */
+    gfloat integral_0,      integral_1,      integral_2,      integral_3;
+    gfloat top_left_0,      top_left_1,      top_left_2,      top_left_3;
+    gfloat top_0,           top_1,           top_2,           top_3;
+    gfloat top_right_0,     top_right_1,     top_right_2,     top_right_3;
+    gfloat left_0,          left_1,          left_2,          left_3;
+    gfloat center_0 = 0.f,  center_1 = 0.f,  center_2 = 0.f,  center_3 = 0.f;
+    gfloat right_0,         right_1,         right_2,         right_3;
+    gfloat bottom_left_0,   bottom_left_1,   bottom_left_2,   bottom_left_3;
+    gfloat bottom_0,        bottom_1,        bottom_2,        bottom_3;
+    gfloat bottom_right_0,  bottom_right_1,  bottom_right_2,  bottom_right_3;
+
+    {
+      /*
+       * Top left pixel area:
+       */
+      const gfloat second_0 = in_bptr[0];
+      const gfloat second_1 = in_bptr[1];
+      const gfloat second_2 = in_bptr[2];
+      const gfloat second_3 = in_bptr[3];
+
+      /*
+       * First channel:
+       */
+      top_left_0 =
+        second_0
+        +
+        fast_minmod (in_bptr[-skip],       second_0, in_bptr[skip])
+        * left_x
+        +
+        fast_minmod (in_bptr[-row_skip],   second_0, in_bptr[row_skip])
+        * top_y;
+      /*
+       * Second channel:
+       */
+      top_left_1 =
+        second_1
+        +
+        fast_minmod (in_bptr[-skip+1],     second_1, in_bptr[skip+1])
+        * left_x
+        +
+        fast_minmod (in_bptr[-row_skip+1], second_1, in_bptr[row_skip+1])
+        * top_y;
+      /*
+       * Third channel:
+       */
+      top_left_2 =
+        second_2
+        +
+        fast_minmod (in_bptr[-skip+2],     second_2, in_bptr[skip+2])
+        * left_x
+        +
+        fast_minmod (in_bptr[-row_skip+2], second_2, in_bptr[row_skip+2])
+        * top_y;
+      /*
+       * Fourth channel:
+       */
+      top_left_3 =
+        second_3
+        +
+        fast_minmod (in_bptr[-skip+3],     second_3, in_bptr[skip+3])
+        * left_x
+        +
+        fast_minmod (in_bptr[-row_skip+3], second_3, in_bptr[row_skip+3])
+        * top_y;
+    }
+
+    {
+      /*
+       * Integral over the top strip, excluding the top left and top
+       * right pixel areas which are done separately:
+       */
+      const gfloat* restrict in_first  = in_bptr + skip - row_skip;
+      const gfloat* restrict in_second = in_bptr + skip;
+      const gfloat* restrict in_third  = in_bptr + skip + row_skip;
+      /*
+       * Accumulators:
+       */
+      gfloat parto_0 = 0.f, parto_1 = 0.f, parto_2 = 0.f, parto_3 = 0.f;
+      gfloat party_0 = 0.f, party_1 = 0.f, party_2 = 0.f, party_3 = 0.f;
+
+      gint j = 1;
+      while (j < right_j)
+        {
+          const gfloat second_0 = *in_second++;
+          const gfloat second_1 = *in_second++;
+          const gfloat second_2 = *in_second++;
+          const gfloat second_3 = *in_second++;
+
+          parto_0 += second_0;
+          party_0 += fast_minmod (*in_first++, second_0, *in_third++);
+
+          parto_1 += second_1;
+          party_1 += fast_minmod (*in_first++, second_1, *in_third++);
+
+          parto_2 += second_2;
+          party_2 += fast_minmod (*in_first++, second_2, *in_third++);
+
+          parto_3 += second_3;
+          party_3 += fast_minmod (*in_first++, second_3, *in_third++);
+
+          j++;
+        }
+
+      top_0 = parto_0 + party_0 * top_y;
+      top_1 = parto_1 + party_1 * top_y;
+      top_2 = parto_2 + party_2 * top_y;
+      top_3 = parto_3 + party_3 * top_y;
+    }
+
+    {
+      /*
+       * Top right pixel area:
+       */
+      const gint second  = to_topright;
+      const gint first_x = to_topright - skip;
+      const gint third_x = to_topright + skip;
+      const gint first_y = to_topright - row_skip;
+      const gint third_y = to_topright + row_skip;
+
+      const gfloat second_0 = in_bptr[second];
+      const gfloat second_1 = in_bptr[second+1];
+      const gfloat second_2 = in_bptr[second+2];
+      const gfloat second_3 = in_bptr[second+3];
+
+      top_right_0 =
+        second_0
+        +
+        fast_minmod (in_bptr[first_x], second_0, in_bptr[third_x])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y], second_0, in_bptr[third_y])
+        * top_y;
+      top_right_1 =
+        second_1
+        +
+        fast_minmod (in_bptr[first_x+1], second_1, in_bptr[third_x+1])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+1], second_1, in_bptr[third_y+1])
+        * top_y;
+      top_right_2 =
+        second_2
+        +
+        fast_minmod (in_bptr[first_x+2], second_2, in_bptr[third_x+2])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+2], second_2, in_bptr[third_y+2])
+        * top_y;
+      top_right_3 =
+        second_3
+        +
+        fast_minmod (in_bptr[first_x+3], second_3, in_bptr[third_x+3])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+3], second_3, in_bptr[third_y+3])
+        * top_y;
+    }
+
+    integral_0 =
+      delta_y_top
+      * ( delta_x_left * top_left_0 + top_0 + delta_x_right * top_right_0 );
+    integral_1 =
+      delta_y_top
+      * ( delta_x_left * top_left_1 + top_1 + delta_x_right * top_right_1 );
+    integral_2 =
+      delta_y_top
+      * ( delta_x_left * top_left_2 + top_2 + delta_x_right * top_right_2 );
+    integral_3 =
+      delta_y_top
+      * ( delta_x_left * top_left_3 + top_3 + delta_x_right * top_right_3 );
+
+    {
+      /*
+       * Left strip, excluding top left and bottom left:
+       */
+      gfloat parto_0 = 0.f, parto_1 = 0.f, parto_2 = 0.f, parto_3 = 0.f;
+      gfloat partx_0 = 0.f, partx_1 = 0.f, partx_2 = 0.f, partx_3 = 0.f;
+
+      gint i = 1;
+      while (i < bottom_i)
+        {
+          const gfloat* restrict in_first  = in_bptr + i * row_skip - skip;
+          const gfloat* restrict in_second = in_bptr + i * row_skip;
+          const gfloat* restrict in_third  = in_bptr + i * row_skip + skip;
+
+          const gfloat second_0 = *in_second++;
+          const gfloat second_1 = *in_second++;
+          const gfloat second_2 = *in_second++;
+          const gfloat second_3 = *in_second;
+
+          parto_0 += second_0;
+          partx_0 += fast_minmod (*in_first++, second_0, *in_third++);
+
+          parto_1 += second_1;
+          partx_1 += fast_minmod (*in_first++, second_1, *in_third++);
+
+          parto_2 += second_2;
+          partx_2 += fast_minmod (*in_first++, second_2, *in_third++);
+
+          parto_3 += second_3;
+          partx_3 += fast_minmod (*in_first  , second_3, *in_third  );
+
+          i++;
+        }
+
+      left_0 = parto_0 + partx_0 * left_x;
+      left_1 = parto_1 + partx_1 * left_x;
+      left_2 = parto_2 + partx_2 * left_x;
+      left_3 = parto_3 + partx_3 * left_x;
+    }
+
+    {
+      /*
+       * Pixel areas which are always fully covered by the averaging box:
+       */
+      gint i = 1;
+      while ( i < bottom_i )
+        {
+          const gfloat* restrict in = in_bptr + skip + i * row_skip;
+
+          gint j = 1;
+          while ( j < right_j )
+            {
+              center_0 += *in++;
+              center_1 += *in++;
+              center_2 += *in++;
+              center_3 += *in++;
+              j++;
+            }
+          i++;
+        }
+    }
+
+    {
+      /*
+       * Right strip, excluding top right and bottom right:
+       */
+      gfloat parto_0 = 0.f, parto_1 = 0.f, parto_2 = 0.f, parto_3 = 0.f;
+      gfloat partx_0 = 0.f, partx_1 = 0.f, partx_2 = 0.f, partx_3 = 0.f;
+
+      gint i = 1;
+      while (i < bottom_i)
+        {
+          const gfloat* restrict in_first  =
+            in_bptr + to_topright + i * row_skip - skip;
+          const gfloat* restrict in_second =
+            in_bptr + to_topright + i * row_skip;
+          const gfloat* restrict in_third  =
+            in_bptr + to_topright + i * row_skip + skip;
+
+          const gfloat second_0 = *in_second++;
+          const gfloat second_1 = *in_second++;
+          const gfloat second_2 = *in_second++;
+          const gfloat second_3 = *in_second;
+
+          parto_0 += second_0;
+          partx_0 += fast_minmod (*in_first++, second_0, *in_third++);
+
+          parto_1 += second_1;
+          partx_1 += fast_minmod (*in_first++, second_1, *in_third++);
+
+          parto_2 += second_2;
+          partx_2 += fast_minmod (*in_first++, second_2, *in_third++);
+
+          parto_3 += second_3;
+          partx_3 += fast_minmod (*in_first  , second_3, *in_third  );
+
+          i++;
+        }
+
+      right_0 = parto_0 + partx_0 * right_x;
+      right_1 = parto_1 + partx_1 * right_x;
+      right_2 = parto_2 + partx_2 * right_x;
+      right_3 = parto_3 + partx_3 * right_x;
+    }
+
+    integral_0 += delta_x_left * left_0 + center_0 + delta_x_right * right_0;
+    integral_1 += delta_x_left * left_1 + center_1 + delta_x_right * right_1;
+    integral_2 += delta_x_left * left_2 + center_2 + delta_x_right * right_2;
+    integral_3 += delta_x_left * left_3 + center_3 + delta_x_right * right_3;
+
+    {
+      /*
+       * Bottom left pixel area:
+       */
+      const gint second  = to_leftbottom;
+      const gint first_x = to_leftbottom - skip;
+      const gint third_x = to_leftbottom + skip;
+      const gint first_y = to_leftbottom - row_skip;
+      const gint third_y = to_leftbottom + row_skip;
+
+      const gfloat second_0 = in_bptr[second];
+      const gfloat second_1 = in_bptr[second+1];
+      const gfloat second_2 = in_bptr[second+2];
+      const gfloat second_3 = in_bptr[second+3];
+
+      bottom_left_0 =
+        second_0
+        +
+        fast_minmod (in_bptr[first_x], second_0, in_bptr[third_x])
+        * left_x
+        +
+        fast_minmod (in_bptr[first_y], second_0, in_bptr[third_y])
+        * bottom_y;
+      bottom_left_1 =
+        second_1
+        +
+        fast_minmod (in_bptr[first_x+1], second_1, in_bptr[third_x+1])
+        * left_x
+        +
+        fast_minmod (in_bptr[first_y+1], second_1, in_bptr[third_y+1])
+        * bottom_y;
+      bottom_left_2 =
+        second_2
+        +
+        fast_minmod (in_bptr[first_x+2], second_2, in_bptr[third_x+2])
+        * left_x
+        +
+        fast_minmod (in_bptr[first_y+2], second_2, in_bptr[third_y+2])
+        * bottom_y;
+      bottom_left_3 =
+        second_3
+        +
+        fast_minmod (in_bptr[first_x+3], second_3, in_bptr[third_x+3])
+        * left_x
+        +
+        fast_minmod (in_bptr[first_y+3], second_3, in_bptr[third_y+3])
+        * bottom_y;
+    }
+
+    {
+      /*
+       * Bottom strip, excluding bottom left and bottom right:
+       */
+      const gfloat* restrict in_first  =
+        in_bptr + skip + to_leftbottom - row_skip;
+      const gfloat* restrict in_second =
+        in_bptr + skip + to_leftbottom;
+      const gfloat* restrict in_third  =
+        in_bptr + skip + to_leftbottom + row_skip;
+      gfloat parto_0 = 0.f, parto_1 = 0.f, parto_2 = 0.f, parto_3 = 0.f;
+      gfloat party_0 = 0.f, party_1 = 0.f, party_2 = 0.f, party_3 = 0.f;
+
+      gint j = 1;
+      while (j < right_j)
+        {
+          const gfloat second_0 = *in_second++;
+          const gfloat second_1 = *in_second++;
+          const gfloat second_2 = *in_second++;
+          const gfloat second_3 = *in_second++;
+
+          parto_0 += second_0;
+          party_0 += fast_minmod (*in_first++, second_0, *in_third++);
+
+          parto_1 += second_1;
+          party_1 += fast_minmod (*in_first++, second_1, *in_third++);
+
+          parto_2 += second_2;
+          party_2 += fast_minmod (*in_first++, second_2, *in_third++);
+
+          parto_3 += second_3;
+          party_3 += fast_minmod (*in_first++, second_3, *in_third++);
+
+          j++;
+        }
+
+      bottom_0 = parto_0 + party_0 * bottom_y;
+      bottom_1 = parto_1 + party_1 * bottom_y;
+      bottom_2 = parto_2 + party_2 * bottom_y;
+      bottom_3 = parto_3 + party_3 * bottom_y;
+    }
+
+    {
+      /*
+       * Bottom right pixel area:
+       */
+      const gint second  = to_rightbottom;
+      const gint first_x = to_rightbottom - skip;
+      const gint third_x = to_rightbottom + skip;
+      const gint first_y = to_rightbottom - row_skip;
+      const gint third_y = to_rightbottom + row_skip;
+
+      const gfloat second_0 = in_bptr[second];
+      const gfloat second_1 = in_bptr[second+1];
+      const gfloat second_2 = in_bptr[second+2];
+      const gfloat second_3 = in_bptr[second+3];
+
+      bottom_right_0 =
+        second_0
+        +
+        fast_minmod (in_bptr[first_x],   second_0, in_bptr[third_x])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y],   second_0, in_bptr[third_y])
+        * bottom_y;
+      bottom_right_1 =
+        second_1
+        +
+        fast_minmod (in_bptr[first_x+1], second_1, in_bptr[third_x+1])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+1], second_1, in_bptr[third_y+1])
+        * bottom_y;
+      bottom_right_2 =
+        second_2
+        +
+        fast_minmod (in_bptr[first_x+2], second_2, in_bptr[third_x+2])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+2], second_2, in_bptr[third_y+2])
+        * bottom_y;
+      bottom_right_3 =
+        second_3
+        +
+        fast_minmod (in_bptr[first_x+3], second_3, in_bptr[third_x+3])
+        * right_x
+        +
+        fast_minmod (in_bptr[first_y+3], second_3, in_bptr[third_y+3])
+        * bottom_y;
+    }
+
+    integral_0 +=
+      delta_y_bottom
+      * ( delta_x_left * bottom_left_0 + bottom_0
+          + delta_x_right * bottom_right_0 );
+    integral_1 +=
+      delta_y_bottom
+      * ( delta_x_left * bottom_left_1 + bottom_1
+          + delta_x_right * bottom_right_1 );
+    integral_2 +=
+      delta_y_bottom
+      * ( delta_x_left * bottom_left_2 + bottom_2
+          + delta_x_right * bottom_right_2 );
+    integral_3 +=
+      delta_y_bottom
+      * ( delta_x_left * bottom_left_3 + bottom_3
+          + delta_x_right * bottom_right_3 );
+
+    {
+      /*
+       * The newval array will contain one computed resampled value
+       * per channel:
+       */
+      gfloat newval[channels];
+
+      newval[0] = integral_0 * one_over_box_area;
+      newval[1] = integral_1 * one_over_box_area;
+      newval[2] = integral_2 * one_over_box_area;
+      newval[3] = integral_3 * one_over_box_area;
+
+      /*
+       * Ship out newval (array of computed new pixel values):
+       */
+      babl_process (babl_fish (self->interpolate_format, self->format),
+                    newval, output, 1);
+    }
+  }
+}
+
+static void
+set_property (      GObject*    object,
+                    guint       property_id,
+              const GValue*     value,
+                    GParamSpec* pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+get_property (GObject*    object,
+              guint       property_id,
+              GValue*     value,
+              GParamSpec* pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
diff --git a/gegl/buffer/gegl-sampler-downsize.h b/gegl/buffer/gegl-sampler-downsize.h
new file mode 100644
index 0000000..81cb840
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsize.h
@@ -0,0 +1,48 @@
+/* This file is part of 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/>.
+ *
+ */
+#ifndef __GEGL_SAMPLER_DOWNSIZE_H__
+#define __GEGL_SAMPLER_DOWNSIZE_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSIZE            (gegl_sampler_downsize_get_type ())
+#define GEGL_SAMPLER_DOWNSIZE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSIZE, GeglSamplerDownsize))
+#define GEGL_SAMPLER_DOWNSIZE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_SAMPLER_DOWNSIZE, GeglSamplerDownsizeClass))
+#define GEGL_IS_SAMPLER_DOWNSIZE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSIZE))
+#define GEGL_IS_SAMPLER_DOWNSIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_SAMPLER_DOWNSIZE))
+#define GEGL_SAMPLER_DOWNSIZE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_SAMPLER_DOWNSIZE, GeglSamplerDownsizeClass))
+
+typedef struct _GeglSamplerDownsize      GeglSamplerDownsize;
+typedef struct _GeglSamplerDownsizeClass GeglSamplerDownsizeClass;
+
+struct _GeglSamplerDownsize
+{
+  GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsizeClass
+{
+  GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsize_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-downsmooth.c b/gegl/buffer/gegl-sampler-downsmooth.c
new file mode 100644
index 0000000..7b3b0d7
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsmooth.c
@@ -0,0 +1,26 @@
+/* This file is part of 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/>.
+ *
+ * 2009 (c) Adam Turcotte and Nicolas Robidoux.
+ */
+
+/*
+ * The following #define sets the stage for the compilation of
+ * Downsmooth from the gegl-sampler-downsize.c source code:
+ */
+#define __GEGL_SAMPLER_DOWNSMOOTH_C__
+#include "gegl-sampler-downsize.c"
+#undef __GEGL_SAMPLER_DOWNSMOOTH_C__
diff --git a/gegl/buffer/gegl-sampler-downsmooth.h b/gegl/buffer/gegl-sampler-downsmooth.h
new file mode 100644
index 0000000..f972bda
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsmooth.h
@@ -0,0 +1,48 @@
+/* This file is part of 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/>.
+ *
+ */
+#ifndef __GEGL_SAMPLER_DOWNSMOOTH_H__
+#define __GEGL_SAMPLER_DOWNSMOOTH_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSMOOTH            (gegl_sampler_downsmooth_get_type ())
+#define GEGL_SAMPLER_DOWNSMOOTH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSMOOTH, GeglSamplerDownsmooth))
+#define GEGL_SAMPLER_DOWNSMOOTH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_SAMPLER_DOWNSMOOTH, GeglSamplerDownsmoothClass))
+#define GEGL_IS_SAMPLER_DOWNSMOOTH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSMOOTH))
+#define GEGL_IS_SAMPLER_DOWNSMOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_SAMPLER_DOWNSMOOTH))
+#define GEGL_SAMPLER_DOWNSMOOTH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_SAMPLER_DOWNSMOOTH, GeglSamplerDownsmoothClass))
+
+typedef struct _GeglSamplerDownsmooth      GeglSamplerDownsmooth;
+typedef struct _GeglSamplerDownsmoothClass GeglSamplerDownsmoothClass;
+
+struct _GeglSamplerDownsmooth
+{
+  GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsmoothClass
+{
+  GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsmooth_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler.c b/gegl/buffer/gegl-sampler.c
index 8b9872c..4e4d168 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -31,6 +31,9 @@
 #include "gegl-sampler-linear.h"
 #include "gegl-sampler-cubic.h"
 #include "gegl-sampler-lanczos.h"
+#include "gegl-sampler-downsharp.h"
+#include "gegl-sampler-downsize.h"
+#include "gegl-sampler-downsmooth.h"
 #include "gegl-sampler-sharp.h"
 #include "gegl-sampler-yafr.h"
 
@@ -40,6 +43,7 @@ enum
   PROP_BUFFER,
   PROP_FORMAT,
   PROP_CONTEXT_RECT,
+  PROP_INVERSE_JACOBIAN,
   PROP_LAST
 };
 
@@ -97,6 +101,14 @@ gegl_sampler_class_init (GeglSamplerClass *klass)
                                         "Input pad, for image buffer input.",
                                         GEGL_TYPE_BUFFER,
                                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (
+                 object_class,
+                 PROP_INVERSE_JACOBIAN,
+                 g_param_spec_pointer ("inverse_jacobian",
+                                       "Inverse Jacobian",
+                                       "Inverse Jacobian matrix, for certain samplers.",
+                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 }
 
 static void
@@ -202,141 +214,168 @@ dispose (GObject *gobject)
  * rowstride of 64px * 16bpp:
  */
 gfloat *
-gegl_sampler_get_ptr (GeglSampler *sampler,
-                      gint         x,
-                      gint         y)
+gegl_sampler_get_ptr (      GeglSampler *const sampler,
+                      const gint               x,
+                      const gint               y)
 {
-   guchar *buffer_ptr;
-   gint    dx;
-   gint    dy;
-   gint    bpp;
-   gint    sof;
-
-   bpp = babl_format_get_bytes_per_pixel (sampler->interpolate_format);
-
-   if (sampler->sampler_buffer == NULL
-       ||
-       x + sampler->context_rect.x < sampler->sampler_rectangle.x
-       ||
-       y + sampler->context_rect.y < sampler->sampler_rectangle.y
-       ||
-       x + sampler->context_rect.x + sampler->context_rect.width
-       >= sampler->sampler_rectangle.x + sampler->sampler_rectangle.width
-       ||
-       y + sampler->context_rect.y + sampler->context_rect.height
-       >= sampler->sampler_rectangle.y + sampler->sampler_rectangle.height)
-     {
-       GeglRectangle fetch_rectangle /* = sampler->context_rect */;
-
-       fetch_rectangle.x = x + sampler->context_rect.x;
-       fetch_rectangle.y = y + sampler->context_rect.y;
-
-       /*
-        * Override the fetch rectangle needed by the sampler, hoping
-        * that the extra pixels we fetch comes in useful in subsequent
-        * requests, we assume that it is more likely that further
-        * access is to the right or down of our currently requested
-        * position:
-        */
-       fetch_rectangle.x -= 8;
-       fetch_rectangle.y -= 8;
-       fetch_rectangle.width = 64;
-       fetch_rectangle.height = 64;
-
-       if (sampler->sampler_buffer == NULL )
-         {
-           /*
-            * We always request the same amount of pixels (64kb worth):
-            */
-           sampler->sampler_buffer =
-             g_malloc0 (fetch_rectangle.width * fetch_rectangle.height * bpp);
-         }
-
-       gegl_buffer_get (sampler->buffer,
-                        1.0,
-                        &fetch_rectangle,
-                        sampler->interpolate_format,
-                        sampler->sampler_buffer,
-                        GEGL_AUTO_ROWSTRIDE);
-
-       sampler->sampler_rectangle = fetch_rectangle;
-     }
+  guchar *buffer_ptr;
+  gint    dx;
+  gint    dy;
+  gint    sof;
 
-   dx = x - sampler->sampler_rectangle.x;
-   dy = y - sampler->sampler_rectangle.y;
-   buffer_ptr = (guchar *)sampler->sampler_buffer;
-   sof = ( dy * sampler->sampler_rectangle.width + dx ) * bpp;
+  const gint bpp =
+    babl_format_get_bytes_per_pixel (sampler->interpolate_format);
 
-   return (gfloat*)(buffer_ptr+sof);
+  /*
+   * maximum_width_and_height is the largest number of pixels which
+   * can be be requested in the horizontal or vertical directions (64
+   * in GEGL).
+   */
+  const gint maximum_width_and_height = 64;
+  g_assert (sampler->context_rect.width  <= maximum_width_and_height);
+  g_assert (sampler->context_rect.height <= maximum_width_and_height);
+
+  if (( sampler->sampler_buffer == NULL )
+      ||
+      ( x + sampler->context_rect.x < sampler->sampler_rectangle.x )
+      ||
+      ( y + sampler->context_rect.y < sampler->sampler_rectangle.y )
+      ||
+      ( x + sampler->context_rect.x + sampler->context_rect.width
+        >= sampler->sampler_rectangle.x + sampler->sampler_rectangle.width )
+      ||
+      ( y + sampler->context_rect.y + sampler->context_rect.height
+        >= sampler->sampler_rectangle.y + sampler->sampler_rectangle.height ))
+    {
+      /*
+       * fetch_rectangle will become the value of
+       * sampler->sampler_rectangle:
+       */
+      GeglRectangle fetch_rectangle;
+
+      /*
+       * Override the fetch rectangle needed by the sampler, hoping
+       * that the extra pixels are useful for subsequent requests,
+       * assuming that it is more likely that further access is to the
+       * right or down of our currently requested
+       * position. Consequently, we move the top left corner of the
+       * context_rect by about one fourth of the maximal distance we
+       * can (one fourth of one half = one eight). Given that the
+       * maximum width and height of the fetch_rectangle is 64, so
+       * that half of it is 32, one fourth of the elbow room is at
+       * most 8. If context_rect is large, the corner is not moved
+       * much if at all, as should be.
+       */
+      fetch_rectangle.x =
+        x + sampler->context_rect.x
+        - ( maximum_width_and_height - sampler->context_rect.width  ) / 8;
+      fetch_rectangle.y =
+        y + sampler->context_rect.y
+        - ( maximum_width_and_height - sampler->context_rect.height ) / 8;
+
+      fetch_rectangle.width  = maximum_width_and_height;
+      fetch_rectangle.height = maximum_width_and_height;
+
+      if (sampler->sampler_buffer == NULL)
+        {
+          /*
+           * Always request the same amount of pixels:
+           */
+          sampler->sampler_buffer =
+            g_malloc0 (( maximum_width_and_height * maximum_width_and_height )
+                       * bpp);
+        }
+
+      gegl_buffer_get (sampler->buffer,
+                       1.0,
+                       &fetch_rectangle,
+                       sampler->interpolate_format,
+                       sampler->sampler_buffer,
+                       GEGL_AUTO_ROWSTRIDE);
+
+      sampler->sampler_rectangle = fetch_rectangle;
+    }
+
+  dx = x - sampler->sampler_rectangle.x;
+  dy = y - sampler->sampler_rectangle.y;
+  buffer_ptr = (guchar *)sampler->sampler_buffer;
+  sof = ( dx + dy * sampler->sampler_rectangle.width ) * bpp;
+
+  return (gfloat*)(buffer_ptr+sof);
 }
 
 gfloat *
-gegl_sampler_get_from_buffer (GeglSampler *sampler,
-                              gint         x,
-                              gint         y)
+gegl_sampler_get_from_buffer (      GeglSampler *const sampler,
+                              const gint               x,
+                              const gint               y)
 {
-   guchar *buffer_ptr;
-   gint    dx;
-   gint    dy;
-   gint    bpp;
-   gint    sof;
-
-   bpp = babl_format_get_bytes_per_pixel (sampler->interpolate_format);
-
-   if (sampler->sampler_buffer == NULL
-       ||
-       x < sampler->sampler_rectangle.x
-       ||
-       y < sampler->sampler_rectangle.y
-       ||
-       x >= sampler->sampler_rectangle.x + sampler->sampler_rectangle.width
-       ||
-       y >= sampler->sampler_rectangle.y + sampler->sampler_rectangle.height)
-     {
-       GeglRectangle  fetch_rectangle /* = sampler->context_rect */;
-
-       fetch_rectangle.x = x;
-       fetch_rectangle.y = y;
-
-       /*
-        * We override the fetch rectangle needed by the sampler,
-        * hoping that the extra pixels we fetch comes in useful in
-        * subsequent requests, we assume that it is more likely that
-        * further access is to the right or down of our currently
-        * requested position:
-        */
-       fetch_rectangle.x -= 8;
-       fetch_rectangle.y -= 8;
-       fetch_rectangle.width = 64;
-       fetch_rectangle.height = 64;
-
-       if (sampler->sampler_buffer == NULL )
-         {
-           /*
-            * We always request the same amount of pixels (64kb worth):
-            */
-           sampler->sampler_buffer =
-             g_malloc0 (fetch_rectangle.width * fetch_rectangle.height * bpp);
-         }
-
-       gegl_buffer_get (sampler->buffer,
-                        1.0,
-                        &fetch_rectangle,
-                        sampler->interpolate_format,
-                        sampler->sampler_buffer,
-                        GEGL_AUTO_ROWSTRIDE);
-
-       sampler->sampler_rectangle = fetch_rectangle;
-     }
+  guchar *buffer_ptr;
+  gint    dx;
+  gint    dy;
+  gint    sof;
 
-   dx = x - sampler->sampler_rectangle.x;
-   dy = y - sampler->sampler_rectangle.y;
+  const gint bpp =
+    babl_format_get_bytes_per_pixel (sampler->interpolate_format);
 
-   buffer_ptr = (guchar *)sampler->sampler_buffer;
+  /*
+   * maximum_width_and_height is the largest number of pixels which
+   * can be be requested in the horizontal or vertical directions (64
+   * in GEGL).
+   */
+  const gint maximum_width_and_height = 64;
+  g_assert (sampler->context_rect.width  <= maximum_width_and_height);
+  g_assert (sampler->context_rect.height <= maximum_width_and_height);
+
+  if (( sampler->sampler_buffer == NULL )
+      ||
+      ( x < sampler->sampler_rectangle.x )
+      ||
+      ( y < sampler->sampler_rectangle.y )
+      ||
+      ( x >= sampler->sampler_rectangle.x + sampler->sampler_rectangle.width )
+      ||
+      ( y >= sampler->sampler_rectangle.y + sampler->sampler_rectangle.height ))
+    {
+      /*
+       * fetch_rectangle will become the value of
+       * sampler->sampler_rectangle:
+       */
+      GeglRectangle fetch_rectangle;
+
+      fetch_rectangle.x =
+        x - ( maximum_width_and_height - sampler->context_rect.width  ) / 8;
+      fetch_rectangle.y =
+        y - ( maximum_width_and_height - sampler->context_rect.height ) / 8;
+
+      fetch_rectangle.width  = maximum_width_and_height;
+      fetch_rectangle.height = maximum_width_and_height;
+
+      if (sampler->sampler_buffer == NULL)
+        {
+          /*
+           * Always request the same amount of pixels:
+           */
+          sampler->sampler_buffer =
+            g_malloc0 (( maximum_width_and_height * maximum_width_and_height )
+                       * bpp);
+        }
+
+      gegl_buffer_get (sampler->buffer,
+                       1.0,
+                       &fetch_rectangle,
+                       sampler->interpolate_format,
+                       sampler->sampler_buffer,
+                       GEGL_AUTO_ROWSTRIDE);
+
+      sampler->sampler_rectangle = fetch_rectangle;
+    }
 
-   sof = ( dy * sampler->sampler_rectangle.width + dx ) * bpp;
+  dx = x - sampler->sampler_rectangle.x;
+  dy = y - sampler->sampler_rectangle.y;
+  buffer_ptr = (guchar *)sampler->sampler_buffer;
+  sof = ( dx + dy * sampler->sampler_rectangle.width ) * bpp;
 
-   return (gfloat*)(buffer_ptr+sof);
+  return (gfloat*)(buffer_ptr+sof);
 }
 
 static void
@@ -357,6 +396,10 @@ get_property (GObject    *object,
         g_value_set_pointer (value, self->format);
         break;
 
+      case PROP_INVERSE_JACOBIAN:
+        g_value_set_pointer (value, self->inverse_jacobian);
+        break;
+
       default:
         break;
     }
@@ -380,6 +423,10 @@ set_property (GObject      *object,
         self->format = g_value_get_pointer (value);
         break;
 
+      case PROP_INVERSE_JACOBIAN:
+        self->inverse_jacobian = g_value_get_pointer (value);
+        break;
+
       default:
         break;
     }
@@ -420,6 +467,15 @@ gegl_buffer_interpolation_from_string (const gchar *string)
   if (g_str_equal (string, "lanczos"))
     return GEGL_INTERPOLATION_LANCZOS;
 
+  if (g_str_equal (string, "downsharp"))
+    return GEGL_INTERPOLATION_DOWNSHARP;
+
+  if (g_str_equal (string, "downsize"))
+    return GEGL_INTERPOLATION_DOWNSIZE;
+
+  if (g_str_equal (string, "downsmooth"))
+    return GEGL_INTERPOLATION_DOWNSMOOTH;
+
   return GEGL_INTERPOLATION_NEAREST;
 }
 
@@ -440,6 +496,12 @@ gegl_sampler_type_from_interpolation (GeglInterpolation interpolation)
         return GEGL_TYPE_SAMPLER_YAFR;
       case GEGL_INTERPOLATION_LANCZOS:
         return GEGL_TYPE_SAMPLER_LANCZOS;
+      case GEGL_INTERPOLATION_DOWNSHARP:
+        return GEGL_TYPE_SAMPLER_DOWNSHARP;
+      case GEGL_INTERPOLATION_DOWNSIZE:
+        return GEGL_TYPE_SAMPLER_DOWNSIZE;
+      case GEGL_INTERPOLATION_DOWNSMOOTH:
+        return GEGL_TYPE_SAMPLER_DOWNSMOOTH;
       default:
         return GEGL_TYPE_SAMPLER_LINEAR;
     }
diff --git a/gegl/buffer/gegl-sampler.h b/gegl/buffer/gegl-sampler.h
index aa2353f..d5e9494 100644
--- a/gegl/buffer/gegl-sampler.h
+++ b/gegl/buffer/gegl-sampler.h
@@ -46,6 +46,7 @@ struct _GeglSampler
   GeglRectangle  context_rect;
   void          *sampler_buffer;
   GeglRectangle  sampler_rectangle;
+  GeglMatrix2   *inverse_jacobian;
   gdouble        x; /* mirrors the currently requested */
   gdouble        y; /* coordinates in the instance     */
 };
diff --git a/gegl/gegl-matrix.h b/gegl/gegl-matrix.h
index c5d8624..cb8fb77 100644
--- a/gegl/gegl-matrix.h
+++ b/gegl/gegl-matrix.h
@@ -17,6 +17,7 @@ G_BEGIN_DECLS
  */
 
 typedef gdouble GeglMatrix3 [3][3];
+typedef gdouble GeglMatrix2 [2][2];
 
 /**
  * gegl_matrix3_identity:
diff --git a/operations/affine/affine.c b/operations/affine/affine.c
index 6bb7cbb..6bed8c0 100644
--- a/operations/affine/affine.c
+++ b/operations/affine/affine.c
@@ -237,7 +237,7 @@ op_affine_class_init (OpAffineClass *klass)
                                    g_param_spec_string (
                                      "filter",
                                      _("Filter"),
-                                     _("Filter type (nearest, linear, lanczos, cubic)"),
+                                     _("Filter type (nearest, linear, lanczos, cubic, downsharp, downsize, downsmooth)"),
                                      "linear",
                                      G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, PROP_HARD_EDGES,
@@ -658,6 +658,7 @@ affine_generic (GeglBuffer  *dest,
   gfloat               *dest_buf,
                        *dest_ptr;
   GeglMatrix3           inverse;
+  GeglMatrix2           inverse_jacobian;
   gdouble               u_start,
                         v_start,
                         u_float,
@@ -685,6 +686,13 @@ affine_generic (GeglBuffer  *dest,
 
       gegl_matrix3_copy (inverse, matrix);
       gegl_matrix3_invert (inverse);
+      inverse_jacobian[0][0] = inverse[0][0];
+      inverse_jacobian[0][1] = inverse[0][1];
+      inverse_jacobian[1][0] = inverse[1][0];
+      inverse_jacobian[1][1] = inverse[1][1];
+
+     /* set inverse_jacobian for samplers that support it */
+      sampler->inverse_jacobian = &inverse_jacobian;
 
       u_start = inverse[0][0] * roi->x + inverse[0][1] * roi->y + inverse[0][2];
       v_start = inverse[1][0] * roi->x + inverse[1][1] * roi->y + inverse[1][2];



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