Index: trunk/psLib/src/imageops/psImageConvolve.c
===================================================================
--- trunk/psLib/src/imageops/psImageConvolve.c	(revision 3884)
+++ 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
