const char * UsageLines [] = {
       "Usage: p6orient (output width) (output height)",
       " (pivot point #1) (pivot point #2)",
       "Shifts, rotates, and resizes input image so that",
       " the two input pivots are positioned as",
       " specified on the output image.",
       "Output is cropped or padded (with gray) as needed",
       " to reach specified output dimensions.",
       "Reads P6 PPM image from standard input,",
       " writes PPM image to standard output.",
       "Each pivot point is of the form:",
       " (input across),(input down)=(output across),(output down)",
       " where the leftmost pixels are (- width/2) across,",
       " and the topmost pixels are (- height/2) down.",
       "For example, 0,0=0,0 matches the center of the input image",
       " with the center of the output image.",
       "Pixels are moved, repeated, or skipped to produce output",
       " image.  No recalculating of pixel values is done.",
       "With appropriate choice of pivot points, p6orient can be",
       " used to pad, crop, or resize an image.",
       "For example: 0,0=0,0 0,1=0,1",
       " will keep the center as the center and not resize,",
       " if the supplied output dimensions are smaller than the input",
       " it will crop, and,",
       " if the supplied output dimensions are larger than the input",
       " it will pad.",
       "November 24, 2011.  Newest is at gopher -p users/julianbr sdf.org",
       };
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );

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


void OrientImage (
               char (* * InputPixels) [3],
               int InputWidth, int InputHeight,
               int OutputWidth, int OutputHeight,
               int InputPoint1Across, int InputPoint1Down,
               int OutputPoint1Across, int OutputPoint1Down,
               int InputPoint2Across, int InputPoint2Down,
               int OutputPoint2Across, int OutputPoint2Down)
       {
       char PaddingPixel [] = {200, 200, 200};
       int InputAcross, InputDown, OutputAcross, OutputDown;
       long int InputAcrossRemainder, InputDownRemainder;
       long int OutputDotOutput, OutputDotInput, OutputCrossInput;

       OutputDotOutput
                       = (OutputPoint2Across - OutputPoint1Across)
                               *(OutputPoint2Across - OutputPoint1Across)
                       + (OutputPoint2Down - OutputPoint1Down)
                               *(OutputPoint2Down - OutputPoint1Down);
       OutputDotInput
                       = (OutputPoint2Across - OutputPoint1Across)
                               *(InputPoint2Across - InputPoint1Across)
                       + (OutputPoint2Down - OutputPoint1Down)
                               *(InputPoint2Down - InputPoint1Down);
       OutputCrossInput
                       = (OutputPoint2Down - OutputPoint1Down)
                               *(InputPoint2Across - InputPoint1Across)
                       - (OutputPoint2Across - OutputPoint1Across)
                               *(InputPoint2Down - InputPoint1Down);

       printf ("P6\n");
       printf ("%d", OutputWidth);
       printf (" %d\n", OutputHeight);
       printf ("255\n");

       /* Start at pivot point 1 */
       OutputAcross = OutputPoint1Across;
       OutputDown = OutputPoint1Down;
       InputAcross = InputPoint1Across;
       InputAcrossRemainder = OutputDotOutput/2;
       InputDown = InputPoint1Down;
       InputDownRemainder = OutputDotOutput/2;

       /* Go to top edge */
       while (OutputDown < - OutputHeight/2) {
               InputAcrossRemainder += OutputCrossInput;
               while (InputAcrossRemainder < 0) {
                       InputAcrossRemainder += OutputDotOutput;
                       InputAcross--;
                       }
               while (InputAcrossRemainder >= OutputDotOutput) {
                       InputAcrossRemainder -= OutputDotOutput;
                       InputAcross++;
                       }
               InputDownRemainder += OutputDotInput;
               while (InputDownRemainder < 0) {
                       InputDownRemainder += OutputDotOutput;
                       InputDown--;
                       }
               while (InputDownRemainder >= OutputDotOutput) {
                       InputDownRemainder -= OutputDotOutput;
                       InputDown++;
                       }
               OutputDown++;
               }
       while (OutputDown > - OutputHeight/2) {
               InputAcrossRemainder -= OutputCrossInput;
               while (InputAcrossRemainder < 0) {
                       InputAcrossRemainder += OutputDotOutput;
                       InputAcross--;
                       }
               while (InputAcrossRemainder >= OutputDotOutput) {
                       InputAcrossRemainder -= OutputDotOutput;
                       InputAcross++;
                       }
               InputDownRemainder -= OutputDotInput;
               while (InputDownRemainder < 0) {
                       InputDownRemainder += OutputDotOutput;
                       InputDown--;
                       }
               while (InputDownRemainder >= OutputDotOutput) {
                       InputDownRemainder -= OutputDotOutput;
                       InputDown++;
                       }
               OutputDown--;
               }

       while (OutputDown < OutputHeight - OutputHeight/2) {
               /* Go to left edge */
               while (OutputAcross < - OutputWidth/2) {
                       InputAcrossRemainder += OutputDotInput;
                       while (InputAcrossRemainder < 0) {
                               InputAcrossRemainder += OutputDotOutput;
                               InputAcross--;
                               }
                       while (InputAcrossRemainder >= OutputDotOutput) {
                               InputAcrossRemainder -= OutputDotOutput;
                               InputAcross++;
                               }
                       InputDownRemainder -= OutputCrossInput;
                       while (InputDownRemainder < 0) {
                               InputDownRemainder += OutputDotOutput;
                               InputDown--;
                               }
                       while (InputDownRemainder >= OutputDotOutput) {
                               InputDownRemainder -= OutputDotOutput;
                               InputDown++;
                               }
                       OutputAcross++;
                       }
               while (OutputAcross > - OutputWidth/2) {
                       InputAcrossRemainder -= OutputDotInput;
                       while (InputAcrossRemainder < 0) {
                               InputAcrossRemainder += OutputDotOutput;
                               InputAcross--;
                               }
                       while (InputAcrossRemainder >= OutputDotOutput) {
                               InputAcrossRemainder -= OutputDotOutput;
                               InputAcross++;
                               }
                       InputDownRemainder += OutputCrossInput;
                       while (InputDownRemainder < 0) {
                               InputDownRemainder += OutputDotOutput;
                               InputDown--;
                               }
                       while (InputDownRemainder >= OutputDotOutput) {
                               InputDownRemainder -= OutputDotOutput;
                               InputDown++;
                               }
                       OutputAcross--;
                       }

               while (OutputAcross < OutputWidth - OutputWidth/2) {
                       if (
                                       InputAcross >= - InputWidth/2
                                       && InputAcross < InputWidth
                                               - InputWidth/2
                                       && InputDown >= - InputHeight/2
                                       && InputDown < InputHeight
                                               - InputHeight/2)
                               fwrite (
                                       InputPixels [InputDown
                                               + InputHeight/2]
                                               + InputAcross
                                               + InputWidth/2,
                                       sizeof (InputPixels [0] [0] ),
                                       1,
                                       stdout);
                       else {
                               fwrite (
                                       PaddingPixel,
                                       sizeof (InputPixels [0] [0] ),
                                       1,
                                       stdout);
                               }
                       InputAcrossRemainder += OutputDotInput;
                       while (InputAcrossRemainder < 0) {
                               InputAcrossRemainder += OutputDotOutput;
                               InputAcross--;
                               }
                       while (InputAcrossRemainder >= OutputDotOutput) {
                               InputAcrossRemainder -= OutputDotOutput;
                               InputAcross++;
                               }
                       InputDownRemainder -= OutputCrossInput;
                       while (InputDownRemainder < 0) {
                               InputDownRemainder += OutputDotOutput;
                               InputDown--;
                               }
                       while (InputDownRemainder >= OutputDotOutput) {
                               InputDownRemainder -= OutputDotOutput;
                               InputDown++;
                               }
                       OutputAcross++;
                       }
               InputAcrossRemainder += OutputCrossInput;
               while (InputAcrossRemainder < 0) {
                       InputAcrossRemainder += OutputDotOutput;
                       InputAcross--;
                       }
               while (InputAcrossRemainder >= OutputDotOutput) {
                       InputAcrossRemainder -= OutputDotOutput;
                       InputAcross++;
                       }
               InputDownRemainder += OutputDotInput;
               while (InputDownRemainder < 0) {
                       InputDownRemainder += OutputDotOutput;
                       InputDown--;
                       }
               while (InputDownRemainder >= OutputDotOutput) {
                       InputDownRemainder -= OutputDotOutput;
                       InputDown++;
                       }
               OutputDown++;
               }
       }


int ReadImage (
               char (* * * PixelsPtr) [3],
               int * WidthPtr, int * HeightPtr)
       {
       int i, j, maxval;

       PixelsPtr [0] = NULL;
       if (getchar () != 'P'
                       || getchar () != '6'
                       || getchar () != '\n'
                       || scanf ("%d", WidthPtr) < 1
                       || scanf ("%d", HeightPtr) < 1
                       || scanf ("%d", & maxval) < 1
                       || WidthPtr [0] < 1
                       || HeightPtr [0] < 1
                       || maxval != 255
                       || getchar () != '\n') {
               fprintf (stderr, "***p6orient: Improper input type, not");
               fprintf (stderr, " P6 ppm image with maxval=255.\n");
               return 0;
               }
       PixelsPtr [0] = malloc (HeightPtr [0]*sizeof (PixelsPtr [0] [0] ) );
       if (PixelsPtr [0] == NULL) {
               fprintf (stderr, "***p6orient: Not enough memory.\n");
               return 0;
               }
       for (i = 0; i < HeightPtr [0]; i++)
               PixelsPtr [0] [i] = NULL;
       for (i = 0; i < HeightPtr [0]; i++) {
               PixelsPtr [0] [i] = malloc (
                       WidthPtr [0]*sizeof (PixelsPtr [0] [0] [0] ) );
               if (PixelsPtr [0] [i] == NULL) {
                       fprintf (stderr, "***p6orient: Not enough memory.\n");
                       return 0;
                       }
               }
       for (i = 0; i < HeightPtr [0]; i++) {
               for (j = 0; j < WidthPtr [0]; j++)
                       memset (PixelsPtr [0] [i] [j], '.',
                                       sizeof (PixelsPtr [0] [0] [0] ) );
               }
       for (i = 0; i < HeightPtr [0]; i++) {
               for (j = 0; j < WidthPtr [0]; j++) {
                       if (fread (PixelsPtr [0] [i] [j],
                                       sizeof (PixelsPtr [0] [0] [0] ),
                                       1,
                                       stdin) < 1) {
                               fprintf (stderr, "***p6orient: Premature");
                               fprintf (stderr, " end of input image");
                               fprintf (stderr, " data.\n");
                               return 1; /* proceed anyway */
                               }
                       }
               }
       if (getchar () != EOF)
               fprintf (stderr, "***p6orient: Improper extra input data.\n");
       return 1;
       }


void ClosePixels (
               char (* * Pixels) [3],
               unsigned int height)
       {
       unsigned int i;

       if (Pixels != NULL) {
               for (i = 0; i < height; i++) {
                       if (Pixels [i] != NULL)
                               free (Pixels [i] );
                       }
               free (Pixels);
               }
       }


int ReadPivotPoint (
               char * point,
               int * InputAcrossPtr,
               int * InputDownPtr,
               int * OutputAcrossPtr,
               int * OutputDownPtr)
       {
       char * ptr;
       int negative;

       ptr = point;
       negative = 0;
       if (ptr [0] == '-') {
               negative = 1;
               ptr++;
               }
       InputAcrossPtr [0] = 0;
       while (ptr [0] >= '0' && ptr [0] <= '9') {
               InputAcrossPtr [0] = 10*InputAcrossPtr [0] + (ptr [0] - '0');
               ptr++;
               }
       if (negative)
               InputAcrossPtr [0] = - InputAcrossPtr [0];
       if (ptr [0] != ',') {
               fprintf (stderr, "***p6orient: Expecting comma, found");
               fprintf (stderr, " improper character: \"%s\".\n", point);
               return 0;
               }
       ptr++;
       negative = 0;
       if (ptr [0] == '-') {
               negative = 1;
               ptr++;
               }
       InputDownPtr [0] = 0;
       while (ptr [0] >= '0' && ptr [0] <= '9') {
               InputDownPtr [0] = 10*InputDownPtr [0] + (ptr [0] - '0');
               ptr++;
               }
       if (negative)
               InputDownPtr [0] = - InputDownPtr [0];
       if (ptr [0] != '=') {
               fprintf (stderr, "***p6orient: Expecting =, found improper");
               fprintf (stderr, " character: \"%s\".\n", point);
               return 0;
               }
       ptr++;
       negative = 0;
       if (ptr [0] == '-') {
               negative = 1;
               ptr++;
               }
       OutputAcrossPtr [0] = 0;
       while (ptr [0] >= '0' && ptr [0] <= '9') {
               OutputAcrossPtr [0] = 10*OutputAcrossPtr [0]
                               + (ptr [0] - '0');
               ptr++;
               }
       if (negative)
               OutputAcrossPtr [0] = - OutputAcrossPtr [0];
       if (ptr [0] != ',') {
               fprintf (stderr, "***p6orient: Expecting comma, found");
               fprintf (stderr, " improper character: \"%s\".\n", point);
               return 0;
               }
       ptr++;
       negative = 0;
       if (ptr [0] == '-') {
               negative = 1;
               ptr++;
               }
       OutputDownPtr [0] = 0;
       while (ptr [0] >= '0' && ptr [0] <= '9') {
               OutputDownPtr [0] = 10*OutputDownPtr [0] + (ptr [0] - '0');
               ptr++;
               }
       if (negative)
               OutputDownPtr [0] = - OutputDownPtr [0];
       if (ptr [0] != '\0') {
               fprintf (stderr, "***p6orient: Improper character(s) at");
               fprintf (stderr, " end: \"%s\".\n", point);
               return 0;
               }
       return 1;
       }


int main (int argc, char * argv [] )
       {
       char (* * InputPixels) [3];
       int InputWidth, InputHeight, OutputWidth, OutputHeight;
       int InputPoint1Across, InputPoint1Down;
       int InputPoint2Across, InputPoint2Down;
       int OutputPoint1Across, OutputPoint1Down;
       int OutputPoint2Across, OutputPoint2Down;
       int i, ok;

       if (argc < 2) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc == 5) {
               ok = 1;
               if (sscanf (argv [1], "%d", & OutputWidth) != 1
                               || OutputWidth < 1) {
                       fprintf (stderr, "***p6orient: Expecting output");
                       fprintf (stderr, " width,");
                       fprintf (stderr, " found \"%s\".\n", argv [1] );
                       ok = 0;
                       }
               if (sscanf (argv [2], "%d", & OutputHeight) != 1
                               || OutputHeight < 1) {
                       fprintf (stderr, "***p6orient: Expecting output");
                       fprintf (stderr, " height,");
                       fprintf (stderr, " found \"%s\".\n", argv [2] );
                       }
               if (!ReadPivotPoint (
                               argv [3],
                               & InputPoint1Across, & InputPoint1Down,
                               & OutputPoint1Across, & OutputPoint1Down) )
                       ok = 0;
               if (!ReadPivotPoint (
                               argv [4],
                               & InputPoint2Across, & InputPoint2Down,
                               & OutputPoint2Across, & OutputPoint2Down) )
                       ok = 0;
               if (ok) {
                       if (OutputPoint1Across == OutputPoint2Across
                               && OutputPoint1Down == OutputPoint2Down) {
                               fprintf (stderr, "***p6orient: Both pivot");
                               fprintf (stderr, " points are at same");
                               fprintf (stderr, " output location:");
                               fprintf (stderr, " %d,", OutputPoint1Across);
                               fprintf (stderr, "%d\n", OutputPoint1Down);
                               ok = 0;
                               }
                       }
               if (ok) {
                       if (ReadImage (
                                       & InputPixels,
                                       & InputWidth, & InputHeight) ) {
                               OrientImage (
                                       InputPixels,
                                       InputWidth, InputHeight,
                                       OutputWidth, OutputHeight,
                                       InputPoint1Across, InputPoint1Down,
                                       OutputPoint1Across, OutputPoint1Down,
                                       InputPoint2Across, InputPoint2Down,
                                       OutputPoint2Across, OutputPoint2Down
                                       );
                               ClosePixels (InputPixels, InputHeight);
                               }
                       else
                               ClosePixels (InputPixels, InputHeight);
                       }
               }
       else {
               fprintf (stderr, "Usage: p6orient");
               fprintf (stderr, " (output width)");
               fprintf (stderr, " (output height)");
               fprintf (stderr, " (pivot point #1)");
               fprintf (stderr, " (pivot point #2)\n");
               }
       return 0;
       }