Импорт файла BMP в Turboc ++: проблема: файл BMP не отображается должным образом на экране вывода - PullRequest
0 голосов
/ 19 марта 2019

Я пытаюсь импортировать файл Anand.BMP в графическом окне TCPP ,
, для этого его исходный код выглядит следующим образом
( ПРИМЕЧАНИЕ : у меня естьне упоминаются заголовочные файлы в исходном коде):

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;
  }

Согласно моим предложениямизменили приведенные выше коды следующим образом:
[EDIT] :

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++){...} loop
Итак, пожалуйста, проанализируйте отредактированный код ...

1 Ответ

1 голос
/ 21 марта 2019

Ваш декодирующий код 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-рендерер некоторые управляющие коды обрабатывались независимо от двоичного доступа ...

Теперь то, что вам не хватает:

  1. заголовок

    заголовок используемого BMP отличается от вашего (существует множество вариаций, поэтому я предложил использовать PCX вместо этого). Итак, взгляните на мою _hdr структуру и подражайте вашей ... DWORD это unsigned 32 бит int, WORD это unsigned 16 бит int и BYTE это unsigned 8 бит int. Я думаю, что TCPP знает их, но когда-то я его кодировал, так что я могу ошибаться, поэтому, если в этом случае вместо этого используются соответствующие типы данных.

    Вы также не проверяете правильный формат BMP, который является неправильным и может привести к сбоям, поэтому вы должны хотя бы проверить магическое число и bpp, сжатие и т. Д., Как я это делаю

    Также не забудьте установить выравнивание кода в 1 байт (для этого #pragma pack, но не уверен, поддерживает ли это TCPP. Если нет, выравнивание должно быть где-то в настройках IDE TCPP, вероятно, в компоновщике или компиляторе). ...

  2. палитра

    Загрузка вашей палитры подозрительна, мне не нравится ... сравните ее с моей в процедуре 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
       }
    
  3. Данные изображения

    вы вообще не выравниваете линии сканирования, поэтому ваше декодированное изображение смещено (как наклон). Согласно 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-битное запутывание всего ... Код выше работает для меня с таким выводом:

screenshot

Как вы можете видеть, я также визуализирую палитру для визуальной проверки, и я получил больше вызовов getch(), поскольку ошибочная клавиатура DOSBOX (вероятно, из-за управления таймерами тактового сигнала процессора ) сводит меня с ума ...

...