home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************/
- /* DecodeGIF.m */
- /* implementation file of DecodeGIF class of ViewGif2 application */
- /* Handles decoding of GIF files, and decoding status panel */
- /* January 1990 Carl F. Sutter */
- /*****************************************************************************/
-
- #import "DecodeGIF.h"
- #import <appkit/Application.h>
- #import <appkit/Window.h>
- #import <appkit/Control.h>
- #import <appkit/Panel.h>
- #import <appkit/Slider.h> // for setMinValue, setMaxValue
- #import <appkit/tiff.h> // for NXImageBitmap
- #import <stdlib.h> // for malloc and free
- #import <stdio.h> // for fprintf
- #import <string.h> // for strcpy
-
- /* some external function references to do the actual LZW decoding */
- void StartLZW( BYTE byCodeSizeIn, NXStream *stream,
- BYTE *byR, BYTE *byG, BYTE *byB, BYTE *byMap,
- int width, int height, BOOL bInter );
- long ContinueLZW( int nNumCodes );
-
- @implementation DecodeGIF
-
- /*****************************************************************************/
- /* new - factory method */
- /*****************************************************************************/
- + new
- {
- self = [super new];
- [self setup];
- return( self );
- } /* new 1/22/90 CFS */
-
-
- /*****************************************************************************/
- /* new - factory method with target and action */
- /*****************************************************************************/
- + new:(id )targ action:(SEL )act
- {
- self = [super new];
- [self setup];
- target = targ;
- action = act;
- return( self );
- } /* new:action: 1/23/90 CFS */
-
-
- /*****************************************************************************/
- /* setup - initialize instance variables and panel etc. */
- /*****************************************************************************/
- - setup
- {
- /* load nib defining panel and outlets */
- [NXApp loadNibSection:"DecodeGIF.nib" owner:self];
- bDecoding = NO;
- return( self );
- } /* setup 1/23/90 CFS */
-
-
- /*****************************************************************************/
- /* outlet initialization methods */
- /*****************************************************************************/
- - setDecodePanel:anObject { decodePanel = anObject; return( self ); }
- - setGauge:anObject { gauge = anObject; return( self ); }
- - setWidth:anObject { width = anObject; return( self ); }
- - setHeight:anObject { height = anObject; return( self ); }
- - setNumColors:anObject { numColors = anObject; return( self ); }
- - setFilename:anObject { filename = anObject; return( self ); }
-
-
- /*****************************************************************************/
- /* show - display the decoder panel */
- /*****************************************************************************/
- - show:sender
- {
- [decodePanel makeKeyAndOrderFront:self];
- return( self );
- } /* show 1/23/90 CFS */
-
-
- /*****************************************************************************/
- /* setTarget - sets the object to return queue items to */
- /*****************************************************************************/
- - setTarget:(id)targ
- {
- target = targ;
- return( self );
- } /* setTarget 1/22/90 CFS */
-
-
- /*****************************************************************************/
- /* setAction - sets the method that will be called with the new item */
- /* there will be one parameter - a pointer to a standard C string */
- /*****************************************************************************/
- - setAction:(SEL)aSelector
- {
- action = aSelector;
- return( self );
- } /* setAction 1/22/90 CFS */
-
-
- /*****************************************************************************/
- /* decodeFile - called from controller to initiate decoding process */
- /*****************************************************************************/
- - decodeFile:(const char *)fileName
- {
- strcpy( szPathName, fileName );
- strcpy( szFileName, strrchr( fileName, '/' ) + 1);
- [filename setStringValue:szFileName];
- nStatus = STATUS_OPEN;
- /* use the timer to break up the decoding process and allow other events */
- timer = [[Animator alloc] initChronon:0.0 adaptation:0.0 target:self
- action:@selector(nextStep:) autoStart:YES
- eventMask:NX_ALLEVENTS];
- return( self );
- } /* decodeFile 1/23/90 CFS */
-
-
- /*****************************************************************************/
- /* nextStep - called by timer, parse control to current task */
- /*****************************************************************************/
- - nextStep:sender
- {
- #define CODES_PER_ITERATION 300
- long lReturn;
-
- /* first, go right to the decoder, if LZW decoding is in progress */
- if (bDecoding)
- {
- lReturn = ContinueLZW( CODES_PER_ITERATION );
- if ((lReturn == 0) || (lReturn == -1)) [self finishDecoder:lReturn];
- else [gauge setFloatValue:(float)lReturn];
- }
- /* otherwise, go to the proper step in the rest of the decoding process */
- else switch (nStatus)
- {
- case STATUS_OPEN: [self openFile]; break;
- case STATUS_GLOBAL: [self readGlobalInfo]; break;
- case STATUS_BLOCK: [self readBlockCode]; break;
- case STATUS_IMAGE: [self readImageInfo]; break;
- case STATUS_DECODING: [self startDecoder]; break;
- case STATUS_EXTENSION: [self cancelDecoding]; break; // FIX THIS
- case STATUS_DONE: [self sucessfulDecoding]; break; // ALLOW MULT IMAGES
- }
- return( self );
- } /* nextStep 1/23/90 CFS */
-
-
- /*****************************************************************************/
- /* openFile - open the stream for decoding */
- /*****************************************************************************/
- - (BOOL)openFile
- {
- if (!(stream = NXMapFile( szPathName, NX_READONLY )))
- {
- [self errorAlert:"Could not open GIF file."];
- [self cancelDecoding];
- return( NO );
- }
- nStatus = STATUS_GLOBAL;
- return( YES );
- } /* openFile 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* readGlobalInfo - read the global header from the file */
- /***************************************************************************/
- - (BOOL)readGlobalInfo
- {
- BYTE b1, b2;
- char szSignature[7];
- struct {
- unsigned map:1;
- unsigned res:3;
- unsigned reserved:1;
- unsigned bpp:3;
- } globalInfo;
-
- /* read 6 byte version signature */
- if (![self readBytes:6 data:szSignature]) return( NO );
- szSignature[6] = 0;
- if (0 != strcmp( szSignature, "GIF87a"))
- {
- [self errorAlert:"Not a GIF file - signature not GIF87a."];
- [self cancelDecoding];
- return( NO );
- }
-
- /* read screen width and height */
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
-
- /* read info byte */
- if (![self readBytes:1 data:&globalInfo]) return( NO );
- // GlobalInfo.nResolution = globalInfo.res + 1;
- nBPP = globalInfo.bpp + 1;
-
- /* read background color */
- if (![self readBytes:1 data:&b1]) return( NO );
- // GlobalInfo.nBackground = b1;
-
- /* reserved zero at end of screen descriptor */
- if (![self readBytes:1 data:&b1]) return( NO );
-
- /* read in the global color map, if it exists */
- if (globalInfo.map) [self readMap];
-
- /* everything loaded OK, return TRUE */
- nStatus = STATUS_BLOCK;
- return( YES );
- } /* readGlobalInfo 10/16/89 CFS */
-
-
- /***************************************************************************/
- /* readMap - read a color map from the stream */
- /***************************************************************************/
- - (BOOL)readMap
- {
- nNumColors = 1;
- nNumColors <<= nBPP; /*2^BPP*/
- [numColors setIntValue:nNumColors];
- if (![self readBytes:nNumColors * 3 data:byColorMap]) return( NO );
- return( YES );
- } /* readMap 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* readBlockCode - read the next character blockcode and set status */
- /***************************************************************************/
- - (BOOL)readBlockCode
- {
- char cBlockCode;
-
- if (![self readBytes:1 data:&cBlockCode]) return( NO );
- switch (cBlockCode)
- {
- case ',': nStatus = STATUS_IMAGE; break;
- case '!': nStatus = STATUS_EXTENSION; break;
- case ';': nStatus = STATUS_DONE; break;
- default:
- {
- [self errorAlert:"Bad block code in readNextBlockCode."];
- [self cancelDecoding];
- return( NO );
- }
- }
- return( YES );
- } /* readBlockCode 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* readImageInfo - read the image information header from the file */
- /***************************************************************************/
- - (BOOL)readImageInfo
- {
- BYTE b1, b2;
- struct {
- unsigned map:1;
- unsigned interlace:1;
- unsigned reserved:3;
- unsigned bpp:3;
- } localInfo;
-
- /* read the image margins and size */
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
- // ImageInfo.nLeftMargin = b2 * 256 + b1;
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
- // ImageInfo.nTopMargin = b2 * 256 + b1;
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
- nWidth = b2 * 256 + b1;
- [width setIntValue:nWidth];
- if (![self readBytes:1 data:&b1]) return( NO );
- if (![self readBytes:1 data:&b2]) return( NO );
- nHeight = b2 * 256 + b1;
- [height setIntValue:nHeight];
-
- /* read the image info byte */
- if (![self readBytes:1 data:&localInfo]) return( NO );
- bInterlaced = localInfo.interlace;
- // ImageInfo.nBPP = localInfo.bpp + 1;
-
- if (localInfo.map) [self readMap];
-
- /* everything loaded OK, return TRUE */
- nStatus = STATUS_DECODING;
- return( YES );
- } /* readImageInfo 10/16/89 CFS */
-
-
- /***************************************************************************/
- /* startDecoder - control decoding of the image */
- /***************************************************************************/
- - (BOOL)startDecoder
- {
- BYTE byCodeSize;
-
- [decodePanel setTitle:"Decoding Status - decoding..."];
- /* read the beginning code size - should be same as BPP */
- if (![self readBytes:1 data:&byCodeSize]) return( NO );
-
- /* free any previous image data & allocate space for the current image */
- free( byDataR );
- free( byDataG );
- free( byDataB );
- byDataR = malloc( nWidth * nHeight );
- byDataG = malloc( nWidth * nHeight );
- byDataB = malloc( nWidth * nHeight );
-
- /* set up the time left gauge */
- [gauge setMinValue:0];
- [gauge setMaxValue:nHeight * nWidth];
- [gauge setIntValue:0];
-
- /* start the LZW decoder */
- StartLZW( byCodeSize, stream, byDataR, byDataG, byDataB, byColorMap,
- nWidth, nHeight, bInterlaced );
- bDecoding = YES;
-
- return( YES );
- } /* startDecoder 1/24/90 CFS */
-
-
- /***************************************************************************/
- /* finishDecoder: - clean up after the LZW process */
- /***************************************************************************/
- - (BOOL)finishDecoder:(long)lReturnCode
- {
- NXRect nxrBit;
-
- if (lReturnCode == -1) [self errorAlert:
- "The whole image couldn't be loaded.\nAs much as possible will be shown."];
-
- [gauge setFloatValue:(float)(nWidth * nHeight)];
- /* create a bitmap, and image the data into it */
- [decodePanel setTitle:"Decoding Status - imaging..."];
- nxrBit.size.width = nWidth;
- nxrBit.size.height = nHeight;
- bmpOut = [Bitmap newSize:nxrBit.size.width :nxrBit.size.height
- type:NX_UNIQUEBITMAP];
- [bmpOut setFlip:NO];
- [bmpOut lockFocus];
- NXImageBitmap( &nxrBit, nWidth, nHeight, 8, 3, NX_PLANAR,
- NX_COLORMASK, byDataR, byDataG, byDataB, NULL, NULL );
- [bmpOut unlockFocus];
- [decodePanel setTitle:"Decoding Status"];
-
- bDecoding = NO;
- nStatus = STATUS_DONE;
-
- // ALLOW MULTIPLE IMAGES EVENTUALLY
- // /* read the last block count - should be zero */
- // if (![self readBytes:1 data:&byDummy]) return( NO );
- // nStatus = STATUS_BLOCK;
-
- return( YES );
- } /* finishDecoder: 1/24/90 CFS */
-
-
- /***************************************************************************/
- /* sucessfulDecoding - send decoded image to controller */
- /***************************************************************************/
- - sucessfulDecoding
- {
- [timer free];
- NXCloseMemory( stream, NX_FREEBUFFER );
- [target perform:action with:bmpOut];
- return( self );
- } /* sucessfulDecoding 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* cancelDecoding - notify controller that file couldn't be decoded */
- /***************************************************************************/
- - cancelDecoding
- {
- [timer free];
- NXCloseMemory( stream, NX_FREEBUFFER );
- [target perform:action with:nil with:(id)NO];
- return( self );
- } /* cancelDecoding 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* readBytes - read in the desired number of data bytes */
- /***************************************************************************/
- - (BOOL)readBytes:(int)nNumBytes data:(void *)buf
- {
- int nError;
-
- nError = NXRead( stream, buf, nNumBytes );
- if (nError <= 0)
- {
- [self errorAlert:"File couldn't be completely read."];
- [self cancelDecoding];
- return( NO );
- }
- return( YES );
- } /* readBytes 1/23/90 CFS */
-
-
- /***************************************************************************/
- /* errorAlert - put the message in an alert panel */
- /***************************************************************************/
- - errorAlert:(char *)szMessage
- {
- NXRunAlertPanel( "ViewGif2 Error", szMessage, "OK", NULL, NULL );
- return( self );
- } /* errorAlert 10/30/89 CFS */
-
- @end
-