Index: trunk/ppStats/src/ppStatsReadout.c
===================================================================
--- trunk/ppStats/src/ppStatsReadout.c	(revision 14430)
+++ trunk/ppStats/src/ppStatsReadout.c	(revision 15374)
@@ -119,35 +119,80 @@
         bool get_nSatPixels = false;
         bool get_fSatPixels = false;
+        bool findNumGood = false;       // Return the number of good pixels?
+        bool findFracGood = false;      // Return the fraction of good pixels?
 
         psListIterator *iterator = psListIteratorAlloc(data->summary, PS_LIST_HEAD, false);
         psString choice;
         while ((choice = psListGetAndIncrement(iterator))) {
-            if (!strcasecmp (choice, "SAT_PIXEL_NUM"))  get_nSatPixels = true;
-            if (!strcasecmp (choice, "SAT_PIXEL_FRAC")) get_fSatPixels = true;
-        }
-
-        if (!get_nSatPixels && !get_fSatPixels) {
-            goto readoutDone;
-        }
-
-        // Get the "concepts" of interest
-        float saturation = psMetadataLookupF32(&mdok, readout->parent->concepts, "CELL.SATURATION"); // Saturation level
-        if (!mdok || isnan(saturation)) {
-            psLogMsg(__func__, PS_LOG_WARN, "CELL.SATURATION is not set --- unable to measure N_SAT_PIXELS.\n");
-            if (get_nSatPixels) psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0, NULL, 0);
-            if (get_fSatPixels) psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0, NULL, NAN);
-            goto readoutDone;
-        }
-
-        int nSatPixels = 0;
-        for (int j = 0; j < readout->image->numRows; j++) {
-            for (int i = 0; i < readout->image->numCols; i++) {
-                if (readout->image->data.F32[j][i] >= saturation) {
-                    nSatPixels ++;
+            if (!strcasecmp(choice, "SAT_PIXEL_NUM"))  get_nSatPixels = true;
+            if (!strcasecmp(choice, "SAT_PIXEL_FRAC")) get_fSatPixels = true;
+            if (!strcasecmp(choice, "GOOD_PIXEL_NUM")) findNumGood = true;
+            if (!strcasecmp(choice, "GOOD_PIXEL_NUM")) findFracGood = true;
+        }
+
+        if (get_nSatPixels || get_fSatPixels) {
+            // Find the saturation point
+            float saturation = psMetadataLookupF32(&mdok, readout->parent->concepts,
+                                                   "CELL.SATURATION"); // Saturation level
+            if (!mdok || isnan(saturation)) {
+                psWarning("CELL.SATURATION is not set --- unable to measure N_SAT_PIXELS.\n");
+                if (get_nSatPixels) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0, NULL, 0);
+                }
+                if (get_fSatPixels) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0, NULL, NAN);
+                }
+            } else {
+                int nSatPixels = 0;
+                for (int j = 0; j < readout->image->numRows; j++) {
+                    for (int i = 0; i < readout->image->numCols; i++) {
+                        if (readout->image->data.F32[j][i] >= saturation) {
+                            nSatPixels ++;
+                        }
+                    }
+                }
+                if (get_nSatPixels) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0,
+                                     "Number of saturated pixels", nSatPixels);
+                }
+                if (get_fSatPixels) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0,
+                                     "Fraction of saturated pixels",
+                                     nSatPixels / (float)(readout->image->numRows * readout->image->numCols));
                 }
             }
         }
-        if (get_nSatPixels) psMetadataAddS32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_NUM", 0, NULL, nSatPixels);
-        if (get_fSatPixels) psMetadataAddF32(readoutResults, PS_LIST_TAIL, "SAT_PIXEL_FRAC", 0, NULL, nSatPixels / (double)(readout->image->numRows * readout->image->numCols));
+
+        if (findNumGood || findFracGood) {
+            if (!readout->mask || data->maskVal == 0) {
+                psWarning("Number or fraction of good pixels requested, but no mask provided");
+                if (findNumGood) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_NUM", 0, NULL, 0);
+                }
+                if (findFracGood) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_FRAC", 0, NULL, NAN);
+                }
+            } else {
+                int numBad = 0;            // Number of bad pixels
+                for (int j = 0; j < readout->mask->numRows; j++) {
+                    for (int i = 0; i < readout->mask->numCols; i++) {
+                        if (readout->mask->data.PS_TYPE_MASK_DATA[j][i] & data->maskVal) {
+                            numBad++;
+                        }
+                    }
+                }
+
+                int numTotal = readout->mask->numRows * readout->mask->numCols; // Total number of pixels
+                int numGood = numTotal - numBad; // Number of good pixels
+                if (findNumGood) {
+                    psMetadataAddS32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_NUM", 0,
+                                     "Number of good pixels", numGood);
+                }
+                if (findFracGood) {
+                    psMetadataAddF32(readoutResults, PS_LIST_TAIL, "GOOD_PIXEL_FRAC", 0,
+                                     "Fraction of good pixels", numGood / (float)numTotal);
+                }
+            }
+        }
     }
 
