Index: /trunk/psLib/src/fft/psImageFFT.c
===================================================================
--- /trunk/psLib/src/fft/psImageFFT.c	(revision 3967)
+++ /trunk/psLib/src/fft/psImageFFT.c	(revision 3968)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-04-15 00:12:08 $
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -19,5 +19,4 @@
 #include "psMemory.h"
 #include "psLogMsg.h"
-#include "psImageExtraction.h"
 #include "psImageIO.h"
 
Index: /trunk/psLib/src/image/Makefile.am
===================================================================
--- /trunk/psLib/src/image/Makefile.am	(revision 3967)
+++ /trunk/psLib/src/image/Makefile.am	(revision 3968)
@@ -13,7 +13,9 @@
 libpslibimage_la_SOURCES = \
 	psImage.c \
-	psImageExtraction.c \
+	psImagePixelExtract.c \
+	psImageStructManip.c \
 	psImageIO.c \
-	psImageManip.c \
+	psImageGeomManip.c \
+	psImagePixelManip.c \
 	psImageStats.c \
 	psImageFFT.c \
@@ -29,7 +31,9 @@
 pslibinclude_HEADERS = \
 	psImage.h \
-	psImageExtraction.h \
+	psImagePixelExtract.h \
+	psImageStructManip.h \
 	psImageIO.h \
-	psImageManip.h \
+	psImageGeomManip.h \
+	psImagePixelManip.h \
 	psImageStats.h \
 	psImageFFT.h \
Index: /trunk/psLib/src/image/image.i
===================================================================
--- /trunk/psLib/src/image/image.i	(revision 3967)
+++ /trunk/psLib/src/image/image.i	(revision 3968)
@@ -2,9 +2,11 @@
 %include "psImageConvolve.h"
 %include "psImageErrors.h"
-%include "psImageExtraction.h"
+%include "psImageStructManip.h"
+%include "psImagePixelExtract.h"
 %include "psImageFFT.h"
 %include "psImage.h"
 %include "psImageIO.h"
-%include "psImageManip.h"
+%include "psImageGeomManip.h"
+%include "psImagePixelManip.h"
 %include "psImageStats.h"
 
Index: /trunk/psLib/src/image/psImageConvolve.c
===================================================================
--- /trunk/psLib/src/image/psImageConvolve.c	(revision 3967)
+++ /trunk/psLib/src/image/psImageConvolve.c	(revision 3968)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-04-15 00:12:08 $
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -15,5 +15,5 @@
 #include "psImageConvolve.h"
 #include "psImageFFT.h"
-#include "psImageExtraction.h"
+#include "psImageStructManip.h"
 #include "psBinaryOp.h"
 #include "psMemory.h"
@@ -454,7 +454,9 @@
 
         // subset out the padded area now.
-        psImage* complexOutSansPad = psImageSubset(complexOut,
-                                     FOURIER_PADDING,FOURIER_PADDING,
-                                     FOURIER_PADDING+numCols,FOURIER_PADDING+numRows);
+        psImage* complexOutSansPad = psImageSubset(complexOut,(psRegion) {
+                                         FOURIER_PADDING,FOURIER_PADDING,
+                                         FOURIER_PADDING+numCols,FOURIER_PADDING+numRows
+                                     }
+                                                  );
 
         out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
Index: unk/psLib/src/image/psImageExtraction.c
===================================================================
--- /trunk/psLib/src/image/psImageExtraction.c	(revision 3967)
+++ 	(revision )
@@ -1,812 +1,0 @@
-/** @file  psImageExtraction.c
- *
- *  @brief Contains basic image extraction operations, as specified in the 
- *         PSLIB SDRS sections "Image Pixel Extractions" and "Image Structure
- *         Manipulation".
- *
- *  @ingroup Image
- *
- *  @author Robert DeSonia, MHPCC
- *
- *  @version $Revision: 1.36 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-05-10 01:57:47 $
- *
- *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#include <string.h>
-
-#include "psMemory.h"
-#include "psImageExtraction.h"
-#include "psError.h"
-
-#include "psImageErrors.h"
-
-static psImage* imageSubset(psImage* out,
-                            psImage* image,
-                            psS32 col0,
-                            psS32 row0,
-                            psS32 col1,
-                            psS32 row1)
-{
-    psU32 elementSize;          // size of image element in bytes
-    psU32 inputColOffset;       // offset in bytes to first subset pixel in input row
-
-    if (image == NULL || image->data.V == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        return NULL;
-    }
-
-    if (image->type.dimen != PS_DIMEN_IMAGE) {
-        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
-        return NULL;
-    }
-
-    if (col1 < 1) {
-        col1 = image->numCols + col1;
-    }
-    if (row1 < 1) {
-        row1 = image->numRows + row1;
-    }
-
-    if (    col1 <= col0 ||
-            row1 <= row0 ||
-            col0 >= image->numCols ||
-            row0 >= image->numRows ||
-            col1 > image->numCols ||
-            row1 > image->numRows ) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
-                col0, col1-1, row0, row1-1,
-                image->numCols-1, image->numRows-1);
-        return NULL;
-    }
-    psS32 numRows = row1-row0;
-    psS32 numCols = col1-col0;
-
-    elementSize = PSELEMTYPE_SIZEOF(image->type.type);
-
-    if (image->parent != NULL) { // if this is a child, we need to start working with parent.
-        col0 += image->col0;
-        col1 += image->col0;
-        row0 += image->row0;
-        row1 += image->row0;
-        image = (psImage*)image->parent;
-    }
-
-    // increment the raw data buffer before freeing anything in the 'out'
-    psPtr rawData = psMemIncrRefCounter(image->rawDataBuffer);
-
-    if (out != NULL) {
-        // if a child, need to orphan (disassociate from parent) first
-        if (out->parent != NULL) {
-            psArrayRemove(out->parent->children,out); // remove from parent's knowledge
-            out->parent = NULL; // break link to parent
-        }
-
-        psFree(out->rawDataBuffer); // free the previous data reference
-    } else {
-        out = psAlloc(sizeof(psImage));
-        out->data.V = NULL;
-    }
-
-    out->data.V = psRealloc(out->data.V,sizeof(psPtr)*numRows); // resize row pointer array
-    *(psType*)&out->type = image->type;
-    *(psU32*)&out->numCols = numCols;
-    *(psU32*)&out->numRows = numRows;
-    *(psS32*)&out->row0 = row0;
-    *(psS32*)&out->col0 = col0;
-    out->parent = image;
-    out->children = NULL;
-    out->rawDataBuffer = rawData;
-
-    // set the new psImage's deallocator to the same as the input image
-    psMemSetDeallocator(out,psMemGetDeallocator(image));
-
-    inputColOffset = elementSize * col0;
-    for (psS32 row = 0; row < numRows; row++) {
-        out->data.V[row] = image->data.U8[row0 + row] + inputColOffset;
-    }
-
-    // add output image as a child of the input image.
-    psS32 n = 0;
-    psArray* children = image->children;
-    if (children == NULL) {
-        children = psArrayAlloc(16); // start with a reasonable size for growth
-    } else if (children->nalloc == children->n) { // full?
-        n = children->n;
-        children = psArrayRealloc(children,n*2); // double the array size
-    } else {
-        n = children->n;
-    }
-    children->data[n] = out;
-    children->n = n+1;
-    image->children = children; // push back any change (esp. if children==NULL before)
-
-    return (out);
-}
-
-psImage* psImageSubset(psImage* image,
-                       psS32 col0,
-                       psS32 row0,
-                       psS32 col1,
-                       psS32 row1)
-{
-    return imageSubset(NULL,image,col0,row0,col1,row1);
-}
-
-psImage* psImageSubsection(psImage* image,
-                           const char* section)
-{
-    psS32 col0;
-    psS32 col1;
-    psS32 row0;
-    psS32 row1;
-
-    // section should be of the form '[col0:col1,row0:row1]'
-    if (section == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
-        return NULL;
-    }
-
-    if (sscanf(section,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
-                section);
-        return NULL;
-    }
-
-    if (col0 > col1 || row0 > row1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
-                col0,col1,row0,row1);
-        return NULL;
-    }
-
-    return imageSubset(NULL,image,col0,row0,col1+1,row1+1);
-}
-
-psImage* psImageTrim(psImage* image, psS32 col0, psS32 row0, psS32 col1, psS32 row1)
-{
-    if (image == NULL || image->data.V == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        return NULL;
-    }
-
-    if (image->parent != NULL) {
-        return imageSubset(image,
-                           (psImage*)image->parent,
-                           col0+image->col0,
-                           row0+image->row0,
-                           col1+image->col0,
-                           row1+image->row0);
-    }
-
-    if (col1 < 1) {
-        col1 += image->numCols;
-    }
-
-    if (row1 < 1) {
-        row1 += image->numRows;
-    }
-
-    if (    col0 < 0 ||
-            row0 < 0 ||
-            col1 > image->numCols ||
-            row1 > image->numRows ||
-            col0 >= col1 ||
-            row0 >= row1 ) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
-                col0, col1-1, row0, row1-1,
-                image->numCols-1, image->numRows-1);
-        psFree(image);
-        return NULL;
-    }
-
-    psImageFreeChildren(image);
-
-    psU32 elementSize = PSELEMTYPE_SIZEOF(image->type.type);
-    psU32 numCols = col1-col0;
-    psU32 numRows = row1-row0;
-    psU32 rowSize = elementSize*numCols;
-    psU32 colOffset = elementSize * col0;
-    psU8* imageData = image->rawDataBuffer;
-    for (psS32 row = row0; row < row1; row++) {
-        memmove(imageData,image->data.U8[row] + colOffset,rowSize);
-        imageData += rowSize;
-    }
-
-    *(psU32*)&image->numRows = numRows;
-    *(psU32*)&image->numCols = numCols;
-
-    // XXX: should I really resize the buffers?
-    image->data.V = psRealloc(image->data.V,sizeof(psPtr)*numRows);
-    image->rawDataBuffer = psRealloc(image->rawDataBuffer,rowSize*numRows);
-
-    image->data.V[0] = image->rawDataBuffer;
-    for (psS32 r = 1; r < numRows; r++) {
-        image->data.U8[r] = image->data.U8[r-1] + rowSize;
-    }
-
-    return (image);
-}
-
-psVector* psImageSlice(psVector* out,
-                       psVector* slicePositions,
-                       const psImage* restrict in,
-                       const psImage* restrict mask,
-                       psU32 maskVal,
-                       psS32 col0,
-                       psS32 row0,
-                       psS32 col1,
-                       psS32 row1,
-                       psImageCutDirection direction,
-                       const psStats* stats)
-{
-    double statVal;
-    psStats* myStats;
-    psElemType type;
-    psS32 inRows;
-    psS32 inCols;
-    psS32 delta = 1;
-    psF64* outData;
-
-    if (in == NULL || in->data.V == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    if (col1 < 1) {
-        col1 += in->numCols;
-    }
-
-    if (row1 < 1) {
-        row1 += in->numRows;
-    }
-
-    if (    col0 < 0 ||
-            row0 < 0 ||
-            col1 > in->numCols ||
-            row1 > in->numRows ||
-            col0 >= col1 ||
-            row0 >= row1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
-                col0, col1, row0, row1,
-                in->numCols, in->numRows);
-        psFree(out);
-        return NULL;
-    }
-
-    type = in->type.type;
-    inRows = in->numRows;
-    inCols = in->numCols;
-
-    if (direction == PS_CUT_X_NEG || direction == PS_CUT_Y_NEG) {
-        delta = -1;
-    }
-
-    if (mask != NULL) {
-        if (inRows != mask->numRows || inCols != mask->numCols) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
-                    mask->numCols,mask->numRows,
-                    inCols, inRows);
-            psFree(out);
-            return NULL;
-        }
-        if (mask->type.type != PS_TYPE_MASK) {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,mask->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
-                    typeStr, PS_TYPE_MASK_NAME);
-            psFree(out);
-            return NULL;
-        }
-    }
-
-    if (stats == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_STAT_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    // verify that the stats struct specifies a
-    // single stats operation
-    if (p_psGetStatValue(stats, &statVal) == false) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
-                PS_ERRORTEXT_psImage_BAD_STAT,stats->options);
-        psFree(out);
-        return NULL;
-    }
-    // since stats input is const, I need to
-    // create a 'scratch' stats struct
-    myStats = psAlloc(sizeof(psStats));
-    *myStats = *stats;
-
-    psS32 numCols = col1-col0;
-    psS32 numRows = row1-row0;
-
-    if (direction == PS_CUT_X_POS || direction == PS_CUT_X_NEG) {
-        psVector* imgVec = psVectorAlloc(numRows, type);
-        psVector* maskVec = NULL;
-        psMaskType* maskData = NULL;
-        psU32* outPosition = NULL;
-
-        // recycle output to make a proper sized/type output structure
-        // n.b. type is double as that is the type given for all stats is
-        // psStats.
-        out = psVectorRecycle(out, numCols, PS_TYPE_F64);
-        if (slicePositions != NULL) {
-            slicePositions = psVectorRecycle(slicePositions, numCols, PS_TYPE_U32);
-            outPosition = slicePositions->data.U32;
-        }
-        outData = out->data.F64;
-        if (delta < 0) {
-            outData += numCols - 1;
-            if (outPosition != NULL) {
-                outPosition += numCols - 1;
-            }
-        }
-
-        if (mask != NULL) {
-            maskVec = psVectorAlloc(numRows, mask->type.type);
-        }
-        #define PSIMAGE_CUT_VERTICAL(TYPE) \
-    case PS_TYPE_##TYPE: { \
-            psMaskType* maskVecData = NULL; \
-            for (psS32 c=col0;c<col1;c++) { \
-                ps##TYPE *imgData = in->data.TYPE[row0] + c; \
-                ps##TYPE *imgVecData = imgVec->data.TYPE; \
-                if (maskVec != NULL) { \
-                    maskVecData = maskVec->data.U8; \
-                    maskData = (psMaskType* )(mask->data.U8[row0]) + c; \
-                } \
-                for (psS32 r=row0;r<row1;r++) { \
-                    *(imgVecData++) = *imgData; \
-                    imgData += inCols; \
-                    if (maskVecData != NULL) { \
-                        *(maskVecData++) = *maskData; \
-                        maskData += inCols; \
-                    } \
-                } \
-                myStats = psVectorStats(myStats,imgVec,NULL,maskVec,maskVal); \
-                (void)p_psGetStatValue(myStats,&statVal); \
-                *outData = statVal; \
-                if (outPosition != NULL) { \
-                    *outPosition = c; \
-                    outPosition += delta; \
-                } \
-                outData += delta; \
-            } \
-            break; \
-        }
-
-        switch (type) {
-            PSIMAGE_CUT_VERTICAL(U8);  // Not a requirement
-            PSIMAGE_CUT_VERTICAL(U16);
-            PSIMAGE_CUT_VERTICAL(U32); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(U64); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(S8);
-            PSIMAGE_CUT_VERTICAL(S16); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(S32); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(S64); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(F32);
-            PSIMAGE_CUT_VERTICAL(F64);
-            PSIMAGE_CUT_VERTICAL(C32); // Not a requirement
-            PSIMAGE_CUT_VERTICAL(C64); // Not a requirement
-        default: {
-                char* typeStr;
-                PS_TYPE_NAME(typeStr,type);
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                        typeStr);
-                psFree(out);
-                out = NULL;
-            }
-        }
-        psFree(imgVec);
-        psFree(maskVec);
-    } else if (direction == PS_CUT_Y_POS || direction == PS_CUT_Y_NEG) {
-        // Cut in Y direction
-        psVector* imgVec = NULL;
-        psVector* maskVec = NULL;
-        psS32 elementSize = PSELEMTYPE_SIZEOF(type);
-        psU32* outPosition = NULL;
-
-        // fill in psVector to fake out the statistics functions.
-        imgVec = psAlloc(sizeof(psVector));
-        imgVec->type = in->type;
-        imgVec->n = *(int*)&imgVec->nalloc = numCols;
-        if (mask != NULL) {
-            maskVec = psAlloc(sizeof(psVector));
-            maskVec->type = mask->type;
-            maskVec->n = *(int*)&maskVec->nalloc = numCols;
-        }
-        // recycle output to make a proper sized/type output structure
-        // n.b. type is double as that is the type given for all stats in
-        // psStats.
-        out = psVectorRecycle(out, numRows, PS_TYPE_F64);
-        if (slicePositions != NULL) {
-            slicePositions = psVectorRecycle(slicePositions, numRows, PS_TYPE_U32);
-            outPosition = slicePositions->data.U32;
-        }
-        outData = out->data.F64;
-        if (delta < 0) {
-            outData += numRows-1;
-            if (outPosition != NULL) {
-                outPosition += numRows-1;
-            }
-        }
-
-        for (psS32 r = row0; r < row1; r++) {
-            // point the vector struct to the
-            // data to calculate the stats
-            imgVec->data.U8 = (psPtr )(in->data.U8[r] + col0 * elementSize);
-            if (maskVec != NULL) {
-                maskVec->data.U8 = (psPtr )(mask->data.U8[r] + col0 * sizeof(psMaskType));
-            }
-            myStats = psVectorStats(myStats, imgVec, NULL, maskVec, maskVal);
-            (void)p_psGetStatValue(myStats, &statVal);  // we know it works cause we tested it above
-            *outData = statVal;
-            if (outPosition != NULL) {
-                *outPosition = r;
-                outPosition += delta;
-
-            }
-            outData += delta;
-        }
-        psFree(imgVec);
-        psFree(maskVec);
-    } else { // don't know what the direction flag is
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SLICE_DIRECTION_INVALID,
-                direction);
-        psFree(out);
-        out = NULL;
-    }
-
-    psFree(myStats);
-
-    return out;
-}
-
-psVector* psImageCut(psVector* out,
-                     psVector* cutCols,
-                     psVector* cutRows,
-                     const psImage* in,
-                     const psImage* restrict mask,
-                     psU32 maskVal,
-                     float startCol,
-                     float startRow,
-                     float endCol,
-                     float endRow,
-                     psU32 nSamples,
-                     psImageInterpolateMode mode)
-{
-    if (in == NULL || in->data.V == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-    psS32 numCols = in->numCols;
-    psS32 numRows = in->numRows;
-
-    if (nSamples < 2) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_nSamples_TOOSMALL,
-                nSamples);
-        psFree(out);
-        return NULL;
-    }
-
-    if (startCol < 0 || startCol >= numCols ||
-            startRow < 0 || startRow >= numRows ||
-            endCol < 0 || endCol >= numCols ||
-            endRow < 0 || endRow >= numRows) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_LINE_NOT_IN_IMAGE,
-                startCol,startRow,endCol,endRow,
-                numCols-1,numRows-1);
-        psFree(out);
-        return NULL;
-    }
-
-    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
-                mode);
-        psFree(out);
-        return NULL;
-    }
-
-    if (mask != NULL) {
-        if (numRows != mask->numRows || numCols != mask->numCols) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
-                    mask->numCols,mask->numRows,
-                    numCols-1, numRows);
-            psFree(out);
-            return NULL;
-        }
-        if (mask->type.type != PS_TYPE_MASK) {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,mask->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
-                    typeStr, PS_TYPE_MASK_NAME);
-            psFree(out);
-            return NULL;
-        }
-    }
-
-    //resize the vectors for the coordinate output
-    psF32* cutColsData = NULL;
-    psF32* cutRowsData = NULL;
-    if (cutCols != NULL) {
-        cutCols = psVectorRecycle(cutCols, nSamples, PS_TYPE_F32);
-        cutColsData = cutCols->data.F32;
-    }
-    if (cutRows != NULL) {
-        cutRows = psVectorRecycle(cutRows, nSamples, PS_TYPE_F32);
-        cutRowsData = cutRows->data.F32;
-    }
-
-    out = psVectorRecycle(out, nSamples, in->type.type);
-
-    float dX = (endCol - startCol) / (float)(nSamples-1);
-    float dY = (endRow - startRow) / (float)(nSamples-1);
-
-    #define LINEAR_CUT_CASE(TYPE) \
-case PS_TYPE_##TYPE: { \
-        ps##TYPE* outData = out->data.TYPE; \
-        for (psS32 i = 0; i < nSamples; i++) { \
-            float x = startCol + (float)i*dX; \
-            float y = startRow + (float)i*dY; \
-            /* store off the location of the sample. */ \
-            if (cutColsData != NULL) { \
-                cutColsData[i] = x; \
-            } \
-            if (cutRowsData != NULL) { \
-                cutRowsData[i] = y; \
-            } \
-            outData[i] = psImagePixelInterpolate(in,x,y,mask,maskVal,0,mode); \
-        } \
-    } \
-    break;
-
-
-    switch (in->type.type) {
-        LINEAR_CUT_CASE(U8);
-        LINEAR_CUT_CASE(U16);
-        LINEAR_CUT_CASE(U32);
-        LINEAR_CUT_CASE(U64);
-        LINEAR_CUT_CASE(S8);
-        LINEAR_CUT_CASE(S16);
-        LINEAR_CUT_CASE(S32);
-        LINEAR_CUT_CASE(S64);
-        LINEAR_CUT_CASE(F32);
-        LINEAR_CUT_CASE(F64);
-        LINEAR_CUT_CASE(C32);
-        LINEAR_CUT_CASE(C64);
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,in->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-            psFree(out);
-            out = NULL;
-        }
-    }
-
-    return out;
-}
-
-psVector* psImageRadialCut(psVector* out,
-                           const psImage* in,
-                           const psImage* restrict mask,
-                           psU32 maskVal,
-                           float centerCol,
-                           float centerRow,
-                           const psVector* radii,
-                           const psStats* stats)
-{
-    double statVal;
-
-    /* check the parameters */
-
-    if (in == NULL || in->data.V == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-    psS32 numCols = in->numCols;
-    psS32 numRows = in->numRows;
-
-    if (mask != NULL) {
-        if (numRows != mask->numRows || numCols != mask->numCols) {
-            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
-                    mask->numCols,mask->numRows,
-                    numCols, numRows);
-            psFree(out);
-            return NULL;
-        }
-        if (mask->type.type != PS_TYPE_MASK) {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,mask->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
-                    typeStr, PS_TYPE_MASK_NAME);
-            psFree(out);
-            return NULL;
-        }
-    }
-
-    if (centerCol < 0 || centerCol >= numCols ||
-            centerRow < 0 || centerRow >= numRows) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_CENTER_NOT_IN_IMAGE,
-                centerCol, centerRow,
-                numCols-1, numRows-1);
-        psFree(out);
-        return NULL;
-    }
-
-    if (radii == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_RADII_VECTOR_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    if (radii->n < 2) {
-        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
-                PS_ERRORTEXT_psImage_RADII_VECTOR_TOOSMALL,
-                radii->n);
-        psFree(out);
-        return NULL;
-    }
-
-    if (stats == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_STAT_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    // verify that the stats struct specifies a
-    // single stats operation
-    if (p_psGetStatValue(stats, &statVal) == false) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
-                PS_ERRORTEXT_psImage_BAD_STAT,
-                stats->options);
-        psFree(out);
-        return NULL;
-    }
-
-    /* completed checking the parameters */
-
-    // size the output vector to proper size.
-    psS32 numOut = radii->n - 1;
-    out = psVectorRecycle(out, numOut, PS_TYPE_F64);
-    psF64* outData = out->data.F64;
-
-    psVector* rSqVec = psVectorCopy(NULL, radii, PS_TYPE_F32);
-    psF32* rSq = rSqVec->data.F32;
-
-    psS32 startRow = centerRow - rSq[numOut];
-    psS32 endRow = centerRow + rSq[numOut];
-    psS32 startCol = centerCol - rSq[numOut];
-    psS32 endCol = centerCol + rSq[numOut];
-
-    if (startRow < 0) {
-        startRow = 0;
-    }
-
-    if (startCol < 0) {
-        startCol = 0;
-    }
-
-    if (endRow >= numRows) {
-        endRow = numRows - 1;
-    }
-
-    if (endCol >= numCols) {
-        endCol = numCols - 1;
-    }
-
-    // Square the radii data
-    for (psS32 d = 0; d <= numOut; d++) {
-        rSq[d] *= rSq[d];
-    }
-
-    // create temporary vectors for the data binning step
-    psVector** buffer = psAlloc(sizeof(psVector*)*numOut);
-    psVector** bufferMask = psAlloc(sizeof(psVector*)*numOut);
-    for (psS32 lcv = 0; lcv < numOut; lcv++) {
-        // n.b. alloc enough for the data by making the vectors slightly larger
-        // than the area of the region of interest.
-        buffer[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
-                                    in->type.type);
-        buffer[lcv]->n = 0;
-
-        bufferMask[lcv] = NULL;
-        if (mask != NULL) {
-            bufferMask[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
-                                            PS_TYPE_MASK);
-            bufferMask[lcv]->n = 0;
-        }
-    }
-
-    float dX;
-    float dY;
-    float dist;
-    for (psS32 row=startRow; row <= endRow; row++) {
-        psF32* inRow = in->data.F32[row];
-        psMaskType* maskRow = NULL;
-        if (mask != NULL) {
-            maskRow = mask->data.PS_TYPE_MASK_DATA[row];
-        }
-        for (psS32 col=startCol; col <= endCol; col++) {
-            dX = centerCol - (float)col - 0.5f;
-            dY = centerRow - (float)row - 0.5f;
-            dist = dX*dX+dY*dY;
-            for (psS32 r = 0; r < numOut; r++) {
-                if (rSq[r] < dist && dist < rSq[r+1]) {
-                    psS32 n = buffer[r]->n;
-                    if (n == buffer[r]->nalloc) { // in case buffers already full, expand
-                        buffer[r] = psVectorRealloc(buffer[r], n*2);
-                        if (bufferMask[r] != NULL) {
-                            bufferMask[r] = psVectorRealloc(bufferMask[r], n*2);
-                        }
-                    }
-
-                    buffer[r]->data.F32[n] = inRow[col];
-                    buffer[r]->n = n+1;
-
-                    if (maskRow != NULL) {
-                        bufferMask[r]->data.PS_TYPE_MASK_DATA[n] = maskRow[col];
-                        bufferMask[r]->n = n+1;
-                    }
-
-                    break;
-                }
-            }
-        }
-    }
-
-    psStats* myStats = psAlloc(sizeof(psStats));
-    *myStats = *stats;
-
-    for (psS32 r = 0; r < numOut; r++) {
-        myStats = psVectorStats(myStats,buffer[r], NULL, bufferMask[r],maskVal);
-        (void)p_psGetStatValue(myStats,&statVal);
-        outData[r] = statVal;
-    }
-
-    psFree(myStats);
-
-    for (psS32 lcv = 0; lcv < numOut; lcv++) {
-        psFree(buffer[lcv]);
-        psFree(bufferMask[lcv]);
-    }
-    psFree(buffer);
-    psFree(bufferMask);
-    psFree(rSqVec);
-    return out;
-}
Index: unk/psLib/src/image/psImageExtraction.h
===================================================================
--- /trunk/psLib/src/image/psImageExtraction.h	(revision 3967)
+++ 	(revision )
@@ -1,198 +1,0 @@
-
-/** @file  psImageExtraction.h
-*
-*  @brief Contains basic image extraction operations, as specified in the 
-*         PSLIB SDRS sections "Image Pixel Extractions" and "Image Structure
-*         Manipulation".
-*
-*  @ingroup Image
-*
-*  @author Robert DeSonia, MHPCC
-*
-*  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-02-17 19:26:24 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-#ifndef PSIMAGEEXTRACTION_H
-#define PSIMAGEEXTRACTION_H
-
-#include "psImage.h"
-#include "psVector.h"
-#include "psStats.h"
-
-/// @addtogroup Image
-/// @{
-
-/* Cut direction flag.  Used with psImageCut function.
- */
-typedef enum {
-    PS_CUT_X_POS,                      ///< Cut in the x dimension from left to right
-    PS_CUT_X_NEG,                      ///< Cut in the x dimension from rigth to left
-    PS_CUT_Y_POS,                      ///< Cut in the y dimension from bottom up
-    PS_CUT_Y_NEG,                      ///< Cut in the y dimension from top down.
-} psImageCutDirection;
-
-/** Create a subimage of the specified area.
- *
- *  Extracts a subimage starting at (col0,row0) to (col1-1,row1-1).  Note: 
- *  the column col1 and row row1 are NOT included in the resulting subimage.
- *  In the event that x1 or y1 are non-positive, they shall be interpreted as
- *  being relative to the size of the parent image in that dimension.
- *
- *  If the entire specified subimage is not contained within the parent 
- *  image, an error results and the return value will be NULL.
- *
- *  The resulting psImage does not create a copy of the underlying image 
- *  data.  Future changes in the parent may be reflecting in the child
- *  subimage and vis versa.
- *
- *  @return psImage* : Pointer to psImage.
- *
- */
-psImage* psImageSubset(
-    psImage* image,                    ///< Parent image.
-    psS32 col0,                          ///< starting column of subimage
-    psS32 row0,                          ///< starting row of subimage
-    psS32 col1,                          ///< exclusive end column of subimage.
-    psS32 row1                           ///< exclusive end row of subimage
-);
-
-/** Create a subimage of the specified area.
- *
- * Uses psLib memory allocation functions to create an image based on a larger
- * one.
- *
- * @return psImage* : Pointer to psImage.
- *
- */
-psImage* psImageSubsection(
-    psImage* image,                    ///< Parent image.
-    const char* section                ///< Subsection in the form '[x1:x2,y1:y2]'
-);
-
-/** Trim an image
- *
- *  Trim the specified image in-place, which involves shuffling the pixels 
- *  around in memory.  The pixels in the region [col0:col1,row0:row1] shall consist
- *  the output image.  The column col1 and row row1 are NOT included in the range.
- *  In the event that x1 or y1 are non-positive, they shall be interpreted as
- *  being relative to the size of the parent image in that dimension.
- *
- *  If the entire specified subimage is not contained within the parent 
- *  image, an error results and the return value will be NULL.
- *
- *  N.B. If the input psImage is a child of another psImage, no pixel data 
- *  will be trimmed, rather it equivalent to calling psImageSubset.  If the input 
- *  psImage is, however, a parent psImage, any children will be obliterated,
- *  i.e., freed from memory.
- *
- *  @return psImage*  trimmed image result
- */
-psImage* psImageTrim(
-    psImage* image,                    ///< image to trim
-    psS32 col0,                          ///< column of trim region's left boundary
-    psS32 row0,                          ///< row of trim region's lower boundary
-    psS32 col1,                          ///< column of trim region's right boundary
-    psS32 row1                           ///< row of trim region's upper boundary
-);
-
-/** Extract pixels from rectlinear region to a vector (array of floats).
- *
- *  The output vector contains either col1-col0 or row1-row0 elements, based 
- *  on the value of the direction: e.g., if direction is PS_CUT_X_POS, there 
- *  are col1-col0 elements. The region to be  sliced  is defined by the 
- *  lower-left corner, (col0,row0), and the upper-right corner, (col1,row1). 
- *  Note that the row and column of the  upper right-hand corner  are NOT 
- *  included in the region. In the event that col1 or row1 are negative, they
- *  shall be interpreted as being relative to the size of the parent image in 
- *  that dimension. The input region is collapsed in the direction perpendicular 
- *  to that specified by direction, and each element of the output vectors is 
- *  derived from the statistics of the pixels at that direction coordinate. The
- *  statistic used to derive the output vector value is specified by stats. 
- *  If mask is non-NULL, pixels for which the corresponding mask pixel 
- *  matches maskVal are excluded from operations. If coords is not NULL, the 
- *  calculated coordinates along the slice are returned in this vector. Only 
- *  one of the statistics choices may be specified, otherwise the function 
- *  must return an error.
- *
- *  This function is defined for the following types: psS8, psU16, psF32, psF64.
- *
- * @return psVector    the resulting vector
- */
-psVector* psImageSlice(
-    psVector* out,                     ///< psVector to recycle, or NULL.
-    psVector* slicePositions,
-    ///< If not NULL, it is populated with the coordinate in the slice dimension
-    ///< coorsponding to the output vector's value of the same position in the
-    ///< vector.  This vector maybe resized and retyped as appropriate.
-    const psImage* input,              ///< the input image in which to perform the slice
-    const psImage* mask,               ///< the mask for the input image.
-    psU32 maskVal,                     ///< the mask value to apply to the mask
-    psS32 col0,                        ///< the leftmost column of the slice region
-    psS32 row0,                        ///< the bottommost row of the slice region
-    psS32 col1,                        ///< exclusive end column of the slice region
-    psS32 row1,                        ///< exclusive end row of the slice region
-    psImageCutDirection direction,     ///< the slice dimension and direction
-    const psStats* stats               ///< the statistic to perform in slice operation
-);
-
-/** Extract pixels from an image along a line to a vector (array of floats).
- *
- *  The vector (xs,ys) - (xe,ye) forms the basis of the output vector. Pixels 
- *  are considered in a rectangular region of width dw about this vector. The 
- *  input region is collapsed in the perpendicular direction, and each element 
- *  of the output vector represents pixel-sized boxes, where the value is 
- *  derived from the statistics of the pixels interpolated along the 
- *  perpendicular direction. The specific algorithm which must be used is 
- *  described in the PSLib ADD (PSDC-430-006). The statistic used to derive 
- *  the output vector value is specified by stats. Only one of the statistics 
- *  choices may be specified, otherwise the function must return an error. 
- *  This function must be defined for the following types: psS8, psU16, psF32, 
- *  psF64.
- *
- *  @return psVector    resulting vector
- */
-psVector* psImageCut(
-    psVector* out,                     ///< psVector to recycle, or NULL.
-    psVector* cutCols,                 ///< if not NULL, the calculated column values along the slice (output)
-    psVector* cutRows,                 ///< if not NULL, the calculated row values along the slice (output)
-    const psImage* input,              ///< the input image in which to perform the cut
-    const psImage* mask,               ///< the mask for the input image.
-    psU32 maskVal,                     ///< the mask value to apply to the mask
-    float startCol,                    ///< the column of the start of the cut line
-    float startRow,                    ///< the row of the start of the cut line
-    float endCol,                      ///< the column of the end of the cut line
-    float endRow,                      ///< the row of the end of the cut line
-    psU32 nSamples,                    ///< the number of samples along the cut
-    psImageInterpolateMode mode        ///< the interpolation method to use
-);
-
-/** Extract radial region data to a vector. A vector is constructed where each
- *  vector elements is derived from the statistics of the pixels which land 
- *  within one of a sequence of radii. The radii are centered on the image 
- *  pixel coordinate x,y, and are defined by the sequence of values in the
- *  vector radii. The specific algorithm which must be used is described in 
- *  the PSLib ADD (PSDC-430-006). The statistic used to derive the output 
- *  vector value is specified by stats. Only one of the statistics choices 
- *  may be specified, otherwise the function must return an error. This 
- *  function must be defined for the following types: psS8, psU16, psF32, 
- *  psF64.
- *
- *  @return psVector    resulting vector
- */
-psVector* psImageRadialCut(
-    psVector* out,                     ///< psVector to recycle, or NULL.
-    const psImage* input,              ///< the input image in which to perform the cut
-    const psImage* mask,               ///< the mask for the input image.
-    psU32 maskVal,                     ///< the mask value to apply to the mask
-    float centerCol,                   ///< the column of the center of the cut circle
-    float centerRow,                   ///< the row of the center of the cut circle
-    const psVector* radii,             ///< the radii of the cut circle
-    const psStats* stats               ///< the statistic to perform in operation
-);
-
-/// @}
-
-#endif
Index: /trunk/psLib/src/image/psImageFFT.c
===================================================================
--- /trunk/psLib/src/image/psImageFFT.c	(revision 3967)
+++ /trunk/psLib/src/image/psImageFFT.c	(revision 3968)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-04-15 00:12:08 $
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -19,5 +19,4 @@
 #include "psMemory.h"
 #include "psLogMsg.h"
-#include "psImageExtraction.h"
 #include "psImageIO.h"
 
Index: /trunk/psLib/src/image/psImageGeomManip.c
===================================================================
--- /trunk/psLib/src/image/psImageGeomManip.c	(revision 3968)
+++ /trunk/psLib/src/image/psImageGeomManip.c	(revision 3968)
@@ -0,0 +1,773 @@
+/** @file  psImageGeomManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImageGeomManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+#include "psCoord.h"
+
+psImage* psImageRebin(psImage* out,
+                      const psImage* in,
+                      const psImage* restrict mask,
+                      psMaskType maskVal,
+                      psU32 scale,
+                      const psStats* stats)
+{
+    psS32 inRows;
+    psS32 inCols;
+    psS32 outRows;
+    psS32 outCols;
+    psVector* vec;                     // vector to hold the values of a single bin.
+    psVector* maskVec = NULL;          // vector to hold the mask of a single bin.
+    psMaskType* maskData = NULL;
+    psStats* myStats;
+    double statVal;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    vec = psVectorAlloc(scale * scale, in->type.type);
+
+    if (mask != NULL) {
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            psFree(vec);
+            return NULL;
+        }
+        maskVec = psVectorAlloc(scale * scale, PS_TYPE_MASK);
+        maskData = maskVec->data.PS_TYPE_MASK_DATA;
+    }
+
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    // create output image.
+    inRows = in->numRows;
+    inCols = in->numCols;
+    outRows = (inRows + scale - 1) / scale;     // round-up for remainders
+    outCols = (inCols + scale - 1) / scale;     // round-up for remainders
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    #define PS_IMAGE_REBIN_CASE(type) \
+case PS_TYPE_##type: { \
+        ps##type* outRowData; \
+        ps##type* vecData = vec->data.type; \
+        psMaskType* inRowMask = NULL; \
+        for (psS32 row = 0; row < outRows; row++) { \
+            outRowData = out->data.type[row]; \
+            psS32 inCurrentRow = row*scale; \
+            psS32 inNextRow = (row+1)*scale; \
+            for (psS32 col = 0; col < outCols; col++) { \
+                psS32 inCurrentCol = col*scale; \
+                psS32 inNextCol = (col+1)*scale; \
+                psS32 n = 0; \
+                for (psS32 inRow = inCurrentRow; inRow < inNextRow && inRow < inRows; inRow++) { \
+                    ps##type* inRowData = in->data.type[inRow]; \
+                    if (mask != NULL) { \
+                        inRowMask = mask->data.PS_TYPE_MASK_DATA[inRow]; \
+                    } \
+                    for (psS32 inCol = inCurrentCol; inCol < inNextCol && inCol < inCols; inCol++) { \
+                        if (maskData != NULL) { \
+                            maskData[n] = inRowMask[inCol]; \
+                        } \
+                        vecData[n++] = inRowData[inCol]; \
+                    } \
+                } \
+                vec->n = n; \
+                myStats = psVectorStats(myStats,vec,NULL,maskVec,maskVal); \
+                p_psGetStatValue(myStats,&statVal); \
+                outRowData[col] = (ps##type)statVal; \
+            } \
+        } \
+    } \
+    break;
+
+    switch (in->type.type) {
+        //        PS_IMAGE_REBIN_CASE(U8);       Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(U16);
+        PS_IMAGE_REBIN_CASE(U32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(U64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S8);
+        //        PS_IMAGE_REBIN_CASE(S16);      Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(S32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(F32);
+        PS_IMAGE_REBIN_CASE(F64);
+        //        PS_IMAGE_REBIN_CASE(C32);      Not valid since psVectorStats doesn't allow
+        //        PS_IMAGE_REBIN_CASE(C64);      Not valid since psVectorStats doesn't allow
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    psFree(vec);
+    psFree(maskVec);
+    psFree(myStats);
+
+    return out;
+}
+
+psImage* psImageResample(psImage* out,
+                         const psImage* in,
+                         psS32 scale,
+                         psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    float invScale;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows * scale;
+    outCols = in->numCols * scale;
+    invScale = 1.0f / (float)scale;
+
+    #define PSIMAGE_RESAMPLE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        out = psImageRecycle(out,outCols, outRows, PS_TYPE_##TYPE); \
+        for (psS32 row=0;row<outRows;row++) { \
+            ps##TYPE* rowData = out->data.TYPE[row]; \
+            float inRow = (float)row * invScale; \
+            for (psS32 col=0;col<outCols;col++) { \
+                rowData[col] = psImagePixelInterpolate(in,(float)col*invScale,inRow,NULL,0,0,mode); \
+            } \
+        }  \
+        break; \
+    }
+
+    switch (in->type.type) {
+        PSIMAGE_RESAMPLE_CASE(U8)
+        PSIMAGE_RESAMPLE_CASE(U16)
+        PSIMAGE_RESAMPLE_CASE(U32)
+        PSIMAGE_RESAMPLE_CASE(U64)
+        PSIMAGE_RESAMPLE_CASE(S8)
+        PSIMAGE_RESAMPLE_CASE(S16)
+        PSIMAGE_RESAMPLE_CASE(S32)
+        PSIMAGE_RESAMPLE_CASE(S64)
+        PSIMAGE_RESAMPLE_CASE(F32)
+        PSIMAGE_RESAMPLE_CASE(F64)
+        PSIMAGE_RESAMPLE_CASE(C32)
+        PSIMAGE_RESAMPLE_CASE(C64)
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageRoll(psImage* out,
+                     const psImage* in,
+                     psS32 dx,
+                     psS32 dy)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    elementSize = PSELEMTYPE_SIZEOF(in->type.type);
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    // make dx and dy between 0 and outCols or
+    // outRows, respectively
+    dx = dx % outCols;
+    dy = dy % outRows;
+    if (dx < 0) {
+        dx += outCols;
+    }
+    if (dy < 0) {
+        dy += outRows;
+    }
+
+    psS32 segment1Size = elementSize * (outCols - dx);
+    psS32 segment2Size = elementSize * dx;
+
+    for (psS32 row = 0; row < outRows; row++) {
+        psS32 inRowNumber = row + dy;
+
+        if (inRowNumber >= outRows) {
+            inRowNumber -= outRows;
+        }
+        psU8* inRow = in->data.U8[inRowNumber]; // use byte arithmetic for all types
+        psU8* outRow = out->data.U8[row];
+
+        memcpy(outRow, inRow + segment2Size, segment1Size);
+        memcpy(outRow + segment1Size, inRow, segment2Size);
+    }
+
+    return out;
+}
+
+psImage* psImageRotate(psImage* out,
+                       const psImage* in,
+                       float angle,
+                       psC64 unexposedValue,
+                       psImageInterpolateMode mode)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // put the angle in the range of 0...2PI.
+    angle = (float)((double)angle - (2.0*M_PI) * floor(angle / (2.0*M_PI)));
+
+    if (fabsf(angle - M_PI_2) < FLT_EPSILON) {
+        // perform 1/4 rotate counter-clockwise
+        psS32 numRows = in->numCols;
+        psS32 numCols = in->numRows;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_LEFT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[lastCol-col][row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_LEFT_90(U8);
+            PSIMAGE_ROTATE_LEFT_90(U16);
+            PSIMAGE_ROTATE_LEFT_90(U32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(U64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S8);
+            PSIMAGE_ROTATE_LEFT_90(S16);
+            PSIMAGE_ROTATE_LEFT_90(S32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(F32);
+            PSIMAGE_ROTATE_LEFT_90(F64);
+            PSIMAGE_ROTATE_LEFT_90(C32);
+            PSIMAGE_ROTATE_LEFT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - M_PI) < FLT_EPSILON) {
+        // perform 1/2 rotate
+        psS32 numRows = in->numRows;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numCols;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_180_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                ps##TYPE* inRow = in->data.TYPE[lastRow-row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inRow[lastCol - col]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_180_CASE(U8);
+            PSIMAGE_ROTATE_180_CASE(U16);
+            PSIMAGE_ROTATE_180_CASE(U32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(U64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S8);
+            PSIMAGE_ROTATE_180_CASE(S16);
+            PSIMAGE_ROTATE_180_CASE(S32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(F32);
+            PSIMAGE_ROTATE_180_CASE(F64);
+            PSIMAGE_ROTATE_180_CASE(C32);
+            PSIMAGE_ROTATE_180_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - (M_PI+M_PI_2)) < FLT_EPSILON) {
+        // perform 1/4 rotate clockwise
+        psS32 numRows = in->numCols;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numRows;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_RIGHT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[col][lastRow-row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_RIGHT_90(U8);
+            PSIMAGE_ROTATE_RIGHT_90(U16);
+            PSIMAGE_ROTATE_RIGHT_90(U32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(U64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S8);
+            PSIMAGE_ROTATE_RIGHT_90(S16);
+            PSIMAGE_ROTATE_RIGHT_90(S32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(F32);
+            PSIMAGE_ROTATE_RIGHT_90(F64);
+            PSIMAGE_ROTATE_RIGHT_90(C32);
+            PSIMAGE_ROTATE_RIGHT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle) < FLT_EPSILON) {
+        out = psImageCopy(out, in, in->type.type);
+    } else {
+        psElemType type = in->type.type;
+        psS32 numRows = in->numRows;
+        psS32 numCols = in->numCols;
+        float centerX = (float)(numCols) / 2.0f;
+        float centerY = (float)(numRows) / 2.0f;
+        double cosT = cosf(angle);
+        double sinT = sinf(angle);
+
+        // calculate the corners of the rotated image so we know the proper output image size.
+        // x' = x cos(t) + y sin(t); i.e, x' = (x-centerX)*cosT + (y-centerY)*sinT;
+        // y' = y cos(t) - x sin(t); i.e. y' = (y-centerY)*cosT - (x-centerX)*sinT;
+
+        psS32 outCols = ceil(abs(numCols * cosT) + abs(numRows * sinT)) + 1;
+        psS32 outRows = ceil(abs(numCols * sinT) + abs(numRows * cosT)) + 1;
+        float minX = (float)outCols / -2.0f;
+        psS32 intMinY = outRows / -2;
+
+        out = psImageRecycle(out, outCols, outRows, type);
+
+        /* optimized public domain rotation routine by Karl Lager
+         *
+         * float cosT,sinT;
+         * cosT = cos(t);
+         * sinT = sin(t);
+         * for (y = min_y; y <= max_y; y++) {
+         *     x' = min_x * cosT + y * sinT + x1';
+         *     y' = y * cosT - min_x * sinT + y1';
+         *     for (x = min_x; x <= max_x; x++) {
+         *         if (x', y') is in the bounds of the bitmap, get pixel
+         *            (x', y') and plot the pixel to (x, y) on screen.
+         *         x' += cosT;
+         *         y' -= sinT;
+         *     }
+         * }
+         */
+
+        // precalculate some figures that are used within loop
+        float minXTimesCosTPlusCenterX = minX * cosT + centerX;
+        float CenterYMinusminXTimesSinT = centerY - minX * sinT;
+
+        #define PSIMAGE_ROTATE_ARBITRARY_LOOP(TYPE,MODE) { \
+            if (creal(unexposedValue) < PS_MIN_##TYPE || \
+                    creal(unexposedValue) > PS_MAX_##TYPE || \
+                    cimag(unexposedValue) < PS_MIN_##TYPE || \
+                    cimag(unexposedValue) > PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                        "unexposedValue", \
+                        creal(unexposedValue),cimag(unexposedValue), \
+                        PS_TYPE_##TYPE##_NAME,  \
+                        (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+                psFree(out); \
+                out = NULL; \
+                break; \
+            } \
+            float inX; \
+            float inY; \
+            ps##TYPE* outRow; \
+            for (psS32 y = 0; y < outRows; y++) { \
+                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
+                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
+                outRow = out->data.TYPE[y]; \
+                for (psS32 x = 0; x < outCols; x++) { \
+                    outRow[x] = p_psImagePixelInterpolate##MODE##_##TYPE(in,inX,inY,NULL,0,unexposedValue); \
+                    inX += cosT; \
+                    inY -= sinT; \
+                } \
+            } \
+        }
+
+        #define PSIMAGE_ROTATE_ARBITRARY_CASE(MODE) \
+    case PS_INTERPOLATE_##MODE: \
+        switch (type) { \
+        case PS_TYPE_U8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U8,MODE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U16,MODE); \
+            break; \
+        case PS_TYPE_U32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32,MODE); \
+            break; \
+        case PS_TYPE_U64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U64,MODE); \
+            break;  \
+        case PS_TYPE_S8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S8,MODE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S16,MODE); \
+            break; \
+        case PS_TYPE_S32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32,MODE); \
+            break; \
+        case PS_TYPE_S64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S64,MODE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F32,MODE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F64,MODE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C32,MODE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C64,MODE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                        typeStr); \
+                psFree(out); \
+                out = NULL; \
+            } \
+        } \
+        break;
+
+        switch (mode) {
+            PSIMAGE_ROTATE_ARBITRARY_CASE(FLAT);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR_VARIANCE);
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                    mode);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageShift(psImage* out,
+                      const psImage* in,
+                      float dx,
+                      float dy,
+                      psC64 unexposedValue,
+                      psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+    psElemType type;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    type = in->type.type;
+    elementSize = PSELEMTYPE_SIZEOF(type);
+    out = psImageRecycle(out, outCols, outRows, type);
+
+    #define PSIMAGE_SHIFT_CASE(MODE,TYPE) \
+case PS_TYPE_##TYPE: \
+    if (creal(unexposedValue) < PS_MIN_##TYPE || \
+            creal(unexposedValue) > PS_MAX_##TYPE || \
+            cimag(unexposedValue) < PS_MIN_##TYPE || \
+            cimag(unexposedValue) > PS_MAX_##TYPE) { \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                "unexposedValue", \
+                creal(unexposedValue),cimag(unexposedValue), \
+                PS_TYPE_##TYPE##_NAME,  \
+                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    for (psS32 row=0;row<outRows;row++) { \
+        ps##TYPE* outRow = out->data.TYPE[row]; \
+        float y = dy+(float)row; \
+        for (psS32 col=0;col<outCols;col++) { \
+            outRow[col] = p_psImagePixelInterpolate##MODE##_##TYPE( \
+                          in,dx+(float)col,y,NULL,0,unexposedValue); \
+        } \
+    } \
+    break;
+
+    #define PSIMAGE_SHIFT_ARBITRARY_CASE(MODE) \
+case PS_INTERPOLATE_##MODE: \
+    switch (in->type.type) { \
+        PSIMAGE_SHIFT_CASE(MODE,U8);  \
+        PSIMAGE_SHIFT_CASE(MODE,U16); \
+        PSIMAGE_SHIFT_CASE(MODE,U32);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,U64);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S8);  \
+        PSIMAGE_SHIFT_CASE(MODE,S16); \
+        PSIMAGE_SHIFT_CASE(MODE,S32);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S64);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,F32); \
+        PSIMAGE_SHIFT_CASE(MODE,F64); \
+        PSIMAGE_SHIFT_CASE(MODE,C32); \
+        PSIMAGE_SHIFT_CASE(MODE,C64); \
+        \
+    default: { \
+            char* typeStr; \
+            PS_TYPE_NAME(typeStr,type); \
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                    typeStr); \
+            psFree(out); \
+            out = NULL; \
+        } \
+    } \
+    break;
+
+    switch (mode) {
+        PSIMAGE_SHIFT_ARBITRARY_CASE(FLAT);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR_VARIANCE);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        out = NULL;
+    }
+
+    return out;
+}
+
+
+// XXX: implementation is awaiting working psPlaneTransform functions like
+// invert.  Also, the next SDRS should have a different signature.
+psImage* psImageTransform(psImage *output,
+                          psArray** blankPixels,
+                          const psImage *input,
+                          const psImage *inputMask,
+                          int inputMaskVal,
+                          const psPlaneTransform *outToIn,
+                          const psRegion region,
+                          const psPixels* pixels,
+                          psImageInterpolateMode mode,
+                          int exposedValue)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (outToIn == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_TRANSFORM_NULL);
+        return NULL;
+    }
+
+    int row0 = region.y0;
+    int row1 = region.y1;
+    int col0 = region.x0;
+    int col1 = region.x1;
+    if (col1 < 1) {
+        col1 += input->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += input->numRows;
+    }
+
+    int numRows = row1 - row0;
+    int numCols = col1 - col0;
+
+    if (numRows < 1 || numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "The specified region is invalid.");
+        psFree(output);
+        return NULL;
+    }
+
+    // create the output image.
+    output = psImageRecycle(output, numCols, numRows, input->type.type);
+    *(psS32*)&output->col0 = region.x0;
+    *(psS32*)&output->row0 = region.y0;
+
+
+    // loop through the output image using the domain above and transform
+    // each output pixel to input coordinates and use psImagePixelInterpolate
+    // to determine the pixel value.
+    psPlane outPosition;
+    psPlane* inPosition = NULL;
+    for (int row = 0; row < numRows; row++) {
+        outPosition.y = row+row0;
+        psF32* outputData=output->data.F32[row];
+        for (int col = 0; col < numCols; col++) {
+            outPosition.x = col+col0;
+            // apply the transform to get the position in the input image
+            inPosition = psPlaneTransformApply(inPosition, outToIn, inPosition);
+
+            if (inPosition == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to apply the transform");
+                psFree(output);
+                return NULL;
+            }
+            // interpolate the cooresponding input pixel to get the output pixel value.
+            outputData[col] = (psF32)psImagePixelInterpolate(input,
+                              inPosition->x, inPosition->y,
+                              inputMask, inputMaskVal, exposedValue,
+                              mode);
+
+        }
+    }
+
+    return output;
+}
+
Index: /trunk/psLib/src/image/psImageGeomManip.h
===================================================================
--- /trunk/psLib/src/image/psImageGeomManip.h	(revision 3968)
+++ /trunk/psLib/src/image/psImageGeomManip.h	(revision 3968)
@@ -0,0 +1,157 @@
+/** @file  psImageGeomManip.h
+ *
+ *  @brief Contains basic image geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_GEOM_MANIP_H
+#define PS_IMAGE_GEOM_MANIP_H
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Rebin image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are reduced by a factor of
+ *  1/scale.  The scale, always a positive number, is equal in each dimension and
+ *  specified the number of pixels used to define a new pixel in the output image.
+ *  The output image is generated from all input image pixels. This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage    new image formed by rebinning input image.
+ */
+psImage* psImageRebin(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    const psImage* mask,               ///< mask for input image.  If NULL, no masking is done.
+    psMaskType maskVal,                ///< the bits to check in mask.
+    psU32 scale,                       ///< the scale to rebin for each dimension
+    const psStats* stats
+    ///< the statistic to perform when rebinning.  Only one method should be set.
+);
+
+/** Resample image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are increased by a
+ *  factor of scale. The scale, always a positive number, is equal in each
+ *  dimension. The output image is generated from all input image pixels.
+ *  Each pixel in the output image is derived by interpolating between
+ *  neighboring pixels using the specified interpolation method (mode).
+ *
+ *  @return psImage*    resampled image result
+ */
+psImage* psImageResample(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 scale,                       ///< resample scaling factor
+    psImageInterpolateMode mode        ///< the interpolation mode used in resampling
+);
+
+/** Rotate the input image by given angle, specified in degrees.
+ *
+ *  The output image must contain all of the pixels from the input image in
+ *  their new frame. Pixels in the output image which do not map to input
+ *  pixels should be set to exposed. The center of rotation is always the
+ *  center pixel of the image. The rotation is specified in the sense that a
+ *  positive angle is an anti-clockwise rotation. This function must be
+ *  defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64,
+ *  psC32, psC64.
+ *
+ *  @return psImage*     the rotated image result.
+ */
+psImage* psImageRotate(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float angle,                       ///< the rotation angle in radians.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode used
+);
+
+/** Shift image by an arbitrary number of pixels (dx,dy) in either direction.
+ *
+ *  If the shift values are fractional, the output pixel values should
+ *  interpolate between the input pixel values. The output image has the same
+ *  dimensions as the input image. Pixels which fall off the edge of the
+ *  output image are lost. Newly exposed pixels are set to the value given by
+ *  exposed. This function must be defined for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
+ *
+ *  @return psImage*     the shifted image result.
+ */
+psImage* psImageShift(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float dx,                          ///< the shift in x direction.
+    float dy,                          ///< the shift in y direction.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode to use
+);
+
+/** Roll image by an integer number of pixels in either direction.
+ *
+ *  The output image is the same dimensions as the input image.  Edge pixels
+ *  wrap to the other side (no values are lost).  This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage* the rolled version of the input image.
+ */
+psImage* psImageRoll(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 dx,                          ///< number of pixels to roll in the x-dimension
+    psS32 dy                           ///< number of pixels to roll in the y-dimension
+);
+
+/** Transform the input image according the supplied transformation.
+ *
+ *  Transform the input image according the supplied transformation. The size
+ *  of the transformed image is deï¬ned by the supplied output image, if
+ *  non-NULL, or the region otherwise (size region.x1 - region.x0 by region.y1
+ *  region.y0, with out->x0 = region.x0 and out->y0 = region.y0). If the
+ *  inputMask is non-NULL, those pixels in the inputMask matching inputMaskVal
+ *  are to be ignored in the transformation. The inputMask must be of type
+ *  psU8, and of the same size as the input, otherwise the function shall
+ *  generate an error and return NULL. The transformation outToIn speciï¬es the
+ *  coordinates in the input image of a pixel in the output image â note that
+ *  this is the reverse of what might be naively expected, but it is what is
+ *  required in order to use psImagePixelInterpolate. If the pixels array is
+ *  non-NULL, it shall consist of psPixelCoords, and only those pixels in the
+ *  output image shall be transformed; otherwise, the entire image is
+ *  generated. The interpolation is performed using the speciï¬ed interpolation
+ *  mode. Where a pixel in the output image does not correspond to a pixel in
+ *  the input image (or all appropriate pixels in the input image are
+ *  masked), the value shall be set to exposed, and the pixel added to the
+ *  appropriate list of pixels (psPixels) in the array of blankPixels for
+ *  return to the user. This function must be capable of handling the following
+ *  types for the input (with corresponding types for the output): psF32, psF64.
+ 
+ *
+ *  @return psImage*    The transformed image.
+ */
+psImage* psImageTransform(
+    psImage *output,                   ///< psImage to recycle, or NULL
+    psArray** blankPixels,             ///<
+    const psImage *input,              ///< psImage to apply transform to
+    const psImage *inputMask,          ///< if not NULL, mask of input psImage
+    int inputMaskVal,                  ///< masking value for inputMask
+    const psPlaneTransform *outToIn,   ///< the transform to apply
+    const psRegion region,             ///<
+    const psPixels* pixels,            ///<
+    psImageInterpolateMode mode,       ///<
+    int exposedValue                   ///<
+);
+
+#endif
Index: unk/psLib/src/image/psImageManip.c
===================================================================
--- /trunk/psLib/src/image/psImageManip.c	(revision 3967)
+++ 	(revision )
@@ -1,1139 +1,0 @@
-/** @file  psImageManip.c
- *
- *  @brief Contains basic image pixel and geometry manipulation operations, as
- *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
- *         "Image Geometry Manipulations".
- *
- *  @ingroup Image
- *
- *  @author Robert DeSonia, MHPCC
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-05-14 00:16:46 $
- *
- *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
- */
-
-#include <complex.h>
-#include <math.h>                          // for isfinite(), etc.
-#include <stdlib.h>
-#include <string.h>                        // for memcpy, etc.
-
-#include "psImageManip.h"
-
-#include "psError.h"
-#include "psImage.h"
-#include "psStats.h"
-#include "psMemory.h"
-#include "psImageExtraction.h"
-#include "psConstants.h"
-#include "psImageErrors.h"
-#include "psCoord.h"
-
-psS32 psImageClip(psImage* input,
-                  psF64 min,
-                  psF64 vmin,
-                  psF64 max,
-                  psF64 vmax)
-{
-    psS32 numClipped = 0;
-    psU32 numRows;
-    psU32 numCols;
-
-    if (input == NULL) {
-        return 0;
-    }
-
-    if (max < min) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_MAXMIN,
-                (double)min,(double)max);
-        return 0;
-    }
-
-    numRows = input->numRows;
-    numCols = input->numCols;
-
-    switch (input->type.type) {
-
-        #define psImageClipCase(type) \
-    case PS_TYPE_##type: { \
-            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
-                        "vmin",vmin, PS_TYPE_##type##_NAME, \
-                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
-            } \
-            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
-                        "vmax",vmax, PS_TYPE_##type##_NAME, \
-                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
-            } \
-            for (psU32 row = 0;row<numRows;row++) { \
-                ps##type* inputRow = input->data.type[row]; \
-                for (psU32 col = 0; col < numCols; col++) { \
-                    if ((psF64)inputRow[col] < min) { \
-                        inputRow[col] = (ps##type)vmin; \
-                        numClipped++; \
-                    } else if ((psF64)inputRow[col] > max) { \
-                        inputRow[col] = (ps##type)vmax; \
-                        numClipped++; \
-                    } \
-                } \
-            } \
-        } \
-        break;
-
-        #define psImageClipCaseComplex(type,absfcn)\
-    case PS_TYPE_##type: { \
-            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
-                        "vmin",vmin, PS_TYPE_##type##_NAME, \
-                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
-            } \
-            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
-                        "vmax",vmax, PS_TYPE_##type##_NAME, \
-                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
-            } \
-            for (psU32 row = 0;row<numRows;row++) { \
-                ps##type* inputRow = input->data.type[row]; \
-                for (psU32 col = 0; col < numCols; col++) { \
-                    if (absfcn(inputRow[col]) < min) { \
-                        inputRow[col] = (ps##type)vmin; \
-                        numClipped++; \
-                    } else if (absfcn(inputRow[col]) > max) { \
-                        inputRow[col] = (ps##type)vmax; \
-                        numClipped++; \
-                    } \
-                } \
-            } \
-        } \
-        break;
-
-        psImageClipCase(S8)
-        psImageClipCase(S16)
-        psImageClipCase(S32)            // Not a requirement
-        psImageClipCase(S64)            // Not a requirement
-        psImageClipCase(U8)
-        psImageClipCase(U16)
-        psImageClipCase(U32)            // Not a requirement
-        psImageClipCase(U64)            // Not a requirement
-        psImageClipCase(F32)
-        psImageClipCase(F64)
-        psImageClipCaseComplex(C32, cabsf)
-        psImageClipCaseComplex(C64, cabs)
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,input->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-        }
-    }
-
-    return numClipped;
-}
-
-psS32 psImageClipNaN(psImage* input,
-                     psF64 value)
-{
-    psS32 numClipped = 0;
-    psU32 numRows;
-    psU32 numCols;
-
-    if (input == NULL) {
-        return 0;
-    }
-    numRows = input->numRows;
-    numCols = input->numCols;
-
-    switch (input->type.type) {
-
-        #define psImageClipNaNCase(type) \
-    case PS_TYPE_##type: \
-        for (psU32 row = 0;row<numRows;row++) { \
-            ps##type* inputRow = input->data.type[row]; \
-            for (psU32 col = 0; col < numCols; col++) { \
-                if (! isfinite(inputRow[col])) { \
-                    inputRow[col] = (ps##type)value; \
-                    numClipped++; \
-                } \
-            } \
-        } \
-        break;
-
-        psImageClipNaNCase(F32)
-        psImageClipNaNCase(F64)
-        psImageClipNaNCase(C32)
-        psImageClipNaNCase(C64)
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,input->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-        }
-    }
-
-    return numClipped;
-}
-
-psS32 psImageOverlaySection(psImage* image,
-                            const psImage* overlay,
-                            psS32 col0,
-                            psS32 row0,
-                            const char *op)
-{
-    psU32 imageNumRows;
-    psU32 imageNumCols;
-    psU32 overlayNumRows;
-    psU32 overlayNumCols;
-    psU32 imageRowLimit;
-    psU32 imageColLimit;
-    psElemType type;
-    psU32 pixelsOverlaid = 0;
-
-    if (image == NULL || overlay == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        return pixelsOverlaid;
-    }
-
-    if (op == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImageManip_OPERATION_NULL);
-        return pixelsOverlaid;
-    }
-
-    type = image->type.type;
-
-    if (type != overlay->type.type) {
-        char* typeStr;
-        char* typeStrOverlay;
-        PS_TYPE_NAME(typeStr,type);
-        PS_TYPE_NAME(typeStrOverlay,overlay->type.type);
-        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                PS_ERRORTEXT_psImageManip_OVERLAY_TYPE_MISMATCH,
-                typeStrOverlay, typeStr);
-        return pixelsOverlaid;
-    }
-
-    imageNumRows = image->numRows;
-    imageNumCols = image->numCols;
-    overlayNumRows = overlay->numRows;
-    overlayNumCols = overlay->numCols;
-    imageRowLimit = row0 + overlayNumRows;
-    imageColLimit = col0 + overlayNumCols;
-
-    /* check to see if overlay is within the input image */
-    if ( row0 < 0 ||
-            col0 < 0 ||
-            imageRowLimit > imageNumRows ||
-            imageColLimit > imageNumCols) {
-
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
-                col0, imageColLimit, row0, imageRowLimit,
-                imageNumCols, imageNumRows);
-        return pixelsOverlaid;
-    }
-
-
-    #define psImageOverlayLoop(DATATYPE,OP) { \
-        for (int row=row0;row<imageRowLimit;row++) { \
-            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
-            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-row0]; \
-            for (int col=col0;col<imageColLimit;col++) { \
-                imageRow[col] OP overlayRow[col-col0]; \
-            } \
-        } \
-        pixelsOverlaid += (imageRowLimit - row0) * (imageColLimit - col0); \
-    }
-
-    #define psImageOverlayCase(DATATYPE) \
-case PS_TYPE_##DATATYPE: \
-    switch (*op) { \
-    case '+': \
-        psImageOverlayLoop(DATATYPE,+=); \
-        break; \
-    case '-': \
-        psImageOverlayLoop(DATATYPE,-=); \
-        break; \
-    case '*': \
-        psImageOverlayLoop(DATATYPE,*=); \
-        break; \
-    case '/': \
-        psImageOverlayLoop(DATATYPE,/=); \
-        break; \
-    case '=': \
-        psImageOverlayLoop(DATATYPE,=); \
-        break; \
-    default: \
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                PS_ERRORTEXT_psImageManip_OVERLAY_OPERATOR_INVALID, \
-                op); \
-        return pixelsOverlaid; \
-    } \
-    break;
-
-    switch (type) {
-        psImageOverlayCase(U8);
-        psImageOverlayCase(U16);
-        psImageOverlayCase(U32);       // Not a requirement
-        psImageOverlayCase(U64);       // Not a requirement
-        psImageOverlayCase(S8);
-        psImageOverlayCase(S16);
-        psImageOverlayCase(S32);       // Not a requirement
-        psImageOverlayCase(S64);       // Not a requirement
-        psImageOverlayCase(F32);
-        psImageOverlayCase(F64);
-        psImageOverlayCase(C32);
-        psImageOverlayCase(C64);
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-            return pixelsOverlaid;
-        }
-    }
-
-    return pixelsOverlaid;
-}
-
-psS32 psImageClipComplexRegion(psImage* input,
-                               psC64 min,
-                               psC64 vmin,
-                               psC64 max,
-                               psC64 vmax)
-{
-    psS32 numClipped = 0;
-    psU32 numRows;
-    psU32 numCols;
-    psF64 realMin = creal(min);
-    psF64 imagMin = cimag(min);
-    psF64 realMax = creal(max);
-    psF64 imagMax = cimag(max);
-
-    if (input == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        return 0;
-    }
-
-    if (realMax < realMin) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_MAXMIN_REAL,
-                (double)realMin, (double)realMax);
-        return 0;
-    }
-    if (imagMax < imagMin) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_MAXMIN_IMAG,
-                (double)imagMin, (double)imagMax);
-        return 0;
-    }
-
-    numRows = input->numRows;
-    numCols = input->numCols;
-
-    #define psImageClipComplexRegionCase(type,realfcn,imagfcn) \
-case PS_TYPE_##type: { \
-        if (realfcn(vmin) < PS_MIN_##type || imagfcn(vmin) < PS_MIN_##type || \
-                realfcn(vmin) > PS_MAX_##type || imagfcn(vmin) > PS_MAX_##type ) { \
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
-                    "vmin", creal(vmin), cimag(vmin), \
-                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
-            break; \
-        } \
-        if (realfcn(vmax) > PS_MAX_##type || imagfcn(vmax) > PS_MAX_##type || \
-                realfcn(vmax) < PS_MIN_##type || imagfcn(vmax) < PS_MIN_##type ) { \
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
-                    "vmax", creal(vmax), cimag(vmax), \
-                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
-            break; \
-        } \
-        for (psU32 row = 0;row<numRows;row++) { \
-            ps##type* inputRow = input->data.type[row]; \
-            for (psU32 col = 0; col < numCols; col++) { \
-                if ( (realfcn(inputRow[col]) > realMax) || (imagfcn(inputRow[col]) > imagMax) ) { \
-                    inputRow[col] = (ps##type)vmax; \
-                    numClipped++; \
-                } else if ( (realfcn(inputRow[col]) < realMin) || (imagfcn(inputRow[col]) < imagMin) ){ \
-                    inputRow[col] = (ps##type)vmin; \
-                    numClipped++; \
-                } \
-            } \
-        } \
-    } \
-    break;
-
-    switch (input->type.type) {
-
-        psImageClipComplexRegionCase(C32, crealf, cimagf)
-        psImageClipComplexRegionCase(C64, creal, cimag)
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,input->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-        }
-    }
-
-    return numClipped;
-}
-
-psImage* psImageRebin(psImage* out,
-                      const psImage* in,
-                      const psImage* restrict mask,
-                      psMaskType maskVal,
-                      psU32 scale,
-                      const psStats* stats)
-{
-    psS32 inRows;
-    psS32 inCols;
-    psS32 outRows;
-    psS32 outCols;
-    psVector* vec;                     // vector to hold the values of a single bin.
-    psVector* maskVec = NULL;          // vector to hold the mask of a single bin.
-    psMaskType* maskData = NULL;
-    psStats* myStats;
-    double statVal;
-
-    if (in == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    if (scale < 1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
-                scale);
-        psFree(out);
-        return NULL;
-    }
-
-    if (stats == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_STAT_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    if (p_psGetStatValue(stats, &statVal) == false) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImage_BAD_STAT,
-                stats->options);
-        psFree(out);
-        return NULL;
-    }
-
-    vec = psVectorAlloc(scale * scale, in->type.type);
-
-    if (mask != NULL) {
-        if (mask->type.type != PS_TYPE_MASK) {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,mask->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
-                    typeStr, PS_TYPE_MASK_NAME);
-            psFree(out);
-            psFree(vec);
-            return NULL;
-        }
-        maskVec = psVectorAlloc(scale * scale, PS_TYPE_MASK);
-        maskData = maskVec->data.PS_TYPE_MASK_DATA;
-    }
-
-    myStats = psAlloc(sizeof(psStats));
-    *myStats = *stats;
-
-    // create output image.
-    inRows = in->numRows;
-    inCols = in->numCols;
-    outRows = (inRows + scale - 1) / scale;     // round-up for remainders
-    outCols = (inCols + scale - 1) / scale;     // round-up for remainders
-    out = psImageRecycle(out, outCols, outRows, in->type.type);
-
-    #define PS_IMAGE_REBIN_CASE(type) \
-case PS_TYPE_##type: { \
-        ps##type* outRowData; \
-        ps##type* vecData = vec->data.type; \
-        psMaskType* inRowMask = NULL; \
-        for (psS32 row = 0; row < outRows; row++) { \
-            outRowData = out->data.type[row]; \
-            psS32 inCurrentRow = row*scale; \
-            psS32 inNextRow = (row+1)*scale; \
-            for (psS32 col = 0; col < outCols; col++) { \
-                psS32 inCurrentCol = col*scale; \
-                psS32 inNextCol = (col+1)*scale; \
-                psS32 n = 0; \
-                for (psS32 inRow = inCurrentRow; inRow < inNextRow && inRow < inRows; inRow++) { \
-                    ps##type* inRowData = in->data.type[inRow]; \
-                    if (mask != NULL) { \
-                        inRowMask = mask->data.PS_TYPE_MASK_DATA[inRow]; \
-                    } \
-                    for (psS32 inCol = inCurrentCol; inCol < inNextCol && inCol < inCols; inCol++) { \
-                        if (maskData != NULL) { \
-                            maskData[n] = inRowMask[inCol]; \
-                        } \
-                        vecData[n++] = inRowData[inCol]; \
-                    } \
-                } \
-                vec->n = n; \
-                myStats = psVectorStats(myStats,vec,NULL,maskVec,maskVal); \
-                p_psGetStatValue(myStats,&statVal); \
-                outRowData[col] = (ps##type)statVal; \
-            } \
-        } \
-    } \
-    break;
-
-    switch (in->type.type) {
-        //        PS_IMAGE_REBIN_CASE(U8);       Not valid since psVectorStats doesn't allow
-        PS_IMAGE_REBIN_CASE(U16);
-        PS_IMAGE_REBIN_CASE(U32);      // Not a requirement
-        PS_IMAGE_REBIN_CASE(U64);      // Not a requirement
-        PS_IMAGE_REBIN_CASE(S8);
-        //        PS_IMAGE_REBIN_CASE(S16);      Not valid since psVectorStats doesn't allow
-        PS_IMAGE_REBIN_CASE(S32);      // Not a requirement
-        PS_IMAGE_REBIN_CASE(S64);      // Not a requirement
-        PS_IMAGE_REBIN_CASE(F32);
-        PS_IMAGE_REBIN_CASE(F64);
-        //        PS_IMAGE_REBIN_CASE(C32);      Not valid since psVectorStats doesn't allow
-        //        PS_IMAGE_REBIN_CASE(C64);      Not valid since psVectorStats doesn't allow
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,in->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-            psFree(out);
-            out = NULL;
-        }
-    }
-
-    psFree(vec);
-    psFree(maskVec);
-    psFree(myStats);
-
-    return out;
-}
-
-psImage* psImageResample(psImage* out,
-                         const psImage* in,
-                         psS32 scale,
-                         psImageInterpolateMode mode)
-{
-    psS32 outRows;
-    psS32 outCols;
-    float invScale;
-
-    if (in == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-
-    if (scale < 1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
-                scale);
-        psFree(out);
-        return NULL;
-    }
-
-    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
-                mode);
-        psFree(out);
-        return NULL;
-    }
-
-    // create an output image of the same size
-    // and type
-    outRows = in->numRows * scale;
-    outCols = in->numCols * scale;
-    invScale = 1.0f / (float)scale;
-
-    #define PSIMAGE_RESAMPLE_CASE(TYPE) \
-case PS_TYPE_##TYPE: { \
-        out = psImageRecycle(out,outCols, outRows, PS_TYPE_##TYPE); \
-        for (psS32 row=0;row<outRows;row++) { \
-            ps##TYPE* rowData = out->data.TYPE[row]; \
-            float inRow = (float)row * invScale; \
-            for (psS32 col=0;col<outCols;col++) { \
-                rowData[col] = psImagePixelInterpolate(in,(float)col*invScale,inRow,NULL,0,0,mode); \
-            } \
-        }  \
-        break; \
-    }
-
-    switch (in->type.type) {
-        PSIMAGE_RESAMPLE_CASE(U8)
-        PSIMAGE_RESAMPLE_CASE(U16)
-        PSIMAGE_RESAMPLE_CASE(U32)
-        PSIMAGE_RESAMPLE_CASE(U64)
-        PSIMAGE_RESAMPLE_CASE(S8)
-        PSIMAGE_RESAMPLE_CASE(S16)
-        PSIMAGE_RESAMPLE_CASE(S32)
-        PSIMAGE_RESAMPLE_CASE(S64)
-        PSIMAGE_RESAMPLE_CASE(F32)
-        PSIMAGE_RESAMPLE_CASE(F64)
-        PSIMAGE_RESAMPLE_CASE(C32)
-        PSIMAGE_RESAMPLE_CASE(C64)
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,in->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
-            psFree(out);
-            out = NULL;
-        }
-    }
-
-    return out;
-}
-
-psImage* psImageRoll(psImage* out,
-                     const psImage* in,
-                     psS32 dx,
-                     psS32 dy)
-{
-    psS32 outRows;
-    psS32 outCols;
-    psS32 elementSize;
-
-    if (in == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-    // create an output image of the same size
-    // and type
-    outRows = in->numRows;
-    outCols = in->numCols;
-    elementSize = PSELEMTYPE_SIZEOF(in->type.type);
-    out = psImageRecycle(out, outCols, outRows, in->type.type);
-
-    // make dx and dy between 0 and outCols or
-    // outRows, respectively
-    dx = dx % outCols;
-    dy = dy % outRows;
-    if (dx < 0) {
-        dx += outCols;
-    }
-    if (dy < 0) {
-        dy += outRows;
-    }
-
-    psS32 segment1Size = elementSize * (outCols - dx);
-    psS32 segment2Size = elementSize * dx;
-
-    for (psS32 row = 0; row < outRows; row++) {
-        psS32 inRowNumber = row + dy;
-
-        if (inRowNumber >= outRows) {
-            inRowNumber -= outRows;
-        }
-        psU8* inRow = in->data.U8[inRowNumber]; // use byte arithmetic for all types
-        psU8* outRow = out->data.U8[row];
-
-        memcpy(outRow, inRow + segment2Size, segment1Size);
-        memcpy(outRow + segment1Size, inRow, segment2Size);
-    }
-
-    return out;
-}
-
-psImage* psImageRotate(psImage* out,
-                       const psImage* in,
-                       float angle,
-                       psC64 unexposedValue,
-                       psImageInterpolateMode mode)
-{
-    if (in == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-    // put the angle in the range of 0...2PI.
-    angle = (float)((double)angle - (2.0*M_PI) * floor(angle / (2.0*M_PI)));
-
-    if (fabsf(angle - M_PI_2) < FLT_EPSILON) {
-        // perform 1/4 rotate counter-clockwise
-        psS32 numRows = in->numCols;
-        psS32 numCols = in->numRows;
-        psS32 lastCol = numCols - 1;
-        psElemType type = in->type.type;
-
-        out = psImageRecycle(out, numCols, numRows, type);
-
-        #define PSIMAGE_ROTATE_LEFT_90(TYPE) \
-    case PS_TYPE_##TYPE: { \
-            ps##TYPE** inData = in->data.TYPE; \
-            for (psS32 row=0;row<numRows;row++) { \
-                ps##TYPE* outRow = out->data.TYPE[row]; \
-                for (psS32 col=0;col<numCols;col++) { \
-                    outRow[col] = inData[lastCol-col][row]; \
-                } \
-            } \
-        } \
-        break;
-
-        switch (type) {
-            PSIMAGE_ROTATE_LEFT_90(U8);
-            PSIMAGE_ROTATE_LEFT_90(U16);
-            PSIMAGE_ROTATE_LEFT_90(U32);    //  Not a requirement
-            PSIMAGE_ROTATE_LEFT_90(U64);    //  Not a requirement
-            PSIMAGE_ROTATE_LEFT_90(S8);
-            PSIMAGE_ROTATE_LEFT_90(S16);
-            PSIMAGE_ROTATE_LEFT_90(S32);    //  Not a requirement
-            PSIMAGE_ROTATE_LEFT_90(S64);    //  Not a requirement
-            PSIMAGE_ROTATE_LEFT_90(F32);
-            PSIMAGE_ROTATE_LEFT_90(F64);
-            PSIMAGE_ROTATE_LEFT_90(C32);
-            PSIMAGE_ROTATE_LEFT_90(C64);
-
-        default: {
-                char* typeStr;
-                PS_TYPE_NAME(typeStr,type);
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                        typeStr);
-                psFree(out);
-                return NULL;
-            }
-        }
-    } else if (fabsf(angle - M_PI) < FLT_EPSILON) {
-        // perform 1/2 rotate
-        psS32 numRows = in->numRows;
-        psS32 lastRow = numRows - 1;
-        psS32 numCols = in->numCols;
-        psS32 lastCol = numCols - 1;
-        psElemType type = in->type.type;
-
-        out = psImageRecycle(out, numCols, numRows, type);
-
-        #define PSIMAGE_ROTATE_180_CASE(TYPE) \
-    case PS_TYPE_##TYPE: { \
-            for (psS32 row=0;row<numRows;row++) { \
-                ps##TYPE* outRow = out->data.TYPE[row]; \
-                ps##TYPE* inRow = in->data.TYPE[lastRow-row]; \
-                for (psS32 col=0;col<numCols;col++) { \
-                    outRow[col] = inRow[lastCol - col]; \
-                } \
-            } \
-        } \
-        break;
-
-        switch (type) {
-            PSIMAGE_ROTATE_180_CASE(U8);
-            PSIMAGE_ROTATE_180_CASE(U16);
-            PSIMAGE_ROTATE_180_CASE(U32);    // Not a requirement
-            PSIMAGE_ROTATE_180_CASE(U64);    // Not a requirement
-            PSIMAGE_ROTATE_180_CASE(S8);
-            PSIMAGE_ROTATE_180_CASE(S16);
-            PSIMAGE_ROTATE_180_CASE(S32);    // Not a requirement
-            PSIMAGE_ROTATE_180_CASE(S64);    // Not a requirement
-            PSIMAGE_ROTATE_180_CASE(F32);
-            PSIMAGE_ROTATE_180_CASE(F64);
-            PSIMAGE_ROTATE_180_CASE(C32);
-            PSIMAGE_ROTATE_180_CASE(C64);
-
-        default: {
-                char* typeStr;
-                PS_TYPE_NAME(typeStr,type);
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                        typeStr);
-                psFree(out);
-                return NULL;
-            }
-        }
-    } else if (fabsf(angle - (M_PI+M_PI_2)) < FLT_EPSILON) {
-        // perform 1/4 rotate clockwise
-        psS32 numRows = in->numCols;
-        psS32 lastRow = numRows - 1;
-        psS32 numCols = in->numRows;
-        psElemType type = in->type.type;
-
-        out = psImageRecycle(out, numCols, numRows, type);
-
-        #define PSIMAGE_ROTATE_RIGHT_90(TYPE) \
-    case PS_TYPE_##TYPE: { \
-            ps##TYPE** inData = in->data.TYPE; \
-            for (psS32 row=0;row<numRows;row++) { \
-                ps##TYPE* outRow = out->data.TYPE[row]; \
-                for (psS32 col=0;col<numCols;col++) { \
-                    outRow[col] = inData[col][lastRow-row]; \
-                } \
-            } \
-        } \
-        break;
-
-        switch (type) {
-            PSIMAGE_ROTATE_RIGHT_90(U8);
-            PSIMAGE_ROTATE_RIGHT_90(U16);
-            PSIMAGE_ROTATE_RIGHT_90(U32);     // Not a requirement
-            PSIMAGE_ROTATE_RIGHT_90(U64);     // Not a requirement
-            PSIMAGE_ROTATE_RIGHT_90(S8);
-            PSIMAGE_ROTATE_RIGHT_90(S16);
-            PSIMAGE_ROTATE_RIGHT_90(S32);     // Not a requirement
-            PSIMAGE_ROTATE_RIGHT_90(S64);     // Not a requirement
-            PSIMAGE_ROTATE_RIGHT_90(F32);
-            PSIMAGE_ROTATE_RIGHT_90(F64);
-            PSIMAGE_ROTATE_RIGHT_90(C32);
-            PSIMAGE_ROTATE_RIGHT_90(C64);
-
-        default: {
-                char* typeStr;
-                PS_TYPE_NAME(typeStr,type);
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                        typeStr);
-                psFree(out);
-                return NULL;
-            }
-        }
-    } else if (fabsf(angle) < FLT_EPSILON) {
-        out = psImageCopy(out, in, in->type.type);
-    } else {
-        psElemType type = in->type.type;
-        psS32 numRows = in->numRows;
-        psS32 numCols = in->numCols;
-        float centerX = (float)(numCols) / 2.0f;
-        float centerY = (float)(numRows) / 2.0f;
-        double cosT = cosf(angle);
-        double sinT = sinf(angle);
-
-        // calculate the corners of the rotated image so we know the proper output image size.
-        // x' = x cos(t) + y sin(t); i.e, x' = (x-centerX)*cosT + (y-centerY)*sinT;
-        // y' = y cos(t) - x sin(t); i.e. y' = (y-centerY)*cosT - (x-centerX)*sinT;
-
-        psS32 outCols = ceil(abs(numCols * cosT) + abs(numRows * sinT)) + 1;
-        psS32 outRows = ceil(abs(numCols * sinT) + abs(numRows * cosT)) + 1;
-        float minX = (float)outCols / -2.0f;
-        psS32 intMinY = outRows / -2;
-
-        out = psImageRecycle(out, outCols, outRows, type);
-
-        /* optimized public domain rotation routine by Karl Lager
-         *
-         * float cosT,sinT;
-         * cosT = cos(t);
-         * sinT = sin(t);
-         * for (y = min_y; y <= max_y; y++) {
-         *     x' = min_x * cosT + y * sinT + x1';
-         *     y' = y * cosT - min_x * sinT + y1';
-         *     for (x = min_x; x <= max_x; x++) {
-         *         if (x', y') is in the bounds of the bitmap, get pixel
-         *            (x', y') and plot the pixel to (x, y) on screen.
-         *         x' += cosT;
-         *         y' -= sinT;
-         *     }
-         * }
-         */
-
-        // precalculate some figures that are used within loop
-        float minXTimesCosTPlusCenterX = minX * cosT + centerX;
-        float CenterYMinusminXTimesSinT = centerY - minX * sinT;
-
-        #define PSIMAGE_ROTATE_ARBITRARY_LOOP(TYPE,MODE) { \
-            if (creal(unexposedValue) < PS_MIN_##TYPE || \
-                    creal(unexposedValue) > PS_MAX_##TYPE || \
-                    cimag(unexposedValue) < PS_MIN_##TYPE || \
-                    cimag(unexposedValue) > PS_MAX_##TYPE) { \
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                        PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
-                        "unexposedValue", \
-                        creal(unexposedValue),cimag(unexposedValue), \
-                        PS_TYPE_##TYPE##_NAME,  \
-                        (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
-                psFree(out); \
-                out = NULL; \
-                break; \
-            } \
-            float inX; \
-            float inY; \
-            ps##TYPE* outRow; \
-            for (psS32 y = 0; y < outRows; y++) { \
-                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
-                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
-                outRow = out->data.TYPE[y]; \
-                for (psS32 x = 0; x < outCols; x++) { \
-                    outRow[x] = p_psImagePixelInterpolate##MODE##_##TYPE(in,inX,inY,NULL,0,unexposedValue); \
-                    inX += cosT; \
-                    inY -= sinT; \
-                } \
-            } \
-        }
-
-        #define PSIMAGE_ROTATE_ARBITRARY_CASE(MODE) \
-    case PS_INTERPOLATE_##MODE: \
-        switch (type) { \
-        case PS_TYPE_U8: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(U8,MODE); \
-            break; \
-        case PS_TYPE_U16: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(U16,MODE); \
-            break; \
-        case PS_TYPE_U32:   /* Not a requirement */ \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32,MODE); \
-            break; \
-        case PS_TYPE_U64:   /* Not a requirement */ \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(U64,MODE); \
-            break;  \
-        case PS_TYPE_S8: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(S8,MODE); \
-            break; \
-        case PS_TYPE_S16: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(S16,MODE); \
-            break; \
-        case PS_TYPE_S32:   /* Not a requirement */ \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32,MODE); \
-            break; \
-        case PS_TYPE_S64:   /* Not a requirement */ \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(S64,MODE); \
-            break; \
-        case PS_TYPE_F32: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(F32,MODE); \
-            break; \
-        case PS_TYPE_F64: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(F64,MODE); \
-            break; \
-        case PS_TYPE_C32: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(C32,MODE); \
-            break; \
-        case PS_TYPE_C64: \
-            PSIMAGE_ROTATE_ARBITRARY_LOOP(C64,MODE); \
-            break; \
-        default: { \
-                char* typeStr; \
-                PS_TYPE_NAME(typeStr,type); \
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
-                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
-                        typeStr); \
-                psFree(out); \
-                out = NULL; \
-            } \
-        } \
-        break;
-
-        switch (mode) {
-            PSIMAGE_ROTATE_ARBITRARY_CASE(FLAT);
-            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
-            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR_VARIANCE);
-        default:
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                    PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
-                    mode);
-            psFree(out);
-            out = NULL;
-        }
-    }
-
-    return out;
-}
-
-psImage* psImageShift(psImage* out,
-                      const psImage* in,
-                      float dx,
-                      float dy,
-                      psC64 unexposedValue,
-                      psImageInterpolateMode mode)
-{
-    psS32 outRows;
-    psS32 outCols;
-    psS32 elementSize;
-    psElemType type;
-
-    if (in == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        psFree(out);
-        return NULL;
-    }
-    // create an output image of the same size
-    // and type
-    outRows = in->numRows;
-    outCols = in->numCols;
-    type = in->type.type;
-    elementSize = PSELEMTYPE_SIZEOF(type);
-    out = psImageRecycle(out, outCols, outRows, type);
-
-    #define PSIMAGE_SHIFT_CASE(MODE,TYPE) \
-case PS_TYPE_##TYPE: \
-    if (creal(unexposedValue) < PS_MIN_##TYPE || \
-            creal(unexposedValue) > PS_MAX_##TYPE || \
-            cimag(unexposedValue) < PS_MIN_##TYPE || \
-            cimag(unexposedValue) > PS_MAX_##TYPE) { \
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
-                PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
-                "unexposedValue", \
-                creal(unexposedValue),cimag(unexposedValue), \
-                PS_TYPE_##TYPE##_NAME,  \
-                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
-        psFree(out); \
-        out = NULL; \
-        break; \
-    } \
-    for (psS32 row=0;row<outRows;row++) { \
-        ps##TYPE* outRow = out->data.TYPE[row]; \
-        float y = dy+(float)row; \
-        for (psS32 col=0;col<outCols;col++) { \
-            outRow[col] = p_psImagePixelInterpolate##MODE##_##TYPE( \
-                          in,dx+(float)col,y,NULL,0,unexposedValue); \
-        } \
-    } \
-    break;
-
-    #define PSIMAGE_SHIFT_ARBITRARY_CASE(MODE) \
-case PS_INTERPOLATE_##MODE: \
-    switch (in->type.type) { \
-        PSIMAGE_SHIFT_CASE(MODE,U8);  \
-        PSIMAGE_SHIFT_CASE(MODE,U16); \
-        PSIMAGE_SHIFT_CASE(MODE,U32);     /* Not a requirement */ \
-        PSIMAGE_SHIFT_CASE(MODE,U64);     /* Not a requirement */ \
-        PSIMAGE_SHIFT_CASE(MODE,S8);  \
-        PSIMAGE_SHIFT_CASE(MODE,S16); \
-        PSIMAGE_SHIFT_CASE(MODE,S32);    /* Not a requirement */ \
-        PSIMAGE_SHIFT_CASE(MODE,S64);    /* Not a requirement */ \
-        PSIMAGE_SHIFT_CASE(MODE,F32); \
-        PSIMAGE_SHIFT_CASE(MODE,F64); \
-        PSIMAGE_SHIFT_CASE(MODE,C32); \
-        PSIMAGE_SHIFT_CASE(MODE,C64); \
-        \
-    default: { \
-            char* typeStr; \
-            PS_TYPE_NAME(typeStr,type); \
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
-                    typeStr); \
-            psFree(out); \
-            out = NULL; \
-        } \
-    } \
-    break;
-
-    switch (mode) {
-        PSIMAGE_SHIFT_ARBITRARY_CASE(FLAT);
-        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR);
-        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR_VARIANCE);
-    default:
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
-                mode);
-        psFree(out);
-        out = NULL;
-    }
-
-    return out;
-}
-
-
-// XXX: implementation is awaiting working psPlaneTransform functions like
-// invert.  Also, the next SDRS should have a different signature.
-psImage* psImageTransform(psImage *output,
-                          psArray** blankPixels,
-                          const psImage *input,
-                          const psImage *inputMask,
-                          int inputMaskVal,
-                          const psPlaneTransform *outToIn,
-                          const psRegion region,
-                          const psPixels* pixels,
-                          psImageInterpolateMode mode,
-                          int exposedValue)
-{
-    if (input == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImage_IMAGE_NULL);
-        return NULL;
-    }
-
-    if (outToIn == NULL) {
-        psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                PS_ERRORTEXT_psImageManip_TRANSFORM_NULL);
-        return NULL;
-    }
-
-    int row0 = region.y0;
-    int row1 = region.y1;
-    int col0 = region.x0;
-    int col1 = region.x1;
-    if (col1 < 1) {
-        col1 += input->numCols;
-    }
-
-    if (row1 < 1) {
-        row1 += input->numRows;
-    }
-
-    int numRows = row1 - row0;
-    int numCols = col1 - col0;
-
-    if (numRows < 1 || numCols < 1) {
-        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
-                "The specified region is invalid.");
-        psFree(output);
-        return NULL;
-    }
-
-    // create the output image.
-    output = psImageRecycle(output, numCols, numRows, input->type.type);
-    *(psS32*)&output->col0 = region.x0;
-    *(psS32*)&output->row0 = region.y0;
-
-
-    // loop through the output image using the domain above and transform
-    // each output pixel to input coordinates and use psImagePixelInterpolate
-    // to determine the pixel value.
-    psPlane outPosition;
-    psPlane* inPosition = NULL;
-    for (int row = 0; row < numRows; row++) {
-        outPosition.y = row+row0;
-        psF32* outputData=output->data.F32[row];
-        for (int col = 0; col < numCols; col++) {
-            outPosition.x = col+col0;
-            // apply the transform to get the position in the input image
-            inPosition = psPlaneTransformApply(inPosition, outToIn, inPosition);
-
-            if (inPosition == NULL) {
-                psError(PS_ERR_UNKNOWN, false,
-                        "Failed to apply the transform");
-                psFree(output);
-                return NULL;
-            }
-            // interpolate the cooresponding input pixel to get the output pixel value.
-            outputData[col] = (psF32)psImagePixelInterpolate(input,
-                              inPosition->x, inPosition->y,
-                              inputMask, inputMaskVal, exposedValue,
-                              mode);
-
-        }
-    }
-
-    return output;
-}
-
Index: unk/psLib/src/image/psImageManip.h
===================================================================
--- /trunk/psLib/src/image/psImageManip.h	(revision 3967)
+++ 	(revision )
@@ -1,223 +1,0 @@
-/** @file  psImageManip.h
- *
- *  @brief Contains basic image pixel and geometry manipulation operations, as
- *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
- *         "Image Geometry Manipulations".
- *
- *  @ingroup Image
- *
- *  @author Robert DeSonia, MHPCC
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-05-11 02:29:39 $
- *
- *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
- */
-#ifndef PS_IMAGE_MANIP_H
-#define PS_IMAGE_MANIP_H
-
-#include "psImage.h"
-#include "psCoord.h"
-#include "psStats.h"
-#include "psPixels.h"
-
-/// @addtogroup Image
-/// @{
-
-/** Clip image values outside of range to given values
- *
- *  All pixels with values less than min are set to the value vmin.  all pixels
- *  with values greater than max are set to the value vmax. This function is
- *  defined for psU8, psU16, psS8, psS16, psF32, psF64, psC32, and psC64.
- *
- *  @return psS32     The number of clipped pixels
- */
-psS32 psImageClip(
-    psImage* input,                    ///< the image to clip
-    psF64 min,                         ///< the minimum image value allowed
-    psF64 vmin,                        ///< the value pixels < min are set to
-    psF64 max,                         ///< the maximum image value allowed
-    psF64 vmax                         ///< the value pixels > max are set to
-);
-
-/** Clip image values outside of a specified complex region
- *
- *  All pixels outside of the rectangular region in complex space formed by
- *  the min and max input parameters are set to the value vmax (if either
- *  the real or imaginary portion exceeds the respective max values), or vmin.
- *  This function is defined for psC32, and psC64 imagery only.
- *
- *  @return psS32     The number of clipped pixels
- */
-psS32 psImageClipComplexRegion(
-    psImage* input,                    ///< the image to clip
-    psC64 min,                         ///< the minimum image value allowed
-    psC64 vmin,                        ///< the value pixels < min are set to
-    psC64 max,                         ///< the maximum image value allowed
-    psC64 vmax                         ///< the value pixels > max are set to
-);
-
-/** Clip NaN image pixels to given value.
- *
- *  Pixels with NaN, +Inf, or -Inf values are set to the specified value. This
- *  function is defined for psF32, psF64, psC32, and psC64.
- *
- *  @return psS32     The number of clipped pixels
- */
-psS32 psImageClipNaN(
-    psImage* input,                    ///< the image to clip
-    psF64 value                        ///< the value to set all NaN/Inf values to
-);
-
-/** Overlay subregion of image with another image
- *
- *  Replace the pixels in the image which correspond to the pixels in OVERLAY
- *  with values derived from the IMAGE and OVERLAY based on the given operator
- *  OP.  Valid operators are "=" (set image value to OVERLAY value), "+" (add
- *  OVERLAY value to image value), "-" (subtract OVERLAY from image), "*"
- *  (multiply OVERLAY times image), "/" (divide image by OVERLAY).  This
- *  function is defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
- *
- *  @return psS32         0 if success, non-zero if failed.
- */
-psS32 psImageOverlaySection(
-    psImage* image,                    ///< target image
-    const psImage* overlay,            ///< the overlay image
-    psS32 col0,                        ///< the column to start overlay
-    psS32 row0,                        ///< the row to start overlay
-    const char *op                     ///< the operation to perform for overlay
-);
-
-/** Rebin image to new scale.
- *
- *  A new image is constructed in which the dimensions are reduced by a factor of
- *  1/scale.  The scale, always a positive number, is equal in each dimension and
- *  specified the number of pixels used to define a new pixel in the output image.
- *  The output image is generated from all input image pixels. This function is
- *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
- *
- *  @return psImage    new image formed by rebinning input image.
- */
-psImage* psImageRebin(
-    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
-    const psImage* in,                 ///< input image
-    const psImage* mask,               ///< mask for input image.  If NULL, no masking is done.
-    psMaskType maskVal,                ///< the bits to check in mask.
-    psU32 scale,                       ///< the scale to rebin for each dimension
-    const psStats* stats
-    ///< the statistic to perform when rebinning.  Only one method should be set.
-);
-
-/** Resample image to new scale.
- *
- *  A new image is constructed in which the dimensions are increased by a
- *  factor of scale. The scale, always a positive number, is equal in each
- *  dimension. The output image is generated from all input image pixels.
- *  Each pixel in the output image is derived by interpolating between
- *  neighboring pixels using the specified interpolation method (mode).
- *
- *  @return psImage*    resampled image result
- */
-psImage* psImageResample(
-    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
-    const psImage* in,                 ///< input image
-    psS32 scale,                       ///< resample scaling factor
-    psImageInterpolateMode mode        ///< the interpolation mode used in resampling
-);
-
-/** Rotate the input image by given angle, specified in degrees.
- *
- *  The output image must contain all of the pixels from the input image in
- *  their new frame. Pixels in the output image which do not map to input
- *  pixels should be set to exposed. The center of rotation is always the
- *  center pixel of the image. The rotation is specified in the sense that a
- *  positive angle is an anti-clockwise rotation. This function must be
- *  defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64,
- *  psC32, psC64.
- *
- *  @return psImage*     the rotated image result.
- */
-psImage* psImageRotate(
-    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
-    const psImage* in,                 ///< input image
-    float angle,                       ///< the rotation angle in radians.
-    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
-    psImageInterpolateMode mode        ///< the interpolation mode used
-);
-
-/** Shift image by an arbitrary number of pixels (dx,dy) in either direction.
- *
- *  If the shift values are fractional, the output pixel values should
- *  interpolate between the input pixel values. The output image has the same
- *  dimensions as the input image. Pixels which fall off the edge of the
- *  output image are lost. Newly exposed pixels are set to the value given by
- *  exposed. This function must be defined for the following types: psU8,
- *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
- *
- *  @return psImage*     the shifted image result.
- */
-psImage* psImageShift(
-    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
-    const psImage* in,                 ///< input image
-    float dx,                          ///< the shift in x direction.
-    float dy,                          ///< the shift in y direction.
-    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
-    psImageInterpolateMode mode        ///< the interpolation mode to use
-);
-
-/** Roll image by an integer number of pixels in either direction.
- *
- *  The output image is the same dimensions as the input image.  Edge pixels
- *  wrap to the other side (no values are lost).  This function is
- *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
- *
- *  @return psImage* the rolled version of the input image.
- */
-psImage* psImageRoll(
-    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
-    const psImage* in,                 ///< input image
-    psS32 dx,                          ///< number of pixels to roll in the x-dimension
-    psS32 dy                           ///< number of pixels to roll in the y-dimension
-);
-
-/** Transform the input image according the supplied transformation.
- *
- *  Transform the input image according the supplied transformation. The size
- *  of the transformed image is deï¬ned by the supplied output image, if
- *  non-NULL, or the region otherwise (size region.x1 - region.x0 by region.y1
- *  region.y0, with out->x0 = region.x0 and out->y0 = region.y0). If the
- *  inputMask is non-NULL, those pixels in the inputMask matching inputMaskVal
- *  are to be ignored in the transformation. The inputMask must be of type
- *  psU8, and of the same size as the input, otherwise the function shall
- *  generate an error and return NULL. The transformation outToIn speciï¬es the
- *  coordinates in the input image of a pixel in the output image â note that
- *  this is the reverse of what might be naively expected, but it is what is
- *  required in order to use psImagePixelInterpolate. If the pixels array is
- *  non-NULL, it shall consist of psPixelCoords, and only those pixels in the
- *  output image shall be transformed; otherwise, the entire image is
- *  generated. The interpolation is performed using the speciï¬ed interpolation
- *  mode. Where a pixel in the output image does not correspond to a pixel in
- *  the input image (or all appropriate pixels in the input image are
- *  masked), the value shall be set to exposed, and the pixel added to the
- *  appropriate list of pixels (psPixels) in the array of blankPixels for
- *  return to the user. This function must be capable of handling the following
- *  types for the input (with corresponding types for the output): psF32, psF64.
- 
- *
- *  @return psImage*    The transformed image.
- */
-psImage* psImageTransform(
-    psImage *output,                   ///< psImage to recycle, or NULL
-    psArray** blankPixels,             ///<
-    const psImage *input,              ///< psImage to apply transform to
-    const psImage *inputMask,          ///< if not NULL, mask of input psImage
-    int inputMaskVal,                  ///< masking value for inputMask
-    const psPlaneTransform *outToIn,   ///< the transform to apply
-    const psRegion region,             ///<
-    const psPixels* pixels,            ///<
-    psImageInterpolateMode mode,       ///<
-    int exposedValue                   ///<
-);
-
-#endif
Index: /trunk/psLib/src/image/psImagePixelExtract.c
===================================================================
--- /trunk/psLib/src/image/psImagePixelExtract.c	(revision 3968)
+++ /trunk/psLib/src/image/psImagePixelExtract.c	(revision 3968)
@@ -0,0 +1,597 @@
+/** @file  psImagePixelExtract.c
+ *
+ *  @brief Contains basic image extraction operations, as specified in the
+ *         PSLIB SDRS sections "Image Pixel Extractions".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImagePixelExtract.h"
+#include "psError.h"
+
+#include "psImageErrors.h"
+
+psVector* psImageSlice(psVector* out,
+                       psVector* slicePositions,
+                       const psImage* restrict in,
+                       const psImage* restrict mask,
+                       psU32 maskVal,
+                       psS32 col0,
+                       psS32 row0,
+                       psS32 col1,
+                       psS32 row1,
+                       psImageCutDirection direction,
+                       const psStats* stats)
+{
+    double statVal;
+    psStats* myStats;
+    psElemType type;
+    psS32 inRows;
+    psS32 inCols;
+    psS32 delta = 1;
+    psF64* outData;
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 += in->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += in->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > in->numCols ||
+            row1 > in->numRows ||
+            col0 >= col1 ||
+            row0 >= row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1, row0, row1,
+                in->numCols, in->numRows);
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    inRows = in->numRows;
+    inCols = in->numCols;
+
+    if (direction == PS_CUT_X_NEG || direction == PS_CUT_Y_NEG) {
+        delta = -1;
+    }
+
+    if (mask != NULL) {
+        if (inRows != mask->numRows || inCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    inCols, inRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,stats->options);
+        psFree(out);
+        return NULL;
+    }
+    // since stats input is const, I need to
+    // create a 'scratch' stats struct
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    psS32 numCols = col1-col0;
+    psS32 numRows = row1-row0;
+
+    if (direction == PS_CUT_X_POS || direction == PS_CUT_X_NEG) {
+        psVector* imgVec = psVectorAlloc(numRows, type);
+        psVector* maskVec = NULL;
+        psMaskType* maskData = NULL;
+        psU32* outPosition = NULL;
+
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats is
+        // psStats.
+        out = psVectorRecycle(out, numCols, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numCols, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numCols - 1;
+            if (outPosition != NULL) {
+                outPosition += numCols - 1;
+            }
+        }
+
+        if (mask != NULL) {
+            maskVec = psVectorAlloc(numRows, mask->type.type);
+        }
+        #define PSIMAGE_CUT_VERTICAL(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            psMaskType* maskVecData = NULL; \
+            for (psS32 c=col0;c<col1;c++) { \
+                ps##TYPE *imgData = in->data.TYPE[row0] + c; \
+                ps##TYPE *imgVecData = imgVec->data.TYPE; \
+                if (maskVec != NULL) { \
+                    maskVecData = maskVec->data.U8; \
+                    maskData = (psMaskType* )(mask->data.U8[row0]) + c; \
+                } \
+                for (psS32 r=row0;r<row1;r++) { \
+                    *(imgVecData++) = *imgData; \
+                    imgData += inCols; \
+                    if (maskVecData != NULL) { \
+                        *(maskVecData++) = *maskData; \
+                        maskData += inCols; \
+                    } \
+                } \
+                myStats = psVectorStats(myStats,imgVec,NULL,maskVec,maskVal); \
+                (void)p_psGetStatValue(myStats,&statVal); \
+                *outData = statVal; \
+                if (outPosition != NULL) { \
+                    *outPosition = c; \
+                    outPosition += delta; \
+                } \
+                outData += delta; \
+            } \
+            break; \
+        }
+
+        switch (type) {
+            PSIMAGE_CUT_VERTICAL(U8);  // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U16);
+            PSIMAGE_CUT_VERTICAL(U32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S8);
+            PSIMAGE_CUT_VERTICAL(S16); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(F32);
+            PSIMAGE_CUT_VERTICAL(F64);
+            PSIMAGE_CUT_VERTICAL(C32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(C64); // Not a requirement
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                out = NULL;
+            }
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else if (direction == PS_CUT_Y_POS || direction == PS_CUT_Y_NEG) {
+        // Cut in Y direction
+        psVector* imgVec = NULL;
+        psVector* maskVec = NULL;
+        psS32 elementSize = PSELEMTYPE_SIZEOF(type);
+        psU32* outPosition = NULL;
+
+        // fill in psVector to fake out the statistics functions.
+        imgVec = psAlloc(sizeof(psVector));
+        imgVec->type = in->type;
+        imgVec->n = *(int*)&imgVec->nalloc = numCols;
+        if (mask != NULL) {
+            maskVec = psAlloc(sizeof(psVector));
+            maskVec->type = mask->type;
+            maskVec->n = *(int*)&maskVec->nalloc = numCols;
+        }
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats in
+        // psStats.
+        out = psVectorRecycle(out, numRows, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numRows, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numRows-1;
+            if (outPosition != NULL) {
+                outPosition += numRows-1;
+            }
+        }
+
+        for (psS32 r = row0; r < row1; r++) {
+            // point the vector struct to the
+            // data to calculate the stats
+            imgVec->data.U8 = (psPtr )(in->data.U8[r] + col0 * elementSize);
+            if (maskVec != NULL) {
+                maskVec->data.U8 = (psPtr )(mask->data.U8[r] + col0 * sizeof(psMaskType));
+            }
+            myStats = psVectorStats(myStats, imgVec, NULL, maskVec, maskVal);
+            (void)p_psGetStatValue(myStats, &statVal);  // we know it works cause we tested it above
+            *outData = statVal;
+            if (outPosition != NULL) {
+                *outPosition = r;
+                outPosition += delta;
+
+            }
+            outData += delta;
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else { // don't know what the direction flag is
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SLICE_DIRECTION_INVALID,
+                direction);
+        psFree(out);
+        out = NULL;
+    }
+
+    psFree(myStats);
+
+    return out;
+}
+
+psVector* psImageCut(psVector* out,
+                     psVector* cutCols,
+                     psVector* cutRows,
+                     const psImage* in,
+                     const psImage* restrict mask,
+                     psU32 maskVal,
+                     float startCol,
+                     float startRow,
+                     float endCol,
+                     float endRow,
+                     psU32 nSamples,
+                     psImageInterpolateMode mode)
+{
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (nSamples < 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_nSamples_TOOSMALL,
+                nSamples);
+        psFree(out);
+        return NULL;
+    }
+
+    if (startCol < 0 || startCol >= numCols ||
+            startRow < 0 || startRow >= numRows ||
+            endCol < 0 || endCol >= numCols ||
+            endRow < 0 || endRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_LINE_NOT_IN_IMAGE,
+                startCol,startRow,endCol,endRow,
+                numCols-1,numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols-1, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    //resize the vectors for the coordinate output
+    psF32* cutColsData = NULL;
+    psF32* cutRowsData = NULL;
+    if (cutCols != NULL) {
+        cutCols = psVectorRecycle(cutCols, nSamples, PS_TYPE_F32);
+        cutColsData = cutCols->data.F32;
+    }
+    if (cutRows != NULL) {
+        cutRows = psVectorRecycle(cutRows, nSamples, PS_TYPE_F32);
+        cutRowsData = cutRows->data.F32;
+    }
+
+    out = psVectorRecycle(out, nSamples, in->type.type);
+
+    float dX = (endCol - startCol) / (float)(nSamples-1);
+    float dY = (endRow - startRow) / (float)(nSamples-1);
+
+    #define LINEAR_CUT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE* outData = out->data.TYPE; \
+        for (psS32 i = 0; i < nSamples; i++) { \
+            float x = startCol + (float)i*dX; \
+            float y = startRow + (float)i*dY; \
+            /* store off the location of the sample. */ \
+            if (cutColsData != NULL) { \
+                cutColsData[i] = x; \
+            } \
+            if (cutRowsData != NULL) { \
+                cutRowsData[i] = y; \
+            } \
+            outData[i] = psImagePixelInterpolate(in,x,y,mask,maskVal,0,mode); \
+        } \
+    } \
+    break;
+
+
+    switch (in->type.type) {
+        LINEAR_CUT_CASE(U8);
+        LINEAR_CUT_CASE(U16);
+        LINEAR_CUT_CASE(U32);
+        LINEAR_CUT_CASE(U64);
+        LINEAR_CUT_CASE(S8);
+        LINEAR_CUT_CASE(S16);
+        LINEAR_CUT_CASE(S32);
+        LINEAR_CUT_CASE(S64);
+        LINEAR_CUT_CASE(F32);
+        LINEAR_CUT_CASE(F64);
+        LINEAR_CUT_CASE(C32);
+        LINEAR_CUT_CASE(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psVector* psImageRadialCut(psVector* out,
+                           const psImage* in,
+                           const psImage* restrict mask,
+                           psU32 maskVal,
+                           float centerCol,
+                           float centerRow,
+                           const psVector* radii,
+                           const psStats* stats)
+{
+    double statVal;
+
+    /* check the parameters */
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (centerCol < 0 || centerCol >= numCols ||
+            centerRow < 0 || centerRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_CENTER_NOT_IN_IMAGE,
+                centerCol, centerRow,
+                numCols-1, numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii->n < 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_TOOSMALL,
+                radii->n);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    /* completed checking the parameters */
+
+    // size the output vector to proper size.
+    psS32 numOut = radii->n - 1;
+    out = psVectorRecycle(out, numOut, PS_TYPE_F64);
+    psF64* outData = out->data.F64;
+
+    psVector* rSqVec = psVectorCopy(NULL, radii, PS_TYPE_F32);
+    psF32* rSq = rSqVec->data.F32;
+
+    psS32 startRow = centerRow - rSq[numOut];
+    psS32 endRow = centerRow + rSq[numOut];
+    psS32 startCol = centerCol - rSq[numOut];
+    psS32 endCol = centerCol + rSq[numOut];
+
+    if (startRow < 0) {
+        startRow = 0;
+    }
+
+    if (startCol < 0) {
+        startCol = 0;
+    }
+
+    if (endRow >= numRows) {
+        endRow = numRows - 1;
+    }
+
+    if (endCol >= numCols) {
+        endCol = numCols - 1;
+    }
+
+    // Square the radii data
+    for (psS32 d = 0; d <= numOut; d++) {
+        rSq[d] *= rSq[d];
+    }
+
+    // create temporary vectors for the data binning step
+    psVector** buffer = psAlloc(sizeof(psVector*)*numOut);
+    psVector** bufferMask = psAlloc(sizeof(psVector*)*numOut);
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        // n.b. alloc enough for the data by making the vectors slightly larger
+        // than the area of the region of interest.
+        buffer[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                    in->type.type);
+        buffer[lcv]->n = 0;
+
+        bufferMask[lcv] = NULL;
+        if (mask != NULL) {
+            bufferMask[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                            PS_TYPE_MASK);
+            bufferMask[lcv]->n = 0;
+        }
+    }
+
+    float dX;
+    float dY;
+    float dist;
+    for (psS32 row=startRow; row <= endRow; row++) {
+        psF32* inRow = in->data.F32[row];
+        psMaskType* maskRow = NULL;
+        if (mask != NULL) {
+            maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        }
+        for (psS32 col=startCol; col <= endCol; col++) {
+            dX = centerCol - (float)col - 0.5f;
+            dY = centerRow - (float)row - 0.5f;
+            dist = dX*dX+dY*dY;
+            for (psS32 r = 0; r < numOut; r++) {
+                if (rSq[r] < dist && dist < rSq[r+1]) {
+                    psS32 n = buffer[r]->n;
+                    if (n == buffer[r]->nalloc) { // in case buffers already full, expand
+                        buffer[r] = psVectorRealloc(buffer[r], n*2);
+                        if (bufferMask[r] != NULL) {
+                            bufferMask[r] = psVectorRealloc(bufferMask[r], n*2);
+                        }
+                    }
+
+                    buffer[r]->data.F32[n] = inRow[col];
+                    buffer[r]->n = n+1;
+
+                    if (maskRow != NULL) {
+                        bufferMask[r]->data.PS_TYPE_MASK_DATA[n] = maskRow[col];
+                        bufferMask[r]->n = n+1;
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    psStats* myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    for (psS32 r = 0; r < numOut; r++) {
+        myStats = psVectorStats(myStats,buffer[r], NULL, bufferMask[r],maskVal);
+        (void)p_psGetStatValue(myStats,&statVal);
+        outData[r] = statVal;
+    }
+
+    psFree(myStats);
+
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        psFree(buffer[lcv]);
+        psFree(bufferMask[lcv]);
+    }
+    psFree(buffer);
+    psFree(bufferMask);
+    psFree(rSqVec);
+    return out;
+}
Index: /trunk/psLib/src/image/psImagePixelExtract.h
===================================================================
--- /trunk/psLib/src/image/psImagePixelExtract.h	(revision 3968)
+++ /trunk/psLib/src/image/psImagePixelExtract.h	(revision 3968)
@@ -0,0 +1,132 @@
+/** @file  psImagePixelExtract.h
+*
+*  @brief Contains basic image extraction operations, as specified in the
+*         PSLIB SDRS sections "Image Pixel Extractions".
+*
+*  @ingroup Image
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 02:08:21 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGE_PIXEL_EXTRACT_H
+#define PSIMAGE_PIXEL_EXTRACT_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psStats.h"
+
+/// @addtogroup Image
+/// @{
+
+/* Cut direction flag.  Used with psImageCut function.
+ */
+typedef enum {
+    PS_CUT_X_POS,                      ///< Cut in the x dimension from left to right
+    PS_CUT_X_NEG,                      ///< Cut in the x dimension from rigth to left
+    PS_CUT_Y_POS,                      ///< Cut in the y dimension from bottom up
+    PS_CUT_Y_NEG,                      ///< Cut in the y dimension from top down.
+} psImageCutDirection;
+
+/** Extract pixels from rectlinear region to a vector (array of floats).
+ *
+ *  The output vector contains either col1-col0 or row1-row0 elements, based
+ *  on the value of the direction: e.g., if direction is PS_CUT_X_POS, there
+ *  are col1-col0 elements. The region to be  sliced  is defined by the
+ *  lower-left corner, (col0,row0), and the upper-right corner, (col1,row1).
+ *  Note that the row and column of the  upper right-hand corner  are NOT
+ *  included in the region. In the event that col1 or row1 are negative, they
+ *  shall be interpreted as being relative to the size of the parent image in
+ *  that dimension. The input region is collapsed in the direction perpendicular
+ *  to that specified by direction, and each element of the output vectors is
+ *  derived from the statistics of the pixels at that direction coordinate. The
+ *  statistic used to derive the output vector value is specified by stats.
+ *  If mask is non-NULL, pixels for which the corresponding mask pixel
+ *  matches maskVal are excluded from operations. If coords is not NULL, the
+ *  calculated coordinates along the slice are returned in this vector. Only
+ *  one of the statistics choices may be specified, otherwise the function
+ *  must return an error.
+ *
+ *  This function is defined for the following types: psS8, psU16, psF32, psF64.
+ *
+ * @return psVector    the resulting vector
+ */
+psVector* psImageSlice(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* slicePositions,
+    ///< If not NULL, it is populated with the coordinate in the slice dimension
+    ///< coorsponding to the output vector's value of the same position in the
+    ///< vector.  This vector maybe resized and retyped as appropriate.
+    const psImage* input,              ///< the input image in which to perform the slice
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    psS32 col0,                        ///< the leftmost column of the slice region
+    psS32 row0,                        ///< the bottommost row of the slice region
+    psS32 col1,                        ///< exclusive end column of the slice region
+    psS32 row1,                        ///< exclusive end row of the slice region
+    psImageCutDirection direction,     ///< the slice dimension and direction
+    const psStats* stats               ///< the statistic to perform in slice operation
+);
+
+/** Extract pixels from an image along a line to a vector (array of floats).
+ *
+ *  The vector (xs,ys) - (xe,ye) forms the basis of the output vector. Pixels
+ *  are considered in a rectangular region of width dw about this vector. The
+ *  input region is collapsed in the perpendicular direction, and each element
+ *  of the output vector represents pixel-sized boxes, where the value is
+ *  derived from the statistics of the pixels interpolated along the
+ *  perpendicular direction. The specific algorithm which must be used is
+ *  described in the PSLib ADD (PSDC-430-006). The statistic used to derive
+ *  the output vector value is specified by stats. Only one of the statistics
+ *  choices may be specified, otherwise the function must return an error.
+ *  This function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* cutCols,                 ///< if not NULL, the calculated column values along the slice (output)
+    psVector* cutRows,                 ///< if not NULL, the calculated row values along the slice (output)
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float startCol,                    ///< the column of the start of the cut line
+    float startRow,                    ///< the row of the start of the cut line
+    float endCol,                      ///< the column of the end of the cut line
+    float endRow,                      ///< the row of the end of the cut line
+    psU32 nSamples,                    ///< the number of samples along the cut
+    psImageInterpolateMode mode        ///< the interpolation method to use
+);
+
+/** Extract radial region data to a vector. A vector is constructed where each
+ *  vector elements is derived from the statistics of the pixels which land
+ *  within one of a sequence of radii. The radii are centered on the image
+ *  pixel coordinate x,y, and are defined by the sequence of values in the
+ *  vector radii. The specific algorithm which must be used is described in
+ *  the PSLib ADD (PSDC-430-006). The statistic used to derive the output
+ *  vector value is specified by stats. Only one of the statistics choices
+ *  may be specified, otherwise the function must return an error. This
+ *  function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageRadialCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float centerCol,                   ///< the column of the center of the cut circle
+    float centerRow,                   ///< the row of the center of the cut circle
+    const psVector* radii,             ///< the radii of the cut circle
+    const psStats* stats               ///< the statistic to perform in operation
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/image/psImagePixelManip.c
===================================================================
--- /trunk/psLib/src/image/psImagePixelManip.c	(revision 3968)
+++ /trunk/psLib/src/image/psImagePixelManip.c	(revision 3968)
@@ -0,0 +1,397 @@
+/** @file  psImagePixelManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImagePixelManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+#include "psCoord.h"
+
+psS32 psImageClip(psImage* input,
+                  psF64 min,
+                  psF64 vmin,
+                  psF64 max,
+                  psF64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+
+    if (max < min) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN,
+                (double)min,(double)max);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipCase(type) \
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if ((psF64)inputRow[col] < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if ((psF64)inputRow[col] > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        #define psImageClipCaseComplex(type,absfcn)\
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if (absfcn(inputRow[col]) < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if (absfcn(inputRow[col]) > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipCase(S8)
+        psImageClipCase(S16)
+        psImageClipCase(S32)            // Not a requirement
+        psImageClipCase(S64)            // Not a requirement
+        psImageClipCase(U8)
+        psImageClipCase(U16)
+        psImageClipCase(U32)            // Not a requirement
+        psImageClipCase(U64)            // Not a requirement
+        psImageClipCase(F32)
+        psImageClipCase(F64)
+        psImageClipCaseComplex(C32, cabsf)
+        psImageClipCaseComplex(C64, cabs)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageClipNaN(psImage* input,
+                     psF64 value)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipNaNCase(type) \
+    case PS_TYPE_##type: \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if (! isfinite(inputRow[col])) { \
+                    inputRow[col] = (ps##type)value; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipNaNCase(F32)
+        psImageClipNaNCase(F64)
+        psImageClipNaNCase(C32)
+        psImageClipNaNCase(C64)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageOverlaySection(psImage* image,
+                            const psImage* overlay,
+                            psS32 col0,
+                            psS32 row0,
+                            const char *op)
+{
+    psU32 imageNumRows;
+    psU32 imageNumCols;
+    psU32 overlayNumRows;
+    psU32 overlayNumCols;
+    psU32 imageRowLimit;
+    psU32 imageColLimit;
+    psElemType type;
+    psU32 pixelsOverlaid = 0;
+
+    if (image == NULL || overlay == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return pixelsOverlaid;
+    }
+
+    if (op == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_OPERATION_NULL);
+        return pixelsOverlaid;
+    }
+
+    type = image->type.type;
+
+    if (type != overlay->type.type) {
+        char* typeStr;
+        char* typeStrOverlay;
+        PS_TYPE_NAME(typeStr,type);
+        PS_TYPE_NAME(typeStrOverlay,overlay->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageManip_OVERLAY_TYPE_MISMATCH,
+                typeStrOverlay, typeStr);
+        return pixelsOverlaid;
+    }
+
+    imageNumRows = image->numRows;
+    imageNumCols = image->numCols;
+    overlayNumRows = overlay->numRows;
+    overlayNumCols = overlay->numCols;
+    imageRowLimit = row0 + overlayNumRows;
+    imageColLimit = col0 + overlayNumCols;
+
+    /* check to see if overlay is within the input image */
+    if ( row0 < 0 ||
+            col0 < 0 ||
+            imageRowLimit > imageNumRows ||
+            imageColLimit > imageNumCols) {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, imageColLimit, row0, imageRowLimit,
+                imageNumCols, imageNumRows);
+        return pixelsOverlaid;
+    }
+
+
+    #define psImageOverlayLoop(DATATYPE,OP) { \
+        for (int row=row0;row<imageRowLimit;row++) { \
+            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
+            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-row0]; \
+            for (int col=col0;col<imageColLimit;col++) { \
+                imageRow[col] OP overlayRow[col-col0]; \
+            } \
+        } \
+        pixelsOverlaid += (imageRowLimit - row0) * (imageColLimit - col0); \
+    }
+
+    #define psImageOverlayCase(DATATYPE) \
+case PS_TYPE_##DATATYPE: \
+    switch (*op) { \
+    case '+': \
+        psImageOverlayLoop(DATATYPE,+=); \
+        break; \
+    case '-': \
+        psImageOverlayLoop(DATATYPE,-=); \
+        break; \
+    case '*': \
+        psImageOverlayLoop(DATATYPE,*=); \
+        break; \
+    case '/': \
+        psImageOverlayLoop(DATATYPE,/=); \
+        break; \
+    case '=': \
+        psImageOverlayLoop(DATATYPE,=); \
+        break; \
+    default: \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_OVERLAY_OPERATOR_INVALID, \
+                op); \
+        return pixelsOverlaid; \
+    } \
+    break;
+
+    switch (type) {
+        psImageOverlayCase(U8);
+        psImageOverlayCase(U16);
+        psImageOverlayCase(U32);       // Not a requirement
+        psImageOverlayCase(U64);       // Not a requirement
+        psImageOverlayCase(S8);
+        psImageOverlayCase(S16);
+        psImageOverlayCase(S32);       // Not a requirement
+        psImageOverlayCase(S64);       // Not a requirement
+        psImageOverlayCase(F32);
+        psImageOverlayCase(F64);
+        psImageOverlayCase(C32);
+        psImageOverlayCase(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            return pixelsOverlaid;
+        }
+    }
+
+    return pixelsOverlaid;
+}
+
+psS32 psImageClipComplexRegion(psImage* input,
+                               psC64 min,
+                               psC64 vmin,
+                               psC64 max,
+                               psC64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+    psF64 realMin = creal(min);
+    psF64 imagMin = cimag(min);
+    psF64 realMax = creal(max);
+    psF64 imagMax = cimag(max);
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return 0;
+    }
+
+    if (realMax < realMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_REAL,
+                (double)realMin, (double)realMax);
+        return 0;
+    }
+    if (imagMax < imagMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_IMAG,
+                (double)imagMin, (double)imagMax);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    #define psImageClipComplexRegionCase(type,realfcn,imagfcn) \
+case PS_TYPE_##type: { \
+        if (realfcn(vmin) < PS_MIN_##type || imagfcn(vmin) < PS_MIN_##type || \
+                realfcn(vmin) > PS_MAX_##type || imagfcn(vmin) > PS_MAX_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmin", creal(vmin), cimag(vmin), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        if (realfcn(vmax) > PS_MAX_##type || imagfcn(vmax) > PS_MAX_##type || \
+                realfcn(vmax) < PS_MIN_##type || imagfcn(vmax) < PS_MIN_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmax", creal(vmax), cimag(vmax), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if ( (realfcn(inputRow[col]) > realMax) || (imagfcn(inputRow[col]) > imagMax) ) { \
+                    inputRow[col] = (ps##type)vmax; \
+                    numClipped++; \
+                } else if ( (realfcn(inputRow[col]) < realMin) || (imagfcn(inputRow[col]) < imagMin) ){ \
+                    inputRow[col] = (ps##type)vmin; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+    } \
+    break;
+
+    switch (input->type.type) {
+
+        psImageClipComplexRegionCase(C32, crealf, cimagf)
+        psImageClipComplexRegionCase(C64, creal, cimag)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
Index: /trunk/psLib/src/image/psImagePixelManip.h
===================================================================
--- /trunk/psLib/src/image/psImagePixelManip.h	(revision 3968)
+++ /trunk/psLib/src/image/psImagePixelManip.h	(revision 3968)
@@ -0,0 +1,90 @@
+/** @file  psImagePixelManip.h
+ *
+ *  @brief Contains basic image pixel manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations"
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_PIXEL_MANIP_H
+#define PS_IMAGE_PIXEL_MANIP_H
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Clip image values outside of range to given values
+ *
+ *  All pixels with values less than min are set to the value vmin.  all pixels
+ *  with values greater than max are set to the value vmax. This function is
+ *  defined for psU8, psU16, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClip(
+    psImage* input,                    ///< the image to clip
+    psF64 min,                         ///< the minimum image value allowed
+    psF64 vmin,                        ///< the value pixels < min are set to
+    psF64 max,                         ///< the maximum image value allowed
+    psF64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip image values outside of a specified complex region
+ *
+ *  All pixels outside of the rectangular region in complex space formed by
+ *  the min and max input parameters are set to the value vmax (if either
+ *  the real or imaginary portion exceeds the respective max values), or vmin.
+ *  This function is defined for psC32, and psC64 imagery only.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipComplexRegion(
+    psImage* input,                    ///< the image to clip
+    psC64 min,                         ///< the minimum image value allowed
+    psC64 vmin,                        ///< the value pixels < min are set to
+    psC64 max,                         ///< the maximum image value allowed
+    psC64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip NaN image pixels to given value.
+ *
+ *  Pixels with NaN, +Inf, or -Inf values are set to the specified value. This
+ *  function is defined for psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipNaN(
+    psImage* input,                    ///< the image to clip
+    psF64 value                        ///< the value to set all NaN/Inf values to
+);
+
+/** Overlay subregion of image with another image
+ *
+ *  Replace the pixels in the image which correspond to the pixels in OVERLAY
+ *  with values derived from the IMAGE and OVERLAY based on the given operator
+ *  OP.  Valid operators are "=" (set image value to OVERLAY value), "+" (add
+ *  OVERLAY value to image value), "-" (subtract OVERLAY from image), "*"
+ *  (multiply OVERLAY times image), "/" (divide image by OVERLAY).  This
+ *  function is defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32         0 if success, non-zero if failed.
+ */
+psS32 psImageOverlaySection(
+    psImage* image,                    ///< target image
+    const psImage* overlay,            ///< the overlay image
+    psS32 col0,                        ///< the column to start overlay
+    psS32 row0,                        ///< the row to start overlay
+    const char *op                     ///< the operation to perform for overlay
+);
+
+#endif
Index: /trunk/psLib/src/image/psImageStructManip.c
===================================================================
--- /trunk/psLib/src/image/psImageStructManip.c	(revision 3968)
+++ /trunk/psLib/src/image/psImageStructManip.c	(revision 3968)
@@ -0,0 +1,242 @@
+/** @file  psImageStructManip.c
+ *
+ *  @brief Contains basic image structure manipulations, as specified in the
+ *         PSLIB SDRS sections "Image Structure Manipulation".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImageStructManip.h"
+#include "psError.h"
+
+#include "psImageErrors.h"
+
+static psImage* imageSubset(psImage* out,
+                            psImage* image,
+                            psS32 col0,
+                            psS32 row0,
+                            psS32 col1,
+                            psS32 row1)
+{
+    psU32 elementSize;          // size of image element in bytes
+    psU32 inputColOffset;       // offset in bytes to first subset pixel in input row
+
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 = image->numCols + col1;
+    }
+    if (row1 < 1) {
+        row1 = image->numRows + row1;
+    }
+
+    if (    col1 <= col0 ||
+            row1 <= row0 ||
+            col0 >= image->numCols ||
+            row0 >= image->numRows ||
+            col1 > image->numCols ||
+            row1 > image->numRows ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        return NULL;
+    }
+    psS32 numRows = row1-row0;
+    psS32 numCols = col1-col0;
+
+    elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+
+    if (image->parent != NULL) { // if this is a child, we need to start working with parent.
+        col0 += image->col0;
+        col1 += image->col0;
+        row0 += image->row0;
+        row1 += image->row0;
+        image = (psImage*)image->parent;
+    }
+
+    // increment the raw data buffer before freeing anything in the 'out'
+    psPtr rawData = psMemIncrRefCounter(image->rawDataBuffer);
+
+    if (out != NULL) {
+        // if a child, need to orphan (disassociate from parent) first
+        if (out->parent != NULL) {
+            psArrayRemove(out->parent->children,out); // remove from parent's knowledge
+            out->parent = NULL; // break link to parent
+        }
+
+        psFree(out->rawDataBuffer); // free the previous data reference
+    } else {
+        out = psAlloc(sizeof(psImage));
+        out->data.V = NULL;
+    }
+
+    out->data.V = psRealloc(out->data.V,sizeof(psPtr)*numRows); // resize row pointer array
+    *(psType*)&out->type = image->type;
+    *(psU32*)&out->numCols = numCols;
+    *(psU32*)&out->numRows = numRows;
+    *(psS32*)&out->row0 = row0;
+    *(psS32*)&out->col0 = col0;
+    out->parent = image;
+    out->children = NULL;
+    out->rawDataBuffer = rawData;
+
+    // set the new psImage's deallocator to the same as the input image
+    psMemSetDeallocator(out,psMemGetDeallocator(image));
+
+    inputColOffset = elementSize * col0;
+    for (psS32 row = 0; row < numRows; row++) {
+        out->data.V[row] = image->data.U8[row0 + row] + inputColOffset;
+    }
+
+    // add output image as a child of the input image.
+    psS32 n = 0;
+    psArray* children = image->children;
+    if (children == NULL) {
+        children = psArrayAlloc(16); // start with a reasonable size for growth
+    } else if (children->nalloc == children->n) { // full?
+        n = children->n;
+        children = psArrayRealloc(children,n*2); // double the array size
+    } else {
+        n = children->n;
+    }
+    children->data[n] = out;
+    children->n = n+1;
+    image->children = children; // push back any change (esp. if children==NULL before)
+
+    return (out);
+}
+
+psImage* psImageSubset(psImage* image,
+                       psRegion region)
+{
+    return imageSubset(NULL,image,region.x0, region.y0,
+                       region.x1, region.y1);
+}
+
+psImage* psImageSubsection(psImage* image,
+                           const char* section)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (section == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(section,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                section);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return imageSubset(NULL,image,col0,row0,col1+1,row1+1);
+}
+
+psImage* psImageTrim(psImage* image,
+                     psRegion region)
+{
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->parent != NULL) {
+        return imageSubset(image,
+                           (psImage*)image->parent,
+                           region.x0+image->col0,
+                           region.y0+image->row0,
+                           region.x1+image->col0,
+                           region.y1+image->row0);
+    }
+
+    int col0 = region.x0;
+    int row0 = region.y0;
+    int col1 = region.x1;
+    int row1 = region.y1;
+
+    if (col1 < 1) {
+        col1 += image->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += image->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > image->numCols ||
+            row1 > image->numRows ||
+            col0 >= col1 ||
+            row0 >= row1 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        psFree(image);
+        return NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psU32 elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+    psU32 numCols = col1-col0;
+    psU32 numRows = row1-row0;
+    psU32 rowSize = elementSize*numCols;
+    psU32 colOffset = elementSize * col0;
+    psU8* imageData = image->rawDataBuffer;
+    for (psS32 row = row0; row < row1; row++) {
+        memmove(imageData,image->data.U8[row] + colOffset,rowSize);
+        imageData += rowSize;
+    }
+
+    *(psU32*)&image->numRows = numRows;
+    *(psU32*)&image->numCols = numCols;
+
+    // XXX: should I really resize the buffers?
+    image->data.V = psRealloc(image->data.V,sizeof(psPtr)*numRows);
+    image->rawDataBuffer = psRealloc(image->rawDataBuffer,rowSize*numRows);
+
+    image->data.V[0] = image->rawDataBuffer;
+    for (psS32 r = 1; r < numRows; r++) {
+        image->data.U8[r] = image->data.U8[r-1] + rowSize;
+    }
+
+    return (image);
+}
+
Index: /trunk/psLib/src/image/psImageStructManip.h
===================================================================
--- /trunk/psLib/src/image/psImageStructManip.h	(revision 3968)
+++ /trunk/psLib/src/image/psImageStructManip.h	(revision 3968)
@@ -0,0 +1,85 @@
+/** @file  psImageStructManip.h
+*
+*  @brief Contains basic image structure manipulation operations, as specified
+*         in the PSLIB SDRS sections "Image Structure Manipulation".
+*
+*  @ingroup Image
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 02:08:21 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGE_STRUCT_MANIP_H
+#define PSIMAGE_STRUCT_MANIP_H
+
+#include "psImage.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Create a subimage of the specified area.
+ *
+ *  Deï¬ne a subimage of the speciï¬ed area of the given image. This function
+ *  must raise an error if the requested subset area lies outside of the
+ *  parent image and return NULL. The argument image is the parent image,
+ *  region.x0, region.y0 specify the starting pixel of the subraster, and
+ *  region.x1,region.y1 specify the extent of the desired subraster. Note
+ *  that the row and column of this âupper right-hand cornerâ are NOT included
+ *  in the region. In the event that x1 or y1 are negative, they shall be
+ *  interpreted as being relative to the size of the parent image in that
+ *  dimension. The entire subraster must be contained within the raster of the
+ *  parent image. Note that the refCounter for the parent should be
+ *  incremented.  This function must be deï¬ned for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
+ *
+ *  @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubset(
+    psImage* image,                    ///< Parent image.
+    psRegion region                    ///< region of subimage
+);
+
+/** Create a subimage of the specified area.
+ *
+ * Uses psLib memory allocation functions to create an image based on a larger
+ * one.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubsection(
+    psImage* image,                    ///< Parent image.
+    const char* section                ///< Subsection in the form '[x1:x2,y1:y2]'
+);
+
+/** Trim an image
+ *
+ *  Trim the specified image in-place, which involves shuffling the pixels
+ *  around in memory.  The pixels in the region [col0:col1,row0:row1] shall consist
+ *  the output image.  The column col1 and row row1 are NOT included in the range.
+ *  In the event that x1 or y1 are non-positive, they shall be interpreted as
+ *  being relative to the size of the parent image in that dimension.
+ *
+ *  If the entire specified subimage is not contained within the parent
+ *  image, an error results and the return value will be NULL.
+ *
+ *  N.B. If the input psImage is a child of another psImage, no pixel data
+ *  will be trimmed, rather it equivalent to calling psImageSubset.  If the input
+ *  psImage is, however, a parent psImage, any children will be obliterated,
+ *  i.e., freed from memory.
+ *
+ *  @return psImage*  trimmed image result
+ */
+psImage* psImageTrim(
+    psImage* image,                    ///< image to trim
+    psRegion region                    ///< trim region
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/imageops/psImageConvolve.c
===================================================================
--- /trunk/psLib/src/imageops/psImageConvolve.c	(revision 3967)
+++ /trunk/psLib/src/imageops/psImageConvolve.c	(revision 3968)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-04-15 00:12:08 $
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -15,5 +15,5 @@
 #include "psImageConvolve.h"
 #include "psImageFFT.h"
-#include "psImageExtraction.h"
+#include "psImageStructManip.h"
 #include "psBinaryOp.h"
 #include "psMemory.h"
@@ -454,7 +454,9 @@
 
         // subset out the padded area now.
-        psImage* complexOutSansPad = psImageSubset(complexOut,
-                                     FOURIER_PADDING,FOURIER_PADDING,
-                                     FOURIER_PADDING+numCols,FOURIER_PADDING+numRows);
+        psImage* complexOutSansPad = psImageSubset(complexOut,(psRegion) {
+                                         FOURIER_PADDING,FOURIER_PADDING,
+                                         FOURIER_PADDING+numCols,FOURIER_PADDING+numRows
+                                     }
+                                                  );
 
         out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
Index: /trunk/psLib/src/imageops/psImageGeomManip.c
===================================================================
--- /trunk/psLib/src/imageops/psImageGeomManip.c	(revision 3968)
+++ /trunk/psLib/src/imageops/psImageGeomManip.c	(revision 3968)
@@ -0,0 +1,773 @@
+/** @file  psImageGeomManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImageGeomManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+#include "psCoord.h"
+
+psImage* psImageRebin(psImage* out,
+                      const psImage* in,
+                      const psImage* restrict mask,
+                      psMaskType maskVal,
+                      psU32 scale,
+                      const psStats* stats)
+{
+    psS32 inRows;
+    psS32 inCols;
+    psS32 outRows;
+    psS32 outCols;
+    psVector* vec;                     // vector to hold the values of a single bin.
+    psVector* maskVec = NULL;          // vector to hold the mask of a single bin.
+    psMaskType* maskData = NULL;
+    psStats* myStats;
+    double statVal;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    vec = psVectorAlloc(scale * scale, in->type.type);
+
+    if (mask != NULL) {
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            psFree(vec);
+            return NULL;
+        }
+        maskVec = psVectorAlloc(scale * scale, PS_TYPE_MASK);
+        maskData = maskVec->data.PS_TYPE_MASK_DATA;
+    }
+
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    // create output image.
+    inRows = in->numRows;
+    inCols = in->numCols;
+    outRows = (inRows + scale - 1) / scale;     // round-up for remainders
+    outCols = (inCols + scale - 1) / scale;     // round-up for remainders
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    #define PS_IMAGE_REBIN_CASE(type) \
+case PS_TYPE_##type: { \
+        ps##type* outRowData; \
+        ps##type* vecData = vec->data.type; \
+        psMaskType* inRowMask = NULL; \
+        for (psS32 row = 0; row < outRows; row++) { \
+            outRowData = out->data.type[row]; \
+            psS32 inCurrentRow = row*scale; \
+            psS32 inNextRow = (row+1)*scale; \
+            for (psS32 col = 0; col < outCols; col++) { \
+                psS32 inCurrentCol = col*scale; \
+                psS32 inNextCol = (col+1)*scale; \
+                psS32 n = 0; \
+                for (psS32 inRow = inCurrentRow; inRow < inNextRow && inRow < inRows; inRow++) { \
+                    ps##type* inRowData = in->data.type[inRow]; \
+                    if (mask != NULL) { \
+                        inRowMask = mask->data.PS_TYPE_MASK_DATA[inRow]; \
+                    } \
+                    for (psS32 inCol = inCurrentCol; inCol < inNextCol && inCol < inCols; inCol++) { \
+                        if (maskData != NULL) { \
+                            maskData[n] = inRowMask[inCol]; \
+                        } \
+                        vecData[n++] = inRowData[inCol]; \
+                    } \
+                } \
+                vec->n = n; \
+                myStats = psVectorStats(myStats,vec,NULL,maskVec,maskVal); \
+                p_psGetStatValue(myStats,&statVal); \
+                outRowData[col] = (ps##type)statVal; \
+            } \
+        } \
+    } \
+    break;
+
+    switch (in->type.type) {
+        //        PS_IMAGE_REBIN_CASE(U8);       Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(U16);
+        PS_IMAGE_REBIN_CASE(U32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(U64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S8);
+        //        PS_IMAGE_REBIN_CASE(S16);      Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(S32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(F32);
+        PS_IMAGE_REBIN_CASE(F64);
+        //        PS_IMAGE_REBIN_CASE(C32);      Not valid since psVectorStats doesn't allow
+        //        PS_IMAGE_REBIN_CASE(C64);      Not valid since psVectorStats doesn't allow
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    psFree(vec);
+    psFree(maskVec);
+    psFree(myStats);
+
+    return out;
+}
+
+psImage* psImageResample(psImage* out,
+                         const psImage* in,
+                         psS32 scale,
+                         psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    float invScale;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows * scale;
+    outCols = in->numCols * scale;
+    invScale = 1.0f / (float)scale;
+
+    #define PSIMAGE_RESAMPLE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        out = psImageRecycle(out,outCols, outRows, PS_TYPE_##TYPE); \
+        for (psS32 row=0;row<outRows;row++) { \
+            ps##TYPE* rowData = out->data.TYPE[row]; \
+            float inRow = (float)row * invScale; \
+            for (psS32 col=0;col<outCols;col++) { \
+                rowData[col] = psImagePixelInterpolate(in,(float)col*invScale,inRow,NULL,0,0,mode); \
+            } \
+        }  \
+        break; \
+    }
+
+    switch (in->type.type) {
+        PSIMAGE_RESAMPLE_CASE(U8)
+        PSIMAGE_RESAMPLE_CASE(U16)
+        PSIMAGE_RESAMPLE_CASE(U32)
+        PSIMAGE_RESAMPLE_CASE(U64)
+        PSIMAGE_RESAMPLE_CASE(S8)
+        PSIMAGE_RESAMPLE_CASE(S16)
+        PSIMAGE_RESAMPLE_CASE(S32)
+        PSIMAGE_RESAMPLE_CASE(S64)
+        PSIMAGE_RESAMPLE_CASE(F32)
+        PSIMAGE_RESAMPLE_CASE(F64)
+        PSIMAGE_RESAMPLE_CASE(C32)
+        PSIMAGE_RESAMPLE_CASE(C64)
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageRoll(psImage* out,
+                     const psImage* in,
+                     psS32 dx,
+                     psS32 dy)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    elementSize = PSELEMTYPE_SIZEOF(in->type.type);
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    // make dx and dy between 0 and outCols or
+    // outRows, respectively
+    dx = dx % outCols;
+    dy = dy % outRows;
+    if (dx < 0) {
+        dx += outCols;
+    }
+    if (dy < 0) {
+        dy += outRows;
+    }
+
+    psS32 segment1Size = elementSize * (outCols - dx);
+    psS32 segment2Size = elementSize * dx;
+
+    for (psS32 row = 0; row < outRows; row++) {
+        psS32 inRowNumber = row + dy;
+
+        if (inRowNumber >= outRows) {
+            inRowNumber -= outRows;
+        }
+        psU8* inRow = in->data.U8[inRowNumber]; // use byte arithmetic for all types
+        psU8* outRow = out->data.U8[row];
+
+        memcpy(outRow, inRow + segment2Size, segment1Size);
+        memcpy(outRow + segment1Size, inRow, segment2Size);
+    }
+
+    return out;
+}
+
+psImage* psImageRotate(psImage* out,
+                       const psImage* in,
+                       float angle,
+                       psC64 unexposedValue,
+                       psImageInterpolateMode mode)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // put the angle in the range of 0...2PI.
+    angle = (float)((double)angle - (2.0*M_PI) * floor(angle / (2.0*M_PI)));
+
+    if (fabsf(angle - M_PI_2) < FLT_EPSILON) {
+        // perform 1/4 rotate counter-clockwise
+        psS32 numRows = in->numCols;
+        psS32 numCols = in->numRows;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_LEFT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[lastCol-col][row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_LEFT_90(U8);
+            PSIMAGE_ROTATE_LEFT_90(U16);
+            PSIMAGE_ROTATE_LEFT_90(U32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(U64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S8);
+            PSIMAGE_ROTATE_LEFT_90(S16);
+            PSIMAGE_ROTATE_LEFT_90(S32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(F32);
+            PSIMAGE_ROTATE_LEFT_90(F64);
+            PSIMAGE_ROTATE_LEFT_90(C32);
+            PSIMAGE_ROTATE_LEFT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - M_PI) < FLT_EPSILON) {
+        // perform 1/2 rotate
+        psS32 numRows = in->numRows;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numCols;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_180_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                ps##TYPE* inRow = in->data.TYPE[lastRow-row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inRow[lastCol - col]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_180_CASE(U8);
+            PSIMAGE_ROTATE_180_CASE(U16);
+            PSIMAGE_ROTATE_180_CASE(U32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(U64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S8);
+            PSIMAGE_ROTATE_180_CASE(S16);
+            PSIMAGE_ROTATE_180_CASE(S32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(F32);
+            PSIMAGE_ROTATE_180_CASE(F64);
+            PSIMAGE_ROTATE_180_CASE(C32);
+            PSIMAGE_ROTATE_180_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - (M_PI+M_PI_2)) < FLT_EPSILON) {
+        // perform 1/4 rotate clockwise
+        psS32 numRows = in->numCols;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numRows;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_RIGHT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[col][lastRow-row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_RIGHT_90(U8);
+            PSIMAGE_ROTATE_RIGHT_90(U16);
+            PSIMAGE_ROTATE_RIGHT_90(U32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(U64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S8);
+            PSIMAGE_ROTATE_RIGHT_90(S16);
+            PSIMAGE_ROTATE_RIGHT_90(S32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(F32);
+            PSIMAGE_ROTATE_RIGHT_90(F64);
+            PSIMAGE_ROTATE_RIGHT_90(C32);
+            PSIMAGE_ROTATE_RIGHT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle) < FLT_EPSILON) {
+        out = psImageCopy(out, in, in->type.type);
+    } else {
+        psElemType type = in->type.type;
+        psS32 numRows = in->numRows;
+        psS32 numCols = in->numCols;
+        float centerX = (float)(numCols) / 2.0f;
+        float centerY = (float)(numRows) / 2.0f;
+        double cosT = cosf(angle);
+        double sinT = sinf(angle);
+
+        // calculate the corners of the rotated image so we know the proper output image size.
+        // x' = x cos(t) + y sin(t); i.e, x' = (x-centerX)*cosT + (y-centerY)*sinT;
+        // y' = y cos(t) - x sin(t); i.e. y' = (y-centerY)*cosT - (x-centerX)*sinT;
+
+        psS32 outCols = ceil(abs(numCols * cosT) + abs(numRows * sinT)) + 1;
+        psS32 outRows = ceil(abs(numCols * sinT) + abs(numRows * cosT)) + 1;
+        float minX = (float)outCols / -2.0f;
+        psS32 intMinY = outRows / -2;
+
+        out = psImageRecycle(out, outCols, outRows, type);
+
+        /* optimized public domain rotation routine by Karl Lager
+         *
+         * float cosT,sinT;
+         * cosT = cos(t);
+         * sinT = sin(t);
+         * for (y = min_y; y <= max_y; y++) {
+         *     x' = min_x * cosT + y * sinT + x1';
+         *     y' = y * cosT - min_x * sinT + y1';
+         *     for (x = min_x; x <= max_x; x++) {
+         *         if (x', y') is in the bounds of the bitmap, get pixel
+         *            (x', y') and plot the pixel to (x, y) on screen.
+         *         x' += cosT;
+         *         y' -= sinT;
+         *     }
+         * }
+         */
+
+        // precalculate some figures that are used within loop
+        float minXTimesCosTPlusCenterX = minX * cosT + centerX;
+        float CenterYMinusminXTimesSinT = centerY - minX * sinT;
+
+        #define PSIMAGE_ROTATE_ARBITRARY_LOOP(TYPE,MODE) { \
+            if (creal(unexposedValue) < PS_MIN_##TYPE || \
+                    creal(unexposedValue) > PS_MAX_##TYPE || \
+                    cimag(unexposedValue) < PS_MIN_##TYPE || \
+                    cimag(unexposedValue) > PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                        "unexposedValue", \
+                        creal(unexposedValue),cimag(unexposedValue), \
+                        PS_TYPE_##TYPE##_NAME,  \
+                        (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+                psFree(out); \
+                out = NULL; \
+                break; \
+            } \
+            float inX; \
+            float inY; \
+            ps##TYPE* outRow; \
+            for (psS32 y = 0; y < outRows; y++) { \
+                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
+                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
+                outRow = out->data.TYPE[y]; \
+                for (psS32 x = 0; x < outCols; x++) { \
+                    outRow[x] = p_psImagePixelInterpolate##MODE##_##TYPE(in,inX,inY,NULL,0,unexposedValue); \
+                    inX += cosT; \
+                    inY -= sinT; \
+                } \
+            } \
+        }
+
+        #define PSIMAGE_ROTATE_ARBITRARY_CASE(MODE) \
+    case PS_INTERPOLATE_##MODE: \
+        switch (type) { \
+        case PS_TYPE_U8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U8,MODE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U16,MODE); \
+            break; \
+        case PS_TYPE_U32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32,MODE); \
+            break; \
+        case PS_TYPE_U64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U64,MODE); \
+            break;  \
+        case PS_TYPE_S8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S8,MODE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S16,MODE); \
+            break; \
+        case PS_TYPE_S32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32,MODE); \
+            break; \
+        case PS_TYPE_S64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S64,MODE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F32,MODE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F64,MODE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C32,MODE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C64,MODE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                        typeStr); \
+                psFree(out); \
+                out = NULL; \
+            } \
+        } \
+        break;
+
+        switch (mode) {
+            PSIMAGE_ROTATE_ARBITRARY_CASE(FLAT);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR_VARIANCE);
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                    mode);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageShift(psImage* out,
+                      const psImage* in,
+                      float dx,
+                      float dy,
+                      psC64 unexposedValue,
+                      psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+    psElemType type;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    type = in->type.type;
+    elementSize = PSELEMTYPE_SIZEOF(type);
+    out = psImageRecycle(out, outCols, outRows, type);
+
+    #define PSIMAGE_SHIFT_CASE(MODE,TYPE) \
+case PS_TYPE_##TYPE: \
+    if (creal(unexposedValue) < PS_MIN_##TYPE || \
+            creal(unexposedValue) > PS_MAX_##TYPE || \
+            cimag(unexposedValue) < PS_MIN_##TYPE || \
+            cimag(unexposedValue) > PS_MAX_##TYPE) { \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                "unexposedValue", \
+                creal(unexposedValue),cimag(unexposedValue), \
+                PS_TYPE_##TYPE##_NAME,  \
+                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    for (psS32 row=0;row<outRows;row++) { \
+        ps##TYPE* outRow = out->data.TYPE[row]; \
+        float y = dy+(float)row; \
+        for (psS32 col=0;col<outCols;col++) { \
+            outRow[col] = p_psImagePixelInterpolate##MODE##_##TYPE( \
+                          in,dx+(float)col,y,NULL,0,unexposedValue); \
+        } \
+    } \
+    break;
+
+    #define PSIMAGE_SHIFT_ARBITRARY_CASE(MODE) \
+case PS_INTERPOLATE_##MODE: \
+    switch (in->type.type) { \
+        PSIMAGE_SHIFT_CASE(MODE,U8);  \
+        PSIMAGE_SHIFT_CASE(MODE,U16); \
+        PSIMAGE_SHIFT_CASE(MODE,U32);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,U64);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S8);  \
+        PSIMAGE_SHIFT_CASE(MODE,S16); \
+        PSIMAGE_SHIFT_CASE(MODE,S32);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S64);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,F32); \
+        PSIMAGE_SHIFT_CASE(MODE,F64); \
+        PSIMAGE_SHIFT_CASE(MODE,C32); \
+        PSIMAGE_SHIFT_CASE(MODE,C64); \
+        \
+    default: { \
+            char* typeStr; \
+            PS_TYPE_NAME(typeStr,type); \
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                    typeStr); \
+            psFree(out); \
+            out = NULL; \
+        } \
+    } \
+    break;
+
+    switch (mode) {
+        PSIMAGE_SHIFT_ARBITRARY_CASE(FLAT);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR_VARIANCE);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        out = NULL;
+    }
+
+    return out;
+}
+
+
+// XXX: implementation is awaiting working psPlaneTransform functions like
+// invert.  Also, the next SDRS should have a different signature.
+psImage* psImageTransform(psImage *output,
+                          psArray** blankPixels,
+                          const psImage *input,
+                          const psImage *inputMask,
+                          int inputMaskVal,
+                          const psPlaneTransform *outToIn,
+                          const psRegion region,
+                          const psPixels* pixels,
+                          psImageInterpolateMode mode,
+                          int exposedValue)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (outToIn == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_TRANSFORM_NULL);
+        return NULL;
+    }
+
+    int row0 = region.y0;
+    int row1 = region.y1;
+    int col0 = region.x0;
+    int col1 = region.x1;
+    if (col1 < 1) {
+        col1 += input->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += input->numRows;
+    }
+
+    int numRows = row1 - row0;
+    int numCols = col1 - col0;
+
+    if (numRows < 1 || numCols < 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "The specified region is invalid.");
+        psFree(output);
+        return NULL;
+    }
+
+    // create the output image.
+    output = psImageRecycle(output, numCols, numRows, input->type.type);
+    *(psS32*)&output->col0 = region.x0;
+    *(psS32*)&output->row0 = region.y0;
+
+
+    // loop through the output image using the domain above and transform
+    // each output pixel to input coordinates and use psImagePixelInterpolate
+    // to determine the pixel value.
+    psPlane outPosition;
+    psPlane* inPosition = NULL;
+    for (int row = 0; row < numRows; row++) {
+        outPosition.y = row+row0;
+        psF32* outputData=output->data.F32[row];
+        for (int col = 0; col < numCols; col++) {
+            outPosition.x = col+col0;
+            // apply the transform to get the position in the input image
+            inPosition = psPlaneTransformApply(inPosition, outToIn, inPosition);
+
+            if (inPosition == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Failed to apply the transform");
+                psFree(output);
+                return NULL;
+            }
+            // interpolate the cooresponding input pixel to get the output pixel value.
+            outputData[col] = (psF32)psImagePixelInterpolate(input,
+                              inPosition->x, inPosition->y,
+                              inputMask, inputMaskVal, exposedValue,
+                              mode);
+
+        }
+    }
+
+    return output;
+}
+
Index: /trunk/psLib/src/imageops/psImageGeomManip.h
===================================================================
--- /trunk/psLib/src/imageops/psImageGeomManip.h	(revision 3968)
+++ /trunk/psLib/src/imageops/psImageGeomManip.h	(revision 3968)
@@ -0,0 +1,157 @@
+/** @file  psImageGeomManip.h
+ *
+ *  @brief Contains basic image geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_GEOM_MANIP_H
+#define PS_IMAGE_GEOM_MANIP_H
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Rebin image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are reduced by a factor of
+ *  1/scale.  The scale, always a positive number, is equal in each dimension and
+ *  specified the number of pixels used to define a new pixel in the output image.
+ *  The output image is generated from all input image pixels. This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage    new image formed by rebinning input image.
+ */
+psImage* psImageRebin(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    const psImage* mask,               ///< mask for input image.  If NULL, no masking is done.
+    psMaskType maskVal,                ///< the bits to check in mask.
+    psU32 scale,                       ///< the scale to rebin for each dimension
+    const psStats* stats
+    ///< the statistic to perform when rebinning.  Only one method should be set.
+);
+
+/** Resample image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are increased by a
+ *  factor of scale. The scale, always a positive number, is equal in each
+ *  dimension. The output image is generated from all input image pixels.
+ *  Each pixel in the output image is derived by interpolating between
+ *  neighboring pixels using the specified interpolation method (mode).
+ *
+ *  @return psImage*    resampled image result
+ */
+psImage* psImageResample(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 scale,                       ///< resample scaling factor
+    psImageInterpolateMode mode        ///< the interpolation mode used in resampling
+);
+
+/** Rotate the input image by given angle, specified in degrees.
+ *
+ *  The output image must contain all of the pixels from the input image in
+ *  their new frame. Pixels in the output image which do not map to input
+ *  pixels should be set to exposed. The center of rotation is always the
+ *  center pixel of the image. The rotation is specified in the sense that a
+ *  positive angle is an anti-clockwise rotation. This function must be
+ *  defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64,
+ *  psC32, psC64.
+ *
+ *  @return psImage*     the rotated image result.
+ */
+psImage* psImageRotate(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float angle,                       ///< the rotation angle in radians.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode used
+);
+
+/** Shift image by an arbitrary number of pixels (dx,dy) in either direction.
+ *
+ *  If the shift values are fractional, the output pixel values should
+ *  interpolate between the input pixel values. The output image has the same
+ *  dimensions as the input image. Pixels which fall off the edge of the
+ *  output image are lost. Newly exposed pixels are set to the value given by
+ *  exposed. This function must be defined for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
+ *
+ *  @return psImage*     the shifted image result.
+ */
+psImage* psImageShift(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float dx,                          ///< the shift in x direction.
+    float dy,                          ///< the shift in y direction.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode to use
+);
+
+/** Roll image by an integer number of pixels in either direction.
+ *
+ *  The output image is the same dimensions as the input image.  Edge pixels
+ *  wrap to the other side (no values are lost).  This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage* the rolled version of the input image.
+ */
+psImage* psImageRoll(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 dx,                          ///< number of pixels to roll in the x-dimension
+    psS32 dy                           ///< number of pixels to roll in the y-dimension
+);
+
+/** Transform the input image according the supplied transformation.
+ *
+ *  Transform the input image according the supplied transformation. The size
+ *  of the transformed image is deï¬ned by the supplied output image, if
+ *  non-NULL, or the region otherwise (size region.x1 - region.x0 by region.y1
+ *  region.y0, with out->x0 = region.x0 and out->y0 = region.y0). If the
+ *  inputMask is non-NULL, those pixels in the inputMask matching inputMaskVal
+ *  are to be ignored in the transformation. The inputMask must be of type
+ *  psU8, and of the same size as the input, otherwise the function shall
+ *  generate an error and return NULL. The transformation outToIn speciï¬es the
+ *  coordinates in the input image of a pixel in the output image â note that
+ *  this is the reverse of what might be naively expected, but it is what is
+ *  required in order to use psImagePixelInterpolate. If the pixels array is
+ *  non-NULL, it shall consist of psPixelCoords, and only those pixels in the
+ *  output image shall be transformed; otherwise, the entire image is
+ *  generated. The interpolation is performed using the speciï¬ed interpolation
+ *  mode. Where a pixel in the output image does not correspond to a pixel in
+ *  the input image (or all appropriate pixels in the input image are
+ *  masked), the value shall be set to exposed, and the pixel added to the
+ *  appropriate list of pixels (psPixels) in the array of blankPixels for
+ *  return to the user. This function must be capable of handling the following
+ *  types for the input (with corresponding types for the output): psF32, psF64.
+ 
+ *
+ *  @return psImage*    The transformed image.
+ */
+psImage* psImageTransform(
+    psImage *output,                   ///< psImage to recycle, or NULL
+    psArray** blankPixels,             ///<
+    const psImage *input,              ///< psImage to apply transform to
+    const psImage *inputMask,          ///< if not NULL, mask of input psImage
+    int inputMaskVal,                  ///< masking value for inputMask
+    const psPlaneTransform *outToIn,   ///< the transform to apply
+    const psRegion region,             ///<
+    const psPixels* pixels,            ///<
+    psImageInterpolateMode mode,       ///<
+    int exposedValue                   ///<
+);
+
+#endif
Index: /trunk/psLib/src/imageops/psImagePixelExtract.c
===================================================================
--- /trunk/psLib/src/imageops/psImagePixelExtract.c	(revision 3968)
+++ /trunk/psLib/src/imageops/psImagePixelExtract.c	(revision 3968)
@@ -0,0 +1,597 @@
+/** @file  psImagePixelExtract.c
+ *
+ *  @brief Contains basic image extraction operations, as specified in the
+ *         PSLIB SDRS sections "Image Pixel Extractions".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImagePixelExtract.h"
+#include "psError.h"
+
+#include "psImageErrors.h"
+
+psVector* psImageSlice(psVector* out,
+                       psVector* slicePositions,
+                       const psImage* restrict in,
+                       const psImage* restrict mask,
+                       psU32 maskVal,
+                       psS32 col0,
+                       psS32 row0,
+                       psS32 col1,
+                       psS32 row1,
+                       psImageCutDirection direction,
+                       const psStats* stats)
+{
+    double statVal;
+    psStats* myStats;
+    psElemType type;
+    psS32 inRows;
+    psS32 inCols;
+    psS32 delta = 1;
+    psF64* outData;
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 += in->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += in->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > in->numCols ||
+            row1 > in->numRows ||
+            col0 >= col1 ||
+            row0 >= row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1, row0, row1,
+                in->numCols, in->numRows);
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    inRows = in->numRows;
+    inCols = in->numCols;
+
+    if (direction == PS_CUT_X_NEG || direction == PS_CUT_Y_NEG) {
+        delta = -1;
+    }
+
+    if (mask != NULL) {
+        if (inRows != mask->numRows || inCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    inCols, inRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,stats->options);
+        psFree(out);
+        return NULL;
+    }
+    // since stats input is const, I need to
+    // create a 'scratch' stats struct
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    psS32 numCols = col1-col0;
+    psS32 numRows = row1-row0;
+
+    if (direction == PS_CUT_X_POS || direction == PS_CUT_X_NEG) {
+        psVector* imgVec = psVectorAlloc(numRows, type);
+        psVector* maskVec = NULL;
+        psMaskType* maskData = NULL;
+        psU32* outPosition = NULL;
+
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats is
+        // psStats.
+        out = psVectorRecycle(out, numCols, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numCols, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numCols - 1;
+            if (outPosition != NULL) {
+                outPosition += numCols - 1;
+            }
+        }
+
+        if (mask != NULL) {
+            maskVec = psVectorAlloc(numRows, mask->type.type);
+        }
+        #define PSIMAGE_CUT_VERTICAL(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            psMaskType* maskVecData = NULL; \
+            for (psS32 c=col0;c<col1;c++) { \
+                ps##TYPE *imgData = in->data.TYPE[row0] + c; \
+                ps##TYPE *imgVecData = imgVec->data.TYPE; \
+                if (maskVec != NULL) { \
+                    maskVecData = maskVec->data.U8; \
+                    maskData = (psMaskType* )(mask->data.U8[row0]) + c; \
+                } \
+                for (psS32 r=row0;r<row1;r++) { \
+                    *(imgVecData++) = *imgData; \
+                    imgData += inCols; \
+                    if (maskVecData != NULL) { \
+                        *(maskVecData++) = *maskData; \
+                        maskData += inCols; \
+                    } \
+                } \
+                myStats = psVectorStats(myStats,imgVec,NULL,maskVec,maskVal); \
+                (void)p_psGetStatValue(myStats,&statVal); \
+                *outData = statVal; \
+                if (outPosition != NULL) { \
+                    *outPosition = c; \
+                    outPosition += delta; \
+                } \
+                outData += delta; \
+            } \
+            break; \
+        }
+
+        switch (type) {
+            PSIMAGE_CUT_VERTICAL(U8);  // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U16);
+            PSIMAGE_CUT_VERTICAL(U32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S8);
+            PSIMAGE_CUT_VERTICAL(S16); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(F32);
+            PSIMAGE_CUT_VERTICAL(F64);
+            PSIMAGE_CUT_VERTICAL(C32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(C64); // Not a requirement
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                out = NULL;
+            }
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else if (direction == PS_CUT_Y_POS || direction == PS_CUT_Y_NEG) {
+        // Cut in Y direction
+        psVector* imgVec = NULL;
+        psVector* maskVec = NULL;
+        psS32 elementSize = PSELEMTYPE_SIZEOF(type);
+        psU32* outPosition = NULL;
+
+        // fill in psVector to fake out the statistics functions.
+        imgVec = psAlloc(sizeof(psVector));
+        imgVec->type = in->type;
+        imgVec->n = *(int*)&imgVec->nalloc = numCols;
+        if (mask != NULL) {
+            maskVec = psAlloc(sizeof(psVector));
+            maskVec->type = mask->type;
+            maskVec->n = *(int*)&maskVec->nalloc = numCols;
+        }
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats in
+        // psStats.
+        out = psVectorRecycle(out, numRows, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numRows, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numRows-1;
+            if (outPosition != NULL) {
+                outPosition += numRows-1;
+            }
+        }
+
+        for (psS32 r = row0; r < row1; r++) {
+            // point the vector struct to the
+            // data to calculate the stats
+            imgVec->data.U8 = (psPtr )(in->data.U8[r] + col0 * elementSize);
+            if (maskVec != NULL) {
+                maskVec->data.U8 = (psPtr )(mask->data.U8[r] + col0 * sizeof(psMaskType));
+            }
+            myStats = psVectorStats(myStats, imgVec, NULL, maskVec, maskVal);
+            (void)p_psGetStatValue(myStats, &statVal);  // we know it works cause we tested it above
+            *outData = statVal;
+            if (outPosition != NULL) {
+                *outPosition = r;
+                outPosition += delta;
+
+            }
+            outData += delta;
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else { // don't know what the direction flag is
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SLICE_DIRECTION_INVALID,
+                direction);
+        psFree(out);
+        out = NULL;
+    }
+
+    psFree(myStats);
+
+    return out;
+}
+
+psVector* psImageCut(psVector* out,
+                     psVector* cutCols,
+                     psVector* cutRows,
+                     const psImage* in,
+                     const psImage* restrict mask,
+                     psU32 maskVal,
+                     float startCol,
+                     float startRow,
+                     float endCol,
+                     float endRow,
+                     psU32 nSamples,
+                     psImageInterpolateMode mode)
+{
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (nSamples < 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_nSamples_TOOSMALL,
+                nSamples);
+        psFree(out);
+        return NULL;
+    }
+
+    if (startCol < 0 || startCol >= numCols ||
+            startRow < 0 || startRow >= numRows ||
+            endCol < 0 || endCol >= numCols ||
+            endRow < 0 || endRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_LINE_NOT_IN_IMAGE,
+                startCol,startRow,endCol,endRow,
+                numCols-1,numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols-1, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    //resize the vectors for the coordinate output
+    psF32* cutColsData = NULL;
+    psF32* cutRowsData = NULL;
+    if (cutCols != NULL) {
+        cutCols = psVectorRecycle(cutCols, nSamples, PS_TYPE_F32);
+        cutColsData = cutCols->data.F32;
+    }
+    if (cutRows != NULL) {
+        cutRows = psVectorRecycle(cutRows, nSamples, PS_TYPE_F32);
+        cutRowsData = cutRows->data.F32;
+    }
+
+    out = psVectorRecycle(out, nSamples, in->type.type);
+
+    float dX = (endCol - startCol) / (float)(nSamples-1);
+    float dY = (endRow - startRow) / (float)(nSamples-1);
+
+    #define LINEAR_CUT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE* outData = out->data.TYPE; \
+        for (psS32 i = 0; i < nSamples; i++) { \
+            float x = startCol + (float)i*dX; \
+            float y = startRow + (float)i*dY; \
+            /* store off the location of the sample. */ \
+            if (cutColsData != NULL) { \
+                cutColsData[i] = x; \
+            } \
+            if (cutRowsData != NULL) { \
+                cutRowsData[i] = y; \
+            } \
+            outData[i] = psImagePixelInterpolate(in,x,y,mask,maskVal,0,mode); \
+        } \
+    } \
+    break;
+
+
+    switch (in->type.type) {
+        LINEAR_CUT_CASE(U8);
+        LINEAR_CUT_CASE(U16);
+        LINEAR_CUT_CASE(U32);
+        LINEAR_CUT_CASE(U64);
+        LINEAR_CUT_CASE(S8);
+        LINEAR_CUT_CASE(S16);
+        LINEAR_CUT_CASE(S32);
+        LINEAR_CUT_CASE(S64);
+        LINEAR_CUT_CASE(F32);
+        LINEAR_CUT_CASE(F64);
+        LINEAR_CUT_CASE(C32);
+        LINEAR_CUT_CASE(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psVector* psImageRadialCut(psVector* out,
+                           const psImage* in,
+                           const psImage* restrict mask,
+                           psU32 maskVal,
+                           float centerCol,
+                           float centerRow,
+                           const psVector* radii,
+                           const psStats* stats)
+{
+    double statVal;
+
+    /* check the parameters */
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (centerCol < 0 || centerCol >= numCols ||
+            centerRow < 0 || centerRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_CENTER_NOT_IN_IMAGE,
+                centerCol, centerRow,
+                numCols-1, numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii->n < 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_TOOSMALL,
+                radii->n);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    /* completed checking the parameters */
+
+    // size the output vector to proper size.
+    psS32 numOut = radii->n - 1;
+    out = psVectorRecycle(out, numOut, PS_TYPE_F64);
+    psF64* outData = out->data.F64;
+
+    psVector* rSqVec = psVectorCopy(NULL, radii, PS_TYPE_F32);
+    psF32* rSq = rSqVec->data.F32;
+
+    psS32 startRow = centerRow - rSq[numOut];
+    psS32 endRow = centerRow + rSq[numOut];
+    psS32 startCol = centerCol - rSq[numOut];
+    psS32 endCol = centerCol + rSq[numOut];
+
+    if (startRow < 0) {
+        startRow = 0;
+    }
+
+    if (startCol < 0) {
+        startCol = 0;
+    }
+
+    if (endRow >= numRows) {
+        endRow = numRows - 1;
+    }
+
+    if (endCol >= numCols) {
+        endCol = numCols - 1;
+    }
+
+    // Square the radii data
+    for (psS32 d = 0; d <= numOut; d++) {
+        rSq[d] *= rSq[d];
+    }
+
+    // create temporary vectors for the data binning step
+    psVector** buffer = psAlloc(sizeof(psVector*)*numOut);
+    psVector** bufferMask = psAlloc(sizeof(psVector*)*numOut);
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        // n.b. alloc enough for the data by making the vectors slightly larger
+        // than the area of the region of interest.
+        buffer[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                    in->type.type);
+        buffer[lcv]->n = 0;
+
+        bufferMask[lcv] = NULL;
+        if (mask != NULL) {
+            bufferMask[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                            PS_TYPE_MASK);
+            bufferMask[lcv]->n = 0;
+        }
+    }
+
+    float dX;
+    float dY;
+    float dist;
+    for (psS32 row=startRow; row <= endRow; row++) {
+        psF32* inRow = in->data.F32[row];
+        psMaskType* maskRow = NULL;
+        if (mask != NULL) {
+            maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        }
+        for (psS32 col=startCol; col <= endCol; col++) {
+            dX = centerCol - (float)col - 0.5f;
+            dY = centerRow - (float)row - 0.5f;
+            dist = dX*dX+dY*dY;
+            for (psS32 r = 0; r < numOut; r++) {
+                if (rSq[r] < dist && dist < rSq[r+1]) {
+                    psS32 n = buffer[r]->n;
+                    if (n == buffer[r]->nalloc) { // in case buffers already full, expand
+                        buffer[r] = psVectorRealloc(buffer[r], n*2);
+                        if (bufferMask[r] != NULL) {
+                            bufferMask[r] = psVectorRealloc(bufferMask[r], n*2);
+                        }
+                    }
+
+                    buffer[r]->data.F32[n] = inRow[col];
+                    buffer[r]->n = n+1;
+
+                    if (maskRow != NULL) {
+                        bufferMask[r]->data.PS_TYPE_MASK_DATA[n] = maskRow[col];
+                        bufferMask[r]->n = n+1;
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    psStats* myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    for (psS32 r = 0; r < numOut; r++) {
+        myStats = psVectorStats(myStats,buffer[r], NULL, bufferMask[r],maskVal);
+        (void)p_psGetStatValue(myStats,&statVal);
+        outData[r] = statVal;
+    }
+
+    psFree(myStats);
+
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        psFree(buffer[lcv]);
+        psFree(bufferMask[lcv]);
+    }
+    psFree(buffer);
+    psFree(bufferMask);
+    psFree(rSqVec);
+    return out;
+}
Index: /trunk/psLib/src/imageops/psImagePixelExtract.h
===================================================================
--- /trunk/psLib/src/imageops/psImagePixelExtract.h	(revision 3968)
+++ /trunk/psLib/src/imageops/psImagePixelExtract.h	(revision 3968)
@@ -0,0 +1,132 @@
+/** @file  psImagePixelExtract.h
+*
+*  @brief Contains basic image extraction operations, as specified in the
+*         PSLIB SDRS sections "Image Pixel Extractions".
+*
+*  @ingroup Image
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 02:08:21 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGE_PIXEL_EXTRACT_H
+#define PSIMAGE_PIXEL_EXTRACT_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psStats.h"
+
+/// @addtogroup Image
+/// @{
+
+/* Cut direction flag.  Used with psImageCut function.
+ */
+typedef enum {
+    PS_CUT_X_POS,                      ///< Cut in the x dimension from left to right
+    PS_CUT_X_NEG,                      ///< Cut in the x dimension from rigth to left
+    PS_CUT_Y_POS,                      ///< Cut in the y dimension from bottom up
+    PS_CUT_Y_NEG,                      ///< Cut in the y dimension from top down.
+} psImageCutDirection;
+
+/** Extract pixels from rectlinear region to a vector (array of floats).
+ *
+ *  The output vector contains either col1-col0 or row1-row0 elements, based
+ *  on the value of the direction: e.g., if direction is PS_CUT_X_POS, there
+ *  are col1-col0 elements. The region to be  sliced  is defined by the
+ *  lower-left corner, (col0,row0), and the upper-right corner, (col1,row1).
+ *  Note that the row and column of the  upper right-hand corner  are NOT
+ *  included in the region. In the event that col1 or row1 are negative, they
+ *  shall be interpreted as being relative to the size of the parent image in
+ *  that dimension. The input region is collapsed in the direction perpendicular
+ *  to that specified by direction, and each element of the output vectors is
+ *  derived from the statistics of the pixels at that direction coordinate. The
+ *  statistic used to derive the output vector value is specified by stats.
+ *  If mask is non-NULL, pixels for which the corresponding mask pixel
+ *  matches maskVal are excluded from operations. If coords is not NULL, the
+ *  calculated coordinates along the slice are returned in this vector. Only
+ *  one of the statistics choices may be specified, otherwise the function
+ *  must return an error.
+ *
+ *  This function is defined for the following types: psS8, psU16, psF32, psF64.
+ *
+ * @return psVector    the resulting vector
+ */
+psVector* psImageSlice(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* slicePositions,
+    ///< If not NULL, it is populated with the coordinate in the slice dimension
+    ///< coorsponding to the output vector's value of the same position in the
+    ///< vector.  This vector maybe resized and retyped as appropriate.
+    const psImage* input,              ///< the input image in which to perform the slice
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    psS32 col0,                        ///< the leftmost column of the slice region
+    psS32 row0,                        ///< the bottommost row of the slice region
+    psS32 col1,                        ///< exclusive end column of the slice region
+    psS32 row1,                        ///< exclusive end row of the slice region
+    psImageCutDirection direction,     ///< the slice dimension and direction
+    const psStats* stats               ///< the statistic to perform in slice operation
+);
+
+/** Extract pixels from an image along a line to a vector (array of floats).
+ *
+ *  The vector (xs,ys) - (xe,ye) forms the basis of the output vector. Pixels
+ *  are considered in a rectangular region of width dw about this vector. The
+ *  input region is collapsed in the perpendicular direction, and each element
+ *  of the output vector represents pixel-sized boxes, where the value is
+ *  derived from the statistics of the pixels interpolated along the
+ *  perpendicular direction. The specific algorithm which must be used is
+ *  described in the PSLib ADD (PSDC-430-006). The statistic used to derive
+ *  the output vector value is specified by stats. Only one of the statistics
+ *  choices may be specified, otherwise the function must return an error.
+ *  This function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* cutCols,                 ///< if not NULL, the calculated column values along the slice (output)
+    psVector* cutRows,                 ///< if not NULL, the calculated row values along the slice (output)
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float startCol,                    ///< the column of the start of the cut line
+    float startRow,                    ///< the row of the start of the cut line
+    float endCol,                      ///< the column of the end of the cut line
+    float endRow,                      ///< the row of the end of the cut line
+    psU32 nSamples,                    ///< the number of samples along the cut
+    psImageInterpolateMode mode        ///< the interpolation method to use
+);
+
+/** Extract radial region data to a vector. A vector is constructed where each
+ *  vector elements is derived from the statistics of the pixels which land
+ *  within one of a sequence of radii. The radii are centered on the image
+ *  pixel coordinate x,y, and are defined by the sequence of values in the
+ *  vector radii. The specific algorithm which must be used is described in
+ *  the PSLib ADD (PSDC-430-006). The statistic used to derive the output
+ *  vector value is specified by stats. Only one of the statistics choices
+ *  may be specified, otherwise the function must return an error. This
+ *  function must be defined for the following types: psS8, psU16, psF32,
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageRadialCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float centerCol,                   ///< the column of the center of the cut circle
+    float centerRow,                   ///< the row of the center of the cut circle
+    const psVector* radii,             ///< the radii of the cut circle
+    const psStats* stats               ///< the statistic to perform in operation
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/imageops/psImagePixelManip.c
===================================================================
--- /trunk/psLib/src/imageops/psImagePixelManip.c	(revision 3968)
+++ /trunk/psLib/src/imageops/psImagePixelManip.c	(revision 3968)
@@ -0,0 +1,397 @@
+/** @file  psImagePixelManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImagePixelManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+#include "psCoord.h"
+
+psS32 psImageClip(psImage* input,
+                  psF64 min,
+                  psF64 vmin,
+                  psF64 max,
+                  psF64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+
+    if (max < min) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN,
+                (double)min,(double)max);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipCase(type) \
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if ((psF64)inputRow[col] < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if ((psF64)inputRow[col] > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        #define psImageClipCaseComplex(type,absfcn)\
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if (absfcn(inputRow[col]) < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if (absfcn(inputRow[col]) > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipCase(S8)
+        psImageClipCase(S16)
+        psImageClipCase(S32)            // Not a requirement
+        psImageClipCase(S64)            // Not a requirement
+        psImageClipCase(U8)
+        psImageClipCase(U16)
+        psImageClipCase(U32)            // Not a requirement
+        psImageClipCase(U64)            // Not a requirement
+        psImageClipCase(F32)
+        psImageClipCase(F64)
+        psImageClipCaseComplex(C32, cabsf)
+        psImageClipCaseComplex(C64, cabs)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageClipNaN(psImage* input,
+                     psF64 value)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipNaNCase(type) \
+    case PS_TYPE_##type: \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if (! isfinite(inputRow[col])) { \
+                    inputRow[col] = (ps##type)value; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipNaNCase(F32)
+        psImageClipNaNCase(F64)
+        psImageClipNaNCase(C32)
+        psImageClipNaNCase(C64)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageOverlaySection(psImage* image,
+                            const psImage* overlay,
+                            psS32 col0,
+                            psS32 row0,
+                            const char *op)
+{
+    psU32 imageNumRows;
+    psU32 imageNumCols;
+    psU32 overlayNumRows;
+    psU32 overlayNumCols;
+    psU32 imageRowLimit;
+    psU32 imageColLimit;
+    psElemType type;
+    psU32 pixelsOverlaid = 0;
+
+    if (image == NULL || overlay == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return pixelsOverlaid;
+    }
+
+    if (op == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_OPERATION_NULL);
+        return pixelsOverlaid;
+    }
+
+    type = image->type.type;
+
+    if (type != overlay->type.type) {
+        char* typeStr;
+        char* typeStrOverlay;
+        PS_TYPE_NAME(typeStr,type);
+        PS_TYPE_NAME(typeStrOverlay,overlay->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageManip_OVERLAY_TYPE_MISMATCH,
+                typeStrOverlay, typeStr);
+        return pixelsOverlaid;
+    }
+
+    imageNumRows = image->numRows;
+    imageNumCols = image->numCols;
+    overlayNumRows = overlay->numRows;
+    overlayNumCols = overlay->numCols;
+    imageRowLimit = row0 + overlayNumRows;
+    imageColLimit = col0 + overlayNumCols;
+
+    /* check to see if overlay is within the input image */
+    if ( row0 < 0 ||
+            col0 < 0 ||
+            imageRowLimit > imageNumRows ||
+            imageColLimit > imageNumCols) {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, imageColLimit, row0, imageRowLimit,
+                imageNumCols, imageNumRows);
+        return pixelsOverlaid;
+    }
+
+
+    #define psImageOverlayLoop(DATATYPE,OP) { \
+        for (int row=row0;row<imageRowLimit;row++) { \
+            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
+            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-row0]; \
+            for (int col=col0;col<imageColLimit;col++) { \
+                imageRow[col] OP overlayRow[col-col0]; \
+            } \
+        } \
+        pixelsOverlaid += (imageRowLimit - row0) * (imageColLimit - col0); \
+    }
+
+    #define psImageOverlayCase(DATATYPE) \
+case PS_TYPE_##DATATYPE: \
+    switch (*op) { \
+    case '+': \
+        psImageOverlayLoop(DATATYPE,+=); \
+        break; \
+    case '-': \
+        psImageOverlayLoop(DATATYPE,-=); \
+        break; \
+    case '*': \
+        psImageOverlayLoop(DATATYPE,*=); \
+        break; \
+    case '/': \
+        psImageOverlayLoop(DATATYPE,/=); \
+        break; \
+    case '=': \
+        psImageOverlayLoop(DATATYPE,=); \
+        break; \
+    default: \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_OVERLAY_OPERATOR_INVALID, \
+                op); \
+        return pixelsOverlaid; \
+    } \
+    break;
+
+    switch (type) {
+        psImageOverlayCase(U8);
+        psImageOverlayCase(U16);
+        psImageOverlayCase(U32);       // Not a requirement
+        psImageOverlayCase(U64);       // Not a requirement
+        psImageOverlayCase(S8);
+        psImageOverlayCase(S16);
+        psImageOverlayCase(S32);       // Not a requirement
+        psImageOverlayCase(S64);       // Not a requirement
+        psImageOverlayCase(F32);
+        psImageOverlayCase(F64);
+        psImageOverlayCase(C32);
+        psImageOverlayCase(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            return pixelsOverlaid;
+        }
+    }
+
+    return pixelsOverlaid;
+}
+
+psS32 psImageClipComplexRegion(psImage* input,
+                               psC64 min,
+                               psC64 vmin,
+                               psC64 max,
+                               psC64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+    psF64 realMin = creal(min);
+    psF64 imagMin = cimag(min);
+    psF64 realMax = creal(max);
+    psF64 imagMax = cimag(max);
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return 0;
+    }
+
+    if (realMax < realMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_REAL,
+                (double)realMin, (double)realMax);
+        return 0;
+    }
+    if (imagMax < imagMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_IMAG,
+                (double)imagMin, (double)imagMax);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    #define psImageClipComplexRegionCase(type,realfcn,imagfcn) \
+case PS_TYPE_##type: { \
+        if (realfcn(vmin) < PS_MIN_##type || imagfcn(vmin) < PS_MIN_##type || \
+                realfcn(vmin) > PS_MAX_##type || imagfcn(vmin) > PS_MAX_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmin", creal(vmin), cimag(vmin), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        if (realfcn(vmax) > PS_MAX_##type || imagfcn(vmax) > PS_MAX_##type || \
+                realfcn(vmax) < PS_MIN_##type || imagfcn(vmax) < PS_MIN_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmax", creal(vmax), cimag(vmax), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if ( (realfcn(inputRow[col]) > realMax) || (imagfcn(inputRow[col]) > imagMax) ) { \
+                    inputRow[col] = (ps##type)vmax; \
+                    numClipped++; \
+                } else if ( (realfcn(inputRow[col]) < realMin) || (imagfcn(inputRow[col]) < imagMin) ){ \
+                    inputRow[col] = (ps##type)vmin; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+    } \
+    break;
+
+    switch (input->type.type) {
+
+        psImageClipComplexRegionCase(C32, crealf, cimagf)
+        psImageClipComplexRegionCase(C64, creal, cimag)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
Index: /trunk/psLib/src/imageops/psImagePixelManip.h
===================================================================
--- /trunk/psLib/src/imageops/psImagePixelManip.h	(revision 3968)
+++ /trunk/psLib/src/imageops/psImagePixelManip.h	(revision 3968)
@@ -0,0 +1,90 @@
+/** @file  psImagePixelManip.h
+ *
+ *  @brief Contains basic image pixel manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations"
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_PIXEL_MANIP_H
+#define PS_IMAGE_PIXEL_MANIP_H
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Clip image values outside of range to given values
+ *
+ *  All pixels with values less than min are set to the value vmin.  all pixels
+ *  with values greater than max are set to the value vmax. This function is
+ *  defined for psU8, psU16, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClip(
+    psImage* input,                    ///< the image to clip
+    psF64 min,                         ///< the minimum image value allowed
+    psF64 vmin,                        ///< the value pixels < min are set to
+    psF64 max,                         ///< the maximum image value allowed
+    psF64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip image values outside of a specified complex region
+ *
+ *  All pixels outside of the rectangular region in complex space formed by
+ *  the min and max input parameters are set to the value vmax (if either
+ *  the real or imaginary portion exceeds the respective max values), or vmin.
+ *  This function is defined for psC32, and psC64 imagery only.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipComplexRegion(
+    psImage* input,                    ///< the image to clip
+    psC64 min,                         ///< the minimum image value allowed
+    psC64 vmin,                        ///< the value pixels < min are set to
+    psC64 max,                         ///< the maximum image value allowed
+    psC64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip NaN image pixels to given value.
+ *
+ *  Pixels with NaN, +Inf, or -Inf values are set to the specified value. This
+ *  function is defined for psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipNaN(
+    psImage* input,                    ///< the image to clip
+    psF64 value                        ///< the value to set all NaN/Inf values to
+);
+
+/** Overlay subregion of image with another image
+ *
+ *  Replace the pixels in the image which correspond to the pixels in OVERLAY
+ *  with values derived from the IMAGE and OVERLAY based on the given operator
+ *  OP.  Valid operators are "=" (set image value to OVERLAY value), "+" (add
+ *  OVERLAY value to image value), "-" (subtract OVERLAY from image), "*"
+ *  (multiply OVERLAY times image), "/" (divide image by OVERLAY).  This
+ *  function is defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32         0 if success, non-zero if failed.
+ */
+psS32 psImageOverlaySection(
+    psImage* image,                    ///< target image
+    const psImage* overlay,            ///< the overlay image
+    psS32 col0,                        ///< the column to start overlay
+    psS32 row0,                        ///< the row to start overlay
+    const char *op                     ///< the operation to perform for overlay
+);
+
+#endif
Index: /trunk/psLib/src/imageops/psImageStructManip.c
===================================================================
--- /trunk/psLib/src/imageops/psImageStructManip.c	(revision 3968)
+++ /trunk/psLib/src/imageops/psImageStructManip.c	(revision 3968)
@@ -0,0 +1,242 @@
+/** @file  psImageStructManip.c
+ *
+ *  @brief Contains basic image structure manipulations, as specified in the
+ *         PSLIB SDRS sections "Image Structure Manipulation".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 02:08:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImageStructManip.h"
+#include "psError.h"
+
+#include "psImageErrors.h"
+
+static psImage* imageSubset(psImage* out,
+                            psImage* image,
+                            psS32 col0,
+                            psS32 row0,
+                            psS32 col1,
+                            psS32 row1)
+{
+    psU32 elementSize;          // size of image element in bytes
+    psU32 inputColOffset;       // offset in bytes to first subset pixel in input row
+
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 = image->numCols + col1;
+    }
+    if (row1 < 1) {
+        row1 = image->numRows + row1;
+    }
+
+    if (    col1 <= col0 ||
+            row1 <= row0 ||
+            col0 >= image->numCols ||
+            row0 >= image->numRows ||
+            col1 > image->numCols ||
+            row1 > image->numRows ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        return NULL;
+    }
+    psS32 numRows = row1-row0;
+    psS32 numCols = col1-col0;
+
+    elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+
+    if (image->parent != NULL) { // if this is a child, we need to start working with parent.
+        col0 += image->col0;
+        col1 += image->col0;
+        row0 += image->row0;
+        row1 += image->row0;
+        image = (psImage*)image->parent;
+    }
+
+    // increment the raw data buffer before freeing anything in the 'out'
+    psPtr rawData = psMemIncrRefCounter(image->rawDataBuffer);
+
+    if (out != NULL) {
+        // if a child, need to orphan (disassociate from parent) first
+        if (out->parent != NULL) {
+            psArrayRemove(out->parent->children,out); // remove from parent's knowledge
+            out->parent = NULL; // break link to parent
+        }
+
+        psFree(out->rawDataBuffer); // free the previous data reference
+    } else {
+        out = psAlloc(sizeof(psImage));
+        out->data.V = NULL;
+    }
+
+    out->data.V = psRealloc(out->data.V,sizeof(psPtr)*numRows); // resize row pointer array
+    *(psType*)&out->type = image->type;
+    *(psU32*)&out->numCols = numCols;
+    *(psU32*)&out->numRows = numRows;
+    *(psS32*)&out->row0 = row0;
+    *(psS32*)&out->col0 = col0;
+    out->parent = image;
+    out->children = NULL;
+    out->rawDataBuffer = rawData;
+
+    // set the new psImage's deallocator to the same as the input image
+    psMemSetDeallocator(out,psMemGetDeallocator(image));
+
+    inputColOffset = elementSize * col0;
+    for (psS32 row = 0; row < numRows; row++) {
+        out->data.V[row] = image->data.U8[row0 + row] + inputColOffset;
+    }
+
+    // add output image as a child of the input image.
+    psS32 n = 0;
+    psArray* children = image->children;
+    if (children == NULL) {
+        children = psArrayAlloc(16); // start with a reasonable size for growth
+    } else if (children->nalloc == children->n) { // full?
+        n = children->n;
+        children = psArrayRealloc(children,n*2); // double the array size
+    } else {
+        n = children->n;
+    }
+    children->data[n] = out;
+    children->n = n+1;
+    image->children = children; // push back any change (esp. if children==NULL before)
+
+    return (out);
+}
+
+psImage* psImageSubset(psImage* image,
+                       psRegion region)
+{
+    return imageSubset(NULL,image,region.x0, region.y0,
+                       region.x1, region.y1);
+}
+
+psImage* psImageSubsection(psImage* image,
+                           const char* section)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (section == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(section,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                section);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return imageSubset(NULL,image,col0,row0,col1+1,row1+1);
+}
+
+psImage* psImageTrim(psImage* image,
+                     psRegion region)
+{
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->parent != NULL) {
+        return imageSubset(image,
+                           (psImage*)image->parent,
+                           region.x0+image->col0,
+                           region.y0+image->row0,
+                           region.x1+image->col0,
+                           region.y1+image->row0);
+    }
+
+    int col0 = region.x0;
+    int row0 = region.y0;
+    int col1 = region.x1;
+    int row1 = region.y1;
+
+    if (col1 < 1) {
+        col1 += image->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += image->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > image->numCols ||
+            row1 > image->numRows ||
+            col0 >= col1 ||
+            row0 >= row1 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        psFree(image);
+        return NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psU32 elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+    psU32 numCols = col1-col0;
+    psU32 numRows = row1-row0;
+    psU32 rowSize = elementSize*numCols;
+    psU32 colOffset = elementSize * col0;
+    psU8* imageData = image->rawDataBuffer;
+    for (psS32 row = row0; row < row1; row++) {
+        memmove(imageData,image->data.U8[row] + colOffset,rowSize);
+        imageData += rowSize;
+    }
+
+    *(psU32*)&image->numRows = numRows;
+    *(psU32*)&image->numCols = numCols;
+
+    // XXX: should I really resize the buffers?
+    image->data.V = psRealloc(image->data.V,sizeof(psPtr)*numRows);
+    image->rawDataBuffer = psRealloc(image->rawDataBuffer,rowSize*numRows);
+
+    image->data.V[0] = image->rawDataBuffer;
+    for (psS32 r = 1; r < numRows; r++) {
+        image->data.U8[r] = image->data.U8[r-1] + rowSize;
+    }
+
+    return (image);
+}
+
Index: /trunk/psLib/src/imageops/psImageStructManip.h
===================================================================
--- /trunk/psLib/src/imageops/psImageStructManip.h	(revision 3968)
+++ /trunk/psLib/src/imageops/psImageStructManip.h	(revision 3968)
@@ -0,0 +1,85 @@
+/** @file  psImageStructManip.h
+*
+*  @brief Contains basic image structure manipulation operations, as specified
+*         in the PSLIB SDRS sections "Image Structure Manipulation".
+*
+*  @ingroup Image
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 02:08:21 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGE_STRUCT_MANIP_H
+#define PSIMAGE_STRUCT_MANIP_H
+
+#include "psImage.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Create a subimage of the specified area.
+ *
+ *  Deï¬ne a subimage of the speciï¬ed area of the given image. This function
+ *  must raise an error if the requested subset area lies outside of the
+ *  parent image and return NULL. The argument image is the parent image,
+ *  region.x0, region.y0 specify the starting pixel of the subraster, and
+ *  region.x1,region.y1 specify the extent of the desired subraster. Note
+ *  that the row and column of this âupper right-hand cornerâ are NOT included
+ *  in the region. In the event that x1 or y1 are negative, they shall be
+ *  interpreted as being relative to the size of the parent image in that
+ *  dimension. The entire subraster must be contained within the raster of the
+ *  parent image. Note that the refCounter for the parent should be
+ *  incremented.  This function must be deï¬ned for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
+ *
+ *  @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubset(
+    psImage* image,                    ///< Parent image.
+    psRegion region                    ///< region of subimage
+);
+
+/** Create a subimage of the specified area.
+ *
+ * Uses psLib memory allocation functions to create an image based on a larger
+ * one.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubsection(
+    psImage* image,                    ///< Parent image.
+    const char* section                ///< Subsection in the form '[x1:x2,y1:y2]'
+);
+
+/** Trim an image
+ *
+ *  Trim the specified image in-place, which involves shuffling the pixels
+ *  around in memory.  The pixels in the region [col0:col1,row0:row1] shall consist
+ *  the output image.  The column col1 and row row1 are NOT included in the range.
+ *  In the event that x1 or y1 are non-positive, they shall be interpreted as
+ *  being relative to the size of the parent image in that dimension.
+ *
+ *  If the entire specified subimage is not contained within the parent
+ *  image, an error results and the return value will be NULL.
+ *
+ *  N.B. If the input psImage is a child of another psImage, no pixel data
+ *  will be trimmed, rather it equivalent to calling psImageSubset.  If the input
+ *  psImage is, however, a parent psImage, any children will be obliterated,
+ *  i.e., freed from memory.
+ *
+ *  @return psImage*  trimmed image result
+ */
+psImage* psImageTrim(
+    psImage* image,                    ///< image to trim
+    psRegion region                    ///< trim region
+);
+
+/// @}
+
+#endif
