在Turbo C ++问题中导入BMP文件:输出屏幕中未正确显示BMP文件

问题描述 投票:-2回答:1

我想在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; 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++):“包含for的函数不是内联扩展的” 2:},即在void load()函数的末尾:“包含一些if语句的函数不是内联扩展的”

结果,图像不显示在输出窗口中 Output就是这样显示的

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

c++ bmp file-format turbo-c++ bgi
1个回答
1
投票

你的解码BMP代码有很多问题...正如我在评论中提到的那样,BMP是一个混乱的格式太多,你很快就会丢失,所以你需要让BMP格式与你的解码程序相匹配......

是的你将BMP改为8bpp,但它的格式与你的格式略有不同......

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

经过一段时间的(de)编码,我想出了这个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)和操作系统你需要忽略VCL的东西并用你的BGI替换渲染...然后将AnsiString更改为char*并将文件访问例程更改为您的环境(做不要忘记它应该是二进制访问,但IIRC甚至在TCPP中并不总是有效它在过去将纹理加载到我的3D渲染器中的一些控制代码处理不管二进制访问...

现在你错过了什么:

  1. 头 使用的BMP标题与您的标题不同(有很多变化,这就是我建议使用PCX的原因)。所以看看我的_hdr结构和模仿你的... DWORDunsigned 32位intWORDunsigned 16位intBYTEunsigned 8位int。我认为TCPP知道它们,但我的代码在其中的年龄,所以我可能是错的,所以如果案例使用相关的数据类型。 您也没有检查错误的BMP格式是否错误并且可能导致崩溃,因此您至少应检查幻数和bpp,压缩等...就像我做的那样 另外不要忘记将代码对齐设置为1 Byte(这是#pragma pack的用途,但不确定TCPP是否支持。如果不是对齐应该在TCPP IDE设置中的某处可能在链接器或编译器中...
  2. 调色板 你的调色板加载是可疑的我不喜欢它...在draw例程中与我的比较。 你设置VGA调色板例程也是错误的,请参阅how it should be done。因此,应为每个颜色设置目标颜色,而不是仅为整个调色板设置一次,因此您需要移出内部循环: 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。 您也不使用offset来告诉您文件在图像数据的起始位置。这很重要,因为图像数据可以在调色板之后的任何地方,因为文件中可能存在更多数据,如重要的颜色等...

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

EDIT1

在这里,我从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

只需验证你的inbuild帮助的使用情况(CTRL + F1,而光标在关键字上,你也会看到stdio不是那个包含它所需的那个),因为我在20年前用过这个并且不记得确切...你也需要寻求我认为它的名为fseek和参数类似于我的FileSeek

[Edit2]来自您更新的代码,显而易见,您只需复制粘贴代码而不必考虑...

我设法在TCPP + DOSBOX中编写代码(因为DOSBOX键盘与Borland快捷方式发生冲突,因为屁股很痛苦!)

您没有检查inbuild 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,因为我讨厌它而不是我使用direct memory access and VGA mode 13h但是我编码它所以它类似于你的BGI所以你需要移植它(重新启动gfxinit / exit和putpixel函数体)如果你想使用BGI代替。

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

screenshot

正如你所看到我也渲染了视觉检查的调色板,我得到了更多的getch()调用,因为DOSBOX错误的键盘(可能是因为CPU Clock tics timing control)让我发疯...

© www.soinside.com 2019 - 2024. All rights reserved.