简体   繁体   中英

How to resize a bitmap (BMP) in C - my code is not working

I've been trying to fix this code. Everything seems to be working fine based on the printf tests I've added but the new image isn't resizing horizontally.

This is for an online course I'm taking so the coding may look elementary but we may not have covered more advanced solutions yet.

I've pasted both programs needed to execute the program and also included the pics.

Execute the program using this argument or similar ./copy 2 staff.bmp large.bmp .

要调整大小的图像 调整大小后的图像

// Copies a BMP file

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

#include "bmp.h"

int main(int argc, char *argv[])
{
    // ensure proper usage
    if (argc != 4)
    {
        fprintf(stderr, "Usage: copy infile outfile\n");
        return 1;
    }


    // remember filenames
    int num = atoi(argv[1]);
    char *infile = argv[2];
    char *outfile = argv[3];

    if (num < 1 || num > 100)
    {
        fprintf(stderr, "Resize only 1-100. Try again.\n");
        return 4;
    }

    // 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;
    }

    int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
    printf("padding%i ", padding);

    BITMAPINFOHEADER newbi = bi;
    BITMAPFILEHEADER newbf = bf;

    newbi.biWidth = bi.biWidth * num;
    printf("newbiwidth%i ", newbi.biWidth);

    newbi.biHeight = bi.biHeight * num;
    printf("newbiheight%i ", newbi.biHeight);

    // determine padding for scanlines
    int newpadding = (4 - (newbi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
    printf("newpadding%i ", newpadding);

    newbi.biSizeImage = (newbi.biWidth * sizeof(RGBTRIPLE) + newpadding) * abs(newbi.biHeight);
    printf("newbisizeimage%i ", newbi.biSizeImage);

    newbf.bfSize = newbi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    printf("newbfsize%i ", newbf.bfSize);

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

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



    // temporary storage
    RGBTRIPLE triple;
    RGBTRIPLE *newtriple = malloc(num * sizeof(RGBTRIPLE) + 1);
    RGBTRIPLE *sline = malloc(newbi.biWidth * sizeof(RGBTRIPLE));

    // read RGB triple from infile
    for (int i = 0, biheight = abs(bi.biHeight); i < biheight; i++)
    {
        printf("H%i  ", i);
        for (int j = 0; j < bi.biWidth; j++)
        {
            printf("W%i  ", j);
            fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

            //store triple times resize number
            for (int m = 0; m < num; m++)
            {
                newtriple[m] = triple;
                printf("T%i  ", m);
            }

                    //store new triple as new scanline
                    sline[j] = *newtriple;
                    printf("S%i  ", j);
        }

            //write new scanline to file
            for (int k = 0; k < num; k++)
            {
                printf("F%i  ", k);
                fwrite(sline, newbi.biWidth * 3, 1, outptr);

                // add padding if any
                for (int h = 0; h < newpadding; h++)
                {
                    fputc(0x00, outptr);
                }

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

    }
    free(newtriple);
    free(sline);

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);


    // success
    return 0;
}

// BMP-related data types based on Microsoft's own

#include <stdint.h>

// aliases for C/C++ primitive data types
// https://msdn.microsoft.com/en-us/library/cc230309.aspx
typedef uint8_t  BYTE;
typedef uint32_t DWORD;
typedef int32_t  LONG;
typedef uint16_t WORD;

// information about the type, size, and layout of a file
// https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx
typedef struct
{
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;

// information about the dimensions and color format
// https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
typedef struct
{
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;

// relative intensities of red, green, and blue
// https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx
typedef struct
{
    BYTE rgbtBlue;
    BYTE rgbtGreen;
    BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
for (int j = 0; j < bi.biWidth; j++) { ... sline[j] = *newtriple; }

This is only setting pixels from 0 to the original width, for each row. So for example, if you double the width, then the second right half of the image is not initialized, which is what appears in your output image. You should set all the pixels as shown below. You don't actually need newtriple

Also make sure to skip for padding immediately after reading each row.

for(int j = 0; j < bi.biWidth; j++)
{
    fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
    for(int m = 0; m < num; m++)
    {
        sline[j * num + m] = triple;
    }
    //sline[j] = *newtriple; <- remove
}
fseek(inptr, padding, SEEK_CUR); //<- move fseek here

What sline[j * num + m] is doing:

Lets say we have this row:

sline_in[4]: 0 1 2 3 4

And we want to zoom in with a factor 2. It should become:

sline_out[8]: 0 0 1 1 2 2 3 3 4 4

We use the loop:

for(int j = 0; j < width; j++)
{
    sline_out[j * 2 + 0] = sline_in[j];
    sline_out[j * 2 + 1] = sline_in[j];
}

For an arbitrary zoom factor, we use the following:

for(int j = 0; j < width; j++)
{
    for(int m = 0; m < zoom; m++)
        sline_out[j * zoom + m] = sline_in[j];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM