在 Turboc++ 中导入 BMP 文件 issue:BMP 文件未在输出屏幕中正确显示

Importing BMP file in Turboc++ issue:BMP file is not being displayed properly in the output screen

我正在尝试在 TCPP
的图形 window 中导入 Anand.BMP 文件 为此其源代码如下
注意:我没有在源代码中提到头文件):

struct A
{
 char type[2];
 unsigned long size;
 unsigned short int reserved1,reserved2;
 unsigned long offset;
 unsigned long width,height;
 unsigned short int planes;
 unsigned short int bits;
 unsigned long compression;
 unsigned long imagesize;
 unsigned long xresolution,yresolution;
 unsigned long ncolors;
 unsigned long importantcolors;
}HEADER;
huge DetectSvga()
{
 return 2;
}
void show()
{
 fstream File;
 File.open("C:\TURBOC3\BIN\Anand.BMP",ios::in|ios::binary);
 char ch;
 File.read((char*)&HEADER,sizeof(HEADER));
 unsigned int i;
 char ColorBytes[4];
 char *PaletteData;
 PaletteData=new char[256*3];
 if(PaletteData)
 {
  for(i=0;i<256;i++)
  {
   File.read(ColorBytes,4);
   PaletteData[(int)(i*3+2)]=ColorBytes[0]>>2;
   PaletteData[(int)(i*3+0)]=ColorBytes[2]>>2;
  }
  outp(0x03c8,0);
  for(i=0;i<256*3;i++)
   outp(0x03c9,PaletteData[i]);
  delete[]PaletteData;
 }
 for(i=0;i<HEADER.height;i++)
 {
  for(int j=0;j<HEADER.width;)
  {
   File.read(&ch,1);
   putpixel(0+(j++),0+HEADER.height-i-1,ch);
  }
 }
 File.close();
}
void main()
{
 clrscr();
 int gd=DETECT,gm,a;
 initgraph(&gd,&gm,"C:\TURBOC3\BGI");
 installuserdriver("svga256",&DetectSvga);
 show();
 getch();
 closegraph();
}

现在,我没有在图形中获取 BMP 文件 window,
即,
图形 Window 未正确显示 Anand.bmp; Output是这样显示的
那么如何解决呢?
Here 为了方便起见,我附上了我的 Anand.BMP 文件。

我认为调色板无法通过 PaletteData 指针正确显示,
即,错误在此代码块中:

for(i=0;i<256;i++)
  {
   File.read(ColorBytes,4);
   PaletteData[(int)(i*3+2)]=ColorBytes[0]>>2;
   PaletteData[(int)(i*3+0)]=ColorBytes[2]>>2;
  }

根据建议我修改了上面的代码如下:
[编辑]

typedef unsigned long   DWORD;
typedef unsigned int  WORD;
typedef unsigned short  BYTE;

//---------------------------------------------------------------------------
class BMP
{
 public:
  BYTE *data;
  DWORD size;
  #pragma pack(push,1)
  struct _hdr
  {
   char ID[2];
   DWORD size;
   WORD reserved1[2];  // ?
   DWORD offset;
   DWORD reserved2;    // ?
   DWORD width,height;
   WORD planes;
   WORD bits;
   DWORD compression;
   DWORD imagesize;
   DWORD xresolution,yresolution;
   DWORD ncolors;
   DWORD importantcolors;
  };
  #pragma pack(pop)
  BMP(){ data=NULL; free(); }

  ~BMP(){ free(); }

  void free(){ if (data) delete[] data; data=NULL; size=0;  }
  void load(char* filename)
  {
   FILE *hnd;
   free();
   if ((hnd=fopen(filename, "rb")) == NULL) return; // open file for read binary (not sure with the "b" check in build help)
   size=fseek(hnd,0,2);
   fseek(hnd,0,0);
   BYTE data[256];
   if (data==NULL)          // not enough memory or empty file
   {
    size=0;
    fclose(hnd);      
    return;
   }
   fread(data,256,1,hnd); // read 256 of 1 BYTES into data array
   fclose(hnd); // close file
  }
  void draw(int x0,int y0)
  {
   _hdr *hdr=(_hdr*)data;
   int x,y,xs,ys,skip;
   DWORD pal[256],c;  // palete to convert 8bpp -> 32bit VCL color
   BYTE *p;
   if (size<2) return;
   if (hdr->ID[0]!='B') return;    // check magic number
   if (hdr->ID[1]!='M') return;
   if (hdr->planes!=1) return;     // check format
   if (hdr->bits!=8) return;
   if (hdr->compression!=0) return;
   // palette
   p=data+hdr->offset-(3*256);
   p=data+sizeof(_hdr);
   for (x=0;x<256;x++)
   {
    c =(*p)    ; p++;   // B
    c|=(*p)<< 8; p++;   // G
    c|=(*p)<<16; p++;   // R
         p++;   // A
    pal[x]=c;
   }
   // image
   xs=hdr->width;
   ys=hdr->height;
   p=data+hdr->offset;
   skip=(((hdr->bits*hdr->width)+31)>>5)<<2;  // compute scanline align
   skip-=hdr->width;
   for (y=0;y<ys;y++)
   {
    for (x=0;x<xs;x++,p++)
    {
     putpixel(x0+x,y0+ys-y-1,*p);
    }
    p+=skip;                       // handle align
   }
   y++;
  }
};
//---------------------------------------------------------------------------

    huge DetectSvga()
{
 return 2;
}

void main()
{
 clrscr();
 int gd=DETECT,gm,a;
 initgraph(&gd,&gm,"C:\TURBOC3\BGI");
 installuserdriver("svga256",&DetectSvga);
 BMP bmp;
 bmp.load("C:\TURBOC3\BIN\Anand.BMP");
 bmp.draw(0,0);
 getch();
 closegraph();
}

现在,上面的代码没有给出任何错误,但给出了 2 个警告!!

警告
1: for(x=0;x<256;x++) : "Functions containing for are not expanded inline"
2 : } ,即在 void load() 函数的末尾: "Functions containing some if statements are not expanded inline"

因此图像未显示在输出中 window
Output是这样显示的

我认为 y++; 应该在 for (y=0;y<ys;y++){...} 循环内
所以,请分析编辑后的代码...

你的解码 BMP 代码有很多问题...正如我在评论中提到的,BMP 是一团乱麻,有太多的格式变化,你很快就会迷失方向,所以你需要 BMP 格式与你的解码程序相匹配...

是的,您将 BMP 更改为 8bpp,但它的格式仍然与您的略有不同...

好的,让我们使用你的 this 图片(为什么 imgur 不支持这个???)。

经过一段时间的(解码)编码,我想出了这个 C++/VCL 代码来正确解码你的 bmp:

//---------------------------------------------------------------------------
class BMP
    {
public:
    BYTE *data;
    DWORD size;
    #pragma pack(push,1)
    struct _hdr
        {
        char ID[2];
        DWORD size;
        WORD reserved1[2];  // ?
        DWORD offset;
        DWORD reserved2;    // ?
        DWORD width,height;
        WORD planes;
        WORD bits;
        DWORD compression;
        DWORD imagesize;
        DWORD xresolution,yresolution;
        DWORD ncolors;
        DWORD importantcolors;
        };
    #pragma pack(pop)
    BMP(){ data=NULL; free(); }
    ~BMP(){ free(); }
    void free(){ if (data) delete[] data; data=NULL; size=0;  }

    void load(AnsiString filename)                      // load BMP into memory
        {
        int hnd;
        free();
        hnd=FileOpen(filename,fmOpenRead);              // open file
        if (hnd<0) return;
        size=FileSeek(hnd,0,2);                         // seek to end of file to obtain filesize
             FileSeek(hnd,0,0);                         // seek to start of file
        data=new BYTE[size];                            // allocate memory space for the BMP
        if (data==NULL)                                 // not enough memory or empty file
            {
            size=0;
            FileClose(hnd);
            return;
            }
        FileRead(hnd,data,size);                        // load the data
        FileClose(hnd);
        }
    void draw(Graphics::TBitmap *bmp,int x0,int y0)     // decode/render bitmap onto VCL bitmap
        {
        _hdr *hdr=(_hdr*)data;
        int x,y,xs,ys,skip;
        DWORD pal[256],c;                               // palete to convert 8bpp -> 32bit VCL color
        BYTE *p;
        if (size<2) return;
        if (hdr->ID[0]!='B') return;                    // check magic number
        if (hdr->ID[1]!='M') return;
        if (hdr->planes!=1) return;                     // check format
        if (hdr->bits!=8) return;
        if (hdr->compression!=0) return;
        // palette
        p=data+hdr->offset-(3*256);
        p=data+sizeof(_hdr);
        for (x=0;x<256;x++)
            {
            c =(*p)    ; p++;   // B
            c|=(*p)<< 8; p++;   // G
            c|=(*p)<<16; p++;   // R
                         p++;   // A
            pal[x]=c;
            }
        // image
        xs=hdr->width;
        ys=hdr->height;
        p=data+hdr->offset;
        skip=(((hdr->bits*hdr->width)+31)>>5)<<2;       // compute scanline align
        skip-=hdr->width;
        for (y=0;y<ys;y++)
            {
            DWORD *q=(DWORD*)bmp->ScanLine[y0+ys-y-1];  // target VCL bitmap scanline pointer
            for (x=0;x<xs;x++,p++) q[x0+x]=pal[*p];     // copy pixels to target VCL bitmap
            p+=skip;                                    // handle align
            }
        y++;
        }
    };
//---------------------------------------------------------------------------

和用法:

BMP bmp;
bmp.load("Anand.bmp");
bmp.draw(target_VCL_bitmap,0,0);

因为我有不同的编译器(还有 Borland/Embarcadero)和 OS 你需要忽略 VCL 的东西并用你的 BGI 替换渲染......然后改变 AnsiStringchar* 并将文件访问例程更改为您的环境(不要忘记它应该是二进制访问,但 IIRC 即使它并不总是在 TCPP 中工作,但在过去将纹理加载到我的 3D 渲染器时遇到了一些问题无论二进制访问如何处理控制代码...

现在你缺少的是:

  1. header

    所使用的 header BMP 与你的不同(那里有很多变化,这就是为什么我建议改用 PCX)。所以看看我的 _hdr 结构并模仿你的... DWORDunsigned 32 位 intWORDunsigned 16位 intBYTEunsigned 8 位 int。我认为 TCPP 知道它们,但我在其中编写代码已经很长时间了,所以我可能是错的,所以如果这种情况使用相关的数据类型。

    你也没有检查正确的 BMP 格式,这是错误的,可能会导致崩溃,所以你至少应该像我一样检查幻数和 bpp、压缩等......

    也不要忘记将代码对齐设置为 1 字节(这就是 #pragma pack 的用途,但不确定 TCPP 是否支持。如果不支持,对齐应该位于 TCPP IDE 设置可能在链接器或编译器中...

  2. 调色板

    你的调色板加载很可疑我不喜欢它...在 draw 例程中将它与我的进行比较。

    您设置的 VGA 调色板例程也是错误的,请参阅 how it should be done。因此,应该为每种颜色设置目标颜色,而不仅仅是为整个调色板设置一次,因此您需要移动 out inside 循环:

    for(i=0;i<256*3;)
       {
       outp(0x03c8,i/3);
       outp(0x03c9,PaletteData[i]); i++; // R
       outp(0x03c9,PaletteData[i]); i++; // G
       outp(0x03c9,PaletteData[i]); i++; // B
       }
    
  3. 图像数据

    你根本没有对齐扫描线,这就是你的解码图像发生偏移(歪斜)的原因。根据 Wiki,每条扫描线都与大小对齐:

    (((bits*width)+31)>>5)<<2
    

    所以在每一行解码后跳过文件中未使用的字节。

    您也不要使用 offset 来告诉您图像数据在文件中的起始位置。这很重要,因为图像数据可以位于任何地方,而不仅仅是调色板之后,因为文件中可能存在更多数据,例如重要的颜色等...

正如您所看到的,我将整个图像加载到内存中并从那里解码。因为你在 16 位环境中,你可能不想这样做,因为你的 OS 可能会阻止你分配尽可能多的内存,而且你的内存大小也受到很大限制......但我编码了整个东西所以我不要来回,所以你应该没有问题将它移植到直接从文件解码,就像你现在拥有的那样......

[编辑1]

在这里我挖掘了一些从 TCPP 访问文件的古老例子:

#include <stdio.h>
FILE *hnd;
BYTE data[256];
if ((hnd=fopen("texture.txr", "rb")) == NULL) return; // open file for read binary (not sure with the "b" check in build help)
fread(data,256,1,hnd); // read 256 of 1 BYTES into data array
fclose(hnd); // close file

只需使用您的内置帮助验证用法(CTRL+F1,当光标在关键字上时,如果 stdio 不是那个,您还将看到它需要哪个包含),因为我在 20 年前使用过这个并且没有确切地记住......你还需要寻找我认为它叫做fseek并且参数类似于我的FileSeek

[Edit2] 从您更新的代码中可以明显看出您只是不假思索地复制粘贴代码...

我设法在 TCPP+DOSBOX 中编写了这个代码(哎呀,DOSBOX 键盘冲突 borland 快捷方式...)

您没有检查内置 TCPP 帮助,也没有正确移植这些东西。例如你的 fseek 不像我的 return 文件大小,如果你尝试调试 (F8/F7) 你会立即检测到......所以这里是我的新 C++ TCPP 兼容)代码:

//---------------------------------------------------------------------------
#include <stdio.h>
#include <conio.h>
//---------------------------------------------------------------------------
typedef unsigned long DWORD;
typedef unsigned int  WORD;
typedef unsigned char BYTE;
//---------------------------------------------------------------------------
char far* scr;              // VGA screen
const _sx= 320;             // physical screen size
const _sy= 200;
void gfxinit()
    {
    asm {   mov ax,19
        int 16
        }
    scr=(char far*)0xA0000000;
    }
void gfxexit()
    {
    asm {   mov ax,3
        int 16
        }
    }
void clrscr()
    {
    asm {   push    es
        mov ax,0xA000
        mov es,ax
        mov di,0x0000
        sub ax,ax
        mov cx,32000
        rep stosw
        pop es
        }
    }
void putpixel(int x,int y,char c)
    {
    unsigned int adr;
    if ((x<_sx)&&(x>=0))
     if ((y<_sy)&&(y>=0))
        {
        adr=x+(y*_sx);
        scr[adr]=c;
        }
    }
//---------------------------------------------------------------------------
class BMP
    {
public:
    BYTE *data;
    DWORD size;
    #pragma pack(push,1)
    struct _hdr
        {
        char ID[2];
        DWORD size;
        DWORD reserved1;  // ?
        DWORD offset;
        DWORD reserved2;  // ?
        DWORD width,height;
        WORD planes;
        WORD bits;
        DWORD compression;
        DWORD imagesize;
        DWORD xresolution,yresolution;
        DWORD ncolors;
        DWORD importantcolors;
        };
    #pragma pack(pop)
    BMP(){ data=NULL; free(); }
    ~BMP(){ free(); }
    void free(){ if (data) delete[] data; data=NULL; size=0;  }
    void load(char* filename);
    void draw(int x0,int y0);
    };
//---------------------------------------------------------------------------
void BMP::load(char* filename)
    {
    FILE *hnd;
    free();
    if ((hnd=fopen(filename, "rb")) == NULL) return; // open file for read binary (not sure with the "b" check in build help)
    _hdr hdr;
    hdr.ID[0]=0;
    hdr.ID[1]=0;
    hdr.size=0;
    fread(&hdr,sizeof(_hdr),1,hnd); // read BMP header into memory
    if (hdr.ID[0]=='B')
     if (hdr.ID[1]=='M')
      size=hdr.size;    // get file size
    fseek(hnd,0,0);     // seek back to start
    data=new BYTE[size];
    if (data==NULL)     // not enough memory or empty file
        {
        size=0;
        fclose(hnd);
        return;
        }
    fread(data,size,1,hnd); // read BMP into memory
    fclose(hnd);        // close file
    }
//---------------------------------------------------------------------------
void BMP::draw(int x0,int y0)
    {
    _hdr *hdr=(_hdr*)data;
    int x,y,xs,ys,skip;
    BYTE *p;
    if (size<2) return;
    if (hdr->ID[0]!='B') return;    // check magic number
    if (hdr->ID[1]!='M') return;
    if (hdr->planes!=1) return;     // check format
    if (hdr->bits!=8) return;
    if (hdr->compression!=0) return;
    // palette
    p=data+sizeof(_hdr);
    for (x=0;x<256;x++)
        {
        BYTE r,g,b;
        b=*p>>2; p++;
        g=*p>>2; p++;
        r=*p>>2; p++;
             p++;
        outp(0x3C8,x);
        outp(0x3C9,r);
        outp(0x3C9,g);
        outp(0x3C9,b);
        }
    // image
    xs=hdr->width;
    ys=hdr->height;
    p=data+hdr->offset;
    skip=(((hdr->bits*hdr->width)+31)>>5)<<2;  // compute scanline align
    skip-=hdr->width;
    for (y=0;y<ys;y++,p+=skip)
     for (x=0;x<xs;x++,p++)
      putpixel(x0+x,y0+ys-y-1,*p);
    }
//---------------------------------------------------------------------------
void main()
    {
    BMP bmp;
    bmp.load("C:\Anand.BMP");
    gfxinit();
    clrscr();
    bmp.draw(0,16);
    // draw palette
    for (int x=0;x<256;x++)
     for (int y=0;y<8;y++)
      putpixel(x,y,x);
    getch();
    getch();
    getch();
    gfxexit();
    }
//---------------------------------------------------------------------------

我不使用 BGI 因为我讨厌它而不是我使用 direct memory access and VGA mode 13h 但我编码它所以它类似于你的 BGI 所以如果你想使用 BGI,你需要移植它(删除 gfxinit/exit 和 putpixel 函数体)。

我将 BMP 直接放入 C:\ 所以我不需要担心 exe 本地路径......你确实有很多错误,比如丢弃 data BMP 存储,错误的调色板代码等... 但是你得到的最大错误是 BYTE 定义 因为你的是 16 位而不是 8 位搞砸了一切......代码bove 对我有用这个输出:

如您所见,我还渲染了调色板以进行视觉检查,并且由于 DOSBOX 有问题的键盘(可能是因为 CPU Clock tics timing control),我收到了更多的 getch() 调用疯了...