* 音视频入门文章目录 *

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 文件也没有问题。

原因:

根据四字节对齐的要求,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;
}

彩虹图片已经无法显示出来了:

四字节对齐问题-解决

计算一行像素,四字节对齐后的字节数

// 计算每一行像素 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;
}

711x711 的彩虹图片也显示出来了:
![image-demo-bmp-with-dword-align.jpg](https://img2018.cnblogs.com/blog/1320320/201909/1320320-20190914145522497-2038619653.jpg)


代码:

04-rgb-to-bmp-fix

参考资料:

BMP图像四字节对齐的问题

维基百科-BMP

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

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

内容有误?联系作者:


音视频入门-04-BMP图像四字节对齐的问题的更多相关文章

  1. 音视频入门-05-RGB-TO-BMP使用开源库

    * 音视频入门文章目录 * RGB-TO-BMP 回顾 将 RGB 数据转成 BMP 图片: 了解 BMP 文件格式 准备 BMP 文件头信息 准备 BMP 信息头 BMP 存储 RGB 的顺序是 B ...

  2. 音视频入门-03-RGB转成BMP图片

    * 音视频入门文章目录 * BMP 文件格式解析 BMP 文件由文件头.位图信息头.颜色信息和图形数据四部分组成. 位图文件头(14个字节) 位图信息头(40个字节) 颜色信息 图形数据 文件头与信息 ...

  3. 音视频入门-11-PNG文件格式详解

    * 音视频入门文章目录 * PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节 ...

  4. 音视频入门-12-手动生成一张PNG图片

    * 音视频入门文章目录 * 预热 上一篇 [PNG文件格式详解]详细介绍了 PNG 文件的格式. PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDA ...

  5. 音视频入门-14-JPEG文件格式详解

    * 音视频入门文章目录 * JPEG 文件格式解析 JPEG 文件使用的数据存储方式有多种.最常用的格式称为 JPEG 文件交换格式(JPEG File Interchange Format,JFIF ...

  6. 音视频入门-08-RGB&YUV

    * 音视频入门文章目录 * YUV & RGB 相互转换公式 YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在 ...

  7. 音视频入门-18-手动生成一张GIF图片

    * 音视频入门文章目录 * GIF 编码知识 GIF 包含的数据块: 文件头(Header) 逻辑屏幕标识符(Logical Screen Descriptor) 全局颜色表(Global Color ...

  8. 音视频入门-20-BMP、PNG、JPG、GIF静态图生成GIF动态图

    * 音视频入门文章目录 * 静态图 -> 动态图 前面 [18-手动生成一张GIF图片] 和 [19-使用giflib处理GIF图片] 生成的 GIF 每一帧都是一个颜色,平时用到的 GIF 每 ...

  9. 音视频入门-13-使用开源库生成PNG图片

    * 音视频入门文章目录 * RGB-to-PNG 回顾 上一篇 [手动生成一张PNG图片] 根据 [PNG文件格式详解] 一步一步地手动实现了将 RGB 数据生成了一张 PNG 图片. 有许多开源的 ...

随机推荐

  1. html,body设置{height:100%}[转]

    一般情况下,我们css控制的最高节点就是body,例如设置: body{background:#069;} 则浏览器界面就是完全的#068的背景色.这里看上去是<body>标签下的背景色起 ...

  2. Ionic4.x Javascript 扩展 ActionSheet Alert Toast Loading 以及 ionic 手势相 关事件

    1.ActionSheet 官方文档:https://ionicframework.com/docs/api/action-sheet <ion-header> <ion-toolb ...

  3. CentOS 7部署 Ceph分布式存储架构

    一.概述 随着OpenStack日渐成为开源云计算的标准软件栈,Ceph也已经成为OpenStack的首选后端存储.Ceph是一种为优秀的性能.可靠性和可扩展性而设计的统一的.分布式文件系统. cep ...

  4. LeetCode_27. Remove Element

    27. Remove Element Easy Given an array nums and a value val, remove all instances of that value in-p ...

  5. asp.net core swagger使用及注意事项

    Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.是一款RESTFUL接口的文档在线自动生成+功能测试软件.主要目的是构建标准的.稳定的.可重 ...

  6. css+html 实现 光晕 光圈

    <style> html { height: 100%;}body { height: 100%; background-color: #000; margin: 0; padding: ...

  7. CareerCup Questions List 职业杯题目列表

    网站 www.careercup.com 上的题库列表 # Title Difficulty Company 1 Guards in a museum Hard F, G  2 Bomberman H ...

  8. iOS使用UIImageView展现网络图片(转载)

    在iOS开发过程中,经常会遇到使用UIImageView展现来自网络的图片的情况,最简单的做法如下: [cpp] view plaincopy   - (void)viewDidLoad { [sup ...

  9. iOS-条形码扫描技术SDK:ZBar(转)

    ios条形码扫描技术 iOS 应用里实现条形码扫描功能,有个免费开源的 SDK:ZBar(http://zbar.sourceforge.net/iphone/sdkdoc/install.html) ...

  10. html转图片网页截屏(三),puppeteer

    puppeteer谷歌出品,是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome. 官方github地址:https://github ...