unit Xlib;
interface
type
ColorType = record
Red,Green,Blue:byte;
end; {record}
TableType = array[0..45,0..300] of integer;
SpeedRangeType = 0..10;
SizeSubRange = 1..10;
ImageType = record
deltaX,deltaY:longint;
vSegment,vOffset:word;
Image:pointer;
end; {record}
FontEnumeratedType = (Regular);
FontType = record
Size:SizeSubRange;
end; {record}
SomePaletteType = array[0..255] of ColorType;
PictureType = record
picture:ImageType;
Palette:^SomePaletteType;
end; {record}
ColorRecordType = record
Color:ColorType;
IsAPoint:Boolean;
Location:byte;
end; {record}
GradatedPalType = array[0..255] of ColorRecordType;
GradatedPaletteType = ^GradatedPalType;
procedure InitGraph;
procedure CloseGraph;
procedure SetColor(AColor:byte);
function GetColor:byte;
procedure PutPixel(x,y:word);
function GetPixel(x,y:word):byte;
procedure SetPalette(ColorNumber:byte;AColor:ColorType);
procedure GetPalette(ColorNumber:byte;var TheColor:ColorType);
procedure FadeScreen(Speed:SpeedRangeType);
procedure ClearScreen;
procedure Circle(x,y,Radius:word);
procedure Line(x1,y1,x2,y2:word);
procedure Bar(BeginX,BeginY,EndX,EndY:word);
procedure Rectangle(BeginX,BeginY,EndX,EndY:word);
procedure Fill(x,y:word;FillColor,Border:byte);
procedure GetImage(var Image:ImageType;x1,y1,x2,y2:word);
procedure PutImage(Image:ImageType;x1,y1:word);
procedure KillImage(var Image:ImageType);
procedure PutChar(Font:FontEnumeratedType;xc,yc:word;AChar:char);
procedure WriteStringXY(Font:FontEnumeratedType;xc,yc:word;Thing:string);
procedure WriteString(Font:FontEnumeratedType;Thing:string);
procedure LoadTGA(var Image:PictureType;FileName:string);
procedure DrawPicture(x1,y1:word;Image:PictureType;BeginPart,EndPart:byte);
procedure KillPicture(var Image:PictureType);
procedure InitGradatedPalette(var SomePalette:GradatedPaletteType);
procedure AddKeyPoint(var SomePalette:GradatedPaletteType;Location:byte;Color:ColorType);
procedure GeneratePalette(SomePalette:GradatedPaletteType);
procedure KillGradatedPalette(var SomePalette:GradatedPaletteType);
implementation
uses CRT;
const
PI = 3.1415926;
VGA:Word = $a000;
IOOk=0;
Video=$10;
Crtc_addr=$3d4;
Sequ_addr=$3c4;
TGASize:longint = 64000;
type
ByteBitType = array[1..8] of boolean;
PCXFileType = file of char;
EGAPalType = array[0..47] of byte;
JunkType = array[0..57] of byte;
PCXHeaderType = record
Manufacturer:char; {for some reason, always 10}
Version:char; {who cares}
Encoding:char; {always 1 for RLE}
BitDepth:char; {should be eight - wierd things if not}
x,y:word; {upper left corner of the image}
width,height:word; {Duh}
HRes,VRes:word; {Number of pixels in x & y direction}
EGAPalette:EGAPalType; {who cares, we use MCGA+}
Reserved:char; {Nothing}
ColorPlanes:char; {Number of color planes in the image}
BytesPerLine:word; {Number of bytes per line}
PaletteType:word; {Unimportant}
Padding:JunkType; {Fat at the end of the file}
end; {PCXHeaderType}
PaletteFileType = file of ColorType;
PortLookUpType = array[0..640] of word;
yLookUpType = array[0..400] of word;
var
SinRadiusTable,CosRadiusTable:TableType;
CurrentDrawingColor:byte;
HasBeenInitialized:boolean;
CurrentTextX,CurrentTextY:word;
PortLookUp:PortLookUpType;
yLookUp:yLookUpType;
CurrentPort:word;
function Abs(x:integer):word;
begin
if x<0 then
Abs:=-x
else
Abs:=x;
end; {Abs}
procedure AnnoyingErrorMessage;
begin
if not HasBeenInitialized then begin
writeln('Error #666: The screen has not be initialized. Please call InitGraph;');
halt;
end; {if}
end; {AnnoyingErrorMessage}
procedure SetXtended;
begin
asm
mov ax, $4F02
mov bx, $100
int VIDEO
end;
{ Turn the VGA screen off }
Port[SEQU_ADDR] := 1;
Port[SEQU_ADDR + 1] := Port[SEQU_ADDR + 1] or $20;
{ Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4) }
PortW[SEQU_ADDR] := $0604;
{ Turn off word mode, by setting the Mode Control register
of the CRT Controller (index 0x17, port 0x3d4) }
PortW[CRTC_ADDR] := $E317;
{ Turn off doubleword mode, by setting the Underline Location
register (index 0x14, port 0x3d4) }
PortW[CRTC_ADDR] := $0014;
{ Clear entire video memory, by selecting all four planes, then writing
color 0 to the entire segment. Stoopid FillChar fills 1 byte too short! }
PortW[SEQU_ADDR] := $0F02;
FillChar(Mem[$A000 : 0], $8000, 0);
FillChar(Mem[$A000 : $8000], $8000, 0);
{ Give a small delay to let the screen sort itself out }
Delay(100);
{ Turn the screen back on }
Port[SEQU_ADDR] := 1;
Port[SEQU_ADDR + 1] := Port[SEQU_ADDR + 1] and $DF;
end; {SetXtended}
procedure InitGraph;
var
x,Radius:integer;
Constant:real;
ymov,tymov:word;
begin
HasBeenInitialized:=TRUE;
Constant:=PI/180;
writeln('Computing port lookup table...');
for x:=0 to 640 do
PortLookUp[x]:=$100 shl (x and 3) + 2;
writeln('Computing y lookup table...');
for x:=0 to 400 do
yLookUp[x]:=x shl 7 + x shl 5;
writeln('Computing sine and cosine lookup tables...');
for Radius:=0 to 300 do
for x:=0 to 45 do begin
SinRadiusTable[x,Radius]:=trunc(sin(x*Constant)*Radius);
CosRadiusTable[x,Radius]:=trunc(cos(x*Constant)*Radius);
end; {for}
CurrentTextX:=0; CurrentTextY:=0;
SetXtended;
SetColor(15);
CurrentPort:=PortLookUp[0];
end; {InitGraph}
procedure CloseGraph;
begin
AnnoyingErrorMessage;
asm
mov ax,0003h
int 10h
end; {asm}
writeln('This program uses Mark Rosen''s graphics library.');
writeln('Send E-Mail to mrosen@peganet.com for more information');
end; {CloseGraph}
procedure SetColor(AColor:byte);
begin
AnnoyingErrorMessage;
CurrentDrawingColor:=AColor mod 256;
end; {SetColor}
function GetColor:byte;
begin
AnnoyingErrorMessage;
GetColor:=CurrentDrawingColor;
end; {GetColor}
procedure PutPixel(x,y:word);
var
t:word;
begin
if CurrentPort<>PortLookUp[x] then begin
PortW[SEQU_ADDR] := PortLookUp[x];
CurrentPort:=PortLookUp[x];
end; {if}
{ Calculate address (y * 160 + x div 4) and write pixel }
Mem[$A000 : yLookUp[y]+x shr 2] := CurrentDrawingColor;
end; {PutPixel}
function GetPixel(x,y:word):byte;
var
t:word;
begin
PortW[SEQU_ADDR] := PortLookUp[x];
{ Calculate address (y * 160 + x div 4) and write pixel }
GetPixel:=Mem[$A000 : yLookUp[y]+x shr 2];
end; {GetPixel}
procedure SetPalette(ColorNumber:byte;AColor:ColorType);
begin
AnnoyingErrorMessage;
asm
mov dx,3c8h
mov al,[ColorNumber]
out dx,al
inc dx
mov al,[AColor.Red]
out dx,al
mov al,[AColor.Green]
out dx,al
mov al,[AColor.Blue]
out dx,al
end; {asm}
end; {SetAPaletteEntry}
procedure GetPalette(ColorNumber:byte;var TheColor:ColorType);
var
Rt,Gt,Bt:byte;
begin
AnnoyingErrorMessage;
Rt:=TheColor.Red; Gt:=TheColor.Green; Bt:=TheColor.Blue;
asm
mov dx, 3c7h
mov al, [ColorNumber]
out dx, al
inc dx
inc dx
in al, dx
mov [Rt],al
in al, dx
mov [Gt],al
in al, dx
mov [Bt],al
end; {asm}
TheColor.Red:=Rt; TheColor.Green:=Gt; TheColor.Blue:=Bt;
end; {GetAPaletteEntry}
procedure FadeScreen(Speed:SpeedRangeType);
var
HaveChangedSomething:boolean;
CurrentColor:ColorType;
x,Counter:integer;
begin
AnnoyingErrorMessage;
HaveChangedSomething:=TRUE;
while HaveChangedSomething do begin
Delay(5*(10-Speed));
HaveChangedSomething:=FALSE;
for x:=0 to 255 do begin
GetPalette(x,CurrentColor);
if CurrentColor.Red<>0 then begin
dec(CurrentColor.Red);
HaveChangedSomething:=TRUE;
end; {if}
if CurrentColor.Green<>0 then begin
dec(CurrentColor.Green);
HaveChangedSomething:=TRUE;
end; {if}
if CurrentColor.Blue<>0 then begin
dec(CurrentColor.Blue);
HaveChangedSomething:=TRUE;
end; {if}
SetPalette(x,CurrentColor);
end; {for}
end; {while}
end; {FadeScreen}
procedure Line(x1, y1, x2, y2 : word);
var i,DeltaX,DeltaY,numpixels,
d,dinc1,dinc2,
x,xinc1,xinc2,
y,yinc1,yinc2:integer;
begin
DeltaX:=abs(x2-x1);
DeltaY:=abs(y2-y1);
if DeltaX>=deltay then begin
numpixels:=DeltaX+1;
d:= (2*deltay)-DeltaX;
dinc1:=deltay shl 1;
dinc2:=(deltay-DeltaX) shl 1;
xinc1:=1;
xinc2:=1;
yinc1:=0;
yinc2:=1;
end {if}
else begin
numpixels:=deltay+1;
d:=(2*DeltaX)-deltay;
dinc1:=DeltaX shl 1;
dinc2:=(DeltaX-deltay) shl 1;
xinc1:= 0;
xinc2:= 1;
yinc1:=1;
yinc2:=1;
end; {else}
if x1 > x2 then begin
xinc1:=-xinc1;
xinc2:=-xinc2;
end; {if}
if y1>y2 then begin
yinc1:=-yinc1;
yinc2:=-yinc2;
end; {if}
x:=x1;
y:=y1;
for i := 1 to numpixels do begin
PutPixel(x,y);
if d < 0 then begin
d:= d+dinc1;
x:= x+xinc1;
y:= y+yinc1;
end {f}
else begin
d:= d+dinc2;
x:= x+xinc2;
y:= y+yinc2;
end; {else}
end; {for}
end; {Line}
procedure Bar(BeginX,BeginY,EndX,EndY:word);
var
y,x:word;
size:word;
begin
AnnoyingErrorMessage;
for x:=BeginX to EndX do
for y:=BeginY to EndY do
putpixel(x,y);
end; {Box}
procedure Rectangle(BeginX,BeginY,EndX,EndY:word);
begin
AnnoyingErrorMessage;
Line(BeginX,EndX,BeginY,EndY);
Line(BeginX,EndX,BeginY,EndY);
Line(BeginX,EndX,BeginY,EndY);
Line(BeginX,EndX,BeginY,EndY);
end; {Rectangle}
procedure Circle(x,y,Radius:word);
var
Angle,RealX,RealY,
XPlusRealX,XMinusRealX,XPlusRealY,XMinusRealY,
YPlusRealY,YMinusRealY,YPlusRealX,YMinusRealX:word;
begin
for Angle:=0 to 45 do begin
RealX:=CosRadiusTable[Angle,Radius];
RealY:=SinRadiusTable[Angle,Radius];
XPlusRealX:=x+RealX; XPlusRealY:=x+RealY;
XMinusRealX:=x-RealX; XMinusRealY:=x-RealY;
YPlusRealY:=y+RealY; YPlusRealX:=y+RealX;
YMinusRealY:=y-RealY; YMinusRealX:=y-RealX;
putpixel(XPlusRealX,YPlusRealY);
putpixel(XPlusRealX,YMinusRealY);
putpixel(XMinusRealX,YPlusRealY);
putpixel(XMinusRealX,YMinusRealY);
putpixel(XPlusRealY,YPlusRealX);
putpixel(XPlusRealY,YMinusRealX);
putpixel(XMinusRealY,YPlusRealX);
putpixel(XMinusRealY,YMinusRealX);
end; {for}
end; {Circle}
procedure RecursiveFill(x,y:word;FillColor,Border:byte);
var
Direction:byte;
begin
if (GetPixel(x,y)<>FillColor) and (GetPixel(x,y)<>Border) then begin
putpixel(x,y);
for Direction:=1 to 4 do begin
if (Direction=1) and (y>0) then
RecursiveFill(x,y-1,FillColor,Border)
else if (Direction=2) and (x<319) then
RecursiveFill(x+1,y,FillColor,Border)
else if (Direction=3) and (y<199) then
RecursiveFill(x,y+1,FillColor,Border)
else if (Direction=4) and (x>0) then
RecursiveFill(x-1,y,FillColor,Border);
end; {for}
end; {if}
end; {RecursiveFill}
procedure Fill(x,y:word;FillColor,Border:byte);
begin
setcolor(FillColor);
RecursiveFill(x,y,FillColor,Border);
end; {Fill}
procedure GetImage(var Image:ImageType;x1,y1,x2,y2:word);
var
yImg,yShl,x,y,size,vSeg,vOfs:word;
begin
Image.DeltaX:=abs(x1-x2); Image.DeltaY:=abs(y1-y2);
GetMem(Image.Image,(Image.DeltaX+1)*(Image.DeltaY+1));
Image.vSegment:=seg(Image.Image^); vSeg:=Image.vSegment;
Image.vOffset:=ofs(Image.Image^); vOfs:=Image.vOffset;
for x:=x1 to x2 do begin
for y:=y1 to y2 do begin
Mem[Image.vSegment:Image.vOffset+((y-y1)*(Image.DeltaX+1))+(x-x1)]:=GetPixel(x,y);
end; {for}
end; {for}
end; {GetImage}
procedure PutImage(Image:ImageType;x1,y1:word);
var
yImg,yShl:word;
x,y:word;
begin
for x:=0 to Image.DeltaX do begin
for y:=0 to Image.DeltaY do begin
setcolor(Mem[Image.vSegment:Image.vOffset+(y*(Image.DeltaX+1))+x]);
putpixel(x+x1,y+y1);
end; {for}
end; {for}
end; {PutImage}
procedure KillImage(var Image:ImageType);
begin
AnnoyingErrorMessage;
FreeMem(Image.Image,(Image.DeltaX+1)*(Image.DeltaY+1));
end; {KillImage}
function Exp(base,exponent:word):word;
var
Temp,x:word;
begin
Temp:=1;
for x:=1 to exponent do
Temp:=Temp*base;
Exp:=Temp;
end; {Exp}
procedure ConvertDecimalToBinary(Something:word;var Bin:ByteBitType);
var
x:byte;
temp:word;
begin
for x:=1 to 8 do
Bin[x]:=FALSE;
for x:=7 downto 0 do begin
temp:=trunc(Something div Exp(2,x));
if temp>0 then Something:=Something-Exp(2,x);
if temp=1 then
Bin[x+1]:=TRUE;
end; {for}
end; {ConvertDecimalToBinary}
procedure PutChar(Font:FontEnumeratedType;xc,yc:word;AChar:char);
var
x,y:word;
Line:byte;
Temp:ByteBitType;
begin
AnnoyingErrorMessage;
for y:=0 to 7 do begin
Line:=Mem[$F000:$FA6E+(ord(AChar)*8)+y];
ConvertDecimalToBinary(Line,Temp);
for x:=1 to 8 do
if Temp[x] then putpixel((9-x)+xc,y+yc);
end;
end; {PutChar}
procedure WriteStringXY(Font:FontEnumeratedType;xc,yc:word;Thing:string);
var
TempX,TempY:word;
x:byte;
begin
AnnoyingErrorMessage;
TempX:=xc; TempY:=yc;
for x:=1 to ord(Thing[0]) do begin
PutChar(Font,TempX,TempY,Thing[x]);
TempX:=TempX+8;
if TempX>311 then begin
TempX:=1;
TempY:=TempY+8;
end; {if}
end; {for}
end; {WriteStringXY}
procedure WriteString(Font:FontEnumeratedType;Thing:string);
var
TempX,TempY:word;
x:byte;
begin
AnnoyingErrorMessage;
TempX:=CurrentTextX; TempY:=CurrentTextY;
for x:=1 to ord(Thing[0]) do begin
PutChar(Font,TempX,TempY,Thing[x]);
TempX:=TempX+8;
if TempX>311 then begin
TempX:=1;
TempY:=TempY+8;
end; {if}
end; {for}
CurrentTextX:=CurrentTextX+TempX;
CurrentTextY:=CurrentTextY+TempY;
end; {WriteString}
procedure FlipImage(var SomeImage:ImageType);
var
ey,by,HalfY,x,y:word;
p1seg,p1ofs,p2seg,p2ofs:word;
DeltaXPlus,DeltaYPlus:word;
Temp:byte;
begin
DeltaXPlus:=SomeImage.DeltaX+1; DeltaYPlus:=SomeImage.DeltaY+1;
HalfY:=((DeltaYPlus) div 2)-1;
by:=SomeImage.vOffset; ey:=SomeImage.vOffset+(DeltaXPlus*DeltaYPlus);
p1seg:=SomeImage.vSegment; p2seg:=SomeImage.vSegment;
for y:=0 to HalfY do begin
ey:=ey-DeltaXPlus;
for x:=0 to SomeImage.DeltaX do begin
Temp:=Mem[p1seg:by];
Mem[p1seg:by]:=Mem[p2seg:ey];
Mem[p2seg:ey]:=Temp;
inc(ey);
inc(by);
end; {for}
ey:=ey-DeltaXPlus;
end; {for}
end; {FlipImage}
procedure LoadTGA(var Image:PictureType;FileName:string);
var
fp:file;
TempPalAndHeadPtr:pointer;
TempImage:ImageType;
vSeg,vOfs,x:word;
TempColor:ColorType;
Size,CurSize:longint;
t,tOfs:longint;
begin
assign(fp,FileName);
reset(fp,1); {no error checking as of yet}
GetMem(TempPalAndHeadPtr,786);
new(Image.Palette);
BlockRead(fp,TempPalAndHeadPtr^,786);
vSeg:=seg(TempPalAndHeadPtr^); vOfs:=ofs(TempPalAndHeadPtr^);
Image.Picture.DeltaX:=(Mem[vSeg:vOfs+13] shl 8)+Mem[vSeg:vOfs+12]-1;
Image.Picture.DeltaY:=(Mem[vSeg:vOfs+15] shl 8)+Mem[vSeg:vOfs+14]-1;
vOfs:=vOfs+18;
for x:=0 to 255 do begin
TempColor.Blue:=Mem[vSeg:vOfs] shr 2;
vOfs:=vOfs+1;
TempColor.Green:=Mem[vSeg:vOfs] shr 2;
vOfs:=vOfs+1;
TempColor.Red:=Mem[vSeg:vOfs] shr 2;
vOfs:=vOfs+1;
Image.Palette^[x]:=TempColor;
end; {for}
T:=filesize(fp);
Size:=(Image.Picture.DeltaX+1)*(Image.Picture.DeltaY+1);
GetMem(Image.Picture.Image,(Image.Picture.DeltaX+1)*(Image.Picture.DeltaY+1));
Image.Picture.vSegment:=seg(Image.Picture.Image^);
Image.Picture.vOffset:=ofs(Image.Picture.Image^);
seek(fp,786);
{ blockread(fp,Image.picture.image^,size);}
if (Image.Picture.DeltaX+1)*(Image.Picture.DeltaY+1)<=TGASize then begin
BlockRead(fp,Image.picture.image^,size);
end {if}
else begin
tOfs:=Image.picture.vOffset;
CurSize:=0;
for x:=1 to size div (TGASize) do begin
blockread(fp,ptr(Image.Picture.vSegment,tOfs)^,TGASize-2);
tOfs:=tOfs+TGASize;
CurSize:=CurSize+TGASize;
end; {for}
blockread(fp,ptr(Image.Picture.vSegment,tOfs)^,Size-CurSize);
end; {else}
FreeMem(TempPalAndHeadPtr,786);
FlipImage(Image.Picture);
close(fp);
end; {LoadTGA}
procedure DrawPicture(x1,y1:word;Image:PictureType;BeginPart,EndPart:byte);
var
x,vSeg,vOfs:word;
Color:byte;
begin
for x:=BeginPart to EndPart do
setpalette(x,Image.Palette^[x]);
PutImage(Image.Picture,x1,y1);
end; {DrawPicture}
procedure KillPicture(var Image:PictureType);
begin
KillImage(Image.Picture);
dispose(Image.Palette);
end; {KillPicture}
procedure ClearScreen;
var
x,y:word;
begin
setcolor(0);
for x:=0 to 639 do
for y:=0 to 479 do
putpixel(x,y);
end; {ClearScreen}
procedure InitGradatedPalette(var SomePalette:GradatedPaletteType);
var
Black:ColorType;
x:byte;
begin
AnnoyingErrorMessage;
new(SomePalette);
Black.Red:=0; Black.Green:=0; Black.Blue:=0;
for x:=0 to 255 do begin
SomePalette^[x].Color:=Black;
SomePalette^[x].IsAPoint:=FALSE;
end; {for}
end; {InitGradatedPalette}
procedure AddKeyPoint(var SomePalette:GradatedPaletteType;Location:byte;
Color:ColorType);
begin
AnnoyingErrorMessage;
SomePalette^[Location].Color:=Color;
SomePalette^[Location].IsAPoint:=TRUE;
SomePalette^[Location].Location:=Location;
end; {AddKeyPoint}
procedure GeneratePalette(SomePalette:GradatedPaletteType);
var
TempColor:ColorType;
Temp1,Temp2:ColorRecordType;
Location1,Location2:byte;
x,y:byte;
Number:byte;
Temp:array[0..255] of ColorRecordType;
RealRed,RealGreen,RealBlue,
IncRed,IncGreen,IncBlue:real;
DeltaLoc:word;
begin
AnnoyingErrorMessage;
Number:=0;
{Condense the palette into only hot points}
for x:=0 to 255 do begin
if SomePalette^[x].IsAPoint then begin
Temp[Number]:=SomePalette^[x];
Number:=Number+1;
end; {if}
end; {for}
{actually gradate the palette}
for x:=0 to Number-1 do begin
Location1:=Temp[x].Location;
Location2:=Temp[x+1].Location;
DeltaLoc:=abs(Location2-Location1);
RealRed:=Temp[x].Color.Red; RealGreen:=Temp[x].Color.Green;
RealBlue:=Temp[x].Color.Blue;
IncRed:=(Temp[x+1].Color.Red-Temp[x].Color.Red)/DeltaLoc;
IncGreen:=(Temp[x+1].Color.Green-Temp[x].Color.Green)/DeltaLoc;
IncBlue:=(Temp[x+1].Color.Blue-Temp[x].Color.Blue)/DeltaLoc;
for y:=Location1 to Location2 do begin
TempColor.Red:=trunc(RealRed); TempColor.Green:=trunc(RealGreen);
TempColor.Blue:=trunc(RealBlue);
setpalette(y,TempColor);
RealRed:=RealRed+IncRed;
RealGreen:=RealGreen+IncGreen;
RealBlue:=RealBlue+IncBlue;
end; {for}
end; {for}
end; {GeneratePalette}
procedure KillGradatedPalette(var SomePalette:GradatedPaletteType);
begin
AnnoyingErrorMessage;
dispose(SomePalette);
end; {KillGradatedPalette}
end. {Xlib}