/*********************************************************************************
* @Description : 实现图片的不失真缩小
* @Author : ice_cui
* @Date : 2025-04-29 15:03:13
* @Email : Lazypanda_ice@163.com
* @LastEditTime : 2025-04-29 17:55:53
* @Version : V1.0.0
* @Copyright : Copyright (c) 2025 Lazypanda_ice@163.com All rights reserved.
**********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
// BMP文件头结构体
typedef struct {
unsigned char signature[2];
unsigned int fileSize;
unsigned short reserved1;
unsigned short reserved2;
unsigned int dataOffset;
} BMPFileHeader;
// BMP信息头结构体
typedef struct {
unsigned int headerSize;
int width;
int height;
unsigned short planes;
unsigned short bitCount;
unsigned int compression;
unsigned int imageSize;
int xPixelsPerMeter;
int yPixelsPerMeter;
unsigned int colorsUsed;
unsigned int colorsImportant;
} BMPInfoHeader;
// 读取BMP文件头
void readBMPFileHeader(FILE *file, BMPFileHeader *fileHeader) {
fread(fileHeader->signature, sizeof(unsigned char), 2, file);
fread(&fileHeader->fileSize, sizeof(unsigned int), 1, file);
fread(&fileHeader->reserved1, sizeof(unsigned short), 1, file);
fread(&fileHeader->reserved2, sizeof(unsigned short), 1, file);
fread(&fileHeader->dataOffset, sizeof(unsigned int), 1, file);
}
// 读取BMP信息头
void readBMPInfoHeader(FILE *file, BMPInfoHeader *infoHeader) {
fread(&infoHeader->headerSize, sizeof(unsigned int), 1, file);
fread(&infoHeader->width, sizeof(int), 1, file);
fread(&infoHeader->height, sizeof(int), 1, file);
fread(&infoHeader->planes, sizeof(unsigned short), 1, file);
fread(&infoHeader->bitCount, sizeof(unsigned short), 1, file);
fread(&infoHeader->compression, sizeof(unsigned int), 1, file);
fread(&infoHeader->imageSize, sizeof(unsigned int), 1, file);
fread(&infoHeader->xPixelsPerMeter, sizeof(int), 1, file);
fread(&infoHeader->yPixelsPerMeter, sizeof(int), 1, file);
fread(&infoHeader->colorsUsed, sizeof(unsigned int), 1, file);
fread(&infoHeader->colorsImportant, sizeof(unsigned int), 1, file);
}
// 写入BMP文件头
void writeBMPFileHeader(FILE *file, BMPFileHeader *fileHeader) {
fwrite(fileHeader->signature, sizeof(unsigned char), 2, file);
fwrite(&fileHeader->fileSize, sizeof(unsigned int), 1, file);
fwrite(&fileHeader->reserved1, sizeof(unsigned short), 1, file);
fwrite(&fileHeader->reserved2, sizeof(unsigned short), 1, file);
fwrite(&fileHeader->dataOffset, sizeof(unsigned int), 1, file);
}
// 写入BMP信息头
void writeBMPInfoHeader(FILE *file, BMPInfoHeader *infoHeader) {
fwrite(&infoHeader->headerSize, sizeof(unsigned int), 1, file);
fwrite(&infoHeader->width, sizeof(int), 1, file);
fwrite(&infoHeader->height, sizeof(int), 1, file);
fwrite(&infoHeader->planes, sizeof(unsigned short), 1, file);
fwrite(&infoHeader->bitCount, sizeof(unsigned short), 1, file);
fwrite(&infoHeader->compression, sizeof(unsigned int), 1, file);
fwrite(&infoHeader->imageSize, sizeof(unsigned int), 1, file);
fwrite(&infoHeader->xPixelsPerMeter, sizeof(int), 1, file);
fwrite(&infoHeader->yPixelsPerMeter, sizeof(int), 1, file);
fwrite(&infoHeader->colorsUsed, sizeof(unsigned int), 1, file);
fwrite(&infoHeader->colorsImportant, sizeof(unsigned int), 1, file);
}
// 计算每行的字节数
int calRowSize(int width, int bitCount) {
int rowSize = (width * bitCount + 31) / 32 * 4;
return rowSize;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("use: %s inputaddress outputaddress\n", argv[0]);
return 1;
}
//打开原BMP图片
FILE *inputFile = fopen(argv[1], "rb");
if (inputFile == NULL) {
printf("open file1 is error\n");
return 1;
}
BMPFileHeader fileHeader;
BMPInfoHeader infoHeader;
//读取原BMP的文件头和信息头
readBMPFileHeader(inputFile, &fileHeader);
readBMPInfoHeader(inputFile, &infoHeader);
//将新BMP图片的宽高变为原来的1/2
// 确保原始宽度和高度是偶数
if (infoHeader.width % 2 != 0) {
infoHeader.width--;
}
if (infoHeader.height % 2 != 0) {
infoHeader.height--;
}
int originalWidth = infoHeader.width;
int originalHeight = infoHeader.height;
int newWidth = originalWidth / 2;
int newHeight = originalHeight / 2;
//计算原BMP与新BMP每行的像素点
int originalRowSize = calRowSize(originalWidth, infoHeader.bitCount);
int newRowSize = calRowSize(newWidth, infoHeader.bitCount);
// 更新文件头 计算新的文件大小
fileHeader.fileSize = fileHeader.dataOffset + newRowSize * newHeight;
// 更新信息头 计算新的文件的宽、高以及数据区大小
infoHeader.width = newWidth;
infoHeader.height = newHeight;
infoHeader.imageSize = newRowSize * newHeight;
//打开新BMP图片
FILE *outputFile = fopen(argv[2], "wb");
if (outputFile == NULL) {
printf("open file2 is error\n");
fclose(inputFile);
return 1;
}
// 写入文件头和信息头
writeBMPFileHeader(outputFile, &fileHeader);
writeBMPInfoHeader(outputFile, &infoHeader);
// 跳过原文件的调色板
fseek(inputFile, fileHeader.dataOffset, SEEK_SET);
//为原文件数据申请内存
unsigned char *originalImageData = (unsigned char *)malloc(originalRowSize * originalHeight);
if (originalImageData == NULL) {
printf("malloc is error\n");
fclose(inputFile);
fclose(outputFile);
return 1;
}
// 读取原图像数据
size_t readBytes = fread(originalImageData, sizeof(unsigned char), originalRowSize * originalHeight, inputFile);
if (readBytes != (size_t)(originalRowSize * originalHeight)) {
printf("Error reading image data\n");
free(originalImageData);
fclose(inputFile);
fclose(outputFile);
return 1;
}
//为新文件数据申请内存
unsigned char *newImageData = (unsigned char *)malloc(newRowSize * newHeight);
if (newImageData == NULL) {
printf("malloc for newImageData is error\n");
free(originalImageData);
fclose(inputFile);
fclose(outputFile);
return 1;
}
// 缩小图像 原图像每两个像素点读取一个到新图像中
for (int y = 0; y < newHeight; y++) {
for (int x = 0; x < newWidth; x++) {
int originalX = x * 2;
int originalY = y * 2;
int originalIndex = originalY * originalRowSize + originalX * (infoHeader.bitCount / 8);
int newIndex = y * newRowSize + x * (infoHeader.bitCount / 8);
// 检查索引是否越界
if (originalIndex + (infoHeader.bitCount / 8) > originalRowSize * originalHeight ||
newIndex + (infoHeader.bitCount / 8) > newRowSize * newHeight) {
printf("Index out of bounds: originalIndex = %d, newIndex = %d\n", originalIndex, newIndex);
free(originalImageData);
free(newImageData);
fclose(inputFile);
fclose(outputFile);
return 1;
}
for (int i = 0; i < infoHeader.bitCount / 8; i++) {
newImageData[newIndex + i] = originalImageData[originalIndex + i];
}
}
}
// 写入新图像数据
size_t writtenBytes = fwrite(newImageData, sizeof(unsigned char), newRowSize * newHeight, outputFile);
if (writtenBytes != (size_t)(newRowSize * newHeight)) {
printf("Error writing image data\n");
}
// 释放内存
free(originalImageData);
originalImageData = NULL;
free(newImageData);
newImageData = NULL;
// 关闭文件
fclose(inputFile);
fclose(outputFile);
printf("success!\n");
return 0;
}