const char * UsageLines [] = {
       "Usage: p4erase (P4 pbm eraser mask file for erasing)",
       "Reads P6 ppm image from standard input.  For '1' pixels",
       "on the mask file, replaces the input with the average of",
       "adjacent input pixels.  For '0' pixels on the mask file,",
       "writes input pixels to output unchanged.",
       "Writes PPM image to standard output.",
       "Input and eraser mask file must be same dimensions.",
       "May 12, 2011.  Newest is at gopher -p users/julianbr sdf.org",
       };
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


unsigned char * * ReadEraser (
               int * EraserWidthPtr,
               int * EraserHeightPtr,
               FILE * EraserHdl)
       {
       unsigned char * * EraserLines;
       int c, i;

       if (fgetc (EraserHdl) != 'P'
                       || fgetc (EraserHdl) != '4'
                       || fscanf (EraserHdl, "%d", EraserWidthPtr) != 1
                       || fscanf (EraserHdl, "%d", EraserHeightPtr) != 1
                       || fgetc (EraserHdl) != '\n'
                       || EraserWidthPtr [0] < 1
                       || EraserHeightPtr [0] < 1) {
               fprintf (stderr, "***p4erase: Improper eraser,");
               fprintf (stderr, " must be P4 PBM.\n");
               EraserLines = NULL;
               return EraserLines;
               }
       EraserLines = malloc (EraserHeightPtr [0]*sizeof (EraserLines [0] ) );
       if (EraserLines == NULL) {
               fprintf (stderr, "***p4erase: Not enough memory.\n");
               return EraserLines;
               }
       i = 0;
       while (i < EraserHeightPtr [0] ) {
               EraserLines [i] = malloc (1 + (EraserWidthPtr [0] - 1)/8);
               if (EraserLines [i] == NULL) {
                       while (i > 0) {
                               i--;
                               free (EraserLines [i] );
                               }
                       free (EraserLines);
                       EraserLines = NULL;
                       fprintf (stderr, "***p4erase: Not enough memory.\n");
                       return EraserLines;
                       }
               i++;
               }
       i = 0;
       while (i < EraserHeightPtr [0] ) {
               if (fread (EraserLines [i], 1 + (EraserWidthPtr [0] - 1)/8,
                                       1, EraserHdl) < 1) {
                       for (i = 0; i < EraserHeightPtr [0]; i++)
                               free (EraserLines [i] );
                       free (EraserLines);
                       EraserLines = NULL;
                       fprintf (stderr, "***p4erase: Unexpected end");
                       fprintf (stderr, " of eraser image data.\n");
                       return EraserLines;
                       }
               i++;
               }
       c = fgetc (EraserHdl);
       if (c != EOF) {
               for (i = 0; i < EraserHeightPtr [0]; i++)
                       free (EraserLines [i] );
               free (EraserLines);
               EraserLines = NULL;
               fprintf (stderr, "***p4erase: Excess eraser");
               fprintf (stderr, " image data.\n");
               return EraserLines;
               }
       return EraserLines;
       }


unsigned char * * ReadInput (int * InputWidthPtr, int * InputHeightPtr)
       {
       unsigned char * * InputLines;
       int InputDepth;
       int c, i;

       if (getchar () != 'P'
                       || getchar () != '6'
                       || scanf ("%d", InputWidthPtr) != 1
                       || scanf ("%d", InputHeightPtr) != 1
                       || scanf ("%d", & InputDepth) != 1
                       || getchar () != '\n'
                       || InputWidthPtr [0] < 1
                       || InputHeightPtr [0] < 1) {
               fprintf (stderr, "***p4erase: Improper input,");
               fprintf (stderr, " must be P6 PPM.\n");
               InputLines = NULL;
               return InputLines;
               }
       InputLines = malloc (InputHeightPtr [0]*sizeof (InputLines [0] ) );
       if (InputLines == NULL) {
               fprintf (stderr, "***p4erase: Not enough memory.\n");
               return InputLines;
               }
       i = 0;
       while (i < InputHeightPtr [0] ) {
               InputLines [i] = malloc (3*InputWidthPtr [0] );
               if (InputLines [i] == NULL) {
                       while (i > 0) {
                               i--;
                               free (InputLines [i] );
                               }
                       free (InputLines);
                       InputLines = NULL;
                       fprintf (stderr, "***p4erase: Not enough memory.\n");
                       return InputLines;
                       }
               i++;
               }
       i = 0;
       while (i < InputHeightPtr [0] ) {
               if (fread (InputLines [i], 3*InputWidthPtr [0],
                                       1, stdin) < 1) {
                       for (i = 0; i < InputHeightPtr [0]; i++)
                               free (InputLines [i] );
                       free (InputLines);
                       InputLines = NULL;
                       fprintf (stderr, "***p4erase: Unexpected end");
                       fprintf (stderr, " of input image data.\n");
                       return InputLines;
                       }
               i++;
               }
       c = getchar ();
       if (c != EOF) {
               for (i = 0; i < InputHeightPtr [0]; i++)
                       free (InputLines [i] );
               free (InputLines);
               InputLines = NULL;
               fprintf (stderr, "***p4erase: Excess input");
               fprintf (stderr, " image data.\n");
               }
       return InputLines;
       }


void EraseData (
               unsigned char * * StillToErase,
               unsigned char * * InputLines,
               int width,
               int height)
       {
       unsigned char * * NextToErase;
       unsigned int r, g, b;
       int AnyMorePixels, i, j, NumAdjacent, AllAreErased;

       NextToErase = malloc (height*sizeof (NextToErase [0] ) );
       if (NextToErase == NULL) {
               fprintf (stderr, "***p4erase: Not enough memory.\n");
               return;
               }
       i = 0;
       while (i < height) {
               NextToErase [i] = malloc (1 + (width - 1)/8);
               if (NextToErase [i] == NULL) {
                       while (i > 0) {
                               i--;
                               free (NextToErase [i] );
                               }
                       free (NextToErase);
                       fprintf (stderr, "***p4erase: Not enough memory.\n");
                       return;
                       }
               i++;
               }
       for (i = 0; i < height; i++)
               memcpy (NextToErase [i], StillToErase [i],
                               1 + (width - 1)/8);
       AllAreErased = 0;
       AnyMorePixels = 1;
       while (AnyMorePixels && !AllAreErased) {
               AllAreErased = 1;
               AnyMorePixels = 0;
               for (i = 0; i < height; i++) {
                       for (j = 0; j < width; j++) {
                               if ((StillToErase [i] [j/8]
                                                       & (1 << (7 - j%8) ) )
                                               > 0) {
                                       r = 0;
                                       g = 0;
                                       b = 0;
                                       NumAdjacent = 0;
                                       if (i > 0 && j > 0
                                       && (StillToErase [i - 1] [(j - 1)/8]
                                       & (1 << (7 - (j - 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i - 1] [3*(j - 1) + 0];
                                               g += InputLines
                                               [i - 1] [3*(j - 1) + 1];
                                               b += InputLines
                                               [i - 1] [3*(j - 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (i > 0
                                       && (StillToErase [i - 1] [j/8]
                                       & (1 << (7 - j%8) ) ) == 0) {
                                               r += InputLines
                                               [i - 1] [3*j + 0];
                                               g += InputLines
                                               [i - 1] [3*j + 1];
                                               b += InputLines
                                               [i - 1] [3*j + 2];
                                               NumAdjacent++;
                                               }
                                       if (i > 0 && j < width - 1
                                       && (StillToErase [i - 1] [(j + 1)/8]
                                       & (1 << (7 - (j + 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i - 1] [3*(j + 1) + 0];
                                               g += InputLines
                                               [i - 1] [3*(j + 1) + 1];
                                               b += InputLines
                                               [i - 1] [3*(j + 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (j > 0
                                       && (StillToErase [i] [(j - 1)/8]
                                       & (1 << (7 - (j - 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i] [3*(j - 1) + 0];
                                               g += InputLines
                                               [i] [3*(j - 1) + 1];
                                               b += InputLines
                                               [i] [3*(j - 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (j < width - 1
                                       && (StillToErase [i] [(j + 1)/8]
                                       & (1 << (7 - (j + 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i] [3*(j + 1) + 0];
                                               g += InputLines
                                               [i] [3*(j + 1) + 1];
                                               b += InputLines
                                               [i] [3*(j + 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (i < height - 1 && j > 0
                                       && (StillToErase [i + 1] [(j - 1)/8]
                                       & (1 << (7 - (j - 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i + 1] [3*(j - 1) + 0];
                                               g += InputLines
                                               [i + 1] [3*(j - 1) + 1];
                                               b += InputLines
                                               [i + 1] [3*(j - 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (i < height - 1
                                       && (StillToErase [i + 1] [j/8]
                                       & (1 << (7 - j%8) ) ) == 0) {
                                               r += InputLines
                                               [i + 1] [3*j + 0];
                                               g += InputLines
                                               [i + 1] [3*j + 1];
                                               b += InputLines
                                               [i + 1] [3*j + 2];
                                               NumAdjacent++;
                                               }
                                       if (i < height - 1 && j < width - 1
                                       && (StillToErase [i + 1] [(j + 1)/8]
                                       & (1 << (7 - (j + 1)%8) ) ) == 0) {
                                               r += InputLines
                                               [i + 1] [3*(j + 1) + 0];
                                               g += InputLines
                                               [i + 1] [3*(j + 1) + 1];
                                               b += InputLines
                                               [i + 1] [3*(j + 1) + 2];
                                               NumAdjacent++;
                                               }
                                       if (NumAdjacent == 0)
                                               AnyMorePixels = 1;
                                       else {
                                               NextToErase [i] [j/8]
                                                       &= ~(1 << (7 - j%8) );
                                               InputLines [i] [3*j + 0]
                                                       = (r + NumAdjacent/2)
                                                       /NumAdjacent;
                                               InputLines [i] [3*j + 1]
                                                       = (g + NumAdjacent/2)
                                                       /NumAdjacent;
                                               InputLines [i] [3*j + 2]
                                                       = (b + NumAdjacent/2)
                                                       /NumAdjacent;
                                               }
                                       }
                               else
                                       AllAreErased = 0;
                               }
                       }
               if (AnyMorePixels) {
                       for (i = 0; i < height; i++)
                               memcpy (StillToErase [i], NextToErase [i],
                                               1 + (width - 1)/8);
                       }
               }
       if (AllAreErased) {
               fprintf (stderr, "***p4erase: Cannot erase the entire");
               fprintf (stderr, " input image.\n");
               }
       for (i = 0; i < height; i++)
               free (NextToErase [i] );
       free (NextToErase);
       }


void WriteOutput (
               unsigned char * * outputLines,
               int outputWidth,
               int outputHeight)
       {
       int i;

       printf ("P6\n");
       printf ("%d %d\n", outputWidth, outputHeight);
       printf ("%d\n", 255);
       for (i = 0; i < outputHeight; i++)
               fwrite (outputLines [i], 3*outputWidth, 1, stdout);
       }


void Erase (char * EraserFile)
       {
       FILE * EraserHdl;
       unsigned char * * EraserLines, * * InputLines;
       int EraserWidth, EraserHeight, InputWidth, InputHeight, i;

       EraserLines = NULL;
       EraserHdl = fopen (EraserFile, "rb");
       if (EraserHdl == NULL) {
               fprintf (stderr, "***p4erase:");
               fprintf (stderr, " \"%s\"", EraserFile);
               fprintf (stderr, " not found.\n");
               }
       else {
               EraserLines = ReadEraser (
                               & EraserWidth,
                               & EraserHeight,
                               EraserHdl);
               fclose (EraserHdl);
               }
       InputLines = ReadInput (& InputWidth, & InputHeight);
       if (EraserLines != NULL && InputLines != NULL) {
               if (EraserWidth != InputWidth || EraserHeight != InputHeight) {
                       fprintf (stderr, "***p4erase: Eraser dimensions");
                       fprintf (stderr, " %dx%d", EraserWidth, EraserHeight);
                       fprintf (stderr, " don't match input dimensions");
                       fprintf (stderr, " %dx%d.\n", InputWidth, InputHeight);
                       }
               else {
                       EraseData (
                                       EraserLines,
                                       InputLines,
                                       EraserWidth,
                                       EraserHeight);
                       WriteOutput (
                                       InputLines,
                                       EraserWidth,
                                       EraserHeight);
                       }
               }
       if (EraserLines != NULL) {
               for (i = 0; i < EraserHeight; i++)
                       free (EraserLines [i] );
               free (EraserLines);
               }
       if (InputLines != NULL) {
               for (i = 0; i < InputHeight; i++)
                       free (InputLines [i] );
               free (InputLines);
               }
       }


int main (int argc, char * argv [] )
       {
       int i;

       if (argc < 2) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc == 2)
               Erase (argv [1] );
       else {
               fprintf (stderr, "Usage: p4erase");
               fprintf (stderr, " (eraser file)\n");
               }
       return 0;
       }