求基于stbi的PNG转Bitmap并将数据存入内存的示例代码
将PNG转换为内存中的Bitmap数据(基于stb库)
你可以手动构造Bitmap的文件头、信息头并填充像素数据,直接在内存中生成完整的BMP数据块,无需写入磁盘。以下是完整的示例代码,基于stb_image现有逻辑扩展:
首先定义Bitmap的结构头(注意字节对齐,避免结构体padding导致的错误):
#include <stdint.h> #include <stdlib.h> #include <string.h> #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // Bitmap文件头(固定14字节) #pragma pack(push, 1) typedef struct { uint16_t bfType; // 必须为0x4D42(对应字符'BM') uint32_t bfSize; // 整个BMP数据块的总大小(字节) uint16_t bfReserved1; // 保留字段,设为0 uint16_t bfReserved2; // 保留字段,设为0 uint32_t bfOffBits; // 像素数据相对于数据块开头的偏移量(14+40=54) } BITMAPFILEHEADER; // Bitmap信息头(固定40字节,标准DIB头) typedef struct { uint32_t biSize; // 信息头大小,固定为40 int32_t biWidth; // 图像宽度(像素) int32_t biHeight; // 图像高度(像素:正数表示从下到上存储,负数表示从上到下) uint16_t biPlanes; // 颜色平面数,固定为1 uint16_t biBitCount; // 每个像素的位数,这里用24位RGB uint32_t biCompression; // 压缩方式,0表示无压缩 uint32_t biSizeImage; // 像素数据的总大小(含行对齐填充字节) int32_t biXPelsPerMeter; // 水平分辨率,可选设为0 int32_t biYPelsPerMeter; // 垂直分辨率,可选设为0 uint32_t biClrUsed; // 使用的颜色数,0表示全部 uint32_t biClrImportant; // 重要颜色数,0表示全部 } BITMAPINFOHEADER; #pragma pack(pop)
核心转换函数:输入stbi加载的PNG数据,输出内存中的BMP数据块和总大小
// 将stbi加载的RGB图像转换为内存中的BMP数据 // 参数: // image_data: stbi_load_from_memory返回的像素数据(RGB顺序) // width/height: 图像宽高 // channels: 图像通道数(stbi返回值,支持3或4,自动忽略Alpha通道转为24位RGB) // out_bmp_size: 输出BMP数据的总字节数 // 返回:内存中的BMP数据指针,需手动调用free释放 uint8_t* png_to_memory_bmp(const uint8_t* image_data, int width, int height, int channels, uint32_t* out_bmp_size) { // 计算BMP每行字节数(必须按4字节对齐) int row_bytes = ((width * 3 + 3) / 4) * 4; uint32_t pixel_data_size = (uint32_t)row_bytes * height; uint32_t total_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pixel_data_size; // 分配内存存储完整BMP数据 uint8_t* bmp_data = (uint8_t*)malloc(total_size); if (!bmp_data) return NULL; // 填充文件头 BITMAPFILEHEADER* file_header = (BITMAPFILEHEADER*)bmp_data; file_header->bfType = 0x4D42; file_header->bfSize = total_size; file_header->bfReserved1 = 0; file_header->bfReserved2 = 0; file_header->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 填充信息头 BITMAPINFOHEADER* info_header = (BITMAPINFOHEADER*)(bmp_data + sizeof(BITMAPFILEHEADER)); info_header->biSize = sizeof(BITMAPINFOHEADER); info_header->biWidth = width; // 设为负数,让BMP按从上到下顺序存储(和stbi加载的PNG顺序一致) info_header->biHeight = -height; info_header->biPlanes = 1; info_header->biBitCount = 24; info_header->biCompression = 0; info_header->biSizeImage = pixel_data_size; info_header->biXPelsPerMeter = 0; info_header->biYPelsPerMeter = 0; info_header->biClrUsed = 0; info_header->biClrImportant = 0; // 转换像素格式:RGB转BGR,处理行对齐 uint8_t* pixel_dst = bmp_data + file_header->bfOffBits; const uint8_t* pixel_src = image_data; for (int y = 0; y < height; y++) { uint8_t* row_dst = pixel_dst + y * row_bytes; const uint8_t* row_src = pixel_src + y * width * channels; for (int x = 0; x < width; x++) { // 提取RGB通道(RGBA则忽略Alpha) uint8_t r = row_src[x * channels + 0]; uint8_t g = row_src[x * channels + 1]; uint8_t b = row_src[x * channels + 2]; // BMP采用BGR存储顺序 row_dst[x * 3 + 0] = b; row_dst[x * 3 + 1] = g; row_dst[x * 3 + 2] = r; } // 行对齐补0(如果当前行字节数不是4的倍数) int padding = row_bytes - width * 3; if (padding > 0) { memset(row_dst + width * 3, 0, padding); } } *out_bmp_size = total_size; return bmp_data; }
使用示例(结合stbi_load_from_memory):
int main() { // 假设已从内存读取PNG数据到png_buffer,大小为png_size const uint8_t* png_buffer = ...; size_t png_size = ...; int width, height, channels; // 强制转为3通道RGB,自动忽略Alpha uint8_t* png_data = stbi_load_from_memory(png_buffer, png_size, &width, &height, &channels, 3); if (!png_data) { // 加载失败处理 return 1; } uint32_t bmp_size; uint8_t* bmp_data = png_to_memory_bmp(png_data, width, height, channels, &bmp_size); if (bmp_data) { // 这里可直接使用bmp_data,比如存入内存缓存、发送网络等 // ... // 使用完毕后释放内存 free(bmp_data); } // 释放stbi加载的PNG数据 stbi_image_free(png_data); return 0; }
关键注意点:
- 行对齐:Bitmap要求每行像素数据必须是4字节的倍数,必须计算
row_bytes并填充补0字节,否则图像会出现偏移。 - 颜色顺序:stbi加载的PNG是RGB顺序,Bitmap存储的是BGR顺序,必须交换R和B通道。
- 存储方向:Bitmap默认从下到上存储像素,设置
biHeight为负数可改为从上到下,避免图像倒置。 - 内存管理:转换得到的
bmp_data需手动free,stbi加载的png_data需用stbi_image_free释放。
内容的提问来源于stack exchange,提问作者TippuR
相关产品推荐
相关产品推荐

