Index: /trunk/psLib/src/mathtypes/psImage.c
===================================================================
--- /trunk/psLib/src/mathtypes/psImage.c	(revision 9874)
+++ /trunk/psLib/src/mathtypes/psImage.c	(revision 9875)
@@ -9,6 +9,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.117 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-11-06 02:00:40 $
+ *  @version $Revision: 1.118 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-11-07 07:45:33 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -811,4 +811,5 @@
     if (valid00 && valid10 && valid01 && valid11) { \
         /* formula from the ADD */ \
+        /* XXX this had V01 and V10 exchanged! */ \
         return V00*FRACFUNC((1.0-fracX)*(1.0-fracY)) + V10*FRACFUNC(fracX*(1.0-fracY)) + \
                V01*FRACFUNC(fracY*(1.0-fracX)) + V11*FRACFUNC(fracX*fracY); \
@@ -852,4 +853,6 @@
 }
 
+// XXX this would be much faster to use a 3x3 kernel to determine the shift
+// the same function can be used for all equivalent (kernel-based) shifts...
 #define PSIMAGE_PIXEL_INTERPOLATE_BICUBE(TYPE, RETURNTYPE, SUFFIX, FRACFUNC) \
 inline RETURNTYPE p_psImagePixelInterpolateBICUBE_##SUFFIX( \
Index: /trunk/psLib/test/imageops/.cvsignore
===================================================================
--- /trunk/psLib/test/imageops/.cvsignore	(revision 9874)
+++ /trunk/psLib/test/imageops/.cvsignore	(revision 9875)
@@ -21,2 +21,3 @@
 *.da
 gmon.out
+tap_psImageShift
Index: /trunk/psLib/test/imageops/Makefile.am
===================================================================
--- /trunk/psLib/test/imageops/Makefile.am	(revision 9874)
+++ /trunk/psLib/test/imageops/Makefile.am	(revision 9875)
@@ -1,9 +1,17 @@
-AM_CPPFLAGS = $(SRCINC) -I$(top_srcdir)/test/tap/src $(PSLIB_CFLAGS)
+
+AM_CPPFLAGS = \
+	$(SRCINC) \
+	-I$(top_srcdir)/test/tap/src \
+	-I$(top_srcdir)/test/pstap/src \
+	$(PSLIB_CFLAGS)
+
 AM_LDFLAGS = \
 	$(top_builddir)/src/libpslib.la  \
 	$(top_builddir)/test/tap/src/libtap.la \
+	$(top_builddir)/test/pstap/src/libpstap.la \
 	$(PSLIB_LIBS)
 
-TEST_PROGS =
+TEST_PROGS = \
+	tap_psImageShift
 
 if BUILD_TESTS
Index: /trunk/psLib/test/imageops/tap_psImageShift.c
===================================================================
--- /trunk/psLib/test/imageops/tap_psImageShift.c	(revision 9875)
+++ /trunk/psLib/test/imageops/tap_psImageShift.c	(revision 9875)
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(39);
+
+    diag("psImageShift() tests");
+
+    // tests using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+        diag("shift a delta function by an integer offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // integer shifts should be exact
+        psImage *shift = psImageShift (NULL, image, 1.0, 1.0, 0.0, PS_INTERPOLATE_BILINEAR);
+        ok_float (shift->data.F32[10][10], 0.0, "point 10,10 should be 0.0 : %f", shift->data.F32[10][10]);
+        ok_float (shift->data.F32[10][11], 0.0, "point 10,11 should be 0.0 : %f", shift->data.F32[10][11]);
+        ok_float (shift->data.F32[10][12], 0.0, "point 10,12 should be 0.0 : %f", shift->data.F32[10][12]);
+        ok_float (shift->data.F32[11][10], 0.0, "point 11,10 should be 0.0 : %f", shift->data.F32[11][10]);
+        ok_float (shift->data.F32[11][11], 1.0, "point 11,11 should be 1.0 : %f", shift->data.F32[11][11]);
+        ok_float (shift->data.F32[11][12], 0.0, "point 11,12 should be 0.0 : %f", shift->data.F32[11][12]);
+        ok_float (shift->data.F32[12][10], 0.0, "point 12,10 should be 0.0 : %f", shift->data.F32[12][10]);
+        ok_float (shift->data.F32[12][11], 0.0, "point 12,11 should be 0.0 : %f", shift->data.F32[12][11]);
+        ok_float (shift->data.F32[12][12], 0.0, "point 12,12 should be 0.0 : %f", shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+        diag("shift a delta function by an fractional pixel offset");
+
+        // generate simple image (delta function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // XXX is this needed?
+        psImageInit (image, 0.0);
+
+        // use delta function as input test image
+        image->data.F32[10][10] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 0.2, 0.4, 0.0, PS_INTERPOLATE_BILINEAR);
+        diag("these require tolerance of 4 epsilon (why?)");
+        ok_float_tol (shift->data.F32[10][10], 0.8*0.6, 4*FLT_EPSILON, "point 10,10 should be %f : %f", 0.8*0.6, shift->data.F32[10][10]);
+        ok_float_tol (shift->data.F32[10][11], 0.2*0.6, 4*FLT_EPSILON, "point 10,11 should be %f : %f", 0.2*0.6, shift->data.F32[10][11]);
+        ok_float_tol (shift->data.F32[10][12], 0.0,     4*FLT_EPSILON, "point 10,12 should be %f : %f", 0.0,     shift->data.F32[10][12]);
+        ok_float_tol (shift->data.F32[11][10], 0.8*0.4, 4*FLT_EPSILON, "point 11,10 should be %f : %f", 0.8*0.4, shift->data.F32[11][10]);
+        ok_float_tol (shift->data.F32[11][11], 0.2*0.4, 4*FLT_EPSILON, "point 11,11 should be %f : %f", 0.2*0.4, shift->data.F32[11][11]);
+        ok_float_tol (shift->data.F32[11][12], 0.0,     4*FLT_EPSILON, "point 11,12 should be %f : %f", 0.0,     shift->data.F32[11][12]);
+        ok_float_tol (shift->data.F32[12][10], 0.0,     4*FLT_EPSILON, "point 12,10 should be %f : %f", 0.0,     shift->data.F32[12][10]);
+        ok_float_tol (shift->data.F32[12][11], 0.0,     4*FLT_EPSILON, "point 12,11 should be %f : %f", 0.0,     shift->data.F32[12][11]);
+        ok_float_tol (shift->data.F32[12][12], 0.0,     4*FLT_EPSILON, "point 12,12 should be %f : %f", 0.0,     shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // test by measuring gaussian centroid before and after
+    // using BILINEAR interpolation
+    {
+        psMemId id = psMemGetId();
+
+        diag("shift a gaussian and measure centroid");
+
+        // generate simple image (gauss function)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // build a simple gaussian (16,16),(2,1) as input test image
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                float z = PS_SQR(i - 16.0)/8.0 + PS_SQR(j - 16.0)/2.0;
+                image->data.F32[j][i] = exp(-z);
+            }
+        }
+
+        // measure centroid of unshifted gaussian (should be 16.0,16.0)
+        float xo = 0.0;
+        float yo = 0.0;
+        float no = 0.0;
+        for (int j = 0; j < image->numRows; j++)
+        {
+            for (int i = 0; i < image->numCols; i++) {
+                xo += i*image->data.F32[j][i];
+                yo += j*image->data.F32[j][i];
+                no += image->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+        diag ("these require a tolerance of 100 epsilon");
+        ok_float_tol (xo, 16.0, 100*FLT_EPSILON, "x-centroid should be %f : %f", 16.0, xo);
+        ok_float_tol (xo, 16.0, 100*FLT_EPSILON, "y-centroid should be %f : %f", 16.0, yo);
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 1.2, -1.2, 0.0, PS_INTERPOLATE_BILINEAR);
+
+        // measure centroid of shifted gaussian (should be 17.2,14.8)
+        xo = yo = no = 0.0;
+        for (int j = 0; j < shift->numRows; j++)
+        {
+            for (int i = 0; i < shift->numCols; i++) {
+                xo += i*shift->data.F32[j][i];
+                yo += j*shift->data.F32[j][i];
+                no += shift->data.F32[j][i];
+            }
+        }
+        xo /= no;
+        yo /= no;
+
+        diag ("these require a tolerance of 100 epsilon");
+        ok_float_tol (xo, 17.2, 100*FLT_EPSILON, "x-centroid should be %f : %f", 17.2, xo);
+        ok_float_tol (yo, 14.8, 100*FLT_EPSILON, "y-centroid should be %f : %f", 14.8, yo);
+
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // tests using BICUBE interpolation
+    {
+        psMemId id = psMemGetId();
+
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        // use delta function as input test image
+        image->data.F32[11][11] = 1;
+
+        // fractional shifts introduce smoothing,
+        psImage *shift = psImageShift (NULL, image, 0.2, 0.4, 0.0, PS_INTERPOLATE_BICUBE);
+        ok_float_tol (shift->data.F32[10][10], -0.157778, 4*FLT_EPSILON, "point 10,10 should be %f : %f", -0.157778, shift->data.F32[10][10]);
+        ok_float_tol (shift->data.F32[10][11], +0.168889, 4*FLT_EPSILON, "point 10,11 should be %f : %f", +0.168889, shift->data.F32[10][11]);
+        ok_float_tol (shift->data.F32[10][12], -0.131111, 4*FLT_EPSILON, "point 10,12 should be %f : %f", -0.131111, shift->data.F32[10][12]);
+        ok_float_tol (shift->data.F32[11][10], +0.142221, 4*FLT_EPSILON, "point 11,10 should be %f : %f", +0.142221, shift->data.F32[11][10]);
+        ok_float_tol (shift->data.F32[11][11], +0.488884, 4*FLT_EPSILON, "point 11,11 should be %f : %f", +0.488884, shift->data.F32[11][11]);
+        ok_float_tol (shift->data.F32[11][12], +0.208878, 4*FLT_EPSILON, "point 11,12 should be %f : %f", +0.208878, shift->data.F32[11][12]);
+        ok_float_tol (shift->data.F32[12][10], -0.064500, 4*FLT_EPSILON, "point 12,10 should be %f : %f", -0.064500, shift->data.F32[12][10]);
+        ok_float_tol (shift->data.F32[12][11], +0.302066, 4*FLT_EPSILON, "point 12,11 should be %f : %f", +0.302066, shift->data.F32[12][11]);
+        ok_float_tol (shift->data.F32[12][12], +0.041884, 4*FLT_EPSILON, "point 12,12 should be %f : %f", +0.041884, shift->data.F32[12][12]);
+        psFree(shift);
+        psFree(image);
+
+        skip_end();
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+    return exit_status();
+}
Index: /trunk/psLib/test/mathtypes/.cvsignore
===================================================================
--- /trunk/psLib/test/mathtypes/.cvsignore	(revision 9874)
+++ /trunk/psLib/test/mathtypes/.cvsignore	(revision 9875)
@@ -16,2 +16,3 @@
 *.da
 gmon.out
+tap_psImageInterpolate
Index: /trunk/psLib/test/mathtypes/tap_psImageInterpolate.c
===================================================================
--- /trunk/psLib/test/mathtypes/tap_psImageInterpolate.c	(revision 9874)
+++ /trunk/psLib/test/mathtypes/tap_psImageInterpolate.c	(revision 9875)
@@ -8,5 +8,5 @@
 int main (void)
 {
-    plan_tests(14);
+    plan_tests(47);
 
     diag("psImageInterpolate() tests");
@@ -15,4 +15,41 @@
     {
         psMemId id = psMemGetId();
+
+        diag ("interpolate a delta function");
+
+        // generate simple image (x ramp)
+        psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
+        ok(image != NULL, "psImage successfully allocated");
+        skip_start(image == NULL, 5, "Skipping tests because psImageAlloc() failed");
+
+        image->data.F32[10][10] = 1;
+
+        // center of pixels is 0.5, 0.5
+        float value;
+
+        value = psImagePixelInterpolate (image, 10.5, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        ok_float (value, 1.0, "pixel center value - %f", value);
+
+        diag ("why do I need to have tolerances of 4epsilon or so??");
+        value = psImagePixelInterpolate (image, 10.9, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        ok_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        value = psImagePixelInterpolate (image, 10.5, 10.9, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        ok_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        value = psImagePixelInterpolate (image, 10.1, 10.5, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        ok_float_tol (value, 0.6, 4.0*FLT_EPSILON, "pixel value - %.20f", value);
+
+        skip_end();
+
+        psFree(image);
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    // very simple tests: no mask, bilinear mode, xramp image only
+    {
+        psMemId id = psMemGetId();
+
+        diag ("interpolate an x-ramp");
 
         // generate simple image (x ramp)
@@ -66,5 +103,7 @@
         psMemId id = psMemGetId();
 
-        // generate simple image (x ramp)
+        diag ("interpolate a y-ramp: ");
+
+        // generate simple image (y ramp)
         psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
         ok(image != NULL, "psImage successfully allocated");
@@ -106,4 +145,6 @@
         psMemId id = psMemGetId();
 
+        diag ("interpolate an x-ramp (bicube)");
+
         // generate simple image (x ramp)
         psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
@@ -136,13 +177,15 @@
         ok_float (value, 2.8, "coord: 2.8, 2.8, value: %f", value);
 
-        // no extrapolation: unsure of result
+        diag ("coords outside of nominal range (1 < x < Nx - 2) return 'uncover'");
+
+        // no extrapolation: these return the 'uncover' value
         value = psImagePixelInterpolate (image, 0.8, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
-        ok_float (value, 0.8, "coord: 0.8, 2.8, value: %f", value);
+        ok_float (value, 0.0, "coord: 0.8, 2.8, value: %f", value);
 
         value = psImagePixelInterpolate (image, 0.3, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
-        ok_float (value, 0.5, "coord: 0.3, 2.8, value: %f", value);
+        ok_float (value, 0.0, "coord: 0.3, 2.8, value: %f", value);
 
         value = psImagePixelInterpolate (image, -0.2, 2.8, NULL, 0, 0.0, PS_INTERPOLATE_BICUBE);
-        ok_float (value, 0.5, "coord: -0.2, 2.8, value: %f", value);
+        ok_float (value, 0.0, "coord: -0.2, 2.8, value: %f", value);
 
         skip_end();
@@ -156,5 +199,7 @@
         psMemId id = psMemGetId();
 
-        // generate simple image (x ramp)
+        diag ("interpolate a y-ramp (bicube)");
+
+        // generate simple image (y ramp)
         psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
         ok(image != NULL, "psImage successfully allocated");
@@ -196,4 +241,6 @@
         psMemId id = psMemGetId();
 
+        diag ("interpolate a quadratic shape (bicube)");
+
         // generate simple image (x ramp)
         psImage *image = psImageAlloc(32, 32, PS_TYPE_F32);
Index: /trunk/psLib/test/pstap/src/pstap.h
===================================================================
--- /trunk/psLib/test/pstap/src/pstap.h	(revision 9874)
+++ /trunk/psLib/test/pstap/src/pstap.h	(revision 9875)
@@ -6,19 +6,27 @@
 return exit_status()
 
-       #define mem() ok(psMemCheckLeaks(psMemGetLastId(), NULL, stdout, false) == 0, "Memory Leaks")
+       # define mem() ok(psMemCheckLeaks(psMemGetLastId(), NULL, stdout, false) == 0, "Memory Leaks")
 
-       #define checkLeaks false
+       # define checkLeaks false
 
-       #define checkMem() if(checkLeaks) mem()
+       # define checkMem() if(checkLeaks) mem()
 
-           #ifdef __GNUC__
+           # ifdef __GNUC__
 
-           // use to test the value of a double
-           # define ok_double(VALUE,EXPECT,COMMENT, ...)\
-           ok((fabsl((VALUE)-(EXPECT)) < DBL_EPSILON), COMMENT, ## __VA_ARGS__);
+           // use to test the value of a float
+           # define ok_float(VALUE,EXPECT,COMMENT, ...)\
+           ok((fabs((VALUE)-(EXPECT)) < FLT_EPSILON), COMMENT, ## __VA_ARGS__);
 
-// use to test the value of a float
-# define ok_float(VALUE,EXPECT,COMMENT, ...)\
-ok((fabs((VALUE)-(EXPECT)) < FLT_EPSILON), COMMENT, ## __VA_ARGS__);
+// use to test the value of a double
+# define ok_double(VALUE,EXPECT,COMMENT, ...)\
+ok((fabsl((VALUE)-(EXPECT)) < DBL_EPSILON), COMMENT, ## __VA_ARGS__);
+
+// use to test the value of a float within a defined tolerance
+# define ok_float_tol(VALUE,EXPECT,TOL,COMMENT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < (TOL)), COMMENT, ## __VA_ARGS__);
+
+// use to test the value of a double within a defined tolerance
+# define ok_double_tol(VALUE,EXPECT,TOL,COMMENT, ...)\
+ok((fabsl((VALUE)-(EXPECT)) < (TOL)), COMMENT, ## __VA_ARGS__);
 
 # define ok_str(VALUE,EXPECT,COMMENT, ...)\
@@ -30,4 +38,8 @@
 #elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
 
+// use to test the value of a float
+# define ok_float(VALUE,EXPECT, ...)\
+ok((fabs((VALUE)-(EXPECT)) < FLT_EPSILON), __VA_ARGS__);
+
 // use to test the value of a double
 # define ok_double(VALUE,EXPECT, ...)\
@@ -35,6 +47,10 @@
 
 // use to test the value of a float
-# define ok_float(VALUE,EXPECT, ...)\
-ok((fabs((VALUE)-(EXPECT)) < FLT_EPSILON), __VA_ARGS__);
+# define ok_float_tol(VALUE,EXPECT,TOL, ...)\
+ok((fabs((VALUE)-(EXPECT)) < (TOL)), __VA_ARGS__);
+
+// use to test the value of a double
+# define ok_double_tol(VALUE,EXPECT,TOL, ...)\
+ok((fabsl((VALUE)-(EXPECT)) < )(TOL)), __VA_ARGS__);
 
 # define ok_str(VALUE,EXPECT, ...)\
