ZAMYLA CHAN: Let's enlarge our C repertoire with resize.
In resize, we're given an infile bitmap, and our job
is to create an outfile file that scales that bitmap by n, a user-given number.
OK, so let's break this down.
Today, we'll talk about how to open the in-file, then create an outfile
and update that outfile's header information.
Then we'll read into the infile scan line, pixel by pixel,
resizing horizontally as we go.
It's going to be really important to talk about padding
and understand exactly where to put it in, and when to skip over it.
Finally, we'll talk about resizing the file vertically, as well.
Now this is a lot to do, but luckily, we have a good starting point,
because copy.c actually implements a lot of this functionality.
Copy.c will open a file, update the header information, then
it will read each scanline-- pixel by pixel from the infile--
and then write those pixels into the outfile scanline.
So even though it doesn't necessarily resize the file for us,
it's a great place to start.
So let's copy the code from copy.c into resize.c.
All right, so now that we've done that, we
want to update the outfile header information.
So let's talk about bitmaps for a sec.
Bitmaps are simply an arrangement of bytes, just like any other file
you'll encounter.
It's just a matter of how we interpret this arrangement of bytes.
So take a peek inside bmp.h for more information.
With a resize bitmap, we'll have an updated header.
So what's changing in the header?
Well, our file size is changing, our image size,
and of course, the width and height of our file.
So what are those variables, exactly?
Well, within the bitmap info header, we have
biWidth, which indicates the width of the image
in pixels, not including padding.
More on padding in a sec.
Then we also have biHeight, which represents
the height of the image in pixels.
Last in the bitmap in full header we have the biSizeImage,
which is the total size of the image in bytes, including pixels and padding.
Then we move to the bitmap file header, where bfSize includes
the total size of the file in bytes.
So this includes pixels, padding, and the headers.
So the bfSize can be calculated by adding
the size of the image from the bitmap info header,
and adding that to the size of those headers in bytes.
OK, so when we scale the image by n, what's changing?
Well, pretty obviously, the width and the height
are both going to be scaled by n.
But what's happening to biSizeImage, and bfSize?
Up to you to figure that out.
So now that we've dealt with the header, let's start looking at the pixels.
So to read into the infile scanline, copy.c
calls the function fread, which takes in four parameters.
The first is a pointer to the struct that will contain the bytes that you're
reading, and then the size of each element to read,
then the number of those elements to read, followed by the file pointer
that you want to read those from.
So now that we know how to read into the scanline for the infile,
let's talk about resizing.
We're going to tackle resizing horizontally first.
For this, essentially what we need to do is for each pixel in the row,
we want to write it n times.
So for an n of 2, I'll go to my orange pixel and write that twice.
Then proceed to my red pixel, write that twice.
And finally my blue pixel, I'll write twice, as well.
We call upon the function fwrite to write those pixels into our outfile.
Fwrite takes in four parameters-- the pointer
to the struct that contains the bytes you're reading from,
then the size and number of the elements you want to write,
and finally, the destination of those files as a file pointer.
Now that we've talked about resizing horizontally,
it's time to get to the padding.
In a bitmap, each pixel is represented with three bytes.
Now the length of each scanline of a bitmap
must be a multiple of four bytes.
Now, if the number of pixels is not a multiple of four,
then we'll need to include padding, where padding its just zeroes.
Here's an example where I have four RGBtriples.
Each triple is comprised of three bytes.
So four triples, three bytes each, that's equal to 12 bytes.
Because that's a multiple of four, I don't need any padding.
Now say I have five RGBtriples-- five triples times
three bytes each equals 15.
Not a multiple of four.
So I need to add one unit of padding in order
to reach a multiple of four bytes.
If I have, say, six triples, then I'll need
to add padding, as well, but a different amount than last time.
In this case, two zeroes.
Finally, in this example, if I have three triples, than 3 times 3 is 9.
So in order to bump it up, I need to add three units of padding.
It's really important to understand the concept of padding.
Don't worry about the equation, I've provided it here,
and you can also find it in the problem specification.
You'll notice that the padding takes into account the size of an RGBtriple,
as well as the width of the image in pixels, found in the bi header.
Because we're resizing the outfile, the outfile and the infile
will have different widths.
So the padding will most likely be different.
The other important fact to remember is that padding isn't an RGBtriple-- so we
won't be f-reading, or f-writing it in.
Instead, to write padding, will use the fputc function,
which takes in a character to write, and then the file pointer to write it to.
So in this case, we'll be putting in a 0 for the padding into our outfile.
Let's look at an example using any n equals 2 for simplicity.
At the first pixel, we'll resize horizontally,
so we'll write that twice into the outfile.
Moving on to the second triple, we'll also write that twice,
resizing horizontally.
Now you'll notice here that while the infile requires padding,
the outfile doesn't.
The infile only has two pixels, which results in six bytes--
so it needs two units of padding to reach a multiple of four
bytes for the scanline.
Whereas, the outfile has four triples, so that
results in a multiple of four bytes for that scanline
without needing any padding.
The key takeaway here is that the padding
may very well be different from the infile to the outfile.
Looking back to the equation for padding,
we know that this depends on the biWidth.
But recall from earlier, we know that biWidth is going
to be changing from the old to the new.
So perhaps we need to calculate how the padding is
going to change, but also keep track of both the old and the new padding.
With that said, let's go back to some pseudocode code.
Now this is pseudocode code just for resizing horizontally-- it's
not going to be the final algorithm for your entire problem.
But to resize horizontally for each row, and for each pixel in the row,
we want to write to the outfile n times.
Then we want to write the outfiles padding, once we finish that row.
But then skip over the infiles padding so that we
reach the next row in the infile.
Try to see if you can implement resize by simply stretching by n horizontally
first.
Then we can move on to resizing vertically.
In resize, every pixel is repeated n times.
That's the horizontal resizing component.
But then every row is also repeated n times.
So say I have an n of three, and a single pixel.
Than that pixel is going to be repeated three times in a row,
then that row is going to be repeated three times, as well.
All right, so now let's move on to a more complex example with more
than one pixel needed to resize.
I'll go to my first pixel and write that twice, then go to the next,
write that twice, and then the third, write that twice.
We've already achieved this by resizing horizontally.
Resizing vertically entails repeating that process another time,
so that we've repeated the row twice.
Only then will we move on to the next row in the infile,
and repeat that same process.
There are two overarching methods in which you can resize vertically,
and even within those methods, there are multiple options for implementation.
So the first type of method is a rewrite method,
in which you remember the pixels that need to be written in the array,
and then write that array as many times as needed.
Or there's the recopy method, in which you go back
to the start of the original scanline, and recopy that line as necessary.
I've mentioned a couple times now seeking back or skipping
forward padding, lines, et cetera.
What I mean by this, is that we need to move the file position indicator.
The file position indicator is essentially
a cursor-- whenever you fread into a file,
there is a cursor that keeps track of where you are in that file.
If you ever want to move that, then you call
upon fseek-- which takes in the file pointer that you're seeking in,
the number of bytes that you want to move the cursor, and then
finally, whether or not you want to move relative to the current position
in the file, the beginning of the file, or the end.
Now that you're equipped with fseek, we can
talk about pseudocode for the rewrite method.
Now remember that even within the rewrite method,
there are multiple ways to implement this.
So your pseudocode in your implementation
might very well be different than mine, and that's OK-- as long as it works,
and the basic premise is the same.
That for each row, we're going to make a copy of the array
that we want to write, then for n times, we'll write that array to the outfile,
and write the outfile padding.
Once we're done, we'll fseek over the infile padding,
and then repeat for the next row.
Now we come to the recopy method.
In the recopy method, the infile cursor starts at the beginning of a row.
Then for n minus 1 times, we'll want to write the pixel scaling horizontally
to the outfile, and adding the padding to the outfile as necessary.
Then once we've written that, we'll send the infile cursor back
to the beginning of the row, and repeat that process.
After we've done that n minus 1 times, then we'll write the pixels once more,
scaling horizontally, adding padding to the outfile.
Only then will we skip over the infile padding, bringing us to the next row,
ready to repeat the process.
We're in the homestretch-- so let's just wrap up and summarize, and make sure
that we're on the same page.
We want to open the infile and create an outfile,
updating the outfile's header information appropriately,
and keeping track of whichever variables we need to from the infile.
Then, we'll read into that infile scanline pixel
by pixel, resizing horizontally, remembering to add padding,
and resizing vertically.
Now, I admit, my own code didn't work perfectly
the first time that I tried resize.
And yours might not, either.
So if that happens, make sure to put pen to paper,
draw out your pixels-- perhaps a simple smiley, or just a box--
and follow through your own code to make sure that the logic works.
And you might find your bug that way.
Good luck.
My name is Zamyla and this was resize.