Index: trunk/psModules/src/detrend/Makefile.am
===================================================================
--- trunk/psModules/src/detrend/Makefile.am	(revision 18867)
+++ trunk/psModules/src/detrend/Makefile.am	(revision 18893)
@@ -13,4 +13,5 @@
 	pmDetrendDB.c \
 	pmShutterCorrection.c \
+	pmDetrendThreads.c \
 	pmShifts.c \
 	pmDark.c
@@ -28,4 +29,5 @@
 	pmDetrendDB.h \
 	pmShutterCorrection.h \
+	pmDetrendThreads.h \
 	pmShifts.h \
 	pmDark.h
Index: trunk/psModules/src/detrend/pmBias.c
===================================================================
--- trunk/psModules/src/detrend/pmBias.c	(revision 18867)
+++ trunk/psModules/src/detrend/pmBias.c	(revision 18893)
@@ -14,14 +14,56 @@
 #include "pmFPAview.h"
 #include "pmFPACalibration.h"
+#include "pmDetrendThreads.h"
 
 #include "pmOverscan.h"
 #include "pmBias.h"
+
+bool pmBiasSubtractScan_Threaded (psThreadJob *job) {
+    pmReadout *in = job->args->data[0];
+    pmReadout *sub = job->args->data[1];
+    float scale  = PS_SCALAR_VALUE(job->args->data[2],F32);
+    int xOffset  = PS_SCALAR_VALUE(job->args->data[3],S32);
+    int yOffset  = PS_SCALAR_VALUE(job->args->data[4],S32);
+    int rowStart = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int rowStop  = PS_SCALAR_VALUE(job->args->data[6],S32);
+    bool status = pmBiasSubtractScan (in, sub, scale, xOffset, yOffset, rowStart, rowStop);
+    return status;
+}
+
+bool pmBiasSubtractScan (pmReadout *in, pmReadout *sub, float scale, int xOffset, int yOffset, int rowStart, int rowStop) {
+
+    psImage *inImage  = in->image;      // The input image
+    psImage *inMask   = in->mask;       // The input mask
+    psImage *subImage = sub->image;     // The image to be subtracted
+    psImage *subMask  = sub->mask;      // The mask for the subtraction image
+
+    if (scale == 1.0) {
+        for (int i = rowStart; i < rowStop; i++) {
+            for (int j = 0; j < inImage->numCols; j++) {
+                inImage->data.F32[i][j] -= subImage->data.F32[i+yOffset][j+xOffset];
+                if (inMask && subMask) {
+                    inMask->data.U8[i][j] |= subMask->data.U8[i+yOffset][j+xOffset];
+                }
+            }
+        }
+    } else {
+        for (int i = rowStart; i < rowStop; i++) {
+            for (int j = 0; j < inImage->numCols; j++) {
+                inImage->data.F32[i][j] -= subImage->data.F32[i+yOffset][j+xOffset] * scale;
+                if (inMask && subMask) {
+                    inMask->data.U8[i][j] |= subMask->data.U8[i+yOffset][j+xOffset];
+                }
+            }
+        }
+    }
+    return true;
+}
 
 // pmBiasSubtractFrame(): this routine will take as input a readout for the input image and a readout for the bias
 // image.  The bias image is subtracted in place from the input image.
 bool pmBiasSubtractFrame(pmReadout *in, // Input readout
-                          const pmReadout *sub, // Readout to be subtracted from input
-                          float scale   // Scale to apply before subtracting
-                         )
+			 pmReadout *sub, // Readout to be subtracted from input
+			 float scale   // Scale to apply before subtracting
+    )
 {
     PS_ASSERT_PTR_NON_NULL(in, false);
@@ -36,7 +78,5 @@
 
     psImage *inImage  = in->image;      // The input image
-    psImage *inMask   = in->mask;       // The input mask
     psImage *subImage = sub->image;     // The image to be subtracted
-    psImage *subMask  = sub->mask;      // The mask for the subtraction image
 
     // Check parities
@@ -76,22 +116,56 @@
     }
 
-    if (scale == 1.0) {
-        for (int i = 0; i < inImage->numRows; i++) {
-            for (int j = 0; j < inImage->numCols; j++) {
-                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub];
-                if (inMask && subMask) {
-                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
-                }
-            }
-        }
-    } else {
-        for (int i = 0; i < inImage->numRows; i++) {
-            for (int j = 0; j < inImage->numCols; j++) {
-                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub] * scale;
-                if (inMask && subMask) {
-                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
-                }
-            }
-        }
+    int xOffset = x0in - x0sub;
+    int yOffset = y0in - y0sub;
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) { 
+	threaded = false;
+	scanRows = inImage->numRows;
+    }
+
+    for (int rowStart = 0; rowStart < inImage->numRows; rowStart += scanRows) {
+      int rowStop = PS_MIN (rowStart + scanRows, inImage->numRows);
+
+# define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
+      psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
+      psArrayAdd(ARRAY, 1, scalar); \
+      psFree (scalar); }
+      
+      if (threaded) {
+	  // allocate a job, construct the arguments for this job
+	  psThreadJob *job = psThreadJobAlloc ("PSMODULES_DETREND_BIAS");
+	  psArrayAdd (job->args, 1, in);
+	  psArrayAdd (job->args, 1, sub);
+	  PS_ARRAY_ADD_SCALAR (job->args, scale, PS_TYPE_F32);
+	  PS_ARRAY_ADD_SCALAR (job->args, xOffset, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, yOffset, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStart, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStop, PS_TYPE_S32);
+
+	  // ppImageDetrendReadout(config, options, view)
+	  if (!psThreadJobAddPending (job)) {
+	      return false;
+	  }
+      } else {
+	  pmBiasSubtractScan (in, sub, scale, xOffset, yOffset, rowStart, rowStop);
+      }
+    }
+
+    if (threaded) {
+	// wait here for the threaded jobs to finish
+	if (!psThreadPoolWait ()) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+	    return false;
+	}
+	fprintf (stderr, "success for threaded jobs\n");
+
+	// each job records its own goodPixel values; sum them here
+	// we have only supplied one type of job, so we can assume the types here
+	psThreadJob *job = NULL;
+	while ((job = psThreadJobGetDone()) != NULL) {
+	    psFree (job);
+	}
     }
 
@@ -100,5 +174,5 @@
 
 bool pmBiasSubtract(pmReadout *in, pmOverscanOptions *overscanOpts,
-                    const pmReadout *bias, const pmReadout *dark, const pmFPAview *view)
+                    pmReadout *bias, pmReadout *dark, const pmFPAview *view)
 {
     psTrace("psModules.detrend", 4,
Index: trunk/psModules/src/detrend/pmBias.h
===================================================================
--- trunk/psModules/src/detrend/pmBias.h	(revision 18867)
+++ trunk/psModules/src/detrend/pmBias.h	(revision 18893)
@@ -5,6 +5,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-08-15 20:21:18 $
+ * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:24:47 $
  * Copyright 2004--2006 Institute for Astronomy, University of Hawaii
  */
@@ -22,6 +22,6 @@
 bool pmBiasSubtract(pmReadout *in,      ///< Input readout, to be overscan/bias/dark corrected
                     pmOverscanOptions *overscanOpts, ///< Options for overscan subtraction, or NULL
-                    const pmReadout *bias, ///< Bias to subtract, or NULL
-                    const pmReadout *dark, ///< Dark to scale and subtract, or NULL
+                    pmReadout *bias, ///< Bias to subtract, or NULL
+                    pmReadout *dark, ///< Dark to scale and subtract, or NULL
                     const pmFPAview *view ///< View for readout of interest
                    );
@@ -30,7 +30,10 @@
 // image.  The bias image is subtracted in place from the input image.
 bool pmBiasSubtractFrame(pmReadout *in, // Input readout
-                          const pmReadout *sub, // Readout to be subtracted from input
-                          float scale   // Scale to apply before subtracting
+			 pmReadout *sub, // Readout to be subtracted from input
+			 float scale   // Scale to apply before subtracting
     );
+
+bool pmBiasSubtractScan_Threaded (psThreadJob *job);
+bool pmBiasSubtractScan (pmReadout *in, pmReadout *sub, float scan, int xOffset, int yOffset, int rowStart, int rowStop);
 
 /// @}
Index: trunk/psModules/src/detrend/pmDark.c
===================================================================
--- trunk/psModules/src/detrend/pmDark.c	(revision 18867)
+++ trunk/psModules/src/detrend/pmDark.c	(revision 18893)
@@ -11,4 +11,5 @@
 #include "pmFPAWrite.h"
 #include "pmReadoutStack.h"
+#include "pmDetrendThreads.h"
 
 #include "pmDark.h"
@@ -366,5 +367,45 @@
 }
 
-bool pmDarkApply(pmReadout *readout, const pmCell *dark, psMaskType bad)
+bool pmDarkApplyScan_Threaded (psThreadJob *job) {
+    pmReadout *readout   = job->args->data[0];
+    pmCell *dark         = job->args->data[1];
+    psPolynomialMD *poly = job->args->data[2];
+    psVector *values     = job->args->data[3];
+
+    psMaskType bad = PS_SCALAR_VALUE(job->args->data[4],U8);
+    bool doNorm    = PS_SCALAR_VALUE(job->args->data[5],U8);
+    float norm     = PS_SCALAR_VALUE(job->args->data[6],F32);
+    int rowStart   = PS_SCALAR_VALUE(job->args->data[7],S32);
+    int rowStop    = PS_SCALAR_VALUE(job->args->data[8],S32);
+    bool status = pmDarkApplyScan (readout, dark, poly, values, bad, doNorm, norm, rowStart, rowStop);
+    return status;
+}
+
+bool pmDarkApplyScan (pmReadout *readout, const pmCell *dark, psPolynomialMD *poly, psVector *values, psMaskType bad, bool doNorm, float norm, int rowStart, int rowStop) {
+
+    int numCols = readout->image->numCols;
+    int numTerms = dark->readouts->n;   // Number of polynomial terms
+
+    // thread here by scan
+    for (int y = rowStart; y < rowStop; y++) {
+        for (int x = 0; x < numCols; x++) {
+            for (int i = 0; i < numTerms; i++) {
+                pmReadout *ro = dark->readouts->data[i]; // Dark readout
+                poly->coeff->data.F64[i] = ro->image->data.F32[y][x];
+            }
+            float value = psPolynomialMDEval(poly, values); // Value of dark current
+            if (doNorm) {
+                value *= norm;
+            }
+            readout->image->data.F32[y][x] -= value;
+            if (readout->mask && !isfinite(value)) {
+                readout->mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
+            }
+        }
+    }
+    return true;
+}
+
+bool pmDarkApply(pmReadout *readout, pmCell *dark, psMaskType bad)
 {
     PS_ASSERT_PTR_NON_NULL(readout, false);
@@ -433,19 +474,56 @@
     psFree(orders);
 
-    for (int y = 0; y < numRows; y++) {
-        for (int x = 0; x < numCols; x++) {
-            for (int i = 0; i < numTerms; i++) {
-                pmReadout *ro = dark->readouts->data[i]; // Dark readout
-                poly->coeff->data.F64[i] = ro->image->data.F32[y][x];
-            }
-            float value = psPolynomialMDEval(poly, values); // Value of dark current
-            if (doNorm) {
-                value *= norm;
-            }
-            readout->image->data.F32[y][x] -= value;
-            if (readout->mask && !isfinite(value)) {
-                readout->mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
-            }
-        }
+    // thread here by scan
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) { 
+	threaded = false;
+	scanRows = readout->image->numRows;
+    }
+
+    for (int rowStart = 0; rowStart < readout->image->numRows; rowStart += scanRows) {
+      int rowStop = PS_MIN (rowStart + scanRows, readout->image->numRows);
+
+# define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
+      psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
+      psArrayAdd(ARRAY, 1, scalar); \
+      psFree (scalar); }
+      
+      if (threaded) {
+	  // allocate a job, construct the arguments for this job
+	  psThreadJob *job = psThreadJobAlloc ("PSMODULES_DETREND_DARK");
+	  psArrayAdd (job->args, 1, readout);
+	  psArrayAdd (job->args, 1, dark);
+	  psArrayAdd (job->args, 1, poly);
+	  psArrayAdd (job->args, 1, values);
+	  PS_ARRAY_ADD_SCALAR (job->args, bad, PS_TYPE_MASK);
+	  PS_ARRAY_ADD_SCALAR (job->args, doNorm, PS_TYPE_U8);
+	  PS_ARRAY_ADD_SCALAR (job->args, norm, PS_TYPE_F32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStart, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStop, PS_TYPE_S32);
+
+	  // ppImageDetrendReadout(config, options, view)
+	  if (!psThreadJobAddPending (job)) {
+	      return false;
+	  }
+      } else {
+	  pmDarkApplyScan (readout, dark, poly, values, bad, doNorm, norm, rowStart, rowStop);
+      }
+    }
+
+    if (threaded) {
+	// wait here for the threaded jobs to finish
+	if (!psThreadPoolWait ()) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+	    return false;
+	}
+	fprintf (stderr, "success for threaded jobs\n");
+
+	// free the done jobs
+	psThreadJob *job = NULL;
+	while ((job = psThreadJobGetDone()) != NULL) {
+	    psFree (job);
+	}
     }
 
Index: trunk/psModules/src/detrend/pmDark.h
===================================================================
--- trunk/psModules/src/detrend/pmDark.h	(revision 18867)
+++ trunk/psModules/src/detrend/pmDark.h	(revision 18893)
@@ -40,7 +40,10 @@
     );
 
+bool pmDarkApplyScan_Threaded (psThreadJob *job);
+bool pmDarkApplyScan (pmReadout *readout, const pmCell *dark, psPolynomialMD *poly, psVector *values, psMaskType bad, bool doNorm, float norm, int rowStart, int rowStop);
+
 // Apply dark
 bool pmDarkApply(pmReadout *readout,    // Readout to which to apply dark
-                 const pmCell *dark,    // Dark to apply
+                 pmCell *dark,    // Dark to apply
                  psMaskType bad         // Mask value to give bad pixels
     );
Index: trunk/psModules/src/detrend/pmDetrendThreads.c
===================================================================
--- trunk/psModules/src/detrend/pmDetrendThreads.c	(revision 18893)
+++ trunk/psModules/src/detrend/pmDetrendThreads.c	(revision 18893)
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <string.h>
+
+#include "psPolynomialMD.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmHDUUtils.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+
+#include "pmOverscan.h"
+#include "pmBias.h"
+#include "pmDark.h"
+#include "pmShutterCorrection.h"
+#include "pmFlatField.h"
+#include "pmDetrendThreads.h"
+
+static int scanRows = 0;
+
+int pmDetrendGetScanRows () {
+    return scanRows;
+}    
+
+bool pmDetrendSetThreadTasks (int newScanRows) {
+
+    psAssert (scanRows == 0, "programming error: program called pmDetrendSetThreadTasks twice");
+    scanRows = newScanRows;
+
+    psThreadTask *task = NULL;
+
+    task = psThreadTaskAlloc ("PSMODULES_DETREND_BIAS", 7);
+    task->function = &pmBiasSubtractScan_Threaded;
+    psThreadTaskAdd (task);
+
+    task = psThreadTaskAlloc ("PSMODULES_DETREND_DARK", 9);
+    task->function = &pmDarkApplyScan_Threaded;
+    psThreadTaskAdd (task);
+
+    task = psThreadTaskAlloc ("PSMODULES_DETREND_SHUTTER", 7);
+    task->function = &pmShutterCorrectionApplyScan_Threaded;
+    psThreadTaskAdd (task);
+
+    task = psThreadTaskAlloc ("PSMODULES_DETREND_FLAT", 9);
+    task->function = &pmFlatFieldScan_Threaded;
+    psThreadTaskAdd (task);
+
+    return true;
+}
Index: trunk/psModules/src/detrend/pmDetrendThreads.h
===================================================================
--- trunk/psModules/src/detrend/pmDetrendThreads.h	(revision 18893)
+++ trunk/psModules/src/detrend/pmDetrendThreads.h	(revision 18893)
@@ -0,0 +1,23 @@
+/* @file pmDetrendThreads.h
+ * @brief theading functions related to detrends
+ * @author Eugene Magnier, IfA
+ *
+ * @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:24:47 $
+ * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_DETREND_THREADS_H
+#define PM_DETREND_THREADS_H
+
+/// @addtogroup detrend Detrend Creation and Application
+/// @{
+
+/// init the thread handler tasks for detrending
+bool pmDetrendSetThreadTasks (int newScanRows);
+
+/// get the requested number of scan rows per thread
+int pmDetrendGetScanRows ();
+
+/// @}
+#endif
Index: trunk/psModules/src/detrend/pmFlatField.c
===================================================================
--- trunk/psModules/src/detrend/pmFlatField.c	(revision 18867)
+++ trunk/psModules/src/detrend/pmFlatField.c	(revision 18893)
@@ -11,4 +11,60 @@
 #include "pmFPAMaskWeight.h"
 #include "pmFlatField.h"
+#include "pmDetrendThreads.h"
+
+bool pmFlatFieldScan_Threaded (psThreadJob *job) {
+    psImage *inImage   = job->args->data[0];
+    psImage *inMask    = job->args->data[1];
+    psImage *flatImage = job->args->data[2];
+    psImage *flatMask  = job->args->data[3];
+
+    psMaskType badFlat = PS_SCALAR_VALUE(job->args->data[4],U8);
+    int xOffset        = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int yOffset        = PS_SCALAR_VALUE(job->args->data[6],S32);
+    int rowStart       = PS_SCALAR_VALUE(job->args->data[7],S32);
+    int rowStop        = PS_SCALAR_VALUE(job->args->data[8],S32);
+    bool status = pmFlatFieldScan (inImage, inMask, flatImage, flatMask, badFlat, xOffset, yOffset, rowStart, rowStop);
+    return status;
+}
+
+// Macro for all PS types
+#define FLAT_DIVISION_CASE(TYPE, SPECIAL)	\
+    case PS_TYPE_##TYPE:			\
+    for (int j = rowStart; j < rowStop; j++) { \
+        for (int i = 0; i < inImage->numCols; i++) { \
+            ps##TYPE flatValue = flatImage->data.TYPE[j + yOffset][i + xOffset]; \
+            if (!isfinite(flatValue) || flatValue <= 0.0 || \
+                (flatMask && flatMask->data.U8[j + yOffset][i + xOffset])) { \
+                if (inMask) { \
+                    inMask->data.PS_TYPE_MASK_DATA[j][i] |= badFlat; \
+                } \
+                inImage->data.TYPE[j][i] = SPECIAL; \
+            } else { \
+                inImage->data.TYPE[j][i] /= flatValue; \
+            } \
+        } \
+    } \
+    break;
+
+bool pmFlatFieldScan (psImage *inImage, psImage *inMask, psImage *flatImage, psImage *flatMask, psMaskType badFlat, int xOffset, int yOffset, int rowStart, int rowStop) {
+
+    switch (inImage->type.type) {
+        FLAT_DIVISION_CASE(U8,  0);
+        FLAT_DIVISION_CASE(U16, 0);
+        FLAT_DIVISION_CASE(U32, 0);
+        FLAT_DIVISION_CASE(U64, 0);
+        FLAT_DIVISION_CASE(S8,  0);
+        FLAT_DIVISION_CASE(S16, 0);
+        FLAT_DIVISION_CASE(S32, 0);
+        FLAT_DIVISION_CASE(S64, 0);
+        FLAT_DIVISION_CASE(F32, NAN);
+        FLAT_DIVISION_CASE(F64, NAN);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported type for input image: %x\n",
+                inImage->type.type);
+        return false;
+    }
+    return true;
+}
 
 bool pmFlatField(pmReadout *in, const pmReadout *flat, psMaskType badFlat)
@@ -71,38 +127,54 @@
     }
 
-    // Macro for all PS types
-    #define FLAT_DIVISION_CASE(TYPE, SPECIAL) \
-case PS_TYPE_##TYPE: \
-    for (int j = 0; j < inImage->numRows; j++) { \
-        for (int i = 0; i < inImage->numCols; i++) { \
-            ps##TYPE flatValue = flatImage->data.TYPE[j + yOffset][i + xOffset]; \
-            if (!isfinite(flatValue) || flatValue <= 0.0 || \
-                (flatMask && flatMask->data.U8[j + yOffset][i + xOffset])) { \
-                if (inMask) { \
-                    inMask->data.PS_TYPE_MASK_DATA[j][i] |= badFlat; \
-                } \
-                inImage->data.TYPE[j][i] = SPECIAL; \
-            } else { \
-                inImage->data.TYPE[j][i] /= flatValue; \
-            } \
-        } \
-    } \
-    break;
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) { 
+	threaded = false;
+	scanRows = inImage->numRows;
+    }
 
-    switch (inImage->type.type) {
-        FLAT_DIVISION_CASE(U8,  0);
-        FLAT_DIVISION_CASE(U16, 0);
-        FLAT_DIVISION_CASE(U32, 0);
-        FLAT_DIVISION_CASE(U64, 0);
-        FLAT_DIVISION_CASE(S8,  0);
-        FLAT_DIVISION_CASE(S16, 0);
-        FLAT_DIVISION_CASE(S32, 0);
-        FLAT_DIVISION_CASE(S64, 0);
-        FLAT_DIVISION_CASE(F32, NAN);
-        FLAT_DIVISION_CASE(F64, NAN);
-    default:
-        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unsupported type for input image: %x\n",
-                inImage->type.type);
-        return false;
+    for (int rowStart = 0; rowStart < inImage->numRows; rowStart += scanRows) {
+      int rowStop = PS_MIN (rowStart + scanRows, inImage->numRows);
+
+# define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
+      psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
+      psArrayAdd(ARRAY, 1, scalar); \
+      psFree (scalar); }
+      
+      if (threaded) {
+	  // allocate a job, construct the arguments for this job
+	  psThreadJob *job = psThreadJobAlloc ("PSMODULES_DETREND_FLAT");
+	  psArrayAdd (job->args, 1, inImage);
+	  psArrayAdd (job->args, 1, inMask);
+	  psArrayAdd (job->args, 1, flatImage);
+	  psArrayAdd (job->args, 1, flatMask);
+	  PS_ARRAY_ADD_SCALAR (job->args, badFlat, PS_TYPE_U8);
+	  PS_ARRAY_ADD_SCALAR (job->args, xOffset, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, yOffset, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStart, PS_TYPE_S32);
+	  PS_ARRAY_ADD_SCALAR (job->args, rowStop, PS_TYPE_S32);
+
+	  // ppImageDetrendReadout(config, options, view)
+	  if (!psThreadJobAddPending (job)) {
+	      return false;
+	  }
+      } else {
+	  pmFlatFieldScan (inImage, inMask, flatImage, flatMask, badFlat, xOffset, yOffset, rowStart, rowStop);
+      }
+    }
+
+    if (threaded) {
+	// wait here for the threaded jobs to finish
+	if (!psThreadPoolWait ()) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+	    return false;
+	}
+	fprintf (stderr, "success for threaded jobs\n");
+
+	// free done jobs
+	psThreadJob *job = NULL;
+	while ((job = psThreadJobGetDone()) != NULL) {
+	    psFree (job);
+	}
     }
 
Index: trunk/psModules/src/detrend/pmFlatField.h
===================================================================
--- trunk/psModules/src/detrend/pmFlatField.h	(revision 18867)
+++ trunk/psModules/src/detrend/pmFlatField.h	(revision 18893)
@@ -5,6 +5,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-06-02 03:51:03 $
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:24:47 $
  * Copyright 2004-2006 Institute for Astronomy, University of Hawaii
  */
@@ -27,4 +27,9 @@
                  psMaskType badFlat     ///< Mask value to give bad flat pixels
                 );
+
+bool pmFlatFieldScan_Threaded (psThreadJob *job);
+bool pmFlatFieldScan (psImage *inImage, psImage *inMask, psImage *flatImage, psImage *flatMask, psMaskType badFlag, int xOffset, int yOffset, int rowStart, int rowStop);
+
+
 /// @}
 #endif
Index: trunk/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- trunk/psModules/src/detrend/pmShutterCorrection.c	(revision 18867)
+++ trunk/psModules/src/detrend/pmShutterCorrection.c	(revision 18893)
@@ -14,4 +14,5 @@
 #include "pmConceptsAverage.h"
 #include "pmReadoutStack.h"
+#include "pmDetrendThreads.h"
 
 #include "pmShutterCorrection.h"
@@ -655,4 +656,32 @@
 
 
+bool pmShutterCorrectionApplyScan_Threaded (psThreadJob *job) {
+    psImage *image        = job->args->data[0];
+    psImage *shutterImage = job->args->data[1];
+    psImage *mask         = job->args->data[2];
+
+    float exptime    = PS_SCALAR_VALUE(job->args->data[3],F32);
+    psMaskType blank = PS_SCALAR_VALUE(job->args->data[4],U8);
+    int rowStart     = PS_SCALAR_VALUE(job->args->data[5],S32);
+    int rowStop      = PS_SCALAR_VALUE(job->args->data[6],S32);
+    bool status = pmShutterCorrectionApplyScan (image, shutterImage, mask, exptime, blank, rowStart, rowStop);
+    return status;
+}
+
+bool pmShutterCorrectionApplyScan(psImage *image, psImage *shutterImage, psImage *mask, float exptime, psMaskType blank, int rowStart, int rowStop)
+{
+    for (int y = rowStart; y < rowStop; y++) {
+	for (int x = 0; x < image->numCols; x++) {
+	    if (mask && !isfinite(shutterImage->data.F32[y][x])) {
+		mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
+		image->data.F32[y][x] = NAN;
+		continue;
+	    }
+	    image->data.F32[y][x] *= exptime / (exptime + shutterImage->data.F32[y][x]);
+	}
+    }
+    return true;
+}
+
 bool pmShutterCorrectionApply(pmReadout *readout, const pmReadout *shutter, psMaskType blank)
 {
@@ -700,4 +729,11 @@
     psImage *image = readout->image;    // Image to correct
     psImage *mask = readout->mask;      // Corresponding mask
+
+    bool threaded = true;
+    int scanRows = pmDetrendGetScanRows();
+    if (scanRows == 0) { 
+	threaded = false;
+	scanRows = image->numRows;
+    }
 
     if (exptime <= 0.0) {
@@ -720,14 +756,45 @@
         psFree (line);
     } else {
-        for (int y = 0; y < image->numRows; y++) {
-            for (int x = 0; x < image->numCols; x++) {
-                if (mask && !isfinite(shutterImage->data.F32[y][x])) {
-                    mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
-                    image->data.F32[y][x] = NAN;
-                    continue;
-                }
-                image->data.F32[y][x] *= exptime / (exptime + shutterImage->data.F32[y][x]);
-            }
-        }
+	for (int rowStart = 0; rowStart < image->numRows; rowStart += scanRows) {
+	    int rowStop = PS_MIN (rowStart + scanRows, image->numRows);
+
+# define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
+      psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
+      psArrayAdd(ARRAY, 1, scalar); \
+      psFree (scalar); }
+      
+	    if (threaded) {
+		// allocate a job, construct the arguments for this job
+		psThreadJob *job = psThreadJobAlloc ("PSMODULES_DETREND_SHUTTER");
+		psArrayAdd (job->args, 1, image);
+		psArrayAdd (job->args, 1, shutterImage);
+		psArrayAdd (job->args, 1, mask);
+		PS_ARRAY_ADD_SCALAR (job->args, exptime, PS_TYPE_F32);
+		PS_ARRAY_ADD_SCALAR (job->args, blank, PS_TYPE_MASK);
+		PS_ARRAY_ADD_SCALAR (job->args, rowStart, PS_TYPE_S32);
+		PS_ARRAY_ADD_SCALAR (job->args, rowStop, PS_TYPE_S32);
+
+		// ppImageDetrendReadout(config, options, view)
+		if (!psThreadJobAddPending (job)) {
+		    return false;
+		}
+	    } else {
+		pmShutterCorrectionApplyScan (image, shutterImage, mask, exptime, blank, rowStart, rowStop);
+	    }
+	}
+	if (threaded) {
+	    // wait here for the threaded jobs to finish
+	    if (!psThreadPoolWait ()) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
+		return false;
+	    }
+	    fprintf (stderr, "success for threaded jobs\n");
+
+	    // free the done jobs
+	    psThreadJob *job = NULL;
+	    while ((job = psThreadJobGetDone()) != NULL) {
+		psFree (job);
+	    }
+	}
     }
     psFree(shutterImage);
Index: trunk/psModules/src/detrend/pmShutterCorrection.h
===================================================================
--- trunk/psModules/src/detrend/pmShutterCorrection.h	(revision 18867)
+++ trunk/psModules/src/detrend/pmShutterCorrection.h	(revision 18893)
@@ -5,6 +5,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-08-01 23:58:37 $
+ * @version $Revision: 1.18 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-08-05 01:24:47 $
  * Copyright 2006 Institute for Astronomy, University of Hawaii
  */
@@ -124,4 +124,7 @@
     );
 
+bool pmShutterCorrectionApplyScan_Threaded (psThreadJob *job);
+bool pmShutterCorrectionApplyScan(psImage *image, psImage *shutterImage, psImage *mask, float exptime, psMaskType blank, int rowStart, int rowStop);
+
 /// Apply a shutter correction
 ///
Index: trunk/psModules/src/psmodules.h
===================================================================
--- trunk/psModules/src/psmodules.h	(revision 18867)
+++ trunk/psModules/src/psmodules.h	(revision 18893)
@@ -13,4 +13,5 @@
 #include <pmConfig.h>
 #include <pmDetrendDB.h>
+#include <pmDetrendThreads.h>
 #include <pmHDU.h>
 #include <pmFPA.h>
