home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / NeXT-Icons / next-icon@gun.com / Apps / ImagePortfolio / imageReps.subproj / GifImageRep.m < prev    next >
Encoding:
Text File  |  1993-06-03  |  12.0 KB  |  451 lines

  1. // -------------------------------------------------------------------------------------
  2. // GifImageRep.m
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9. // Note: Much of the source in this object has been a heavy modification of various
  10. //       public domain sources for GIF file conversion programs.
  11. // -------------------------------------------------------------------------------------
  12.  
  13. #import <objc/objc.h>
  14. #import <appkit/appkit.h>
  15. #import <libc.h>
  16. #import <stdlib.h>
  17. #import <stdio.h>
  18. #import <string.h>
  19. #import <dpsclient/dpsclient.h>
  20. #import <defaults/defaults.h>
  21. #import "GifImageRep.h"
  22.  
  23. // -------------------------------------------------------------------------------------
  24. // error codes & descriptions
  25. #define        errNONE                    0
  26. #define        errTABLE_OVERFLOW        1
  27. #define        errRASTER_SIZE            2
  28. #define        errNO_COLORMAP            3
  29. #define        errINVALID_FILE            4
  30. #define        errBAD_BLOCK            5
  31. #define        errCANT_LOAD            6
  32. #define        errEND_OF_FILE            7
  33. #define        errNO_IMAGE                8
  34. static char *errDesc[] = {
  35.             "none",
  36.             "code table overflow",
  37.             "raster has the wrong size",
  38.             "no colormap present for image",
  39.             "invalid GIF file",
  40.             "illegal GIF block type",
  41.             "unable to load file (unknown reason)",
  42.             "reached premature end-of-file"
  43.             "no image found in file",
  44.             0
  45. };
  46. const char *errorDesc(int x) { return errDesc[x]; }
  47.  
  48. // -------------------------------------------------------------------------------------
  49. @implementation GifImageRep
  50.  
  51. // -------------------------------------------------------------------------------------
  52. // internal support methods
  53. // -------------------------------------------------------------------------------------
  54.  
  55. /* decode a raster image */
  56. // Note: A 'goto'? Yuck! I know their ugly, but I just haven't remove it yet. sorry.
  57. - (int)_readRaster
  58. {
  59.     u_char        *ch, buf[260], *fill;
  60.     u_int        count, datm;
  61.     int            i, err, code, bits, thisCode;
  62.     int            codeTable[4096][2];    // LZW compression code data
  63.     int            stack[4096 * 2], *sp;
  64.     int            _datasize, _codesize, _maxsize;
  65.     int            _clear, _avail, _oldcode, _firstcode;
  66.  
  67.     /* initialize */
  68.     err        = errNONE;
  69.     fill       = imageRaster;
  70.     _datasize  = getc(imageStream);
  71.     _clear     = 1 << _datasize;
  72.     _codesize  = _datasize + 1;
  73.     _avail     = _clear + 2;
  74.     _maxsize   = _clear * 2;
  75.     _oldcode   = -1;
  76.     _firstcode = -1;
  77.     bits       = 0;
  78.     datm       = 0;
  79.     sp         = stack;
  80.  
  81.     /* prefill table */
  82.     for (i = 0; i < 4096; i++) {
  83.         codeTable[i][0] = 0;
  84.         codeTable[i][1] = (i < _clear)? i : 0;
  85.     }
  86.   
  87.     /* decode raster */
  88.     for (count = getc(imageStream); count > 0; count = getc(imageStream)) {
  89.   
  90.         /* read block */
  91.         if (fread(buf, 1, count, imageStream) != count) { err = errEND_OF_FILE; break; }
  92.     
  93.         /* process block */
  94.         for (ch = buf; count-- > 0; ch++) {
  95.             datm += (u_int)(*ch) << bits;
  96.             bits += 8;
  97.             while (bits >= _codesize) {
  98.       
  99.                 /* get code */
  100.                 code = datm & ((1 << _codesize) - 1);
  101.                 datm >>= _codesize;
  102.                 bits -= _codesize;
  103.  
  104.                 /* clear table */
  105.                 if (code == _clear) {
  106.                     _codesize  = _datasize + 1;
  107.                     _maxsize   = _clear * 2;
  108.                     _avail     = _clear + 2;
  109.                     _oldcode   = -1;
  110.                     _firstcode = -1;
  111.                     sp         = stack;
  112.                     for (i = 0; i < 4096; i++) {
  113.                         codeTable[i][0] = 0;
  114.                         codeTable[i][1] = (i < _clear)? i : 0;
  115.                     }
  116.                     continue;
  117.                 }
  118.         
  119.                 /* end of decompression */
  120.                 if (code == _clear + 1) goto exitloop;  /* for non-standard GIF files */
  121.  
  122.                 /* save first code */
  123.                 if (_firstcode == -1) {
  124.                     _firstcode = _oldcode = code;
  125.                     *fill++ = code;
  126.                     continue;
  127.                 }
  128.  
  129.                 /* save this code */
  130.                 thisCode = code;
  131.  
  132.                 /* code check */
  133.                 if (code >= _avail) {
  134.                     *sp++ = _firstcode;
  135.                     code = _oldcode;
  136.                 }
  137.  
  138.                 /* place codes on stack */
  139.                 while (code >= _clear) {
  140.                     *sp++ = codeTable[code][1];
  141.                     if (code == codeTable[code][0]) {
  142.                         err = errTABLE_OVERFLOW;
  143.                         goto exitloop;
  144.                     }
  145.                     code = codeTable[code][0];
  146.                 }
  147.                 *sp++ = _firstcode = codeTable[code][1];
  148.  
  149.                 /* save in table */
  150.                 code = _avail;
  151.                 if (code < 4096) {
  152.                     codeTable[code][0] = _oldcode;
  153.                     codeTable[code][1] = _firstcode;
  154.                     _avail++;
  155.                     if ((_avail >= _maxsize) && (_maxsize < 4096)) {
  156.                         _maxsize *= 2;
  157.                         _codesize++;
  158.                     }
  159.                 }
  160.  
  161.                 /* save old code */
  162.                 _oldcode = thisCode;
  163.         
  164.                 /* flush stack */
  165.                 while (sp > stack) *fill++ = *--sp;
  166.         
  167.             }
  168.         }
  169.     
  170.     }
  171.   
  172. exitloop:
  173.     if (err == errEND_OF_FILE) {
  174.         err = errNONE;
  175.         NXLogError("GIF premature end of file");
  176.     }
  177.     if (!err && (fill != imageRaster + rasterSize)) err = errRASTER_SIZE;
  178.     return err;
  179.  
  180. }
  181.  
  182. /* load image raster */
  183. - (int)_loadImage
  184. {
  185.     u_char        buf[10];
  186.     BOOL        local, interleaved;
  187.     int            err, left;
  188.  
  189.     /* set file position */
  190.     if (imagePos) fseek(imageStream, imagePos, SEEK_SET);
  191.     else imagePos = ftell(imageStream);
  192.   
  193.     /* read image information */
  194.     fread(buf, 1, 9, imageStream);
  195.     left        = buf[0] + (buf[1] << 8);
  196.     rasterTop   = buf[2] + (buf[3] << 8);
  197.     pixWide     = buf[4] + (buf[5] << 8);
  198.     pixHigh     = buf[6] + (buf[7] << 8);
  199.     local       = (buf[8] & 0x80)? YES : NO;
  200.     interleaved = (buf[8] & 0x40)? YES : NO;
  201.     rasterSize  = (u_long)pixWide * (u_long)pixHigh;
  202.  
  203.     /* convert color map */
  204.     if (local) {
  205.         colorMapCount = 1 << ((buf[8] & 0x7) + 1);
  206.         fread(colorMap, 3, colorMapCount, imageStream);
  207.     } else
  208.     if (!isGlobalMap) return errNO_COLORMAP;
  209.  
  210.     /* read image raster */
  211.     if (!imageRaster) memset((imageRaster=(u_char*)malloc(rasterSize)), 0, rasterSize);
  212.     if ((err = [self _readRaster]) && (err != errRASTER_SIZE)) return err;
  213.  
  214.     /* remove any interleave */
  215.     if (interleaved) {
  216.         int        i, r = 0;
  217.         u_char    *temp = (u_char*)malloc(rasterSize);
  218.         u_short    *tbl = (u_short*)malloc(pixHigh * sizeof(u_short));
  219.         for (i = rasterTop    ; i < (int)(rasterTop + pixHigh); i += 8) tbl[i] = r++;
  220.         for (i = rasterTop + 4; i < (int)(rasterTop + pixHigh); i += 8) tbl[i] = r++;
  221.         for (i = rasterTop + 2; i < (int)(rasterTop + pixHigh); i += 4) tbl[i] = r++;
  222.         for (i = rasterTop + 1; i < (int)(rasterTop + pixHigh); i += 2) tbl[i] = r++;
  223.         for (i = 0, r = rasterTop; r < (int)(rasterTop + pixHigh); i++, r++)
  224.             memcpy(&temp[i * pixWide], &imageRaster[tbl[r] * pixWide], pixWide);
  225.         free(tbl);
  226.         free(imageRaster);
  227.         imageRaster = temp;
  228.         rasterTop = 0;
  229.     }
  230.  
  231.     /* return successful */
  232.     return errNONE;
  233.   
  234. }
  235.  
  236. /* read GIF header info */
  237. - (int)_readGIFHeader
  238. {
  239.     u_int    screenWidth, screenHeight;                                // screen dimensions
  240.     u_int    background;
  241.     u_char    buf[256];
  242.  
  243.     /* rewind file pointer */
  244.     rewind(imageStream);
  245.   
  246.     /* check gif signiture type */
  247.     if (![[self class] validImageType:imageStream]) return errINVALID_FILE;
  248.     
  249.     /* read global information */
  250.     fread(buf, 1, 7, imageStream);
  251.     screenWidth  = buf[0] + (buf[1] << 8);                            // not used
  252.     screenHeight = buf[2] + (buf[3] << 8);                            // not used
  253.     isGlobalMap  = (buf[4] & 0x80)? YES : NO;                        // global color map?
  254.     background   = buf[5];                                            // not used
  255.     aspectRatio  = (buf[6])? ((float)buf[6] + 15.0) / 64.0 : 1.0;    // aspect ratio
  256.  
  257.     /* load global colormap */
  258.     if (isGlobalMap) {
  259.         colorMapCount = 1 << ((buf[4] & 0x07) + 1);
  260.         fread(colorMap, 3, colorMapCount, imageStream);
  261.     }
  262.   
  263.     return errNONE;
  264. }
  265.  
  266. /* initialize from file */
  267. - (int)_readImageAtPosition:(long int)overridePos
  268. {
  269.     int    err;
  270.  
  271.     /* read gif header info */
  272.     if (err = [self _readGIFHeader]) return err;
  273.  
  274.     /* set file position override */
  275.     if (overridePos) fseek(imageStream, overridePos, SEEK_SET);
  276.   
  277.     /* loop through file */
  278.     for(;;) {
  279.         int err;
  280.         u_char buf[256], ch = getc(imageStream);
  281.         switch (ch) {
  282.             case ',' :
  283.                 if (err = [self _loadImage]) return err;
  284.                 imageNextPos = ftell(imageStream);
  285.                 return errNONE;
  286.             case '!' :    // skip extension
  287.                 for (ch=getc(imageStream); ch=getc(imageStream);)
  288.                     fread(buf, 1, ch, imageStream);
  289.                 break;
  290.             case '\0':
  291.                 break;    /* non-standard files */
  292.             case ';' :
  293.                 return imageNextPos? errNONE: errNO_IMAGE;
  294.             default  :
  295.                 return errBAD_BLOCK;
  296.         }
  297.     }
  298.  
  299.     /* return any error */
  300.     return errNONE;
  301.   
  302. }
  303.  
  304. // -------------------------------------------------------------------------------------
  305. // internal init method
  306. // -------------------------------------------------------------------------------------
  307.  
  308. /* init from file stream */
  309. - _initFromFile:(const char*)filename atPos:(long int)overridePos
  310. {
  311.     NXSize    pSize;
  312.   
  313.     /* generic initialization */
  314.     [self initDrawMethod:@selector(_drawImage:) inObject:self];
  315.   
  316.     /* local var init */
  317.     imageRaster        = (u_char*)nil;
  318.     pixWide          = 0;
  319.     pixHigh         = 0;
  320.     rasterSize        = 0L;
  321.     isGlobalMap        = NO;
  322.     colorMapCount    = 0;
  323.     imagePos        = 0L;
  324.     imageNextPos    = 0L;
  325.   
  326.     /* verify stream */
  327.     imageStream = fopen(filename, "r");
  328.     if (!imageStream) { [self free]; return (id)nil; }
  329.   
  330.     /* initialize */
  331.     loadError = [self _readImageAtPosition:overridePos];
  332.     if (loadError == errNO_IMAGE) { [self free]; return (id)nil; }
  333.     if (!loadError && (!pixWide || !pixHigh)) loadError = errCANT_LOAD;
  334.     if (loadError) {
  335.         NXLogError("GIF stream error: %s", errDesc[loadError]);
  336.         [self free];
  337.         return (id)nil;
  338.     }
  339.  
  340.     /* init size (check aspect ratio) */
  341.     pSize.width  = (float)pixWide;
  342.     pSize.height = (float)pixHigh;
  343.     if (aspectRatio < 1.0) pSize.width  *= 1.0 / aspectRatio; else
  344.     if (aspectRatio > 1.0) pSize.height *= aspectRatio;
  345.  
  346.     /* init custom image rep */
  347.     [self setSize:&pSize];
  348.     [self setPixelsWide:pixWide];
  349.     [self setPixelsHigh:pixHigh];
  350.     [self setBitsPerSample:8];
  351.     [self setAlpha:NO];
  352.     [self setNumColors:256];
  353.  
  354.     return self;
  355. }
  356.  
  357. // -------------------------------------------------------------------------------------
  358. // public advertised methods
  359. // -------------------------------------------------------------------------------------
  360.  
  361. /* return true if file is gif image (only check file extension) */
  362. + (BOOL)validImageFile:(const char*)filename
  363. {
  364.     char    *extn = rindex((char*)filename, '.');
  365.     if (!extn) return NO;
  366.     return strcasecmp(extn,".gif") && strcmp(extn,".g")? NO : YES;
  367. }
  368.  
  369. /* check file type */
  370. + (BOOL)validImageType:(FILE*)fd
  371. {
  372.     char    buf[16];
  373.     fread(buf, 1, 6, fd);
  374.     return strncasecmp(buf, "GIF87a", 6) && strncasecmp(buf, "GIF89a", 6)? NO : YES;
  375. }
  376.  
  377. /* read all images into a list */
  378. + (List*)newListFromFile:(const char*)filename
  379. {
  380.     id            list, _class = self;
  381.     long int    pos;
  382.     
  383.     /* verify file */
  384.     if (![self validImageFile:filename]) return (id)nil;
  385.  
  386.     /* start loading images */
  387.     list = [[[List alloc] initCount:1] empty];
  388.     for (pos = 0L;;) {
  389.         if (!(self = [[_class alloc] _initFromFile:filename atPos:pos])) break;
  390.         [list addObject:self];
  391.         pos = imageNextPos;
  392.     }
  393.     
  394.     /* return list */
  395.     if (![list count]) { [list free]; list = (id)nil; }
  396.     return list;
  397.     
  398. }
  399.  
  400. /* init from file stream */
  401. - initFromFile:(const char*)filename
  402. {
  403.     if (![[self class] validImageFile:filename]) { [self free]; return (id)nil; }
  404.     return [self _initFromFile:filename atPos:0L];
  405. }
  406.  
  407. /* free gif */
  408. - free
  409. {
  410.     if (imageRaster) free(imageRaster);
  411.     if (imageStream) fclose(imageStream);
  412.     return [super free];
  413. }
  414.  
  415. // -------------------------------------------------------------------------------------
  416. // custom image draw
  417. // -------------------------------------------------------------------------------------
  418.  
  419. /* draw image */
  420. - _drawImage:mySelf
  421. {
  422.     int                r, c, bpr;
  423.     u_char            *p;
  424.     NXRect            rect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  425.     const u_char    *bitmap[5] = { 0, 0, 0, 0, 0 };
  426.  
  427.     /* make sure the image raster is loaded */
  428.     if (loadError || (!imageRaster && (loadError=[self _loadImage]))) return self;
  429.  
  430.     /* build image bitmap */
  431.     bitmap[0] = p = (u_char*)malloc(3L * rasterSize);
  432.     for (r = rasterTop; r < (int)(rasterTop + pixHigh); r++) {
  433.         u_char *s = &imageRaster[r * pixWide];
  434.         for (c = 0; c < (int)pixWide; c++, p+=3) memcpy(p, colorMap[s[c]], 3);
  435.     }
  436.  
  437.     /* render bitmap */
  438.     rect.size = size;
  439.     bpr = (7 + pixWide * 8 * 3) / 8;
  440.     NXDrawBitmap(&rect, pixWide, pixHigh, 8, 3, 8*3, bpr, NO, NO, NX_RGBColorSpace, bitmap);
  441.  
  442.     /* free bitmap storage */
  443.     free((char*)bitmap[0]);
  444.  
  445.     /* return successful */
  446.     return self;
  447.   
  448. }
  449.  
  450. @end
  451.