/* A parallel program using POSIX threads to invert an image
 *
 * Usage: pinvert input output
 *
 * Jerod Weinman
 * 12 June 2008
 */

#include "pbmio.h"
#include <stdlib.h>
#include <pthread.h>

#define NUM_THREADS 512

typedef struct 
{
     pbm_t *img;
     int threadNum;
} invertThr_t;

void* invertThread(void *invertParam);
void threadedInvert(pbm_t *img);

/* Threaded function that performs the inversion on a subset of image rows */
void* invertThread(void *invertParam)
{
     pbm_t *img;
     int threadNum;

     /* Unpack parameters for easier reference */
     img = ((invertThr_t*)invertParam)->img;
     threadNum = ((invertThr_t*)invertParam)->threadNum;

     printf("thread %d",threadNum);

     /* Do the inversion, but starting at our threadNumber row, and
      * doing only one every NUM_THREAD rows.
      */
     int i,j;
     for (i=threadNum ; i < img->rows ; i+=NUM_THREADS)
          for (j=0 ; j < img->cols ; j++)
               img->bits[i][j] = !img->bits[i][j];

					return NULL;
}

/* Set up and run parallel threads to flip every bit in a bitmap image */
void threadedInvert(pbm_t *img)
{
     int res;                        /* Result of starting thread */
     pthread_t threads[NUM_THREADS]; /* The threads */
     pthread_attr_t attr;            /* Thread attributes */
     invertThr_t invParams[NUM_THREADS]; /* Packaged thread params */

     /* Initialize and set thread detached attribute as joinable */
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

     /* Package parameters for threads */
     int i;
     for (i=0 ; i<NUM_THREADS ; i++)
     {
          invParams[i].img = img;
          invParams[i].threadNum = i;
     }

     /* Create threads */
     for (i=0 ; i<NUM_THREADS ; i++)
     {
          
          res = pthread_create(&threads[i], &attr, invertThread, 
                               (void*)&(invParams[i]));

          if (res!=0)
          {
               fprintf(stderr,"Could not create thread\n");
               exit (1);
          }
     }

     /* Wait for all threads to complete */
     for (i=0 ; i<NUM_THREADS ; i++)
     {
          pthread_join(threads[i],(void*)res);
     }
     
}

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

     pbm_t im; /* The image */
     
     /* Check arguments */
     if (argc!=3)
     {
          fprintf(stderr,"Usage: %s input output",argv[0]);
          exit (1);
     }
     
     pbmread(argv[1],&im); /* Read the image */
     
     threadedInvert(&im); /* Do a multi-threaded inversion */

     pbmwrite(argv[2],&im); /* Store the result */

					return 0;
}



