Trying to do basic Steganography on a PPM Image.
I have the basic algorithm completed. Read in the file, check the header starts with P6, get the image width and height, and the pixel data.
I need to have a total of four methods: ReadPPM, WritePPM, WriteMsg and ReadMsg.
I have the ReadImg and WriteImg methods down, but where I am stuck is with my WriteMsg method. This is basic steganography that just writes each bit of the string to the last bit in each byte. The first 8 bytes are suppose to contain the size of the string being hidden, then each byte after that starts the hidden message.
My idea was to create a massive array that holds the binary code for the size of the string, then the binary code of the string itself. I'm just trying to figure out how I would take that array and add it to each byte in the image.
Any help is much appreciated. Here is my current code:
#include<stdio.h>
#include<stdlib.h>
typedef struct {
unsigned char red,green,blue;
} PPMPixel;
typedef struct {
int x, y;
PPMPixel *data;
} PPMImage;
#define CREATOR "RPFELGUEIRAS"
#define RGB_COMPONENT_COLOR 255
static PPMImage *readPPM(const char *filename)
{
char buff[16];
PPMImage *img;
FILE *fp;
int c, rgb_comp_color;
//open PPM file for reading
fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);
}
//read image format
if (!fgets(buff, sizeof(buff), fp)) {
perror(filename);
exit(1);
}
//check the image format
if (buff[0] != 'P' || buff[1] != '6') {
fprintf(stderr, "Invalid image format (must be 'P6')\n");
exit(1);
}
//alloc memory form image
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//check for comments
c = getc(fp);
while (c == '#') {
while (getc(fp) != '\n') ;
c = getc(fp);
}
ungetc(c, fp);
//read image size information
if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
exit(1);
}
//read rgb component
if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
exit(1);
}
//check rgb component depth
if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
exit(1);
}
while (fgetc(fp) != '\n') ;
//memory allocation for pixel data
img->data = (PPMPixel*)malloc(img->x * img->y * sizeof(PPMPixel));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//read pixel data from file
if (fread(img->data, 3 * img->x, img->y, fp) != img->y) {
fprintf(stderr, "Error loading image '%s'\n", filename);
exit(1);
}
fclose(fp);
return img;
}
void writePPM(const char *filename, PPMImage *img)
{
FILE *fp;
//open file for output
fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);
}
//write the header file
//image format
fprintf(fp, "P6\n");
//comments
fprintf(fp, "# Created by %s\n",CREATOR);
//image size
fprintf(fp, "%d %d\n",img->x,img->y);
// rgb component depth
fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);
// pixel data
fwrite(img->data, 3 * img->x, img->y, fp);
fclose(fp);
}
void writeMsg(PPMImage *img, char *s)
{
int i;
int len;
len = sizeof(s);
if (img)
{
j = 0;
for (i=0; i < img->x * img->y; i++)
{
while(j < 8)
{
if(len & 0x80)
{
img->data[i].red= img->data[i].red | 0x01;
}
else
{
img->data[i].red= img->data[i].red & 0xFE;
}
len=len << 1;
j++;
if (len & 0x80)
{
img->data[i].green= img->data[i].green | 0x01;
}
else
{
img->data[i].green= img->data[i].green & 0xFE;
}
len = len << 1;
j++;
if (len & 0x80)
{
img->data[i].blue= img->data[i].blue | 0x01;
}
else
{
img->data[i].blue= img->data[i].blue & 0xFE;
}
j++;
}
}
}
}
To extract just a single bit from a byte, do this:
bit(i) = byte >> i & 0x1
This shifts the byte's bits to the right i times, and then ands it with 0000 0001 (such that all bits except the lowest are zeroed, and the lowest bit is 0 for 0 and 1 for 1).
You can do similar for 16 bit short, 32 bit int, 64 bit long... And even for the chars of a string. You can use sizeof(char) to see how many bytes are in a char.
But of course you'll have more than one char (or long or int or...) to extra bits from. To decide which element to extra a bit from:
If you want the ith bit and elements are x bits wide, then get the i%x bit from element [i/x]
Now that you have this bit, to place it inside of a byte (or int or char or...), do:
steganographybyte = originalbyte&(~0x1) + bit
What this means is, you take the number ... 0000 0001, invert its bits so it's ... 1111 1110, and it with the original byte so all of its bits are preserved EXCEPT the lowest, then just add your bit in.
Related
Trying to solve this problem i keep finding the same errors:
:) recover.c exists.
:) recover.c compiles.
:) handles lack of forensic image
:( recovers 000.jpg correctly
000.jpg not found
:( recovers middle images correctly
001.jpg not found
:( recovers 049.jpg correctly
049.jpg not found
That makes me think that the files are not been read or opened at all and i can't find why because the code compiles.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main(int argc, char* argv[])
{
//checks if there is an input
if (argc != 2)
{
printf("./recover Usage: ./recover image \n");
return 1;
}
else
{
char* filename = argv[1];
FILE* input = fopen(filename, "r");
if (strcmp(filename, "card.raw") != 0)
{
printf("Unable to open: %s\n", filename);
return 2;
}
else
{
//Create buffer
uint8_t buffer[512];
//Create pointer for the buffer
FILE* output = NULL;
// create 8 bytes file
char files[8];
//Files counters
int jpeg_counter = 0;
//Check
while (fread(buffer, sizeof(buffer), 1, input))
{
//check 4 first bytes of file to see if they contain the jpg signature
if ((buffer[0] == 0xff) && (buffer[1] == 0xd8) && (buffer[2] == 0xff) && ((buffer[3] & 0xf0) == 0xe0))
{
if (output != NULL) {
// sprintf(char *str, const char *format, ...) and "03i" means 3 digits in format 001, 002...
sprintf(files, "%03i.jpg", jpeg_counter);
//use created pointer to save jpeg files from input folder card.raw
output = fopen(filename, "w");
//add jpeg to the new buffer using ouput pointer
fwrite(buffer, sizeof(buffer), 1, output);
// update counter
jpeg_counter++;
}
else //no more files to read(end of folder array) - or no images found.
{
printf("Could not open file\n");
return 0;
}
}
}
fclose(output);
fclose(input);
return 0;
}
}
}
Here is another attempt at a solution, however, it only writes out the first 512 bytes (unlike the original) but I don't think jpeg uses 512 bytes fixed chunks so neither the op or or #BarmakShemirani solution would work as expected. (buffer[3] & 0xf0) == 0xe0 means variable sized, application specific and there is a reference that data is laid out like TIFF, In either case op did not share card.raw so whatever format is used would be speculation:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILENAME_LEN 8
int main(int argc, char* argv[]) {
int r = 0;
FILE *input = NULL;
if (argc != 2) {
printf("./recover Usage: ./recover image \n");
r = 1;
goto out;
}
char *filename = argv[1];
input = fopen(filename, "rb");
if (!strcmp(filename, "card.raw")) {
printf("Unable to open: %s\n", filename);
r = 2;
goto out;
}
for(int jpeg_counter = 0; !r; jpeg_counter++) {
uint8_t buffer[512];
size_t n = fread(buffer, sizeof(buffer), 1, input);
// use an existing library instead?
if(
n < 4 ||
buffer[0] != 0xff ||
buffer[1] != 0xd8 ||
buffer[2] != 0xff ||
(buffer[3] & 0xf0) != 0xe0
) {
printf("invalid header\n");
r = 3;
break;
}
char filename2[FILENAME_LEN];
if(snprintf(filename2, FILENAME_LEN, "%03i.jpg", jpeg_counter) >= FILENAME_LEN) {
printf("%s: output filename truncated", filename);
r = 4;
break;
};
FILE *output = fopen(filename2, "wb");
if(!output) {
printf("%s: fopen failed\n", filename);
r = 5;
break;
}
// TBD: replace with a loop once we figure out
// how a given file is terminated.
if(fwrite(buffer, n, 1, output) != n) {
printf("%s: write failed\n", filename);
r = 6;
}
fclose(output);
}
out:
if(input) fclose(input);
return r;
}
uint8_t buffer[512];
fread(buffer, sizeof(buffer), 1, input)
This should be change to: fread(buffer, 1, sizeof(buffer), input). This way fread will read up to 512 bytes. When it gets to the end of file, it reads whatever is left, for example 1 byte, and returns that number. Likewise, fwrite should change. It should write the same number which was read earlier.
Open the file in binary mode.
If fopen fails then stop immediately.
Check the file header only once, not every read loop.
Your condition for checking the file header may have a typo (buffer[3] & 0xf0) == 0xe0. Checking the first 3 bytes should be okay.
int main()
{
FILE* input = fopen("input.jpg", "rb");
if (!input)
{ perror("input error"); return 0; }
FILE* output = fopen("output.jpg", "wb");
if (!output)
{ perror("output error"); fclose(input); return 0; }
uint8_t buf[1000];
int check_header = 1;
while (1)
{
size_t read_count = fread(buf, 1, sizeof(buf), input);
if (!read_count)
break;
if (check_header)
{
if (buf[0] == 0xff && buf[1] == 0xd8 && buf[2] == 0xff)
// && buf[3] == 0xe0 ?
{ printf("header is okay\n"); }
else
{ printf("not okay\n"); break; }
check_header = 0; //don't check header again
}
fwrite(buf, 1, read_count, output);
}
fclose(input);
fclose(output);
return 0;
}
Thanks for the help. Tried those and got the same error. What surprisingly worked for me was to refactor even more the code and give use of the counter for the if conditions as shown below.
int main(int argc, char* argv[])
{
//checks if there is an input
if (argc != 2)
{
printf(".Usage: Looking for card.raw \n");
return 1;
}
else
{
char* filename = argv[1];
FILE* input;
input = fopen(filename, "r");
//to track if it fails to open
if (input == NULL)
{
printf("Could not open file");
return 2;
}
//Create buffer. Unsigned int variable type. Array of 512 bytes
uint8_t buffer[512];
//Create pointer for the buffer.
FILE* output = NULL;
// create 8 bytes file
char files[8];
//Files counters
int jpeg_counter = 0;
//Check
while (fread(buffer, sizeof(buffer), 1, input))
{
//check 4 first bytes of file to see if they contain the jpg signature
if ((buffer[0] == 0xff) && (buffer[1] == 0xd8) && (buffer[2] == 0xff) && ((buffer[3] & 0xf0) == 0xe0))
{
if (jpeg_counter != 0)
{
fclose(output);
}
// sprintf(char *str, const char *format, ...) and "03i" means 3 digits in format 001, 002...
sprintf(files, "%03i.jpg", jpeg_counter);
//use created pointer to save jpeg files from input folder card.raw
output = fopen(files, "w");
//add jpeg to the new buffer using ouput pointer
fwrite(buffer, sizeof(buffer), 1, output);
// update counter
jpeg_counter++;
} else if (jpeg_counter != 0)
{
fwrite(buffer, sizeof(buffer), 1, output);
}
}
fclose(output);
return 0;
}
}
I am trying to use fread on a file containing multiple JPEGs and write the JPEGs into new files, but before I can do that I need to properly look through the file and look for the JPEGs based on their first bytes based on the if statement at the bottom of the code below.
I have not been able to get into the if statement, and have been trying to print out the bytes, but I have been running into issues in printing.
I'm looking to print just the 0 byte of the buffer, but my output is looking like this:
711151a6
cec117f0
7603c9a9
73599166
I'm very new to C and fread, and any help would be appreciated!
Code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Check for 2 arguments, the name of the program and the file being read
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
else
{
//Open the file
FILE * fp;
fp = fopen(argv[1], "r");
//Get file length
fseek(fp, 0, SEEK_END);
int f_length = ftell(fp);
fseek(fp, 0, SEEK_SET);
// If not file is found then exit
if(fp == NULL)
{
printf("File not found\n");
return 2;
}
// Allocate buffer for fread function
int *buffer = (int*)malloc(f_length);
if (buffer == NULL)
{
printf("Buffer is null\n");
return 1;
}
// Read thorugh the file
while(fread(buffer, 512, 1, fp) == 1)
{
for (int i = 0; i < 1; i++)
{
printf("%x\n", buffer[i]);
}
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
printf("Found a jpg\n");
}
}
// Exit the program
return 0;
}
}
int *buffer is not correct because the intention is to deal with bytes and not ints. If int * is used, then for example, buffer[0] will be the first 4 bytes and not the first byte as intended. Change that to unsigned char *buffer.
So explicitly, that line should be the following (including removing the unnecessary cast):
unsigned char *buffer = malloc(f_length);
I am trying to scan a file looking for 1MB JPEGs that would be stored contiguously. My approach is create a structure to match the first 4 bytes with the JPEG signature and, if true, write the entire 512 buffer to a named file until I find another jpeg signature then I create a new file. The code below creates 2 files, neither of which are readable as the first few bytes are not part of the jpeg signature. Any ideas where I went wrong? I tried variations of my if statement where I test for the signature but no luck thus far.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
typedef struct
{
BYTE first;
BYTE second;
BYTE third;
BYTE fourth;
}
JPGTEST;
int main(int argc, char* argv[])
{
FILE* inptr = fopen("card.raw", "r");
if (inptr == NULL)
{
printf("Could not open file\n");
return 2;
}
FILE* outptr;
//initialize jpeg count and variable for filename
int count = 0;
char name[8];
//allocate memory
char buffer[512];
JPGTEST myjpg;
int is_open = 0;
while (fread(&buffer, 512, 1, inptr) != 0)
{
//test first 4 bytes to see if jpeg
fread(&myjpg, sizeof(JPGTEST), 1, inptr);
//if match, name and write to file
if (myjpg.first == 0xff && myjpg.second == 0xd8 && myjpg.third == 0xff && (myjpg.fourth == 0xe0 || myjpg.fourth == 0xe1))
{
sprintf(name, "%03d.jpg", count);
if (is_open == 0)
{
outptr = fopen(name, "w");
fwrite(buffer, sizeof(buffer),1,outptr);
is_open = 1;
}
if (is_open == 1)
{
fclose(outptr);
outptr = fopen(name, "w");
fwrite(buffer, sizeof(buffer),1,outptr);
count++;
}
}
else
{
if (is_open == 1)
{
fwrite(buffer, sizeof(buffer),1,outptr);
}
}
}
fclose(inptr);
fclose(outptr);
return 0;
}
You are opening the files in text mode. You need to open them in binary mode instead:
FILE* inptr = fopen("card.raw", "rb");
outptr = fopen(name, "wb");
Aside from that, you are calling fread() too many times, and just generally not managing the files correctly.
Try something more this instead:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
#pragma pack(push, 1)
typedef struct
{
BYTE first;
BYTE second;
BYTE third;
BYTE fourth;
}
JPGTEST;
#pragma pack(pop)
int main(int argc, char* argv[])
{
FILE* inptr = fopen("card.raw", "rb");
if (inptr == NULL)
{
printf("Could not open file\n");
return 2;
}
FILE* outptr = NULL;
//initialize jpeg count and variable for filename
int count = 0;
char name[8];
//allocate memory
char buffer[512];
JPGTEST myjpg;
while (fread(buffer, sizeof(buffer), 1, inptr) > 0)
{
//test first 4 bytes to see if jpeg
memcpy(&myjpg, buffer, sizeof(JPGTEST));
//if match, name and write to file
if ((myjpg.first == 0xff) && (myjpg.second == 0xd8) && (myjpg.third == 0xff) && ((myjpg.fourth == 0xe0) || (myjpg.fourth == 0xe1)))
{
if (outptr != NULL)
{
fclose(outptr);
outptr = NULL;
}
++count;
sprintf(name, "%03d.jpg", count);
outptr = fopen(name, "wb");
}
if (outptr != NULL)
fwrite(buffer, sizeof(buffer), 1, outptr);
}
fclose(inptr);
if (outptr != NULL)
fclose(outptr);
return 0;
}
Replace your second fread inside the loop with the below:
memcpy((void *)&myjpg, (void *) buffer, sizeof(JPGTEST));
And include string.h header file in your code for memcpy function.
What you are doing wrong is that after reading first 512 you are not making any use of them rather you are again reading 4 bytes without checking first 4 bytes of previously read 512 bytes.
The problem is you are not resetting the file position indicator after you read into buffer, to do so use, fseek. I.e.:
//test first 4 bytes to see if jpeg
fseek (inptr, SEEK_SET, 0);
fread(&myjpg, sizeof(JPGTEST), 1, inptr);
That will provide you with a test of the jpg header in myjpg:
sizeof (myjpg): 4
first: ff second: d8 third ff fourth: e0
However, that will also cause logic issues you will have to rework. It is better to simply read the values from buffer as suggested by the other answer.
You can do something like the following to simplify your signature comparisons:
#include ...
#define JPEG_SIGNATURE 0xFFD8
void
reverse_bytes (void const *data, long unsigned size)
{
char *ptr = (char *)data;
int i = 0;
while (i < size / 2)
{
char temp = ptr[i];
ptr[i] = ptr[size - i - 1];
ptr[size - i - 1] = temp;
i++;
}
}
int
main()
{
FILE *fptr = fopen("path/to/image.jpeg", "rb");
short bytes = 0;
fread(&bytes, sizeof(char), sizeof(bytes), fptr);
reverse_bytes(bytes, sizeof(bytes)); // Refer to [1]
switch (bytes)
{
case JPEG_SIGNATURE:
printf("JPEG image!");
break;
default:
printf("Unknown format!");
break;
}
return 0;
}
This can be extended to many different formats by adding more cases in the switch statement and a little more work.
This is indeed not a full or proper answer but, I hope it is helpful for others who come through this post!
NOTE: I omitted things like exception handling for brevity!
References:
Why is Fread reading unsigned-int in reverse order?
I have to write a program where I take user input for a PPM image and then mirror the image from left to right (basically flipping it over the y-axis). So if the image was <, the new image would now be >. This should not be a 180 degree rotation, as that would make the image upside down. It should still be the same, just reflected.
I have the code here that I used to input and output the PPM, but I don't know how to mirror it. I made a function that would theoretically be used for the mirror code, but I'm not sure if that's the best way to do it either. If you know a better place to put the code, be my guest.
I have researched this topic quite a bit, but was only able to find issues where people needed to rotate the image.
Here is what I have so far:
#include<stdio.h>
#include<stdlib.h> //for fopen()
typedef struct {
unsigned char red,green,blue;
} pixel_t; //struct for pixels
typedef struct {
int x, y;
pixel_t *data;
} PPMImage; //struct for creating the image
#define RGB_COMPONENT_COLOR 255
static PPMImage *readPPM(const char *filename)
{
char buff[16];
PPMImage *img;
FILE *fp;
int c, rgb_comp_color;
fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);} //opens the ppm and checks to make sure it can be opened
if (!fgets(buff, sizeof(buff), fp)) {
perror(filename);
exit(1);} //read the format of the image
if (buff[0] != 'P' || buff[1] != '6') {
fprintf(stderr, "Invalid image format (must be 'P6')\n");
exit(1);} //checks to see if the format is ppm
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);} //allocates the memory needed to form the input image
c = getc(fp);
while (c == '#') {
while (getc(fp) != '\n') ;
c = getc(fp);
}//checks for comments
ungetc(c, fp);
if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
exit(1);} //reads the size of the image, height becomes img->y, and width becomes img->x
if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
exit(1);} //reads how much of each color there is
if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
exit(1);} //makes sure the the component is 8 bits
while (fgetc(fp) != '\n') ;
img->data = (pixel_t*)malloc(img->x * img->y * sizeof(pixel_t));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);} //allocates the memory need for the pixel data
if (fread(img->data, 3 * img->x, img->y, fp) != img->y) {
fprintf(stderr, "Error loading image '%s'\n", filename);
exit(1);} //reads the pixel data
fclose(fp);
return img;
}
void writePPM(const char *filename, PPMImage *img)
{
FILE *fp;
fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);} //opens the file for output
//write the header file
//image format
fprintf(fp, "P6\n");
//image size
fprintf(fp, "%d %d\n",img->x,img->y);
// rgb component depth
fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);
// pixel data
fwrite(img->data, 3 * img->x, img->y, fp);
fclose(fp);
}
void mirror(PPMImage *img)
{
//this is where I want to insert the code for mirroring the image
}
int main(int argc, char* argv[]){ //takes command line parameters
PPMImage *image;
char* filename = argv[1];
image = readPPM(filename);
mirror(image);
writePPM("OutputFile.ppm",image); //creates the output file
printf("Press Enter");
getchar();
}
Your mirror() function can work on the image one row at a time. For each row, take the left-most pixel in the row and swap its value with the value of the right-most pixel in the row. Then take the second-left-most pixel and swap its value with the second-right-most pixel, and so on, until the column-positions of the pixels you are swapping "meet in the middle". (Then move on to the next row and do the same thing to it, until you've done all the rows).
Note that if the image contains an odd number of columns, there will be one column in the center of the image that stays unmodified (since it forms the axis around which the mirroring occurs). With an even number of columns, all columns will be swapped.
To mirror image vertically you can use this function:
void mirrorVert(PPMImage *img)
{
int y;
int x;
const int middleX = img->x / 2;
pixel_t tmp;
pixel_t* p;
for (y = 0; y < img->y; ++y)
{
p = img->data + y * img->x;
for (x = 0; x < middleX; ++x)
{
// swap pixels
tmp = p[x];
p[x] = p[img->x - 1 - x];
p[img->x - 1 - x] = tmp;
}
}
}
And to mirror it horizontally:
void mirrorHoriz(PPMImage *img)
{
const int line_size = img->x * sizeof(pixel_t);
const int middle = img->y / 2;
int y;
// allocate swap buffer
pixel_t* buff = (pixel_t*)malloc(line_size);
pixel_t* top;
pixel_t* bottom;
for (y = 0; y < middle; ++y)
{
// swap lines from top and bottom
top = img->data + (y * img->x);
bottom = img->data + ((img->y - y - 1) * img->x);
memcpy(buff, top, line_size);
memcpy(top, bottom, line_size);
memcpy(bottom, buff, line_size);
}
}
I need to read a PPM file and store it in an array written in C.
Can anybody help me out doing this?
Thanks a lot.
The following code shows how to read, change the pixel colour and write an image in
PPM format. I hope it helps.
#include<stdio.h>
#include<stdlib.h>
typedef struct {
unsigned char red,green,blue;
} PPMPixel;
typedef struct {
int x, y;
PPMPixel *data;
} PPMImage;
#define CREATOR "RPFELGUEIRAS"
#define RGB_COMPONENT_COLOR 255
static PPMImage *readPPM(const char *filename)
{
char buff[16];
PPMImage *img;
FILE *fp;
int c, rgb_comp_color;
//open PPM file for reading
fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);
}
//read image format
if (!fgets(buff, sizeof(buff), fp)) {
perror(filename);
exit(1);
}
//check the image format
if (buff[0] != 'P' || buff[1] != '6') {
fprintf(stderr, "Invalid image format (must be 'P6')\n");
exit(1);
}
//alloc memory form image
img = (PPMImage *)malloc(sizeof(PPMImage));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//check for comments
c = getc(fp);
while (c == '#') {
while (getc(fp) != '\n') ;
c = getc(fp);
}
ungetc(c, fp);
//read image size information
if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
exit(1);
}
//read rgb component
if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
exit(1);
}
//check rgb component depth
if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
exit(1);
}
while (fgetc(fp) != '\n') ;
//memory allocation for pixel data
img->data = (PPMPixel*)malloc(img->x * img->y * sizeof(PPMPixel));
if (!img) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
//read pixel data from file
if (fread(img->data, 3 * img->x, img->y, fp) != img->y) {
fprintf(stderr, "Error loading image '%s'\n", filename);
exit(1);
}
fclose(fp);
return img;
}
void writePPM(const char *filename, PPMImage *img)
{
FILE *fp;
//open file for output
fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
exit(1);
}
//write the header file
//image format
fprintf(fp, "P6\n");
//comments
fprintf(fp, "# Created by %s\n",CREATOR);
//image size
fprintf(fp, "%d %d\n",img->x,img->y);
// rgb component depth
fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);
// pixel data
fwrite(img->data, 3 * img->x, img->y, fp);
fclose(fp);
}
void changeColorPPM(PPMImage *img)
{
int i;
if(img){
for(i=0;i<img->x*img->y;i++){
img->data[i].red=RGB_COMPONENT_COLOR-img->data[i].red;
img->data[i].green=RGB_COMPONENT_COLOR-img->data[i].green;
img->data[i].blue=RGB_COMPONENT_COLOR-img->data[i].blue;
}
}
}
int main(){
PPMImage *image;
image = readPPM("can_bottom.ppm");
changeColorPPM(image);
writePPM("can_bottom2.ppm",image);
printf("Press any key...");
getchar();
}
Here is the
PPM specification.
The PPM file is built in 9 sections separated by white-spaces.
Open the file
read until the first white space and check you got P6. Then skip other white-spaces.
read until the next white space, convert your buffer to an integer width. Then skip other white-spaces
read until the next white space, convert your buffer to an integer height. Then skip other white-spaces
Allocate a 2D array of integers in the size of height*width
read the max-val
read line by line and fill the array