Introduction to Audio and Video-04-BMP Image Four-byte Alignment

Keywords: C++

Directory of Audio and Video Introduction Articles

Four-byte alignment of BMP images

The bits representing the pixels in BMP bitmaps are aligned in action units, and the size of each row is taken up as a multiple of 4 bytes (32-bit DWORD). If the height of the image is greater than 1, multiple lines aligned by filling form an array of pixels.

The number of bytes required for a full stored row of pixels can be calculated by this formula:

  • The end of each line multiplies the length of the line by filling in several bytes of data (not necessarily zero). When an array of pixels is read into memory, the starting address of each row must be a multiple of 4. This restriction is only for the array of pixels in memory. For storage, it only requires the size of each row to be a multiple of 4 bytes, and there is no restriction on file offset.
  • For example, for a 24-bit color bitmap, if its width is 1 pixel, it will fill 1 byte in addition to 3 bytes for each row of data (blue, green, red). For a 2-pixel width, it will need 2 bytes; for a 3-pixel width, it will need 3 bytes; and for a 4-pixel width, it will not. Fill it up.
  • Under the same image condition, the bitmap image file is usually much larger than the image file using other compression algorithms.

Four-byte alignment problem-discovery

700 x700 BMP file without four-byte alignment

before With RGB, a 700x700 rainbow image was spelled out and successfully saved into a BMP file.

Four-byte alignment was not performed at that time, and the saved BMP file was no problem.

<br/>

Reason:
According to the four-byte alignment requirement, 700 x 3 = 2100, 2100/4 = 525, row pixel data has been four-byte aligned.

What happens if there is no alignment

Change the image size to 711x711:

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

// Seven Colors of Rainbow
u_int32_t rainbowColors[] = {
        0XFF0000, // red
        0XFFA500, // orange
        0XFFFF00, // yellow
        0X00FF00, // green
        0X007FFF, // young
        0X0000FF, // blue
        0X8B00FF  // purple
};

/*bmp file header*/
typedef struct {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BitmapFileHeader;

/*bmp info header*/
typedef struct {
    unsigned int   biSize; /* Size of info header */
    int            biWidth; /* Width of image */
    int            biHeight; /* Height of image */
    unsigned short biPlanes; /* Number of color planes */
    unsigned short biBitCount; /* Number of bits per pixel */
    unsigned int   biCompression; /* Type of compression to use */
    unsigned int   biSizeImage; /* Size of image data */
    int            biXPelsPerMeter; /* X pixels per meter */
    int            biYPelsPerMeter; /* Y pixels per meter */
    unsigned int   biClrUsed; /* Number of colors used */
    unsigned int   biClrImportant; /* Number of important colors */
} BitmapInfoHeader;

void writeRGBToBmp(char *filename, int width, int height) {
    FILE *bitmapFile = fopen(filename, "wb");
    if(!bitmapFile) {
        printf("Could not write file \n");
        return;
    }

    uint16_t bfType = 0x4d42;

    BitmapFileHeader fileHeader;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + width*height*3;
    fileHeader.bfOffBits = 0x36;

    BitmapInfoHeader infoHeader;
    infoHeader.biSize = sizeof(BitmapInfoHeader);
    infoHeader.biWidth = width;
    infoHeader.biHeight = -height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biSizeImage = 0;
    infoHeader.biCompression = 0;
    infoHeader.biXPelsPerMeter = 5000;
    infoHeader.biYPelsPerMeter = 5000;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
    fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
    fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);

    for (int i = 0; i < width; ++i) {

        // Current color
        u_int32_t currentColor = rainbowColors[0];
        if(i < 100) {
            currentColor = rainbowColors[0];
        } else if(i < 200) {
            currentColor = rainbowColors[1];
        } else if(i < 300) {
            currentColor = rainbowColors[2];
        } else if(i < 400) {
            currentColor = rainbowColors[3];
        } else if(i < 500) {
            currentColor = rainbowColors[4];
        } else if(i < 600) {
            currentColor = rainbowColors[5];
        } else if(i < 700) {
            currentColor = rainbowColors[6];
        }
        // Current color R component
        u_int8_t R = (currentColor & 0xFF0000) >> 16;
        // Current color G component
        u_int8_t G = (currentColor & 0x00FF00) >> 8;
        // Current color B component
        u_int8_t B = currentColor & 0x0000FF;

        for (int j = 0; j < height; ++j) {
            // Write a pixel RGB24 to a file in BGR order
            fwrite(&B, 1, 1, bitmapFile);
            fwrite(&G, 1, 1, bitmapFile);
            fwrite(&R, 1, 1, bitmapFile);
        }
    }

    fclose(bitmapFile);
}

int main() {
    writeRGBToBmp("/Users/hubin/Desktop/rainbow-711x711.bmp", 711, 711);
    return 0;
}

<br/>

Rainbow pictures can no longer be displayed:

Four-byte alignment problem-solving

Calculate the number of bytes in a row of pixels aligned with four bytes

// Calculate the number of bytes after 4-byte alignment of each row of pixels
int caculateLineBytes(int width) {
    //********* Four-byte Alignment**********
    return (24 * width + 31)/32 *4;
    //********* Four-byte Alignment**********
}

Write data to a row of pixels

// Calculate the number of bytes required for four-byte alignment of a row of pixels
int lineBytes = caculateLineBytes(width);

for (int i = 0; i < width; ++i) {
    u_int32_t currentColor = rainbowColors[i];
    u_int8_t R = (currentColor & 0xFF0000) >> 16;
    u_int8_t G = (currentColor & 0x00FF00) >> 8;
    u_int8_t B = currentColor & 0x0000FF;

    // An array that stores a row of pixel data
    u_int8_t lineBytesArray[lineBytes];

    for (int j = 0; j < height; ++j) {
        int currentIndex = 3*j;
        lineBytesArray[currentIndex] = B;
        lineBytesArray[currentIndex+1] = G;
        lineBytesArray[currentIndex+2] = R;
    }

    // Write a line of four-byte aligned pixel data to a file
    fwrite(lineBytesArray, sizeof(lineBytesArray), 1, file);
}

Complete code

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

// Seven Colors of Rainbow
u_int32_t rainbowColors[] = {
        0XFF0000, // red
        0XFFA500, // orange
        0XFFFF00, // yellow
        0X00FF00, // green
        0X007FFF, // young
        0X0000FF, // blue
        0X8B00FF  // purple
};

/*bmp file header*/
typedef struct {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BitmapFileHeader;

/*bmp info header*/
typedef struct {
    unsigned int   biSize; /* Size of info header */
    int            biWidth; /* Width of image */
    int            biHeight; /* Height of image */
    unsigned short biPlanes; /* Number of color planes */
    unsigned short biBitCount; /* Number of bits per pixel */
    unsigned int   biCompression; /* Type of compression to use */
    unsigned int   biSizeImage; /* Size of image data */
    int            biXPelsPerMeter; /* X pixels per meter */
    int            biYPelsPerMeter; /* Y pixels per meter */
    unsigned int   biClrUsed; /* Number of colors used */
    unsigned int   biClrImportant; /* Number of important colors */
} BitmapInfoHeader;

// Calculate the number of bytes after 4-byte alignment of each row of pixels
int caculateLineBytes(int width) {
    //********* Four-byte Alignment**********
    return (24 * width + 31)/32 *4;
    //********* Four-byte Alignment**********
}

void writeRGBToBmp(char *filename, int width, int height) {
    FILE *bitmapFile = fopen(filename, "wb");
    if(!bitmapFile) {
        printf("Could not write file \n");
        return;
    }

    uint16_t bfType = 0x4d42;

    int lineBytes = caculateLineBytes(width);

    BitmapFileHeader fileHeader;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + lineBytes*height;
    fileHeader.bfOffBits = 0x36;

    BitmapInfoHeader infoHeader;
    infoHeader.biSize = sizeof(BitmapInfoHeader);
    infoHeader.biWidth = width;
    infoHeader.biHeight = -height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biSizeImage = 0;
    infoHeader.biCompression = 0;
    infoHeader.biXPelsPerMeter = 5000;
    infoHeader.biYPelsPerMeter = 5000;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
    fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
    fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);

    for (int i = 0; i < width; ++i) {

        // Current color
        u_int32_t currentColor = rainbowColors[0];
        if(i < 100) {
            currentColor = rainbowColors[0];
        } else if(i < 200) {
            currentColor = rainbowColors[1];
        } else if(i < 300) {
            currentColor = rainbowColors[2];
        } else if(i < 400) {
            currentColor = rainbowColors[3];
        } else if(i < 500) {
            currentColor = rainbowColors[4];
        } else if(i < 600) {
            currentColor = rainbowColors[5];
        } else if(i < 700) {
            currentColor = rainbowColors[6];
        }
        // Current color R component
        u_int8_t R = (currentColor & 0xFF0000) >> 16;
        // Current color G component
        u_int8_t G = (currentColor & 0x00FF00) >> 8;
        // Current color B component
        u_int8_t B = currentColor & 0x0000FF;

        u_int8_t lineBytesArray[lineBytes];

        for (int j = 0; j < height; ++j) {
            int currentIndex = 3*j;
            lineBytesArray[currentIndex] = B;
            lineBytesArray[currentIndex+1] = G;
            lineBytesArray[currentIndex+2] = R;
        }

        fwrite(lineBytesArray, sizeof(lineBytesArray), 1, bitmapFile);
    }
    fclose(bitmapFile);
}

int main() {
    writeRGBToBmp("/Users/staff/Desktop/rainbow-711x711-fix.bmp", 711, 711);
    return 0;
}

<br/>
Rainbow images of 711x711 are also shown:

Code:

04-rgb-to-bmp-fix

Reference material:

Four-byte alignment of BMP images

Wikipedia-BMP

non-dword-aligned-pixel-to-dword-aligned-bitmap

generate-bmp-file-from-array-of-rgb-values

Is the content wrong? Contact author:

Posted by eruna on Sat, 14 Sep 2019 00:05:39 -0700