[comp.sys.atari.st] GIF decompressor in GFA Basic 3.0

logajan@ns.network.com (John Logajan) (06/07/91)

REM  This GFA Basic 3.0 program unpacks the images in GIF files, creating RGB
REM  color tables, and an uncompressed image array.  Each byte in the image
REM  array points into the intensity values in the RGB color tables.  This means
REM  the image can contain only 256 different colors, but those colors can be
REM  selected from a palette of 16.7 million.
REM
REM  *THIS IS NOT A COMPLETE PROGRAM*  It does not display the GIF images.
REM  I leave that up to you to figure out how to do.  Good luck.  By the way,
REM  this is pretty slow.  It does run much faster if compiled.
REM
REM  Totally original code by John Logajan, June 6, 1991.  I don't know what
REM  legal status it has, as CompuServe owns GIF, and I've heard rumors that
REM  the owners of the LZW compression routines aren't happy with CompuServe.
REM  If it was up to me, this code would be released into the public domain.
REM
INPUT "Filename: ",a$
OPEN "I",#1,a$
DIM pcl|(260),prep&(4097),scar|(4097),temp|(4097)
pcp%=VARPTR(pcl|(0))
sig$=""
version$=""
FOR j&=1 TO 3
  sig$=sig$+CHR$(INP(#1))
NEXT j&
FOR j&=1 TO 3
  version$=version$+CHR$(INP(#1))
NEXT j&
IF sig$="GIF"                               !  Check for GIF signature.
  swidth&=INP(#1)+INP(#1)*256               !  Some sort of screen size
  sheight&=INP(#1)+INP(#1)*256              !  neither of which I use.
  tmp|=INP(#1)
  global!=BTST(tmp|,7)                      ! Check for Global Color Map.
  gpixel&=(tmp| AND 7)+1                    ! Global bits per pixel.
  gcolres&=(SHR|(tmp|,4) AND 7)+1           ! Color range. I don't use.
  gbackground&=INP(#1)                      ! Background color. I don't use.
  tmp|=INP(#1)
  IF global!                                  ! We have a Global Color Map.
    crange&=SHL(1,gpixel&)                    ! Color range.
    pixel&=gpixel&                            ! Bits per pixel.
    DIM r|(crange&),g|(crange&),b|(crange&)   ! RGB color map reserved.
    FOR j&=0 TO crange&-1                     ! Go gety the map RGB values.
      r|(j&)=INP(#1)
      g|(j&)=INP(#1)
      b|(j&)=INP(#1)
    NEXT j&
  ENDIF
  REPEAT                             ! Main Image(s) Loop
    sep|=INP(#1)
    IF sep|=&H21                     ! Throw away any Extension Blocks
      tmp|=INP(#1)
      DO
        bc|=INP(#1)
        EXIT IF bc|=0
        FOR j&=1 TO bc|
          tmp|=INP(#1)
        NEXT j&
      LOOP
      sep|=INP(#1)
    ENDIF
    IF sep|=&H2C                            ! Look for Image Descriptor Start
      offleft&=INP(#1)+INP(#1)*256          ! Some sort of offset, I don't use.
      offtop&=INP(#1)+INP(#1)*256           ! I don't use.
      width&=INP(#1)+INP(#1)*256            ! Image width (pixels)
      height&=INP(#1)+INP(#1)*256           ! Image height (pixels)
      imglen%=width&*height&                ! Total pixels in the image.
      DIM pic|(imglen%+256)                 ! Reserve image space + spare room.
      pici%=0
      tmp|=INP(#1)
      uselocal!=BTST(tmp|,7)                ! Look for Local Color Map
      interlace!=BTST(tmp|,6)               ! See if image is interlaced.
      lpixel&=(tmp| AND 7)+1                ! Local bits per pixel.
      pass|=0
      hline&=0
      vcol&=0
      lstep&=8
      IF uselocal!                           ! We have a Local Color Map
        crange&=SHL(1,lpixel&)               ! Color range
        pixel&=lpixel&                       ! Bits per pixel
        IF NOT global!
          DIM r|(crange&),g|(crange&),b|(crange&) ! Reserve room for color map.
        ENDIF
        FOR j&=0 TO crange&-1                ! Go fill local color map.
          r|(j&)=INP(#1)
          g|(j&)=INP(#1)
          b|(j&)=INP(#1)
        NEXT j&
      ENDIF
      PRINT crange&;" colors."
      PRINT width&;" * ";height&
      REM
      REM This is the LZW decompresser
      REM
      initcodesize&=INP(#1)              ! How many bits in first code.
      clearcode&=SHL(1,initcodesize&)    ! Now we know the clear code.
      eofcode&=clearcode&+1              ! Now we know the eof code.
      firstfree&=clearcode&+2            ! Where to put first incoming code.
      FOR j&=0 TO clearcode&-1           ! We put roots into string table.
        REM Prep&() entries point to the previous character
        prep&(j&)=5000                   ! Are roots so we make them too big.
        REM scar|() entries are the actual string value at that point
        scar|(j&)=j&
      NEXT j&
      INC initcodesize&
      cbp&=0
      cpt&=0
      lpt&=0
      GOSUB codeclear                    ! Let's get started.
      REPEAT                             ! Do a whole compressed image.
        GOSUB getcode                    ! Get a code.
        IF code&=clearcode&              ! Always test it for clear code.
          GOSUB codeclear
        ELSE
          IF code&<freecode&             ! We have this code.
            GOSUB codeout                ! Expand it.
            prep&(freecode&)=oldcode&    ! And save it as next code,
            scar|(freecode&)=pscar|      !   appending current character.
          ELSE                           ! We don't have this code.
            prep&(freecode&)=oldcode&    ! So, save it,
            scar|(freecode&)=pscar|      !   appending previous first character.
            GOSUB codeout                ! Expand it.
          ENDIF
          INC freecode&                  ! Get ready for next.
          oldcode&=code&                 ! Remember last code.
          IF freecode&>=maxcode&         ! Look out for variable sized codes.
            IF codesize&<12              ! Don't go over 12bit limit.
              INC codesize&              ! Adjust code size up one bit.
              maxcode&=SHL(1,codesize&)
              readmask&=maxcode&-1
            ENDIF
          ENDIF
        ENDIF
      UNTIL code&=eofcode&               ! We're out'a here.
      sep|=INP(#1)
    ENDIF
  UNTIL sep|=&H3B                     ! Look for GIF Terminator
  REM
  PRINT "Your code goes here."
  REM
  REM pic|() contains image, one byte per pixel -- pointing into RGB maps.
  REM The image data in pic|() is left to right, top to bottom
  REM i.e. The first 640 bytes of a 640 pixel wide image make up the first
  REM      horizontal line, the next 640 bytes make up the second line...
  REM width& = number of horizontal pixels (bytes)
  REM height& = number of vertical pixels (bytes)
  REM r|()   contains the red level values 0-255 intensities.
  REM g|()   contains the green level values 0-255 intensities.
  REM b|()   contains the blue level values 0-255 intensities.
  REM crange& = the number of entries in each color map.
  REM
  REM Have fun.
  REM
ELSE
  PRINT "Not a GIF file."
ENDIF
END
PROCEDURE getcode
  REM This routine extracts code bits out of a very long bit stream.
  REM cpt&=current byte in the buffer block
  REM cbp&=current start bit in the byte
  REM codesize&=current code bit width
  REM lpt&=last byte in the buffer block
  sumb&=codesize&+cbp&
  smo%=lpt&-cpt&
  REM A 10 bit code can span 3 bytes, so there are many cases to handle.
  IF sumb&<=8
    IF smo%<1
      GOSUB getblock
    ENDIF
    code&=SHR&(pcl|(cpt&),cbp&) AND readmask&
    IF sumb&=8
      INC cpt&
      cbp&=0
    ELSE
      cbp&=sumb&
    ENDIF
  ELSE
    IF sumb&<=16
      IF smo%<2
        GOSUB getblock
      ENDIF
      code&=SHR&(pcl|(cpt&),cbp&) OR SHL&(pcl|(cpt&+1),8-cbp&) AND readmask&
      IF sumb&=16
        ADD cpt&,2
        cbp&=0
      ELSE
        INC cpt&
        cbp&=sumb&-8
      ENDIF
    ELSE
      IF smo%<3
        GOSUB getblock
      ENDIF
      code&=SHR&(pcl|(cpt&),cbp&) OR SHL&(pcl|(cpt&+1),8-cbp&) OR SHL&(pcl|(cpt&+2),16-cbp&) AND readmask&
      REM
      REM Just in case that line gets truncated in transit, here it is:
      REM code&=SHR&(pcl|(cpt&),cbp&) OR SHL&(pcl|(cpt&+1),8-cbp&)
      REM              OR SHL&(pcl|(cpt&+2),16-cbp&) AND readmask&
      REM
      ADD cpt&,2
      cbp&=sumb&-16
    ENDIF
  ENDIF
RETURN
PROCEDURE getblock
  REM The image date is broken up in blocks less than 256 bytes long in the
  REM GIF file.  I get one of those blocks whenever I am running short.
  smo2&=0
  WHILE cpt&<>lpt&              ! Move any as yet unused bytes from previous
    pcl|(smo2&)=pcl|(cpt&)      !   block up to front.
    INC smo2&
    INC cpt&
  WEND
  cpt&=0
  bc|=INP(#1)
  lpt&=bc|+smo%
  IF bc|<>0                     ! And then stick the new block on behind.
    BGET #1,pcp%+smo%,bc|
  ENDIF
RETURN
PROCEDURE codeclear
  REM This clears the code table.  It does this right away, and every 4096
  REM   codes sent thereafter -- and sometimes sooner.
  freecode&=firstfree&
  codesize&=initcodesize&
  maxcode&=SHL(1,codesize&)
  readmask&=maxcode&-1
  REPEAT
    GOSUB getcode
  UNTIL code&<>clearcode&
  GOSUB codeout                  ! We always send the first code out right
  oldcode&=code&                 !   away after a code clear.
RETURN
PROCEDURE codeout
  REM  This routine expands the code into real image data.
  oz&=-1
  ooz&=code&
  REPEAT                         ! I have to extract the string backwards
    INC oz&
    temp|(oz&)=scar|(ooz&)
    ooz&=prep&(ooz&)
  UNTIL ooz&=5000
  pscar|=temp|(oz&)
  IF interlace!                  ! A nitwit interlaced image.  Bleech.
    FOR ooz&=oz& TO 0 STEP -1    ! Take the backward string and put it into
      IF vcol&=width&            !  the picture image, forwards.
        vcol&=0
        hline&=hline&+lstep&     ! Set up the crazy interlace cases.
        IF hline&>=height&
          INC pass|
          IF pass|=1
            lstep&=8
            hline&=4
          ENDIF
          IF pass|=2
            lstep&=4
            hline&=2
          ENDIF
          IF pass|=3
            lstep&=2
            hline&=1
          ENDIF
          IF pass|=4
            hline&=height&
          ENDIF
        ENDIF
        pici%=hline&*width&          ! I've figured out where it goes.
      ENDIF
      pic|(pici%)=g|(temp|(ooz&))    ! Now I finally put it there.
      INC pici%
      INC vcol&
    NEXT ooz&
  ELSE                                ! Ah, a seqential image.
    FOR ooz&=oz& TO 0 STEP -1         ! Take the backward string and put it
      pic|(pici%)=g|(temp|(ooz&))     ! into the picture image, forwards.
      INC pici%
    NEXT ooz&
  ENDIF
RETURN
-- 
- John Logajan @ Network Systems; 7600 Boone Ave; Brooklyn Park, MN 55428
- logajan@ns.network.com, 612-424-4888, Fax 612-424-2853