[gegl] Add new samplers from gsoc
- From: Øyvind Kolås <ok src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gegl] Add new samplers from gsoc
- Date: Sun, 7 Feb 2010 16:04:42 +0000 (UTC)
commit 130c793c70195cf900017f74505b863a3c29a6d5
Author: Adam Turcotte <adam turcotte gmail com>
Date: Fri Jan 15 17:33:13 2010 -0500
Add new samplers from gsoc
gegl/buffer/Makefile.am | 22 +-
gegl/buffer/gegl-buffer-access.c | 5 +-
gegl/buffer/gegl-buffer.c | 5 +-
gegl/buffer/gegl-buffer.h | 5 +-
gegl/buffer/gegl-sampler-downsharpfast.c | 26 +
gegl/buffer/gegl-sampler-downsharpfast.h | 48 ++
gegl/buffer/gegl-sampler-downsizefast.c | 631 +++++++++++++++
gegl/buffer/gegl-sampler-downsizefast.h | 48 ++
gegl/buffer/gegl-sampler-downsmoothfast.c | 26 +
gegl/buffer/gegl-sampler-downsmoothfast.h | 48 ++
gegl/buffer/gegl-sampler-sharp.c | 806 ------------------
gegl/buffer/gegl-sampler-sharp.h | 50 --
gegl/buffer/gegl-sampler-upsharp.c | 1166 ++++++++++++++++++++++++++
gegl/buffer/gegl-sampler-upsharp.h | 48 ++
gegl/buffer/gegl-sampler-upsize.c | 679 ++++++++++++++++
gegl/buffer/gegl-sampler-upsize.h | 50 ++
gegl/buffer/gegl-sampler-upsmooth.c | 1259 +++++++++++++++++++++++++++++
gegl/buffer/gegl-sampler-upsmooth.h | 48 ++
gegl/buffer/gegl-sampler-yafr.c | 687 ----------------
gegl/buffer/gegl-sampler-yafr.h | 50 --
gegl/buffer/gegl-sampler.c | 33 +-
operations/affine/affine.c | 2 +-
22 files changed, 4117 insertions(+), 1625 deletions(-)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 7b0eb22..9fe52ba 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -34,10 +34,11 @@ libbuffer_la_SOURCES = \
gegl-sampler-linear.c \
gegl-sampler-nearest.c \
gegl-sampler-downsharp.c \
- gegl-sampler-downsize.c \
+ gegl-sampler-downsize.c \
gegl-sampler-downsmooth.c \
- gegl-sampler-sharp.c \
- gegl-sampler-yafr.c \
+ gegl-sampler-upsharp.c \
+ gegl-sampler-upsize.c \
+ gegl-sampler-upsmooth.c \
gegl-region-generic.c \
gegl-tile.c \
gegl-tile-source.c \
@@ -66,10 +67,11 @@ libbuffer_la_SOURCES = \
gegl-sampler-linear.h \
gegl-sampler-nearest.h \
gegl-sampler-downsharp.h \
- gegl-sampler-downsize.h \
+ gegl-sampler-downsize.h \
gegl-sampler-downsmooth.h \
- gegl-sampler-sharp.h \
- gegl-sampler-yafr.h \
+ gegl-sampler-upsharp.h \
+ gegl-sampler-upsize.h \
+ gegl-sampler-upsmooth.h \
gegl-region.h \
gegl-region-generic.h \
gegl-tile.h \
@@ -78,12 +80,12 @@ libbuffer_la_SOURCES = \
gegl-tile-backend.h \
gegl-tile-backend-file.h \
gegl-tile-backend-tiledir.h \
- gegl-tile-backend-ram.h \
+ gegl-tile-backend-ram.h \
gegl-tile-handler.h \
- gegl-tile-handler-chain.h \
+ gegl-tile-handler-chain.h \
gegl-tile-handler-cache.h \
gegl-tile-handler-empty.h \
- gegl-tile-handler-log.h \
- gegl-tile-handler-zoom.h \
+ gegl-tile-handler-log.h \
+ gegl-tile-handler-zoom.h \
gegl-id-pool.h
diff --git a/gegl/buffer/gegl-buffer-access.c b/gegl/buffer/gegl-buffer-access.c
index 82fdfa8..5d7773d 100644
--- a/gegl/buffer/gegl-buffer-access.c
+++ b/gegl/buffer/gegl-buffer-access.c
@@ -40,8 +40,9 @@
#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-sampler-upsharp.h"
+#include "gegl-sampler-upsize.h"
+#include "gegl-sampler-upsmooth.h"
#include "gegl-buffer-index.h"
#include "gegl-tile-backend.h"
#include "gegl-buffer-iterator.h"
diff --git a/gegl/buffer/gegl-buffer.c b/gegl/buffer/gegl-buffer.c
index 25b5aa6..b545ee3 100644
--- a/gegl/buffer/gegl-buffer.c
+++ b/gegl/buffer/gegl-buffer.c
@@ -66,8 +66,9 @@
#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-sampler-upsharp.h"
+#include "gegl-sampler-upsize.h"
+#include "gegl-sampler-upsmooth.h"
#include "gegl-types-internal.h"
#include "gegl-utils.h"
#include "gegl-id-pool.h"
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index 1d0ff57..76a82a0 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -300,8 +300,9 @@ typedef enum {
GEGL_INTERPOLATION_DOWNSHARP,
GEGL_INTERPOLATION_DOWNSIZE,
GEGL_INTERPOLATION_DOWNSMOOTH,
- GEGL_INTERPOLATION_SHARP,
- GEGL_INTERPOLATION_YAFR
+ GEGL_INTERPOLATION_UPSHARP,
+ GEGL_INTERPOLATION_UPSIZE,
+ GEGL_INTERPOLATION_UPSMOOTH
} GeglInterpolation;
/**
diff --git a/gegl/buffer/gegl-sampler-downsharpfast.c b/gegl/buffer/gegl-sampler-downsharpfast.c
new file mode 100644
index 0000000..983d48d
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsharpfast.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
+ * Downsharpfast from the gegl-sampler-downsizefast.c source code:
+ */
+#define __GEGL_SAMPLER_DOWNSHARPFAST_C__
+#include "gegl-sampler-downsizefast.c"
+#undef __GEGL_SAMPLER_DOWNSHARPFAST_C__
diff --git a/gegl/buffer/gegl-sampler-downsharpfast.h b/gegl/buffer/gegl-sampler-downsharpfast.h
new file mode 100644
index 0000000..7242187
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsharpfast.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_DOWNSHARPFAST_H__
+#define __GEGL_SAMPLER_DOWNSHARPFAST_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSHARPFAST (gegl_sampler_downsharpfast_get_type ())
+#define GEGL_SAMPLER_DOWNSHARPFAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSHARPFAST, GeglSamplerDownsharpfast))
+#define GEGL_SAMPLER_DOWNSHARPFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_DOWNSHARPFAST, GeglSamplerDownsharpfastClass))
+#define GEGL_IS_SAMPLER_DOWNSHARPFAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSHARPFAST))
+#define GEGL_IS_SAMPLER_DOWNSHARPFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_DOWNSHARPFAST))
+#define GEGL_SAMPLER_DOWNSHARPFAST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_DOWNSHARPFAST, GeglSamplerDownsharpfastClass))
+
+typedef struct _GeglSamplerDownsharpfast GeglSamplerDownsharpfast;
+typedef struct _GeglSamplerDownsharpfastClass GeglSamplerDownsharpfastClass;
+
+struct _GeglSamplerDownsharpfast
+{
+ GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsharpfastClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsharpfast_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-downsizefast.c b/gegl/buffer/gegl-sampler-downsizefast.c
new file mode 100644
index 0000000..f7a2470
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsizefast.c
@@ -0,0 +1,631 @@
+/* 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) Eric Daoust, Adam Turcotte, Nicolas Robidoux and Ã?yvind
+ * Kolås.
+ */
+
+/*
+ * Acknowledgements: Eric Daoust and Adam Turcotte's GEGL 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 fast methods tuned for transformations
+ * for which downsampling is more typical than upsampling.
+ *
+ * ===================
+ * 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.
+ *
+ * Downsmoothfast 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_DOWNSHARPFAST_C__)
+#include "gegl-sampler-downsharpfast.h"
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+#include "gegl-sampler-downsmoothfast.h"
+#else
+#include "gegl-sampler-downsizefast.h"
+#endif
+#include "gegl-matrix.h"
+
+/*
+ * 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_DOWNSHARPFAST_C__)
+static void gegl_sampler_downsharpfast_get ( GeglSampler* self,
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+static void gegl_sampler_downsmoothfast_get ( GeglSampler* self,
+#else
+static void gegl_sampler_downsizefast_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_DOWNSHARPFAST_C__)
+G_DEFINE_TYPE (GeglSamplerDownsharpfast,
+ gegl_sampler_downsharpfast,
+ GEGL_TYPE_SAMPLER)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+G_DEFINE_TYPE (GeglSamplerDownsmoothfast,
+ gegl_sampler_downsmoothfast,
+ GEGL_TYPE_SAMPLER)
+#else
+G_DEFINE_TYPE (GeglSamplerDownsizefast,
+ gegl_sampler_downsizefast,
+ GEGL_TYPE_SAMPLER)
+#endif
+
+static GObjectClass * parent_class = NULL;
+
+static void
+#if defined (__GEGL_SAMPLER_DOWNSHARPFAST_C__)
+gegl_sampler_downsharpfast_class_init (GeglSamplerDownsharpfastClass *klass)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+gegl_sampler_downsmoothfast_class_init (GeglSamplerDownsmoothfastClass *klass)
+#else
+gegl_sampler_downsizefast_class_init (GeglSamplerDownsizefastClass *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_DOWNSHARPFAST_C__)
+ sampler_class->get = gegl_sampler_downsharpfast_get;
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+ sampler_class->get = gegl_sampler_downsmoothfast_get;
+#else
+ sampler_class->get = gegl_sampler_downsizefast_get;
+#endif
+}
+
+static void
+#if defined (__GEGL_SAMPLER_DOWNSHARPFAST_C__)
+gegl_sampler_downsharpfast_init (GeglSamplerDownsharpfast *self)
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+gegl_sampler_downsmoothfast_init (GeglSamplerDownsmoothfast *self)
+#else
+gegl_sampler_downsizefast_init (GeglSamplerDownsizefast *self)
+#endif
+{
+ /*
+ * ===================
+ * UNSATISFACTORY HACK
+ * ===================
+ *
+ * The stencil offsets context_rect.x and context.y are set to -1
+ * even though 0 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 0 but they are set to -1 for safety. For
+ * mysterious reasons (having to do with negative locations near
+ * zero?), 0 does not behave quite as expected in some of the
+ * downsampling methods. If and when GEGL has more robust variable
+ * context_rect handling, it could be brought back to 0.
+ */
+ GEGL_SAMPLER (self)->context_rect.x = -SAFETY_OFFSET;
+ GEGL_SAMPLER (self)->context_rect.y = -SAFETY_OFFSET;
+ GEGL_SAMPLER (self)->context_rect.width = 2 + SAFETY_OFFSET;
+ GEGL_SAMPLER (self)->context_rect.height = 2 + SAFETY_OFFSET;
+ GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
+}
+
+static void
+#if defined (__GEGL_SAMPLER_DOWNSHARPFAST_C__)
+gegl_sampler_downsharpfast_get ( GeglSampler* self,
+#elif defined (__GEGL_SAMPLER_DOWNSMOOTHFAST_C__)
+gegl_sampler_downsmoothfast_get ( GeglSampler* self,
+#else
+gegl_sampler_downsizefast_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_DOWNSHARPFAST_C__
+ * ( .25 * sqrt(2.) );
+#elif defined __GEGL_SAMPLER_DOWNSMOOTHFAST_C__
+ ;
+#else
+ * .5;
+#endif
+
+ const gdouble scaled_half_preimage_height =
+ ( far_y >= 0. ? far_y : -far_y )
+#if defined __GEGL_SAMPLER_DOWNSHARPFAST_C__
+ * ( .25 * sqrt(2.) );
+#elif defined __GEGL_SAMPLER_DOWNSMOOTHFAST_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. 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 - 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;
+
+ {
+ /*
+ * 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);
+
+ /*
+ * Useful pointer shifts:
+ */
+ const gint skip = channels;
+ const gint row_skip = pixels_per_fetched_row * channels;
+
+ /*
+ * Computation of the integral of the piecewise linear "minmod
+ * surface" over the rectangle:
+ */
+ /*
+ * Accumulators for various pieces:
+ */
+ gfloat top_0 = 0.f, top_1 = 0.f, top_2 = 0.f, top_3 = 0.f;
+ gfloat left_0 = 0.f, left_1 = 0.f, left_2 = 0.f, left_3 = 0.f;
+ gfloat center_0 = 0.f, center_1 = 0.f, center_2 = 0.f, center_3 = 0.f;
+ gfloat right_0 = 0.f, right_1 = 0.f, right_2 = 0.f, right_3 = 0.f;
+ gfloat bottom_0 = 0.f, bottom_1 = 0.f, bottom_2 = 0.f, bottom_3 = 0.f;
+
+ /*
+ * Top left pixel area:
+ */
+ /*
+ * First channel:
+ */
+ const gfloat top_left_0 = in_bptr[0];
+ /*
+ * Second channel:
+ */
+ const gfloat top_left_1 = in_bptr[1];
+ /*
+ * Third channel:
+ */
+ const gfloat top_left_2 = in_bptr[2];
+ /*
+ * Fourth channel:
+ */
+ const gfloat top_left_3 = in_bptr[3];
+
+ {
+ /*
+ * Integral over the top strip, excluding the top left and top
+ * right pixel areas which are done separately:
+ */
+ const gfloat* restrict in = in_bptr + skip;
+ /*
+ * Accumulators:
+ */
+ gint j = 1;
+ while (j < right_j)
+ {
+ top_0 += *in++;
+ top_1 += *in++;
+ top_2 += *in++;
+ top_3 += *in++;
+ j++;
+ }
+ }
+
+ {
+ const gint to_topright = right_j * skip;
+
+ /*
+ * Top right pixel area:
+ */
+ const gfloat top_right_0 = in_bptr[to_topright];
+ const gfloat top_right_1 = in_bptr[to_topright+1];
+ const gfloat top_right_2 = in_bptr[to_topright+2];
+ const gfloat top_right_3 = in_bptr[to_topright+3];
+
+ 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;
+
+ gfloat integral_0 =
+ delta_y_top
+ * ( delta_x_left * top_left_0 + top_0 + delta_x_right * top_right_0 );
+ gfloat integral_1 =
+ delta_y_top
+ * ( delta_x_left * top_left_1 + top_1 + delta_x_right * top_right_1 );
+ gfloat integral_2 =
+ delta_y_top
+ * ( delta_x_left * top_left_2 + top_2 + delta_x_right * top_right_2 );
+ gfloat 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:
+ */
+ gint i = 1;
+ while (i < bottom_i)
+ {
+ const gfloat* restrict in = in_bptr + i * row_skip;
+ left_0 += *in++;
+ left_1 += *in++;
+ left_2 += *in++;
+ left_3 += *in++;
+ i++;
+ }
+ }
+
+ {
+ /*
+ * 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:
+ */
+ gint i = 1;
+ while (i < bottom_i)
+ {
+ const gfloat* restrict in = in_bptr + to_topright + i * row_skip;
+ right_0 += *in++;
+ right_1 += *in++;
+ right_2 += *in++;
+ right_3 += *in++;
+ i++;
+ }
+ }
+
+ 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;
+
+ {
+ const gint to_leftbottom = bottom_i * row_skip;
+
+ /*
+ * Bottom left pixel area:
+ */
+ const gfloat bottom_left_0 = in_bptr[to_leftbottom];
+ const gfloat bottom_left_1 = in_bptr[to_leftbottom+1];
+ const gfloat bottom_left_2 = in_bptr[to_leftbottom+2];
+ const gfloat bottom_left_3 = in_bptr[to_leftbottom+3];
+
+ {
+ /*
+ * Bottom strip, excluding bottom left and bottom right:
+ */
+ const gfloat* restrict in = in_bptr + skip + to_leftbottom;
+ gint j = 1;
+ while (j < right_j)
+ {
+ bottom_0 += *in++;
+ bottom_1 += *in++;
+ bottom_2 += *in++;
+ bottom_3 += *in++;
+ j++;
+ }
+ }
+
+ {
+ const gint to_rightbottom = to_topright + to_leftbottom;
+
+ /*
+ * Bottom right pixel area:
+ */
+ const gfloat bottom_right_0 = in_bptr[to_rightbottom];
+ const gfloat bottom_right_1 = in_bptr[to_rightbottom+1];
+ const gfloat bottom_right_2 = in_bptr[to_rightbottom+2];
+ const gfloat bottom_right_3 = in_bptr[to_rightbottom+3];
+
+ const gfloat delta_y_bottom = bottom_plus_half - absolute_bottom_i;
+
+ 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-downsizefast.h b/gegl/buffer/gegl-sampler-downsizefast.h
new file mode 100644
index 0000000..abc1473
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsizefast.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_DOWNSIZEFAST_H__
+#define __GEGL_SAMPLER_DOWNSIZEFAST_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSIZEFAST (gegl_sampler_downsizefast_get_type ())
+#define GEGL_SAMPLER_DOWNSIZEFAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSIZEFAST, GeglSamplerDownsizefast))
+#define GEGL_SAMPLER_DOWNSIZEFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_DOWNSIZEFAST, GeglSamplerDownsizefastClass))
+#define GEGL_IS_SAMPLER_DOWNSIZEFAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSIZEFAST))
+#define GEGL_IS_SAMPLER_DOWNSIZEFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_DOWNSIZEFAST))
+#define GEGL_SAMPLER_DOWNSIZEFAST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_DOWNSIZEFAST, GeglSamplerDownsizefastClass))
+
+typedef struct _GeglSamplerDownsizefast GeglSamplerDownsizefast;
+typedef struct _GeglSamplerDownsizefastClass GeglSamplerDownsizefastClass;
+
+struct _GeglSamplerDownsizefast
+{
+ GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsizefastClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsizefast_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-downsmoothfast.c b/gegl/buffer/gegl-sampler-downsmoothfast.c
new file mode 100644
index 0000000..3b70187
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsmoothfast.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
+ * Downsmoothfast from the gegl-sampler-downsizefast.c source code:
+ */
+#define __GEGL_SAMPLER_DOWNSMOOTHFAST_C__
+#include "gegl-sampler-downsizefast.c"
+#undef __GEGL_SAMPLER_DOWNSMOOTHFAST_C__
diff --git a/gegl/buffer/gegl-sampler-downsmoothfast.h b/gegl/buffer/gegl-sampler-downsmoothfast.h
new file mode 100644
index 0000000..6c51c8f
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-downsmoothfast.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_DOWNSMOOTHFAST_H__
+#define __GEGL_SAMPLER_DOWNSMOOTHFAST_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST (gegl_sampler_downsmoothfast_get_type ())
+#define GEGL_SAMPLER_DOWNSMOOTHFAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST, GeglSamplerDownsmoothfast))
+#define GEGL_SAMPLER_DOWNSMOOTHFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST, GeglSamplerDownsmoothfastClass))
+#define GEGL_IS_SAMPLER_DOWNSMOOTHFAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST))
+#define GEGL_IS_SAMPLER_DOWNSMOOTHFAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST))
+#define GEGL_SAMPLER_DOWNSMOOTHFAST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_DOWNSMOOTHFAST, GeglSamplerDownsmoothfastClass))
+
+typedef struct _GeglSamplerDownsmoothfast GeglSamplerDownsmoothfast;
+typedef struct _GeglSamplerDownsmoothfastClass GeglSamplerDownsmoothfastClass;
+
+struct _GeglSamplerDownsmoothfast
+{
+ GeglSampler parent_instance;
+};
+
+struct _GeglSamplerDownsmoothfastClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_downsmoothfast_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-upsharp.c b/gegl/buffer/gegl-sampler-upsharp.c
new file mode 100644
index 0000000..1212f25
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsharp.c
@@ -0,0 +1,1166 @@
+/* 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, �yvind Kolås and Geert
+ * Jordaens.
+ */
+
+/*
+ * ===============
+ * UPSHARP SAMPLER
+ * ===============
+ *
+ * "Upsharp" is the GEGL name of the Nohalo level 2 resampler
+ *
+ * ====================================================================
+ * THIS CODE ONLY IMPLEMENTS THE SECOND LOWEST QUALITY NOHALO RESAMPLER
+ * ====================================================================
+ *
+ * This code implement Nohalo for (quality) level = 2.
+ *
+ * ================
+ * NOHALO RESAMPLER
+ * ================
+ *
+ * "Nohalo" is a family of parameterized resamplers with a mission:
+ * smoothly straightening oblique lines without undesirable
+ * side-effects, in particular, without much blurring and with
+ * absolutely no added haloing.
+ *
+ * The main parameter of the family is the number of "levels" of
+ * binary subdivision which are performed. Level = 0 can be thought of
+ * as being plain vanilla bilinear resampling; level = 1 is then the
+ * first "non-classical" method of the family.
+ *
+ * Although it increases computational cost, additional levels
+ * increase the quality of the resampled pixel value unless the
+ * resampled location happens to be exactly where a subdivided grid
+ * point (for this level) is located, in which case further levels do
+ * not change the answer, and consequently do not increase its
+ * quality.
+ *
+ * ===============
+ * MAIN PROPERTIES
+ * ===============
+ *
+ * =======================
+ * Nohalo is interpolatory
+ * =======================
+ *
+ * Nohalo preserves point values: If asked for the value at (the
+ * center of) an input pixel, the sampler returns the corresponding
+ * value, unchanged. In addition, because Nohalo is continuous, if
+ * asked for a value at a location "very close" to the center of an
+ * input pixel, then the sampler returns a value "very close" to
+ * it. (Nohalo is not smoothing like, say, B-Spline
+ * pseudo-interpolation, or Snohalo with nonzero smoothing.)
+ *
+ * ============================================
+ * Nohalo is co-monotone ("nohalo" = "no halo")
+ * ============================================
+ *
+ * What monotonicity more or less means here is that the resampled
+ * value is in the range of the four closest input
+ * values. Monotonicity implies that Nohalo does not add haloing. It
+ * also implies that clamping is unnecessary (provided abyss values
+ * are within the range of acceptable values, which is always the
+ * case). (Note: plain vanilla bilinear and nearest neighbour are also
+ * co-monotone.)
+ *
+ * Note: If the abyss policy is such that all values are within the
+ * "clamping range," and/or all the sampling locations are in the
+ * convex hull of the input image pixel locations, clamping is not
+ * necessary. If the abyss policy is an extrapolating one---for
+ * example, linear or bilinear extrapolation---clamping is still
+ * unnecessary unless one attempts to resample outside of the convex
+ * hull of the input pixel positions. Consequence: the usual
+ * "interpolatory" image size convention (often associated with "pixel
+ * center-based coordinates") does not require clamping when using
+ * linear extrapolation abyss policy when performing image resizing,
+ * but the usual "exact area" image size convention (often associated
+ * with "pixel corner-based coordinates") requires clamping at
+ * locations very close to or outside the boundary. With the usual
+ * nearest neighbour abyss policy, or a "fill with legal color" abyss
+ * policy, claming is not necessary.
+ *
+ * ========================
+ * Nohalo is a local method
+ * ========================
+ *
+ * The value of the reconstructed intensity surface at any point
+ * depends on the values of (at most) 12 nearby input values, located
+ * in a "cross" centered at the closest four input pixel centers.
+ *
+ * ===============================
+ * Nohalo is second order accurate
+ * ===============================
+ *
+ * (Except possibly near the boundary: it is easy to make this
+ * property carry over everywhere but this requires a tuned abyss
+ * policy---linear extrapolation, say---or building the boundary
+ * conditions inside the sampler.) Nohalo is exact on linear
+ * intensity profiles, meaning that if the input pixel values (in the
+ * stencil) are obtained from a function of the form
+ *
+ * f(x,y) = a + b*x + c*y (a, b, c constants),
+ *
+ * then the computed pixel value is exactly the value of f(x,y) at the
+ * asked-for sampling location.
+ *
+ * With nearest neighbour or "extend with fixed background color,"
+ * Nohalo is NOT exact on linears near and past the boundary.
+ *
+ * ===================
+ * Nohalo is nonlinear
+ * ===================
+ *
+ * In particular, resampling a sum of images may not be the same as
+ * summing the resamples. (This occurs even without taking into
+ * account over and underflow issues: images can only take values
+ * within a banded range, and consequently non-monotone linear
+ * samplers are also not truly linear.)
+ *
+ * ==========
+ * WEAKNESSES
+ * ==========
+ *
+ * In some cases, the first level nonlinear computation is a
+ * waste. For example, in the interior of a bichromatic image region,
+ * the nonlinear component of the level 1 Nohalo is zero, and
+ * consequently Nohalo level 1 boils down to bilinear. For such
+ * images, either stick to bilinear, or use a higher level (quality)
+ * setting. (There is no real harm in using Nohalo when it boils down
+ * to bilinear if one does not mind wasting cycles.)
+ *
+ * ==================================
+ * CENTER-BASED COORDINATE CONVENTION
+ * ==================================
+ *
+ * This code uses the "center-based coordinate" convention, in which
+ * pixels are understood to be centered at integer index
+ * locations. For example, the very first actual image pixel may be
+ * located at (0,0), and the last one at (N-1,M-1), where M is the
+ * number of pixel rows of the input image, and N is its number of
+ * pixel columns.
+ */
+
+/*
+ * Acknowledgements: Adam Turcotte's Nohalo programming funded by Google
+ * Summer of Code 2009. Nicolas Robidoux's research on Nohalo funded
+ * in part by an NSERC (National Science and Engineering Research
+ * Council of Canada) Discovery Grant.
+ *
+ * Nicolas Robidoux thanks Ralf Meyer, Geert Jordaens, �yvind Kolås,
+ * John Cupitt, Minglun Gong, and Sven Neumann for useful comments and
+ * code.
+ */
+
+#include "config.h"
+#include <glib-object.h>
+
+#include "gegl.h"
+#include "gegl-types-internal.h"
+#include "gegl-buffer-private.h"
+
+#include "gegl-sampler-upsharp.h"
+
+/*
+ * FAST_MINMOD is an implementation of the minmod function which only
+ * needs two conditional moves. FAST_MINMOD(a,b,a_times_a,a_times_b)
+ * "returns" minmod(a,b). The parameter ("input") a_times_a is assumed
+ * to contain the square of a; a_times_b, the product of a and b.
+ *
+ * This version is most suitable for images with flat (constant)
+ * colour areas, since a, which is a pixel difference, will often be
+ * 0, in which case both forward branches are likely.
+ *
+ * For uncompressed natural images in high bit depth (images for which
+ * the slopes a and b are unlikely to be equal to zero or be equal to
+ * each other), we recommend using
+ *
+ * ( (a_times_b)>=0. ? 1. : 0. ) * ( (a_times_b)<(a_times_a) ? (b) : (a) )
+ *
+ * instead. With this second version, the forward branch of the second
+ * conditional move is taken when |b|>|a| and when a*b<0. However, the
+ * "else" branch is taken when a=0 (or when a=b), which is why the
+ * above version is not recommended for images with regions with
+ * constant pixel values (or regions with pixel values which vary
+ * bilinearly).
+ */
+#define FAST_MINMOD(a,b,a_times_a,a_times_b) \
+ ( (a_times_b)>=0.f ? 1.f : 0.f ) * ( (a_times_a)<=(a_times_b) ? (a) : (b) )
+
+/*
+ * 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) ( (gint)(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
+};
+
+static void gegl_sampler_upsharp_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output);
+
+static void set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec);
+
+static void get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec);
+
+G_DEFINE_TYPE (GeglSamplerUpsharp, gegl_sampler_upsharp, GEGL_TYPE_SAMPLER)
+
+static void
+gegl_sampler_upsharp_class_init (GeglSamplerUpsharpClass *klass)
+{
+ GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ sampler_class->get = gegl_sampler_upsharp_get;
+}
+
+static void
+gegl_sampler_upsharp_init (GeglSamplerUpsharp *self)
+{
+ GEGL_SAMPLER (self)->context_rect.x = -1;
+ GEGL_SAMPLER (self)->context_rect.y = -1;
+ GEGL_SAMPLER (self)->context_rect.width = 4;
+ GEGL_SAMPLER (self)->context_rect.height = 4;
+ GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
+}
+
+static void inline
+nohalo_step1 (const gfloat uno_thr,
+ const gfloat uno_fou,
+ const gfloat dos_two,
+ const gfloat dos_thr,
+ const gfloat dos_fou,
+ const gfloat dos_fiv,
+ const gfloat tre_one,
+ const gfloat tre_two,
+ const gfloat tre_thr,
+ const gfloat tre_fou,
+ const gfloat tre_fiv,
+ const gfloat qua_one,
+ const gfloat qua_two,
+ const gfloat qua_thr,
+ const gfloat qua_fou,
+ const gfloat qua_fiv,
+ const gfloat cin_two,
+ const gfloat cin_thr,
+ const gfloat cin_fou,
+ gfloat* restrict uno_two_1,
+ gfloat* restrict uno_thr_1,
+ gfloat* restrict dos_one_1,
+ gfloat* restrict dos_two_1,
+ gfloat* restrict dos_thr_1,
+ gfloat* restrict dos_fou_1,
+ gfloat* restrict tre_one_1,
+ gfloat* restrict tre_two_1,
+ gfloat* restrict tre_thr_1,
+ gfloat* restrict tre_fou_1,
+ gfloat* restrict qua_two_1,
+ gfloat* restrict qua_thr_1)
+{
+ /*
+ * This function calculates the missing ten double density pixel
+ * values, and also returns the "already known" two, so that the
+ * twelve values which make up the stencil of nohalo level 1 are
+ * available. One level of nohalo subdivision is then applied to
+ * these 12 values, prior to applying bilinear interpolation.
+ */
+ /*
+ * THE STENCIL OF INPUT VALUES:
+ *
+ * Pointer arithmetic is used to implicitly reflect the input
+ * stencil about tre_thr---assumed closer to the sampling location
+ * than other pixels (ties are OK)---in such a way that after
+ * reflection the sampling point is to the bottom right of tre_thr.
+ *
+ * The following code and picture assumes that the stencil reflexion
+ * has already been performed.
+ *
+ *
+ * (ix,iy-2) (ix+1,iy-2)
+ * = uno_thr = uno_fou
+ *
+ *
+ *
+ * (ix-1,iy-1) (ix,iy-1) (ix+1,iy-1) (ix+2,iy-1)
+ * = dos_two = dos_thr = dos_fou = dos_fiv
+ *
+ *
+ *
+ * (ix-2,iy) (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy)
+ * = tre_one = tre_two = tre_thr = tre_fou = tre_fiv
+ * X
+ *
+ *
+ * (ix-2,iy) (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1)
+ * = qua_one = qua_two = qua_thr = qua_fou = qua_fiv
+ *
+ *
+ *
+ * (ix-1,iy+2) (ix,iy+2) (ix+1,iy+2)
+ * = cin_two = cin_thr = cin_fou
+ *
+ *
+ * The above input pixel values are the ones needed in order to make
+ * available to the second level the following first (and
+ * zero=input) level values:
+ *
+ * uno_two_1 = uno_thr_1 =
+ * (ix,iy-1/2) (ix+1/2,iy-1/2)
+ *
+ *
+ *
+ *
+ * dos_one_1 = dos_two_1 = dos_thr_1 = dos_fou_1 =
+ * (ix-1/2,iy) (ix,iy) (ix+1/2,iy) (ix+1,iy)
+ *
+ * X
+ *
+ *
+ * tre_one_1 = tre_two_1 = tre_thr_1 = tre_fou_1 =
+ * (ix-1/2,iy+1/2) (ix,iy+1/2) (ix+1/2,iy+1/2) (ix+1,iy+1/2)
+ *
+ *
+ *
+ *
+ * qua_two_1 = qua_thr_1 =
+ * (ix,iy+1) (ix+1/2,iy+1)
+ *
+ *
+ * to which nohalo level 1 is applied by the caller.
+ */
+
+ /*
+ * Computation of the nonlinear slopes: If two consecutive pixel
+ * value differences have the same sign, the smallest one (in
+ * absolute value) is taken to be the corresponding slope; if the
+ * two consecutive pixel value differences don't have the same sign,
+ * the corresponding slope is set to 0. In other words, apply minmod
+ * to comsecutive differences.
+ */
+ /*
+ * Two vertical simple differences:
+ */
+ const gfloat d_dostre_two = tre_two - dos_two;
+ const gfloat d_trequa_two = qua_two - tre_two;
+ const gfloat d_quacin_two = cin_two - qua_two;
+ /*
+ * Thr(ee) vertical differences:
+ */
+ const gfloat d_unodos_thr = dos_thr - uno_thr;
+ const gfloat d_dostre_thr = tre_thr - dos_thr;
+ const gfloat d_trequa_thr = qua_thr - tre_thr;
+ const gfloat d_quacin_thr = cin_thr - qua_thr;
+ /*
+ * Fou(r) vertical differences:
+ */
+ const gfloat d_unodos_fou = dos_fou - uno_fou;
+ const gfloat d_dostre_fou = tre_fou - dos_fou;
+ const gfloat d_trequa_fou = qua_fou - tre_fou;
+ const gfloat d_quacin_fou = cin_fou - qua_fou;
+ /*
+ * Dos horizontal differences:
+ */
+ const gfloat d_dos_twothr = dos_thr - dos_two;
+ const gfloat d_dos_thrfou = dos_fou - dos_thr;
+ const gfloat d_dos_foufiv = dos_fiv - dos_fou;
+ /*
+ * Tre(s) horizontal differences:
+ */
+ const gfloat d_tre_onetwo = tre_two - tre_one;
+ const gfloat d_tre_twothr = tre_thr - tre_two;
+ const gfloat d_tre_thrfou = tre_fou - tre_thr;
+ const gfloat d_tre_foufiv = tre_fiv - tre_fou;
+ /*
+ * Qua(ttro) horizontal differences:
+ */
+ const gfloat d_qua_onetwo = qua_two - qua_one;
+ const gfloat d_qua_twothr = qua_thr - qua_two;
+ const gfloat d_qua_thrfou = qua_fou - qua_thr;
+ const gfloat d_qua_foufiv = qua_fiv - qua_fou;
+
+ /*
+ * Recyclable vertical products and squares:
+ */
+ const gfloat d_dostre_times_trequa_two = d_dostre_two * d_trequa_two;
+ const gfloat d_trequa_two_sq = d_trequa_two * d_trequa_two;
+ const gfloat d_trequa_times_quacin_two = d_quacin_two * d_trequa_two;
+
+ const gfloat d_unodos_times_dostre_thr = d_unodos_thr * d_dostre_thr;
+ const gfloat d_dostre_thr_sq = d_dostre_thr * d_dostre_thr;
+ const gfloat d_dostre_times_trequa_thr = d_trequa_thr * d_dostre_thr;
+ const gfloat d_trequa_times_quacin_thr = d_trequa_thr * d_quacin_thr;
+ const gfloat d_quacin_thr_sq = d_quacin_thr * d_quacin_thr;
+
+ const gfloat d_unodos_times_dostre_fou = d_unodos_fou * d_dostre_fou;
+ const gfloat d_dostre_fou_sq = d_dostre_fou * d_dostre_fou;
+ const gfloat d_dostre_times_trequa_fou = d_trequa_fou * d_dostre_fou;
+ const gfloat d_trequa_times_quacin_fou = d_trequa_fou * d_quacin_fou;
+ const gfloat d_quacin_fou_sq = d_quacin_fou * d_quacin_fou;
+ /*
+ * Recyclable horizontal products and squares:
+ */
+ const gfloat d_dos_twothr_times_thrfou = d_dos_twothr * d_dos_thrfou;
+ const gfloat d_dos_thrfou_sq = d_dos_thrfou * d_dos_thrfou;
+ const gfloat d_dos_thrfou_times_foufiv = d_dos_foufiv * d_dos_thrfou;
+
+ const gfloat d_tre_onetwo_times_twothr = d_tre_onetwo * d_tre_twothr;
+ const gfloat d_tre_twothr_sq = d_tre_twothr * d_tre_twothr;
+ const gfloat d_tre_twothr_times_thrfou = d_tre_thrfou * d_tre_twothr;
+ const gfloat d_tre_thrfou_times_foufiv = d_tre_thrfou * d_tre_foufiv;
+ const gfloat d_tre_foufiv_sq = d_tre_foufiv * d_tre_foufiv;
+
+ const gfloat d_qua_onetwo_times_twothr = d_qua_onetwo * d_qua_twothr;
+ const gfloat d_qua_twothr_sq = d_qua_twothr * d_qua_twothr;
+ const gfloat d_qua_twothr_times_thrfou = d_qua_thrfou * d_qua_twothr;
+ const gfloat d_qua_thrfou_times_foufiv = d_qua_thrfou * d_qua_foufiv;
+ const gfloat d_qua_foufiv_sq = d_qua_foufiv * d_qua_foufiv;
+
+ /*
+ * Minmod slopes and first level pixel values:
+ */
+ const gfloat dos_thr_y =
+ FAST_MINMOD( d_dostre_thr, d_unodos_thr,
+ d_dostre_thr_sq,
+ d_unodos_times_dostre_thr );
+ const gfloat tre_thr_y =
+ FAST_MINMOD( d_dostre_thr, d_trequa_thr,
+ d_dostre_thr_sq,
+ d_dostre_times_trequa_thr );
+
+ const gfloat val_uno_two_1 =
+ .5 * ( dos_thr + tre_thr )
+ +
+ .25 * ( dos_thr_y - tre_thr_y );
+
+ const gfloat qua_thr_y =
+ FAST_MINMOD( d_quacin_thr, d_trequa_thr,
+ d_quacin_thr_sq,
+ d_trequa_times_quacin_thr );
+
+ const gfloat val_tre_two_1 =
+ .5 * ( tre_thr + qua_thr )
+ +
+ .25 * ( tre_thr_y - qua_thr_y );
+
+ const gfloat tre_fou_y =
+ FAST_MINMOD( d_dostre_fou, d_trequa_fou,
+ d_dostre_fou_sq,
+ d_dostre_times_trequa_fou );
+ const gfloat qua_fou_y =
+ FAST_MINMOD( d_quacin_fou, d_trequa_fou,
+ d_quacin_fou_sq,
+ d_trequa_times_quacin_fou );
+
+ const gfloat val_tre_fou_1 =
+ .5 * ( tre_fou + qua_fou )
+ +
+ .25 * ( tre_fou_y - qua_fou_y );
+
+ const gfloat tre_two_x =
+ FAST_MINMOD( d_tre_twothr, d_tre_onetwo,
+ d_tre_twothr_sq,
+ d_tre_onetwo_times_twothr );
+ const gfloat tre_thr_x =
+ FAST_MINMOD( d_tre_twothr, d_tre_thrfou,
+ d_tre_twothr_sq,
+ d_tre_twothr_times_thrfou );
+
+ const gfloat val_dos_one_1 =
+ .5 * ( tre_two + tre_thr )
+ +
+ .25 * ( tre_two_x - tre_thr_x );
+
+ const gfloat tre_fou_x =
+ FAST_MINMOD( d_tre_foufiv, d_tre_thrfou,
+ d_tre_foufiv_sq,
+ d_tre_thrfou_times_foufiv );
+
+ const gfloat tre_thr_x_minus_tre_fou_x =
+ tre_thr_x - tre_fou_x;
+
+ const gfloat val_dos_thr_1 =
+ .5 * ( tre_thr + tre_fou )
+ +
+ .25 * tre_thr_x_minus_tre_fou_x;
+
+ const gfloat qua_thr_x =
+ FAST_MINMOD( d_qua_twothr, d_qua_thrfou,
+ d_qua_twothr_sq,
+ d_qua_twothr_times_thrfou );
+ const gfloat qua_fou_x =
+ FAST_MINMOD( d_qua_foufiv, d_qua_thrfou,
+ d_qua_foufiv_sq,
+ d_qua_thrfou_times_foufiv );
+
+ const gfloat qua_thr_x_minus_qua_fou_x =
+ qua_thr_x - qua_fou_x;
+
+ const gfloat val_qua_thr_1 =
+ .5 * ( qua_thr + qua_fou )
+ +
+ .25 * qua_thr_x_minus_qua_fou_x;
+ const gfloat val_tre_thr_1 =
+ .125 * ( tre_thr_x_minus_tre_fou_x + qua_thr_x_minus_qua_fou_x )
+ +
+ .5 * ( val_tre_two_1 + val_tre_fou_1 );
+
+ const gfloat dos_fou_y =
+ FAST_MINMOD( d_dostre_fou, d_unodos_fou,
+ d_dostre_fou_sq,
+ d_unodos_times_dostre_fou );
+ const gfloat dos_thr_x =
+ FAST_MINMOD( d_dos_thrfou, d_dos_twothr,
+ d_dos_thrfou_sq,
+ d_dos_twothr_times_thrfou );
+ const gfloat dos_fou_x =
+ FAST_MINMOD( d_dos_thrfou, d_dos_foufiv,
+ d_dos_thrfou_sq,
+ d_dos_thrfou_times_foufiv );
+
+ const gfloat val_uno_thr_1 =
+ .25 * ( dos_fou - tre_thr )
+ +
+ .125 * ( dos_fou_y - tre_fou_y + dos_thr_x - dos_fou_x )
+ +
+ .5 * ( val_uno_two_1 + val_dos_thr_1 );
+
+ const gfloat qua_two_x =
+ FAST_MINMOD( d_qua_twothr, d_qua_onetwo,
+ d_qua_twothr_sq,
+ d_qua_onetwo_times_twothr );
+ const gfloat tre_two_y =
+ FAST_MINMOD( d_trequa_two, d_dostre_two,
+ d_trequa_two_sq,
+ d_dostre_times_trequa_two );
+ const gfloat qua_two_y =
+ FAST_MINMOD( d_trequa_two, d_quacin_two,
+ d_trequa_two_sq,
+ d_trequa_times_quacin_two );
+
+ const gfloat val_tre_one_1 =
+ .25 * ( qua_two - tre_thr )
+ +
+ .125 * ( qua_two_x - qua_thr_x + tre_two_y - qua_two_y )
+ +
+ .5 * ( val_dos_one_1 + val_tre_two_1 );
+
+ /*
+ * Return level 1 stencil values:
+ */
+ *uno_two_1 = val_uno_two_1;
+ *uno_thr_1 = val_uno_thr_1;
+ *dos_one_1 = val_dos_one_1;
+ *dos_two_1 = tre_thr;
+ *dos_thr_1 = val_dos_thr_1;
+ *dos_fou_1 = tre_fou;
+ *tre_one_1 = val_tre_one_1;
+ *tre_two_1 = val_tre_two_1;
+ *tre_thr_1 = val_tre_thr_1;
+ *tre_fou_1 = val_tre_fou_1;
+ *qua_two_1 = qua_thr;
+ *qua_thr_1 = val_qua_thr_1;
+}
+
+static inline gfloat
+nohalo_step2 (const gfloat w_times_z,
+ const gfloat x_times_z_over_4_plus_x_times_y_over_8,
+ const gfloat w_times_y_over_4_plus_x_times_y_over_8,
+ const gfloat x_times_y_over_8,
+ const gfloat uno_two,
+ const gfloat uno_thr,
+ const gfloat dos_one,
+ const gfloat dos_two,
+ const gfloat dos_thr,
+ const gfloat dos_fou,
+ const gfloat tre_one,
+ const gfloat tre_two,
+ const gfloat tre_thr,
+ const gfloat tre_fou,
+ const gfloat qua_two,
+ const gfloat qua_thr)
+{
+ /*
+ * THE STENCIL OF INPUT VALUES:
+ *
+ * The footprint (stencil) of Nohalo level 1 is the same as, say,
+ * Catmull-Rom, with the exception that the four corner values are
+ * not used:
+ *
+ * (ix,iy-1) (ix+1,iy-1)
+ * = uno_two = uno_thr
+ *
+ * (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy)
+ * = dos_one = dos_two = dos_thr = dos_fou
+ *
+ * (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1)
+ * = tre_one = tre_two = tre_thr = tre_fou
+ *
+ * (ix,iy+2) (ix+1,iy+2)
+ * = qua_two = qua_thr
+ *
+ * Here, ix is the (pseudo-)floor of the requested left-to-right
+ * location, iy is the floor of the requested up-to-down location.
+ *
+ * Pointer arithmetic is used to implicitly reflect the input
+ * stencil so that the requested pixel location is closer to
+ * dos_two, The above consequently corresponds to the case in which
+ * absolute_x is closer to ix than ix+1, and absolute_y is closer to
+ * iy than iy+1. For example, if relative_x_is_rite = 1 but
+ * relative_y_is_down = 0 (see below), then dos_two corresponds to
+ * (ix+1,iy), dos_thr corresponds to (ix,iy) etc, and the three
+ * missing double density values are halfway between dos_two and
+ * dos_thr, halfway between dos_two and tre_two, and at the average
+ * of the four central positions.
+ *
+ * The following code assumes that the stencil has been suitably
+ * reflected.
+ */
+
+ /*
+ * Computation of the nonlinear slopes: If two consecutive pixel
+ * value differences have the same sign, the smallest one (in
+ * absolute value) is taken to be the corresponding slope; if the
+ * two consecutive pixel value differences don't have the same sign,
+ * the corresponding slope is set to 0. In other words, apply minmod
+ * to comsecutive differences.
+ *
+ * Dos(s) horizontal differences:
+ */
+ const gfloat prem_dos = dos_two - dos_one;
+ const gfloat deux_dos = dos_thr - dos_two;
+ const gfloat troi_dos = dos_fou - dos_thr;
+ /*
+ * Tre(s) horizontal differences:
+ */
+ const gfloat prem_tre = tre_two - tre_one;
+ const gfloat deux_tre = tre_thr - tre_two;
+ const gfloat troi_tre = tre_fou - tre_thr;
+ /*
+ * Two vertical differences:
+ */
+ const gfloat prem_two = dos_two - uno_two;
+ const gfloat deux_two = tre_two - dos_two;
+ const gfloat troi_two = qua_two - tre_two;
+ /*
+ * Thr(ee) vertical differences:
+ */
+ const gfloat prem_thr = dos_thr - uno_thr;
+ const gfloat deux_thr = tre_thr - dos_thr;
+ const gfloat troi_thr = qua_thr - tre_thr;
+
+ /*
+ * Products useful for minmod:
+ */
+ const gfloat deux_prem_dos = deux_dos * prem_dos;
+ const gfloat deux_deux_dos = deux_dos * deux_dos;
+ const gfloat deux_troi_dos = deux_dos * troi_dos;
+
+ const gfloat deux_prem_two = deux_two * prem_two;
+ const gfloat deux_deux_two = deux_two * deux_two;
+ const gfloat deux_troi_two = deux_two * troi_two;
+
+ const gfloat deux_prem_tre = deux_tre * prem_tre;
+ const gfloat deux_deux_tre = deux_tre * deux_tre;
+ const gfloat deux_troi_tre = deux_tre * troi_tre;
+
+ const gfloat deux_prem_thr = deux_thr * prem_thr;
+ const gfloat deux_deux_thr = deux_thr * deux_thr;
+ const gfloat deux_troi_thr = deux_thr * troi_thr;
+
+ /*
+ * Terms computed here to put space between the computation of key
+ * quantities and the related conditionals:
+ */
+ const gfloat twice_dos_two_plus_dos_thr = ( dos_two + dos_thr ) * 2.f;
+ const gfloat twice_dos_two_plus_tre_two = ( dos_two + tre_two ) * 2.f;
+ const gfloat twice_deux_thr_plus_deux_dos = ( deux_thr + deux_dos ) * 2.f;
+
+ /*
+ * Compute the needed "right" (at the boundary between one input
+ * pixel areas) double resolution pixel value:
+ */
+ const gfloat four_times_dos_twothr =
+ twice_dos_two_plus_dos_thr
+ +
+ FAST_MINMOD( deux_dos, prem_dos, deux_deux_dos, deux_prem_dos )
+ -
+ FAST_MINMOD( deux_dos, troi_dos, deux_deux_dos, deux_troi_dos );
+
+ /*
+ * Compute the needed "down" double resolution pixel value:
+ */
+ const gfloat four_times_dostre_two =
+ twice_dos_two_plus_tre_two
+ +
+ FAST_MINMOD( deux_two, prem_two, deux_deux_two, deux_prem_two )
+ -
+ FAST_MINMOD( deux_two, troi_two, deux_deux_two, deux_troi_two );
+
+ /*
+ * Compute the "diagonal" (at the boundary between four input pixel
+ * areas) double resolution pixel value:
+ */
+ const gfloat partial_eight_times_dostre_twothr =
+ twice_deux_thr_plus_deux_dos
+ +
+ FAST_MINMOD( deux_tre, prem_tre, deux_deux_tre, deux_prem_tre )
+ -
+ FAST_MINMOD( deux_tre, troi_tre, deux_deux_tre, deux_troi_tre )
+ +
+ FAST_MINMOD( deux_thr, prem_thr, deux_deux_thr, deux_prem_thr )
+ -
+ FAST_MINMOD( deux_thr, troi_thr, deux_deux_thr, deux_troi_thr );
+
+ /*
+ * Compute the output pixel value with bilinear applied to the
+ * reconstructed double density data:
+ */
+ const gfloat newval =
+ w_times_z * dos_two
+ +
+ x_times_z_over_4_plus_x_times_y_over_8 * four_times_dos_twothr
+ +
+ w_times_y_over_4_plus_x_times_y_over_8 * four_times_dostre_two
+ +
+ x_times_y_over_8 * partial_eight_times_dostre_twothr;
+
+ return newval;
+}
+
+#define NOHALO_SELECT_REFLECT(tl,tr,bl,br) ( \
+ ( \
+ (tl) * is_top_left \
+ + \
+ (tr) * is_top_rite \
+ ) \
+ + \
+ ( \
+ (bl) * is_bot_left \
+ + \
+ (br) * is_bot_rite ) \
+ )
+
+static void
+gegl_sampler_upsharp_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output)
+{
+ /*
+ * Needed constants related to the input pixel value pointer
+ * provided by gegl_sampler_get_ptr (self, ix, iy). pixels_per_row
+ * corresponds to fetch_rectangle.width in gegl_sampler_get_ptr.
+ */
+ const gint channels = 4;
+ const gint pixels_per_row = 64;
+ const gint row_skip = channels * pixels_per_row;
+
+ const gint ix_0 = FAST_PSEUDO_FLOOR (absolute_x + .5);
+ const gint iy_0 = FAST_PSEUDO_FLOOR (absolute_y + .5);
+
+ const gfloat* restrict input_bptr =
+ (gfloat*) gegl_sampler_get_ptr (self, ix_0, iy_0);
+
+ const gfloat x_0 = absolute_x - ix_0;
+ const gfloat y_0 = absolute_y - iy_0;
+
+ const gint sign_of_x_0 = 2 * ( x_0 >= 0. ) - 1;
+ const gint sign_of_y_0 = 2 * ( y_0 >= 0. ) - 1;
+
+ const gint shift_forw_1_pix = sign_of_x_0 * channels;
+ const gint shift_forw_1_row = sign_of_y_0 * row_skip;
+
+ const gint shift_back_1_pix = -shift_forw_1_pix;
+ const gint shift_back_1_row = -shift_forw_1_row;
+
+ const gint shift_back_2_pix = 2 * shift_back_1_pix;
+ const gint shift_back_2_row = 2 * shift_back_1_row;
+ const gint shift_forw_2_pix = 2 * shift_forw_1_pix;
+ const gint shift_forw_2_row = 2 * shift_forw_1_row;
+
+ const gint uno_thr_shift = shift_back_2_row;
+ const gint uno_fou_shift = shift_forw_1_pix + shift_back_2_row;
+
+ const gint dos_two_shift = shift_back_1_pix + shift_back_1_row;
+ const gint dos_thr_shift = shift_back_1_row;
+ const gint dos_fou_shift = shift_forw_1_pix + shift_back_1_row;
+ const gint dos_fiv_shift = shift_forw_2_pix + shift_back_1_row;
+
+ const gint tre_one_shift = shift_back_2_pix;
+ const gint tre_two_shift = shift_back_1_pix;
+ const gint tre_thr_shift = 0;
+ const gint tre_fou_shift = shift_forw_1_pix;
+ const gint tre_fiv_shift = shift_forw_2_pix;
+
+ const gint qua_one_shift = shift_back_2_pix + shift_forw_1_row;
+ const gint qua_two_shift = shift_back_1_pix + shift_forw_1_row;
+ const gint qua_thr_shift = shift_forw_1_row;
+ const gint qua_fou_shift = shift_forw_1_pix + shift_forw_1_row;
+ const gint qua_fiv_shift = shift_forw_2_pix + shift_forw_1_row;
+
+ const gint cin_two_shift = shift_back_1_pix + shift_forw_2_row;
+ const gint cin_thr_shift = shift_forw_2_row;
+ const gint cin_fou_shift = shift_forw_1_pix + shift_forw_2_row;
+
+ /*
+ * Channel by channel computation of the new pixel values:
+ */
+ gfloat uno_two_0, uno_thr_0;
+ gfloat dos_one_0, dos_two_0, dos_thr_0, dos_fou_0;
+ gfloat tre_one_0, tre_two_0, tre_thr_0, tre_fou_0;
+ gfloat qua_two_0, qua_thr_0;
+
+ gfloat uno_two_1, uno_thr_1;
+ gfloat dos_one_1, dos_two_1, dos_thr_1, dos_fou_1;
+ gfloat tre_one_1, tre_two_1, tre_thr_1, tre_fou_1;
+ gfloat qua_two_1, qua_thr_1;
+
+ gfloat uno_two_2, uno_thr_2;
+ gfloat dos_one_2, dos_two_2, dos_thr_2, dos_fou_2;
+ gfloat tre_one_2, tre_two_2, tre_thr_2, tre_fou_2;
+ gfloat qua_two_2, qua_thr_2;
+
+ gfloat uno_two_3, uno_thr_3;
+ gfloat dos_one_3, dos_two_3, dos_thr_3, dos_fou_3;
+ gfloat tre_one_3, tre_two_3, tre_thr_3, tre_fou_3;
+ gfloat qua_two_3, qua_thr_3;
+
+ /*
+ * The newval array will contain one computed resampled value per
+ * channel:
+ */
+ gfloat newval[channels];
+
+ /*
+ * First channel:
+ */
+ nohalo_step1 (input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ &uno_two_0,
+ &uno_thr_0,
+ &dos_one_0,
+ &dos_two_0,
+ &dos_thr_0,
+ &dos_fou_0,
+ &tre_one_0,
+ &tre_two_0,
+ &tre_thr_0,
+ &tre_fou_0,
+ &qua_two_0,
+ &qua_thr_0);
+
+ {
+ /*
+ * Computation of the needed weights (coefficients).
+ */
+ const gfloat x = ( 2 * sign_of_x_0 ) * x_0 - .5;
+ const gfloat y = ( 2 * sign_of_y_0 ) * y_0 - .5;
+
+ const gint x_is_rite = ( x >= 0. );
+ const gint y_is_down = ( y >= 0. );
+ const gint x_is_left = !x_is_rite;
+ const gint y_is___up = !y_is_down;
+
+ const gint is_bot_rite = x_is_rite & y_is_down;
+ const gint is_bot_left = x_is_left & y_is_down;
+ const gint is_top_rite = x_is_rite & y_is___up;
+ const gint is_top_left = x_is_left & y_is___up;
+
+ const gint sign_of_x = 2 * x_is_rite - 1;
+ const gint sign_of_y = 2 * y_is_down - 1;
+
+ const gfloat w_1 = ( 2 * sign_of_x ) * x;
+ const gfloat z_1 = ( 2 * sign_of_y ) * y;
+ const gfloat x_1 = 1. - w_1;
+ const gfloat w_1_times_z_1 = w_1 * z_1;
+ const gfloat x_1_times_z_1 = x_1 * z_1;
+
+ const gfloat w_1_times_y_1_over_4 = .25 * ( w_1 - w_1_times_z_1 );
+ const gfloat x_1_times_z_1_over_4 = .25 * x_1_times_z_1;
+ const gfloat x_1_times_y_1_over_8 = .125 * ( x_1 - x_1_times_z_1 );
+
+ const gfloat w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8 =
+ w_1_times_y_1_over_4 + x_1_times_y_1_over_8;
+ const gfloat x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8 =
+ x_1_times_z_1_over_4 + x_1_times_y_1_over_8;
+
+ newval[0] =
+ nohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ NOHALO_SELECT_REFLECT( uno_two_0, uno_thr_0, qua_two_0, qua_thr_0 ),
+ NOHALO_SELECT_REFLECT( uno_thr_0, uno_two_0, qua_thr_0, qua_two_0 ),
+ NOHALO_SELECT_REFLECT( dos_one_0, dos_fou_0, tre_one_0, tre_fou_0 ),
+ NOHALO_SELECT_REFLECT( dos_two_0, dos_thr_0, tre_two_0, tre_thr_0 ),
+ NOHALO_SELECT_REFLECT( dos_thr_0, dos_two_0, tre_thr_0, tre_two_0 ),
+ NOHALO_SELECT_REFLECT( dos_fou_0, dos_one_0, tre_fou_0, tre_one_0 ),
+ NOHALO_SELECT_REFLECT( tre_one_0, tre_fou_0, dos_one_0, dos_fou_0 ),
+ NOHALO_SELECT_REFLECT( tre_two_0, tre_thr_0, dos_two_0, dos_thr_0 ),
+ NOHALO_SELECT_REFLECT( tre_thr_0, tre_two_0, dos_thr_0, dos_two_0 ),
+ NOHALO_SELECT_REFLECT( tre_fou_0, tre_one_0, dos_fou_0, dos_one_0 ),
+ NOHALO_SELECT_REFLECT( qua_two_0, qua_thr_0, uno_two_0, uno_thr_0 ),
+ NOHALO_SELECT_REFLECT( qua_thr_0, qua_two_0, uno_thr_0, uno_two_0 ));
+
+ /*
+ * Second channel:
+ *
+ * Shift input pointer by one channel:
+ */
+ input_bptr++;
+
+ nohalo_step1 (input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ &uno_two_1,
+ &uno_thr_1,
+ &dos_one_1,
+ &dos_two_1,
+ &dos_thr_1,
+ &dos_fou_1,
+ &tre_one_1,
+ &tre_two_1,
+ &tre_thr_1,
+ &tre_fou_1,
+ &qua_two_1,
+ &qua_thr_1);
+
+ newval[1] =
+ nohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ NOHALO_SELECT_REFLECT( uno_two_1, uno_thr_1, qua_two_1, qua_thr_1 ),
+ NOHALO_SELECT_REFLECT( uno_thr_1, uno_two_1, qua_thr_1, qua_two_1 ),
+ NOHALO_SELECT_REFLECT( dos_one_1, dos_fou_1, tre_one_1, tre_fou_1 ),
+ NOHALO_SELECT_REFLECT( dos_two_1, dos_thr_1, tre_two_1, tre_thr_1 ),
+ NOHALO_SELECT_REFLECT( dos_thr_1, dos_two_1, tre_thr_1, tre_two_1 ),
+ NOHALO_SELECT_REFLECT( dos_fou_1, dos_one_1, tre_fou_1, tre_one_1 ),
+ NOHALO_SELECT_REFLECT( tre_one_1, tre_fou_1, dos_one_1, dos_fou_1 ),
+ NOHALO_SELECT_REFLECT( tre_two_1, tre_thr_1, dos_two_1, dos_thr_1 ),
+ NOHALO_SELECT_REFLECT( tre_thr_1, tre_two_1, dos_thr_1, dos_two_1 ),
+ NOHALO_SELECT_REFLECT( tre_fou_1, tre_one_1, dos_fou_1, dos_one_1 ),
+ NOHALO_SELECT_REFLECT( qua_two_1, qua_thr_1, uno_two_1, uno_thr_1 ),
+ NOHALO_SELECT_REFLECT( qua_thr_1, qua_two_1, uno_thr_1, uno_two_1 ));
+
+ input_bptr++;
+
+ nohalo_step1 (input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ &uno_two_2,
+ &uno_thr_2,
+ &dos_one_2,
+ &dos_two_2,
+ &dos_thr_2,
+ &dos_fou_2,
+ &tre_one_2,
+ &tre_two_2,
+ &tre_thr_2,
+ &tre_fou_2,
+ &qua_two_2,
+ &qua_thr_2);
+
+ newval[2] =
+ nohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ NOHALO_SELECT_REFLECT( uno_two_2, uno_thr_2, qua_two_2, qua_thr_2 ),
+ NOHALO_SELECT_REFLECT( uno_thr_2, uno_two_2, qua_thr_2, qua_two_2 ),
+ NOHALO_SELECT_REFLECT( dos_one_2, dos_fou_2, tre_one_2, tre_fou_2 ),
+ NOHALO_SELECT_REFLECT( dos_two_2, dos_thr_2, tre_two_2, tre_thr_2 ),
+ NOHALO_SELECT_REFLECT( dos_thr_2, dos_two_2, tre_thr_2, tre_two_2 ),
+ NOHALO_SELECT_REFLECT( dos_fou_2, dos_one_2, tre_fou_2, tre_one_2 ),
+ NOHALO_SELECT_REFLECT( tre_one_2, tre_fou_2, dos_one_2, dos_fou_2 ),
+ NOHALO_SELECT_REFLECT( tre_two_2, tre_thr_2, dos_two_2, dos_thr_2 ),
+ NOHALO_SELECT_REFLECT( tre_thr_2, tre_two_2, dos_thr_2, dos_two_2 ),
+ NOHALO_SELECT_REFLECT( tre_fou_2, tre_one_2, dos_fou_2, dos_one_2 ),
+ NOHALO_SELECT_REFLECT( qua_two_2, qua_thr_2, uno_two_2, uno_thr_2 ),
+ NOHALO_SELECT_REFLECT( qua_thr_2, qua_two_2, uno_thr_2, uno_two_2 ));
+
+ input_bptr++;
+
+ nohalo_step1 (input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ &uno_two_3,
+ &uno_thr_3,
+ &dos_one_3,
+ &dos_two_3,
+ &dos_thr_3,
+ &dos_fou_3,
+ &tre_one_3,
+ &tre_two_3,
+ &tre_thr_3,
+ &tre_fou_3,
+ &qua_two_3,
+ &qua_thr_3);
+
+ newval[3] =
+ nohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ NOHALO_SELECT_REFLECT( uno_two_3, uno_thr_3, qua_two_3, qua_thr_3 ),
+ NOHALO_SELECT_REFLECT( uno_thr_3, uno_two_3, qua_thr_3, qua_two_3 ),
+ NOHALO_SELECT_REFLECT( dos_one_3, dos_fou_3, tre_one_3, tre_fou_3 ),
+ NOHALO_SELECT_REFLECT( dos_two_3, dos_thr_3, tre_two_3, tre_thr_3 ),
+ NOHALO_SELECT_REFLECT( dos_thr_3, dos_two_3, tre_thr_3, tre_two_3 ),
+ NOHALO_SELECT_REFLECT( dos_fou_3, dos_one_3, tre_fou_3, tre_one_3 ),
+ NOHALO_SELECT_REFLECT( tre_one_3, tre_fou_3, dos_one_3, dos_fou_3 ),
+ NOHALO_SELECT_REFLECT( tre_two_3, tre_thr_3, dos_two_3, dos_thr_3 ),
+ NOHALO_SELECT_REFLECT( tre_thr_3, tre_two_3, dos_thr_3, dos_two_3 ),
+ NOHALO_SELECT_REFLECT( tre_fou_3, tre_one_3, dos_fou_3, dos_one_3 ),
+ NOHALO_SELECT_REFLECT( qua_two_3, qua_thr_3, uno_two_3, uno_thr_3 ),
+ NOHALO_SELECT_REFLECT( qua_thr_3, qua_two_3, uno_thr_3, uno_two_3 ));
+
+ /*
+ * Ship out the array of new pixel values:
+ */
+ babl_process (babl_fish (self->interpolate_format, self->format),
+ newval, output, 1);
+ }
+}
+
+static void
+set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ /* G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec); */
+}
+
+static void
+get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec)
+{
+ /* G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec); */
+}
diff --git a/gegl/buffer/gegl-sampler-upsharp.h b/gegl/buffer/gegl-sampler-upsharp.h
new file mode 100644
index 0000000..d483991
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsharp.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_UPSHARP_H__
+#define __GEGL_SAMPLER_UPSHARP_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_UPSHARP (gegl_sampler_upsharp_get_type ())
+#define GEGL_SAMPLER_UPSHARP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_UPSHARP, GeglSamplerUpsharp))
+#define GEGL_SAMPLER_UPSHARP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_UPSHARP, GeglSamplerUpsharpClass))
+#define GEGL_IS_SAMPLER_UPSHARP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_UPSHARP))
+#define GEGL_IS_SAMPLER_UPSHARP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_UPSHARP))
+#define GEGL_SAMPLER_UPSHARP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_UPSHARP, GeglSamplerUpsharpClass))
+
+typedef struct _GeglSamplerUpsharp GeglSamplerUpsharp;
+typedef struct _GeglSamplerUpsharpClass GeglSamplerUpsharpClass;
+
+struct _GeglSamplerUpsharp
+{
+ GeglSampler parent_instance;
+};
+
+struct _GeglSamplerUpsharpClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_upsharp_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-upsize.c b/gegl/buffer/gegl-sampler-upsize.c
new file mode 100644
index 0000000..acf1913
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsize.c
@@ -0,0 +1,679 @@
+/* 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) Eric Daoust, Nicolas Robidoux, Adam Turcotte and Ã?yvind
+ * Kolås.
+ */
+
+/*
+ * ==============
+ * UPSIZE SAMPLER
+ * ==============
+ *
+ * "Upsize" is the GEGL name of the symmetrized MP-quadratic
+ * (Monotonicity Preserving with derivative estimated by fitting a
+ * parabola (quadratic polynomial)) method, which essentially is
+ * Catmull-Rom with derivatives clamped so as to ensure monotonicity.
+ *
+ * 1D MP-quadratic (for curve, not surface, interpolation) is
+ * described in
+ *
+ * Accurate Monotone Cubic Interpolation, by Hung T. Huynh, published
+ * in the SIAM Journal on Numerical Analysis, Volume 30, Issue 1
+ * (February 1993), pages 57-100, 1993. ISSN:0036-1429.
+ *
+ * and in NASA technical memorandum 103789, which can be downloaded
+ * from http://
+ * ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19910011517_1991011517.pdf
+ *
+ * In order to ensure reflexion symmetry about diagonal lines, 1D
+ * MP-quadratic is performed two different ways---horizontally then
+ * vertically, and vertically then horizontally---and
+ * averaged. (Symmetry about 45 degree lines is not automatically
+ * respected because MP-quadratic is a nonlinear method: interpolating
+ * horizontally then vertically does not necessarily give the same as
+ * interpolating vertically then horizontally.)
+ */
+/*
+ * Acknowledgements: Eric Daoust and Adam Turcotte's GEGL programming
+ * funded by Google Summer of Code 2009. Nicolas Robidoux thanks
+ * Chantal Racette, Ralf Meyer, Minglun Gong, John Cupitt, Geert
+ * Jordaens, �yvind Kolås and Sven Neumann for useful comments and
+ * code.
+ */
+
+#include "config.h"
+#include <glib-object.h>
+
+#include "gegl.h"
+#include "gegl-types-internal.h"
+#include "gegl-buffer-private.h"
+#include "gegl-sampler-upsize.h"
+
+/*
+ * FAST_MINMOD is an implementation of the minmod function which only
+ * needs two conditional moves. FAST_MINMOD(a,b,a_times_a,a_times_b)
+ * "returns" minmod(a,b). The parameter ("input") a_times_a is assumed
+ * to contain the square of a; a_times_b, the product of a and b.
+ *
+ * This version is most suitable for images with flat (constant)
+ * colour areas, since a, which is a pixel difference, will often be
+ * 0, in which case both forward branches are likely.
+ *
+ * For uncompressed natural images in high bit depth (images for which
+ * the slopes a and b are unlikely to be equal to zero or be equal to
+ * each other), we recommend using
+ *
+ * ( (a_times_b)>=0. ? 1. : 0. ) * ( (a_times_b)<(a_times_a) ? (b) : (a) )
+ *
+ * instead. With this second version, the forward branch of the second
+ * conditional move is taken when |b|>|a| and when a*b<0. However, the
+ * "else" branch is taken when a=0 (or when a=b), which is why the
+ * above version is not recommended for images with regions with
+ * constant pixel values (or regions with pixel values which vary
+ * bilinearly).
+ */
+#define FAST_MINMOD(a,b,a_times_a,a_times_b) \
+ ( (a_times_b)>=0.f ? 1.f : 0.f ) * ( (a_times_a)<=(a_times_b) ? (a) : (b) )
+
+/*
+ * 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) ( (gint)(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
+};
+
+static inline gfloat mp_quadratic (const gfloat one,
+ const gfloat two,
+ const gfloat thr,
+ const gfloat fou,
+ const gfloat coef1,
+ const gfloat coef2,
+ const gfloat coef3);
+
+static void gegl_sampler_upsize_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output );
+
+static void set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec);
+
+static void get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec);
+
+G_DEFINE_TYPE (GeglSamplerUpsize, gegl_sampler_upsize, GEGL_TYPE_SAMPLER)
+
+static void
+gegl_sampler_upsize_class_init (GeglSamplerUpsizeClass *klass)
+{
+ GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ sampler_class->get = gegl_sampler_upsize_get;
+ }
+
+static void
+gegl_sampler_upsize_init (GeglSamplerUpsize *self)
+{
+ GEGL_SAMPLER (self)->context_rect.x = -1;
+ GEGL_SAMPLER (self)->context_rect.y = -1;
+ GEGL_SAMPLER (self)->context_rect.width = 4;
+ GEGL_SAMPLER (self)->context_rect.height = 4;
+ GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
+}
+
+static inline gfloat mp_quadratic (const gfloat one,
+ const gfloat two,
+ const gfloat thr,
+ const gfloat fou,
+ const gfloat coef_thr_point,
+ const gfloat half_coef_two_slope,
+ const gfloat half_coef_thr_slope)
+{
+ /*
+ * Computation of the slopes and slope limiters:
+ *
+ * Differences:
+ */
+ const gfloat prem = two - one;
+ const gfloat deux = thr - two;
+ const gfloat troi = fou - thr;
+
+ const gfloat part = two + deux * coef_thr_point;
+
+ /*
+ * Products useful for the minmod computations:
+ */
+ const gfloat deux_prem = deux * prem;
+ const gfloat deux_deux = deux * deux;
+ const gfloat deux_troi = deux * troi;
+
+ /*
+ * Twice the horizontal limiter slopes (twice_lx) interwoven with
+ * twice the Catmull-Rom slopes (twice_sx). Because we have twice
+ * the Catmull-Rom slope, we need to use 6 times the minmod slope
+ * instead of the usual 3 (specified by the cited article).
+ */
+ const gfloat twice_lx_two =
+ 6.f * FAST_MINMOD (deux, prem, deux_deux, deux_prem);
+ const gfloat twice_sx_two = deux + prem;
+ const gfloat twice_lx_thr =
+ 6.f * FAST_MINMOD (deux, troi, deux_deux, deux_troi);
+ const gfloat twice_sx_thr = deux + troi;
+
+ const gfloat sx_sx_two = twice_sx_two * twice_sx_two;
+ const gfloat sx_lx_two = twice_sx_two * twice_lx_two;
+ const gfloat sx_sx_thr = twice_sx_thr * twice_sx_thr;
+ const gfloat sx_lx_thr = twice_sx_thr * twice_lx_thr;
+
+ /*
+ * Result of the first interpolations along horizontal lines. Note
+ * that the Catmull-Rom slope almost always satisfies the
+ * monotonicity constraint, hence twice_sx is "likely" to be the one
+ * selected by minmod.
+ */
+ const gfloat newval =
+ part +
+ + half_coef_two_slope
+ * FAST_MINMOD (twice_sx_two, twice_lx_two, sx_sx_two, sx_lx_two)
+ + half_coef_thr_slope
+ * FAST_MINMOD (twice_sx_thr, twice_lx_thr, sx_sx_thr, sx_lx_thr);
+
+ return newval;
+}
+
+static inline gfloat
+symmetrized_monotone_catmull_rom (const gfloat coef_rite_point,
+ const gfloat coef_bot_point,
+ const gfloat half_coef_left_slope,
+ const gfloat half_coef_rite_slope,
+ const gfloat half_coef_top_slope,
+ const gfloat half_coef_bot_slope,
+ const gfloat uno_one,
+ const gfloat uno_two,
+ const gfloat uno_thr,
+ const gfloat uno_fou,
+ const gfloat dos_one,
+ const gfloat dos_two,
+ const gfloat dos_thr,
+ const gfloat dos_fou,
+ const gfloat tre_one,
+ const gfloat tre_two,
+ const gfloat tre_thr,
+ const gfloat tre_fou,
+ const gfloat qua_one,
+ const gfloat qua_two,
+ const gfloat qua_thr,
+ const gfloat qua_fou)
+{
+ /*
+ * STENCIL (FOOTPRINT) OF INPUT VALUES:
+ *
+ * The stencil of Symmetrized Monotone Catmull-Rom is the same as
+ * the standard Catmull-Rom's:
+ *
+ * (ix-1,iy-1) (ix,iy-1) (ix+1,iy-1) (ix+2,iy-1)
+ * = uno_one = uno_two = uno_thr = uno_fou
+ *
+ * (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy)
+ * = dos_one = dos_two = dos_thr = dos_fou
+ * X
+ * (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1)
+ * = tre_one = tre_two = tre_thr = tre_fou
+ *
+ * (ix-1,iy+2) (ix,iy+2) (ix+1,iy+2) (ix+2,iy+2)
+ * = qua_one = qua_two = qua_thr = qua_fou
+ *
+ * where ix is the (pseudo-)floor of the requested left-to-right
+ * location ("X"), and iy is the floor of the requested up-to-down
+ * location.
+ */
+ /*
+ * OUTLINE:
+ *
+ * First, four horizontal cubic Hermite interpolations are performed
+ * to get values on the vertical line which passes through X, and
+ * then these four values are used to perform cubic Hermite
+ * interpolation in the vertical direction to get one approximation
+ * of the pixel value at X,
+ *
+ * Then, four vertical cubic Hermite interpolations are performed to
+ * get values on the horizontal line which passes through X, and
+ * then these four values are used to perform cubic Hermite
+ * interpolation in the horizontal direction to get another
+ * approximation of the pixel value at X,
+ *
+ * These two interpolated pixel values are then averaged.
+ */
+
+ /*
+ * Computation of the slopes and slope limiters:
+ *
+ * Uno horizontal differences:
+ */
+ const gfloat uno = mp_quadratic (uno_one,
+ uno_two,
+ uno_thr,
+ uno_fou,
+ coef_rite_point,
+ half_coef_left_slope,
+ half_coef_rite_slope);
+ /*
+ * Do the same with the other three horizontal lines.
+ *
+ * Dos horizontal line:
+ */
+ const gfloat dos = mp_quadratic (dos_one,
+ dos_two,
+ dos_thr,
+ dos_fou,
+ coef_rite_point,
+ half_coef_left_slope,
+ half_coef_rite_slope);
+ /*
+ * Tre(s) horizontal line:
+ */
+ const gfloat tre = mp_quadratic (tre_one,
+ tre_two,
+ tre_thr,
+ tre_fou,
+ coef_rite_point,
+ half_coef_left_slope,
+ half_coef_rite_slope);
+ /*
+ * Qua(ttro) horizontal line:
+ */
+ const gfloat qua = mp_quadratic (qua_one,
+ qua_two,
+ qua_thr,
+ qua_fou,
+ coef_rite_point,
+ half_coef_left_slope,
+ half_coef_rite_slope);
+
+ /*
+ * Perform the interpolation along the one vertical line (filled
+ * with results obtained by interpolating along horizontal lines):
+ */
+ const gfloat partial_y = mp_quadratic(uno,
+ dos,
+ tre,
+ qua,
+ coef_bot_point,
+ half_coef_top_slope,
+ half_coef_bot_slope);
+
+ /*
+ * Redo with four vertical lines (and the corresponding horizontal
+ * one).
+ *
+ * One:
+ */
+ const gfloat one = mp_quadratic (uno_one,
+ dos_one,
+ tre_one,
+ qua_one,
+ coef_bot_point,
+ half_coef_top_slope,
+ half_coef_bot_slope);
+ /*
+ * Two:
+ */
+ const gfloat two = mp_quadratic (uno_two,
+ dos_two,
+ tre_two,
+ qua_two,
+ coef_bot_point,
+ half_coef_top_slope,
+ half_coef_bot_slope);
+ /*
+ * Thr(ee):
+ */
+ const gfloat thr = mp_quadratic (uno_thr,
+ dos_thr,
+ tre_thr,
+ qua_thr,
+ coef_bot_point,
+ half_coef_top_slope,
+ half_coef_bot_slope);
+ /*
+ * Fou(r):
+ */
+ const gfloat fou = mp_quadratic (uno_fou,
+ dos_fou,
+ tre_fou,
+ qua_fou,
+ coef_bot_point,
+ half_coef_top_slope,
+ half_coef_bot_slope);
+
+ /*
+ * Final horizontal line of vertical results:
+ */
+ const gfloat prem_x = two - one;
+ const gfloat deux_x = thr - two;
+ const gfloat troi_x = fou - thr;
+
+ const gfloat partial_newval = partial_y + two + coef_rite_point * deux_x;
+
+ const gfloat deux_prem_x = deux_x * prem_x;
+ const gfloat deux_deux_x = deux_x * deux_x;
+ const gfloat deux_troi_x = deux_x * troi_x;
+
+ const gfloat twice_l_two =
+ 6.f * FAST_MINMOD (deux_x, prem_x, deux_deux_x, deux_prem_x);
+ const gfloat twice_s_two = deux_x + prem_x;
+ const gfloat twice_l_thr =
+ 6.f * FAST_MINMOD (deux_x, troi_x, deux_deux_x, deux_troi_x);
+ const gfloat twice_s_thr = deux_x + troi_x;
+
+ const gfloat s_s_two = twice_s_two * twice_s_two;
+ const gfloat s_l_two = twice_s_two * twice_l_two;
+ const gfloat s_s_thr = twice_s_thr * twice_s_thr;
+ const gfloat s_l_thr = twice_s_thr * twice_l_thr;
+
+ const gfloat newval =
+ (
+ partial_newval +
+ + half_coef_left_slope * FAST_MINMOD (twice_s_two, twice_l_two,
+ s_s_two, s_l_two)
+ + half_coef_rite_slope * FAST_MINMOD (twice_s_thr, twice_l_thr,
+ s_s_thr, s_l_thr)
+ ) * .5f;
+
+ return newval;
+}
+
+static void
+gegl_sampler_upsize_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output)
+{
+ /*
+ * Needed constants related to the input pixel value pointer
+ * provided by gegl_sampler_get_ptr (self, ix, iy). pixels_per_row
+ * corresponds to fetch_rectangle.width in gegl_sampler_get_ptr.
+ */
+ const gint channels = 4;
+ const gint pixels_per_row = 64;
+
+ /*
+ * Pointer shifts to access the values of the 4x4 footprint:
+ */
+ const gint pixel_skip = channels;
+ const gint row_skip = channels * pixels_per_row;
+
+ const gint uno_one_shift = -row_skip - pixel_skip;
+ const gint uno_two_shift = -row_skip;
+ const gint uno_thr_shift = -row_skip + pixel_skip;
+ const gint uno_fou_shift = -row_skip + pixel_skip * 2;
+
+ const gint dos_one_shift = -pixel_skip;
+ const gint dos_two_shift = 0;
+ const gint dos_thr_shift = pixel_skip;
+ const gint dos_fou_shift = pixel_skip * 2;
+
+ const gint tre_one_shift = row_skip - pixel_skip;
+ const gint tre_two_shift = row_skip;
+ const gint tre_thr_shift = row_skip + pixel_skip;
+ const gint tre_fou_shift = row_skip + pixel_skip * 2;
+
+ const gint qua_one_shift = row_skip * 2 - pixel_skip;
+ const gint qua_two_shift = row_skip * 2;
+ const gint qua_thr_shift = row_skip * 2 + pixel_skip;
+ const gint qua_fou_shift = row_skip * 2 + pixel_skip * 2;
+
+ /*
+ * floor's surrogate FAST_PSEUDO_FLOOR is used to make sure that the
+ * transition through 0 is smooth. If it is known that absolute_x
+ * and absolute_y will never be less than 0, plain cast---that is,
+ * const gint ix = absolute_x---should be used instead. Actually,
+ * any function which agrees with floor for non-integer values, and
+ * picks one of the two possibilities for integer values, can be
+ * used. FAST_PSEUDO_FLOOR fits the bill.
+ */
+ const gint ix = FAST_PSEUDO_FLOOR (absolute_x);
+ const gint iy = FAST_PSEUDO_FLOOR (absolute_y);
+
+ /*
+ * Move the pointer to (the first band of) the top/left pixel of the
+ * 2x2 group of pixel centers which contains the sampling location
+ * in its convex hull (this is dos_two below).
+ */
+ const gfloat* restrict input_bptr =
+ (gfloat*) gegl_sampler_get_ptr (self, ix, iy);
+
+ /*
+ * x is the x-coordinate of the sampling point relative to the
+ * position of top left corner of the convex hull of the 2x2 block
+ * of closest pixels. Similarly for y. Range of values: [0,1). Note
+ * that the RHS is computed in double precision, which is good for
+ * round off because absolute_x and ix may both be large, even
+ * thought their difference may be small.
+ */
+ const gfloat x = absolute_x - ix;
+ const gfloat y = absolute_y - iy;
+
+ /*
+ * Hermite interpolation coefficients:
+ */
+ const gfloat x_squared = x * x;
+ const gfloat y_squared = y * y;
+ const gfloat twice_x = x + x;
+ const gfloat twice_y = y + y;
+ const gfloat half_x_squared_minus_x = .5f * ( x_squared - x );
+ const gfloat half_y_squared_minus_y = .5f * ( y_squared - y );
+
+ const gfloat coef_rite_point = x_squared * ( 3.f - twice_x );
+ const gfloat coef_bot_point = y_squared * ( 3.f - twice_y );
+ /*
+ * We implicitly use
+ *
+ * coef_left_point = 1. - coef_rite_point;
+ * coef_top_point = 1. - coef_bot_point;
+ *
+ * This is done using a difference (which is computed anyway) to
+ * account for the fact that plus and minus coef_rite/bot_point are
+ * used.
+ */
+ /*
+ * The "half" are because we later compute double the slope. In
+ * other words, this is an example of constant folding:
+ */
+ const gfloat half_coef_rite_slope = x * half_x_squared_minus_x;
+ const gfloat half_coef_bot_slope = y * half_y_squared_minus_y;
+ const gfloat half_coef_left_slope =
+ half_coef_rite_slope - half_x_squared_minus_x;
+ const gfloat half_coef_top_slope =
+ half_coef_bot_slope - half_y_squared_minus_y;
+
+ /*
+ * Channel by channel computation of the new pixel values:
+ */
+
+ /*
+ * The newval array will contain one computed resampled value per
+ * channel:
+ */
+ gfloat newval[channels];
+
+ /*
+ * First channel:
+ */
+ newval[0] = symmetrized_monotone_catmull_rom (coef_rite_point,
+ coef_bot_point,
+ half_coef_left_slope,
+ half_coef_rite_slope,
+ half_coef_top_slope,
+ half_coef_bot_slope,
+ input_bptr[ uno_one_shift ],
+ input_bptr[ uno_two_shift ],
+ input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ dos_one_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ]);
+ /*
+ * Second channel:
+ */
+ newval[1] = symmetrized_monotone_catmull_rom (coef_rite_point,
+ coef_bot_point,
+ half_coef_left_slope,
+ half_coef_rite_slope,
+ half_coef_top_slope,
+ half_coef_bot_slope,
+ input_bptr[ uno_one_shift+1 ],
+ input_bptr[ uno_two_shift+1 ],
+ input_bptr[ uno_thr_shift+1 ],
+ input_bptr[ uno_fou_shift+1 ],
+ input_bptr[ dos_one_shift+1 ],
+ input_bptr[ dos_two_shift+1 ],
+ input_bptr[ dos_thr_shift+1 ],
+ input_bptr[ dos_fou_shift+1 ],
+ input_bptr[ tre_one_shift+1 ],
+ input_bptr[ tre_two_shift+1 ],
+ input_bptr[ tre_thr_shift+1 ],
+ input_bptr[ tre_fou_shift+1 ],
+ input_bptr[ qua_one_shift+1 ],
+ input_bptr[ qua_two_shift+1 ],
+ input_bptr[ qua_thr_shift+1 ],
+ input_bptr[ qua_fou_shift+1 ]);
+ /*
+ * Third channel:
+ */
+ newval[2] = symmetrized_monotone_catmull_rom (coef_rite_point,
+ coef_bot_point,
+ half_coef_left_slope,
+ half_coef_rite_slope,
+ half_coef_top_slope,
+ half_coef_bot_slope,
+ input_bptr[ uno_one_shift+2 ],
+ input_bptr[ uno_two_shift+2 ],
+ input_bptr[ uno_thr_shift+2 ],
+ input_bptr[ uno_fou_shift+2 ],
+ input_bptr[ dos_one_shift+2 ],
+ input_bptr[ dos_two_shift+2 ],
+ input_bptr[ dos_thr_shift+2 ],
+ input_bptr[ dos_fou_shift+2 ],
+ input_bptr[ tre_one_shift+2 ],
+ input_bptr[ tre_two_shift+2 ],
+ input_bptr[ tre_thr_shift+2 ],
+ input_bptr[ tre_fou_shift+2 ],
+ input_bptr[ qua_one_shift+2 ],
+ input_bptr[ qua_two_shift+2 ],
+ input_bptr[ qua_thr_shift+2 ],
+ input_bptr[ qua_fou_shift+2 ]);
+ /*
+ * Fourth channel:
+ */
+ newval[3] = symmetrized_monotone_catmull_rom (coef_rite_point,
+ coef_bot_point,
+ half_coef_left_slope,
+ half_coef_rite_slope,
+ half_coef_top_slope,
+ half_coef_bot_slope,
+ input_bptr[ uno_one_shift+3 ],
+ input_bptr[ uno_two_shift+3 ],
+ input_bptr[ uno_thr_shift+3 ],
+ input_bptr[ uno_fou_shift+3 ],
+ input_bptr[ dos_one_shift+3 ],
+ input_bptr[ dos_two_shift+3 ],
+ input_bptr[ dos_thr_shift+3 ],
+ input_bptr[ dos_fou_shift+3 ],
+ input_bptr[ tre_one_shift+3 ],
+ input_bptr[ tre_two_shift+3 ],
+ input_bptr[ tre_thr_shift+3 ],
+ input_bptr[ tre_fou_shift+3 ],
+ input_bptr[ qua_one_shift+3 ],
+ input_bptr[ qua_two_shift+3 ],
+ input_bptr[ qua_thr_shift+3 ],
+ input_bptr[ qua_fou_shift+3 ]);
+ /*
+ * Ship out newval:
+ */
+ babl_process (babl_fish (self->interpolate_format, self->format),
+ newval, output, 1);
+}
+
+static void
+set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+}
+
+static void
+get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+}
diff --git a/gegl/buffer/gegl-sampler-upsize.h b/gegl/buffer/gegl-sampler-upsize.h
new file mode 100644
index 0000000..8abe50f
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsize.h
@@ -0,0 +1,50 @@
+/* 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_UPSIZE_H__
+#define __GEGL_SAMPLER_UPSIZE_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_UPSIZE (gegl_sampler_upsize_get_type ())
+#define GEGL_SAMPLER_UPSIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_UPSIZE, GeglSamplerUpsize))
+#define GEGL_SAMPLER_UPSIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_UPSIZE, GeglSamplerUpsizeClass))
+#define GEGL_IS_SAMPLER_UPSIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_UPSIZE))
+#define GEGL_IS_SAMPLER_UPSIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_UPSIZE))
+#define GEGL_SAMPLER_UPSIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_UPSIZE, GeglSamplerUpsizeClass))
+
+typedef struct _GeglSamplerUpsize GeglSamplerUpsize;
+typedef struct _GeglSamplerUpsizeClass GeglSamplerUpsizeClass;
+
+struct _GeglSamplerUpsize
+{
+ GeglSampler parent_instance;
+ /*< private >*/
+};
+
+struct _GeglSamplerUpsizeClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_upsize_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-sampler-upsmooth.c b/gegl/buffer/gegl-sampler-upsmooth.c
new file mode 100644
index 0000000..740c653
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsmooth.c
@@ -0,0 +1,1259 @@
+/* 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, Eric Daoust, �yvind Kolås
+ * and Geert Jordaens.
+ */
+
+/*
+ * ================
+ * UPSMOOTH SAMPLER
+ * ================
+ *
+ * "Upsmooth" is the GEGL name of the Snohalo level 1.5 resampler with
+ * maximum reasonable smoothing.
+ *
+ * =========================================================================
+ * ONLY IMPLEMENTS THE SECOND LOWEST QUALITY SNOHALO RESAMPLER WITH MAX BLUR
+ * =========================================================================
+ *
+ * This code implements Snohalo for (quality) level = 1.5: the
+ * smoothing step is only performed once, and the Nohalo subdivision
+ * is performed twice. (Snohalo level 2 would be smooth -> subdivide
+ * -> smooth -> subdivide, for a total of two smoothing and two
+ * subdivision steps). For Snohalo level 1.5, we only do smooth ->
+ * subdivide -> subdivide. Although the smoothing step is only used
+ * once, the smoothing (= blur = antialiasing) parameter set to its
+ * maximum reasonable value.
+ *
+ * This sampler has quite strong antialiasing. For very smooth or
+ * blurry images, Nohalo level 2 (= Snohalo level 1.5 (with blur = 0)
+ * = Snohalo level 2 (with blur = 0), currently called
+ * gegl-sampler-upsharp.* in GEGL), or MP-quadratic (= monotone
+ * interpolatory cubic splines = Fritsch and Carlson, currently called
+ * gegl-sampler-upsize.* in GEGL) are probably better choices.
+ */
+
+/*
+ * Acknowledgements: Adam Turcotte and Eric Daoust's Snohalo
+ * programming funded by Google Summer of Code 2009. Nicolas
+ * Robidoux's research on Nohalo funded in part by an NSERC (National
+ * Science and Engineering Research Council of Canada) Discovery
+ * Grant.
+ *
+ * Nicolas Robidoux thanks Minglun Gong, Ralf Meyer, John Cupitt and
+ * Sven Neumann for useful comments and code.
+ */
+
+#include "config.h"
+#include <glib-object.h>
+
+#include "gegl.h"
+#include "gegl-types-internal.h"
+#include "gegl-buffer-private.h"
+
+#include "gegl-sampler-upsmooth.h"
+
+/*
+ * FAST_MINMOD is an implementation of the minmod function which only
+ * needs two conditional moves. FAST_MINMOD(a,b,a_times_a,a_times_b)
+ * "returns" minmod(a,b). The parameter ("input") a_times_a is assumed
+ * to contain the square of a; a_times_b, the product of a and b.
+ *
+ * This version is most suitable for images with flat (constant)
+ * colour areas, since a, which is a pixel difference, will often be
+ * 0, in which case both forward branches are likely.
+ *
+ * For uncompressed natural images in high bit depth (images for which
+ * the slopes a and b are unlikely to be equal to zero or be equal to
+ * each other), we recommend using
+ *
+ * ( (a_times_b)>=0. ? 1. : 0. ) * ( (a_times_b)<(a_times_a) ? (b) : (a) )
+ *
+ * instead. With this second version, the forward branch of the second
+ * conditional move is taken when |b|>|a| and when a*b<0. However, the
+ * "else" branch is taken when a=0 (or when a=b), which is why the
+ * above version is not recommended for images with regions with
+ * constant pixel values (or regions with pixel values which vary
+ * bilinearly).
+ */
+#define FAST_MINMOD(a,b,a_times_a,a_times_b) \
+ ( (a_times_b)>=0.f ? 1.f : 0.f ) * ( (a_times_a)<=(a_times_b) ? (a) : (b) )
+
+/*
+ * 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) ( (gint)(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
+};
+
+static void gegl_sampler_upsmooth_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output);
+
+static void set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec);
+
+static void get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec);
+
+G_DEFINE_TYPE (GeglSamplerUpsmooth, gegl_sampler_upsmooth, GEGL_TYPE_SAMPLER)
+
+static void
+gegl_sampler_upsmooth_class_init (GeglSamplerUpsmoothClass *klass)
+{
+ GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ sampler_class->get = gegl_sampler_upsmooth_get;
+}
+
+static void
+gegl_sampler_upsmooth_init (GeglSamplerUpsmooth *self)
+{
+ GEGL_SAMPLER (self)->context_rect.x = -1;
+ GEGL_SAMPLER (self)->context_rect.y = -1;
+ GEGL_SAMPLER (self)->context_rect.width = 4;
+ GEGL_SAMPLER (self)->context_rect.height = 4;
+ GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
+}
+
+static void inline
+snohalo_step1 (const gfloat cer_thr_in,
+ const gfloat cer_fou_in,
+ const gfloat uno_two_in,
+ const gfloat uno_thr_in,
+ const gfloat uno_fou_in,
+ const gfloat uno_fiv_in,
+ const gfloat dos_one_in,
+ const gfloat dos_two_in,
+ const gfloat dos_thr_in,
+ const gfloat dos_fou_in,
+ const gfloat dos_fiv_in,
+ const gfloat dos_six_in,
+ const gfloat tre_zer_in,
+ const gfloat tre_one_in,
+ const gfloat tre_two_in,
+ const gfloat tre_thr_in,
+ const gfloat tre_fou_in,
+ const gfloat tre_fiv_in,
+ const gfloat tre_six_in,
+ const gfloat qua_zer_in,
+ const gfloat qua_one_in,
+ const gfloat qua_two_in,
+ const gfloat qua_thr_in,
+ const gfloat qua_fou_in,
+ const gfloat qua_fiv_in,
+ const gfloat qua_six_in,
+ const gfloat cin_one_in,
+ const gfloat cin_two_in,
+ const gfloat cin_thr_in,
+ const gfloat cin_fou_in,
+ const gfloat cin_fiv_in,
+ const gfloat sei_two_in,
+ const gfloat sei_thr_in,
+ const gfloat sei_fou_in,
+ gfloat* restrict uno_two_1,
+ gfloat* restrict uno_thr_1,
+ gfloat* restrict dos_one_1,
+ gfloat* restrict dos_two_1,
+ gfloat* restrict dos_thr_1,
+ gfloat* restrict dos_fou_1,
+ gfloat* restrict tre_one_1,
+ gfloat* restrict tre_two_1,
+ gfloat* restrict tre_thr_1,
+ gfloat* restrict tre_fou_1,
+ gfloat* restrict qua_two_1,
+ gfloat* restrict qua_thr_1)
+{
+ /*
+ * This function calculates the missing ten double density pixel
+ * values, and also returns the "already known" two, so that the
+ * twelve values which make up the stencil of snohalo level 1 are
+ * available. One level of snohalo subdivision is then applied to
+ * these 24 values, prior to applying bilinear interpolation.
+ */
+ /*
+ * THE STENCIL OF INPUT VALUES:
+ *
+ * Pointer arithmetic is used to implicitly reflect the input
+ * stencil about tre_thr---assumed closer to the sampling location
+ * than other pixels (ties are OK)---in such a way that after
+ * reflection the sampling point is to the bottom right of tre_thr.
+ *
+ * The following code and picture assumes that the stencil reflexion
+ * has already been performed.
+ *
+ *
+ * (ix,iy-3) (ix+1,iy-3)
+ * = cer_thr = cer_fou
+ *
+ *
+ *
+ * (ix-1,iy-2) (ix,iy-2) (ix+1,iy-2) (ix+1,iy-3)
+ * = uno_two = uno_thr = uno_fou = uno_fiv
+ *
+ *
+ *
+ * (ix-2,iy-1) (ix-1,iy-1) (ix,iy-1) (ix+1,iy-1) (ix+2,iy-1) (ix+3,iy-1)
+ * = dos_one = dos_two = dos_thr = dos_fou = dos_fiv = dos_six
+ *
+ *
+ *
+ * (ix-3,iy) (ix-2,iy) (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy) (ix+3,iy)
+ * = tre_zer = tre_one = tre_two = tre_thr = tre_fou = tre_fiv = tre_six
+ * X
+ *
+ *
+ * (ix-3,iy) (ix-2,iy) (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1) (ix+3,iy+1)
+ * = qua_zer = qua_one = qua_two = qua_thr = qua_fou = qua_fiv = qua_six
+ *
+ *
+ *
+ * (ix-2,iy+2) (ix-1,iy+2) (ix,iy+2) (ix+1,iy+2) (ix+2,iy+2)
+ * = cin_one = cin_two = cin_thr = cin_fou = cin_fiv
+ *
+ *
+ *
+ * (ix-1,iy+3) (ix,iy+3) (ix+1,iy+3)
+ * = sei_two = sei_thr = sei_fou
+ *
+ *
+ * The above input pixel values are the ones needed in order to make
+ * available to the second level the following first level values:
+ *
+ * uno_two_1 = uno_thr_1 =
+ * (ix,iy-1/2) (ix+1/2,iy-1/2)
+ *
+ *
+ *
+ *
+ * dos_one_1 = dos_two_1 = dos_thr_1 = dos_fou_1 =
+ * (ix-1/2,iy) (ix,iy) (ix+1/2,iy) (ix+1,iy)
+ *
+ * X
+ *
+ *
+ * tre_one_1 = tre_two_1 = tre_thr_1 = tre_fou_1 =
+ * (ix-1/2,iy+1/2) (ix,iy+1/2) (ix+1/2,iy+1/2) (ix+1,iy+1/2)
+ *
+ *
+ *
+ *
+ * qua_two_1 = qua_thr_1 =
+ * (ix,iy+1) (ix+1/2,iy+1)
+ *
+ *
+ * to which nohalo level 1 is applied by the caller.
+ */
+
+ /*
+ * Computation of the blurred input pixel values:
+ */
+ const gfloat uno_two_plus_cer_thr_in = uno_two_in + cer_thr_in;
+ const gfloat uno_thr_plus_cer_fou_in = uno_thr_in + cer_fou_in;
+
+ const gfloat dos_one_plus_uno_two_in = dos_one_in + uno_two_in;
+ const gfloat dos_two_plus_uno_thr_in = dos_two_in + uno_thr_in;
+ const gfloat dos_thr_plus_uno_fou_in = dos_thr_in + uno_fou_in;
+ const gfloat dos_fou_plus_uno_fiv_in = dos_fou_in + uno_fiv_in;
+
+ const gfloat tre_zer_plus_dos_one_in = tre_zer_in + dos_one_in;
+ const gfloat tre_one_plus_dos_two_in = tre_one_in + dos_two_in;
+ const gfloat tre_two_plus_dos_thr_in = tre_two_in + dos_thr_in;
+ const gfloat tre_thr_plus_dos_fou_in = tre_thr_in + dos_fou_in;
+ const gfloat tre_fou_plus_dos_fiv_in = tre_fou_in + dos_fiv_in;
+ const gfloat tre_fiv_plus_dos_six_in = tre_fiv_in + dos_six_in;
+
+ const gfloat qua_zer_plus_tre_one_in = qua_zer_in + tre_one_in;
+ const gfloat qua_one_plus_tre_two_in = qua_one_in + tre_two_in;
+ const gfloat qua_two_plus_tre_thr_in = qua_two_in + tre_thr_in;
+ const gfloat qua_thr_plus_tre_fou_in = qua_thr_in + tre_fou_in;
+ const gfloat qua_fou_plus_tre_fiv_in = qua_fou_in + tre_fiv_in;
+ const gfloat qua_fiv_plus_tre_six_in = qua_fiv_in + tre_six_in;
+
+ const gfloat cin_one_plus_qua_two_in = cin_one_in + qua_two_in;
+ const gfloat cin_two_plus_qua_thr_in = cin_two_in + qua_thr_in;
+ const gfloat cin_thr_plus_qua_fou_in = cin_thr_in + qua_fou_in;
+ const gfloat cin_fou_plus_qua_fiv_in = cin_fou_in + qua_fiv_in;
+ const gfloat cin_fiv_plus_qua_six_in = cin_fiv_in + qua_six_in;
+
+ const gfloat sei_two_plus_cin_thr_in = sei_two_in + cin_thr_in;
+ const gfloat sei_thr_plus_cin_fou_in = sei_thr_in + cin_fou_in;
+ const gfloat sei_fou_plus_cin_fiv_in = sei_fou_in + cin_fiv_in;
+
+ const gfloat uno_thr =
+ .125f * ( uno_two_plus_cer_thr_in + dos_thr_plus_uno_fou_in )
+ + .5f * uno_thr_in;
+ const gfloat uno_fou =
+ .125f * ( uno_thr_plus_cer_fou_in + dos_fou_plus_uno_fiv_in )
+ + .5f * uno_fou_in;
+
+ const gfloat dos_two =
+ .125f * ( dos_one_plus_uno_two_in + tre_two_plus_dos_thr_in )
+ + .5f * dos_two_in;
+ const gfloat dos_thr =
+ .125f * ( dos_two_plus_uno_thr_in + tre_thr_plus_dos_fou_in )
+ + .5f * dos_thr_in;
+ const gfloat dos_fou =
+ .125f * ( dos_thr_plus_uno_fou_in + tre_fou_plus_dos_fiv_in )
+ + .5f * dos_fou_in;
+ const gfloat dos_fiv =
+ .125f * ( dos_fou_plus_uno_fiv_in + tre_fiv_plus_dos_six_in )
+ + .5f * dos_fiv_in;
+
+ const gfloat tre_one =
+ .125f * ( tre_zer_plus_dos_one_in + qua_one_plus_tre_two_in )
+ + .5f * tre_one_in;
+ const gfloat tre_two =
+ .125f * ( tre_one_plus_dos_two_in + qua_two_plus_tre_thr_in )
+ + .5f * tre_two_in;
+ const gfloat tre_thr =
+ .125f * ( tre_two_plus_dos_thr_in + qua_thr_plus_tre_fou_in )
+ + .5f * tre_thr_in;
+ const gfloat tre_fou =
+ .125f * ( tre_thr_plus_dos_fou_in + qua_fou_plus_tre_fiv_in )
+ + .5f * tre_fou_in;
+ const gfloat tre_fiv =
+ .125f * ( tre_fou_plus_dos_fiv_in + qua_fiv_plus_tre_six_in )
+ + .5f * tre_fiv_in;
+
+ const gfloat qua_one =
+ .125f * ( qua_zer_plus_tre_one_in + cin_one_plus_qua_two_in )
+ + .5f * qua_one_in;
+ const gfloat qua_two =
+ .125f * ( qua_one_plus_tre_two_in + cin_two_plus_qua_thr_in )
+ + .5f * qua_two_in;
+ const gfloat qua_thr =
+ .125f * ( qua_two_plus_tre_thr_in + cin_thr_plus_qua_fou_in )
+ + .5f * qua_thr_in;
+ const gfloat qua_fou =
+ .125f * ( qua_thr_plus_tre_fou_in + cin_fou_plus_qua_fiv_in )
+ + .5f * qua_fou_in;
+ const gfloat qua_fiv =
+ .125f * ( qua_fou_plus_tre_fiv_in + cin_fiv_plus_qua_six_in )
+ + .5f * qua_fiv_in;
+
+ const gfloat cin_two =
+ .125f * ( cin_one_plus_qua_two_in + sei_two_plus_cin_thr_in )
+ + .5f * cin_two_in;
+ const gfloat cin_thr =
+ .125f * ( cin_two_plus_qua_thr_in + sei_thr_plus_cin_fou_in )
+ + .5f * cin_thr_in;
+ const gfloat cin_fou =
+ .125f * ( cin_thr_plus_qua_fou_in + sei_fou_plus_cin_fiv_in )
+ + .5f * cin_fou_in;
+
+ /*
+ * Computation of the nonlinear slopes: If two consecutive pixel
+ * value differences have the same sign, the smallest one (in
+ * absolute value) is taken to be the corresponding slope; if the
+ * two consecutive pixel value differences don't have the same sign,
+ * the corresponding slope is set to 0. In other words, apply minmod
+ * to consecutive differences.
+ */
+ /*
+ * Two vertical simple differences:
+ */
+ const gfloat d_dostre_two = tre_two - dos_two;
+ const gfloat d_trequa_two = qua_two - tre_two;
+ const gfloat d_quacin_two = cin_two - qua_two;
+ /*
+ * Thr(ee) vertical differences:
+ */
+ const gfloat d_unodos_thr = dos_thr - uno_thr;
+ const gfloat d_dostre_thr = tre_thr - dos_thr;
+ const gfloat d_trequa_thr = qua_thr - tre_thr;
+ const gfloat d_quacin_thr = cin_thr - qua_thr;
+ /*
+ * Fou(r) vertical differences:
+ */
+ const gfloat d_unodos_fou = dos_fou - uno_fou;
+ const gfloat d_dostre_fou = tre_fou - dos_fou;
+ const gfloat d_trequa_fou = qua_fou - tre_fou;
+ const gfloat d_quacin_fou = cin_fou - qua_fou;
+ /*
+ * Dos horizontal differences:
+ */
+ const gfloat d_dos_twothr = dos_thr - dos_two;
+ const gfloat d_dos_thrfou = dos_fou - dos_thr;
+ const gfloat d_dos_foufiv = dos_fiv - dos_fou;
+ /*
+ * Tre(s) horizontal differences:
+ */
+ const gfloat d_tre_onetwo = tre_two - tre_one;
+ const gfloat d_tre_twothr = tre_thr - tre_two;
+ const gfloat d_tre_thrfou = tre_fou - tre_thr;
+ const gfloat d_tre_foufiv = tre_fiv - tre_fou;
+ /*
+ * Qua(ttro) horizontal differences:
+ */
+ const gfloat d_qua_onetwo = qua_two - qua_one;
+ const gfloat d_qua_twothr = qua_thr - qua_two;
+ const gfloat d_qua_thrfou = qua_fou - qua_thr;
+ const gfloat d_qua_foufiv = qua_fiv - qua_fou;
+
+ /*
+ * Recyclable vertical products and squares:
+ */
+ const gfloat d_dostre_times_trequa_two = d_dostre_two * d_trequa_two;
+ const gfloat d_trequa_two_sq = d_trequa_two * d_trequa_two;
+ const gfloat d_trequa_times_quacin_two = d_quacin_two * d_trequa_two;
+
+ const gfloat d_unodos_times_dostre_thr = d_unodos_thr * d_dostre_thr;
+ const gfloat d_dostre_thr_sq = d_dostre_thr * d_dostre_thr;
+ const gfloat d_dostre_times_trequa_thr = d_trequa_thr * d_dostre_thr;
+ const gfloat d_trequa_times_quacin_thr = d_trequa_thr * d_quacin_thr;
+ const gfloat d_quacin_thr_sq = d_quacin_thr * d_quacin_thr;
+
+ const gfloat d_unodos_times_dostre_fou = d_unodos_fou * d_dostre_fou;
+ const gfloat d_dostre_fou_sq = d_dostre_fou * d_dostre_fou;
+ const gfloat d_dostre_times_trequa_fou = d_trequa_fou * d_dostre_fou;
+ const gfloat d_trequa_times_quacin_fou = d_trequa_fou * d_quacin_fou;
+ const gfloat d_quacin_fou_sq = d_quacin_fou * d_quacin_fou;
+ /*
+ * Recyclable horizontal products and squares:
+ */
+ const gfloat d_dos_twothr_times_thrfou = d_dos_twothr * d_dos_thrfou;
+ const gfloat d_dos_thrfou_sq = d_dos_thrfou * d_dos_thrfou;
+ const gfloat d_dos_thrfou_times_foufiv = d_dos_foufiv * d_dos_thrfou;
+
+ const gfloat d_tre_onetwo_times_twothr = d_tre_onetwo * d_tre_twothr;
+ const gfloat d_tre_twothr_sq = d_tre_twothr * d_tre_twothr;
+ const gfloat d_tre_twothr_times_thrfou = d_tre_thrfou * d_tre_twothr;
+ const gfloat d_tre_thrfou_times_foufiv = d_tre_thrfou * d_tre_foufiv;
+ const gfloat d_tre_foufiv_sq = d_tre_foufiv * d_tre_foufiv;
+
+ const gfloat d_qua_onetwo_times_twothr = d_qua_onetwo * d_qua_twothr;
+ const gfloat d_qua_twothr_sq = d_qua_twothr * d_qua_twothr;
+ const gfloat d_qua_twothr_times_thrfou = d_qua_thrfou * d_qua_twothr;
+ const gfloat d_qua_thrfou_times_foufiv = d_qua_thrfou * d_qua_foufiv;
+ const gfloat d_qua_foufiv_sq = d_qua_foufiv * d_qua_foufiv;
+
+ /*
+ * Minmod slopes and first level pixel values:
+ */
+ const gfloat dos_thr_y =
+ FAST_MINMOD( d_dostre_thr, d_unodos_thr,
+ d_dostre_thr_sq,
+ d_unodos_times_dostre_thr );
+ const gfloat tre_thr_y =
+ FAST_MINMOD( d_dostre_thr, d_trequa_thr,
+ d_dostre_thr_sq,
+ d_dostre_times_trequa_thr );
+
+ const gfloat val_uno_two_1 =
+ .5 * ( dos_thr + tre_thr )
+ +
+ .25 * ( dos_thr_y - tre_thr_y );
+
+ const gfloat qua_thr_y =
+ FAST_MINMOD( d_quacin_thr, d_trequa_thr,
+ d_quacin_thr_sq,
+ d_trequa_times_quacin_thr );
+
+ const gfloat val_tre_two_1 =
+ .5 * ( tre_thr + qua_thr )
+ +
+ .25 * ( tre_thr_y - qua_thr_y );
+
+ const gfloat tre_fou_y =
+ FAST_MINMOD( d_dostre_fou, d_trequa_fou,
+ d_dostre_fou_sq,
+ d_dostre_times_trequa_fou );
+ const gfloat qua_fou_y =
+ FAST_MINMOD( d_quacin_fou, d_trequa_fou,
+ d_quacin_fou_sq,
+ d_trequa_times_quacin_fou );
+
+ const gfloat val_tre_fou_1 =
+ .5 * ( tre_fou + qua_fou )
+ +
+ .25 * ( tre_fou_y - qua_fou_y );
+
+ const gfloat tre_two_x =
+ FAST_MINMOD( d_tre_twothr, d_tre_onetwo,
+ d_tre_twothr_sq,
+ d_tre_onetwo_times_twothr );
+ const gfloat tre_thr_x =
+ FAST_MINMOD( d_tre_twothr, d_tre_thrfou,
+ d_tre_twothr_sq,
+ d_tre_twothr_times_thrfou );
+
+ const gfloat val_dos_one_1 =
+ .5 * ( tre_two + tre_thr )
+ +
+ .25 * ( tre_two_x - tre_thr_x );
+
+ const gfloat tre_fou_x =
+ FAST_MINMOD( d_tre_foufiv, d_tre_thrfou,
+ d_tre_foufiv_sq,
+ d_tre_thrfou_times_foufiv );
+
+ const gfloat tre_thr_x_minus_tre_fou_x =
+ tre_thr_x - tre_fou_x;
+
+ const gfloat val_dos_thr_1 =
+ .5 * ( tre_thr + tre_fou )
+ +
+ .25 * tre_thr_x_minus_tre_fou_x;
+
+ const gfloat qua_thr_x =
+ FAST_MINMOD( d_qua_twothr, d_qua_thrfou,
+ d_qua_twothr_sq,
+ d_qua_twothr_times_thrfou );
+ const gfloat qua_fou_x =
+ FAST_MINMOD( d_qua_foufiv, d_qua_thrfou,
+ d_qua_foufiv_sq,
+ d_qua_thrfou_times_foufiv );
+
+ const gfloat qua_thr_x_minus_qua_fou_x =
+ qua_thr_x - qua_fou_x;
+
+ const gfloat val_qua_thr_1 =
+ .5 * ( qua_thr + qua_fou )
+ +
+ .25 * qua_thr_x_minus_qua_fou_x;
+ const gfloat val_tre_thr_1 =
+ .125 * ( tre_thr_x_minus_tre_fou_x + qua_thr_x_minus_qua_fou_x )
+ +
+ .5 * ( val_tre_two_1 + val_tre_fou_1 );
+
+ const gfloat dos_fou_y =
+ FAST_MINMOD( d_dostre_fou, d_unodos_fou,
+ d_dostre_fou_sq,
+ d_unodos_times_dostre_fou );
+ const gfloat dos_thr_x =
+ FAST_MINMOD( d_dos_thrfou, d_dos_twothr,
+ d_dos_thrfou_sq,
+ d_dos_twothr_times_thrfou );
+ const gfloat dos_fou_x =
+ FAST_MINMOD( d_dos_thrfou, d_dos_foufiv,
+ d_dos_thrfou_sq,
+ d_dos_thrfou_times_foufiv );
+
+ const gfloat val_uno_thr_1 =
+ .25 * ( dos_fou - tre_thr )
+ +
+ .125 * ( dos_fou_y - tre_fou_y + dos_thr_x - dos_fou_x )
+ +
+ .5 * ( val_uno_two_1 + val_dos_thr_1 );
+
+ const gfloat qua_two_x =
+ FAST_MINMOD( d_qua_twothr, d_qua_onetwo,
+ d_qua_twothr_sq,
+ d_qua_onetwo_times_twothr );
+ const gfloat tre_two_y =
+ FAST_MINMOD( d_trequa_two, d_dostre_two,
+ d_trequa_two_sq,
+ d_dostre_times_trequa_two );
+ const gfloat qua_two_y =
+ FAST_MINMOD( d_trequa_two, d_quacin_two,
+ d_trequa_two_sq,
+ d_trequa_times_quacin_two );
+
+ const gfloat val_tre_one_1 =
+ .25 * ( qua_two - tre_thr )
+ +
+ .125 * ( qua_two_x - qua_thr_x + tre_two_y - qua_two_y )
+ +
+ .5 * ( val_dos_one_1 + val_tre_two_1 );
+
+ /*
+ * Return level 1 stencil values:
+ */
+ *uno_two_1 = val_uno_two_1;
+ *uno_thr_1 = val_uno_thr_1;
+ *dos_one_1 = val_dos_one_1;
+ *dos_two_1 = tre_thr;
+ *dos_thr_1 = val_dos_thr_1;
+ *dos_fou_1 = tre_fou;
+ *tre_one_1 = val_tre_one_1;
+ *tre_two_1 = val_tre_two_1;
+ *tre_thr_1 = val_tre_thr_1;
+ *tre_fou_1 = val_tre_fou_1;
+ *qua_two_1 = qua_thr;
+ *qua_thr_1 = val_qua_thr_1;
+}
+
+static inline gfloat
+snohalo_step2 (const gfloat w_times_z,
+ const gfloat x_times_z_over_4_plus_x_times_y_over_8,
+ const gfloat w_times_y_over_4_plus_x_times_y_over_8,
+ const gfloat x_times_y_over_8,
+ const gfloat uno_two,
+ const gfloat uno_thr,
+ const gfloat dos_one,
+ const gfloat dos_two,
+ const gfloat dos_thr,
+ const gfloat dos_fou,
+ const gfloat tre_one,
+ const gfloat tre_two,
+ const gfloat tre_thr,
+ const gfloat tre_fou,
+ const gfloat qua_two,
+ const gfloat qua_thr)
+{
+ /*
+ * THE STENCIL OF INPUT VALUES:
+ *
+ * The footprint (stencil) of Nohalo level 1 is the same as, say,
+ * Catmull-Rom, with the exception that the four corner values are
+ * not used:
+ *
+ * (ix,iy-1) (ix+1,iy-1)
+ * = uno_two = uno_thr
+ *
+ * (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy)
+ * = dos_one = dos_two = dos_thr = dos_fou
+ *
+ * (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1)
+ * = tre_one = tre_two = tre_thr = tre_fou
+ *
+ * (ix,iy+2) (ix+1,iy+2)
+ * = qua_two = qua_thr
+ *
+ * Here, ix is the (pseudo-)floor of the requested left-to-right
+ * location, iy is the floor of the requested up-to-down location.
+ *
+ * Pointer arithmetic is used to implicitly reflect the input
+ * stencil so that the requested pixel location is closer to
+ * dos_two, The above consequently corresponds to the case in which
+ * absolute_x is closer to ix than ix+1, and absolute_y is closer to
+ * iy than iy+1. For example, if relative_x_is_rite = 1 but
+ * relative_y_is_down = 0 (see below), then dos_two corresponds to
+ * (ix+1,iy), dos_thr corresponds to (ix,iy) etc, and the three
+ * missing double density values are halfway between dos_two and
+ * dos_thr, halfway between dos_two and tre_two, and at the average
+ * of the four central positions.
+ *
+ * The following code assumes that the stencil has been suitably
+ * reflected.
+ */
+
+ /*
+ * Computation of the nonlinear slopes: If two consecutive pixel
+ * value differences have the same sign, the smallest one (in
+ * absolute value) is taken to be the corresponding slope; if the
+ * two consecutive pixel value differences don't have the same sign,
+ * the corresponding slope is set to 0. In other words, apply minmod
+ * to comsecutive differences.
+ *
+ * Dos(s) horizontal differences:
+ */
+ const gfloat prem_dos = dos_two - dos_one;
+ const gfloat deux_dos = dos_thr - dos_two;
+ const gfloat troi_dos = dos_fou - dos_thr;
+ /*
+ * Tre(s) horizontal differences:
+ */
+ const gfloat prem_tre = tre_two - tre_one;
+ const gfloat deux_tre = tre_thr - tre_two;
+ const gfloat troi_tre = tre_fou - tre_thr;
+ /*
+ * Two vertical differences:
+ */
+ const gfloat prem_two = dos_two - uno_two;
+ const gfloat deux_two = tre_two - dos_two;
+ const gfloat troi_two = qua_two - tre_two;
+ /*
+ * Thr(ee) vertical differences:
+ */
+ const gfloat prem_thr = dos_thr - uno_thr;
+ const gfloat deux_thr = tre_thr - dos_thr;
+ const gfloat troi_thr = qua_thr - tre_thr;
+
+ /*
+ * Products useful for minmod:
+ */
+ const gfloat deux_prem_dos = deux_dos * prem_dos;
+ const gfloat deux_deux_dos = deux_dos * deux_dos;
+ const gfloat deux_troi_dos = deux_dos * troi_dos;
+
+ const gfloat deux_prem_two = deux_two * prem_two;
+ const gfloat deux_deux_two = deux_two * deux_two;
+ const gfloat deux_troi_two = deux_two * troi_two;
+
+ const gfloat deux_prem_tre = deux_tre * prem_tre;
+ const gfloat deux_deux_tre = deux_tre * deux_tre;
+ const gfloat deux_troi_tre = deux_tre * troi_tre;
+
+ const gfloat deux_prem_thr = deux_thr * prem_thr;
+ const gfloat deux_deux_thr = deux_thr * deux_thr;
+ const gfloat deux_troi_thr = deux_thr * troi_thr;
+
+ /*
+ * Terms computed here to put space between the computation of key
+ * quantities and the related conditionals:
+ */
+ const gfloat twice_dos_two_plus_dos_thr = ( dos_two + dos_thr ) * 2.f;
+ const gfloat twice_dos_two_plus_tre_two = ( dos_two + tre_two ) * 2.f;
+ const gfloat twice_deux_thr_plus_deux_dos = ( deux_thr + deux_dos ) * 2.f;
+
+ /*
+ * Compute the needed "right" (at the boundary between one input
+ * pixel areas) double resolution pixel value:
+ */
+ const gfloat four_times_dos_twothr =
+ twice_dos_two_plus_dos_thr
+ +
+ FAST_MINMOD( deux_dos, prem_dos, deux_deux_dos, deux_prem_dos )
+ -
+ FAST_MINMOD( deux_dos, troi_dos, deux_deux_dos, deux_troi_dos );
+
+ /*
+ * Compute the needed "down" double resolution pixel value:
+ */
+ const gfloat four_times_dostre_two =
+ twice_dos_two_plus_tre_two
+ +
+ FAST_MINMOD( deux_two, prem_two, deux_deux_two, deux_prem_two )
+ -
+ FAST_MINMOD( deux_two, troi_two, deux_deux_two, deux_troi_two );
+
+ /*
+ * Compute the "diagonal" (at the boundary between four input pixel
+ * areas) double resolution pixel value:
+ */
+ const gfloat partial_eight_times_dostre_twothr =
+ twice_deux_thr_plus_deux_dos
+ +
+ FAST_MINMOD( deux_tre, prem_tre, deux_deux_tre, deux_prem_tre )
+ -
+ FAST_MINMOD( deux_tre, troi_tre, deux_deux_tre, deux_troi_tre )
+ +
+ FAST_MINMOD( deux_thr, prem_thr, deux_deux_thr, deux_prem_thr )
+ -
+ FAST_MINMOD( deux_thr, troi_thr, deux_deux_thr, deux_troi_thr );
+
+ /*
+ * Compute the output pixel value with bilinear applied to the
+ * reconstructed double density data:
+ */
+ const gfloat newval =
+ w_times_z * dos_two
+ +
+ x_times_z_over_4_plus_x_times_y_over_8 * four_times_dos_twothr
+ +
+ w_times_y_over_4_plus_x_times_y_over_8 * four_times_dostre_two
+ +
+ x_times_y_over_8 * partial_eight_times_dostre_twothr;
+
+ return newval;
+}
+
+#define SNOHALO_SELECT_REFLECT(tl,tr,bl,br) ( \
+ ( \
+ (tl) * is_top_left \
+ + \
+ (tr) * is_top_rite \
+ ) \
+ + \
+ ( \
+ (bl) * is_bot_left \
+ + \
+ (br) * is_bot_rite ) \
+ )
+
+static void
+gegl_sampler_upsmooth_get ( GeglSampler* restrict self,
+ const gdouble absolute_x,
+ const gdouble absolute_y,
+ void* restrict output)
+{
+ /*
+ * Needed constants related to the input pixel value pointer
+ * provided by gegl_sampler_get_ptr (self, ix, iy). pixels_per_row
+ * corresponds to fetch_rectangle.width in gegl_sampler_get_ptr.
+ */
+ const gint channels = 4;
+ const gint pixels_per_row = 64;
+ const gint row_skip = channels * pixels_per_row;
+
+ const gint ix_0 = FAST_PSEUDO_FLOOR (absolute_x + .5);
+ const gint iy_0 = FAST_PSEUDO_FLOOR (absolute_y + .5);
+
+ const gfloat* restrict input_bptr =
+ (gfloat*) gegl_sampler_get_ptr (self, ix_0, iy_0);
+
+ const gfloat x_0 = absolute_x - ix_0;
+ const gfloat y_0 = absolute_y - iy_0;
+
+ const gint sign_of_x_0 = 2 * ( x_0 >= 0. ) - 1;
+ const gint sign_of_y_0 = 2 * ( y_0 >= 0. ) - 1;
+
+ const gint shift_forw_1_pix = sign_of_x_0 * channels;
+ const gint shift_forw_1_row = sign_of_y_0 * row_skip;
+
+ const gint shift_back_1_pix = -shift_forw_1_pix;
+ const gint shift_back_1_row = -shift_forw_1_row;
+
+ const gint shift_back_2_pix = 2 * shift_back_1_pix;
+ const gint shift_back_2_row = 2 * shift_back_1_row;
+ const gint shift_forw_2_pix = 2 * shift_forw_1_pix;
+ const gint shift_forw_2_row = 2 * shift_forw_1_row;
+
+ const gint shift_back_3_pix = 3 * shift_back_1_pix;
+ const gint shift_back_3_row = 3 * shift_back_1_row;
+ const gint shift_forw_3_pix = 3 * shift_forw_1_pix;
+ const gint shift_forw_3_row = 3 * shift_forw_1_row;
+
+ const gint cer_thr_shift = shift_back_3_row;
+ const gint cer_fou_shift = shift_forw_1_pix + shift_back_3_row;
+
+ const gint uno_two_shift = shift_back_1_pix + shift_back_2_row;
+ const gint uno_thr_shift = shift_back_2_row;
+ const gint uno_fou_shift = shift_forw_1_pix + shift_back_2_row;
+ const gint uno_fiv_shift = shift_forw_2_pix + shift_back_2_row;
+
+ const gint dos_one_shift = shift_back_2_pix + shift_back_1_row;
+ const gint dos_two_shift = shift_back_1_pix + shift_back_1_row;
+ const gint dos_thr_shift = shift_back_1_row;
+ const gint dos_fou_shift = shift_forw_1_pix + shift_back_1_row;
+ const gint dos_fiv_shift = shift_forw_2_pix + shift_back_1_row;
+ const gint dos_six_shift = shift_forw_3_pix + shift_back_1_row;
+
+ const gint tre_zer_shift = shift_back_3_pix;
+ const gint tre_one_shift = shift_back_2_pix;
+ const gint tre_two_shift = shift_back_1_pix;
+ const gint tre_thr_shift = 0;
+ const gint tre_fou_shift = shift_forw_1_pix;
+ const gint tre_fiv_shift = shift_forw_2_pix;
+ const gint tre_six_shift = shift_forw_3_pix;
+
+ const gint qua_zer_shift = shift_back_3_pix + shift_forw_1_row;
+ const gint qua_one_shift = shift_back_2_pix + shift_forw_1_row;
+ const gint qua_two_shift = shift_back_1_pix + shift_forw_1_row;
+ const gint qua_thr_shift = shift_forw_1_row;
+ const gint qua_fou_shift = shift_forw_1_pix + shift_forw_1_row;
+ const gint qua_fiv_shift = shift_forw_2_pix + shift_forw_1_row;
+ const gint qua_six_shift = shift_forw_3_pix + shift_forw_1_row;
+
+ const gint cin_one_shift = shift_back_2_pix + shift_forw_2_row;
+ const gint cin_two_shift = shift_back_1_pix + shift_forw_2_row;
+ const gint cin_thr_shift = shift_forw_2_row;
+ const gint cin_fou_shift = shift_forw_1_pix + shift_forw_2_row;
+ const gint cin_fiv_shift = shift_forw_2_pix + shift_forw_2_row;
+
+ const gint sei_two_shift = shift_back_1_pix + shift_forw_3_row;
+ const gint sei_thr_shift = shift_forw_3_row;
+ const gint sei_fou_shift = shift_forw_1_pix + shift_forw_3_row;
+
+ /*
+ * Channel by channel computation of the new pixel values:
+ */
+ gfloat uno_two_0, uno_thr_0;
+ gfloat dos_one_0, dos_two_0, dos_thr_0, dos_fou_0;
+ gfloat tre_one_0, tre_two_0, tre_thr_0, tre_fou_0;
+ gfloat qua_two_0, qua_thr_0;
+
+ gfloat uno_two_1, uno_thr_1;
+ gfloat dos_one_1, dos_two_1, dos_thr_1, dos_fou_1;
+ gfloat tre_one_1, tre_two_1, tre_thr_1, tre_fou_1;
+ gfloat qua_two_1, qua_thr_1;
+
+ gfloat uno_two_2, uno_thr_2;
+ gfloat dos_one_2, dos_two_2, dos_thr_2, dos_fou_2;
+ gfloat tre_one_2, tre_two_2, tre_thr_2, tre_fou_2;
+ gfloat qua_two_2, qua_thr_2;
+
+ gfloat uno_two_3, uno_thr_3;
+ gfloat dos_one_3, dos_two_3, dos_thr_3, dos_fou_3;
+ gfloat tre_one_3, tre_two_3, tre_thr_3, tre_fou_3;
+ gfloat qua_two_3, qua_thr_3;
+
+ /*
+ * The newval array will contain one computed resampled value per
+ * channel:
+ */
+ gfloat newval[channels];
+
+ /*
+ * First channel:
+ */
+ snohalo_step1 (input_bptr[ cer_thr_shift ],
+ input_bptr[ cer_fou_shift ],
+ input_bptr[ uno_two_shift ],
+ input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ uno_fiv_shift ],
+ input_bptr[ dos_one_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ dos_six_shift ],
+ input_bptr[ tre_zer_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ tre_six_shift ],
+ input_bptr[ qua_zer_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ qua_six_shift ],
+ input_bptr[ cin_one_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ input_bptr[ cin_fiv_shift ],
+ input_bptr[ sei_two_shift ],
+ input_bptr[ sei_thr_shift ],
+ input_bptr[ sei_fou_shift ],
+ &uno_two_0,
+ &uno_thr_0,
+ &dos_one_0,
+ &dos_two_0,
+ &dos_thr_0,
+ &dos_fou_0,
+ &tre_one_0,
+ &tre_two_0,
+ &tre_thr_0,
+ &tre_fou_0,
+ &qua_two_0,
+ &qua_thr_0);
+
+ {
+ /*
+ * Computation of the needed weights (coefficients).
+ */
+ const gfloat x = ( 2 * sign_of_x_0 ) * x_0 - .5;
+ const gfloat y = ( 2 * sign_of_y_0 ) * y_0 - .5;
+
+ const gint x_is_rite = ( x >= 0. );
+ const gint y_is_down = ( y >= 0. );
+ const gint x_is_left = !x_is_rite;
+ const gint y_is___up = !y_is_down;
+
+ const gint is_bot_rite = x_is_rite & y_is_down;
+ const gint is_bot_left = x_is_left & y_is_down;
+ const gint is_top_rite = x_is_rite & y_is___up;
+ const gint is_top_left = x_is_left & y_is___up;
+
+ const gint sign_of_x = 2 * x_is_rite - 1;
+ const gint sign_of_y = 2 * y_is_down - 1;
+
+ const gfloat w_1 = ( 2 * sign_of_x ) * x;
+ const gfloat z_1 = ( 2 * sign_of_y ) * y;
+ const gfloat x_1 = 1. - w_1;
+ const gfloat w_1_times_z_1 = w_1 * z_1;
+ const gfloat x_1_times_z_1 = x_1 * z_1;
+
+ const gfloat w_1_times_y_1_over_4 = .25 * ( w_1 - w_1_times_z_1 );
+ const gfloat x_1_times_z_1_over_4 = .25 * x_1_times_z_1;
+ const gfloat x_1_times_y_1_over_8 = .125 * ( x_1 - x_1_times_z_1 );
+
+ const gfloat w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8 =
+ w_1_times_y_1_over_4 + x_1_times_y_1_over_8;
+ const gfloat x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8 =
+ x_1_times_z_1_over_4 + x_1_times_y_1_over_8;
+
+ newval[0] =
+ snohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ SNOHALO_SELECT_REFLECT( uno_two_0, uno_thr_0, qua_two_0, qua_thr_0 ),
+ SNOHALO_SELECT_REFLECT( uno_thr_0, uno_two_0, qua_thr_0, qua_two_0 ),
+ SNOHALO_SELECT_REFLECT( dos_one_0, dos_fou_0, tre_one_0, tre_fou_0 ),
+ SNOHALO_SELECT_REFLECT( dos_two_0, dos_thr_0, tre_two_0, tre_thr_0 ),
+ SNOHALO_SELECT_REFLECT( dos_thr_0, dos_two_0, tre_thr_0, tre_two_0 ),
+ SNOHALO_SELECT_REFLECT( dos_fou_0, dos_one_0, tre_fou_0, tre_one_0 ),
+ SNOHALO_SELECT_REFLECT( tre_one_0, tre_fou_0, dos_one_0, dos_fou_0 ),
+ SNOHALO_SELECT_REFLECT( tre_two_0, tre_thr_0, dos_two_0, dos_thr_0 ),
+ SNOHALO_SELECT_REFLECT( tre_thr_0, tre_two_0, dos_thr_0, dos_two_0 ),
+ SNOHALO_SELECT_REFLECT( tre_fou_0, tre_one_0, dos_fou_0, dos_one_0 ),
+ SNOHALO_SELECT_REFLECT( qua_two_0, qua_thr_0, uno_two_0, uno_thr_0 ),
+ SNOHALO_SELECT_REFLECT( qua_thr_0, qua_two_0, uno_thr_0, uno_two_0 ));
+
+ /*
+ * Second channel:
+ *
+ * Shift input pointer by one channel:
+ */
+ input_bptr++;
+
+ snohalo_step1 (input_bptr[ cer_thr_shift ],
+ input_bptr[ cer_fou_shift ],
+ input_bptr[ uno_two_shift ],
+ input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ uno_fiv_shift ],
+ input_bptr[ dos_one_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ dos_six_shift ],
+ input_bptr[ tre_zer_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ tre_six_shift ],
+ input_bptr[ qua_zer_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ qua_six_shift ],
+ input_bptr[ cin_one_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ input_bptr[ cin_fiv_shift ],
+ input_bptr[ sei_two_shift ],
+ input_bptr[ sei_thr_shift ],
+ input_bptr[ sei_fou_shift ],
+ &uno_two_1,
+ &uno_thr_1,
+ &dos_one_1,
+ &dos_two_1,
+ &dos_thr_1,
+ &dos_fou_1,
+ &tre_one_1,
+ &tre_two_1,
+ &tre_thr_1,
+ &tre_fou_1,
+ &qua_two_1,
+ &qua_thr_1);
+
+ newval[1] =
+ snohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ SNOHALO_SELECT_REFLECT( uno_two_1, uno_thr_1, qua_two_1, qua_thr_1 ),
+ SNOHALO_SELECT_REFLECT( uno_thr_1, uno_two_1, qua_thr_1, qua_two_1 ),
+ SNOHALO_SELECT_REFLECT( dos_one_1, dos_fou_1, tre_one_1, tre_fou_1 ),
+ SNOHALO_SELECT_REFLECT( dos_two_1, dos_thr_1, tre_two_1, tre_thr_1 ),
+ SNOHALO_SELECT_REFLECT( dos_thr_1, dos_two_1, tre_thr_1, tre_two_1 ),
+ SNOHALO_SELECT_REFLECT( dos_fou_1, dos_one_1, tre_fou_1, tre_one_1 ),
+ SNOHALO_SELECT_REFLECT( tre_one_1, tre_fou_1, dos_one_1, dos_fou_1 ),
+ SNOHALO_SELECT_REFLECT( tre_two_1, tre_thr_1, dos_two_1, dos_thr_1 ),
+ SNOHALO_SELECT_REFLECT( tre_thr_1, tre_two_1, dos_thr_1, dos_two_1 ),
+ SNOHALO_SELECT_REFLECT( tre_fou_1, tre_one_1, dos_fou_1, dos_one_1 ),
+ SNOHALO_SELECT_REFLECT( qua_two_1, qua_thr_1, uno_two_1, uno_thr_1 ),
+ SNOHALO_SELECT_REFLECT( qua_thr_1, qua_two_1, uno_thr_1, uno_two_1 ));
+
+ input_bptr++;
+
+ snohalo_step1 (input_bptr[ cer_thr_shift ],
+ input_bptr[ cer_fou_shift ],
+ input_bptr[ uno_two_shift ],
+ input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ uno_fiv_shift ],
+ input_bptr[ dos_one_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ dos_six_shift ],
+ input_bptr[ tre_zer_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ tre_six_shift ],
+ input_bptr[ qua_zer_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ qua_six_shift ],
+ input_bptr[ cin_one_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ input_bptr[ cin_fiv_shift ],
+ input_bptr[ sei_two_shift ],
+ input_bptr[ sei_thr_shift ],
+ input_bptr[ sei_fou_shift ],
+ &uno_two_2,
+ &uno_thr_2,
+ &dos_one_2,
+ &dos_two_2,
+ &dos_thr_2,
+ &dos_fou_2,
+ &tre_one_2,
+ &tre_two_2,
+ &tre_thr_2,
+ &tre_fou_2,
+ &qua_two_2,
+ &qua_thr_2);
+
+ newval[2] =
+ snohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ SNOHALO_SELECT_REFLECT( uno_two_2, uno_thr_2, qua_two_2, qua_thr_2 ),
+ SNOHALO_SELECT_REFLECT( uno_thr_2, uno_two_2, qua_thr_2, qua_two_2 ),
+ SNOHALO_SELECT_REFLECT( dos_one_2, dos_fou_2, tre_one_2, tre_fou_2 ),
+ SNOHALO_SELECT_REFLECT( dos_two_2, dos_thr_2, tre_two_2, tre_thr_2 ),
+ SNOHALO_SELECT_REFLECT( dos_thr_2, dos_two_2, tre_thr_2, tre_two_2 ),
+ SNOHALO_SELECT_REFLECT( dos_fou_2, dos_one_2, tre_fou_2, tre_one_2 ),
+ SNOHALO_SELECT_REFLECT( tre_one_2, tre_fou_2, dos_one_2, dos_fou_2 ),
+ SNOHALO_SELECT_REFLECT( tre_two_2, tre_thr_2, dos_two_2, dos_thr_2 ),
+ SNOHALO_SELECT_REFLECT( tre_thr_2, tre_two_2, dos_thr_2, dos_two_2 ),
+ SNOHALO_SELECT_REFLECT( tre_fou_2, tre_one_2, dos_fou_2, dos_one_2 ),
+ SNOHALO_SELECT_REFLECT( qua_two_2, qua_thr_2, uno_two_2, uno_thr_2 ),
+ SNOHALO_SELECT_REFLECT( qua_thr_2, qua_two_2, uno_thr_2, uno_two_2 ));
+
+ input_bptr++;
+
+ snohalo_step1 (input_bptr[ cer_thr_shift ],
+ input_bptr[ cer_fou_shift ],
+ input_bptr[ uno_two_shift ],
+ input_bptr[ uno_thr_shift ],
+ input_bptr[ uno_fou_shift ],
+ input_bptr[ uno_fiv_shift ],
+ input_bptr[ dos_one_shift ],
+ input_bptr[ dos_two_shift ],
+ input_bptr[ dos_thr_shift ],
+ input_bptr[ dos_fou_shift ],
+ input_bptr[ dos_fiv_shift ],
+ input_bptr[ dos_six_shift ],
+ input_bptr[ tre_zer_shift ],
+ input_bptr[ tre_one_shift ],
+ input_bptr[ tre_two_shift ],
+ input_bptr[ tre_thr_shift ],
+ input_bptr[ tre_fou_shift ],
+ input_bptr[ tre_fiv_shift ],
+ input_bptr[ tre_six_shift ],
+ input_bptr[ qua_zer_shift ],
+ input_bptr[ qua_one_shift ],
+ input_bptr[ qua_two_shift ],
+ input_bptr[ qua_thr_shift ],
+ input_bptr[ qua_fou_shift ],
+ input_bptr[ qua_fiv_shift ],
+ input_bptr[ qua_six_shift ],
+ input_bptr[ cin_one_shift ],
+ input_bptr[ cin_two_shift ],
+ input_bptr[ cin_thr_shift ],
+ input_bptr[ cin_fou_shift ],
+ input_bptr[ cin_fiv_shift ],
+ input_bptr[ sei_two_shift ],
+ input_bptr[ sei_thr_shift ],
+ input_bptr[ sei_fou_shift ],
+ &uno_two_3,
+ &uno_thr_3,
+ &dos_one_3,
+ &dos_two_3,
+ &dos_thr_3,
+ &dos_fou_3,
+ &tre_one_3,
+ &tre_two_3,
+ &tre_thr_3,
+ &tre_fou_3,
+ &qua_two_3,
+ &qua_thr_3);
+
+ newval[3] =
+ snohalo_step2 (w_1_times_z_1,
+ x_1_times_z_1_over_4_plus_x_1_times_y_1_over_8,
+ w_1_times_y_1_over_4_plus_x_1_times_y_1_over_8,
+ x_1_times_y_1_over_8,
+ SNOHALO_SELECT_REFLECT( uno_two_3, uno_thr_3, qua_two_3, qua_thr_3 ),
+ SNOHALO_SELECT_REFLECT( uno_thr_3, uno_two_3, qua_thr_3, qua_two_3 ),
+ SNOHALO_SELECT_REFLECT( dos_one_3, dos_fou_3, tre_one_3, tre_fou_3 ),
+ SNOHALO_SELECT_REFLECT( dos_two_3, dos_thr_3, tre_two_3, tre_thr_3 ),
+ SNOHALO_SELECT_REFLECT( dos_thr_3, dos_two_3, tre_thr_3, tre_two_3 ),
+ SNOHALO_SELECT_REFLECT( dos_fou_3, dos_one_3, tre_fou_3, tre_one_3 ),
+ SNOHALO_SELECT_REFLECT( tre_one_3, tre_fou_3, dos_one_3, dos_fou_3 ),
+ SNOHALO_SELECT_REFLECT( tre_two_3, tre_thr_3, dos_two_3, dos_thr_3 ),
+ SNOHALO_SELECT_REFLECT( tre_thr_3, tre_two_3, dos_thr_3, dos_two_3 ),
+ SNOHALO_SELECT_REFLECT( tre_fou_3, tre_one_3, dos_fou_3, dos_one_3 ),
+ SNOHALO_SELECT_REFLECT( qua_two_3, qua_thr_3, uno_two_3, uno_thr_3 ),
+ SNOHALO_SELECT_REFLECT( qua_thr_3, qua_two_3, uno_thr_3, uno_two_3 ));
+
+ /*
+ * Ship out the array of new pixel values:
+ */
+ babl_process (babl_fish (self->interpolate_format, self->format),
+ newval, output, 1);
+ }
+}
+
+static void
+set_property ( GObject* gobject,
+ guint property_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+}
+
+static void
+get_property (GObject* gobject,
+ guint property_id,
+ GValue* value,
+ GParamSpec* pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+}
diff --git a/gegl/buffer/gegl-sampler-upsmooth.h b/gegl/buffer/gegl-sampler-upsmooth.h
new file mode 100644
index 0000000..6b16246
--- /dev/null
+++ b/gegl/buffer/gegl-sampler-upsmooth.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_UPSMOOTH_H__
+#define __GEGL_SAMPLER_UPSMOOTH_H__
+
+#include "gegl-sampler.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_SAMPLER_UPSMOOTH (gegl_sampler_upsmooth_get_type ())
+#define GEGL_SAMPLER_UPSMOOTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_SAMPLER_UPSMOOTH, GeglSamplerUpsmooth))
+#define GEGL_SAMPLER_UPSMOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_SAMPLER_UPSMOOTH, GeglSamplerUpsmoothClass))
+#define GEGL_IS_SAMPLER_UPSMOOTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_SAMPLER_UPSMOOTH))
+#define GEGL_IS_SAMPLER_UPSMOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_SAMPLER_UPSMOOTH))
+#define GEGL_SAMPLER_UPSMOOTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_SAMPLER_UPSMOOTH, GeglSamplerUpsmoothClass))
+
+typedef struct _GeglSamplerUpsmooth GeglSamplerUpsmooth;
+typedef struct _GeglSamplerUpsmoothClass GeglSamplerUpsmoothClass;
+
+struct _GeglSamplerUpsmooth
+{
+ GeglSampler parent_instance;
+};
+
+struct _GeglSamplerUpsmoothClass
+{
+ GeglSamplerClass parent_class;
+};
+
+GType gegl_sampler_upsmooth_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 5778384..719d090 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -34,8 +34,9 @@
#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-sampler-upsharp.h"
+#include "gegl-sampler-upsize.h"
+#include "gegl-sampler-upsmooth.h"
enum
{
@@ -464,15 +465,6 @@ gegl_buffer_interpolation_from_string (const gchar *string)
g_str_equal (string, "bicubic"))
return GEGL_INTERPOLATION_CUBIC;
- if (g_str_equal (string, "sharp"))
- return GEGL_INTERPOLATION_SHARP;
-
- if (g_str_equal (string, "yafr"))
- return GEGL_INTERPOLATION_YAFR;
-
- if (g_str_equal (string, "lanczos"))
- return GEGL_INTERPOLATION_LANCZOS;
-
if (g_str_equal (string, "downsharp"))
return GEGL_INTERPOLATION_DOWNSHARP;
@@ -482,6 +474,15 @@ gegl_buffer_interpolation_from_string (const gchar *string)
if (g_str_equal (string, "downsmooth"))
return GEGL_INTERPOLATION_DOWNSMOOTH;
+ if (g_str_equal (string, "upsharp"))
+ return GEGL_INTERPOLATION_UPSHARP;
+
+ if (g_str_equal (string, "upsize"))
+ return GEGL_INTERPOLATION_UPSIZE;
+
+ if (g_str_equal (string, "upsmooth"))
+ return GEGL_INTERPOLATION_UPSMOOTH;
+
return GEGL_INTERPOLATION_NEAREST;
}
@@ -496,10 +497,6 @@ gegl_sampler_type_from_interpolation (GeglInterpolation interpolation)
return GEGL_TYPE_SAMPLER_LINEAR;
case GEGL_INTERPOLATION_CUBIC:
return GEGL_TYPE_SAMPLER_CUBIC;
- case GEGL_INTERPOLATION_SHARP:
- return GEGL_TYPE_SAMPLER_SHARP;
- case GEGL_INTERPOLATION_YAFR:
- return GEGL_TYPE_SAMPLER_YAFR;
case GEGL_INTERPOLATION_LANCZOS:
return GEGL_TYPE_SAMPLER_LANCZOS;
case GEGL_INTERPOLATION_DOWNSHARP:
@@ -508,6 +505,12 @@ gegl_sampler_type_from_interpolation (GeglInterpolation interpolation)
return GEGL_TYPE_SAMPLER_DOWNSIZE;
case GEGL_INTERPOLATION_DOWNSMOOTH:
return GEGL_TYPE_SAMPLER_DOWNSMOOTH;
+ case GEGL_INTERPOLATION_UPSHARP:
+ return GEGL_TYPE_SAMPLER_UPSHARP;
+ case GEGL_INTERPOLATION_UPSIZE:
+ return GEGL_TYPE_SAMPLER_UPSIZE;
+ case GEGL_INTERPOLATION_UPSMOOTH:
+ return GEGL_TYPE_SAMPLER_UPSMOOTH;
default:
return GEGL_TYPE_SAMPLER_LINEAR;
}
diff --git a/operations/affine/affine.c b/operations/affine/affine.c
index 641cf12..47df3ba 100644
--- a/operations/affine/affine.c
+++ b/operations/affine/affine.c
@@ -252,7 +252,7 @@ op_affine_class_init (OpAffineClass *klass)
g_param_spec_string (
"filter",
_("Filter"),
- _("Filter type (nearest, linear, lanczos, cubic, downsharp, downsize, downsmooth)"),
+ _("Filter type (nearest, linear, lanczos, cubic, downsharp, downsize, downsmooth, upsharp, upsize, upsmooth)"),
"linear",
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_HARD_EDGES,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]