//========================================================================= #define PROGRAMMER "SOLUTIONS, NoFrills" #define PROG_CODE "soln" #define COURSE "ECE-1021" #define YEAR (2003) #define TERM "Fall" #define SECTION (0) #define ASSIGNMENT "HW #7A" #define REVISION (0) #define TITLE "BMP Basics" #define SUBTITLE "Color to Black & White" #define EMAIL "wbahn@eas.uccs.edu" #define FILENAME "07a0soln.c" //========================================================================= // PROBLEM: // // Write a program that allows opens a BMP file and copies it to a new // file while converting it to B&W using the RMS method. // SOLUTION: // // Because of the number of variables associated with the files, and // because they are not yet packaged into a structure than can be passed // around, this program places pretty much everything into main(). // // PSEUDOCODE: // // 1) TASK: Get file names from User // 1.1) TASK: Get input file name from User // 1.2) TASK: Get output file name from User // 2) TASK: Open and Verify both BMP files // 3) TASK: Read Header Data from input BMP file // 3.1) TASK: Read File Header // 3.2) TASK: Read Info Header // 4) TASK: Check Header data for compatibility (exit if not) // 5) TASK: Write Header Data to output BMP file // 5.1) TASK: Write File Header // 5.1) TASK: Write Info Header // 6) TASK: Copy data from input to output while converting to grayscale // 6.1) TASK: Move to start of Pixel Data in Both Files // 6.2) TASK: Print out image size and number of pixels // 6.3) TASK: Read/Convert/Write image pixel-by-pixel //== INCLUDE FILES ======================================================== #include // printf() #include // sqrt() #include // exit() #include // strlen() //== MACRO DEFINITIONS ==================================================== #define FALSE (0) #define TRUE (!FALSE) #define RED (2) #define GREEN (1) #define BLUE (0) //== TOP LEVEL SUPPORE FUNCTION PROTOTYPES ================================ void PrintHeader(void); FILE *OpenAndVerify(char *name, char *mode); unsigned char GrayScale(unsigned char r, unsigned char g, unsigned char b); //== MAIN FUNCTION ======================================================== int main(void) { char bfType[2]; // The characters "BM" unsigned long bfSize; // The size of the file in bytes unsigned int bfReserved1; // Unused - must be zero unsigned int bfReserved2; // Unused - must be zero unsigned long bfOffBits; // Offset to start of Pixel Data unsigned long biSize; // Header Size - Must be at least 40 unsigned long biWidth; // Image width in pixels unsigned long biHeight; // Image height in pixels unsigned int biPlanes; // Must be 1 unsigned int biBitCount; // Bits per pixel - (1,2,4,8,16,24,32} unsigned long biCompression; // Compression type (0 = uncompressed) unsigned long biSizeImage; // Image Size - uncompressed may be zero unsigned long biXPelsPerMeter; // Preferred resolution (pixels/meter) unsigned long biYPelsPerMeter; // Preferred resolution (pixels/meter) unsigned long biClrUsed; // Color Map entries that are used unsigned long biClrImportant; // Number of significant colors int compatible; // Whether input image can be used int row, col; unsigned char gray; unsigned long pixels; // Total number of pixels in image unsigned char color[3]; // In blue-green-red order FILE *fp_in; FILE *fp_out; char bmp_infilename[20]; char bmp_outfilename[20]; PrintHeader(); // TASK: Get input file name from User printf("Enter the name of the color BMP file to read (w/ext): "); fgets(bmp_infilename, 20, stdin); if(10 == bmp_infilename[strlen(bmp_infilename)-1]) bmp_infilename[strlen(bmp_infilename)-1] = '\0'; // TASK: Get output file name from User printf("Enter the name of the B&W BMP file to create (w/ext): "); fgets(bmp_outfilename, 20, stdin); if(10 == bmp_outfilename[strlen(bmp_outfilename)-1]) bmp_outfilename[strlen(bmp_outfilename)-1] = '\0'; // TASK: Open and Verify both BMP files fp_in = OpenAndVerify(bmp_infilename, "rb"); fp_out = OpenAndVerify(bmp_outfilename, "wb"); // TASK: Read Header Data from input BMP file // Read File Header fseek(fp_in, 0, SEEK_SET); // Go to beginning of File Header fread(&bfType, 1, 2, fp_in); fread(&bfSize, 4, 1, fp_in); fread(&bfReserved1, 2, 1, fp_in); fread(&bfReserved2, 2, 1, fp_in); fread(&bfOffBits, 4, 1, fp_in); // Read Info Header fseek(fp_in, 14, SEEK_SET); // Go to beginning of Info Header fread(&biSize, 4, 1, fp_in); fread(&biWidth, 4, 1, fp_in); fread(&biHeight, 4, 1, fp_in); fread(&biPlanes, 2, 1, fp_in); fread(&biBitCount, 2, 1, fp_in); fread(&biCompression, 4, 1, fp_in); fread(&biSizeImage, 4, 1, fp_in); fread(&biXPelsPerMeter, 4, 1, fp_in); fread(&biYPelsPerMeter, 4, 1, fp_in); fread(&biClrUsed, 4, 1, fp_in); fread(&biClrImportant, 4, 1, fp_in); // TASK: Check Header data for compatibility compatible = TRUE; if( 'B' != bfType[0] ) compatible = FALSE; if( 'M' != bfType[1] ) compatible = FALSE; if( 0 != bfReserved1 ) compatible = FALSE; if( 0 != bfReserved2 ) compatible = FALSE; if( 40 != biSize ) compatible = FALSE; if( 1 != biPlanes ) compatible = FALSE; if( 24 != biBitCount ) compatible = FALSE; if( 0 != biCompression ) compatible = FALSE; if( 40 != biSize ) compatible = FALSE; if( 40 != biSize ) compatible = FALSE; if(!compatible) { printf("Input BMP file is not compatible with this reader.\n"); exit(2); } // TASK: Write Header Data to output BMP file // Write File Header fseek(fp_out, 0, SEEK_SET); // Go to beginning of File Header fwrite(&bfType, 1, 2, fp_out); fwrite(&bfSize, 4, 1, fp_out); fwrite(&bfReserved1, 2, 1, fp_out); fwrite(&bfReserved2, 2, 1, fp_out); fwrite(&bfOffBits, 4, 1, fp_out); // Write Info Header fseek(fp_out, 14, SEEK_SET); // Go to beginning of Info Header fwrite(&biSize, 4, 1, fp_out); fwrite(&biWidth, 4, 1, fp_out); fwrite(&biHeight, 4, 1, fp_out); fwrite(&biPlanes, 2, 1, fp_out); fwrite(&biBitCount, 2, 1, fp_out); fwrite(&biCompression, 4, 1, fp_out); fwrite(&biSizeImage, 4, 1, fp_out); fwrite(&biXPelsPerMeter, 4, 1, fp_out); fwrite(&biYPelsPerMeter, 4, 1, fp_out); fwrite(&biClrUsed, 4, 1, fp_out); fwrite(&biClrImportant, 4, 1, fp_out); // TASK: Copy data from input to output while converting to grayscale // Move to start of Pixel Data in Both Files fseek(fp_in, bfOffBits, SEEK_SET); fseek(fp_out, bfOffBits, SEEK_SET); // Print out image size and number of pixels pixels = biWidth*biHeight; printf("Imported Image: %lux%lu (%lu pixels)\n", biWidth, biHeight, pixels); // Read/Convert/Write image pixel-by-pixel pixels = 0; for(row = 0; row < biHeight; row++) { for(col = 0; col < biWidth; col++) { fread(color, 1, 3, fp_in); // Read a Color Triplet in gray = GrayScale(color[RED], color[GREEN], color[BLUE]); color[RED] = color[GREEN] = color[BLUE] = gray; fwrite(color, 1, 3, fp_out); // Write a Gray Scale Triplet out pixels++; } printf("Row: %4i\r", row); } // Print out exported image size and number of pixels (should match) printf("Exported Image: %ix%i (%lu pixels)\n", col, row, pixels); // TASK: Final Housekeeping fclose(fp_in ); fclose(fp_out); return(0); } //== SUPPORT FUNCTIONS ==================================================== void PrintHeader(void) { printf("========================================" "=======================================\n"); printf("Course....... %s-%i (%s %i)\n", COURSE, SECTION, TERM, YEAR); printf("Programmer... %s (%s)\n", PROGRAMMER, PROG_CODE); printf("Assignment... %s (Rev %i) (Source Code in %s)\n", ASSIGNMENT, REVISION, FILENAME); printf("Description.. %s\n", TITLE); printf(" %s\n", SUBTITLE); printf("========================================" "=======================================\n"); return; } //== LOWER LEVEL FUNCTION PROTOTYPES ====================================== //== TOP LEVEL SUPPORT FUNCTION DEFINITIONS =============================== FILE *OpenAndVerify(char *name, char *mode) { FILE *fp; fp = fopen(name, mode); if(NULL == fp) { printf("ABORT! - Failed to open file <%s> (mode %s)\n", name, mode); exit(1); } return fp; } unsigned char GrayScale(unsigned char r, unsigned char g, unsigned char b) { return ((unsigned char) sqrt( ( (double) ( ((unsigned int) r) *((unsigned int) r) ) + (double) ( ((unsigned int) g) *((unsigned int) g) ) + (double) ( ((unsigned int) b) *((unsigned int) b) ) ) / 3.0 ) ); } //== LOWER LEVEL SUPPORT FUNCTION DEFINITIONS =============================