-2

我正在尝试在TCPP的图形窗口中导入 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 文件,

图形窗口没有正确显示 Anand.bmp;输出是这样显示的,
那么如何解决呢?
为了方便起见,我在这里附上我的 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++)“包含for的函数未内联扩展”
2:,}即在void load()函数末尾:“包含某些if语句的函数未内联扩展”

结果图像未显示在输出窗口中
输出显示如下

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

4

1 回答 1

2

您的解码 BMP 代码有很多问题...正如我在评论中提到的那样,BMP 的格式变化太多,您很快就会迷失方向,因此您需要 BMP 格式与您的解码程序相匹配...

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

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

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

//---------------------------------------------------------------------------
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)和操作系统,你需要忽略 VCL 的东西并用你的 BGI 替换渲染......然后更改AnsiStringtochar*并将文件访问例程更改为你的环境(不要忘记它应该是二进制访问,但 IIRC 即使在 TCPP 中并不总是有效,过去在将纹理加载到我的 3D 渲染器时也会遇到问题,而不管二进制访问如何处理一些控制代码......

现在你缺少什么:

  1. 标题

    使用的 BMP 标头与您的不同(有很多变化,这就是我建议使用PCX的原因)。所以看看我的_hdr结构并模仿你的......它DWORDunsigned32 位、int16位和8位。我认为 TCPP 知道它们,但我在其中编码了年龄,所以我可能是错的,所以如果案例使用相关的数据类型来代替。WORDunsignedintBYTEunsignedint

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

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

  2. 调色板

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

    此外,您的设置 VGA 调色板例程是错误的,请参阅应该如何完成。因此,应该为每种颜色设置目标颜色,而不仅仅是为整个调色板设置一次,因此您需要移动 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
    

    因此,只需在每行解码后跳过文件中未使用的 BYTE。

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

正如你所看到的,我将整个图像加载到内存中并从那里解码。当您处于 16 位环境中时,您可能不想这样做,因为您的操作系统可能会阻止您分配尽可能多的内存,而且您的内存大小也受到了很大的限制......但是我对整个内容进行了编码,所以我不回去并转发,所以你应该没有问题将它移植到像现在这样直接从文件中解码......

[编辑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 年前使用它并且不记得确切...您还需要 seek 我认为它的调用fseek和参数与我的相似FileSeek

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

我设法在TCPP+DOSBOX中编写了这个代码(哎呀,当DOSBOX键盘与 borland 快捷键发生冲突时,真是让人头疼……)

您没有检查内置 TCPP 帮助,也没有正确移植这些东西。例如fseek,如果您尝试调试(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,因为我讨厌它,而是使用直接内存访问和 VGA 模式 13h,但我对其进行了编码,因此它与您的BGI相似,因此您需要移植它(删除 gfxinit/exit 和 putpixel 函数体),如果你想改用BGI

我将 BMP 直接放入其中C:\,因此我无需担心 exe 本地路径……您确实有很多错误,例如丢弃dataBMP 存储、错误的调色板代码等……但是您遇到的最大错误是BYTE定义因为你的是 16 位而不是 8 位搞乱一切......上面的代码适用于我这个输出:

截屏

正如你所看到的,我还渲染了调色板以进行视觉检查,我接到了更多getch()的电话,因为 DOSBOX 错误的键盘(可能是因为CPU 时钟抽动时序控制)让我发疯......

于 2019-03-21T09:18:12.683 回答