Вывод BMP в видеорежимах VESA
Очень часто в форумах всплывают такие вопросы, как вывод на экран BMP и работа с видеорежимами VESA. Этот пример ответит на оба вопроса.
Программа писалась в студенческие годы, в качестве лабораторной работы. Студенты должны были получить спецификации и писать модули для поддержки файлов разных графических форматов, но были выставлены автоматы, и все замялось.
Все закончилось на том, что включался видеорежим, были написаны кое-какие функции для него (blt(), putpixel() и line())
bmp.cpp
#include "vesa.h";
#include <math.h>;
#include "bmp.h";
void BMP_PutFromFile(TMode m, int x, int y, char * fname) {
FILE* fp;
struct BMPHeader bmp;
TPallete pal[256]; // Палитра
char paloffset; // Смещение в палитре;
if (x>m.w || y>m.h) return; // Если не влезает в экран.
fp=fopen(fname,"rb");
fread(&bmp,54,1,fp);
if (bmp.ID==0x4D42) { // Проверим ID
int rw=m.w-x-1; if (rw>bmp.Width) rw=bmp.Width; // Требуемая ширина
int rh=m.h-y-1; if (rh>bmp.Height) rh=bmp.Height; // Требуемая высота
int bpl=bmp.ISize/bmp.Height; // Байт на строку у BMP
ul pnt=(ul)m.VLFB;
pnt=pnt+(y+rh)*m.bpl+x*m.bytespp; // Установим указатель на нижнюю строку картинки
// 24 бита не пакованая ##############################################
if (bmp.Bits==24 && bmp.Comp==0) {
if (bmp.Height-rh!=0) fseek(fp, (bmp.Height-rh)*bpl ,SEEK_CUR);
int fseeksize=bpl-rw*3; // Пропускаемая часть в файле
int pntmove=m.bpl+rw*4; // Смещене указателя на экране
for (int i=0;i<rh;i++) {
for (int j=0;j<rw;j++) {
fread((void *)pnt, 3, 1, fp);
pnt+=4;
}
fseek(fp, fseeksize, SEEK_CUR);
pnt-=pntmove;
}
}
// 8 бит не пакованая ################################################
if (bmp.Bits==8 && bmp.Comp==0) {
if (bpl==0) bpl=bmp.Width;
fread(&pal,1024,1,fp); // Читаем палитру
if (bmp.Height-rh!=0) fseek(fp, (bmp.Height-rh)*bpl ,SEEK_CUR);
int fseeksize=bpl-rw; // Пропускаемая часть в файле
int pntmove=m.bpl+rw*4; // Смещене указателя на экране
char * pnto;
for (int i=0;i<rh;i++) {
for (int j=0;j<rw;j++) {
fread(&paloffset, 1, 1, fp);
pnto=(char *)pnt;
pnto[2]=pal[paloffset].r;
pnto[1]=pal[paloffset].g;
pnto[0]=pal[paloffset].b;
pnt+=4;
}
fseek(fp, fseeksize, SEEK_CUR);
pnt-=pntmove;
}
} // end of 8 bit
}
fclose(fp);
}
Download this code: bmp.cpp
glib2000.cpp
#include <conio.h>;
#include "vesa.h";
#include "bmp.h";
char VScreen[1024*768*4];
void DrawHistogram(char * scr, int size, char *s) {
int R[256];
int G[256];
int B[256];
int i,val;
char value;
memset (&R, 0, sizeof(R));
memset (&G, 0, sizeof(G));
memset (&B, 0, sizeof(B));
for (i=0;i<size;i=i+4) { // Рассчитываются гистограммы
value=scr[i]; R[value]=R[value]+1;//s[i]=255-value;
value=scr[i+1]; G[value]=G[value]+1;//s[i+1]=255-value;
value=scr[i+2]; B[value]=B[value]+1;//s[i+2]=255-value;
}
int sumR=0;for (i=0;i<255;i++) sumR=sumR+R[i];
int maxR=sumR>>6;
int sumG=0;for (i=0;i<255;i++) sumG=sumG+G[i];
int maxG=sumG>>6;
int sumB=0;for (i=0;i<255;i++) sumB=sumB+B[i];
int maxB=sumB>>6;
int maxM=(maxR+maxG+maxB)/3;
maxR=maxM;
maxG=maxM;
maxB=maxM;
// Вывод гистограммы для R
Line(9,151,9,10,0xFFFFFF);Line(9,151,522,151,0xFFFFFF);
for (i=0;i<256;i++) {
val=(R[i]*140)/maxR;if (val>140) val=140;
Line(10+(i<<1),150,10+(i<<1),150-val,i<<16);
Line(11+(i<<1),150,11+(i<<1),150-val+1,0xC0C0C0);
}
Line(9,301,9,160,0xFFFFFF);Line(9,301,522,301,0xFFFFFF);
for (i=0;i<256;i++) {
val=(G[i]*140)/maxG;if (val>140) val=140;
Line(10+(i<<1),300,10+(i<<1),300-val,i<<8);
Line(11+(i<<1),300,11+(i<<1),300-val+1,0xC0C0C0);
}
Line(9,451,9,310,0xFFFFFF);Line(9,451,522,451,0xFFFFFF);
for (i=0;i<256;i++) {
val=(B[i]*140)/maxB;if (val>140) val=140;
Line(10+(i<<1),450,10+(i<<1),450-val,i);
Line(11+(i<<1),450,11+(i<<1),450-val+1,0xC0C0C0);
}
}
void main(void) {
cm=SetVESAMode(640,480,32); // Включение видеорежима.
cm.VLFB=(uc *)VScreen; // Установка виртуального экрана.
InitGraph();
BMP_PutFromFile(cm,0,0,"29sc091.bmp");
DrawHistogram((char *)cm.VLFB, cm.w*cm.h*cm.bytespp, (char *)cm.LFB);
blt(cm);
getch();
SetVGAMode(3);
}
Download this code: glib2000.cpp
vesa.cpp
#include "vesa.h";
#include <conio.h>;
VBE_InfoBlock far * vinfo;
VBE_ModeInfoBlock far * minfo;
int *scr;
int dwpl;
// Возвращает версию VESA ####################################################
int GetVBEVersion(void) {
union REGS r;
us seg,sel;
static VBE_RMI RMI;
struct SREGS sr;
ul ver;
memset (&cm, 0, sizeof(cm));
r.x.eax=0x0100;
r.x.ebx=600>>4;
int386(0x31,&r,&r);
if (r.x.cflag) {
puts (" ¦ DOS memory alocate error.");
return(0);
}
seg = r.w.ax; // RM segment
sel = r.w.dx; // PM selector
vinfo = (VBE_InfoBlock far *) MK_FP (sel, 0);
memset (&sr, 0, sizeof (sr));
memset (&RMI, 0, sizeof (RMI));
RMI.EAX = 0x00004f00; // Get VBE Info
RMI.ES = seg;
RMI.EDI = 0;
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
if (r.x.cflag) {
puts (" ¦ DPMI 300h failed.");
return(0);
}
if ((us) RMI.EAX != 0x004f) {
puts (" ¦ Error Getting Video Controller Info.");
return(0);
}
return(vinfo -> VbeVersion);
}
void PutVBEVersion(void) {
int ver=GetVBEVersion();
printf("%d.%d",(ver & 0xFF00)>>8 ,ver & 0x00FF);
}
// Включение VESA видео-режима ###########################
// 0 - Ok
// 1 - DPMI 300h failed
// 2 - Error Getting Video Controller Info
// 3 - DOS memory alocate error
// 4 - Video mode no found
// 5 - Error mapping physical to linear
// 6 - Error Init Mode
TMode SetVESAMode(int xmax, int ymax, int bpp) {
static VBE_RMI RMI;
union REGS r;
struct SREGS sr;
us sel,seg,x,y,ic;
ul phys;
int mmode;
TMode cm;
r.x.eax=0x0100;
r.x.ebx=600>>4;
int386(0x31,&r,&r);
if (r.x.cflag) {
cm.error=1;
return(cm);
}
seg = r.w.ax; // RM segment
sel = r.w.dx; // PM selector
vinfo = (VBE_InfoBlock far *) MK_FP (sel, 0);
memset (&sr, 0, sizeof (sr));
memset (&RMI, 0, sizeof (RMI));
RMI.EAX = 0x00004f00; // Get VBE Info
RMI.ES = seg;
RMI.EDI = 0;
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
if (r.x.cflag) {
cm.error=1;
return(cm);
}
if ((us) RMI.EAX != 0x004f) {
cm.error=2;
return(cm);
}
r.x.eax = 0x0100;
r.x.ebx = 300 >> 4;
int386 (0x31, &r, &r);
if (r.x.cflag) {
cm.error=3;
return(cm);
}
seg = r.w.ax; // RM segment
sel = r.w.dx; // PM selector
minfo = (VBE_ModeInfoBlock far *) MK_FP (sel, 0);
memset (&sr, 0, sizeof (sr));
memset (&RMI, 0, sizeof (RMI));
mmode=0;
if (vinfo -> VbeVersion >= 0x0200) {
for (ic=0x4100;ic<0x4200;ic++) {
RMI.EAX = 0x00004f01; // Get VBE Mode Info
RMI.ECX = ic;
RMI.ES = seg;
RMI.EDI = 0;
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
if (r.x.cflag) {
cm.error=1;
return(cm);
}
if ((minfo->BitsPerPixel==bpp) &&
(minfo->XResolution==xmax) &&
(minfo->YResolution==ymax) &&
((us)RMI.EAX==0x004f)) {
if (mmode==0) {mmode=ic;}
}
}
if (mmode!=0) cm.banked=1; else cm.banked=0;
}
if (mmode==0) {
for (ic=0x0100;ic<0x0200;ic++) {
RMI.EAX = 0x00004f01; // Get VBE Mode Info
RMI.ECX = ic;
RMI.ES = seg;
RMI.EDI = 0;
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
if (r.x.cflag) {
cm.error=1;
return(cm);
}
if ((minfo->BitsPerPixel==bpp)&&
(minfo->XResolution==xmax)&&
(minfo->YResolution==ymax)&&
((us)RMI.EAX==0x004f)) {
if (mmode==0) {mmode=ic;}
}
}
}
if (mmode==0) {
cm.error=4;
return(cm);
}
RMI.EAX = 0x00004f01; // Get VBE Mode Info
RMI.ECX = mmode;
RMI.ES = seg;
RMI.EDI = 0;
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
phys=minfo->PhysBasePtr;
cm.bpp=minfo->BitsPerPixel;
cm.bytespp=cm.bpp/8;
cm.h=minfo->YResolution;
cm.w=minfo->XResolution;
cm.bpl=minfo->BytesPerScanLine;
r.w.ax = 0x0800;
r.w.bx = (phys >> 0x10);
r.w.cx = (phys & 0xffff);
r.w.si = (long)((minfo->YResolution * minfo->BytesPerScanLine)>>16);
r.w.di = minfo->YResolution * minfo->BytesPerScanLine;
int386 (0x31, &r, &r);
if (r.x.cflag) {
cm.error=5;
return(cm);
}
cm.LFB = (uc near *) (((ul) r.w.bx << 16) | r.w.cx);
r.w.ax = 0x4f02;
r.w.bx = mmode;
int386 (0x10, &r, &r);
if (r.w.ax != 0x004f) {
cm.error=6;
return(cm);
}
cm.error=0;
return(cm);
}
// Установка VGA режима ######################################################
void SetVGAMode(int mode) {
static VBE_RMI RMI;
union REGS r;
struct SREGS sr;
memset (&sr, 0, sizeof (sr));
memset (&RMI, 0, sizeof (RMI));
memset (&r, 0, sizeof (r));
RMI.EAX = mode; // Get VBE Mode Info
r.w.ax = 0x0300;
r.h.bl = 0x10;
r.h.bh = 0;
r.w.cx = 0;
sr.es = FP_SEG (&RMI);
r.x.edi = FP_OFF (&RMI);
int386x (0x31, &r, &r, &sr);
}
void blt(TMode m) {
if (m.bpp==32 && m.banked==1) { // Если включен 32 битный LFB режим
memcpy((void *)m.LFB, (void *)m.VLFB, m.w * m.h * m.bytespp);
}
}
void InitGraph(void) {
dwpl=cm.bpl>>2;
scr=(int *)cm.VLFB;
}
void PutPixel(int x,int y,int color) {
scr[y*dwpl+x]=color;
}
int sgn(int i) {
if (i==0) return(0);
if (i>0) return(1);
return(-1);
}
void Line(int x1,int y1,int x2,int y2,int color) {
int dx,dy,r,dr1,dr2,x,y,sx,sy;
dx=abs(x2-x1);
dy=abs(y2-y1);
sx=sgn(x2-x1);
sy=sgn(y2-y1);
if (dx>dy) {
r=dx;
dr1=-dy<<1;
dr2=(dx-dy)<<1;
y=y1;
x=x1;
x2=x2+sx;
do {
PutPixel(x,y,color);
if (r>0) r=r+dr1;
else {
r=r+dr2;
y=y+sy;
PutPixel(x,y,color);
}
x=x+sx;
} while (x!=x2);
}
else {
r=dy;
dr1=-dx<<1;
dr2=(dy-dx)<<1;
y=y1;
x=x1;
y2=y2+sy;
do {
PutPixel(x,y,color);
if (r>0) r=r+dr1;
else {
r=r+dr2;
x=x+sx;
PutPixel(x,y,color);
}
y=y+sy;
} while (y!=y2);
}
}
Download this code: vesa.cpp
bmp.h
struct BMPHeader
{
unsigned short ID; // 'BM' for a Windows BitMaP
unsigned int FSize; // Size of file
unsigned short Res1; // Not used
unsigned short Res2; // Not used
unsigned int Image; // Offset of image into file
unsigned int stSize; // число байт, занимаемых структурой BITMAPINFOHEADER
unsigned int Width; // Width of image
unsigned int Height; // Height of image
unsigned short Num; // число битовых плоскостей устройства
unsigned short Bits; // Number of bits per pixel
unsigned int Comp; // Type of compression, 0 for uncompressed, 1,2 for RLE
unsigned int ISize; // Size of image in bytes
unsigned int XRes; // X dots per metre (not inches! for US, unbelievable!)
unsigned int YRes; // Y dots per metre
unsigned int PSize; // Palette size (number of colours) if not zero
unsigned int PImportant; // Probably reserved, currently 0
};
typedef struct SPallete { // Тип описывающий элемент палитры.
char b,g,r,x;
} TPallete;
void BMP_PutFromFile(TMode cm, int x, int y, char * fname);
Download this code: bmp.h
vesa.h
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
typedef unsigned short us;
typedef unsigned int ui;
typedef unsigned char uc;
typedef unsigned long ul;
typedef struct SVBE_InfoBlock {
uc VbeSignature[4]; // VBE Signature
us VbeVersion; // VBE Version
ul OemStringPtr; // Pointer to OEM String
ul Capabilities; // Capabilities of graphics cont.
ul VideoModePtr; // Pointer to Video Mode List
// Added for VBE 1.2+
us TotalMemory; // Number of 64kb memory blocks
// Added for VBE 2.0+
us OemSoftwareRev; // VBE implementation Software revision
ul OemVendorNamePtr; // Pointer to Vendor Name String
ul OemProductNamePtr; // Pointer to Product Name String
ul OemProductRevPtr; // Pointer to Product Revision String
us AccelVbeVersion; // VBE/AF Version
ul AccelVideoModePtr; // Pointer to Acclelerated Mode List
uc Reserved20[216]; // Reserved for VBE implementation
// scratch area
uc OemData[256]; // Data Area for OEM Strings
uc NaVsyakiySloochay[100]; // 2e
} VBE_InfoBlock;
typedef struct SVBE_ModeInfoBlock {
us ModeAttributes; // mode attributes
uc WinAAttributes; // window A attributes
uc WinBAttributes; // window B attributes
us WinGranularity; // window granularity
us WinSize; // window size
us WinASegment; // window A start segment
us WinBSegment; // window B start segment
ul WinFuncPtr; // pointer to window function
us BytesPerScanLine; // bytes per scan line
// Mandatory information for VBE 1.2 and above
us XResolution; // horizontal resolution in pixels or chars
us YResolution; // vertical resolution in pixels or chars
uc XCharSize; // character cell width in pixels
uc YCharSize; // character cell height in pixels
uc NumberOfPlanes; // number of memory planes
uc BitsPerPixel; // bits per pixel
uc NumberOfBanks; // number of banks
uc MemoryModel; // memory model type
uc BankSize; // bank size in KB
uc NumberOfImagePages; // number of images
uc Reserved12; // reserved for page function
// Direct Color fields (required
// for direct/6 and YUV/7 memory models)
uc RedMaskSize; // size of direct color red mask in bits
uc RedFieldPosition; // bit position of lsb of red mask
uc GreenMaskSize; // size of direct color green mask in bits
uc GreenFieldPosition; // bit position of lsb of green mask
uc BlueMaskSize; // size of direct color blue mask in bits
uc BlueFieldPosition; // bit position of lsb of blue mask
uc RsvdMaskSize; // size of direct color reserved mask in bits
uc RsvdFieldPosition; // bit position of lsb of reserved mask
uc DirectColorModeInfo; // direct color mode attributes
// Mandatory information for VBE 2.0 and above
ul PhysBasePtr; // physical address for flat frame buffer
ul OffScreenMemOffset; // pointer to start of off screen memory
us OffScreenMemSize; // amount of off screen memory in 1k units
uc Reserved20[206]; // remainder of ModeInfoBlock
uc NaVsyakiySloochay[100]; // 2e
} VBE_ModeInfoBlock;
typedef struct SVBE_RMI { // Rreal Mode Interrupt (RMI) structure
ul EDI, ESI, EBP, ReservedByRMI,
EBX, EDX, ECX, EAX;
us flags,
ES, DS,
FS, GS,
IP, CS,
SP, SS;
} VBE_RMI;
typedef struct SMode { // Тип описывающий видео-режим.
ul bpl; // Байт на строку растра.
ul w; // Ширина
ul h; // Высота
ul bpp; // Бит на точку
ul bytespp; // Байт на точку
ul banked; // (1)LFB or (0)Banked
ul error; // Ошибка (обязательно надо проверять)
uc* LFB; // Указатель на LFB
uc* VLFB; // Указатель на Виртуальный экран
} TMode;
TMode cm; // Текущий видеорежим.
int GetVBEVersion(void);
void PutVBEVersion(void);
TMode SetVESAMode(int xmax, int ymax, int bpp);
void SetVGAMode(int mode);
void InitGraph(void);
void blt(TMode m);
void PutPixel(int x,int y,int color);
void Line(int x1,int y1,int x2,int y2,int color);
Download this code: vesa.h
Комментарии