- #1
Enharmonics
- 29
- 2
Homework Statement
The assignment I'm doing involves manipulating an image stored in a 24-bit uncompressed BMP file. We were provided with two pre-written files (a header and a .c source file) that contain functions that read and write the image file, as well as a flip() function (mirrors the image) that was meant as an example (so we could better understand the way to manipulate the image).
The main point of the assignment was to write two functions, enlarge() and rotate(), which enlarge the image by some integer scale factor or rotate the image clockwise or counterclockwise by some degree. I've got that part done already. My program works perfectly, except for this (excerpt from the instructions):
The program should take the follow command-line options:
% bmptool [-s scale | -r degree | -f ] [-o output_file] [input_file]
Where -s means to scale the image by the given 'scale' (a positive integer: 1, 2, 3, ...), -r means to rotate the image by 'degree' (which must be a multiple of 90, clockwise if positive, counter-clockwise if negative), and -f means to flip the image horizontally.
You are required to use getopt() to process the command-line.
If '-o output_file' is missing, use standard output. If 'input_file' is missing, use standard input. The options to use standard input or standard output will only be used when chaining commands. Make sure the program returns 0 on success. In that case, one can 'chain' the commands using pipes, like:
% bmptool -s 4 1.bmp | bmptool -r -90 | bmptool -f -o 2.bmp
What I'm confused about is the italicized bit. I asked my professor, and she clarified some things:
1. The first command in a chain MUST provide an input file name, and the last command in a chain MUST provide an output file name.
2. If a command does not provide an output file name, then "the output goes to stdout as a binary stream", and "if it goes to stdout as a binary stream, the next one [here I presume she means the next command] in the chain can then read it from stdin" [sic].
The trouble is, I don't know how to do this or even understand how it works. This was never explained in class, and my textbook doesn't really cover how it works either (I guess because it's not really a C-specific thing).
The first thing I don't understand is how I could write something to stdout before the first iteration (i.e. the first command in the chain) of my program terminates, then read that same something in from stdin on the next iteration (i.e. the next command). I've been doing some Googling, and I managed to find this thread:
https://stackoverflow.com/questions...-reading-from-stdout-unix-linux-c-programming
In which multiple users seem to echo the sentiment that stdin and stdout are in fact "the same file descriptor", or "point to the same place", such that writing something to stdout would store that something in the same place that stdin would try to read from (a buffer of some kind)? Is that correct?
Another thing I don't understand is what function(s) I'm supposed to use to make the output "go to stdout as a binary stream". Our code is meant to represent the image as an array of PIXELs, which are a pre-defined struct of unsigned chars (see the header file for struct definition).
Is it possible to write an array of structs to stdout/read it from stdin? If so, how? I can't find any functions in the C documentation that can do this. I don't even know where to look.
Homework Equations
The Attempt at a Solution
I'll include my program's main() (where the getopt parsing happens, as well as file writing and so on) just in case anyone wants to see it. I've left out a significant chunk of the code where the enlarge(), rotate(), and flip() functions are defined since they're working properly and not really relevant to the problem at hand anyway.
I'll go ahead and toss in the code from the header file my professor provided as well.
The header file:
Code:
#ifndef BMPLIB_H
#define BMPLIB_H
typedef struct {
unsigned short padding;
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
typedef struct {
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
unsigned int biXPelsPerMeter;
unsigned int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
} PIXEL;
#define DEFAULT_BITMAP_OFFSET 1078
/* Read an uncompressed 24-bit bmp from a file named 'filename' (if
null, it's standard input); return the number of 'rows', the number
of 'cols', and the 'bitmap' as an array of PIXELs. The function
return 0 if successful. */
int readFile (char* filename, int* rows, int* cols, PIXEL** bitmap);/* Write an uncompressed 24-bit bmp to a file named 'filename' (if
null, it's standard output); the dimension of the bmp is the number
of 'rows' by the number of 'cols', and the 'bitmap' contains an
array of PIXELs. The function return 0 if successful. */
int writeFile (char* filename, int rows, int cols, PIXEL* bitmap);
/* Read bmp file header from file 'fd', return the number of 'rows',
the number of 'cols', and the 'start' position of the bitmap. The
function returns 0 if successful. */
int readHeader(int fd, int *rows, int *cols, unsigned int *start);
/* Write bmp file header to file 'fd'; the dimention of the bitmap is
the number of 'rows' by the number of 'cols', and it starts at the
'start' position. The function returns 0 if successful. */
int writeHeader(int fd, int rows, int cols, unsigned int start);
/* Read the 'bitmap' from file 'fd'; the dimention of the bitmap is
the number of 'rows' by the number of 'cols', and it starts at the
'start' position. The function returns 0 if successful. */
int readBits(int fd, PIXEL* bitmap, int rows, int cols, unsigned int start);
/* Write the 'bitmap' to file 'fd'; the dimention of the bitmap is the
number of 'rows' by the number of 'cols', and it starts at the
'start' position. The function returns 0 if successful. */
int writeBits(int fd, int rows, int cols, PIXEL* bitmap, unsigned int start);
#endif /*BMPLIB_H*/
My main():
Code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bmplib.h"
#define STRING_SIZE 100
// The flags for case sensitivity
// and an output file. Declared
// as globals so they can be used
// in auxiliary functions without
// being passed
int sflag = 0, rflag = 0, fflag = 0, oflag = 0;
// These are intended to represent the boolean
// values true and false (there's no native bool
// data type in C, apparently)
const int TRUE = 1;
const int FALSE = 0;
// Type alias for the bool type
typedef int bool;
// ----- FUNCTION PROTOTYPES -----
int enlarge(PIXEL* original, int rows, int cols, int scale,
PIXEL** new, int* newrows, int* newcols);
int rotate(PIXEL* original, int rows, int cols, int rotation,
PIXEL** new, int* newrows, int* newcols);
int main(int argc, char **argv)
{
// GETOPT CODE FROM CLASS, MODIFIED
// TO FIT ASSIGNMENT SPECIFICATIONS
extern char *optarg;
extern int optind;
int c, err = 0; // Stores the multiplier for the scale option
int scale = 0;
// Stores the degree value for the degree option
int degrees = 0;
// These arrays will store the input/output file
// directories
char inFileDirectory[STRING_SIZE];
char outFileDirectory[STRING_SIZE];
// This will be used when validating the input/output
// files
bool done = FALSE;
// Input/output files that will be validated
FILE *inFile;
FILE *outFile;
static char usage[] = "Usage: %s [-s scale | -r degree | -f] [-o output_file] [input_file]\n";
while ((c = getopt(argc, argv, "s:r:fo:")) != -1)
switch (c) {
case 's':
sflag = 1;
// If a scale value was entered,
// convert it to an integer and
// store it in scaleValue
scale = atoi(optarg);
break;
case 'r':
rflag = 1;
// If a degree value was entered,
// convert it to an integer and store
// it in degrees
degrees = atoi(optarg);
break;
case 'f':
fflag = 1;
break;
case 'o':
oflag = 1;
// If an output file name
// was entered, copy it
// to fileDirectory
if (optarg != NULL)
{
strcpy(outFileDirectory, argv[optind]);
// Attempt to open the output file
// immediately
//outFile = fopen(outFileDirectory, "w+");
}
// Otherwise, no output file name
// was entered, so use standard output
// as per assignment instructions
else
{
}
break;
default:
err = 1;
break;
}
if (err) {
// Generic error message
printf("ERROR: Invalid input.\n");
fprintf(stderr, usage, argv[0]);
exit(1);
}
// ----- BMPTOOL CODE STARTS HERE -----
// If argv[optind] isn't NULL, that means the user
// entered an input file as an option, so copy it into inFileDirectory
if (argv[optind] != NULL)
{
strcpy(inFileDirectory, argv[optind]);
}
// Otherwise, the user didn't enter an input file name, so use
// standard input as per assignment instructions
/*
// Otherwise, use standard input to get the input file name
else
{
// Do/while loop to validate the input file
do
{
printf("Please enter the directory of your BMP file: ");
fgets(inFileDirectory, STRING_SIZE, stdin);
// Attempt to open the input file
inFile = fopen(inFileDirectory, "r+");
// If the file wasn't opened successfully,
// notify the user
if (!inFile)
{
printf("Invalid file directory or file does not exist.\n");
}
// Otherwise, we have a valid input file, so terminate the loop
else
{
done = TRUE;
}
} while (!done);
}
// Check if the outFile was successfully
// opened within getopt -o option handling
if (!outFile)
{
// If not, prompt the user for input
// until we have a valid output file
do
{
printf("Please enter the directory to output a BMP file to:");
fgets(outFileDirectory, STRING_SIZE, stdout);
// Attempt to open the output file
outFile = fopen(outFileDirectory, "w+");
if (!outFile)
{
printf("Invalid file directory.\n");
}
// Otherwise, we have a valid output file, so terminate the loop
else
{
done = TRUE;
}
} while (!done);
}
*/
// Original row and columns
int r, k;
// Original and new bmp image pointers
PIXEL *b, *nb;
// Scale only
if (sflag && !rflag && !fflag)
{
// New row and columns. Given as pointers because they're pointers in the
// declaration of enlarge and rotate
int nr, nk;
// Read in the image file
readFile("example.bmp", &r, &k, &b);
// Scale the image
enlarge(b, r, k, scale, &nb, &nr, &nk);
// Write the scaled image file
writeFile("result.bmp", nr, nk, nb);
}
// Rotate only
if (rflag && !sflag && !fflag)
{
int nr, nk;
// Read in the image file
readFile("example.bmp", &r, &k, &b);
// Rotate the image
rotate(b, r, k, degrees, &nb, &nr, &nk);
// Write the rotated image file
writeFile("result.bmp", nr, nk, nb);
}
// Flip only
if (fflag && !sflag && !rflag)
{
// Read in the image file
readFile("example.bmp", &r, &k, &b);
// Flip the image
flip(b, &nb, r, k);
// Write the flipped image file
writeFile("result.bmp", r, k, nb);
return 0;
}
// Scale and rotate only
if (sflag && rflag && !fflag)
{
int nr, nk;
// First, perform the scaling
readFile("example.bmp", &r, &k, &b);
enlarge(b, r, k, scale, &nb, &nr, &nk);
writeFile("result.bmp", nr, nk, nb);
// Now perform the rotation
readFile("result.bmp", &r, &k, &b);
rotate(b, r, k, degrees, &nb, &nr, &nk);
writeFile("result.bmp", nr, nk, nb);
return 0;
}
// Scale and flip only
if (sflag && fflag && !rflag)
{
int nr, nk;
// First, perform the scaling
readFile("example.bmp", &r, &k, &b);
enlarge(b, r, k, scale, &nb, &nr, &nk);
writeFile("result.bmp", nr, nk, nb);
// Next, perform the flip
readFile("result.bmp", &r, &k, &b);
flip(b, &nb, r, k);
writeFile("result.bmp", r, k, nb);
return 0;
}
// Rotate and flip only
if (rflag && fflag && !sflag)
{
int nr, nk;
// First, perform the rotation
readFile("example.bmp", &r, &k, &b);
rotate(b, r, k, degrees, &nb, &nr, &nk);
writeFile("result.bmp", r, k, nb);
// Now perform the flip
readFile("result.bmp", &r, &k, &b);
flip(b, &nb, r, k);
writeFile("result.bmp", r, k, nb);
return 0;
}
// Scale, rotate and flip
if (sflag && rflag && fflag)
{
int nr, nk;
// First, perform the scaling
readFile("example.bmp", &r, &k, &b);
enlarge(b, r, k, scale, &nb, &nr, &nk);
writeFile("result.bmp", nr, nk, nb);
// Now perform the rotation
readFile("result.bmp", &r, &k, &b);
rotate(b, r, k, degrees, &nb, &nr, &nk);
writeFile("result.bmp", nr, nk, nb);
// Finally, perform the flip
readFile("result.bmp", &r, &k, &b);
flip(b, &nb, r, k);
writeFile("result.bmp", r, k, nb);
return 0;
}
free(b);
free(nb);
return 0;
}