在控制台上显示 BMP 映像时堆损坏

Corrupted heap while display a BMP image on console

本文关键字:损坏 映像 BMP 控制台 显示      更新时间:2023-10-16

>我有一个练习。它说,C程序应该能够读取位图文件的信息,然后它应该在控制台上显示图片。

我已经编写了代码,但是当它无法正常工作时。 当我调试代码时,看起来堆已损坏。我认为我在ScanPixelline功能中有一个已知的故障/错误。

我不知道如何解决它。有人可以帮我检查一下吗? 我对C编程比较陌生。

#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include "windows.h"
#pragma pack(1)
struct BMP
{
char Type[2];           //File type. Set to "BM".
int32_t Size;     //Size in BYTES of the file.
int16_t Reserved1;      //Reserved. Set to zero.
int16_t Reserved2;      //Reserved. Set to zero.
int32_t OffSet;   //Offset to the data.
int32_t headsize; //Size of rest of header. Set to 40.
int32_t Width;     //Width of bitmap in pixels.
int32_t Height;     //  Height of bitmap in pixels.
int16_t Planes;    //Number of Planes. Set to 1.
int16_t BitsPerPixel;       //Number of Bits per pixels.
int32_t Compression;   //Compression. Usually set to 0.
int32_t SizeImage;  //Size in bytes of the bitmap.
int32_t XPixelsPreMeter;     //Horizontal pixels per meter.
int32_t YPixelsPreMeter;     //Vertical pixels per meter.
int32_t ColorsUsed;   //Number of colors used.
int32_t ColorsImportant;  //Number of "important" colors.
};
struct Color
{
unsigned char B;
unsigned char G;
unsigned char R;
};
struct ColorTable
{
Color    *colors;
unsigned long length;
};
struct PixelArray
{
Color    **pixels;
unsigned long rowCount;
unsigned long columnCount;
};
void readBMP(char *File_Name, BMP &a)
{
FILE *p = fopen(File_Name, "rb");
if (p == NULL)
{
printf("Can't open file!");
fclose(p);
return;
}
else
{
fread(&a, sizeof(BMP), 1, p);
}
fclose(p);
}
void Get_Inf(BMP a)
{
if (a.Type[0] != 'B' || a.Type[1] != 'M')
{
printf("This is not a BMP file");
}
else
{
printf("This is a BMP filen");
printf("The size of this file is %lu bytesn", a.Size);
printf("The witdth of this image is %lu pixelsn", a.Width);
printf("The height of this image is %lu pixelsn", a.Height);
printf("The number of bits per pixels in this image is %un", a.BitsPerPixel);
}
}
void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}
void skipBmpPadding(char count)
{
FILE *pointer__ = fopen("test.bmp", "rb");
if (count == 0)
{
fclose(pointer__);
return;
}
char padding[3];
fread(&padding, sizeof(char), count, pointer__);
fclose(pointer__);
//file.read((char *)&padding, count);
}
void ReadPixelArray(BMP a, PixelArray &data)
{
FILE *pointer = fopen("test.bmp", "rb");
data.rowCount = a.Height;
data.columnCount = a.Width;
data.pixels = new Color*[data.rowCount];
char paddingCount = (4 - (a.Width * (a.BitsPerPixel / 8) % 4)) % 4;
fseek(pointer, 54, SEEK_SET);
for (int i = 0; i < data.rowCount; i++)
{
scanBmpPixelLine(data.pixels[data.rowCount - i - 1], a.Width);
skipBmpPadding(paddingCount);
}
}
void drawBmp(BMP a, PixelArray data)
{
HWND console = GetConsoleWindow();
HDC hdc = GetDC(console);
for (int i = 0; i < a.Height; i++)
for (int j = 0; j < a.Width; j++)
{
Color pixel = data.pixels[i][j];
SetPixel(hdc, j, i, RGB(pixel.R, pixel.G, pixel.B));
}
ReleaseDC(console, hdc);
}
void releaseBmpPixelArray(PixelArray data)
{
for (int i = 0; i < data.rowCount; i++)
delete[]data.pixels[i];
delete[]data.pixels;
}
int main()
{
char file_name[] = "test.bmp";
BMP a;
PixelArray data;
readBMP(file_name, a);
Get_Inf(a);
ReadPixelArray(a, data);
drawBmp(a, data);
releaseBmpPixelArray(data);
}

此函数:

void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}

对于初学者来说,该函数的目的似乎是从文件中读取一行像素数据。 但相反,它会重新打开文件并从开头(标头字节所在的位置)读取。我不确定你是否知道这一点...

但崩溃是这条线的结果:

fread(line, sizeof(Color), sizeof(Color)*length, pointer_);

第二个参数 sizeof(Color) 是每个元素的大小。 第三个参数是要读取的元素数。 从文件中读取的总字节数将是第二个参数乘以第三个参数。所以你已经冗余地乘以sizeof(Color)太多次了。结果是它将覆盖line缓冲区。

要修复,它应该是:

fread(line, sizeof(Color), length, pointer_);

您可能希望将从ReadPixelArray函数获取的FILE* pointer传递到此函数中,而不是重新打开每一行的文件。

另一个代码审查注释。 您应该只将整个文件读入内存,而不是为每个操作冗余地打开和关闭文件。然后解析标头并设置指向标头后面第一行的指针。