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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 05:01:19