[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]