0

Despite all my attempts, I could not figure out how to fix my code to work properly. The vertical rows come out, but they always are different, random colors. On the other hand, the horizontal rows come out perfectly.

// Copies a BMP file

#include <stdio.h>
#include <stdlib.h>
#include <cs50.h>

#include "bmp.h"

int main(int argc, char *argv[])
{

int n = atoi(argv[1]);
// ensure proper usage
if (argc != 4 || (n > 100 || n < 0))
{
    fprintf(stderr, "Usage: copy infile outfile\n");
    return 1;
}

// remember filenames
char *infile = argv[2];
char *outfile = argv[3];

// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
    fprintf(stderr, "Could not open %s.\n", infile);
    return 2;
}

// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
    fclose(inptr);
    fprintf(stderr, "Could not create %s.\n", outfile);
    return 3;
}

// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);

// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);

// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
    bi.biBitCount != 24 || bi.biCompression != 0)
{
    fclose(outptr);
    fclose(inptr);
    fprintf(stderr, "Unsupported file format.\n");
    return 4;
}

// determine padding for scanlines
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

// stores the original biWidth and biHeight in variables
int originalbiWidth = bi.biWidth;
int originalbiHeight = bi.biHeight;

// multiplies the biWidth and biHeight by n
bi.biWidth *= n;
bi.biHeight *= n;

// determines padding for scanlines after they are scaled by a factor of n
int outputPadding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

bi.biSizeImage = (bi.biWidth * sizeof(RGBTRIPLE) + outputPadding) * abs(bi.biHeight);
bf.bfSize = bi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);

// write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);

// array which temporarily allocates memory to write an RGB triple into the array n times
RGBTRIPLE *tripleArray = malloc(bi.biWidth * sizeof(RGBTRIPLE));
if (tripleArray == NULL)
    {
        fclose(outptr);
        fclose(inptr);
        fprintf(stderr, "Not enough memory for operation.");
        return 5;
    }

// iterate over infile's scanlines
for (int i = 0, biHeight = abs(originalbiHeight); i < biHeight; i++)
{
    // iterate over pixels in scanline
    for (int j = 0; j < originalbiWidth; j++)
    {
        // temporary storage
        RGBTRIPLE triple;

        // read RGB triple from infile
        fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

        for (int k = 0; k < n; k++)
        {
            // write RGB triple to outfile
            fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
        }
    }

    // write RGBTRIPLEs in ith row to tripleArray
    for (int l = 0; l < bi.biWidth; l++)
    {
        tripleArray += l;

        // temporary storage
        RGBTRIPLE triple;

        // read RGBTRIPLE from outfile
        fread(&triple, sizeof(RGBTRIPLE), 1, outptr);
        *tripleArray = triple;

        if (l == bi.biWidth - 1)
        {
            tripleArray -= l;
        }
    }

    // skip over padding, if any
    fseek(inptr, padding, SEEK_CUR);

    // then add it back (to demonstrate how)
    for (int k = 0; k < outputPadding; k++)
    {
        fputc(0x00, outptr);
    }

    // write scanfile to output file n times
    for (int a = 0; a < n; a++)
    {
        // write RGBTRIPLES in tripleArray to next line in output file
        for (int m = 0; m < bi.biWidth; m++)
        {
            tripleArray += m;
            fwrite(&tripleArray, sizeof(RGBTRIPLE), 1, outptr);
        }

        // add padding to the new vertical lines
        for (int o = 0; o < outputPadding; o++)
        {
            fputc(0x00, outptr);
        }
    }

}

free(tripleArray);

// close infile
fclose(inptr);

// close outfile
fclose(outptr);

// success
return 0;
}
1

The j loop in this form makes no sense to me, the l and the a loop should cover input and output separately. This includes the first loop writing an output padding. Something with the j loop is right, though, and it's the boundaries and nesting.

While it is possible to add to and subtract from pointers, you're doing it mostly wrong, leaving tripleArray pointing to a totally different point in RAM.

You need to make sure you read originalbiWidth pixels, copying each n times into your tripleArray. This could be done with pointer arithmetics, first copying a pixel into *tripleArray, then incrementing tripleArray, and after the originalbiWidth loop resetting the pointer by subtracting bi.biWidth.

Or, go with array syntax, which I would prefer. Less fancy, much more readable. The index to write can be calculated from the indices of the nested loops, or use a counter you increment.

You could handle that in the a loop in a similar way, but there is an easier one: fwrite takes two numbers, a block size and a number of blocks to write (that's contiguous blocks, not same block a number of times). So you can fwrite(tripleArray, sizeof(RGBTRIPLE), bi.biWidth, outptr); and it will write the whole memory range at once, no need for an m loop or array/pointer arithmetics.

Additionally, &tripleArray is the memory address of the variable, not the memory address it is pointing to (that would be tripleArray).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .