本文最后更新于 163 天前,其中的信息可能已经有所发展或是发生改变。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#pragma pack(push, 1)
typedef struct
{
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct
{
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("用法: %s <输入BMP> <输出BMP>\n", argv[0]);
return -1;
}
FILE *in = fopen(argv[1], "rb");
if (!in)
{
perror("打开输入文件失败");
return -1;
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
fread(&fh, 1, sizeof(fh), in);
fread(&ih, 1, sizeof(ih), in);
// 仅处理 24 位无压缩 BMP
if (ih.biBitCount != 24 || ih.biCompression != 0)
{
printf("仅支持 24 位未压缩 BMP\n");
fclose(in);
return -1;
}
int old_width = ih.biWidth;
int old_height = ih.biHeight;
int old_bitcount = ih.biBitCount;
// ... 前面的代码不变 (直到计算出 old_row_size) ...
int old_row_size = ((old_width * old_bitcount + 31) / 32) * 4;
int old_pixel_size = old_row_size * old_height; // 原图数据总大小
// 1. 【新增】把原图完整读到内存中
unsigned char *old_pixels = (unsigned char *)malloc(old_pixel_size);
if (!old_pixels) {
perror("原图内存分配失败");
return -1;
}
fseek(in, fh.bfOffBits, SEEK_SET);
fread(old_pixels, 1, old_pixel_size, in);
// 新图像尺寸计算 (你的代码)
int new_width = old_width / 2;
int new_height = old_height / 2;
int new_row_size = ((new_width * 24 + 31) / 32) * 4;
int new_pixel_size = new_row_size * new_height;
// 分配新像素缓冲区 (你的代码)
unsigned char *new_pixels = (unsigned char *)calloc(new_pixel_size, 1);
if (!new_pixels) {
perror("内存分配失败");
return -1;
}
// 2. 【核心修改】二维坐标映射 (最近邻插值法缩放)
// 遍历新图的每一个像素点
for (int y = 0; y < new_height; y++)
{
for (int x = 0; x < new_width; x++)
{
// 计算当前新像素在 new_pixels 数组里的起始位置
// 公式:当前行数 * 新行宽 + 当前列数 * 3
int new_index = y * new_row_size + x * 3;
// 计算这个点对应在原图中的位置
// 因为缩小了2倍,所以去原图里找 (x*2, y*2) 这个坐标的像素
int old_x = x * 2;
int old_y = y * 2;
int old_index = old_y * old_row_size + old_x * 3;
// 把原图的 B, G, R 复制到新图
new_pixels[new_index] = old_pixels[old_index]; // B
new_pixels[new_index + 1] = old_pixels[old_index + 1]; // G
new_pixels[new_index + 2] = old_pixels[old_index + 2]; // R
}
}
// ... 后面构建 new_fh, new_ih 并写入文件的代码不变 ...
// 记得最后释放新分配的 old_pixels
free(old_pixels);
// 构建新文件头和信息头
BITMAPFILEHEADER new_fh;
BITMAPINFOHEADER new_ih;
new_fh.bfType = 0x4D42;
new_fh.bfSize = sizeof(new_fh) + sizeof(new_ih) + new_pixel_size;
new_fh.bfReserved1 = 0;
new_fh.bfReserved2 = 0;
new_fh.bfOffBits = sizeof(new_fh) + sizeof(new_ih);
new_ih.biSize = sizeof(new_ih);
new_ih.biWidth = new_width;
new_ih.biHeight = new_height; // 保持与原图相同的符号(正数)
new_ih.biPlanes = 1;
new_ih.biBitCount = 24;
new_ih.biCompression = 0;
new_ih.biSizeImage = new_pixel_size;
new_ih.biXPelsPerMeter = ih.biXPelsPerMeter; // 保持原分辨率或填默认值
new_ih.biYPelsPerMeter = ih.biYPelsPerMeter;
new_ih.biClrUsed = 0;
new_ih.biClrImportant = 0;
FILE *out = fopen(argv[2], "wb");
if (!out)
{
perror("创建输出文件失败");
free(new_pixels);
return -1;
}
fwrite(&new_fh, 1, sizeof(new_fh), out);
fwrite(&new_ih, 1, sizeof(new_ih), out);
fwrite(new_pixels, 1, new_pixel_size, out);
fclose(out);
free(new_pixels);
printf("缩小一半的图像已生成: %s\n", argv[2]);
return 0;
}


