CS50 Pset3 Resize(More)异常:图片缩小后无法正常显示
CS50 Pset3 Resize(More) 缩小操作生成无效BMP文件问题
正在完成哈佛大学CS50课程的Pset3 Resize(More)任务,图片放大功能正常,但执行缩小操作时,生成的文件无法正常显示,打开后全是文本和大量问号。以下是我的代码:
// Copies a BMP file #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "bmp.h" int resizeLarger(FILE* outptr, FILE* inptr, int n); int resizeSmaller(FILE* outptr, FILE* inptr, int n); int main(int argc, char *argv[]) { // ensure proper usage if (argc != 4) { fprintf(stderr, "Usage: resize count[-100-0|0-100] infile outfile\n"); return 1; } // remember filenames char *infile = argv[2]; char *outfile = argv[3]; int n = atoi(argv[1]); // open input file FILE *inptr = fopen(infile, "r"); if (inptr == NULL) { fprintf(stderr, "Could not open %s.\n", infile); return 2; } // open output file FILE *outptr = fopen(outfile, "w"); if (outptr == NULL) { fclose(inptr); fprintf(stderr, "Could not create %s.\n", outfile); return 3; } if(n>0){ resizeLarger(outptr,inptr,n); }else{ resizeSmaller(outptr,inptr,n); } // success return 0; } int resizeLarger(FILE* outptr, FILE* inptr, int n) { // read infile's BITMAPFILEHEADER BITMAPFILEHEADER bf,bfR; fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); bfR = bf; // read infile's BITMAPINFOHEADER BITMAPINFOHEADER bi,biR; fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); biR = bi; // ensure infile is (likely) a 24-bit uncompressed BMP 4.0 if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 || bi.biBitCount != 24 || bi.biCompression != 0) { fclose(outptr); fclose(inptr); fprintf(stderr, "Unsupported file format.\n"); return 4; } biR.biWidth = bi.biWidth * n; biR.biHeight = bi.biHeight * n; int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; int outPadding = (4 - (biR.biHeight * sizeof(RGBTRIPLE)) % 4) % 4; bfR.bfSize = 54 + ((biR.biWidth + outPadding) * abs(biR.biHeight))*sizeof(RGBTRIPLE); biR.biSizeImage = ((biR.biWidth * sizeof(RGBTRIPLE) + outPadding) * abs(biR.biHeight)); printf("%i - %i; %i\n",biR.biWidth,biR.biHeight,biR.biSizeImage); // write outfile's BITMAPFILEHEADER fwrite(&bfR, sizeof(BITMAPFILEHEADER), 1, outptr); // write outfile's BITMAPINFOHEADER fwrite(&biR, sizeof(BITMAPINFOHEADER), 1, outptr); // determine padding for scanlines // iterate over infile's scanlines for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) { for(int m=0; m<n; m++){ // iterate over pixels in scanline for (int j = 0; j < bi.biWidth; j++) { // temporary storage RGBTRIPLE triple; // read RGB triple from infile fread(&triple, sizeof(RGBTRIPLE), 1, inptr); for(int l = 0;l<n;l++){ // write RGB triple to outfile fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr); } } for (int k = 0; k < outPadding; k++) { fputc(0x00, outptr); } if (m < n - 1) { fseek(inptr, -bi.biWidth * sizeof(RGBTRIPLE), SEEK_CUR); } } fseek(inptr, padding, SEEK_CUR); } // close infile fclose(inptr); // close outfile fclose(outptr); return 0; } int resizeSmaller(FILE* outptr, FILE* inptr, int n) { int counter = abs(n); BITMAPFILEHEADER bf,bfR; fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); bfR = bf; // read infile's BITMAPINFOHEADER BITMAPINFOHEADER bi,biR; fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); biR = bi; // ensure infile is (likely) a 24-bit uncompressed BMP 4.0 if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 || bi.biBitCount != 24 || bi.biCompression != 0) { fclose(outptr); fclose(inptr); fprintf(stderr, "Unsupported file format.\n"); return 4; } biR.biWidth = bi.biWidth / counter; biR.biHeight = bi.biHeight / counter; int padding = (4-(bi.biWidth * sizeof(RGBTRIPLE))%4)%4; int outPadding = (4-(biR.biWidth * sizeof(RGBTRIPLE))%4)%4; bfR.bfSize = 54 + ((biR.biWidth + outPadding) * abs(biR.biHeight))*sizeof(RGBTRIPLE); biR.biSizeImage = ((biR.biWidth * sizeof(RGBTRIPLE) + outPadding) * abs(biR.biHeight)); fwrite(&biR,sizeof(BITMAPINFOHEADER),1,outptr); fwrite(&bfR,sizeof(BITMAPFILEHEADER),1,outptr); RGBTRIPLE triple; fseek(inptr, 54, SEEK_SET); fseek(outptr, 54, SEEK_SET); printf("%i - %i; bfSize - %i; biSizeImage - %i; counter - %i\n",biR.biWidth,biR.biHeight,bfR.bfSize,biR.biSizeImage,counter); for(int i =0 ; i<abs(biR.biHeight);i++){ for(int j = 0; j<biR.biWidth;j++){ fread(&triple,sizeof(RGBTRIPLE),1,inptr); fwrite(&triple,sizeof(RGBTRIPLE),1,outptr); fseek(inptr,sizeof(RGBTRIPLE) * (counter - 1),SEEK_CUR); } for(int j=0;j<outPadding;j++){ fputc(0x00,outptr); } fseek(inptr,(bi.biWidth * sizeof(RGBTRIPLE) + padding)*(counter-1),SEEK_CUR); } fclose(outptr); fclose(inptr); return 0; }
问题排查与修复
核心问题1:BMP文件头写入顺序错误
BMP格式强制要求先写入BITMAPFILEHEADER,再写入BITMAPINFOHEADER,但你的resizeSmaller函数里完全写反了顺序:
// 错误顺序:先写INFO头,再写FILE头 fwrite(&biR,sizeof(BITMAPINFOHEADER),1,outptr); fwrite(&bfR,sizeof(BITMAPFILEHEADER),1,outptr);
这会直接破坏BMP文件的结构,系统无法识别为图片,自然显示乱码。
核心问题2:行跳转的fseek计算错误
处理完一行像素后,你需要先跳过当前原行的padding,再跳过多余的counter-1行(包含每行的像素+padding),当前代码未处理原行padding就跳转,导致后续读取位置完全偏移。
修复后的resizeSmaller函数
int resizeSmaller(FILE* outptr, FILE* inptr, int n) { int counter = abs(n); BITMAPFILEHEADER bf,bfR; fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); bfR = bf; BITMAPINFOHEADER bi,biR; fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); biR = bi; if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 || bi.biBitCount != 24 || bi.biCompression != 0) { fclose(outptr); fclose(inptr); fprintf(stderr, "Unsupported file format.\n"); return 4; } biR.biWidth = bi.biWidth / counter; biR.biHeight = bi.biHeight / counter; int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; int outPadding = (4 - (biR.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; bfR.bfSize = bf.bfOffBits + ((biR.biWidth * sizeof(RGBTRIPLE) + outPadding) * abs(biR.biHeight)); biR.biSizeImage = ((biR.biWidth * sizeof(RGBTRIPLE) + outPadding) * abs(biR.biHeight)); // 修正顺序:先写FILE头,再写INFO头 fwrite(&bfR, sizeof(BITMAPFILEHEADER), 1, outptr); fwrite(&biR, sizeof(BITMAPINFOHEADER), 1, outptr); RGBTRIPLE triple; // 定位到原文件像素区域(用bf.bfOffBits更通用) fseek(inptr, bf.bfOffBits, SEEK_SET); for(int i = 0 ; i < abs(biR.biHeight); i++){ for(int j = 0; j < biR.biWidth; j++){ fread(&triple, sizeof(RGBTRIPLE), 1, inptr); fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr); // 跳过当前行中剩余的counter-1个像素 fseek(inptr, sizeof(RGBTRIPLE) * (counter - 1), SEEK_CUR); } // 写入输出文件的行padding for(int j=0; j < outPadding; j++){ fputc(0x00, outptr); } // 先跳过当前原行的padding fseek(inptr, padding, SEEK_CUR); // 再跳过多余的counter-1行(每行像素+padding) fseek(inptr, (bi.biWidth * sizeof(RGBTRIPLE) + padding) * (counter - 1), SEEK_CUR); } fclose(outptr); fclose(inptr); return 0; }
额外说明
bfR.bfSize用bf.bfOffBits + biR.biSizeImage计算更准确,避免硬编码54;- 如果原图片宽高不能被缩小倍数整除,当前代码会直接截断剩余像素,若需要平滑缩小(比如取区域平均值),可额外处理,但这不是文件损坏的原因。
内容的提问来源于stack exchange,提问作者Dalbaeb
相关产品推荐
相关产品推荐

