Instructions
Objective
Write a C program to implement functions to encode, decode message ppm image.
Requirements and Specifications
For this assignments you will have at a minimal eight files:
- driver.c – This is where main lives.
- ppm.h – I will provide this file. It will contain the prototypes of the functions I implement. These functions deal specifically with a ppm file. It will also provide the definition of two structs.
- One for the header.
- One for the pixels
- ppm.c – This file is where I implemented the functions listed in the ppm.h file.
- EncodeDecode.h – I will provide this file. It will contain additional prototypes of functions I needed to encode and decode a message in an image.
- EncodeDecode.c – This file is where I implement the functions listed in EncodeDecode.h
- utils.h – this provides the prototypes for two functions needed to check the number of command line arguments passed to the program. It also contains a function that check if the file pointers opened successfully.
- utils.c – this file is where I implement the functions for utils.h
- makefile
Each of your header files (.h) must have header guards. Points will be deducted if you do not provide header guards I also removed all of the #include statements, you will need to provide these.
Screenshots of output
Source Code
Driver.c/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#include
#include
#include "utils.h"
#include "EncodeDecode.h"
int main(int argc, char* argv[])
{
checkArgs(argc, 2);
FILE* in = fopen(argv[1], "r");
checkFile(in);
FILE* out = fopen(argv[2], "w");
checkFile(out);
/*This is my test message. It can be something different*/
char* msg = "I hope this works!";
encodeMsg(in, out, msg);
FILE* dec = fopen(argv[2], "r");
checkFile(dec);
decodeMsg(dec);
fclose(in);
fclose(out);
fclose(dec);
return EXIT_SUCCESS;
}
EncodeDecode.c
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#include
#include
#include
#include "EncodeDecode.h"
/* Parameters: pixels - pixel_t double pointer that contains the ppm
* pixel information.
* header - header_t that contains the ppm header
* Return: output - none
* This function removes the lowest decimal digit (rounds to 0) in all
* the pixel 2D array
*/
void removeDigit(pixel_t** pixels, header_t header)
{
int i, j;
// go through all pixels rows
for (i = 0; i < header.height; i++)
{
// go through all pixels in row
for (j = 0; j < header.width; j++)
{
// remove digit by dividing by 10 and then multiplying by 10
pixels[i][j].r = (pixels[i][j].r / 10) * 10;
pixels[i][j].g = (pixels[i][j].g / 10) * 10;
pixels[i][j].b = (pixels[i][j].b / 10) * 10;
}
}
}
/* Parameters: in - FILE pointer pointing to the input PPM file
* out - FILE pointer pointing to the output PPM file
* msg - char array containing the message to be encoded
* Return: output - none
* This function encodes the given message using the input PPM image
* and saves the PPM with the encoded message in the output PPM image
*/
void encodeMsg(FILE* in, FILE* out, char* msg)
{
int i, j, x, y;
int pixpos, binpos;
int binary[9];
header_t header;
pixel_t** pixels;
// load header from input image
header = readHeader(in);
// load pixels from input image
pixels = readPixel(in, header);
// remove the digits in all pixels
removeDigit(pixels, header);
// start at position 0 in image
pixpos = 0;
// go through all characters in message including the last zero
for (i = 0; i <= strlen(msg); i++)
{
// convert character to binary
charToBinary(msg[i], binary);
// start at binary position 0
binpos = 0;
// get the 3 required pixels for saving the char
for (j = 0; j < 3; j++)
{
// get pixel position x,y in image
x = pixpos % header.width;
y = pixpos / header.width;
//add the bits of the char
pixels[y][x].r += binary[binpos++];
pixels[y][x].g += binary[binpos++];
pixels[y][x].b += binary[binpos++];
// advance pixel position
pixpos++;
}
}
// save file
writeHeader(out, header);
writePixels(out, pixels, header);
// free all allocated memory
freeMemory(pixels, header);
}
/* Parameters: c - character to be converted
* bin - integer array where the binary bits will be saved
* Return: output - none
* This function converts the given character to a binary number of 9
* bits. It assumes that the bin array can hold at least 9 values.
*/
void charToBinary(char c, int *bin)
{
int i;
// first bit is always zero (9th bit)
bin[0] = 0;
// repeat 8 times for the 8 bits in the char
for (i = 0; i < 8; i++)
{
// if sign is 1, save a 1, otherwise save zero
bin[i + 1] = (c < 0);
// shift once to the left to move next bit to msb position
c <<= 1;
}
}
/* Parameters: bin - integer array holding a binary number
* Return: output - character that has been converted from the binary
* This function converts the given binary number of 9 bits.
* to a character. It assumes that the bin array holds at least 9 values.
*/
unsigned char binToCharacter(int* bin)
{
unsigned char c;
int i;
// initialize char to zero
c = 0;
// repeat 8 times for the 8 bits in the char (first bit is zero)
for (i = 1; i < 9; i++)
// shift c and save bit
c = (c << 1) | bin[i];
return c;
}
/* Parameters: n - integer with the bit to be saved
* bin - integer array that will hold the binary number
* index - integer pointer to an index
* Return: output - none
* This function saves the given bit in the binary number of 9 bits at the
* given index. When the index reaches 9, the binary is converted to a
* character and then it's printed on the screen. It assumes that the bin
* array can hold at least 9 values and that the index pointer is not NULL
*/
void queue(int n, int* bin, int* index)
{
unsigned char c;
// save the digit, increment the index
bin[(*index)++] = n;
// if queue is full
if (*index >= 9)
{
// convert binary to character
c = binToCharacter(bin);
// if valid char
if (c < 255 && c > 0)
// print character
printf("%c", c);
// reset position
*index = 0;
}
}
/* Parameters: n - character to be converted
* bin - integer array where the binary bits will be saved
* Return: output - none
* This function converts the given integer to a binary number of 32
* bits. It assumes that the bin array can hold at least 32 values.
*/
void dec2bin(int* bin, int n)
{
int i;
// repeat 32 times for the 32 bits in the int
for (i = 0; i < 32; i++)
{
// if bit sign is 1, save a 1, otherwise save zero
bin[i] = (n < 0);
// shift once to the left to move next bit to msb position
n <<= 1;
}
}/* Parameters: in - FILE pointer pointing to the input PPM file
* Return: output - none
* This function decodes a message encoded in the input PPM image
* and prints the decoded message on the screen
*/
void decodeMsg(FILE* in)
{
int i, j;
int binpos;
int binary[9];
header_t header;
pixel_t** pixels;
// load header from input image
header = readHeader(in);
// load pixels from input image
pixels = readPixel(in, header);
// start at position 0 in binary
binpos = 0;
// go through all pixels rows
for (i = 0; i < header.height; i++)
{
// go through all pixels in row
for (j = 0; j < header.width; j++)
{
// get the bits of the char by getting the decimal digit and
// passing it to queue to be printed
queue(pixels[i][j].r % 10, binary, &binpos);
queue(pixels[i][j].g % 10, binary, &binpos);
queue(pixels[i][j].b % 10, binary, &binpos);
}
}
// free all allocated memory
freeMemory(pixels, header);
}
EncodeDecode.h
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#ifndef ENCODEDECODE_H
#define ENCODEDECODE_H
#include
#include
#include "ppm.h"
void removeDigit(pixel_t** , header_t );
void encodeMsg(FILE* in, FILE* out, char* msg);
void charToBinary(char , int *);
unsigned char binToCharacter(int*);
void queue(int, int*, int*);
void dec2bin(int*, int);
void decodeMsg(FILE*);
#endif /* ENCODEDECODE_H */
Ppm.c
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#include
#include
#include
#include "ppm.h"
/* Parameters: f - FILE pointer to an input ppm file
* Return: output - header_t that contains the ppm header
* This function reads the PPM header information from the given file.
*/
header_t readHeader(FILE* f)
{
header_t header;
// read the magic numbers
fread(header.magicNum, 2, 1, f);
// clear whitespace and comments
ckws_comments(f);
// read the width
fscanf(f, "%d", &header.width);
// clear whitespace and comments
ckws_comments(f);
// read the height
fscanf(f, "%d", &header.height);
// clear whitespace and comments
ckws_comments(f);
// read the max value
fscanf(f, "%d", &header.maxVal);
// read the last whitespace
fgetc(f);
return header;
}
/* Parameters: f - FILE pointer to an input ppm file
* header - header_t that contains the ppm header
* Return: output - pixel_t double pointer that contains the ppm
* pixel information.
* This function reads the PPM pixel information from the given file.
* It allocates a 2D array to read the pixel data and returns it.
*/
pixel_t** readPixel(FILE* f, header_t header)
{
pixel_t** pixels;
int i;
int j;
int bytes;
char buffer[2];
// number of bytes per sample (r,g,b), 1 if < 256, 2 otherwise
bytes = (header.maxVal < 256) ? 1 : 2;
// allocate space for all row pointers
pixels = (pixel_t**) malloc(header.height * sizeof(pixel_t*));
// go through all pixels rows
for (i = 0; i < header.height; i++)
{
// allocate space for all pixels in the row
pixels[i] = (pixel_t*) malloc(header.width * sizeof(pixel_t));
// go through all pixels in row
for (j = 0; j < header.width; j++)
{
// read the red part
fread(buffer, bytes, 1, f);
// if maxvalue fits in 1 byte
if (header.maxVal < 256)
pixels[i][j].r = buffer[0];
// if maxvalue fits in 2 bytes, first byte is msb
else
pixels[i][j].r = (buffer[0] << 8) | buffer[1];
// read the green part
fread(buffer, bytes, 1, f);
// if maxvalue fits in 1 byte
if (header.maxVal < 256)
pixels[i][j].g = buffer[0];
// if maxvalue fits in 2 bytes, first byte is msb
else
pixels[i][j].g = (buffer[0] << 8) | buffer[1];
// read the blue part
fread(buffer, bytes, 1, f);
// if maxvalue fits in 1 byte
if (header.maxVal < 256)
pixels[i][j].b = buffer[0];
// if maxvalue fits in 2 bytes, first byte is msb
else
pixels[i][j].b = (buffer[0] << 8) | buffer[1];
}
}
return pixels;
}
/* Parameters: f - FILE pointer to an output ppm file
* header - header_t that contains the ppm header
* Return: output - none
* This function writes the PPM header information into the given file.
*/
void writeHeader(FILE* f, header_t header)
{
// write the magic numbers
fprintf(f, "%c%c\n", header.magicNum[0], header.magicNum[1]);
// write the width x height
fprintf(f, "%d %d\n", header.width, header.height);
// write the max value and newline
fprintf(f, "%d\n", header.maxVal);
}
/* Parameters: f - FILE pointer to an output ppm file
* pixels - pixel_t double pointer that contains the ppm
* pixel information.
* header - header_t that contains the ppm header
* Return: output - none
* This function writes the PPM pixel information into the given file.
*/
void writePixels(FILE* f, pixel_t** pixels, header_t header)
{
int i;
int j;
int bytes;
char buffer[2];
// number of bytes per sample (r,g,b), 1 if < 256, 2 otherwise
bytes = (header.maxVal < 256) ? 1 : 2;
// go through all pixels rows
for (i = 0; i < header.height; i++)
{
// go through all pixels in row
for (j = 0; j < header.width; j++)
{
// if maxvalue fits in 1 byte
if (bytes == 1)
buffer[0] = pixels[i][j].r;
// if maxvalue fits in 2 bytes, first byte is msb
else
{
buffer[0] = pixels[i][j].r >> 8;
buffer[1] = pixels[i][j].r;
}
// write the red part
fwrite(buffer, bytes, 1, f);
// if maxvalue fits in 1 byte
if (bytes == 1)
buffer[0] = pixels[i][j].g;
// if maxvalue fits in 2 bytes, first byte is msb
else
{
buffer[0] = pixels[i][j].g >> 8;
buffer[1] = pixels[i][j].g;
}
// write the green part
fwrite(buffer, bytes, 1, f);
// if maxvalue fits in 1 byte
if (bytes == 1)
buffer[0] = pixels[i][j].b;
// if maxvalue fits in 2 bytes, first byte is msb
else
{
buffer[0] = pixels[i][j].b >> 8;
buffer[1] = pixels[i][j].b;
}
// write the blue part
fwrite(buffer, bytes, 1, f);
}
}
}
/* Parameters: f - FILE pointer pointing to a ppm input file
* Return: output - none
* This function removes all the whitespace and comments found from
* the current position.
*/
void ckws_comments (FILE *f)
{
int c;
while (1)
{
// ignore all whitespace
while (isspace(c = fgetc(f)));
// if a comment
if (c == '#')
// read until newline
while ((c = fgetc(f)) != '\n');
// if not a comment
else
{
// undo the last read
ungetc(c, f);
// exit the loop to return
break;
}
}
}
/* Parameters: pixels - pixel_t double pointer that contains the ppm
* pixel information.
* header - header_t that contains the ppm header
* Return: output - none
* This function frees all the allocated memory used by the pixel 2D array
*/
void freeMemory(pixel_t** pixels, header_t header)
{
int i;
// go through all pixels rows
for (i = 0; i < header.height; i++)
// free space for all pixels
free(pixels[i]);
// free space for all rows
free(pixels);
}
Ppm.h
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#ifndef PPM_H
#define PPM_H
#include
typedef struct
{
unsigned char r;
unsigned char g;
unsigned char b;
}pixel_t;
typedef struct
{
char magicNum[3];
int width;
int height;
int maxVal;
}header_t;
header_t readHeader(FILE*);
pixel_t** readPixel(FILE*, header_t);
void writeHeader(FILE*, header_t);
void writePixels(FILE*, pixel_t**, header_t);
void ckws_comments (FILE *);
void freeMemory(pixel_t**, header_t);
#endif /* PPM_H */
Utils.c
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#include
#include
#include "utils.h"
/* Parameters: argc - number of command line arguments
* n - required number of command line arguments
* Return: output - none
* This function verifies that the number of command line arguments
* is at least the required n. If not, it exits with error.
*/
void checkArgs(int argc, int n)
{
// if not enough arguments, exit with error
if (argc < n)
{
fprintf(stderr, "Error: Not enough command line arguments\n");
exit(EXIT_FAILURE);
}
}
/* Parameters: f - FILE pointer pointing to a file that has been opened
* Return: output - none
* This function verifies that the file has been opened coorrectly.
* If not, it exits with error.
*/
void checkFile(FILE* f)
{
// if file was not opened, exit with error
if (f == NULL)
{
fprintf(stderr, "Error: Unable to open file\n");
exit(EXIT_FAILURE);
}
}
Utils.h
/*************************
*Your names
*CPSC 2310 F21 Section 00?
*Your emails
*************************/
#ifndef UTILS_H
#define UTILS_H
#include
void checkArgs(int, int);
void checkFile(FILE*);
#endif /* UTILS_H */