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