我正在尝试在 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++){...}
循环之内
因此,请分析编辑后的代码...
答案 0 :(得分:1)
您的解码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替换呈现...然后将AnsiString
更改为char*
并根据您的环境更改文件访问例程(不要忘记它应该是二进制访问,但是IIRC甚至在TCPP中并不总是有效,过去在将纹理加载到我的3D渲染器中时,无论二进制访问如何,都处理了一些控制代码, ...
现在您缺少了什么:
标题
所使用的BMP标头与您使用的标头不同(存在很多变体,这就是为什么我建议改用PCX的原因)。因此,看看我的_hdr
结构并模仿您的结构吧……DWORD
是unsigned
32位int
,WORD
是unsigned
16 int
位和BYTE
位是unsigned
8位int
。我认为TCPP知道它们,但是我在其中编码已有很长时间了,所以如果情况使用相关的数据类型,我可能是错的。
您也不检查错误的BMP格式是否正确,并可能导致崩溃,因此您至少应像我一样检查幻数和bpp,压缩等...
也不要忘记将代码对齐方式设置为1字节(这是#pragma pack
的目的,但是不确定TCPP是否支持该对齐方式。如果不是,则对齐方式应该位于TCPP IDE设置中的链接器中或编译器...
调色板
您的调色板加载很可疑,我不喜欢它……将其与我的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
}
图像数据
您根本没有对齐扫描线,这就是您的解码图像偏移(偏斜)的原因。根据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
只需在您的内置帮助中验证用法(当光标位于某个关键字上时,使用CTRL + F1即可,如果您不是stdio的话,您还将看到其中包括所需的内容),就像我20年前使用的那样,不记得确切了...您还需要寻找,我认为它的名称为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 ,因为我讨厌使用direct memory access and VGA mode 13h,但是我对其进行了编码,使其类似于您的 BGI ,因此您需要移植(删除gfxinit / exit和putpixel函数体),如果您想使用 BGI 。
我将BMP直接放置到C:\
中,因此我不必担心exe本地路径...您确实有很多错误,例如为data
保留了BMP存储空间,错误的调色板代码等... 但是您遇到的最大错误是BYTE
定义,因为您的错误是16位而不是8位,使所有内容搞乱了。上面的代码对我有用:
如您所见,我还渲染了调色板以进行视觉检查,因为DOSBOX错误的键盘(可能是由于CPU Clock tics timing control引起),我接到了更多getch()
个电话……