This is GifImageRep.m in view mode; [Download] [Up]
// ------------------------------------------------------------------------------------- // GifImageRep.m // ------------------------------------------------------------------------------------- // Permission is granted to freely redistribute this source code, and to use fragments // of this code in your own applications if you find them to be useful. This class, // along with the source code, come with no warranty of any kind, and the user assumes // all responsibility for its use. // ------------------------------------------------------------------------------------- // Note: Much of the source in this object has been a heavy modification of various // public domain sources for GIF file conversion programs. // ------------------------------------------------------------------------------------- #import <objc/objc.h> #import <appkit/appkit.h> #import <libc.h> #import <stdlib.h> #import <stdio.h> #import <string.h> #import <dpsclient/dpsclient.h> #import <defaults/defaults.h> #import "GifImageRep.h" // ------------------------------------------------------------------------------------- // error codes & descriptions #define errNONE 0 #define errTABLE_OVERFLOW 1 #define errRASTER_SIZE 2 #define errNO_COLORMAP 3 #define errINVALID_FILE 4 #define errBAD_BLOCK 5 #define errCANT_LOAD 6 #define errEND_OF_FILE 7 #define errNO_IMAGE 8 static char *errDesc[] = { "none", "code table overflow", "raster has the wrong size", "no colormap present for image", "invalid GIF file", "illegal GIF block type", "unable to load file (unknown reason)", "reached premature end-of-file" "no image found in file", 0 }; const char *errorDesc(int x) { return errDesc[x]; } // ------------------------------------------------------------------------------------- @implementation GifImageRep // ------------------------------------------------------------------------------------- // internal support methods // ------------------------------------------------------------------------------------- /* decode a raster image */ // Note: A 'goto'? Yuck! I know their ugly, but I just haven't remove it yet. sorry. - (int)_readRaster { u_char *ch, buf[260], *fill; u_int count, datm; int i, err, code, bits, thisCode; int codeTable[4096][2]; // LZW compression code data int stack[4096 * 2], *sp; int _datasize, _codesize, _maxsize; int _clear, _avail, _oldcode, _firstcode; /* initialize */ err = errNONE; fill = imageRaster; _datasize = getc(imageStream); _clear = 1 << _datasize; _codesize = _datasize + 1; _avail = _clear + 2; _maxsize = _clear * 2; _oldcode = -1; _firstcode = -1; bits = 0; datm = 0; sp = stack; /* prefill table */ for (i = 0; i < 4096; i++) { codeTable[i][0] = 0; codeTable[i][1] = (i < _clear)? i : 0; } /* decode raster */ for (count = getc(imageStream); count > 0; count = getc(imageStream)) { /* read block */ if (fread(buf, 1, count, imageStream) != count) { err = errEND_OF_FILE; break; } /* process block */ for (ch = buf; count-- > 0; ch++) { datm += (u_int)(*ch) << bits; bits += 8; while (bits >= _codesize) { /* get code */ code = datm & ((1 << _codesize) - 1); datm >>= _codesize; bits -= _codesize; /* clear table */ if (code == _clear) { _codesize = _datasize + 1; _maxsize = _clear * 2; _avail = _clear + 2; _oldcode = -1; _firstcode = -1; sp = stack; for (i = 0; i < 4096; i++) { codeTable[i][0] = 0; codeTable[i][1] = (i < _clear)? i : 0; } continue; } /* end of decompression */ if (code == _clear + 1) goto exitloop; /* for non-standard GIF files */ /* save first code */ if (_firstcode == -1) { _firstcode = _oldcode = code; *fill++ = code; continue; } /* save this code */ thisCode = code; /* code check */ if (code >= _avail) { *sp++ = _firstcode; code = _oldcode; } /* place codes on stack */ while (code >= _clear) { *sp++ = codeTable[code][1]; if (code == codeTable[code][0]) { err = errTABLE_OVERFLOW; goto exitloop; } code = codeTable[code][0]; } *sp++ = _firstcode = codeTable[code][1]; /* save in table */ code = _avail; if (code < 4096) { codeTable[code][0] = _oldcode; codeTable[code][1] = _firstcode; _avail++; if ((_avail >= _maxsize) && (_maxsize < 4096)) { _maxsize *= 2; _codesize++; } } /* save old code */ _oldcode = thisCode; /* flush stack */ while (sp > stack) *fill++ = *--sp; } } } exitloop: if (err == errEND_OF_FILE) { err = errNONE; NXLogError("GIF premature end of file"); } if (!err && (fill != imageRaster + rasterSize)) err = errRASTER_SIZE; return err; } /* load image raster */ - (int)_loadImage { u_char buf[10]; BOOL local, interleaved; int err, left; /* set file position */ if (imagePos) fseek(imageStream, imagePos, SEEK_SET); else imagePos = ftell(imageStream); /* read image information */ fread(buf, 1, 9, imageStream); left = buf[0] + (buf[1] << 8); rasterTop = buf[2] + (buf[3] << 8); pixWide = buf[4] + (buf[5] << 8); pixHigh = buf[6] + (buf[7] << 8); local = (buf[8] & 0x80)? YES : NO; interleaved = (buf[8] & 0x40)? YES : NO; rasterSize = (u_long)pixWide * (u_long)pixHigh; /* convert color map */ if (local) { colorMapCount = 1 << ((buf[8] & 0x7) + 1); fread(colorMap, 3, colorMapCount, imageStream); } else if (!isGlobalMap) return errNO_COLORMAP; /* read image raster */ if (!imageRaster) memset((imageRaster=(u_char*)malloc(rasterSize)), 0, rasterSize); if ((err = [self _readRaster]) && (err != errRASTER_SIZE)) return err; /* remove any interleave */ if (interleaved) { int i, r = 0; u_char *temp = (u_char*)malloc(rasterSize); u_short *tbl = (u_short*)malloc(pixHigh * sizeof(u_short)); for (i = rasterTop ; i < (int)(rasterTop + pixHigh); i += 8) tbl[i] = r++; for (i = rasterTop + 4; i < (int)(rasterTop + pixHigh); i += 8) tbl[i] = r++; for (i = rasterTop + 2; i < (int)(rasterTop + pixHigh); i += 4) tbl[i] = r++; for (i = rasterTop + 1; i < (int)(rasterTop + pixHigh); i += 2) tbl[i] = r++; for (i = 0, r = rasterTop; r < (int)(rasterTop + pixHigh); i++, r++) memcpy(&temp[i * pixWide], &imageRaster[tbl[r] * pixWide], pixWide); free(tbl); free(imageRaster); imageRaster = temp; rasterTop = 0; } /* return successful */ return errNONE; } /* read GIF header info */ - (int)_readGIFHeader { u_int screenWidth, screenHeight; // screen dimensions u_int background; u_char buf[256]; /* rewind file pointer */ rewind(imageStream); /* check gif signiture type */ if (![[self class] validImageType:imageStream]) return errINVALID_FILE; /* read global information */ fread(buf, 1, 7, imageStream); screenWidth = buf[0] + (buf[1] << 8); // not used screenHeight = buf[2] + (buf[3] << 8); // not used isGlobalMap = (buf[4] & 0x80)? YES : NO; // global color map? background = buf[5]; // not used aspectRatio = (buf[6])? ((float)buf[6] + 15.0) / 64.0 : 1.0; // aspect ratio /* load global colormap */ if (isGlobalMap) { colorMapCount = 1 << ((buf[4] & 0x07) + 1); fread(colorMap, 3, colorMapCount, imageStream); } return errNONE; } /* initialize from file */ - (int)_readImageAtPosition:(long int)overridePos { int err; /* read gif header info */ if (err = [self _readGIFHeader]) return err; /* set file position override */ if (overridePos) fseek(imageStream, overridePos, SEEK_SET); /* loop through file */ for(;;) { int err; u_char buf[256], ch = getc(imageStream); switch (ch) { case ',' : if (err = [self _loadImage]) return err; imageNextPos = ftell(imageStream); return errNONE; case '!' : // skip extension for (ch=getc(imageStream); ch=getc(imageStream);) fread(buf, 1, ch, imageStream); break; case '\0': break; /* non-standard files */ case ';' : return imageNextPos? errNONE: errNO_IMAGE; default : return errBAD_BLOCK; } } /* return any error */ return errNONE; } // ------------------------------------------------------------------------------------- // internal init method // ------------------------------------------------------------------------------------- /* init from file stream */ - _initFromFile:(const char*)filename atPos:(long int)overridePos { NXSize pSize; /* generic initialization */ [self initDrawMethod:@selector(_drawImage:) inObject:self]; /* local var init */ imageRaster = (u_char*)nil; pixWide = 0; pixHigh = 0; rasterSize = 0L; isGlobalMap = NO; colorMapCount = 0; imagePos = 0L; imageNextPos = 0L; /* verify stream */ imageStream = fopen(filename, "r"); if (!imageStream) { [self free]; return (id)nil; } /* initialize */ loadError = [self _readImageAtPosition:overridePos]; if (loadError == errNO_IMAGE) { [self free]; return (id)nil; } if (!loadError && (!pixWide || !pixHigh)) loadError = errCANT_LOAD; if (loadError) { NXLogError("GIF stream error: %s", errDesc[loadError]); [self free]; return (id)nil; } /* init size (check aspect ratio) */ pSize.width = (float)pixWide; pSize.height = (float)pixHigh; if (aspectRatio < 1.0) pSize.width *= 1.0 / aspectRatio; else if (aspectRatio > 1.0) pSize.height *= aspectRatio; /* init custom image rep */ [self setSize:&pSize]; [self setPixelsWide:pixWide]; [self setPixelsHigh:pixHigh]; [self setBitsPerSample:8]; [self setAlpha:NO]; [self setNumColors:256]; return self; } // ------------------------------------------------------------------------------------- // public advertised methods // ------------------------------------------------------------------------------------- /* return true if file is gif image (only check file extension) */ + (BOOL)validImageFile:(const char*)filename { char *extn = rindex((char*)filename, '.'); if (!extn) return NO; return strcasecmp(extn,".gif") && strcmp(extn,".g")? NO : YES; } /* check file type */ + (BOOL)validImageType:(FILE*)fd { char buf[16]; fread(buf, 1, 6, fd); return strncasecmp(buf, "GIF87a", 6) && strncasecmp(buf, "GIF89a", 6)? NO : YES; } /* read all images into a list */ + (List*)newListFromFile:(const char*)filename { id list, _class = self; long int pos; /* verify file */ if (![self validImageFile:filename]) return (id)nil; /* start loading images */ list = [[[List alloc] initCount:1] empty]; for (pos = 0L;;) { if (!(self = [[_class alloc] _initFromFile:filename atPos:pos])) break; [list addObject:self]; pos = imageNextPos; } /* return list */ if (![list count]) { [list free]; list = (id)nil; } return list; } /* init from file stream */ - initFromFile:(const char*)filename { if (![[self class] validImageFile:filename]) { [self free]; return (id)nil; } return [self _initFromFile:filename atPos:0L]; } /* free gif */ - free { if (imageRaster) free(imageRaster); if (imageStream) fclose(imageStream); return [super free]; } // ------------------------------------------------------------------------------------- // custom image draw // ------------------------------------------------------------------------------------- /* draw image */ - _drawImage:mySelf { int r, c, bpr; u_char *p; NXRect rect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; const u_char *bitmap[5] = { 0, 0, 0, 0, 0 }; /* make sure the image raster is loaded */ if (loadError || (!imageRaster && (loadError=[self _loadImage]))) return self; /* build image bitmap */ bitmap[0] = p = (u_char*)malloc(3L * rasterSize); for (r = rasterTop; r < (int)(rasterTop + pixHigh); r++) { u_char *s = &imageRaster[r * pixWide]; for (c = 0; c < (int)pixWide; c++, p+=3) memcpy(p, colorMap[s[c]], 3); } /* render bitmap */ rect.size = size; bpr = (7 + pixWide * 8 * 3) / 8; NXDrawBitmap(&rect, pixWide, pixHigh, 8, 3, 8*3, bpr, NO, NO, NX_RGBColorSpace, bitmap); /* free bitmap storage */ free((char*)bitmap[0]); /* return successful */ return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.