0

I have been trying to figure this out on my own for a long time. No success. Then I tried to look for online help but could not find specifically what I wanted. It's true that I found alternative ways to do it but my issue is more related to why doesn't it work the way it is now coded. I need to understand why so to proceed forward. If not I will not be consolidating my learning.

The outfile seems to be sizing correctly when the resizing is done by a factor of 2 or more but the colors are all wrong on the lines that are being duplicated. It also does not work when using a factor of 1: in this case the picure is correct but the hexa code seems all wrong.

Anyway, here is the code. Your help will be greatly appreciated:


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#include "bmp.h"

int main(int argc, char *argv[])
{
    // ensure proper usage
    float n = atof(argv[1]);

    if (n == 0)
    {
        printf("Usage: ./resize n infile outfile\n");
        return 1;
    }

    else if ((argc != 4) || (n / trunc(n)) != 1 || (n < 1) || (n > 100))
    {
        printf("Usage: ./resize n 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)
    {
        printf("Could not open %s.\n", infile);
        return 2;
    }

    // open output file
    FILE *outptr = fopen(outfile, "w");
    FILE *outptread = fopen(outfile, "r");
    if (outptr == NULL || outptread == NULL)
    {
        fclose(inptr);
        printf("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);
        printf("Unsupported file format.\n");
        return 4;
    }

    int Factor = n;

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

    // determine padding for scanlines in new file
    int NewPadding = (4 - (bi.biWidth * Factor * sizeof(RGBTRIPLE)) % 4) % 4;

    // store original width and height
    int OriginalWidth = abs(bi.biWidth);
    int OriginalHeight = abs(bi.biHeight);

    // delete afterwards
    printf("Factor: %i\nWidth: %i\nHeight: %i\n", Factor, bi.biWidth, bi.biHeight);
    printf("bfSize: %i\nbfoffbits: %i\nbisizeimage: %i\nOriginalPadding; %i\nNewpadding: %i\n", bf.bfSize, bf.bfOffBits, bi.biSizeImage, OriginalPadding, NewPadding);
    printf("\n");

    // change key information BITMAPFILEHEADER and BITMAPFILEHEADER due to resizing
    bi.biWidth = bi.biWidth * Factor;
    bi.biHeight = bi.biHeight * Factor;
    bf.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + abs(bi.biWidth) * abs(bi.biHeight) * sizeof(RGBTRIPLE) + NewPadding * abs(bi.biHeight) * sizeof(BYTE);
    bi.biSizeImage = bf.bfSize - bf.bfOffBits;

    // delete afterwards
    printf("Factor: %i\nWidth: %i\nHeight: %i\n", Factor, bi.biWidth, bi.biHeight);
    printf("bfSize: %i\nbfoffbits: %i\nbisizeimage: %i\nOriginalPadding; %i\nNewpadding: %i\n", bf.bfSize, bf.bfOffBits, bi.biSizeImage, OriginalPadding, NewPadding);

    // create temp variable to store each scanline in outputfile
    int ScanlineBytes = sizeof(RGBTRIPLE) * bi.biWidth + sizeof(BYTE) * NewPadding;
    BYTE* Scanline = malloc(sizeof(BYTE) * ScanlineBytes);

    printf("Size of Scanline in Bytes: %i\n", ScanlineBytes); // delete afterwards

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

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

    // iterate over infile's scanlines
    for (int i = 0; i < OriginalHeight; i++)
    {
        // iterate over pixels in scanline
        for (int j = 0; j < OriginalWidth; j++)
        {
            // temporary storage
            RGBTRIPLE triple;

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

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

        }

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

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

        // write (Factor - 1) additional scanlines for new height
        fseek(outptread, -1 * ScanlineBytes, SEEK_END);
        fread(&Scanline, ScanlineBytes, 1, outptread);
        for (int z = 0; z < (Factor - 1); z++)
            {
                fwrite(&Scanline, ScanlineBytes, 1, outptr);
            }

    }

    // close infile
    fclose(inptr);

    // close outfiles
    fclose(outptr);
    fclose(outptread);

    // free Scanline
    free(Scanline);

    // success
    return 0;
}

1

The code opens two pointers to the output file, outptr to write and outptread to read from the output file. When the code writes to the output file, it also updates values in the file pointer struct outptr, but it doesn't update the outptread pointer. That means that SEEKEND associated with outptread is still pointing at the end of the file when it only contained the headers.

If you look at the raw data contents of the output file, you'll see that the data that should contain the second copy of each line actually contains data from the end of the header of the same length. The read pointer is not being updated to show that the output file has had additional data added.

That's why it doesn't work.

I'll add that this is probably the first time I've seen this approach. It can be made to work, and it is unique, but not the most efficient. File reads and writes are terribly inefficient compared to memory reads and writes, so it would be better to store the line in memory and write it out as many times as needed.

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

1
  • 1
    That was perfectly clear including the part of it being ineficient. It makes sense as file reading and writing is not as fast as using memory. Thank you very much for your help. I will try to get this to work even if it is not the most efficient way but will remember your valuable input in future similar situations. – Pedro Lemos de Mendonça Apr 18 '19 at 8:12

You must log in to answer this question.

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