[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]
{$A+,B-,D+,E+,F-,G+,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V-,X+,Y+}
{$M 16384,0,32786}
Program BitMap; { rotates/pans/scales a 256x256 bitmap }
USES CRT; { by Paul H. Kahler Jan 1994 }
Var SinTable,CosTable: Array[0..255] of integer;
Sin2Table,Cos2Table: Array[0..255] of integer;
Map:word; {used as a pointer to the bitmap}
Procedure MakeTables; {Creates sin/cos tables}
Var direction:integer;
angle:real;
begin
For Direction:=0 to 255 do begin {use 256 degrees in circle}
angle:=Direction;
angle:=angle*3.14159265/128;
SinTable[Direction]:=round(Sin(angle)*256);
CosTable[Direction]:=round(Cos(angle)*256);
Sin2Table[Direction]:=round(Sin(angle+3.14159265/2)*256*1.2);
Cos2Table[Direction]:=round(Cos(angle+3.14159265/2)*256*1.2);
end; { the 1.2 accounts for pixel aspect ratio }
end;
Procedure DrawScreen(x,y,scale:word; rot:byte);
var Temp:Longint; {used for intermediate large values}
ddx,ddy,d2x,d2y:integer;
i,j:word;
label hloop,vloop,nodraw;
begin
{ the following 8 lines of code calculate a 'right' and 'down' vector used
for scanning the source bitmap. I use quotes because these directions
depend on the rotation. For example, with a rotation, 'right' could mean
up and to the left while 'down' means up and to the right. Since the
destination image (screen) is scanned left-right/top-bottom, the bitmap
needs to be scanned in arbitrary directions to get a rotation. }
Temp:=(CosTable[rot]);Temp:=(Temp*Scale) div 256;
ddx:=Temp;
Temp:=(SinTable[rot]);Temp:=(Temp*Scale) div 256;
ddy:=Temp;
{ Different tables are used for the 'down' vector to account for the non-
square pixels in mode 13h (320x200). The 90 degree difference is built
into the tables. If you don't like that, then use (rot+64)and255 here
and take the pi/2 out of CreateTables. To each his own I guess. }
Temp:=(Cos2Table[rot]);Temp:=(Temp*SCALE) div 256;
d2x:=Temp;
Temp:=(Sin2Table[rot]);Temp:=(Temp*SCALE) div 256;
d2y:=Temp;
{ Since we want to rotate around the CENTER of the screen and not the upper
left corner, we need to move 160 pixels 'left' and 100 'up' in the bitmap.}
i:=x-ddx*160-d2x*100; j:=y-ddy*160-d2y*100;
{ The following chunk of assembly does the good stuff. It redraws the entire
screen by scanning left-right/top-bottom on screen while also scanning the
bitmap in the arbitrary directions determined above. }
ASM
push ds
mov ax,[Map] {get segment of bitmap}
mov ds,ax
mov ax,$a000 {set es: to video memory}
mov es,ax
mov ax,0 {set ds: to upper left corner of}
mov di,ax {the video memory}
mov ax,[ddx] {this is just to speed things up later}
mov si,ax {add ax,si faster than add ax,[ddx] }
mov cx,200 {Number of rows on Screen}
vloop:
push cx
mov ax,[i] {start scanning the source bitmap}
mov dx,[j] {at i,j which were calculated above.}
mov cx,320 {Number of coulumns on screen}
hloop:
add ax,si {add the 'right' vector to the current}
add dx,[ddy] {bitmap coordinates. 8.8 fixed point}
mov bl,ah { bx = 256*int(y)+int(x) }
mov bh,dh
mov bl,[ds:bx] { load a pixel from source }
mov [es:di],bl { copy it to destination }
inc di { advance to next destination pixel }
{*** by repeating the above 7 instructions 5 times, and reducing
the loop count to 64, I have hit 37fps on a 486-33 with a
fast video card. ***}
loop hloop {End of horizontal loop}
mov ax,d2x { get the 'down' vector }
mov dx,d2y
{ add si,2 } {** uncomment this instr. for extra fun **}
add i,ax { i,j is the starting coords for a line }
add j,dx { so this moves down one line }
pop cx { get the row count back and loop }
loop vloop { End of verticle loop }
pop ds { Restore the ds }
end;
end;
Procedure GraphMode; {start 320x200x256 mode}
begin
Asm
Mov AH,00
Mov AL,13h
Int 10h
end;
end;
Procedure AllocateMem; {returns a segment pointer for a 64K bitmap}
label noerror;
begin
asm
mov ah,$48
mov bx,$1000 { request 64K }
int $21
jnc noerror
mov ax,0000
noerror: mov Map,ax { The segment pointer goes in Map }
end;
If Map=0 then begin
Writeln('Could not allocate enough memory');
Writeln('Program ending...');
Halt;end;
end;
Procedure GiveBackMem; {returns the memory used for the map to the system}
begin
asm
mov ah,$49
mov dx,Map
mov es,dx
int $21
end;
end;
Procedure DrawImage; {draws a test image which shows some limitations.}
{ If anyone stuffs in code to load a picture in a standard format
(ie .gif .bmp etc..) I'd like if you send me a copy. Preferably
something simple. This will have to do for now. }
Var x,y:integer;
Begin
for x:=-32768 to 32767 do mem[Map:x]:=0;
for y:=0 to 15 do {this just frames the area}
for x:=y to 255 do begin
mem[Map:Y*256+x]:=1;
mem[Map:X*256+y]:=2;
end;
for y:=16 to 47 do { this part show Aliasing effects }
for x:=16 to 255 do mem[Map:Y*256+x]:=2+(x and 1)+(y and 1);
for y:= -50 to 50 do { this draw the circles }
for x:= round(-sqrt(2500 - y*y)) to round(sqrt(2500 - y*y)) do
mem[Map:(y+100)*256+x+100]:=5+(X*X+Y*Y) div 100;
for x:=0 to 100 do { These lines also show sampling effects }
for y:=0 to 8 do
mem[Map:(Y*2560)+x+41100]:=5;
end;
Var rot,dr:word;
x,y,dist,dd:word;
Begin
AllocateMem;
DrawImage;
MakeTables;
GraphMode;
x:=32768; y:=0; {this corresponds to (128,0) in fixed point}
rot:=0; dr:=1; {rotation angle and it's delta}
dist:=1200; dd:=65534; {distance to bitmap (sort of) and its delta}
repeat
DrawScreen(x,y,dist,lo(rot));
rot:=rot+dr;
y:=y+128; {slow panning. 1/2 pixel per frame}
dist:=dist+dd;
if (dist=2000) or (dist=2) then dd:=-dd;
if random(150)=1 then dr:=random(7)-3;
until keypressed;
GiveBackMem;
ASM {back to 80x25}
MOV AX,3
INT 10h
END;
end.
[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]