You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:05:41