How to crop a BMP image in half using C

75 Views Asked by At

I am writing a program that reads a bmp file, and then outputs the same BMP file but cropped in half. I'm doing this by simply dividing the bmp height in half, and that results in the top half of the BMP image being cropped out. However, I can only crop out the top half. I am trying to find a way to crop out the bottom half of the BMP image, but it seems that the pixels begin writing from the bottom of the file and I am trying to get them to start halfway through the file, or maybe start from the top down.

// Update the width and height in the BMP header
    header.width = newWidth;
    header.height = newHeight;

    printf("New header width: %d\n", header.width);
    printf("New header height: %d\n", header.height);

    // Write the modified BMP header to the output file
    fwrite(&header, sizeof(BMPHeader), 1, outputFile);

    // Calculate the padding
    int padding = (4 - (header.width * (header.bpp / 8)) % 4) % 4;

    // Copy the image data
    unsigned char pixel[4];
    for (int y = 0; y < newHeight; y++) {

            for (int x = 0; x < header.width; x++) {
                fread(pixel, sizeof(unsigned char), header.bpp / 8, inputFile);
                fwrite(pixel, sizeof(unsigned char), header.bpp / 8, outputFile);
            }
            for (int p = 0; p < padding; p++) {
                fputc(0, outputFile); // Write padding for the new image
            }

    }

    // Close the files
    fclose(inputFile);
    fclose(outputFile);

    printf("BMP image cropped successfully\n");

This is essentially all the code that does the image cropping. I'm only using stdio.h and stdlib.h libraries and would like to keep it that way. The outputted image is the bottom half of the original image, but I would like to also be able to find a way to keep the top half instead. The original BMP image I am using is 3200x1200, and I am setting the new height to be 600 instead of 1200 so the new image can be cut in half vertically.

EDIT: The header.height and header.width variables are both positive. header.height = 1200, and header.width = 3200 originally.

Original BMP image (3200x1200)

Cropped image (3200x600)

1

There are 1 best solutions below

4
Joop Eggen On
int originalHeight = header.height;
...
int lineSize = newWidth * header.bpp / 8 + padding;
int skippedLines = originalHeight - newHeight;
long skippedBytes = ((long)lineSize) * skippedLines;

fseek(inputFile, skippedBytes, SEEK_CUR); // Skip half the lines at top.
unsigned char *linePixels = malloc(lineSize);
if (!linePixels)
    exit(137);
for (int y = 0; y < newHeight; y++) {
    fread(linePixels, sizeof(unsigned char), lineSize, inputFile);
    // You could copy the old padding.
    //memset(linePixels + lineSize - padding, '\0', padding);
    fwrite(linePixels, sizeof(unsigned char), lineSize, outputFile);
}
free(linePixels);

This will skip reading the first half containing the top lines (reversed y direction).

As already was commented there are more header fields (biSizeImage). The BMP format contains many variants. Also copying a bit buffered, more the 3-4 bytes apiece, would be better. As I introduced a lineSize I used that as buffer size. The code does not become less but more readable.