fpc/rtl/inc/graph/text.inc
Jonas Maebe d045295cd4 + added Log and Id tags
* added first FPC support, only VGA works to some extend for now
  * use -dasmgraph to use assembler routines, otherwise Pascal
    equivalents are used
  * use -dsupportVESA to support VESA (crashes under FPC for now)
  * only dispose vesainfo at closegrph if a vesa card was detected
  * changed int32 to longint (int32 is not declared under FPC)
  * changed the declaration of almost every procedure in graph.inc to
    "far;" becquse otherwise you can't assign them to procvars under TP
    real mode (but unexplainable "data segnment too large" errors prevent
    it from working under real mode anyway)
1999-07-12 13:27:06 +00:00

704 lines
25 KiB
PHP

{
$Id$
This file is part of the Free Pascal run time library.
Copyright (c) 1993,98 by the Free Pascal development team
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{***************************************************************************}
{ Text output routines }
{***************************************************************************}
const
maxfonts = 16; { maximum possible fonts }
MaxChars = 255; { Maximum nr. of characters in a file }
Prefix_Size = $80; { prefix size to skip }
SIGNATURE = '+'; { Signature of CHR file }
type
pbyte = ^byte;
pword = ^word;
{ Prefix header of Font file }
PFHeader = ^TFHeader;
TFHeader = packed record
header_size: word; {* Version 2.0 Header Format *}
font_name: array[1..4] of char;
font_size: word; {* Size in byte of file *}
font_major: byte; {* Driver Version Information *}
font_minor: byte;
min_major: byte; {* BGI Revision Information *}
min_minor: byte;
end;
{ Font record information }
PHeader = ^THeader;
THeader = packed record
Signature: char; { signature byte }
Nr_chars: integer; { number of characters in file }
Reserved: byte;
First_char: byte; { first character in file }
cdefs : integer; { offset to character definitions }
scan_flag: byte; { TRUE if char is scanable }
org_to_cap: byte; { Height from origin to top of capitol }
org_to_base:byte; { Height from origin to baseline }
org_to_dec: byte; { Height from origin to bot of decender }
_reserved: array[1..4] of char;
Unused: byte;
end;
TOffsetTable =array[0..MaxChars] of Integer;
TWidthTable =array[0..MaxChars] of byte;
tfontrec = packed record
name : string[8];
header : THeader; { font header }
pheader : TFHeader; { prefix header }
offsets : TOffsetTable;
widths : TWidthTable;
instr : pchar;
end;
pStroke = ^TStroke;
TStroke = packed record
opcode: byte;
x: integer; { relative x offset character }
y: integer; { relative y offset character }
end;
TStrokes = Array[0..1000] of TStroke;
opcodes = (_END_OF_CHAR, _DO_SCAN, _MOVE, _DRAW);
var
fonts : array[1..maxfonts] of tfontrec;
Strokes: TStrokes; {* Stroke Data Base *}
Stroke_count: Array[0..MaxChars] of integer; {* Stroke Count Table *}
{***************************************************************************}
{ Internal support routines }
{***************************************************************************}
function testfont(p : pchar) : boolean;
begin
testfont:=(p[0]='P') and
(p[1]='K') and
(p[2]=#8) and
(p[3]=#8);
end;
function InstallUserFont(const FontFileName : string) : integer;
begin
_graphresult:=grOk;
{ first check if we do not allocate too many fonts! }
if installedfonts=maxfonts then
begin
_graphresult:=grError;
InstallUserFont := DefaultFont;
exit;
end;
inc(installedfonts);
fonts[installedfonts].name:=FontFileName;
fonts[installedfonts].instr := nil;
InstallUserFont:=installedfonts;
end;
function Decode(byte1,byte2: char; var x,y: integer): integer;
{ This routines decoes a signle word in a font opcode section }
{ to a stroke record. }
var
b1,b2: shortint;
Begin
b1:=shortint(byte1);
b2:=shortint(byte2);
{ Decode the CHR OPCODE }
Decode:=integer(((b1 and $80) shr 6)+((b2 and $80) shr 7));
{ Now get the X,Y coordinates }
{ bit 0..7 only which are considered }
{ signed values. }
{$R-}
b1:=b1 and $7f;
b2:=b2 and $7f;
{ Now if the MSB of these values are set }
{ then the value is signed, therefore we }
{ sign extend it... }
if (b1 and $40)<>0 then b1:=b1 or $80;
if (b2 and $40)<>0 then b2:=b2 or $80;
x:=integer(b1);
y:=integer(b2);
{$ifdef debug}
{$R+}
{$endif debug}
end;
function unpack(buf: pchar; index: integer; var Stroke: TStrokes): integer;
var
pb: pword;
po: TStrokes;
num_ops: integer;
opcode, i, opc: word;
counter: integer;
lindex: integer;
jx, jy: integer;
begin
num_ops := 0;
counter := index;
lindex :=0;
while TRUE do {* For each byte in buffer *}
Begin
Inc(num_ops); {* Count the operation *}
opcode := decode( buf[counter], buf[counter+1] ,jx, jy );
Inc(counter,2);
if( opcode = ord(_END_OF_CHAR) ) then break; {* Exit loop at end of char *}
end;
counter:=index;
for i:=0 to num_ops-1 do { /* For each opcode in buffer */ }
Begin
opc := decode(buf[counter], buf[counter+1], po[lindex].x, po[lindex].y); {* Decode the data field *}
inc(counter,2);
po[lindex].opcode := opc; {* Save the opcode *}
Inc(lindex);
end;
Stroke:=po;
unpack := num_ops; {* return OPS count *}
end;
procedure GetTextPosition(var xpos,ypos: longint; const TextString: string);
begin
if CurrentTextInfo.Font = DefaultFont then
begin
if Currenttextinfo.direction=horizdir then
begin
case Currenttextinfo.horiz of
centertext : XPos:=(textwidth(textstring) shr 1);
lefttext : XPos:=0;
righttext : XPos:=textwidth(textstring);
end;
case Currenttextinfo.vert of
centertext : YPos:=-(textheight(textstring) shr 1);
bottomtext : YPos:=-textheight(textstring);
toptext : YPos:=0;
end;
end else
begin
case Currenttextinfo.horiz of
centertext : XPos:=(textheight(textstring) shr 1);
lefttext : XPos:=textheight(textstring);
righttext : XPos:=textheight(textstring);
end;
case Currenttextinfo.vert of
centertext : YPos:=(textwidth(textstring) shr 1);
bottomtext : YPos:=0;
toptext : YPos:=textwidth(textstring);
end;
end;
end
else
begin
if Currenttextinfo.direction=horizdir then
begin
case CurrentTextInfo.horiz of
centertext : XPos:=(textwidth(textstring) shr 1);
lefttext : XPos:=0;
righttext : XPos:=textwidth(textstring);
end;
case CurrentTextInfo.vert of
centertext : YPos:=(textheight(textstring) shr 1);
bottomtext : YPos:=0;
toptext : YPos:=textheight(textstring);
end;
end else
begin
case CurrentTextInfo.horiz of
centertext : XPos:=(textheight(textstring) shr 1);
lefttext : XPos:=0;
righttext : XPos:=textheight(textstring);
end;
case CurrentTextInfo.vert of
centertext : YPos:=(textwidth(textstring) shr 1);
bottomtext : YPos:=0;
toptext : YPos:=textwidth(textstring);
end;
end;
end;
end;
{***************************************************************************}
{ Exported routines }
{***************************************************************************}
function RegisterBGIfont(font : pointer) : integer;
var
hp : pchar;
b : word;
i,j: longint;
Header: THeader;
counter: longint;
FontData: pchar;
FHeader: TFHeader;
begin
RegisterBGIfont:=grInvalidFontNum;
i:=0;
{ Check if the font header is valid first of all }
if testfont(font) then
begin
hp:=pchar(font);
{ Move to EOF in prefix header }
while (hp[i] <> chr($1a)) do Inc(i);
move(hp[i+1],FHeader,sizeof(TFHeader));
move(hp[Prefix_Size],header,sizeof(THeader));
{ check if the font name is already allocated? }
i:=Prefix_Size+sizeof(THeader);
for b:=1 to installedfonts do
begin
if fonts[b].name=FHeader.Font_name then
begin
move(FHeader,fonts[b].PHeader,sizeof(TFHeader));
move(Header,fonts[b].Header,sizeof(THeader));
move(hp[i],Fonts[b].Offsets[Fonts[b].Header.First_Char],Fonts[b].Header.Nr_chars*sizeof(integer));
Inc(i,Fonts[b].Header.Nr_chars*sizeof(integer));
move(hp[i],Fonts[b].Widths[Fonts[b].Header.First_Char],Fonts[b].Header.Nr_chars*sizeof(byte));
Inc(i,Fonts[b].Header.Nr_chars*sizeof(byte));
counter:=Fonts[b].PHeader.font_size+PREFIX_SIZE-i;
{ allocate also space for null }
GetMem(FontData,Counter+1);
move(hp[i],FontData^,Counter);
{ Null terminate the string }
FontData[counter+1] := #0;
if fonts[b].header.Signature<> SIGNATURE then
begin
_graphResult:=grInvalidFont;
Freemem(FontData, Counter+1);
exit;
end;
fonts[b].instr:=FontData;
RegisterBGIfont:=b;
end;
end;
end
else
RegisterBGIFont:=grInvalidFont;
end;
procedure GetTextSettings(var TextInfo : TextSettingsType);
begin
textinfo:=currenttextinfo;
end;
function TextHeight(const TextString : string) : word;
begin
if Currenttextinfo.font=DefaultFont
then TextHeight:=8*CurrentTextInfo.CharSize
else
TextHeight:=fonts[Currenttextinfo.font].header.org_to_cap-
round(fonts[Currenttextinfo.font].header.org_to_base * CurrentYRatio) ;
end;
function TextWidth(const TextString : string) : word;
var i,x : Integer;
c : byte;
begin
x := 0;
{ if this is the default font ... }
if Currenttextinfo.font = Defaultfont then
TextWidth:=length(TextString)*8*CurrentTextInfo.CharSize
{ This is a stroked font ... }
else begin
for i:=1 to length(TextString) do
begin
c:=byte(textstring[i]);
dec(c,fonts[Currenttextinfo.font].header.first_char);
if (c<0) or (c>=fonts[Currenttextinfo.font].header.nr_chars) then
continue;
x:=x+byte(fonts[Currenttextinfo.font].widths[c]);
end;
TextWidth:=round(x * CurrentXRatio) ;
end;
end;
procedure OutTextXY(x,y : integer;const TextString : string);
type
Tpoint = record
X,Y: Integer;
end;
var
ch: char;
b1,b2 : shortint;
b3 : byte;
c : byte;
i,j,k : longint;
oldvalues : linesettingstype;
nextpos : word;
xpos,ypos,offs: longint;
counter : longint;
FontBitmap : TBitmapChar;
chr: char;
cnt1,cnt2 : integer;
cnt3,cnt4 : integer;
charsize : word;
TextBuffer : array[1..sizeof(string)*2] of Tpoint;
WriteMode : word;
CurX, CurY : integer;
begin
{ save current write mode }
WriteMode := CurrentWriteMode;
CurrentWriteMode := NormalPut;
GetTextPosition(xpos,ypos,textstring);
X:=X-XPos; Y:=Y+YPos;
XPos:=X; YPos:=Y;
CharSize := CurrentTextInfo.Charsize;
if Currenttextinfo.font=DefaultFont then
begin
c:=length(textstring);
{ We must a length strength which is ZERO based }
Dec(c);
if CurrentTextInfo.direction=HorizDir then
{ Horizontal direction }
begin
for i:=0 to c do
begin
chr := TextString[i+1];
xpos:=x+(i shl 3)*Charsize;
{ we copy the character bitmap before accessing it }
{ this improves speed on non optimizing compilers }
{ since it is one less address calculation. }
Fontbitmap:=TBitmapChar(DefaultFontData[chr]);
{ no scaling }
if CharSize = 1 then
Begin
for j:=0 to 7 do
for k:=0 to 7 do
if Fontbitmap[j,k] <> 0 then PutPixel(xpos+k,j+y,CurrentColor);
end
else
{ perform scaling of bitmap font }
Begin
j:=0;
cnt3:=0;
while j < 7 do
begin
{ X-axis scaling }
for cnt4 := 0 to charsize-1 do
begin
k:=0;
cnt2 := 0;
while k < 7 do
begin
for cnt1 := 0 to charsize-1 do
begin
If FontBitmap[j,k] <> 0 then
PutPixel(xpos+cnt1+cnt2,y+cnt3+cnt4,CurrentColor);
end;
Inc(k);
Inc(cnt2,charsize);
end;
end;
Inc(j);
Inc(cnt3,charsize);
end;
end;
end;
end
else
{ Vertical direction }
begin
for i:=0 to c do
begin
chr := TextString[i+1];
Fontbitmap:=TBitmapChar(DefaultFontData[chr]);
ypos := y-(i shl 3)*CharSize;
{ no scaling }
if CharSize = 1 then
Begin
for j:=0 to 7 do
for k:=0 to 7 do
if Fontbitmap[j,k] <> 0 then PutPixel(xpos+j,ypos-k,
CurrentColor);
end
else
{ perform scaling of bitmap font }
Begin
j:=0;
cnt3:=0;
while j < 7 do
begin
{ X-axis scaling }
for cnt4 := 0 to charsize-1 do
begin
k:=0;
cnt2 := 0;
while k < 7 do
begin
for cnt1 := 0 to charsize-1 do
begin
If FontBitmap[j,k] <> 0 then
PutPixel(xpos+cnt3-cnt4,ypos+cnt1-cnt2,
CurrentColor);
end;
Inc(k);
Inc(cnt2,charsize);
end;
end;
Inc(j);
Inc(cnt3,charsize);
end;
end;
end;
end;
end else
{ This is a stroked font which is already loaded into memory }
begin
getlinesettings(oldvalues);
{ reset line style to defaults }
setlinestyle(solidln,oldvalues.pattern,normwidth);
if Currenttextinfo.direction=vertdir then
xpos:=xpos + Textheight(textstring);
CurX:=xpos; CurY:=ypos; x:=xpos; y:=ypos;
for i:=1 to length(textstring) do
begin
c:=byte(textstring[i]);
Stroke_Count[c] := unpack( fonts[CurrentTextInfo.font].instr,
fonts[CurrentTextInfo.font].Offsets[c], Strokes );
counter:=0;
while true do
begin
if CurrentTextInfo.direction=VertDir then
begin
xpos:=x-round(Strokes[counter].Y*CurrentXRatio);
ypos:=y-round(Strokes[counter].X*CurrentYRatio);
end
else
begin
xpos:=x+round(Strokes[counter].X*CurrentXRatio) ;
ypos:=y-round(Strokes[counter].Y*CurrentYRatio) ;
end;
case opcodes(Strokes[counter].opcode) of
_END_OF_CHAR: break;
_DO_SCAN: begin
{ Currently unsupported };
end;
_MOVE : Begin
CurX := XPos;
CurY := YPos;
end;
_DRAW: Begin
Line(CurX,CurY,xpos,ypos);
CurX:=xpos;
CurY:=ypos;
end;
else
Begin
end;
end;
Inc(counter);
end; { end while }
if Currenttextinfo.direction=VertDir then
y:=y-round(byte(fonts[CurrenttextInfo.font].widths[c])*CurrentXRatio)
else
x:=x+round(byte(fonts[Currenttextinfo.font].widths[c])*CurrentXRatio) ;
end;
setlinestyle( oldvalues.linestyle, oldvalues.pattern, oldvalues.thickness);
end;
{ restore write mode }
CurrentWriteMode := WriteMode;
end;
procedure OutText(const TextString : string);
var x,y:integer;
begin
{ Save CP }
x:=CurrentX;
y:=CurrentY;
OutTextXY(CurrentX,CurrentY,TextString);
{ If the direction is Horizontal and the justification left }
{ then and only then do we update the CP }
if (Currenttextinfo.direction=HorizDir) and
(Currenttextinfo.horiz=LeftText) then
inc(x,textwidth(TextString));
{ Update the CP }
CurrentX := X;
CurrentY := Y;
end;
procedure SetTextJustify(horiz,vert : word);
begin
if (horiz<0) or (horiz>2) or
(vert<0) or (vert>2) then
begin
_graphresult:=grError;
exit;
end;
Currenttextinfo.horiz:=horiz;
Currenttextinfo.vert:=vert;
end;
procedure SetTextStyle(font,direction : word;charsize : word);
var
f : file;
Prefix: array[0..Prefix_Size-1] of char; {* File Prefix Holder *}
Length, Current: longint;
FontData: Pchar;
Base: longint;
hp : pchar;
i : longint;
begin
if font>installedfonts then
begin
_graphresult:=grInvalidFontNum;
exit;
end;
Currenttextinfo.font:=font;
if (direction<>HorizDir) and (direction<>VertDir) then
direction:=HorizDir;
Currenttextinfo.direction:=direction;
{ According to the Turbo Pascal programmer's reference }
{ maximum charsize for bitmapped font is 10 }
if (CurrentTextInfo.Font = DefaultFont) and (Charsize > 10) then
Currenttextinfo.charsize:=10
else
Currenttextinfo.charsize:=charsize;
{ This is only valid for stroked fonts }
if (charsize <> usercharsize) then
begin
CurrentXRatio := charsize / 4;
CurrentYRatio := charsize / 4;
end;
{ if this is a stroked font then load it if not already loaded }
{ into memory... }
if (font>DefaultFont) and not assigned(fonts[font].instr) then
begin
assign(f,bgipath+fonts[font].name+'.CHR');
reset(f,1);
if ioresult<>0 then
begin
_graphresult:=grFontNotFound;
Currenttextinfo.font:=DefaultFont;
exit;
end;
{* Read in the file prefix *}
BlockRead(F, Prefix, Prefix_Size);
hp:=Prefix;
i:=0;
while (hp[i] <> chr($1a)) do Inc(i);
move(hp[i+1],fonts[font].PHeader,sizeof(TFHeader));
(* Read in the Header file *)
BlockRead(F,fonts[font].Header,Sizeof(THeader));
Base := FilePos(F); {* Remember the address of table*}
BlockRead(F,Fonts[font].Offsets[Fonts[font].Header.First_Char],Fonts[font].Header.Nr_chars*sizeof(integer));
{* Load the character width table into memory. *}
base := filePos( f );
BlockRead(F,Fonts[font].Widths[Fonts[font].Header.First_Char],Fonts[font].Header.Nr_chars*sizeof(byte));
{* Determine the length of the stroke database. *}
current := FilePos( f ); {* Current file location *}
Seek( f, FileSize(F)); {* Go to the end of the file *}
length := FilePos( f ); {* Get the file length *}
Seek( f, current); {* Restore old file location *}
{* Load the stroke database. *}
{ also allocate space for Null character }
Getmem(FontData, Length+1); {* Create space for font data *}
BlockRead(F, FontData^, length-current); {* Load the stroke data *}
FontData[length-current+1] := #0;
if fonts[font].header.Signature<> SIGNATURE then
begin
_graphResult:=grInvalidFont;
Currenttextinfo.font:=DefaultFont;
Freemem(FontData, Length+1);
exit;
end;
fonts[font].instr:=FontData;
if not testfont(Prefix) then
begin
_graphresult:=grInvalidFont;
Currenttextinfo.font:=DefaultFont;
Freemem(FontData, Length+1);
end;
close(f);
end;
end;
procedure SetUserCharSize(Multx,Divx,Multy,Divy : word);
begin
CurrentXRatio := MultX / DivX;
CurrentYRatio := MultY / DivY;
end;
{
$Log$
Revision 1.4 1999-07-12 13:27:16 jonas
+ added Log and Id tags
* added first FPC support, only VGA works to some extend for now
* use -dasmgraph to use assembler routines, otherwise Pascal
equivalents are used
* use -dsupportVESA to support VESA (crashes under FPC for now)
* only dispose vesainfo at closegrph if a vesa card was detected
* changed int32 to longint (int32 is not declared under FPC)
* changed the declaration of almost every procedure in graph.inc to
"far;" becquse otherwise you can't assign them to procvars under TP
real mode (but unexplainable "data segnment too large" errors prevent
it from working under real mode anyway)
}