{Fill is unstable for circles with radii greater than 49? Wierd rounding???}
{$A+}
{$G+}
{$N+,E-}
unit Graphics;
interface
type
{Some memory for a screen}
MCGAScreenType = array[0..63900] of byte;
{The main data structure for a page}
PageType = record
Page:^MCGAScreenType;
Segment,Offset:word;
end; {record}
const
{the color constants}
Black = 0;
Blue = 1;
Green = 2;
Cyan = 3;
Red = 4;
Magenta = 5;
Brown = 6;
LightGray = 7;
DarkGray = 8;
LightBlue = 9;
LightGreen = 10;
LightCyan = 11;
LightRed = 12;
LightMagenta = 13;
Yellow = 14;
White = 15;
{We only have one screen mode, so these can be constants}
MinX = 0; MinY = 0;
MaxX = 319; MaxY = 199;
{Constants used in paging and movies to determine whether to draw
the palette}
PaletteEnabled = TRUE;
PaletteDisabled = FALSE;
{An unusual name (most likely not used as an hot-area indentifier)
used in the PollHotAreas function}
Nothing = 'FooF'; {An unusual name}
{A constant that points to the video buffer}
TheScreen:PageType = (Page:ptr($a000,0000);Segment:$a000;Offset:0000);
type
{Records the elements of a color}
ColorType = record
Red,Green,Blue:byte;
end; {record}
{Used to store the sin and cos lookup tables}
TableType = array[0..45,0..300] of integer;
{SubRange for the speed of a FadeDown}
SpeedRangeType = 0..10;
{Used to store an image}
ImageType = record
deltaX,deltaY:integer;
vSegment,vOffset:word;
Image:pointer;
end; {record}
{Font size... useless because I don't want to do anything}
SizeSubRange = 1..10;
{Which direction to play a movie in}
DirectionType = (Foreward,Backward);
{Which font type... useless because there is only regular}
FontEnumeratedType = (Regular);
{Useless stuff about a font}
FontType = record
Size:SizeSubRange;
end; {record}
{Variables that determine the characteristics of a fire}
FireType = record
c,last:word;
x1,y1,x2,y2:word;
PalBegin,PalEnd,PalLen:byte;
end; {record}
{Defines a point in the gradated palette}
ColorRecordType = record
Color:ColorType;
IsAPoint:Boolean;
Location:byte;
end; {record}
{Defines a coordinate}
CordType = record
x,y:word;
end; {record}
MapNodePtr = ^MapNodeType;
{Defines one node (area) of an image map}
MapNodeType = record
next:MapNodePtr;
name:string;
number:integer;
b1,e1,b2,e2:CordType;
end; {record}
{The large map-type definition}
MapType = record
Render:PageType;
Rendered:boolean;
First,Last:MapNodePtr;
NumItems:word;
end; {record}
{A palette}
PaletteType = array[0..255] of ColorType;
PaletteTypePtr = ^PaletteType;
MovieNodePtr = ^MovieNode;
{Defines one node (frame) of a movie}
MovieNode = record
Picture:ImageType;
Palette:PaletteTypePtr;
prev,next:MovieNodePtr;
end; {record}
{Defines a movie}
MovieType = record
Front,Last:MovieNodePtr;
NumFrames:word;
end; {record}
{Defines a gradated palette; max 256 colors}
GradatedPalType = array[0..255] of ColorRecordType;
GradatedPaletteType = ^GradatedPalType;
{The palette for a picture}
SomePaletteType = array[0..255] of ColorType;
{A Picture!!! I love my TGAs and PCXes}
PictureType = record
picture:ImageType;
Palette:^SomePaletteType;
end; {record}
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 RotatePalette(Start,Finish:byte);
procedure SavePalette(FileName:string);
procedure LoadPalette(FileName:string);
procedure InitGradatedPalette(var SomePalette:GradatedPaletteType);
procedure AddKeyPoint(var SomePalette:GradatedPaletteType;Location:byte;Color:ColorType);
procedure GeneratePalette(SomePalette:GradatedPaletteType);
procedure KillGradatedPalette(var SomePalette:GradatedPaletteType);
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 InitFire(var SomeFire:FireType;x1,y1,x2,y2:word;BeginPal,EndPal:byte);
procedure DoFire(SomeFire:FireType);
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 LoadPCX(var Image:PictureType;FileName:string);
procedure LoadTGA(var Image:PictureType;FileName:string);
procedure DrawPicture(x1,y1:word;Image:PictureType;BeginPart,EndPart:byte);
procedure KillPicture(var Image:PictureType);
procedure InitPage(var SomePage:PageType);
procedure SetActivePage(SomePage:PageType);
procedure CopyPage(Source:PageType;var Destination:PageType);
procedure CopyPageToVGA(SomePage:PageType);
procedure CopyVGAToPage(var SomePage:PageType);
procedure KillPage(SomePage:PageType);
procedure InitMap(var SomeMap:MapType);
procedure AddHotArea(var SomeMap:MapType;Name:string;bx1,by1,ex1,ey1,bx2,by2,ex2,ey2:word);
procedure RenderHotAreas(var SomeMap:MapType);
function PollHotAreas(SomeMap:MapType):string;
procedure KillMap(var SomeMap:MapType);
procedure FlipImage(var SomeImage:ImageType);
procedure MirrorImage(var SomeImage:ImageType);
procedure InitMovie(var SomeMovie:MovieType);
procedure AddSomeFrame(var SomeMovie:MovieType;x1,y1,x2,y2:word);
procedure PlayMovie(SomeMovie:MovieType;pX,pY:word;UsePalette:boolean;Direction:DirectionType);
procedure KillMovie(var SomeMovie:MovieType);
implementation
uses CRT,Mouse,Memory;
const
PI = 3.1415926;
VGA:Word = $a000;
IOOk = 0;
type
ByteBitType = array[1..8] of boolean;
PaletteFileType = file of ColorType;
YAsmLookUpType = array[0..300] of word;
var
{Hold the sin and cos tables}
SinRadiusTable,CosRadiusTable:TableType;
{The current drawing color used in practically every procedure}
CurrentDrawingColor:byte;
{Determines if InitGraph has been called}
HasBeenInitialized:boolean;
{Point to the location of the invisible cursor (WriteString)}
CurrentTextX,CurrentTextY:word;
{A lookup table for the y cords. (y-1)*320}
AsmLookUp:YAsmLookUpType;
{The current page}
ActivePage:PageType;
{The segment and offset of the current page}
CurSeg,CurOfs:word;
{Converts degrees to radians}
function DegreesToRadians(Degrees:real):real;
begin
DegreesToRadians:=PI*Degrees/180;
end; {DegreeesToRadians}
{Converts radians to degrees}
function RadiansToDegrees(Radians:real):real;
begin
RadiansToDegrees:=180*Radians/PI;
end; {RadiansToDegrees}
{If you don't call InitGraph, feel the wrath!}
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}
{Goes into 320x200x256 mode}
procedure SetMCGA;
begin
asm
mov ax,0013h
int 10h
end; {asm}
end; {SetMCGA}
{Sets MCGA mode and does some lookup tables}
procedure InitGraph;
var
x,Radius:integer;
Constant:real;
ymov,tymov:word;
begin
HasBeenInitialized:=TRUE;
Constant:=PI/180;
writeln('Computing y lookup table...');
for x:=0 to 200 do begin
ymov:=x shl 6;
tymov:=ymov;
ymov:=ymov shl 2;
ymov:=ymov+tymov;
AsmLookUp[x]:=ymov;
end; {for}
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;
SetMCGA;
SetColor(15);
ActivePage:=TheScreen;
CurSeg:=$a000;
CurOfs:=0000;
TheScreen.Segment:=$a000;
TheScreen.Offset:=$0000;
TheScreen.Page:=ptr($a000,0000);
end; {InitGraph}
{Returns to text mode}
procedure CloseGraph;
begin
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}
{Sets the current color}
procedure SetColor(AColor:byte);
begin
AnnoyingErrorMessage;
CurrentDrawingColor:=AColor;
end; {SetColor}
{Returns the current color}
function GetColor:byte;
begin
AnnoyingErrorMessage;
GetColor:=CurrentDrawingColor;
end; {GetColor}
{Draws a pixel at (x,y)}
procedure PutPixel(x,y:word);
var
t:word;
begin
t:=AsmLookUp[y];
asm
mov es,[CurSeg]
mov di,[t]
add di,[CurOfs]
add di,[X]
mov al,[CurrentDrawingColor]
mov es:[di],al
end; {asm}
end; {PutPixel}
{Returns the color at (x,y)}
function GetPixel(x,y:word):byte;
var
TempColor:byte;
t:word;
begin
AnnoyingErrorMessage;
t:=AsmLookUp[y mod 320];
x:=x mod 320;
asm
mov es,[CurSeg]
mov di,[t]
add di,[CurOfs]
add di,[x]
mov al,es:[di]
mov [TempColor],al
end; {asm}
GetPixel:=TempColor;
end; {GetPixel}
{Sets a color register to a color}
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}
{Returns the contents of a color register}
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}
{Fades the screen to black, destroying the palette}
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}
{Rotates all colors from Start to Finish}
procedure RotatePalette(Start,Finish:byte);
var
x:byte;
Holding,Temp:ColorType;
begin
AnnoyingErrorMessage;
GetPalette(Start,Holding);
for x:=Start+1 to Finish do begin
GetPalette(x,Temp);
SetPalette(x-1,Temp);
end; {for}
SetPalette(Finish,Holding);
end; {RotatePalette}
{Stores the contents of a file into FileName}
procedure SavePalette(FileName:string);
var
Food:PaletteFileType;
TempColor:ColorType;
x:byte;
begin
AnnoyingErrorMessage;
assign(Food,FileName);
rewrite(Food);
for x:=0 to 255 do begin
GetPalette(x,TempColor);
write(Food,TempColor);
end; {for}
close(Food);
end; {SavePalette}
{Loads a palette from disk; must be valid}
procedure LoadPalette(FileName:string);
var
Food:PaletteFileType;
TempColor:ColorType;
x:byte;
begin
AnnoyingErrorMessage;
assign(Food,FileName);
{$I-}
Reset(Food);
{$I+}
if IOResult=IOOk then begin
for x:=0 to 255 do begin
Read(Food,TempColor);
SetPalette(x,TempColor);
end; {for}
Close(Food);
end; {if}
end; {LoadPalette}
{Draws a circle on the screen; a bit irregular because of rounding errors}
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 SwapW(var a,b:word);
var
Temp:word;
begin
Temp:=a;
a:=b;
b:=Temp;
end; {SwapW}
procedure SwapB(var a,b:byte);
var
Temp:word;
begin
Temp:=a;
a:=b;
b:=Temp;
end; {SwapB}
{Draws a vertical line on the screen}
procedure VerticalLine(BeginX,BeginY,EndY:word);
var
y:word;
begin
if BeginY>EndY then swapW(BeginY,EndY);
for y:=BeginY to EndY do
putpixel(BeginX,y);
end; {VerticalLine}
{Draws a horizontal line on the screen really quickly}
procedure HorizontalLine(BeginX,EndX,BeginY:word);
var
t,size:word;
begin
t:=AsmLookUp[BeginY mod 200];
size:=abs(EndX-BeginX);
asm
mov es,[CurSeg]
mov di,[t]
add di,[BeginX]
add di,[CurOfs]
mov al,[CurrentDrawingColor]
mov cx,[size]
rep stosb
end; {asm}
end; {HorizontalLine}
{Draws a line from (x1,y1) to (x2,y2) using Bresenham's algorithm}
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 x1=x2 then
VerticalLine(x1,y2,y2)
else if y1=y2 then
HorizontalLine(x1,x2,y1)
else begin
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; {else}
end; {Line}
{Draws a filled box on the screen}
procedure Bar(BeginX,BeginY,EndX,EndY:word);
var
y,t,x:word;
size:word;
begin
AnnoyingErrorMessage;
size:=abs(EndX-BeginX);
for y:=BeginY to EndY do begin
t:=AsmLookUp[y];
asm
mov cx,[size]
mov al,[CurrentDrawingColor]
mov es,[CurSeg]
mov di,[t]
add di,[CurOfs]
add di,[BeginX]
rep stosb
mov di,[t]
add di,[CurOfs]
add di,[size]
add di,[BeginX]
stosb
end; {asm}
end; {for}
end; {Box}
{Draws a rectangle on the screen}
procedure Rectangle(BeginX,BeginY,EndX,EndY:word);
begin
AnnoyingErrorMessage;
HorizontalLine(BeginX,EndX,BeginY);
HorizontalLine(BeginX,EndX,EndY);
VerticalLine(BeginX,BeginY,EndY);
VerticalLine(EndX,BeginY,EndY);
end; {Rectangle}
{Allocates some memory and stores parts of the screen into it}
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;
size:=Image.DeltaX+1;
yImg:=0;
for y:=0 to Image.DeltaY do begin
YShl:=AsmLookUp[y+y1];
asm
mov cx,[size]
@1:
mov es,[CurSeg]
mov di,[CurOfs]
add di,[YShl]
add di,cx
add di,[x1]
dec di
mov al,es:[di]
mov es,[vSeg]
mov di,[vOfs]
add di,[YImg]
add di,cx
dec di
mov es:[di],al
dec cx
jnz @1
end; {asm}
yImg:=yImg+Image.DeltaX+1;
end; {for}
end; {GetImage}
{Transfers a stored image from memory to the screen}
procedure PutImage(Image:ImageType;x1,y1:word);
var
yImg,yShl:word;
x,y:word;
begin
yImg:=0;
for y:=0 to Image.DeltaY do begin
yShl:=AsmLookUp[y+y1];
asm
mov cx,[Image.DeltaX]
inc cx
@1:
mov es,[Image.vSegment]
mov di,[Image.vOffset]
add di,[yImg]
add di,cx
dec di
mov al,es:[di]
mov es,[CurSeg]
mov di,[CurOfs]
add di,[yShl]
add di,[x1]
add di,cx
dec di
mov es:[di],al
dec cx
jnz @1
end; {asm}
yImg:=yImg+Image.DeltaX+1;
end; {for}
end; {PutImage}
{Deallocates the memory used by the image}
procedure KillImage(var Image:ImageType);
begin
AnnoyingErrorMessage;
FreeMem(Image.Image,(Image.DeltaX+1)*(Image.DeltaY+1));
end; {KillImage}
{I can't remember the exp(ln) thing}
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}
{Converts a byte to binary notation (array of booleans)}
procedure ConvertDecimalToBinary(Something:byte;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}
{Draws a character on the screen from ROM}
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}
{Draws a string at (x,y), looping at the end of the screen}
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}
{Draws a string at the current text location, wrapping at the end}
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}
{Flips an image from left-right}
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}
{Loads a valid PCX file onto the screen}
procedure LoadPCX(var Image:PictureType;FileName:string);
var
fp:file;
numbytes,index,pSeg,pOfs,vSeg,vOfs:word;
count,imgSize,fpcnt,x,size:word;
dataval:byte;
ImagePtr:pointer;
TempColor:ColorType;
begin
assign(fp, FileName);
{$I-}
reset(fp,1);
{$I+}
if IOResult<>IOOk then begin
CloseGraph;
writeln('Error 111: You specified an invalid file name when calling LoadPCX');
halt;
end; {if}
size:=filesize(fp);
GetMem(ImagePtr,size);
blockread(fp,ImagePtr^,size);
vSeg:=seg(ImagePtr^); vOfs:=ofs(ImagePtr^);
new(Image.palette);
Image.Picture.DeltaX:=Mem[vSeg:vOfs+8] + (Mem[vSeg:vOfs+9] shl 8);
Image.Picture.DeltaY:=Mem[vSeg:vOfs+10] + (Mem[vSeg:vOfs+11] shl 8);
imgSize:=(Image.Picture.DeltaX+1)*(Image.Picture.DeltaY+1);
GetMem(image.picture.image,imgSize);
pSeg:=seg(Image.Picture.Image^); pOfs:=ofs(Image.Picture.Image^);
Image.Picture.vSegment:=pSeg; Image.Picture.vOffset:=pOfs;
count:=0;
fpcnt:=128;
while count<imgSize do begin
dataval:=Mem[vSeg:vOfs+fpcnt];
inc(fpcnt);
if (dataval>=192) and (dataval<=255) then begin
numbytes:=dataval-192;
dataval:=Mem[vSeg:vOfs+fpcnt];
inc(fpcnt);
asm
mov cx,[numbytes]
mov es,[pSeg]
mov di,[pOfs]
add di,[count]
mov al,[dataval]
rep stosb
end; {Asm}
count:=count+numbytes;
end {if}
else begin
asm
mov es,[pSeg]
mov di,[pOfs]
add di,[count]
mov al,[dataval]
mov es:[di],al
end; {asm}
count:=count+1;
end; {else}
end; {for}
fpcnt:=size-768;
for x:=0 to 255 do begin
index:=3*x;
TempColor.Red:=Mem[vSeg:vOfs+index+fpcnt] shr 2;
TempColor.Green:=Mem[vSeg:vOfs+index+1+fpcnt] shr 2;
TempColor.Blue:=Mem[vSeg:vOfs+index+2+fpcnt] shr 2;
image.palette^[x]:=TempColor;
end; {for}
FreeMem(ImagePtr,size);
close(fp);
end; {LoadPCX}
{Loads a valid TGA onto the screen}
procedure LoadTGA(var Image:PictureType;FileName:string);
var
fp:file;
TempPalAndHeadPtr:pointer;
TempImage:ImageType;
vSeg,vOfs,x:word;
TempColor:ColorType;
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}
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^,(Image.Picture.DeltaX+1)*(Image.Picture.DeltaY+1));
FreeMem(TempPalAndHeadPtr,786);
FlipImage(Image.Picture);
close(fp);
end; {LoadTGA}
{Draws a picture and a specifed part of the palette}
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}
{Deallocates the memory used by a picture}
procedure KillPicture(var Image:PictureType);
begin
KillImage(Image.Picture);
dispose(Image.Palette);
end; {KillPicture}
{Initializes the array structure for a gradated palette}
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}
{Adds a key point to the gradated palette}
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}
{Generates a palette based on the current key points}
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}
{Deallocates the memory used by a gradated palette}
procedure KillGradatedPalette(var SomePalette:GradatedPaletteType);
begin
AnnoyingErrorMessage;
dispose(SomePalette);
end; {KillGradatedPalette}
{Initalizes the characteristics of a fire}
procedure InitFire(var SomeFire:FireType;x1,y1,x2,y2:word;BeginPal,EndPal:byte);
var
Test:GradatedPaletteType;
Temp:ColorType;
x:byte;
ymov,tymov:word;
begin
InitGradatedPalette(Test);
Temp.Red:=63; Temp.Green:=63; Temp.Blue:=0;
AddKeyPoint(Test,EndPal,Temp);
Temp.Red:=63; Temp.Green:=0; Temp.Blue:=0;
AddKeyPoint(Test,(BeginPal+EndPal) div 2,Temp);
Temp.Red:=0; Temp.Green:=0; Temp.Blue:=0;
AddKeyPoint(Test,BeginPal,Temp);
GeneratePalette(Test);
SomeFire.c:=y1+abs(y2-y1) div 2;
SomeFire.last:=AsmLookUp[y2];
SomeFire.x1:=x1; SomeFire.y1:=y1;
SomeFire.x2:=x2; SomeFire.y2:=y2;
SomeFire.PalBegin:=BeginPal;
SomeFire.PalEnd:=EndPal;
SomeFire.PalLen:=abs(BeginPal-EndPal);
end; {InitFire}
{Actually draws one iteration of the fire onto the screen}
procedure DoFire(SomeFire:FireType);
var
x,y,cury,curyn:word;
t1,t2,t:byte;
begin
cury:=somefire.last;
for x:=SomeFire.x1 to SomeFire.x2 do begin
t:=random(SomeFire.PalLen)+SomeFire.PalBegin;
asm
mov es,[CurSeg]
mov di,[Cury]
add di,[CurOfs]
add di,[X]
mov al,[t]
mov es:[di],al
end; {asm}
end; {for}
for y:=SomeFire.y2-1 downto SomeFire.y1+1 do begin
cury:=AsmLookUp[y];
curyn:=AsmLookUp[y+1];
for x:=SomeFire.x1+1 to SomeFire.x2-1 do begin
asm
mov es,[CurSeg]
mov di,[curyn]
add di,[X]
add di,[CurOfs]
mov al,es:[di-1]
mov [t1],al
mov al,es:[di+1]
mov [t2],al
end;
t:=(t1+t2) shr 1;
if (t>0) and (y<SomeFire.c) then t:=t-1;
if t<SomeFire.PalBegin then t:=SomeFire.PalBegin;
asm
mov es,[CurSeg]
mov di,[cury]
add di,[CurOfs]
add di,[X]
mov al,[t]
mov es:[di],al
end; {asm}
end; {for}
end; {for}
end; {DoFire}
{Clears the screen really quickly}
procedure ClearScreen;
begin
CurrentTextX:=0;
CurrentTextY:=0;
asm
mov ax,0
mov es,[CurSeg]
mov di,[CurOfs]
{A word is two bytes}
mov cx,32000
rep stosw
end; {asm}
end; {ClearScreen}
{Initaizlizes the dynamic music used by a page}
procedure InitPage(var SomePage:PageType);
var
vseg,vofs:word;
begin
new(SomePage.page);
SomePage.Segment:=seg(SomePage.Page^);
SomePage.Offset:=ofs(SomePage.Page^);
vseg:=SomePage.Segment;
vofs:=SomePage.Offset;
asm
mov ax,0
mov es,[vseg]
mov di,[vofs]
mov cx,32000
rep stosw
end; {asm}
end; {InitPage}
{Sets the active page}
procedure SetActivePage(SomePage:PageType);
begin
ActivePage:=SomePage;
CurSeg:=SomePage.Segment;
CurOfs:=SomePage.Offset;
end; {SetActivePage}
{Copies the contents of a page into another page}
procedure CopyPage(Source:PageType;var Destination:PageType);
var
v1seg,v1ofs,v2seg,v2ofs:word;
begin
v1Seg:=Source.Segment; v1ofs:=Source.Offset;
v2Seg:=Destination.segment; v2ofs:=Destination.offset;
asm
mov cx,64000
mov bx,[v1ofs]
mov dx,[v2ofs]
@1:
mov es,[v1seg]
mov di,cx
add di,bx
mov al,es:[di]
mov es,[v2seg]
mov di,cx
add di,dx
mov es:[di],al
dec cx
jnz @1
end; {asm}
end; {CopyPage}
{Optimized for copying the contents of a page into the VGA memory}
procedure CopyPageToVGA(SomePage:PageType);
var
vaddr:word;
begin
vaddr:=SomePage.segment;
asm
mov cx,64000
mov bx,OFFSET [SomePage]
@1:
mov es,[vaddr]
mov di,cx
add di,bx
mov al,es:[di]
mov es,[VGA]
mov es:[di],al
dec cx
jnz @1
end; {asm}
end; {CopyPageToVGA}
{Copies the contents of the Video buffer into a page}
procedure CopyVGAToPage(var SomePage:PageType);
var
vaddr:word;
begin
vaddr:=SomePage.Segment;
asm
mov cx,64000
mov bx,OFFSET [SomePage]
@1:
mov es,[VGA]
mov al,es:[di]
mov es,[vaddr]
mov di,cx
add di,bx
mov es:[di],al
dec cx
jnz @1
end; {asm}
end; {CopyPageToVGA}
{Disposes the dynamic memory used by the paging procedures}
procedure KillPage(SomePage:PageType);
begin
dispose(SomePage.Page);
end; {KillPage}
{Initializes the linked-list structure used by the maps}
procedure InitMap(var SomeMap:MapType);
begin
SomeMap.First:=nil;
SomeMap.Last:=nil;
SomeMap.NumItems:=0;
InitPage(SomeMap.Render);
SomeMap.Rendered:=FALSE;
end; {InitMap}
{Adds a hot area, defined by four coordinates, to the linked list}
procedure AddHotArea(var SomeMap:MapType;Name:string;bx1,by1,ex1,ey1,bx2,by2,ex2,ey2:word);
var
Temp:MapNodePtr;
TempCord:CordType;
begin
new(Temp);
{nothing in the list}
if SomeMap.First=nil then begin
SomeMap.First:=Temp;
SomeMap.Last:=Temp;
end {if}
else
SomeMap.Last^.next:=Temp;
SomeMap.Last:=Temp;
Temp^.next:=nil;
Temp^.Name:=Name;
TempCord.x:=bx1; TempCord.y:=by1;
Temp^.b1:=TempCord;
TempCord.x:=ex1; TempCord.y:=ey1;
Temp^.e1:=TempCord;
TempCord.x:=bx2; TempCord.y:=by2;
Temp^.b2:=TempCord;
TempCord.x:=ex2; TempCord.y:=ey2;
Temp^.e2:=TempCord;
SomeMap.NumItems:=SomeMap.NumItems+1;
Temp^.number:=SomeMap.NumItems;
end; {AddHotArea}
{Checks and sees if the mouse is inside some area}
function CheckIfMouseInside(CurrentNode:MapNodeType):boolean;
begin
CheckIfMouseInside:=(GetPixel(GetMouseX,GetMouseY)=CurrentNode.Number) and ButtonDown;
end; {CheckIfMouseInside}
{For efficency, the image mapping procedures use a page that denotes
the pre-rendered locations of each hot-area}
procedure RenderHotAreas(var SomeMap:MapType);
var
Temp:MapNodePtr;
OldPage:PageType;
OldSeg,OldOfs:word;
OldColor:byte;
begin
OldColor:=GetColor;
OldSeg:=CurSeg; OldOfs:=CurOfs;
OldPage:=ActivePage;
SetActivePage(SomeMap.Render);
Temp:=SomeMap.First;
{There are only 256 colors in the display, so you will get some
errors if you try to add more than 256 hot-areas}
while (Temp<>nil) do begin
setcolor(Temp^.number);
line(Temp^.b1.x,Temp^.b1.y,Temp^.e1.x,Temp^.e1.y);
line(Temp^.e1.x,Temp^.e1.y,Temp^.b2.x,Temp^.b2.y);
line(Temp^.b2.x,Temp^.b2.y,Temp^.e2.x,Temp^.e2.y);
line(Temp^.e2.x,Temp^.e2.y,Temp^.b1.x,Temp^.b1.y);
Fill((Temp^.b1.x+Temp^.e1.x) div 2,(Temp^.b1.y+Temp^.b2.y) div 2,Temp^.number,Temp^.number);
Temp:=Temp^.next;
end; {while}
ActivePage:=OldPage;
CurSeg:=OldSeg;
CurOfs:=OldOfs;
SetColor(OldColor);
end; {RenderHotAreas}
{Sees if the user has clicked on a hot area}
function PollHotAreas(SomeMap:MapType):string;
var
Temp:MapNodePtr;
OldPage:PageType;
OldSeg,OldOfs:word;
begin
OldSeg:=CurSeg; OldOfs:=CurOfs;
OldPage:=ActivePage;
SetActivePage(SomeMap.Render);
Temp:=SomeMap.First;
while (Temp<>nil) and not CheckIfMouseInside(Temp^) do
Temp:=Temp^.next;
if Temp=nil then
PollHotAreas:=Nothing
else
PollHotAreas:=Temp^.name;
ActivePage:=OldPage;
CurSeg:=OldSeg; CurOfs:=OldOfs;
end; {PollHotAreas}
{Deallocates the dynamic memory used in the mapping procedures}
procedure KillMap(var SomeMap:MapType);
var
TempPtr,NextPtr:MapNodePtr;
begin
TempPtr:=SomeMap.First;
while (TempPtr<>nil) do begin
NextPtr:=TempPtr^.next;
dispose(TempPtr);
TempPtr:=NextPtr;
end; {while}
KillPage(SomeMap.Render);
end; {KillMap}
{Mirrors an image from top-bottom}
procedure MirrorImage(var SomeImage:ImageType);
var
TempImage:ImageType;
p1Seg,p1Ofs,p2Seg,p2Ofs,HalfX,x,y:word;
DeltaXPlus,DeltaYPlus:word;
Temp:byte;
begin
DeltaXPlus:=SomeImage.DeltaX+1; DeltaYPlus:=SomeImage.DeltaY+1;
HalfX:=((DeltaXPlus) div 2)-1;
for x:=0 to HalfX do begin
for y:=0 to SomeImage.DeltaY do begin
p1seg:=SomeImage.vSegment;
p1ofs:=SomeImage.vOffset+(y*(DeltaXPlus))+x;
p2seg:=SomeImage.vSegment;
p2ofs:=SomeImage.vOffset+(y*(DeltaXPlus))+abs(SomeImage.DeltaX-x);
Temp:=Mem[p1seg:p1ofs];
Mem[p1seg:p1ofs]:=Mem[p2seg:p2ofs];
Mem[p2seg:p2ofs]:=Temp;
end; {for}
end; {for}
end; {MirrorImage}
{Initializes the movie doubly linked-list structure}
procedure InitMovie(var SomeMovie:MovieType);
begin
SomeMovie.Front:=nil;
SomeMovie.Last:=nil;
SomeMovie.NumFrames:=0;
end; {InitMovie}
{Adds a frame to the end of the movie linked list}
procedure AddSomeFrame(var SomeMovie:MovieType;x1,y1,x2,y2:word);
var
TempPtr:MovieNodePtr;
TempColor:ColorType;
x:word;
begin
new(TempPtr);
new(TempPtr^.Palette);
GetImage(TempPtr^.Picture,x1,y1,x2,y2);
for x:=0 to 255 do
getpalette(x,TempPtr^.Palette^[x]);
TempPtr^.next:=nil;
{No items in the list}
if (SomeMovie.Front=nil) then begin
SomeMovie.Front:=TempPtr;
TempPtr^.Prev:=nil;
end {if}
{Otherwise, do stuff}
else begin
SomeMovie.Last^.next:=TempPtr;
TempPtr^.Prev:=SomeMovie.Last;
end; {else}
SomeMovie.Last:=TempPtr;
SomeMovie.NumFrames:=SomeMovie.NumFrames+1;
end; {AddSomeFrame}
{Waits until the video card has completed the vertical retrace}
procedure WaitRetrace; assembler;
label l1, l2;
asm
mov dx,3DAh
l1:
in al,dx
and al,08h
jnz l1
l2:
in al,dx
and al,08h
jz l2
end; {WaitRetrace}
{Draws a frame on the screen}
procedure DrawSomething(SomeFrame:MovieNodePtr;pX,pY:word;UsePalette:boolean);
var
x:byte;
begin
WaitRetrace;
PutImage(SomeFrame^.Picture,pX,pY);
if UsePalette then begin
for x:=0 to 255 do begin
SetPalette(x,SomeFrame ^.Palette^[x]);
end; {for}
end; {if}
end; {DrawSomething}
{Plays a movie in the specified direction}
procedure PlayMovie(SomeMovie:MovieType;pX,pY:word;UsePalette:boolean;Direction:DirectionType);
var
x:integer;
TempPtr:MovieNodePtr;
begin
if Direction=Foreward then begin
TempPtr:=SomeMovie.Front;
for x:=1 to SomeMovie.NumFrames do begin
DrawSomething(TempPtr,pX,pY,UsePalette);
TempPtr:=TempPtr^.next;
end; {for}
end {if}
else if Direction=Backward then begin
TempPtr:=SomeMovie.Last;
for x:=1 to SomeMovie.NumFrames do begin
DrawSomething(TempPtr,pX,pY,UsePalette);
TempPtr:=TempPtr^.prev;
end; {for}
end; {if}
end; {PlayMovie}
{Disposes the dynamic memory used by the movie procedures}
procedure KillMovie(var SomeMovie:MovieType);
var
TempPtr,NextPtr:MovieNodePtr;
begin
TempPtr:=SomeMovie.Front;
while (TempPtr<>nil) do begin
NextPtr:=TempPtr^.next;
dispose(TempPtr);
TempPtr:=NextPtr;
end; {while}
SomeMovie.Front:=nil;
SomeMovie.Last:=nil;
SomeMovie.NumFrames:=0;
end; {KillMovie}
{Draws a pixel at the current position at at all the other surreounding
positions}
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}
{The main procedures the initiates the recursive filling}
procedure Fill(x,y:word;FillColor,Border:byte);
var
OldColor:byte;
begin
OldColor:=GetColor;
setcolor(FillColor);
RecursiveFill(x,y,FillColor,Border);
SetColor(OldColor);
end; {Fill}
begin
{The default is no initialization}
HasBeenInitialized:=FALSE;
end. {Graphics}