Index: trunk/psLib/src/image/psImageManip.c
===================================================================
--- trunk/psLib/src/image/psImageManip.c	(revision 1226)
+++ trunk/psLib/src/image/psImageManip.c	(revision 1250)
@@ -10,6 +10,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-07-15 19:58:37 $
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-07-21 23:39:10 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -24,4 +24,5 @@
 #include "psStats.h"
 #include "psMemory.h"
+#include "psImageStats.h"
 
 bool getSpecifiedStatValue(const psStats* stats, double* value);
@@ -527,2 +528,345 @@
     return out;
 }
+
+psImage* psImageRotate(psImage* out, const psImage* in, float angle, float unexposedValue, psImageInterpolateMode mode)
+{
+    if (in == NULL) {
+        psError(__func__,"The input image was NULL.");
+        psFree(out);
+        return NULL;
+    }
+
+    // put the angle in the range of 0...360.
+    angle = angle - 360.0f*floor(angle/360.0f);
+
+    if (fabsf(angle-90.0f) < FLT_EPSILON) {
+        // perform 1/4 rotate counter-clockwise
+        int numRows = in->numCols;
+        int numCols = in->numRows;
+        int 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 (int row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (int 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);
+            PSIMAGE_ROTATE_LEFT_90(U64);
+            PSIMAGE_ROTATE_LEFT_90(S8);
+            PSIMAGE_ROTATE_LEFT_90(S16);
+            PSIMAGE_ROTATE_LEFT_90(S32);
+            PSIMAGE_ROTATE_LEFT_90(S64);
+            PSIMAGE_ROTATE_LEFT_90(F32);
+            PSIMAGE_ROTATE_LEFT_90(F64);
+            PSIMAGE_ROTATE_LEFT_90(C32);
+            PSIMAGE_ROTATE_LEFT_90(C64);
+        default:
+            psError(__func__,"Unsupported type (%d)",type);
+            psFree(out);
+            return NULL;
+        }
+    } else if (fabsf(angle-180.0f) < FLT_EPSILON) {
+        // perform 1/2 rotate
+        int numRows = in->numRows;
+        int lastRow = numRows - 1;
+        int numCols = in->numCols;
+        int 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 (int row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                ps##TYPE* inRow = in->data.TYPE[lastRow-row]; \
+                for (int 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);
+            PSIMAGE_ROTATE_180_CASE(U64);
+            PSIMAGE_ROTATE_180_CASE(S8);
+            PSIMAGE_ROTATE_180_CASE(S16);
+            PSIMAGE_ROTATE_180_CASE(S32);
+            PSIMAGE_ROTATE_180_CASE(S64);
+            PSIMAGE_ROTATE_180_CASE(F32);
+            PSIMAGE_ROTATE_180_CASE(F64);
+            PSIMAGE_ROTATE_180_CASE(C32);
+            PSIMAGE_ROTATE_180_CASE(C64);
+        default:
+            psError(__func__,"Unsupported type (%d)",type);
+            psFree(out);
+            return NULL;
+        }
+    } else if (fabsf(angle-270.0f) < FLT_EPSILON) {
+        // perform 1/4 rotate clockwise
+        int numRows = in->numCols;
+        int lastRow = numRows - 1;
+        int 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 (int row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (int 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);
+            PSIMAGE_ROTATE_RIGHT_90(U64);
+            PSIMAGE_ROTATE_RIGHT_90(S8);
+            PSIMAGE_ROTATE_RIGHT_90(S16);
+            PSIMAGE_ROTATE_RIGHT_90(S32);
+            PSIMAGE_ROTATE_RIGHT_90(S64);
+            PSIMAGE_ROTATE_RIGHT_90(F32);
+            PSIMAGE_ROTATE_RIGHT_90(F64);
+            PSIMAGE_ROTATE_RIGHT_90(C32);
+            PSIMAGE_ROTATE_RIGHT_90(C64);
+        default:
+            psError(__func__,"Unsupported type (%d)",type);
+            psFree(out);
+            return NULL;
+        }
+    } else if (fabsf(angle) < FLT_EPSILON) {
+        out = psImageCopy(out,in,in->type.type);
+    } else {
+        psElemType type = in->type.type;
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        double centerX = (float)(numCols) / 2.0f;
+        float centerY = (float)(numRows) / 2.0f;
+        float t = angle*(3.14159265358f/180.0f);
+        float cosT = cosf(t);
+        float sinT = sinf(t);
+        float cosNegT = cosf(-t);
+        float sinNegT = sinf(-t);
+
+        // 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;
+
+        #define rotateProjectX(x,y) (x-centerX)*cosNegT + (y-centerY)*sinNegT
+        #define rotateProjectY(x,y) (y-centerY)*cosNegT - (x-centerX)*sinNegT
+
+        // bottom-left, i.e., (0,0)
+        float xbl = rotateProjectX(0,0);
+        float ybl = rotateProjectY(0,0);
+        // bottom-right, i.e., (numCols,0)
+        float xbr = rotateProjectX(numCols,0);
+        float ybr = rotateProjectY(numCols,0);
+        // top-left, i.e., (0,numRows)
+        float xtl = rotateProjectX(0,numRows);
+        float ytl = rotateProjectY(0,numRows);
+        // top-right, i.e., (numCols,numRows)
+        float xtr = rotateProjectX(numCols,numRows);
+        float ytr = rotateProjectY(numCols,numRows);
+
+        float minX = xbl;
+        minX = (minX > xbr) ? xbr : minX;
+        minX = (minX > xtl) ? xtl : minX;
+        minX = (minX > xtr) ? xtr : minX;
+
+        float minY = ybl;
+        minY = (minY > ybr) ? ybr : minY;
+        minY = (minY > ytl) ? ytl : minY;
+        minY = (minY > ytr) ? ytr : minY;
+
+        float maxX = xbl;
+        maxX = (maxX < xbr) ? xbr : maxX;
+        maxX = (maxX < xtl) ? xtl : maxX;
+        maxX = (maxX < xtr) ? xtr : maxX;
+
+        float maxY = ybl;
+        maxY = (maxY < ybr) ? ybr : maxY;
+        maxY = (maxY < ytl) ? ytl : maxY;
+        maxY = (maxY < ytr) ? ytr : maxY;
+
+        int intMinY = minY;
+        int outRows = ceil(maxY-minY)+1;
+        int outCols = ceil(maxX-minX)+1;
+
+        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 (unexposedValue < PS_MIN_##TYPE || unexposedValue > PS_MAX_##TYPE) { \
+                psError(__func__,"The given unexposedValue (%g) is outside of the " \
+                        "image type's range (%g->%g).", \
+                        unexposedValue, (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+                psFree(out); \
+                out = NULL; \
+                break; \
+            } \
+            float inX; \
+            float inY; \
+            ps##TYPE* outRow; \
+            for (int y = 0; y < outRows; y++) { \
+                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
+                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
+                outRow = out->data.TYPE[y]; \
+                for (int x = 0; x < outCols; x++) { \
+                    outRow[x] = p_psImagePixelInterpolate##MODE##_##TYPE(in,inX,inY,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: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32,MODE); \
+            break; \
+        case PS_TYPE_U64: \
+            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: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32,MODE); \
+            break; \
+        case PS_TYPE_S64: \
+            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; \
+        default: \
+            psError(__func__,"Image type (%d) not supported",type); \
+            psFree(out); \
+            out = NULL; \
+        } \
+        break;
+
+        switch (mode) {
+            PSIMAGE_ROTATE_ARBITRARY_CASE(FLAT);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
+        default:
+            psError(__func__,"Unsupported interpolation mode (%d)",mode);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageShift(psImage* out, const psImage* in, float dx, float dy, psF64 unexposedValue, psImageInterpolateMode mode)
+{
+    int outRows;
+    int outCols;
+    int elementSize;
+    psElemType type;
+
+    if (in == NULL) {
+        psError(__func__,"Input image can not be NULL.");
+        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(TYPE) \
+case PS_TYPE_##TYPE: \
+    if (unexposedValue < PS_MIN_##TYPE || unexposedValue > PS_MAX_##TYPE) { \
+        psError(__func__,"The given unexposedValue (%g) is outside of the " \
+                "image type's range (%g->%g).", \
+                unexposedValue, (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    for (int row=0;row<outRows;row++) { \
+        ps##TYPE* outRow = out->data.TYPE[row]; \
+        float y = dy+(float)row; \
+        for (int col=0;col<outCols;col++) { \
+            outRow[col] = psImagePixelInterpolate(in,dx+(float)col,y,unexposedValue,mode); \
+        } \
+    } \
+    break;
+
+    switch (in->type.type) {
+        PSIMAGE_SHIFT_CASE(U8);
+        PSIMAGE_SHIFT_CASE(U16);
+        PSIMAGE_SHIFT_CASE(U32);
+        PSIMAGE_SHIFT_CASE(U64);
+        PSIMAGE_SHIFT_CASE(S8);
+        PSIMAGE_SHIFT_CASE(S16);
+        PSIMAGE_SHIFT_CASE(S32);
+        PSIMAGE_SHIFT_CASE(S64);
+        PSIMAGE_SHIFT_CASE(F32);
+        PSIMAGE_SHIFT_CASE(F64);
+        PSIMAGE_SHIFT_CASE(C32);
+        PSIMAGE_SHIFT_CASE(C64);
+    default:
+        psError(__func__,"Image type (%d) not supported.",type);
+        psFree(out);
+        out = NULL;
+    }
+    return out;
+}
