[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]


{Well, I've also written (rewritten from something in SWAG) a GIF decoder, but
converted it to ASM, so it's quite fast :-)I know GIF should be ignored, but I
wanted to write an asm version, after understanding the LZW algorithm, to see
how fast it could be (however it isn't much optimized). }

=== UNGIF.PAS
{ Decode a standard 320x200, non interlaced, no local color map GIF }
{ Arne de Bruijn, 1995, PD }
{$G+}
const MaxBufSize=32768;
var
 H:file;
 BufPtr,CPtr:pointer; BufEnd:word; { Read data pointers }
 WasLastBlock:boolean;
 MYBuf,MBPtr:pointer; MBEnd:word;  { Unblocked data pointers }

procedure ReadBuf;
{ Read from file into BufPtr and set CPtr the begin buffer, and BufEnd to end
}var
 BufSize:word;
begin
 CPtr:=BufPtr;
 BlockRead(H,BufPtr^,MaxBufSize,BufSize);
 BufEnd:=word(BufPtr)+BufSize;
 WasLastBlock:=BufSize<MaxBufSize;
end;

procedure UnBlock;
{ Convert <1 byte blocklen> <block> stream in CPtr-BufEnd to }
{ 'normal' stream in MyBuf }
const
 InBlockLast:byte=0;
var
 InBlock:word;
begin
 InBlock:=InBlockLast;
 InBlockLast:=0;
 MBPtr:=MyBuf;
 repeat
  if word(CPtr)+InBlock>BufEnd then
   begin
    InBlockLast:=InBlock;
    InBlock:=BufEnd-word(CPtr);
    Dec(InBlockLast,InBlock);
   end;
  Move(CPtr^,MBPtr^,InBlock);
  Inc(word(CPtr),InBlock); Inc(word(MBPtr),InBlock);
  InBlock:=0;
  if word(CPtr)<BufEnd then
   begin
    InBlock:=byte(CPtr^); Inc(word(CPtr));
   end;
 until (InBlock=0) and (word(CPtr)=BufEnd);
 MBEnd:=word(MBPtr);
 MBPtr:=MyBuf;
end;

procedure DecodeGIF(XOff,YOff,XLen,YLen:word; IBits:byte);
{ XOff,YOff,XLen,YLen ignored }
var
 MaxCode,OldCode,FirstItem,FreeItem,InCode,LastCode:word;
 StartCodeSize,CodeSize:byte;
 PixelBuf:array[0..1023] of byte;
 HashVal:array[0..4095] of byte;
 HashPrev:array[0..4095] of word;
 VioP:pointer;
 MBitMask:byte; MMBPtr:pointer; MMBEnd:word;
 L:longint;
begin
 StartCodeSize:=byte(CPtr^); Inc(word(CPtr));
 FirstItem:=1 shl StartCodeSize; Inc(StartCodeSize);
 GetMem(MyBuf,MaxBufSize); UnBlock;
 { Copy vars to local vars, so they can be accessed if ds is modified }
 LastCode:=0; VioP:=ptr($a000,0); MBitMask:=0; MMBPtr:=MBPtr; MMBEnd:=MBEnd;
 asm
  push ds
  push ss; pop es;
 @DoClr:
  mov cl,StartCodeSize   { Initialize vars }
  mov [CodeSize],cl; mov ax,1; shl ax,cl; mov [MaxCode],ax
  mov ax,[FirstItem]; add ax,2; mov [FreeItem],ax
  mov [OldCode],-1
 @LoopIt:                { Start loop }
  lds si,MMBPtr
  lea di,PixelBuf
  mov ax,[si]; mov dx,[si+2]
 @ReLdCont:
  mov cl,[MBitMask]      { Get CodeSize bits }
  mov bx,-1; shr bx,cl; shr ax,cl; not bx
  ror dx,cl; mov ch,CodeSize
  and bx,dx; or ax,bx
  xor bx,bx; add cl,ch; mov bl,cl; and cl,7; shr bx,3; add si,bx
  cmp si,[MMBEnd]; jae @Reload   { Buffer empty? Yes -> Reload }
  mov word ptr [MMBPtr],si; mov [MBitMask],cl
  mov cl,ch; mov bx,1; shl bx,cl; dec bx; and ax,bx
  mov cx,[FirstItem]     { Is it a clear code }
  sub cx,ax; je @DoClr   { Yes -> Initialize vars }
  inc cx; je @End        { Is it an end code (=clear code +1) ?  Yes -> End}
  mov [InCode],ax
  cmp ax,[FreeItem]      { Is it an unknown code ? }
  jb @Known
  mov ax,[OldCode]       { Append last code to output, and use prev code }
  mov cl,byte ptr [LastCode]
  mov es:[di],cl; inc di
 @Known:
  mov si,ax
 @ScanCode:              { Scan through codes loop }
  cmp si,[FirstItem]     { Is it a normal code? (= 1 output byte) }
  jb @EndPut             { Yes -> end loop }
  cmp si,[FreeItem]      { Is it an unknown code? }
  jae @Abort             { Yes -> abort }
  mov al,byte ptr [HashVal+si]
  mov es:[di],al; inc di
  shl si,1; mov si,word ptr [HashPrev+si]
  jmp @ScanCode
 @EndPut:
  mov ax,si; mov [LastCode],ax
  mov es:[di],al; inc di
  cmp byte ptr [OldCode+1],255; je @SkipStore
  mov si,[FreeItem];
  cmp si,4095; ja @SkipStore
  mov byte ptr [HashVal+si],al
  mov ax,[OldCode]
  mov bx,si; shl si,1; mov word ptr [HashPrev+si],ax
  inc bx; mov [FreeItem],bx
 @SkipStore:             { Check if codesize must be incremented }
  cmp bx,[MaxCode]; jb @NoNewCode
  cmp [CodeSize],12; jae @NoNewCode
  inc [CodeSize]; shl [MaxCode],1
 @NoNewCode:
  mov ax,[InCode]; mov [OldCode],ax
  lea cx,PixelBuf        { Write stored output to screen }
  neg cx; add cx,di
  lds si,VioP
 @PutIt:
  dec di                 { Output stored reversed }
  mov al,es:[di]; mov [si],al
  inc si; dec cx
  jnz @PutIt
  mov word ptr [VioP],si
  jmp @LoopIt
 @Abort:                 { Labels here to allow short jumps }
 @End:
  jmp @REnd
 @Reload:
  mov si,word ptr [MMBPtr]; mov bx,[MMBEnd]; sub bx,si
  mov ax,[si]; add si,2
  mov word ptr [L],ax    { Save last 4 bytes of buffer }
  mov ax,[si]; mov word ptr [L+2],ax
  pop ds
  cmp [WasLastBlock],0   { Already at eof? }
  jne @AbortNoPop        { Yes -> Abort }
  push bx
  call ReadBuf
  call Unblock
  mov ax,[MBEnd]; mov [MMBEnd],ax
  mov si,word ptr [MBPtr]
  pop bx
  push ds
  mov ds,word ptr [MMBPtr+2]
  push ss; pop es        { Overwrite temp buf (L) with new values }
  lea di,L               { at the right place }
  add di,bx; mov ax,si; sub ax,bx; mov cx,4; sub cx,bx
  cld;  rep movsb
  mov si,ax              { Initialize vars for @ReLDCont }
  lea di,PixelBuf
  mov ax,word ptr [L]; mov dx,word ptr [L+2]
  jmp @ReLDCont
 @REnd:
  pop ds
 @AbortNoPop:
 end;
 FreeMem(MyBuf,MaxBufSize);
end;

type
 TGifID=array[0..5] of char;
 TGifScrDesc=record ScrWidth,ScrHeight:word; Misc,Background,Zero:byte; end;
const GifID:TGifID='GIF87a';
var
 IBits,I,Misc:byte;
 IWidth,IHeight,IColors:word; XOff,YOff,XLen,YLen:word;
 GifPal:pointer;
begin
 if ParamCount=0 then begin WriteLn('Specify filename!'); Halt(1); end;
 assign(H,ParamStr(1)); reset(H,1);
 if IOResult<>0 then begin WriteLn('Open error'); Halt; end;
 GetMem(BufPtr,MaxBufSize); ReadBuf;
 if TGifID(BufPtr^)<>GifID then begin WriteLn('Bad image!'); Halt; end;
 Inc(word(CPtr),SizeOf(TGifID));
 IWidth:=TGifScrDesc(CPtr^).ScrWidth; IHeight:=TGifScrDesc(CPtr^).ScrHeight;
 IBits:=TGifScrDesc(CPtr^).Misc and 7+1; IColors:=1 shl IBits;
 IWidth:=TGifScrDesc(CPtr^).ScrWidth;
 if TGifScrDesc(CPtr^).Misc and 128<>128 then
  begin WriteLn('No GCT in GIF!'); Halt; end;
 Inc(word(CPtr),SizeOf(TGifScrDesc));
 asm
  mov ax,13h; int 10h  { Enter graphics mode }
  cld                  
  push ds
  mov ax,IColors; mov cx,ax; add ax,ax; add cx,ax  { cx:=IColors*3 }
  lds si,CPtr
  mov dx,3c8h; xor al,al; out dx,al; inc dx        { Set port, start color }
 @AdjCol: lodsb; shr al,2; out dx,al; loop @AdjCol { Set palette }
  pop ds
 end;
 Inc(word(CPtr),IColors*3);
 repeat
  I:=byte(CPtr^); Inc(word(CPtr));
  if I=44 then begin
    XOff:=word(CPtr^);Inc(word(CPtr),2); YOff:=word(CPtr^);Inc(word(CPtr),2);
    XLen:=word(CPtr^);Inc(word(CPtr),2); YLen:=word(CPtr^);Inc(word(CPtr),2);
    Misc:=byte(CPtr^);Inc(word(CPtr),1);
    DecodeGIF(XOff,YOff,XLen,YLen,IBits);
   end;
 until (I<>44) or (word(CPtr)>=BufEnd);
 Close(H);
 FreeMem(BufPtr,MaxBufSize);
 asm mov ah,0; int 16h; end; { Wait for key }
 asm mov ax,3; int 10h; end; { Set text mode }
end.

[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]