Index: trunk/psModules/src/camera/pmFPACopy.c
===================================================================
--- trunk/psModules/src/camera/pmFPACopy.c	(revision 12585)
+++ trunk/psModules/src/camera/pmFPACopy.c	(revision 12589)
@@ -17,25 +17,13 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Bin a region down by specified factors in x and y
-static void binRegion(psRegion *region, // Region to be binned
-                      int xBin, int yBin // Binning in x and y
-                     )
-{
-    assert(region);
-    assert(xBin > 0);
-    assert(yBin > 0);
-
-    // Want to include the lower bound: 1 binned by 4 --> 0; 3 binned by 4 --> 0; 4 binned by 4 --> 1
-    region->x0 = (int)(region->x0 / xBin);
-    region->y0 = (int)(region->y0 / yBin);
-    // Want to exclude the upper bound: 4 binned by 4 --> 1; 5 binned by 4 --> 2; 7 binned by 4 --> 2
-    region->x1 = (int)((region->x1 + xBin - 1) / xBin);
-    region->y1 = (int)((region->y1 + yBin - 1) / yBin);
-}
-
-#if 1
+// File-static functions and macros
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Copy the value for a concept
+#define COPY_CONCEPT(TARGET, SOURCE, NAME, TYPE) { \
+    psMetadataItem *targetItem = psMetadataLookup(TARGET, NAME); \
+    psMetadataItem *sourceItem = psMetadataLookup(SOURCE, NAME); \
+    targetItem->data.TYPE = sourceItem->data.TYPE; }
+
 // Find the blank (image-less) PHU, given a cell.
 static pmHDU *findBlankPHU(const pmCell *cell // The cell for which to find the PHU
@@ -58,5 +46,25 @@
     return NULL;
 }
-#endif
+
+// copy one of the psImage components of the readout
+static void readoutCopyComponent (psImage **target, psImage *source, psImageBinning *binning, bool xFlip, bool yFlip, bool pixels) {
+
+    if (!source) return;
+
+    if (*target) {
+	psFree(*target);
+    }
+    if (pixels) {
+	*target = psImageFlip(NULL, source, xFlip, yFlip);
+	return;
+    } 
+
+    // I have the fine image size, I know the binning factor, determine the ruff image size
+    binning->nXfine = source->numCols;
+    binning->nYfine = source->numRows;
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    *target = psImageAlloc(binning->nXruff, binning->nYruff, source->type.type);
+    return;
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,49 +85,51 @@
     assert(xBin > 0 && yBin > 0);
 
+    // XXX this is a programming / config error
+    if (pixels && (xBin != 1 || yBin != 1)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to copy pixels if binning is set\n");
+        return false;
+    }
+
+    // the binning structure carries the information no how to rebin the images if needed
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXbin = xBin;
+    binning->nYbin = yBin;
+
     psArray *sourceReadouts = source->readouts; // The source readouts
     int numReadouts = sourceReadouts->n; // Number of readouts copied
 
-    // Copy the value for a concept
-    #define COPY_CONCEPT(TARGET, SOURCE, NAME, TYPE) \
-    psMetadataItem *targetItem = psMetadataLookup(TARGET, NAME); \
-    psMetadataItem *sourceItem = psMetadataLookup(SOURCE, NAME); \
-    targetItem->data.TYPE = sourceItem->data.TYPE;
-
     // Need to check/change CELL.XPARITY and CELL.YPARITY
-    bool mdok = true;                   // Status of MD lookup
+    bool mdokS = true;                   // Status of MD lookup
+    bool mdokT = true;                   // Status of MD lookup
     bool xFlip = false;                 // Switch parity in x?
     bool yFlip = false;                 // Switch parity in y?
-    {
-        int xParityTarget = psMetadataLookupS32(&mdok, target->concepts, "CELL.XPARITY"); // Target x parity
-        if (mdok && (xParityTarget == 1 || xParityTarget == -1))
-        {
-            int xParitySource = psMetadataLookupS32(&mdok, source->concepts, "CELL.XPARITY"); // Source parity
-            if (mdok && (xParitySource == 1 || xParitySource == -1) && xParityTarget != xParitySource) {
-                xFlip = true;
-            }
-        } else
-        {
-            // Use the source parity
-            COPY_CONCEPT(target->concepts, source->concepts, "CELL.XPARITY", S32);
-        }
-        int yParityTarget = psMetadataLookupS32(&mdok, target->concepts, "CELL.YPARITY"); // Target y parity
-        if (mdok && (yParityTarget == 1 || yParityTarget == -1))
-        {
-            int yParitySource = psMetadataLookupS32(&mdok, source->concepts, "CELL.YPARITY"); // Source parity
-            if (mdok && (yParitySource == 1 || yParitySource == -1) && yParityTarget != yParitySource) {
-                yFlip = true;
-            }
-        } else
-        {
-            // Use the source parity
-            COPY_CONCEPT(target->concepts, source->concepts, "CELL.YPARITY", S32);
-        }
-        psTrace("psModules.camera", 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
-    }
-
-    if (pixels && (xBin != 1 || yBin != 1)) {
-        psLogMsg(__func__, PS_LOG_WARN, "Unable to copy pixels if binning is set --- copy turned off.\n");
-        pixels = false;
-    }
+
+    // enforce the following conditions:
+    // CELL.XPARITY is required
+    // CELL.XPARITY must be +/- 1
+    int xParitySource = psMetadataLookupS32(&mdokS, source->concepts, "CELL.XPARITY"); // Source parity
+    int xParityTarget = psMetadataLookupS32(&mdokT, target->concepts, "CELL.XPARITY"); // Target x parity
+    assert (mdokS && mdokT);
+    assert (abs(xParitySource) == 1);
+    assert (abs(xParityTarget) == 1);
+    if (xParityTarget != xParitySource) {
+	xFlip = true;
+    } else {
+	// Use the source parity
+	COPY_CONCEPT(target->concepts, source->concepts, "CELL.XPARITY", S32);
+    }
+
+    int yParityTarget = psMetadataLookupS32(&mdokT, target->concepts, "CELL.YPARITY"); // Target y parity
+    int yParitySource = psMetadataLookupS32(&mdokS, source->concepts, "CELL.YPARITY"); // Source parity
+    assert (mdokS && mdokT);
+    assert (abs(yParitySource) == 1);
+    assert (abs(yParityTarget) == 1);
+    if (yParityTarget != yParitySource) {
+	yFlip = true;
+    } else {
+	// Use the source parity
+	COPY_CONCEPT(target->concepts, source->concepts, "CELL.YPARITY", S32);
+    }
+    psTrace("psModules.camera", 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
 
     // Perform deep copy of the images.  I would prefer *not* to do a deep copy, in the interests of speed (we
@@ -132,4 +142,5 @@
 
         // Copy attributes
+	// XXX is this correct under binning?
         targetReadout->col0 = sourceReadout->col0;
         targetReadout->row0 = sourceReadout->row0;
@@ -138,48 +149,8 @@
         targetReadout->data_exists = sourceReadout->data_exists;
 
-        // Copy image
-        if (sourceReadout->image) {
-            if (targetReadout->image) {
-                psFree(targetReadout->image);
-            }
-            if (pixels) {
-                targetReadout->image = psImageFlip(NULL, sourceReadout->image, xFlip, yFlip);
-            } else {
-                psImage *image = sourceReadout->image; // The source image
-                long binnedCols = (image->numCols + xBin - 1) / xBin;
-                long binnedRows = (image->numRows + yBin - 1) / yBin;
-                targetReadout->image = psImageAlloc(binnedCols, binnedRows, image->type.type);
-            }
-        }
-
-        // Copy mask
-        if (sourceReadout->mask) {
-            if (targetReadout->mask) {
-                psFree(targetReadout->mask);
-            }
-            if (pixels) {
-                targetReadout->mask = psImageFlip(NULL, sourceReadout->mask, xFlip, yFlip);
-            } else {
-                psImage *mask = sourceReadout->mask; // The source mask image
-                long binnedCols = (mask->numCols + xBin - 1) / xBin;
-                long binnedRows = (mask->numRows + yBin - 1) / yBin;
-                targetReadout->mask = psImageAlloc(binnedCols, binnedRows, mask->type.type);
-            }
-        }
-
-        // Copy weight
-        if (sourceReadout->weight) {
-            if (targetReadout->weight) {
-                psFree(targetReadout->weight);
-            }
-            if (pixels) {
-                targetReadout->weight = psImageFlip(NULL, sourceReadout->weight, xFlip, yFlip);
-            } else {
-                psImage *weight = sourceReadout->weight; // The source weight image
-                long binnedCols = (weight->numCols + xBin - 1) / xBin;
-                long binnedRows = (weight->numRows + yBin - 1) / yBin;
-                targetReadout->weight = psImageAlloc(binnedCols, binnedRows, weight->type.type);
-            }
-        }
+        // Copy all three image components (image, mask, weight)
+	readoutCopyComponent (&targetReadout->image,  sourceReadout->image,  binning, xFlip, yFlip, pixels);
+	readoutCopyComponent (&targetReadout->mask,   sourceReadout->mask,   binning, xFlip, yFlip, pixels);
+	readoutCopyComponent (&targetReadout->weight, sourceReadout->weight, binning, xFlip, yFlip, pixels);
 
         // Copy bias
@@ -187,16 +158,11 @@
             psListRemove(targetReadout->bias, PS_LIST_HEAD);
         }
+
         // Iterate over the biases
         psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false);
         psImage *bias = NULL;           // Bias image from iteration
         while ((bias = psListGetAndIncrement(biasIter))) {
-            psImage *biasCopy;          // Copy of the bias
-            if (pixels) {
-                biasCopy = psImageFlip(NULL, bias, xFlip, yFlip);
-            } else {
-                long binnedCols = (bias->numCols + xBin - 1) / xBin;
-                long binnedRows = (bias->numRows + yBin - 1) / yBin;
-                biasCopy = psImageAlloc(binnedCols, binnedRows, bias->type.type);
-            }
+            psImage *biasCopy = NULL;          // Copy of the bias
+	    readoutCopyComponent (&biasCopy, bias, binning, xFlip, yFlip, pixels);
             psListAdd(targetReadout->bias, PS_LIST_TAIL, biasCopy);
             psFree(biasCopy);           // Drop reference
@@ -216,19 +182,24 @@
     while ((conceptItem = psMetadataGetAndIncrement(conceptsIter))) {
         psString name = conceptItem->name; // Name of concept
-        if (strcmp(name, "CELL.TRIMSEC") != 0 && strcmp(name, "CELL.BIASSEC") != 0 &&
-                strcmp(name, "CELL.XPARITY") != 0 && strcmp(name, "CELL.YPARITY") != 0 &&
-                strcmp(name, "CELL.X0") != 0 && strcmp(name, "CELL.Y0") != 0) {
-            psMetadataItem *copy = psMetadataItemCopy(conceptItem); // Copy of the concept
-            psMetadataAddItem(target->concepts, copy, PS_LIST_TAIL, PS_META_REPLACE);
-            psFree(copy);               // Drop reference
-        }
+        if (!strcmp(name, "CELL.TRIMSEC")) continue;
+	if (!strcmp(name, "CELL.BIASSEC")) continue;
+	if (!strcmp(name, "CELL.XPARITY")) continue;
+	if (!strcmp(name, "CELL.YPARITY")) continue;
+	if (!strcmp(name, "CELL.X0")) continue;
+	if (!strcmp(name, "CELL.Y0")) continue;
+
+	psMetadataItem *copy = psMetadataItemCopy(conceptItem); // Copy of the concept
+	psMetadataAddItem(target->concepts, copy, PS_LIST_TAIL, PS_META_REPLACE);
+	psFree(copy);               // Drop reference
     }
     psFree(conceptsIter);
 
     // Need to update CELL.TRIMSEC and CELL.BIASSEC if we changed the binning and they exist already.
-    if (xBin != 1 || yBin != 1) {
+    // XXX this code seems to be very similar to pmConceptsUpdate
+    if ((binning->nXbin != 1) || (binning->nYbin != 1)) {
+	bool mdok = false;
         psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // The trim section
         if (mdok && trimsec && !psRegionIsNaN(*trimsec)) {
-            binRegion(trimsec, xBin, yBin);
+            *trimsec = psImageBinningSetRuffRegion(binning, *trimsec);
         }
         psList *biassecs = psMetadataLookupPtr(&mdok, target->concepts, "CELL.BIASSEC"); // The bias sections
@@ -238,5 +209,5 @@
             while ((biassec = psListGetAndIncrement(biassecsIter))) {
                 if (!psRegionIsNaN(*biassec)) {
-                    binRegion(biassec, xBin, yBin);
+                    *biassec = psImageBinningSetRuffRegion(binning, *biassec);
                 }
             }
@@ -246,4 +217,5 @@
 
     // Need to update CELL.X0 and CELL.Y0 if we flipped
+    // XXX this section should probably use a common function consistent with psImageBinning
     if (xFlip) {
         int xZero = psMetadataLookupS32(NULL, source->concepts, "CELL.X0"); // CELL.X0 from source
@@ -254,9 +226,9 @@
         if (sourceBin == 0) {
             // Don't know the binning; assume it is unity
-            sourceBin = xBin;
-        }
-
-        psTrace("psModules.camera", 3, "CELL.X0: Before: %d After: %d\n", xZero,
-                xZero + (xSize - 1) * xParity * sourceBin);
+            sourceBin = binning->nXbin;
+        }
+
+	// XXX make sure this is consistent with the psImageBinning
+        psTrace("psModules.camera", 3, "CELL.X0: Before: %d After: %d\n", xZero, xZero + (xSize - 1) * xParity * sourceBin);
         psTrace("psModules.camera", 9, "(xParity: %d xBin: %d numCols: %d)\n", xParity, sourceBin, xSize);
 
@@ -277,9 +249,8 @@
         if (sourceBin == 0) {
             // Don't know the binning; assume it is unity
-            sourceBin = yBin;
-        }
-
-        psTrace("psModules.camera", 3, "CELL.Y0: Before: %d After: %d\n", yZero,
-                yZero + (ySize - 1) * yParity * sourceBin);
+            sourceBin = binning->nYbin;
+        }
+
+        psTrace("psModules.camera", 3, "CELL.Y0: Before: %d After: %d\n", yZero, yZero + (ySize - 1) * yParity * sourceBin);
         psTrace("psModules.camera", 9, "(yParity: %d yBin: %d numRows: %d)\n", yParity, sourceBin, ySize);
 
@@ -295,4 +266,5 @@
 
     // Update the binning concepts
+    // XXX this should probably be done with a common function using psImageBinning
     psMetadataItem *binItem = psMetadataLookup(target->concepts, "CELL.XBIN");
     binItem->data.S32 *= xBin;
@@ -314,5 +286,5 @@
     pmHDU *targetPHU = findBlankPHU(target); // The target PHU
     if (targetPHU && targetPHU != targetHDU) {
-//        pmHDU *sourcePHU = pmHDUGetHighest(source->parent->parent, source->parent, source); // A source HDU
+	// pmHDU *sourcePHU = pmHDUGetHighest(source->parent->parent, source->parent, source); // A source HDU
         pmHDU *sourcePHU = findBlankPHU(source); // The target PHU
         if (sourcePHU && sourcePHU->header) {
@@ -321,4 +293,5 @@
     }
 
+    psFree (binning);
     target->data_exists = true;
     return true;
Index: trunk/psModules/src/camera/pmFPAMosaic.c
===================================================================
--- trunk/psModules/src/camera/pmFPAMosaic.c	(revision 12585)
+++ trunk/psModules/src/camera/pmFPAMosaic.c	(revision 12589)
@@ -382,104 +382,7 @@
 }
 
-// Mosaic multiple images, with flips, binning and offsets
-static psImage *imageMosaic(const psArray *source, // Images to splice in
-                            const psVector *xFlip, const psVector *yFlip, // Need to flip x and y?
-                            const psVector *xBinSource, // Binning in x of source images
-                            const psVector *yBinSource, // Binning in y of source images
-                            int xBinTarget, int yBinTarget, // Binning in x and y of target images
-                            const psVector *x0, const psVector *y0 // Offsets for source images on target
-                           )
-{
-    assert(source);
-    assert(xFlip && xFlip->type.type == PS_TYPE_U8);
-    assert(yFlip && yFlip->type.type == PS_TYPE_U8);
-    assert(xBinSource && xBinSource->type.type == PS_TYPE_S32);
-    assert(yBinSource && yBinSource->type.type == PS_TYPE_S32);
-    assert(x0 && x0->type.type == PS_TYPE_S32);
-    assert(y0 && y0->type.type == PS_TYPE_S32);
-    assert(xFlip->n == source->n);
-    assert(yFlip->n == source->n);
-    assert(xBinSource->n == source->n);
-    assert(yBinSource->n == source->n);
-    assert(x0->n == source->n);
-    assert(y0->n == source->n);
-
-    if (source->n == 0) {
-        return NULL;
-    }
-
-    // Get the maximum extent of the mosaic image
-    int xMin = INT_MAX;
-    int xMax = - INT_MAX;
-    int yMin = INT_MAX;
-    int yMax = - INT_MAX;
-    psElemType type = 0;
-    int numImages = 0;                  // Number of images
-    psTrace("psModules.camera", 3, "Mosaicking %ld cells.\n", source->n);
-    for (int i = 0; i < source->n; i++) {
-        psImage *image = source->data[i]; // The image of interest
-        if (!image) {
-            continue;
-        }
-        numImages++;
-
-        // Only implemented for F32 and U8 images so far.
-        assert(image->type.type == PS_TYPE_F32 || image->type.type == PS_TYPE_U8);
-        // All input types must be the same
-        if (type == 0) {
-            type = image->type.type;
-        }
-        assert(type == image->type.type);
-
-        // Size of cell in x and y
-        int xParity = xFlip->data.U8[i] ? -1 : 1;
-        int yParity = yFlip->data.U8[i] ? -1 : 1;
-        psTrace("psModules.camera", 5, "Extent of cell %d: %d -> %d , %d -> %d\n", i, x0->data.S32[i],
-                x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols, y0->data.S32[i],
-                y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows);
-
-        COMPARE(x0->data.S32[i], xMin, xMax);
-        COMPARE(y0->data.S32[i], yMin, yMax);
-        // Subtract the parity to get the inclusive limit (not exclusive)
-        COMPARE(x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols - xParity, xMin, xMax);
-        COMPARE(y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows - yParity, yMin, yMax);
-    }
-    if (numImages == 0) {
-        return NULL;
-    }
-
-    // Set up the image
-    // Since both upper and lower values are inclusive, we need to add one to the size
-    float xSize = (float)(xMax - xMin + 1) / (float)xBinTarget;
-    if (xSize - (int)xSize > 0) {
-        xSize += 1;
-    }
-    float ySize = (float)(yMax - yMin + 1) / (float)yBinTarget;
-    if (ySize - (int)ySize > 0) {
-        ySize += 1;
-    }
-
-    psTrace("psModules.camera", 3, "Spliced image will be %dx%d\n", (int)xSize, (int)ySize);
-    psImage *mosaic = psImageAlloc((int)xSize, (int)ySize, type); // The mosaic image
-    psImageInit(mosaic, 0);
-
-    // Next pass through the images to do the mosaicking
-    for (int i = 0; i < source->n; i++) {
-        psImage *image = source->data[i]; // The image of interest
-        if (!image) {
-            continue;
-        }
-        int xParity = xFlip->data.U8[i] ? -1 : 1; // Parity difference, in x
-        int yParity = yFlip->data.U8[i] ? -1 : 1; // Parity difference, in y
-        int xTargetBase = (x0->data.S32[i] - xMin) / xBinTarget; // The base x position in the target frame
-        int yTargetBase = (y0->data.S32[i] - yMin) / yBinTarget; // The base y position in the target frame
-        if (xBinSource->data.S32[i] == xBinTarget && yBinSource->data.S32[i] == yBinTarget &&
-                xFlip->data.U8[i] == 0 && yFlip->data.U8[i] == 0) {
-            // Let someone else do the hard work
-            psImageOverlaySection(mosaic, image, xTargetBase, yTargetBase, "+");
-        } else if (xBinSource->data.S32[i] == xBinTarget && yBinSource->data.S32[i] == yBinTarget) {
-            // There's a difference with the parities, but we don't have to worry about binning
-
-            #define COPY_WITH_PARITY_DIFFERENCE(TYPE) \
+// supporting macros used by imageMosaic()
+// copy pixels without binning
+#define COPY_WITH_PARITY_DIFFERENCE(TYPE) \
         case PS_TYPE_##TYPE: { \
                 for (int y = 0; y < image->numRows; y++) { \
@@ -493,17 +396,8 @@
             break;
 
-            switch (type) {
-                COPY_WITH_PARITY_DIFFERENCE(F32);
-                COPY_WITH_PARITY_DIFFERENCE(U8);
-            default:
-                psAbort("Should never get here.\n");
-            }
-
-        } else {
-            // We have to do all of the hard work ourself
-
-            // In case the original image is binned but the mosaic is not, we need to fill in the
-            // values in the mosaic.
-            #define FILL_IN(TYPE) \
+// In case the original image is binned but the mosaic is not, we need to fill in the values in
+// the mosaic.  this operation should be replaced with a call to one of the functions defined
+// in psImageBinning
+#define FILL_IN(TYPE) \
         case PS_TYPE_##TYPE: \
             for (int y = 0; y < image->numRows; y++) { \
@@ -522,17 +416,132 @@
             break;
 
+// Mosaic multiple images, with flips, binning and offsets
+static psImage *imageMosaic(const psArray *source, // Images to splice in
+                            const psVector *xFlip, const psVector *yFlip, // Need to flip x and y?
+                            const psVector *xBinSource, // Binning in x of source images
+                            const psVector *yBinSource, // Binning in y of source images
+                            int xBinTarget, int yBinTarget, // Binning in x and y of target images
+                            const psVector *x0, const psVector *y0 // Offsets for source images on target
+                           )
+{
+    assert(source);
+    assert(xFlip && xFlip->type.type == PS_TYPE_U8);
+    assert(yFlip && yFlip->type.type == PS_TYPE_U8);
+    assert(xBinSource && xBinSource->type.type == PS_TYPE_S32);
+    assert(yBinSource && yBinSource->type.type == PS_TYPE_S32);
+    assert(x0 && x0->type.type == PS_TYPE_S32);
+    assert(y0 && y0->type.type == PS_TYPE_S32);
+    assert(xFlip->n == source->n);
+    assert(yFlip->n == source->n);
+    assert(xBinSource->n == source->n);
+    assert(yBinSource->n == source->n);
+    assert(x0->n == source->n);
+    assert(y0->n == source->n);
+
+    if (source->n == 0) {
+        return NULL;
+    }
+
+    // Get the maximum extent of the mosaic image
+    int xMin = INT_MAX;
+    int xMax = - INT_MAX;
+    int yMin = INT_MAX;
+    int yMax = - INT_MAX;
+    psElemType type = 0;
+    int numImages = 0;                  // Number of images
+    psTrace("psModules.camera", 3, "Mosaicking %ld cells.\n", source->n);
+    for (int i = 0; i < source->n; i++) {
+        psImage *image = source->data[i]; // The image of interest
+        if (!image) {
+            continue;
+        }
+        numImages++;
+
+        // Only implemented for F32 and U8 images so far.
+        assert(image->type.type == PS_TYPE_F32 || image->type.type == PS_TYPE_U8);
+        // All input types must be the same
+        if (type == 0) {
+            type = image->type.type;
+        }
+        assert(type == image->type.type);
+
+        // Size of cell in x and y
+        int xParity = xFlip->data.U8[i] ? -1 : 1;
+        int yParity = yFlip->data.U8[i] ? -1 : 1;
+        psTrace("psModules.camera", 5, "Extent of cell %d: %d -> %d , %d -> %d\n", i, x0->data.S32[i],
+                x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols, y0->data.S32[i],
+                y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows);
+
+        COMPARE(x0->data.S32[i], xMin, xMax);
+        COMPARE(y0->data.S32[i], yMin, yMax);
+        // Subtract the parity to get the inclusive limit (not exclusive)
+        COMPARE(x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols - xParity, xMin, xMax);
+        COMPARE(y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows - yParity, yMin, yMax);
+    }
+    if (numImages == 0) {
+        return NULL;
+    }
+
+    // Set up the image
+    // Since both upper and lower values are inclusive, we need to add one to the size
+    float xSize = (float)(xMax - xMin + 1) / (float)xBinTarget;
+    if (xSize - (int)xSize > 0) {
+        xSize += 1;
+    }
+    float ySize = (float)(yMax - yMin + 1) / (float)yBinTarget;
+    if (ySize - (int)ySize > 0) {
+        ySize += 1;
+    }
+
+    psTrace("psModules.camera", 3, "Spliced image will be %dx%d\n", (int)xSize, (int)ySize);
+    psImage *mosaic = psImageAlloc((int)xSize, (int)ySize, type); // The mosaic image
+    psImageInit(mosaic, 0);
+
+    // Next pass through the images to do the mosaicking
+    // XXX this function uses summing for the output: is this the right choice?
+    for (int i = 0; i < source->n; i++) {
+        psImage *image = source->data[i]; // The image of interest
+        if (!image) {
+            continue;
+        }
+        int xParity = xFlip->data.U8[i] ? -1 : 1; // Parity difference, in x
+        int yParity = yFlip->data.U8[i] ? -1 : 1; // Parity difference, in y
+        int xTargetBase = (x0->data.S32[i] - xMin) / xBinTarget; // The base x position in the target frame
+        int yTargetBase = (y0->data.S32[i] - yMin) / yBinTarget; // The base y position in the target frame
+
+	// in the first case, we are just copy a section pixel-by-pixel
+        if ((xBinSource->data.S32[i] == xBinTarget) && 
+	    (yBinSource->data.S32[i] == yBinTarget) && 
+	    (xFlip->data.U8[i] == 0) && 
+	    (yFlip->data.U8[i] == 0)) {
+            // Let someone else do the hard work
+            psImageOverlaySection(mosaic, image, xTargetBase, yTargetBase, "+");
+	    continue;
+        } 
+	
+	// in the second case, there's a difference with the parities, but we don't have to
+	// worry about binning
+	if (xBinSource->data.S32[i] == xBinTarget && yBinSource->data.S32[i] == yBinTarget) {
             switch (type) {
-                FILL_IN(F32);
-                FILL_IN(U8);
-            default:
+                COPY_WITH_PARITY_DIFFERENCE(F32);
+                COPY_WITH_PARITY_DIFFERENCE(U8);
+	      default:
                 psAbort("Should never get here.\n");
             }
-
-        } // Various difficulty levels
+	    continue;
+        } 
+	
+	// In the third case, the images are flipped and have different binnnig.
+	// We have to do all of the hard work ourselves
+	switch (type) {
+	    FILL_IN(F32);
+	    FILL_IN(U8);
+	  default:
+	    psAbort("Should never get here.\n");
+	}
     } // Iterating over images
 
     return mosaic;
 }
-
 
 // Add a cell and its various properties to the arrays
