站里讲解BMP格式的文章已经有了,这里主要是分享一个示例程序给需要的同学。这个示例只支持24/32位不带调色板的位图文件解析,并且实现了简单的Resize、亮度调整功能,另外加了一个比较有意思的功能就是把图片转换成RGB像素点的形式(但是输出很容易变得非常大甚至超过4G导致32bit的数值溢出)。
bmp文件有个特点,Windows规定每一行占用字节数必须是4的整数倍,多余的数据用0补足。这个在处理24bit位图时,计算数据索引时需要注意。
bitmap.h
#ifndef _BITMAP_H_
#define _BITMAP_H_
#include "types.h"
#pragma pack(1)
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType;
DWORD bfSize;
UINT16 bfReserved1;
UINT16 bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack()
typedef struct tagBITMAPINFO
{
DWORD biSize;
DWORD biWidth;
DWORD biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
DWORD biXPelsPerMeter;
DWORD biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFO;
#define BMPTYPE_BM 0x4d42
#define blue(color) (BYTE)(color)
#define green(color) (BYTE)((color) >> 8)
#define red(color) (BYTE)((color) >> 16)
#define alpha(color) (BYTE)((color) >> 24)
#define makecolor(b,g,r,a) (((BYTE)(b)) | ((BYTE)(g) << 8) | ((BYTE)(r) << 16) | ((BYTE)(a) << 24) )
#endif
BMP部分:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "bitmap.h"
#pragma pack(1)
typedef struct tagBMP
{
BITMAPFILEHEADER head;
BITMAPINFO info;
BYTE data[0];
} BMP;
#pragma pack()
BMP *create_bmp(DWORD w, DWORD h, WORD bitdepth)
{
DWORD rowsize = (w * bitdepth / 8 + 3) & (~3U);
DWORD size_image = h * rowsize;
DWORD file_size = size_image + sizeof(BMP);
BMP *bmp = malloc(file_size);
if (bmp == NULL)
{
return NULL;
}
bmp->head.bfType = BMPTYPE_BM;
bmp->head.bfSize = file_size;
bmp->head.bfReserved1 = 0;
bmp->head.bfReserved2 = 0;
bmp->head.bfOffBits = sizeof(BMP);
bmp->info.biSize = sizeof(BITMAPINFO);
bmp->info.biWidth = w;
bmp->info.biHeight = h;
bmp->info.biPlanes = 1;
bmp->info.biBitCount = bitdepth;
bmp->info.biCompression = 0;
bmp->info.biSizeImage = size_image;
bmp->info.biXPelsPerMeter = 0;
bmp->info.biYPelsPerMeter = 0;
bmp->info.biClrUsed = 0;
bmp->info.biClrImportant = 0;
return bmp;
}
int save_file(const BMP *bmp, const char *path)
{
if (bmp == NULL)
return -1;
FILE *fp = fopen(path, "wb+");
if (fp == NULL)
return -1;
fwrite(bmp, 1, bmp->head.bfSize, fp);
fclose(fp);
}
BMP *create_from_file(const char *path)
{
BITMAPFILEHEADER headtemp;
FILE *fp = fopen(path, "rb+");
if (fp == NULL)
goto except;
fread(&headtemp, 1, sizeof(BITMAPFILEHEADER), fp);
BMP *bmp = malloc(headtemp.bfSize);
if (bmp == NULL)
{
goto close_file;
}
memcpy(&bmp->head, &headtemp, sizeof(bmp->head));
fread(&bmp->info, 1, headtemp.bfSize - sizeof(BITMAPFILEHEADER), fp);
return bmp;
close_file:
fclose(fp);
except:
return NULL;
}
void destroy_bmp(BMP **pbmp)
{
if (pbmp != NULL)
free(*pbmp);
*pbmp = NULL;
}
处理函数部分
void rect(BMP *bmp, DWORD color, DWORD row, DWORD col, DWORD w, DWORD h)
{
DWORD width = bmp->info.biWidth;
DWORD height = bmp->info.biHeight;
DWORD colend = MIN(col + w, width);
DWORD rowend = MIN(row + h, height);
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD rowsize = (width * dsize + 3) & (~3U);
for (DWORD i = row; i < rowend; i++)
{
for (DWORD j = col; j < colend; j++)
{
BYTE *data = bmp->data + i * rowsize + j * dsize;
data[0] = blue(color);
data[1] = green(color);
data[2] = red(color);
}
}
}
void rect_lightness(BMP *bmp, DWORD row, DWORD col, DWORD w, DWORD h, float ratio)
{
DWORD width = bmp->info.biWidth;
DWORD height = bmp->info.biHeight;
DWORD colend = MIN(col + w, width);
DWORD rowend = MIN(row + h, height);
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD rowsize = (width * dsize + 3) & (~3U);
for (DWORD i = row; i < rowend; i++)
{
for (DWORD j = col; j < colend; j++)
{
BYTE *data = bmp->data + i * rowsize + j * dsize;
data[0] = color_truncate(((float)data[0]) * ratio);
data[1] = color_truncate(((float)data[1]) * ratio);
data[2] = color_truncate(((float)data[2]) * ratio);
}
}
}
BYTE color_truncate(int grlevel)
{
if (grlevel < 0)
return 0;
if (grlevel > 255)
return 255;
return grlevel;
}
DWORD color_lightness(BYTE b, BYTE g, BYTE r, int delta)
{
BYTE blue = color_truncate((int)b + delta);
BYTE green = color_truncate((int)g + delta);
BYTE red = color_truncate((int)r + delta);
return makecolor(blue, green, red, 0);
}
DWORD color_lightnessf(BYTE b, BYTE g, BYTE r, float ratio)
{
BYTE blue = color_truncate(((float)b) * ratio);
BYTE green = color_truncate(((float)g) * ratio);
BYTE red = color_truncate(((float)r) * ratio);
return makecolor(blue, green, red, 0);
}
void lightness(BMP *bmp, int delta)
{
DWORD width = bmp->info.biWidth;
DWORD height = bmp->info.biHeight;
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD rowsize = (width * dsize + 3) & (~3U);
for (DWORD i = 0; i < height; i++)
{
for (DWORD j = 0; j < width; j++)
{
BYTE *data = bmp->data + i * rowsize + j * dsize;
data[0] = color_truncate((int)data[0] + delta);
data[1] = color_truncate((int)data[1] + delta);
data[2] = color_truncate((int)data[2] + delta);
}
}
}
void lightnessf(BMP *bmp, float ratio)
{
DWORD width = bmp->info.biWidth;
DWORD height = bmp->info.biHeight;
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD rowsize = (width * dsize + 3) & (~3U);
for (DWORD i = 0; i < height; i++)
{
for (DWORD j = 0; j < width; j++)
{
BYTE *data = bmp->data + i * rowsize + j * dsize;
data[0] = color_truncate((int)data[0] * ratio);
data[1] = color_truncate((int)data[1] * ratio);
data[2] = color_truncate((int)data[2] * ratio);
}
}
}
BMP *resize(const BMP *bmp, float fscale)
{
int scale = (int)fscale;
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD w = bmp->info.biWidth;
const DWORD h = bmp->info.biHeight;
const DWORD rowsize = (w * dsize + 3) & (~3U);
DWORD newwidth = bmp->info.biWidth * fscale;
DWORD newheight = bmp->info.biHeight * fscale;
DWORD newdsize = 3;
BMP *newbmp = create_bmp(newwidth, newheight, newdsize * 8);
DWORD newrowsize = (newwidth * newdsize + 3) & (~3U);
if (fscale > 1.f)
{
for (DWORD i = 0; i < bmp->info.biHeight; i++)
{
for (DWORD j = 0; j < bmp->info.biWidth; j++)
{
const BYTE *data = bmp->data + i * rowsize + j * dsize;
DWORD color = makecolor(data[0], data[1], data[2], 0);
rect(newbmp, color, i * fscale, j * fscale, fscale, fscale);
}
}
}
else if (fscale < 1.f)
{
for (DWORD i = 0; i < newheight; i++)
{
for (DWORD j = 0; j < newwidth; j++)
{
BYTE *dstdata = newbmp->data + i * newrowsize + j * newdsize;
const BYTE *data = bmp->data + (DWORD)(i / fscale) * rowsize + (DWORD)(j / fscale) * dsize;
dstdata[0] = data[0];
dstdata[1] = data[1];
dstdata[2] = data[2];
}
}
}
return newbmp;
}
BMP *resize_screen(const BMP *bmp, WORD frame_width, WORD pixel_width)
{
int scale = pixel_width * 3 + frame_width * 3;
const DWORD dsize = bmp->info.biBitCount / 8;
const DWORD w = bmp->info.biWidth;
const DWORD h = bmp->info.biHeight;
const DWORD rowsize = (w * dsize + 3) & (~3U);
BMP *newbmp = create_bmp(bmp->info.biWidth * scale, bmp->info.biHeight * scale, 24);
for (DWORD i = 0; i < bmp->info.biHeight; i++)
{
DWORD preblue = 0;
for (DWORD j = 0; j < bmp->info.biWidth; j++)
{
const BYTE *data = bmp->data + i * rowsize + j * dsize;
DWORD color = makecolor(data[0], data[1], data[2], 0);
DWORD gray = data[2] * 0.3 + data[1] * 0.59 + data[0] * 0.11;
float diffusion = 0.5;
DWORD red = makecolor(data[0] * diffusion, data[1] * diffusion, data[2], 0);
DWORD green = makecolor(data[0] * diffusion, data[1], data[2] * diffusion, 0);
DWORD blue = makecolor(data[0], data[1] * diffusion, data[2] * diffusion, 0);
// DWORD frame = color_lightnessf(data[0], data[1], data[2], 0.3);
float lightness = 0.5;
DWORD frameRG = color_lightnessf(0, data[1], data[2], lightness);
DWORD frameGB = color_lightnessf(data[0], data[1], 0, lightness);
DWORD frameBR = color_lightnessf(preblue, 0, data[2], lightness);
preblue = blue;
DWORD frameGray = color_lightnessf(gray, gray, gray, lightness);
DWORD joffset = 0;
rect(newbmp, frameBR, i * scale, j * scale, frame_width, scale);
joffset += frame_width;
rect(newbmp, red, i * scale, j * scale + joffset, pixel_width, scale);
joffset += pixel_width;
rect(newbmp, frameRG, i * scale, j * scale + joffset, frame_width, scale);
joffset += frame_width;
rect(newbmp, green, i * scale, j * scale + joffset, pixel_width, scale);
joffset += pixel_width;
rect(newbmp, frameGB, i * scale, j * scale + joffset, frame_width, scale);
joffset += frame_width;
rect(newbmp, blue, i * scale, j * scale + joffset, pixel_width, scale);
joffset += pixel_width;
rect_lightness(newbmp, i * scale, j * scale, scale, frame_width, lightness);
}
}
return newbmp;
}
测试程序:
int main(int argc, char *argv[])
{
// BMP *bmp = create_bmp(100, 100, 24);
// save_file(bmp, "1.bmp");
// destroy_bmp(&bmp);
BMP *bmp = create_from_file("test.bmp");
BMP *bmpre = resize_screen(bmp, 1, 2);
// BMP *bmps = resize(bmpre, 0.5f);
lightnessf(bmpre, 1.2);
save_file(bmpre, "test1.bmp");
destroy_bmp(&bmp);
// destroy_bmp(&bmps);
destroy_bmp(&bmpre);
return 0;
}
原图
处理后(裁剪)