Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The cs50 resize problem set part 1
/*
Takes in a bmp file
and copies it to a new file
*/
#include <stdio.h>
#include <stdlib.h> // abs()
#include "bmp.h" // bmp structs
int get_padding(int width,int pixel_size);
int main(int argc, char* argv[])
{
//------------------------------------//
//~*~*~*~*VALIDATE USER INPUTS*~*~*~*~//
//-----CREATE READ/WRITE POINTERS-----//
// user entered correct number of arguments
if (argc != 3)
{
printf("Correct usage: <infile> <outfile>\n");
return 1;
}
//printf("%s %s %s\n", argv[0], argv[1], argv[2]);
// NAME OUR USER INPUT ARGUMENTS
char* input_file_name = argv[1];
char* output_file_name = argv[2];
// create infile pointer and check if valid
FILE* input_file_pointer = fopen(input_file_name, "r");
if (input_file_pointer == NULL)
{
printf("File not found, please use a valid file to copy\n");
return 2;
}
// create the outfile pointer while we're at it
FILE* output_file_pointer = fopen(output_file_name, "w");
if (output_file_pointer == NULL)
{
printf("File could not be created!\n");
return 3;
}
//------------------------------------//
//~*~*~*~*~READ/WRITE HEADERS*~*~*~*~*//
//------------------------------------//
// create a variable to hold our first struct from bmp.h
BITMAPFILEHEADER bm_header;
/*
our input pointer is at the beginning of the file
where this data lives. We have to pull in the correct
number of bytes using sizeof(). We only have to pull it
in 1 time.
*/
// fread(destination, size of 1 element, # of elements, source)
fread(&bm_header, sizeof(BITMAPFILEHEADER), 1, input_file_pointer);
/*
we've taken data from the infile and put it in a variable
which we can use to write to our new outfile
*/
// fwrite(source, size of 1 element, # of elements, destination)
fwrite(&bm_header, sizeof(BITMAPFILEHEADER), 1, output_file_pointer);
/*
next we'll repeat this process for the file info section
(it's called file info header but we already did the header
and that makes things less clear)
*/
BITMAPINFOHEADER bm_file_info;
// pull info from input into our variable
fread(&bm_file_info, sizeof(BITMAPINFOHEADER), 1, input_file_pointer);
// push info from our variable to our output
fwrite(&bm_file_info, sizeof(BITMAPINFOHEADER), 1, output_file_pointer);
// now the confusing part...
/*
At this point the input pointer has moved along without us
having to think about it. Imagine a video on youtube just playing
along. Now what if the video you were watching had a part you wanted
to skip over. You would click on the small bar below the video
to skip over a section. We have to do that in a way here for our
image copy program. We want to skip over the padding and then
add it back in ourselves manually. This will be a more useful
technique when we have to resize the image.
*/
/*
in bmp.h we have a struct that represents a single pixel
this is the RGBTRIPLE struct.
We need to know how many are in a single scanline
and how many total rows exist in the image.
We can find out the number of pixels for the width
and height by accessing those values in the
BITMAPHEADERINFO struct we read into.
*/
/*
To make things more complicated we have to add padding
which is an arbitrary byte of zeros represented as 0x00.
We're given the formula for padding so let's hide all that
mess in a function so we don't have to think about it.
get_padding() is all we need to know.
*/
/*
To make things EVEN MORE COMPLICATED
the height in some files is negative which is another issue
we have to solve. Luckily we just have to get the absolute value
which will turn -42 into 42. We'll use our abs() function for this
*/
// ONWARD!!!!!!
// Get all our values ready for looping
int width = bm_file_info.biWidth;
int height = abs(bm_file_info.biHeight);
int padding = get_padding(width, sizeof(RGBTRIPLE));
// uncomment and try without the abs() to see what it does!
// printf("height is %d and padding is %d\n", height, padding);
/*
This is not so different from the headers, because we're just
reading a struct and writing it out.
We have to think about
rows and columns which means a nested loop.
draw on a piece of paper a row of 4 little boxes
then make 2 more rows so you end up with this:
[] [] [] []
[] [] [] []
[] [] [] []
we have to loop through left to right the width of the matrix
then the height. So our first loop is the height because we START
with Row 1 Column 1 then Row 1 Column 2 etc etc.
There will be padding that needs to be added before we
loop to the next row. So that part lives under the loop
that goes through each pixel in a single row. More on that later
*/
// Create a variable to hold a single pixel
RGBTRIPLE pixel;
// LOOPING!
// outer loop goes to each row
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
/*
there is no need to keep track of our pointer
for these reads because the pointer moves on
to the next pixel each time we run
the fread() function
*/
// read it into our variable
fread(&pixel, sizeof(RGBTRIPLE), 1, input_file_pointer);
// write it out from our variable
fwrite(&pixel, sizeof(RGBTRIPLE), 1, output_file_pointer);
}
/*
THIS IS THE YOUTUBE-esque skipping part!
We want the input point to skip over the padding
so the outer loop brings us right to the next row
of pixels.
Then we want to manually add padding to our output file
so everyone is on the same page when reading and writing.
Remember we are keeping track of 2 pointers here. Draw
those squares and draw a little "I" and "O" on each little
square. At the end of the row draw a few squiggly lines for
padding:
[IO] [] [] [] }}}
When we do an fread, the I moves
[O] [I] [] [] }}}
When we do an fwrite, the O moves
[] [OI] [] [] }}}
once we get to the end we're going to manually skip the Input
over the padding and which will put it right where it needs to be
to write the next RGBTRIPLE. Then we will manually add the padding
to our Output so it keeps in line with the input.
int fseek(pointer, offset, reference point)
- pointer is the file pointer we want to manipulate
- offset means by how much (positive numbers go forward
negative numbers send it backward)
- fseek has 3 options for the reference point, which means:
you can move from where the pointer currently is in your
program (SEEK_CUR)
you can move based on the very beginning of the file
(SEEK_SET)
you can move based on the very end of the file
(SEEK_END)
We'll be using SEEK_CUR here
*/
// move the input pointer ahead, past row (scanline) of padding
fseek(input_file_pointer, padding, SEEK_CUR);
/*
now we add the same amount of padding in a loop to the
output. Padding is just a byte of zeros so in hex this is
represented as 0x00. So we'll write 0x00 <padding variable>
number of times.
Because our value is exactly the size of a char we can use
the simpler fputc() function
fputc(char sized value, destination);
- char sized value is any char sized value like our 0x00
- destination is the pointer we want to use, in this case
put that char in the output pointer!
NOTE: no quotations when adding hex values
wrong: "0x00"
right: 0x00
For more on how hex values work in C check this out:
https://tinyurl.com/yc7vdp5e
*/
for(int k = 0; k < padding; k++)
{
fputc(0x00, output_file_pointer);
}
}
/*
Close the streams for our input and output pointers. This
frees up any unused resources back to our system.
*/
fclose(input_file_pointer);
fclose(output_file_pointer);
return 0;
}
// helper functions
int get_padding(int width, int pixel_size)
{
return ( 4 - ( width * pixel_size ) %4 ) % 4;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment