BMP 圖像四字節對齊
表示 BMP 位圖中像素的位元是以行爲單位對齊存儲的,每一行的大小都向上取整爲4字節(32 位 DWORD)的倍數。如果圖像的高度大於 1,多個經過填充實現對齊的行就形成了像素數組。
完整存儲的一行像素所需的字節數可以通過這個公式計算:
- 每一行的末尾通過填充若干個字節的數據(並不一定爲 0)使該行的長度爲 4 字節的倍數。像素數組讀入內存後,每一行的起始地址必須爲 4 的倍數。這個限制僅針對內存中的像素數組,針對存儲時,僅要求每一行的大小爲 4 字節的倍數,對文件的偏移沒有限制。
- 例如:對於 24 位色的位圖,如果它的寬度爲 1 像素,那麼除了每一行的數據(藍、綠、紅)需要佔 3 字節外,還會填充 1 字節;而如果寬爲 2 像素,則需要 2 字節的填充;寬爲 3 像素時,需要 3 字節填充;寬爲 4 像素時則不需要填充。
- 圖像相同的條件下,位圖圖像文件通常比使用其它壓縮算法的圖像文件大很多。
四字節對齊問題-發現
沒有四字節對齊的 700x700 BMP 文件
之前,用 RGB 拼出一張 700x700 的彩虹圖片,併成功保存成 BMP 文件。當時並沒有進行四字節對齊,保存成的 BMP 文件也沒有問題。
<br/>
原因:
根據四字節對齊的要求,700 x 3 = 2100,2100 / 4 = 525,行像素數據已經四字節對齊了。
如果沒有對齊會怎麼樣
將圖片尺寸改成 711x711:
#include <stdio.h>
#include <stdlib.h>
// 彩虹的七種顏色
u_int32_t rainbowColors[] = {
0XFF0000, // 紅
0XFFA500, // 橙
0XFFFF00, // 黃
0X00FF00, // 綠
0X007FFF, // 青
0X0000FF, // 藍
0X8B00FF // 紫
};
/*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) {
// 當前顏色
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];
}
// 當前顏色 R 分量
u_int8_t R = (currentColor & 0xFF0000) >> 16;
// 當前顏色 G 分量
u_int8_t G = (currentColor & 0x00FF00) >> 8;
// 當前顏色 B 分量
u_int8_t B = currentColor & 0x0000FF;
for (int j = 0; j < height; ++j) {
// 按 BGR 順序寫入一個像素 RGB24 到文件中
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/>
彩虹圖片已經無法顯示出來了:
四字節對齊問題-解決
計算一行像素,四字節對齊後的字節數
// 計算每一行像素 4 字節對齊後的字節數
int caculateLineBytes(int width) {
//******* 四字節對齊 *******
return (24 * width + 31)/32 *4;
//******* 四字節對齊 *******
}
寫入一行像素的數據
// 計算一行像素四字節對齊所需字節數
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;
// 存儲一行像素數據的數組
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, file);
}
完整的代碼
#include <stdio.h>
#include <stdlib.h>
// 彩虹的七種顏色
u_int32_t rainbowColors[] = {
0XFF0000, // 紅
0XFFA500, // 橙
0XFFFF00, // 黃
0X00FF00, // 綠
0X007FFF, // 青
0X0000FF, // 藍
0X8B00FF // 紫
};
/*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;
// 計算每一行像素 4 字節對齊後的字節數
int caculateLineBytes(int width) {
//******* 四字節對齊 *******
return (24 * width + 31)/32 *4;
//******* 四字節對齊 *******
}
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) {
// 當前顏色
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];
}
// 當前顏色 R 分量
u_int8_t R = (currentColor & 0xFF0000) >> 16;
// 當前顏色 G 分量
u_int8_t G = (currentColor & 0x00FF00) >> 8;
// 當前顏色 B 分量
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/>
711x711 的彩虹圖片也顯示出來了:
代碼:
參考資料:
non-dword-aligned-pixel-to-dword-aligned-bitmap
generate-bmp-file-from-array-of-rgb-values
內容有誤?聯繫作者: