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