These solutions are for the exclusive use of students enrolled in SI 204 at USNA in Spring 2017. Any other copying or distribution is a violation of the honor concept.

/* SI204 Spring 2017
 * Lab 6: Green screening
 * SAMPLE SOLUTIONS by Dr. Roche
 */

#include <stdio.h>
#include <assert.h>

typedef char cstring[128];

/****************************************************************
 * FUNCTION PROTOTYPES
 ****************************************************************/
int fread_int(FILE* in);
int fread_byte(FILE* in);
void fwrite_int(int data, FILE* out);
void fwrite_byte(unsigned char data, FILE* out);

void read_bmp_head(int* wptr, int* hptr, FILE* in);
void write_bmp_head(int width, int height, FILE* out);

void read_color(int* rptr, int* gptr, int* bptr, FILE* in);
void write_color(int red, int green, int blue, FILE* out);

/****************************************************************
 * MAIN FUNCTION
 ****************************************************************/
int main() {
  int check;

  // read input filename
  cstring infile;
  printf("Input file: ");
  fflush(stdout);
  check = scanf(" %s", infile);
  assert (check == 1);

  FILE* fin = fopen(infile, "rb");
  if (! fin) {
    printf("ERROR: file not found\n");
    return 1;
  }

  // read output filename
  cstring outfile;
  printf("Output file: ");
  fflush(stdout);
  check = scanf(" %s", outfile);
  assert (check == 1);

  FILE* fout = fopen(outfile, "wb");

  // read color replacement
  int rered;
  int regreen;
  int reblue;
  printf("Replacement color values (RGB): ");
  fflush(stdout);
  check = scanf(" %d %d %d", &rered, &regreen, &reblue);
  assert (check == 3);

  // compute image parameters from input file
  int width;
  int height;
  read_bmp_head(&width, &height, fin);

  // start output file
  write_bmp_head(width, height, fout);

  // read and write pixel data
  for (int ycoord=0; ycoord < height; ++ycoord) {
    for (int xcoord=0; xcoord < width; ++xcoord) {
      int r, g, b;
      read_color(&r, &g, &b, fin);
      if (r == 0 && g == 255 && b == 0) {
        // replace green pixels
        write_color(rered, regreen, reblue, fout);
      } else {
        // non-green; write out original again
        write_color(r, g, b, fout);
      }
    }
  }

  // all done!
  fclose(fin);
  fclose(fout);
  printf("Image saved to %s\n", outfile);
  return 0;
}

/****************************************************************
 * FUNCTION DEFINITIONS
 ****************************************************************/

int fread_int(FILE* in) {
  int ans;
  int check = fread(&ans, sizeof(int), 1, in);
  assert (check == 1);
  return ans;
}

int fread_byte(FILE* in) {
  unsigned char ans;
  int check = fread(&ans, sizeof(char), 1, in);
  assert (check == 1);
  return ans;
};

void fwrite_int(int data, FILE* out) {
  fwrite(&data, sizeof(int), 1, out);
}

void fwrite_byte(unsigned char data, FILE* out) {
  fwrite(&data, sizeof(char), 1, out);
}

void read_bmp_head(int* wptr, int* hptr, FILE* in) {
  // variables for important image parameters
  int fhlen = 14;
  int ihlen = 40;
  int fsize;
  int temp;

  // read file header
  temp = fread_byte(in); // magic number part 1
  assert (temp == 'B');
  temp = fread_byte(in); // magic number part 2
  assert (temp == 'M');
  fsize = fread_int(in); // file size
  fread_int(in); // unused 4 bytes
  temp = fread_int(in); // pixels offset
  assert (temp == fhlen + ihlen);

  // read image header
  temp = fread_int(in); // image header length
  assert (temp == ihlen);
  *wptr = fread_int(in); // image width
  *hptr = fread_int(in); // image height
  temp = fread_int(in); // 1 image plane, 24 bits per pixel
  assert (temp == 1 + (24 << 16));
  temp = fread_int(in); // compression type
  assert (temp == 0);
  fread_int(in); // image size (ignored)
  fread_int(in); // horiz resolution (ignored)
  fread_int(in); // vertical resolution (ignored)
  temp = fread_int(in); // color map entries used
  assert (temp == 0);
  fread_int(in); // # of "important" colors (ignored)

  // check file size
  int rowlen = *wptr * 3;
  int pixlen = *hptr * rowlen;
  assert (fsize == fhlen + ihlen + pixlen);
}

void write_bmp_head(int width, int height, FILE* out) {
  // compute image parameters
  int fhlen = 14;
  int ihlen = 40;
  int rowlen = width * 3;
  int pixlen = height * rowlen;

  // file header
  fwrite_byte('B', out); // magic number part 1
  fwrite_byte('M', out); // magic number part 2
  fwrite_int(fhlen + ihlen + pixlen, out); // file size
  fwrite_int(0, out); // unused 4 bytes
  fwrite_int(fhlen + ihlen, out); // pixels offset

  // image header
  fwrite_int(ihlen, out); // image header length
  fwrite_int(width, out); // image width
  fwrite_int(height, out); // image height
  fwrite_int(1 + (24 << 16), out); // 1 image plane, 24 bits per pixel
  fwrite_int(0, out); // compression type
  fwrite_int(0, out); // image size (0 for uncompressed)
  fwrite_int(0, out); // horiz resolution (0 for default)
  fwrite_int(0, out); // vertical resolution (0 for default)
  fwrite_int(0, out); // color map entries used
  fwrite_int(0, out); // # of "important" colors (0 means all)
}

void read_color(int* rptr, int* gptr, int* bptr, FILE* in) {
  // note: pixels are in reverse order BGR
  *bptr = fread_byte(in);
  *gptr = fread_byte(in);
  *rptr = fread_byte(in);
}

void write_color(int red, int green, int blue, FILE* out) {
  // note: pixels are in reverse order BGR
  fwrite_byte(blue, out);
  fwrite_byte(green, out);
  fwrite_byte(red, out);
}