DEGIF TURBO PASCAL UNIT DOCUMENTATION 1.0 INTRODUCTION DEGIF is a Turbo Pascal Unit to decode graphic images stored in CompuServe's Graphic Interchange Format (GIF). With this unit you can write your own GIF decoder or add GIF to any application. "GIF" and "Graphic Interchange Format" are trademarks of H & R Block, Inc. The GIF standard itself is in the public domain. This unit is fully documented but we will not release the source code at this time. This is a shareware product with a registration fee of $25 payable to Cyborg Software Systems, Inc. 3119 Cossell Drive, Indianapolis, IN 46224. This unit, the documentation and the source and executable code for DEGIFER are all Copyright 1988 by Cyborg Software Systems, Inc. You may use this material for personal non-commercial use. If you like it we ask you to register it. If you distribute it for free you must distribute it in its original ARC form with all files intact. If you distribute it for a fee or if you wish to use DEGIF in any commercial application or custom programming written for fee or under contract, you must register it. Violation of the above terms infringes our copyright. Registered users will receive the next update free of charge. Further updates will cost registered users $5. Parts of this code are very loosely based on EXPANDER.INC by Bob Berry which was a Turbo Pascal 3.0 translation of EXPANDER.C written in C by Steve Wilhite. Sincere thanks and credit to them. 2.0 GIF DECODING The best way we know to document the DEGIF unit is to give an example program called DEGIFER. DEGIFER is a very, very primitive GIF display program for CGA graphics. It makes no attempts to match colors. It just plots points in (COLOR mod 4). On 2 color images its great. Most 16 color images are viewible. Beyond that you're on your own. You should print out a copy of DEGIFER.PAS and follow it along its main routine as we go. 2.1 Initialization The first thing you must do is pass a procedure pointer to your routines called GetByte and PutByte. GetByte must be a far-call function to retrieve one byte from the GIF file. PutByte must be a far-call procedure to put one pixel on the screen. We will discuss these routines in section 3.0 below. AddrGetByte and AddrPutByte are a global pointers declared in the DEGIF unit but you must initialize them. The next few lines of DEGIFER's main routine engage the CRT unit and prints the copyright notice. A file name is retrieved from the command line or the user is prompted for one. The file is opened. This is the file that GetByte gets its bytes from. The variables SkipIt, EOFin, Done, BufIndx and Count are initialized. Copyright (c) 1988 Cyborg Software Systems, Inc. page 1. DEGIF TURBO PASCAL UNIT DOCUMENTATION They are specific to this demo and your application may not need them. The DEGIF unit defines the following.... Type MapType=(Global,Local); GIF files may or may not have a global color map and or a local color map for multiple image files. DEGIF needs its variable CurMap:MapType to be initialized before you begin. 2.2 Procedure GetGIFSig All valid GIF files begin with a 6 byte signature. The DEGIF unit provides a procedure GetGIFSig to retrieve it. After calling GetGIFSig the global string GIFSig should contain the string. As of this writing (March 1988) the only valid string is 'GIF87a'. 2.3 Procedure GetScrDes Following the GIF signature in a GIF file is the screen description. The DEGIF unit provides a procedure GetScrDes to retrieve it and initialize several global variables. ScreenWidth and ScreenHeight are type Word and contain the screen width and height in pixels. All images in this file will fit in this virtual screen space. Its up to you to scale, clip or scroll the image if it doesn't fit your physical screen. There is no information anywhere in current GIF files to help determine the proper aspect ratio. You have to just guess that in all likelihood screens of 320 x 200, 640 x 200, 320 x 400 or 640 x 400 are really all supposed to be of the same aspect ratio. What you do with stuff like 640 x 350 or 640 x 480 on a screen that won't hold it is up to you. MapExists[Global] is a boolean that tells you if there is a global color map for this file. BitsOfColorPerPrimary is a Word but the only valid values are [1..8]. I was just too lazy to declare it as a byte or sub-range type . It is the topic of much debate among GIF developers as to how useful it is. It isn't necessary and my experience is it is unreliable. For EGA created images its value should be 2 because each primary color (red, green and blue) is designated by 2 bits. EGA's can therefore have 2^(2+2+2)=64 possible colors. The GIF file might contain only 16 so BitsOfColorPerPrimary doesn't tell you how many you can display at once or how many colors are in this image. BitsPerPixel[Global] is a word that does tell you. You really don't need this value but we give it to you anyway. We provide also provide NumberOfColors[Global] which again is a word but it is in the range [1..256]. It is computed by... NumberOfColors[Global]:= (1 shl BitsPerPixel[Global]); Copyright (c) 1988 Cyborg Software Systems, Inc. page 2. DEGIF TURBO PASCAL UNIT DOCUMENTATION This means GIF color maps always contain 2, 4, 8, 16, 32, 64, 128 or 256 entries. The image may not actually contain that many colors. It may use fewer. I have no problem with 16 color images that contain only 10 or 12 colors. It really raises my dandruff when I download 256 color images with 31 colors!!! Editorial over. Some GIF files are composed of multiple small images that may overlap or may not fill the screen. The standard says you should first fill in a background before plotting the images. Some decoders don't bother. They leave the background black. DEGIF gives you BackgrColorIndex which tells you what color to paint the background. Next DEGIFER calls DisplayScrDes. This is its own routine which not only tells the user the information from the screen descriptor, it also attempts to correct aspect ratio by squeezing the image horizontally, smashing it vertically or clipping it if that doesn't work. You are responsible for coming up with such topological gymnastics and should feel free to do it however you want. 2.4 Procedure GetColorMap The GIF standard provides for an optional global color map. Most GIF files contain one. GetScrDes sets the boolean MapExists[Global] to true if there is a global map. The map is put in the following arrays.... RedValue,GreenValue,BlueValue: array [0..255] of byte; Only values in the range [0..NumberOfColors[Global]-1] are defined. They are intensity values from [0..255] for the red, blue and green components of the color. After calling GetColorMap you must decide on a mapping method. This is the proverbial thing that "separates the men from the boys" when it comes to implementing GIF. DEGIFER does a total cop-out and ignores the map completely. It has the following statements... if MapExists[Global] then GetColorMap; DoMapping; ...but you'll probably want to implement it as... if MapExists[Global] then begin GetColorMap; DoMapping end else UseDefaultMap; The job of your DoMapping is to fill the array Color which is defined by the DEGIF unit as... Color: array [Global..Local,0..255] of byte; DoMapping should look like this... procedure DoMapping; Copyright (c) 1988 Cyborg Software Systems, Inc. page 3. DEGIF TURBO PASCAL UNIT DOCUMENTATION var I:integer; begin for I:=0 to NumberOfColors[CurMap]-1 do Color[CurMap,I]:= { Some function of } { RedValue[I], } { BlueValue[I] & } { GreenValue[I] } end; Here is a sample DoMapping that computes a least squares mapping for a 16 color mode on a PCjr or Tandy 1000... procedure DoMapping; const MyRed:array [0..15] of byte= (000,000,000,000, 170,170,170,170, 085,085,085,085, 255,255,255,255); MyGreen:array [0..15] of byte= (000,000,170,170, 000,000,170,170, 085,085,255,255, 085,085,255,255); MyBlue:array [0..15] of byte= (000,170,000,170, 000,170,000,170, 085,255,085,255, 085,255,085,255); var I,J,K:byte; D1,D2:real; begin for I:=0 to NumberOfColors[CurMap]-1 do begin D1:=9999.99; K:=0; for J:=0 to 15 do begin D2:= SQRT( (RedValue[I]-MyRed[J]) * (RedValue[I]-MyRed[J])+ (GreenValue[I]-MyGreen[J]) * (GreenValue[I]-MyGreen[J])+ (BlueValue[I]-MyBlue[J]) * (BlueValue[I]-MyBlue[J]) ); if D2 < D1 then begin K:=J; D1:=D2 end end; Color[CurMap,I]:=K end end; 2.5 Reading blocks Its time now to get down to actual plotting! DEGIFER uses the GRAPH3 procedure GraphColorMode to get into CGA's 320 x 200 x 4 color mode. The next thing to appear in a GIF file is a block. There are currently only two types. Image blocks are preceded by a comma. Extension blocks are preceded by an exclamation point. Any characters other than a ',' or '!' may be ignored. Keep reading until there is nothing left to read. Currently no extension blocks are defined. The DEGIF unit has a procedure that lets you skip extension blocks. It is cleverly called SkipExtendBlock. 2.6 Procedure GetImageDescription After finding an image block which begins with a comma you should call DEGIF's GetImageDescription procedure. It sets up the following global variables declared in DEGIF. Unless noted, all are type word. Copyright (c) 1988 Cyborg Software Systems, Inc. page 4. DEGIF TURBO PASCAL UNIT DOCUMENTATION ImageLeft and ImageTop define the coordinates of this image relative to the screen description. A value of 0,0 is the upper left corner of the screen. ImageWidth and ImageHeight give the dimensions of this image. The image may overlap previous images. It may or may not fill the screen. It should never exceed the screen description. MapExists[Local] is a boolean that tells you if a local color map exists for this image. If no local map exists then you should use the global map. Subsequent images never use old local maps. They always use their own local maps or they revert to the global map. The following code implements this properly. if MapExists[Local] then begin CurMap:=Local; GetColorMap; DoMapping end else CurMap:=Global; Note that GetColorMap and DoMapping depend on CurMap. The boolean variable Interlaced tells you how to plot the scan lines. See section 3.2 for a discussion of interlaced images. BitsPerPixel[Local] and NumberOfColors[Local] tell you the same things about the local map that their global brothers tell you about the global map. The AdjustImage routine is specific to DEGIFER and you may or may not need it. 2.7 Function ExpandGIF The data blocks which follow are the image data index values compressed in a variable length 12 bit-maximum modified LZW compression scheme. HUH? That's what I said when I first became a GIF developer. They just handed me a bunch of code written in "C" by Steve Wilhite. They said just use this code and everything will be ok. There was one problem. I don't know "C"! Enter Bob Berry who sees C and Pascal. My first GIF decoder used his Turbo Pascal 3.0 translation from Wilhite's "C". I plugged. It chugged. Out came GIF! Presto! Since then I've rewritten ExpandGIF several times on my own and I understand "image data index values compressed in a variable length 12 bit maximum modified LZW compression scheme." But you don't need to. ExpandGIF calls your GetByte routine to get compressed data one byte at a time. It does all the magic by calling your PutByte routine to put the dots on the screen. Copyright (c) 1988 Cyborg Software Systems, Inc. page 5. DEGIF TURBO PASCAL UNIT DOCUMENTATION When the entire image is done ExpandGIF returns an integer. Anything other than a 0 is an error. 3.0 ROUTINES YOU PROVIDE Routines like DoMapping and DisplayScreenDescription and AdjustImage are so totally your responsibility that we really can't say much more about them because you may want to do them differently. The GetByte and PutByte need more explanation. DO NOT FORGET TO INCLUDE THESE TWO LINES! AddrGetByte:=@GetByte; AddrPutByte:=@PutByte; 3.1 Function GetByte You must provide a function GetByte:Byte which returns sequential bytes from somewhere. It will most likely be a file but DEGIF could get its information from a modem to view GIF on-line. You'd need more information to do this than is provided here. (When I figure it out myself I'll have a new DEGIF uploaded.) This function must be a far function and is compiled with the {$F+} directive. The simplest GetByte is.. {$F+} function GetByte:byte; var b:byte; begin Read(InFile,b); GetByte:=b end; {$F-} 3.2 Procedure PutByte(Pix:integer) PutByte isn't that simple. It passes you an integer in the range [0..255] that is the index to the Color array. PutByte should plot the color given by Color[CurMap,Pix] at location x,y. The catch is what are x and y? On non-interlaced images you just start at X:=ImageLeft and Y:=ImageTop. You plot points along Y and increment X until you've plotted ImageWidth of them. That is until X=ImageLeft+ImageWidth. Then you reset X to ImageLeft, increment Y and plot the next line. Interlaced images start with Y=ImageTop but they make 4 interlaced passes. Pass 1 begins at ImageTop+0 and plots every 8th line. Pass 2 starts at ImageTop+4 and also plots every 8th line. Pass 3 starts at ImageTop+2 and plots every 4th line. Pass 4 starts at ImageTop+1 and plots every other line. These 4 passes fill the screen. In DEGIFER's PutByte routine I invented a Pass 5 which starts at Copyright (c) 1988 Cyborg Software Systems, Inc. page 6. DEGIF TURBO PASCAL UNIT DOCUMENTATION ImageTop+0 and plots every line. My procedure AdjustImage contains... if Interlaced then Pass:=1 else Pass:=5; This fully implements interlaced images. The PutByte routine must be compiled with the {$F+} far directive on. 4.0 INTERFACE Here is the interface section of the DEGIF unit. unit degif; interface type MapType=(Global,Local); var GIFSig:String[6]; {GIF ID string usually = 'GIF87a'} CurMap:MapType; {Current Map in use} BitsPerPixel, {in this image} NumberOfColors {in this image} :array [Global..Local] of word; BackgrColorIndex, {Background Color index} BitsOfColorPerPrimary, {For example on EGA=2} ImageLeft, {Left edge of image relative to virtual screen} ImageTop, {Top edge of image relative to virtual screen} ImageWidth, {in pixels} ImageHeight, {in pixels} ScreenHeight, {in pixels} ScreenWidth:word; {in pixels} MapExists:array [Global..Local] of boolean; Interlaced:boolean; AddrGetByte, {Pointer to user supplied GetByte} AddrPutByte:pointer; {Pointer to user supplied PutByte} RedValue,GreenValue,BlueValue: array [0..255] of byte; Color: array [Global..Local,0..255] of byte; function ExpandGif: integer; procedure GetColorMap; procedure GetGIFSig; procedure GetImageDescription; procedure GetScrDes; procedure SkipExtendBlock; 5.0 FINAL WORDS This unit is written entirely in Turbo Pascal 4.0 and contains no assembler or Inline code. My #1 goal was to get this available as soon as Copyright (c) 1988 Cyborg Software Systems, Inc. page 7. DEGIF TURBO PASCAL UNIT DOCUMENTATION possible. My #2 goal will be to make it fast. After that I'll add on-line viewing support and any new extensions to the GIF87a standard. To make it worth my trouble I need your support. Please show your appreciation and register this product. Copyright (c) 1988 Cyborg Software Systems, Inc. page 8.