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