在 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 替换渲染......然后改变 AnsiString
到 char*
并将文件访问例程更改为您的环境(不要忘记它应该是二进制访问,但 IIRC 即使它并不总是在 TCPP 中工作,但在过去将纹理加载到我的 3D 渲染器时遇到了一些问题无论二进制访问如何处理控制代码...
现在你缺少的是:
header
所使用的 header 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。因此,应该为每种颜色设置目标颜色,而不仅仅是为整个调色板设置一次,因此您需要移动 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
}
图像数据
你根本没有对齐扫描线,这就是你的解码图像发生偏移(歪斜)的原因。根据 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()
调用疯了...
我正在尝试在 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 替换渲染......然后改变 AnsiString
到 char*
并将文件访问例程更改为您的环境(不要忘记它应该是二进制访问,但 IIRC 即使它并不总是在 TCPP 中工作,但在过去将纹理加载到我的 3D 渲染器时遇到了一些问题无论二进制访问如何处理控制代码...
现在你缺少的是:
header
所使用的 header 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。因此,应该为每种颜色设置目标颜色,而不仅仅是为整个调色板设置一次,因此您需要移动 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 }
图像数据
你根本没有对齐扫描线,这就是你的解码图像发生偏移(歪斜)的原因。根据 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()
调用疯了...