Ваш декодирующий код BMP имеет много проблем ... Как я уже упоминал в моих комментариях, BMP - это беспорядок со слишком большим количеством вариантов формата, в котором вы очень быстро теряетесь, поэтому вам нужно иметь формат BMP, соответствующий вашей процедуре декодирования ... .
Да, вы меняете BMP на 8bpp, но его формат немного отличается от вашего ...
Хорошо, давайте использовать это ваше изображение (почему, черт возьми, 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) и ОС, вам нужно проигнорировать материал 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. Если нет, выравнивание должно быть где-то в настройках IDE TCPP, вероятно, в компоновщике или компиляторе). ...
палитра
Загрузка вашей палитры подозрительна, мне не нравится ... сравните ее с моей в процедуре draw
.
Кроме того, неправильно настроена процедура настройки палитры VGA. См. Как это сделать . Таким образом, целевой цвет должен быть установлен для каждого цвета, а не только один раз для всей палитры, поэтому вам нужно переместить внутренний цикл:
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-битной среде, вы, возможно, не захотите этого делать, так как ваша ОС может помешать вам выделить столько памяти, а также вы ограничены в объеме памяти ... Но я написал весь код, поэтому я не вернусь назад. и вперед, чтобы у вас не было проблем с переносом кода на декодирование непосредственно из файла, как у вас сейчас ...
[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 конфликтует с клавиатурой бордовые сочетания клавиш ...)
Вы не проверили встроенную справку по 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 ... У вас там было много ошибок, таких как data
для хранения BMP, неправильный код палитры и т. Д. . Но самой большой ошибкой, которую вы получили, было BYTE
определение , поскольку у вас было 16-битное, а не 8-битное запутывание всего ... Код выше работает для меня с таким выводом:
Как вы можете видеть, я также визуализирую палитру для визуальной проверки, и я получил больше вызовов getch()
, поскольку ошибочная клавиатура DOSBOX (вероятно, из-за управления таймерами тактового сигнала процессора ) сводит меня с ума ...