{by Franz Glaser, Austria meg-glaser@eunet.at}
{Save textscreen 80x25 VGA to .BMP files, using VGA character set and 16 colors. The unit is intended to be included in a main program, for creation of manuals or advertisements or homepages (convert .BMP to .GIF with a paint program). The unit is an extension of the graphic mode Graph2BMP16 with charset painting.
The unit writes to sequential files
numbered
SCRV0000.BMP
SCRV0001.BMP etc. in the current (at
start) directory.
The unit is usually included with some {$IFDEF SCRSHOT} in the application program's Uses line.
Drawback: exception chars (wide 9-pixel chars) are not used, on most VGA cards the 'M' and several other chars are painted extra wide on the CRT, but not here in the .BMP file. This is due to the fact that the .BMP is not painted with 720 pixels, but with 640, where each char can be only 8 pixels wide. The 720 pixel mode would lead to an unwanted w/h aspect ratio of the whole .BMP image.
The unit is limited to 80x25 VGA TEXTmode screens. It is much work to enhance it to 80x43 or similar textmodes}
Interface
Uses CRT,DOS;
{this is the only interface procedure. It
must be called on some function key or HandleEvent}
Procedure Screen2BMP;
Implementation
Type TCSet = Array[0..15] of Byte; {1 char}
TCharSet = Array[#0..#255] of TCSet;
PCharSet = ^TCharSet;
Str7 = String[7];
TChr = Record
Bu : Char; {German: Buchstabe, Farbe
= Character, Color}
Fa : Byte;
End;
TCline = Array[0..79] of TChr;
TScreen = Array[0..24] of TCline;
PScreen = ^TScreen;
Const BMPnum:Integer=-1;
CXlat : Array[0..16] of Byte =
(0, 4, 2, 6, 1, 5, 3, 8,
7,$0C,$0A,$0E,$09,$0D,$0B,$0F,8); {do
not modify this, modify BMPXLAT instead}
VidMode : Byte = 0;
ScrPage : Byte = 0;
LinesPC : Byte = 16;
Const BMPhead:Array[0..117] of Byte=
($42,$4D,$60,$04,$02,$00,0,0,0,0,$76,0,0,0,$28,0,
0,0,$88,$02,0,0,$98,$01,0,0,$01,0,$04,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,$10,0,
0,0,0,0,0,0,
0,0,0,0, {36H=54z = address
of RGB palette}
0,0,$B0,0,
0,$A0,0,0,
0,$40,$90,0,
$A0,0,0,0,
$80,0,$80,0,
$80,$80,0,0,
$60,$60,$60,0,
$C0,$C0,$C0,0,
0,0,$FF,0,
0,$FF,0,0,
0,$FF,$FF,0,
$FF,0,0,0,
$FF,0,$FF,0,
$FF,$FF,0,0,
$FF,$FF,$FF,0); {optimized? Palette,
do not modify here, see at BMPXLAT}
Var Screen : PScreen ;
ChrSet : PCharSet;
BMPpath : PathStr; {must
be global}
Procedure MakeLFN(W : Word); {German: Laufende
Nummer=counter}
Var I : Integer;
S7 : String[7];
Begin
Str(W:4,S7);
for I := 1 to 3 do
if S7[I]=' ' then S7[I] := '0';
I := length(BMPpath)-7;
Move(S7[1],BMPpath[I],4); {a little
dirty?}
End;
Procedure Text2BMP16; {the writing procedure}
Var F : File;
B : Array[-2..321] of Byte;
written,XC,YC,X,Y,XX : Integer;
Bord,CL,FF,FB,GF,GB,Px : Byte;
CW : TChr;
Begin
MakeLFN(BMPnum);
Inc(BMPnum); {for the next following
screenshot}
Assign(F,BMPpath);
{$I-} Rewrite(F,1); {$I+}
if IOresult=0 then
Begin
Write(^G); {start beep, screenshot
will take several seconds}
BlockWrite(F,BMPhead,sizeof(BMPhead),written);
Bord := CXlat[16]+CXlat[16] shl 4; {2
pixels / byte}
Fillchar(B,sizeof(B),Bord);
For Y := 3 downto 0 do
BlockWrite(F,B,sizeof(B),written); {bottom
border}
For YC := 24 downto 0 do
Begin
for Y := 15 downto 0 do {bmp
reads from bottom to top}
Begin
XX := 0;
{pixel-byte pointer}
for XC :=
0 to 79 do
Begin
CW := Screen^[YC][XC]; {german: Farbe=color,
Buchstabe=character}
CL := ChrSet^[CW.Bu][Y];
{char_line}
FF := CXlat[CW.Fa and $0F]; {foregnd
color}
FB := CXlat[(CW.Fa shr 4) and 7]; {backgnd color,
mask off blinking bit. Paper doesn't blink}
GF := FF shl 4; GB := FB shl 4; {prep
for speed}
for X := 0 to 3 do {1 char = 4 pixel-pairs}
Begin
if (CL and $40)=$40 then
Px := FF
else
Px := FB;
if (CL and $80)=$80 then {hi nibble}
Px := Px OR GF
else
Px := Px OR GB;
B[XX] := Px; {write 2 pixels (16color)
to BMP line}
Inc(XX); {up
til 319}
CL := CL shl 2; {shift pixels of char_line}
End;
End;
BlockWrite(F,B,sizeof(B),written);
{1 video line}
End;
End;
Fillchar(B,sizeof(B),Bord);
For Y := 3 downto 0 do
BlockWrite(F,B,sizeof(B),written); {top
border}
Close(F);
Write(^G); {beep}
End; {if IOresult=0}
End;
{invoked from the main program on function-key}
Procedure Screen2BMP;
Begin
ASM
PUSH
BP
PUSH
DS
MOV
AX,0F00H {get video mode}
INT
10H
POP
DS
MOV
VidMode,AL {3 = CO80}
AND
BH,3
MOV
ScrPage,BH {usually 0}
PUSH
DS
MOV
AX,1130H {get charset}
MOV
BX,0600H { Int 43h = Cur
CharSet, probably you prefer this? }
INT
10H
POP
DS
MOV
WORD PTR ChrSet, BP
MOV
WORD PTR ChrSet+2,ES
MOV
LinesPC,CL {video lines per character:
CGA=8, EGA=14, VGA/400=16}
POP
BP
End;
if (VidMode = 3) and (LinesPC=16) then {check
for 80x25 VGA}
Begin
Screen := Ptr($B800,ScrPage*4096);
Text2BMP16;
End;
End;
{setup:
* next SCRV####.BMP
* read BMP-palette file}
Procedure InitText2BMP;
Var X,Y,I : Integer;
FN,U : PathStr;
SR : SearchRec;
T : Text;
Begin
BMPnum := 0;
BMPpath := FExpand('SCRV????.BMP'); {you
can modify the first 4 chars, but must be 4 chars}
FindFirst(BMPpath,Archive or ReadOnly,SR);
while DOSerror=0 do
Begin
FN := Copy(SR.Name,5,4);
Val(FN,X,Y);
if Y=0 then
if X >= BMPnum then
BMPnum :=
X+1; {detect highest #### of existing .BMP file}
FindNext(SR);
End;
{now read the color mapping definition file}
Assign(T,'BMPXLAT.INI'); {first try
actual CD workdir}
{$I-} Reset(T); {$I+}
I := IOresult;
if I=2 then {if not found then try
load dir of .EXE}
Begin
FN := ParamStr(0);
while (FN > '') and (FN[length(FN)]
<> '\') do
Dec(FN[0]); {clip
'applic.exe'}
FN := FN+'BMPXLAT.INI';
Assign(T,FN);
{$I-} Reset(T); {$I+}
I := IOresult;
End;
if I=0 then
Begin
I := 0;
Repeat
ReadLn(T,FN);
if FN > '' then
Begin
Val(FN,X,Y);
if Y=0 then
CXlat[I] := X;
End;
Inc(I);
Until (I > 16) or EOF(T);
I := 0;
Readln(T,FN);
if FN='PALETTE RGB' then
Repeat
ReadLn(T,FN);
X := 0;
Repeat
{read 3 numbers RGB per line}
U := '';
while (FN > '') and not (FN[1] in ['0'..'9']) do Delete(FN,1,1);
Repeat
U := U+FN[1];
Delete(FN,1,1);
Until (FN='') or not (FN[1] in ['0'..'9']);
if U > '' then
Val(U,BMPhead[54+4*I+X],Y);
Inc(X);
Until FN='';
Inc(I);
Until EOF(T) or (I >
15);
Close(T);
End
else
Begin
Assign(T,'BMPXLAT.INI');
Rewrite(T); {if
no xlat found, create new with default values}
for I := 0 to 16 do
Writeln(T,CXlat[I]);
{from default translation table}
Writeln(T,'PALETTE RGB');
for I := 0 to 15 do
Begin
for X :=
0 to 2 do
Begin
Write(T,BMPhead[54+4*I+X]); {from default palette}
if X < 2 then Write(T,',') else Write(T,^M^J);
End;
End;
Writeln(T,'end of RGB palette.');
Writeln(T,'>>>--SCR2BMP--> (c) 1997
MEG-Glaser A-4191'); {add explanation to ini-file}
Writeln(T,' fixed format:');
Writeln(T,'lines 1...16: SCR {=CGA/EGA/VGA16}
-> RGB translation list.');
Writeln(T,'line 17: border color
(RGB#).');
Writeln(T,'line 18: "PALETTE RGB"
key string.');
Writeln(T,'lines 19..34: RGB Palette
(### 0..15).');
Writeln(T,'If SCR2BMP does not find
this BMPXLAT.INI file in the CD directory or in the');
Writeln(T,'load directory, it will
create the file with default values.');
Close(T);
End;
End;
Begin
InitText2BMP;
End.
Das AAG - Programm ist ein Chef-Programm für Handwerker und Kaufleute, zum "Anbahnen und Abwickeln von Geschäften".
http://members.eunet.at/meg-glaser