Resizing a BMP image (making it smaller)

I assume you want to shrink the image by skipping rows and columns using the variables w, h, and diff. For instance, if we set the scaling factor f to 0.5, diff is assigned to 1, and every other rows/columns will be skipped to scale the image by 0.5x. Then there are two crutial issues in the loop with i and j:

  • You are resetting w in if(w==diff){ w=0; } just after w++;. Then w keeps being 0 and no columns are skipped.
  • You are putting the if(h==0){ condition in outer block. Then the pixels are not read while h==0. In order to shrink the image, you need to keep on reading every pixels regardless of the condition, and write the pixel if the conditions meet.

Then the loop will be improved as:

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

            // read RGB triple from infile
            fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
            if (w == 0 && h == 0) {
                // write RGB triple to outfile
                fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
            }
            w++;
            if (w > diff){
               w = 0;
            }
        }
        // skip over padding, if any
        fseek(inptr, padding, SEEK_CUR);

        // padding to the output file, if any
        if (h == 0) {
            for (int k = 0; k < padding_resize; k++) {
                fputc(0x00, outptr);
            }
        }

        h++;
        if (h > diff){
            h = 0;
        }
    }

It will work under limited conditions: f=0.5 and the image width and height are even numbers. But in general conditions it still does not work well. For instance, if we set f to 0.4, bi_resize.biWidth and bi_resize.biHeight will be calculated with multiplying original size by 0.4, while diff will be calculated to 1. Then they cause conflicts between the header information and the actual pixels.

Here is another hint to solve the problems:

  • The common approach to manipulate image is to store the entire pixels into the memory at first. Then you can random-access any pixels in the following process and the code will be more straightforward. Basic idea is to iterate over the destination coordinates and retroject back to the coordinates of the source image to pick the pixel values.
  • Your posted input image is too small to identify the problem, because thin lines and small dots are easily collapsed just with filtering even if the algorithm is appropriate. Better to use larger images to evaluate.

Here is my rewrite based on your code:

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

#include "bmp.h"

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

    // read the scaling factor
    float f = atof(argv[1]);
    if (f <= 0 || f > 1) {
        fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
        return 1;
    }
    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;
    }

    BITMAPFILEHEADER bf_resize = bf;
    BITMAPINFOHEADER bi_resize = bi;
    bi_resize.biWidth = bi.biWidth * f;
    bi_resize.biHeight = bi.biHeight * f;
    int padding = bi.biWidth % 4;       // you can simplify the calculation
    int padding_resize = bi_resize.biWidth % 4;
    bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * bi_resize.biHeight;
    bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // allocate mamory for the rgb triplets of the original (input) image
    RGBTRIPLE *pix = malloc(sizeof(RGBTRIPLE) * bi.biWidth * bi.biHeight);
    if (pix == NULL) {
        fprintf(stderr, "malloc failed.\n");
        return 5;
    }
    // temporary storage
    RGBTRIPLE triple;

    // read the entire pixels of the original image and store into the memory
    for (int i = 0; i < bi.biHeight; i++) {
        for (int j = 0; j < bi.biWidth; j++) {
            fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
            pix[i * bi.biWidth + j] = triple;
        }
        // skip over padding, if any
        fseek(inptr, padding, SEEK_CUR);
    }

    // write outfile's header
    fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
    fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);

    // write the pixels of destination (resized) image
    for (int i = 0; i < bi_resize.biHeight; i++) {
        for (int j = 0; j < bi_resize.biWidth; j++) {
            // calculate the corresponding coorinates in the original image
            int m = (i / f + 0.5);              // +0.5 for rounding
            if (m > bi.biHeight - 1) {          // limit the value
                m = bi.biHeight - 1;
            }
            int n = (j / f + 0.5);
            if (n > bi.biWidth - 1) {
                n = bi.biWidth - 1;
            }
            // pick the pixel value at the coordinate
            triple = pix[m * bi.biWidth + n];
            // write RGB triplet to outfile
            fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
        }
        // padding for the output image, if any
        for (int j = 0; j < padding_resize; j++) {
            fputc(0x00, outptr);
        }
    }
    free(pix);
    fclose(inptr);
    fclose(outptr);

    return 0;
}

Input image: enter image description here Output image with f=0.4: enter image description here