const char * UsageLines [] = {
       "Usage: p6unshade",
       "Reads P6 PPM from standard input.",
       "Attempts to remove slow variations in brightness by",
       "comparing each pixel with the average of nearby pixels.",
       "Writes PPM of same dimensions to standard output.",
       "May 25, 2011.  Newest is at gopher -p users/julianbr sdf.org",
       };
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );

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


const int Weights [] [7] = {
       {0, 1, 1, 1, 1, 1, 0},
       {1, 1, 2, 2, 2, 1, 1},
       {1, 2, 2, 2, 2, 2, 1},
       {1, 2, 2, 2, 2, 2, 1},
       {1, 2, 2, 2, 2, 2, 1},
       {1, 1, 2, 2, 2, 1, 1},
       {0, 1, 1, 1, 1, 1, 0},
       };
const int NumRows = sizeof (Weights)/sizeof (Weights [0] );
const int NumColumns = sizeof (Weights [0] )/sizeof (Weights [0] [0] );


void ReadRow (int width, unsigned char * * Rows, int * EndOfInputPtr)
       {
       int i;

       free (Rows [0] );
       i = 0;
       while (i < NumRows - 1) {
               Rows [i] = Rows [i + 1];
               i++;
               }
       Rows [i] = NULL;
       while (i > NumRows/2 && Rows [i - 1] == NULL)
               i--;
       while (!EndOfInputPtr [0] && i < NumRows) {
               Rows [i] = malloc (3*width);
               if (Rows [i] == NULL) {
                       fprintf (stderr, "***p6unshade:");
                       fprintf (stderr, " Not enough memory.\n");
                       EndOfInputPtr [0] = 1;
                       }
               else if (fread (Rows [i], 3*width, 1, stdin) < 1)
                       EndOfInputPtr [0] = 1;
               i++;
               }
       }


void UnshadePixel (int across, int width, unsigned char * * Rows)
       {
       unsigned long int weight, weighted;
       unsigned char average;
       int row, column, a, value, pixel;

       weight = 0;
       weighted = 0;
       for (row = 0; row < NumRows; row++) {
               if (Rows [row] != NULL) {
                       for (column = 0; column < NumColumns; column++) {
                               a = across + column - NumColumns/2;
                               if (a > -1 && a < width) {
                                       weight += Weights [row] [column];
                                       weighted += Weights [row] [column]
                                               *Rows [row] [3*a + 0]
                                               + Weights [row] [column]
                                               *Rows [row] [3*a + 1]
                                               + Weights [row] [column]
                                               *Rows [row] [3*a + 2];
                                       }
                               }
                       }
               }
       if (weight > 0 && Rows [NumRows/2] != NULL) {
               average = (weighted + 3*weight/2) / (3*weight);
               for (pixel = 0; pixel < 3; pixel++) {
                       value = Rows [NumRows/2] [3*across + pixel]
                               + 128 - average;
                       if (value < 0)
                               value = 0;
                       if (value > 255)
                               value = 255;
                       putchar (value);
                       }
               }
       else {
               for (pixel = 0; pixel < 3; pixel++)
                       putchar (128);
               }
       }


void UnshadeFile (void)
       {
       unsigned char * * Rows;
       int across, down, width, height, maxval, EndOfInput, i;

       if (getchar () != 'P' || getchar () != '6') {
               fprintf (stderr, "***p6unshade: Improper");
               fprintf (stderr, " input type, not P6.\n");
               }
       else if (scanf ("%d%d%d", & width, & height, & maxval) < 3) {
               fprintf (stderr, "***p6unshade: Improper");
               fprintf (stderr, " or incomplete header.\n");
               }
       else if (maxval > 255) {
               fprintf (stderr, "***p6unshade: Found maxval %d.", maxval);
               fprintf (stderr, "  Maxvals above 255 are not implemented.\n");
               }
       else if (getchar () == EOF) {
               fprintf (stderr, "***p6unshade:");
               fprintf (stderr, " End of input before image.\n");
               }
       else {
               Rows = malloc (NumRows*sizeof (Rows [0] ) );
               if (Rows == NULL) {
                       fprintf (stderr, "***p6unshade:");
                       fprintf (stderr, " Not enough memory.\n");
                       }
               else {
                       printf ("P6\n");
                       printf ("%d %d\n255\n", width, height);
                       for (i = 0; i < NumRows; i++)
                               Rows [i] = NULL;
                       EndOfInput = 0;
                       for (down = 0; down < height; down++) {
                               ReadRow (width, Rows, & EndOfInput);
                               for (across = 0; across < width; across++)
                                       UnshadePixel (across, width, Rows);
                               }
                       for (i = 0; i < NumRows; i++)
                               free (Rows [i] );
                       free (Rows);
                       }
               }
       }


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

       if (argc > 1) {
               fprintf (stderr, "***p6unshade: Unrecognized");
               fprintf (stderr, " \"%s\".\n", argv [1] );
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else
               UnshadeFile ();
       return 0;
       }