/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * filename: m-gif.c * * * * UTIL C-source: Medical Image Conversion Utility * * * * purpose : read and write GIF files * * * * project : (X)MedCon by Erik Nolf * * * * Functions : MdcCheckGIF() - Check for GIF format * * MdcReadGIF() - Read GIF file * * MdcDoExtension() - Handle extensions * * MdcReadGifHeader() - Read header block * * MdcReadGifImageBlk() - Read image block * * MdcReadGifControlBlk() - Read control block * * MdcReadGifPlainTextBlk() - Read plain text block * * MdcReadGifApplicationBlk() - Read application block * * MdcUnpackImage() - Unpack LZW compressed image * * MdcPutGifLine() - Copy line to memory buffer * * MdcWriteGIF() - Write GIF file * * MdcGetGifOpt() - Get specific GIF options * * MdcWriteGifHeader() - Write gif header * * MdcWriteControlBlock() - Write control block * * MdcWriteImageBlock() - Write image block * * MdcWriteImage() - Write LZW compressed image * * MdcWriteCommentBlock() - Write comment block * * MdcWriteLoopBlock() - Write loop block * * MdcWriteApplicationBlock() - Write application block * * MdcInitTable() - Initialize compression table* * MdcFlush() - Flush code the code buffer * * MdcWriteCode() - Write code to code buffer * * * * Notes : Code fragments addapted from Alchemy Mindworks, Inc. * * Original code GIF reader/writer copyright (c) 1991 * * * * "Supercharged bitmapped graphics" * * written by Steve Rimmer * * published by Windcrest(r)/McGraw-Hill * * ISBN: 0-8306-3788-5 * * * * We only write GIF89a animated gifs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* $Id: m-gif.c,v 1.36 2007/05/21 20:16:12 enlf Exp $ */ /* Copyright (C) 1997-2007 by Erik Nolf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Place - Suite 330, Boston, MA 02111-1307, USA. */ /**************************************************************************** H E A D E R S ****************************************************************************/ #include "m-depend.h" #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #ifndef _WIN32 #include #endif #endif #include "medcon.h" /**************************************************************************** D E F I N E S ****************************************************************************/ #define largest_code 4095 /* largest possible code */ #define table_size 5003 /* table dimensions */ static Uint8 code_buffer[259]; /* where the codes go */ static Int16 oldcode[table_size]; /* the table */ static Int16 currentcode[table_size]; static Uint8 newcode[table_size]; static Int16 code_size; static Int16 clear_code; static Int16 eof_code; static Int16 bit_offset; static Int16 byte_offset; static Int16 bits_left; static Int16 max_code; static Int16 free_code; /**************************************************************************** F U N C T I O N S ****************************************************************************/ int MdcCheckGIF(FILEINFO *fi) { MDC_GIFHEADER gh; memset(&gh,0,MDC_GIF_GH_SIZE); if ( fread((char *)&gh,1,MDC_GIF_GH_SIZE,fi->ifp) != MDC_GIF_GH_SIZE ) return(MDC_BAD_READ); if ( memcmp(gh.sig,MDC_GIF_SIG,3) ) return(MDC_FRMT_NONE); return(MDC_FRMT_GIF); } /* unpack a GIF file */ char *MdcReadGIF(FILEINFO *fi) { FILE *fp=fi->ifp; MDC_GIFHEADER gh; MDC_GIFIMAGEBLOCK iblk; Uint32 b, c, img=0, number=0; char *err=NULL; Uint32 bytes; if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading GIF:"); if (MDC_VERBOSE) MdcPrntMesg("GIF Reading <%s> ...",fi->ifname); if (MDC_ECHO_ALIAS == MDC_YES) { MdcEchoAliasName(fi); return(NULL); /* a little pointless for GIF */ } /* initialize structures */ memset(&gh,0,MDC_GIF_GH_SIZE); memset(&iblk,0,MDC_GIF_IBLK_SIZE); /* put some defaults we use */ fi->endian=MDC_FILE_ENDIAN=MDC_LITTLE_ENDIAN; /* always for a GIF */ fi->dim[0] = 4; fi->dim[4]=1; /* make sure it's a GIF file */ if ( MdcReadGifHeader(fp,&gh) != MDC_YES ) return("GIF Bad read gifheader"); if ( memcmp(gh.sig, MDC_GIF_SIG, 3) ) return("No GIF file"); if (MDC_INFO) { MdcPrntScrn("GIFHEADER (%d bytes)\n",MDC_GIF_GH_SIZE); MdcPrintLine('-',MDC_HALF_LENGTH); } fi->bits=8; /* (gh.flags & 0x0007) + 1; */ if (MDC_INFO) { MdcPrntScrn("signature: %.6s\n",gh.sig); MdcPrntScrn("screen width: %d\nscreen height: %d\n", gh.screenwidth,gh.screenheight); MdcPrntScrn("global palette: "); } /* get colour map if there is one */ if (gh.flags & 0x80) { if (fi->map < 2) fi->map=MDC_MAP_PRESENT; c = 3 * (1 << ((gh.flags & 0x0007) + 1)); if (fread(fi->palette,1,c,fp) != c) return("GIF Bad read global palette"); if (MDC_INFO) { MdcPrntScrn("Yes\n"); MdcPrntScrn("bits: %hd\n",(gh.flags & 7)+1 ); MdcPrntScrn("colors: %hd\n",(1 << ((gh.flags & 0x0007)+1))); MdcPrntScrn("sorted: "); if (gh.flags > 0x0008 ) MdcPrntScrn("Yes\n"); else MdcPrntScrn("No\n"); MdcPrntScrn("background: %hd\n", (int)gh.background); MdcPrntScrn("aspect: %hd\n\n",(int)gh.aspect); } } else if (MDC_INFO) MdcPrntScrn("No\n"); /* step through the blocks */ while((c=(Int16)fgetc(fp))==',' || c=='!' || c==0) { /* if it's an image block... */ if (c == ',') { if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,0.0,NULL); if (MDC_INFO) { MdcPrntScrn("\n"); MdcPrintLine('=',MDC_HALF_LENGTH); MdcPrntScrn("\nIMAGEBLOCK %03d (%d bytes)\n",number+1 ,MDC_GIF_IBLK_SIZE); MdcPrintLine('-',MDC_HALF_LENGTH); } /* get the start of the image block */ if (MdcReadGifImageBlk(fp,&iblk) != MDC_YES) return("GIF Bad read imageblock"); number+=1; if (!MdcGetStructID(fi,number)) return("GIF Bad malloc IMG_DATA struct"); /* fill in the IMG_DATA struct */ fi->image[img].width=(Uint32) iblk.width; fi->image[img].height=(Uint32) iblk.height; if (MDC_INFO) { MdcPrntScrn("image left: %hu\n",iblk.left); MdcPrntScrn("image top: %hu\n",iblk.top); MdcPrntScrn("image width: %hu\n",iblk.width); MdcPrntScrn("image height: %hu\n",iblk.height); MdcPrntScrn("interlaced: "); if ( iblk.flags & 0x0040 ) MdcPrntScrn("Yes\n"); else MdcPrntScrn("No\n"); MdcPrntScrn("local palette: "); } /* get the local colour map if there is one */ if (iblk.flags & 0x80) { if (fi->map < 2) fi->map=MDC_MAP_PRESENT; if (MDC_INFO) { MdcPrntScrn("Yes\n"); MdcPrntScrn("sorted: "); if ( iblk.flags & 0x0020 ) MdcPrntScrn("Yes\n"); else MdcPrntScrn("No\n"); } b = 3*(1<<((iblk.flags & 0x0007) + 1)); if (fread(fi->palette,1,b,fp) != b) return("GIF Bad read local palette"); } else if (MDC_INFO) MdcPrntScrn("No\n"); /* get the initial code size */ if ((c=(Int16)fgetc(fp))==EOF) return("GIF Bad read initial code"); fi->image[img].bits = c; fi->image[img].flags = iblk.flags; /* get an image buffer */ bytes=MdcPixels2Bytes(fi->image[img].width*fi->image[img].height*8); if ( (fi->image[img].buf=MdcGetImgBuffer(bytes)) == NULL ) return("GIF Bad malloc image buffer"); if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,0.5,NULL); /* unpack the image */ err = MdcUnpackImage(fi,img); if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,1.0,NULL); /* fill in the FILEINFO struct */ fi->bits=fi->image[img].bits=8; fi->image[img].type=BIT8_U; fi->dim[3] = number; img = number; /* warning if there was an error */ if (err != NULL) { MdcPrntWarn(err); err = MdcHandleTruncated(fi,fi->number,MDC_NO); if (err != NULL) return(err); break; } }else if (c == '!') MdcDoExtension(fi); } if (fi->number == 0) return("GIF No valid images found"); MdcCloseFile(fi->ifp); if (fi->truncated) return("GIF Truncated image file"); return NULL; } /* this function is called when the GIF decoder encounters an extension */ void MdcDoExtension(FILEINFO *fi) { FILE *fp=fi->ifp; MDC_GIFPLAINTEXT pt; MDC_GIFCONTROLBLOCK cb; MDC_GIFAPPLICATION ap; Int16 c,n,i; memset(&pt,0,MDC_GIF_TBLK_SIZE); memset(&cb,0,MDC_GIF_CBLK_SIZE); memset(&ap,0,MDC_GIF_ABLK_SIZE); if (MDC_INFO) { MdcPrntScrn("\n"); MdcPrintLine('=',MDC_HALF_LENGTH); } switch (c=(Int16)fgetc(fp)) { case 0x0001: /* plain text descriptor */ if (MdcReadGifPlainTextBlk(fp,&pt) == MDC_YES) { if (MDC_INFO) { MdcPrntScrn("\nPLAIN TEXT BLOCK\n"); MdcPrintLine('-', MDC_HALF_LENGTH); MdcPrntScrn("This block requires %u bytes\n",pt.blocksize); MdcPrntScrn("Text location at (%u,%u)\n",pt.left,pt.top); MdcPrntScrn("Grid dimensions are %u by %u\n",pt.gridwidth ,pt.gridheight); MdcPrntScrn("Cell dimensions are %u by %u\n",pt.cellwidth ,pt.cellheight); MdcPrntScrn("Foregound colour is %u\n",pt.forecolour); MdcPrntScrn("Background colour is %u\n",pt.backcolour); } do { if ((n=(Int16)fgetc(fp)) != EOF) { for (i=0;i 0 && n != EOF); }else{ MdcPrntWarn("GIF Bad read plain text block"); } break; case 0x00f9: /* graphic control block */ if (MdcReadGifControlBlk(fp,&cb) == MDC_YES) { if (MDC_INFO) { MdcPrntScrn("\nCONTROL BLOCK\n"); MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("This block requires %u bytes\n",cb.blocksize); switch((cb.flags >> 2) & 0x0007) { case 0: MdcPrntScrn("No disposal specified\n"); break; case 1: MdcPrntScrn("Do not dispose\n"); break; case 2: MdcPrntScrn("Dispose to background colour\n"); break; case 3: MdcPrntScrn("Dispose to previous graphic\n"); break; default: MdcPrntScrn("Unknown disposal procedure\n"); break; } if (cb.flags & 0x0002) MdcPrntScrn("User input required - delay for %g seconds\n" ,(float)cb.delay/100.); else MdcPrntScrn("No user input required\n"); if (cb.flags & 0x0001) MdcPrntScrn("Transparent colour: %u\n",cb.transparent_colour); else MdcPrntScrn("No transparent_colour\n"); } }else{ MdcPrntWarn("GIF Bad read control block"); } break; case 0x00fe: /* comment extension */ if (MDC_INFO) { MdcPrntScrn("\nCOMMENT BLOCK\n"); MdcPrintLine('-',MDC_HALF_LENGTH); } do { if ((n=(Int16)fgetc(fp)) != EOF) { if (n > 0) { fi->comment = (char *)MdcRealloc(fi->comment,fi->comm_length+n+2); if (fi->comment == NULL) { MdcPrntWarn("Couldn't allocate comment buffer"); }else if (fi->comm_length==0) fi->comment[fi->comm_length] = '\0'; } for (i=0;icomment != NULL) { fi->comment[fi->comm_length++] = c; } } if ((n <= 0) && (fi->comment != NULL)) { fi->comment[fi->comm_length++] = '\n'; fi->comment[fi->comm_length] = '\0'; } } }while (n > 0 && n != EOF); break; case 0x00ff: /* application extension */ if (MdcReadGifApplicationBlk(fp,&ap) == MDC_YES) { if (MDC_INFO) { MdcPrntScrn("\nAPPLICATION BLOCK\n"); MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("This block requires %d bytes\n",ap.blocksize); MdcPrntScrn("Identification string: %.8s\n",ap.applstring); MdcPrntScrn("Authentication string: %.3s\n",ap.authentication); } do { if ((n=(Int16)fgetc(fp)) != EOF) { if (MDC_INFO) MdcPrntScrn("\nSub-block requires %d bytes:\n",n); for (i=0;i 0 && n != EOF); }else{ MdcPrntWarn("GIF Bad read application block"); } break; default: /* something else */ MdcPrntWarn("GIF Unknown extension 0x%02.2x\n",c & 0x00ff); n=(Int16)fgetc(fp); for (i=0;isig ,bb ,6); memcpy(&gh->screenwidth ,bb+6,2); MdcSWAP(gh->screenwidth); memcpy(&gh->screenheight,bb+8,2); MdcSWAP(gh->screenheight); gh->flags = (Uint8) bb[10]; gh->background = (Uint8) bb[11]; gh->aspect = (Uint8) bb[12]; return(MDC_YES); } int MdcReadGifImageBlk(FILE *fp, MDC_GIFIMAGEBLOCK *ib) { char bb[MDC_GIF_IBLK_SIZE]; if (fread(bb,1,MDC_GIF_IBLK_SIZE,fp) != MDC_GIF_IBLK_SIZE) return(MDC_NO); memcpy(&ib->left ,bb ,2); MdcSWAP(ib->left); memcpy(&ib->top ,bb+2,2); MdcSWAP(ib->top); memcpy(&ib->width ,bb+4,2); MdcSWAP(ib->width); memcpy(&ib->height,bb+6,2); MdcSWAP(ib->height); ib->flags = (Uint8) bb[8]; return(MDC_YES); } int MdcReadGifControlBlk(FILE *fp, MDC_GIFCONTROLBLOCK *cb) { char bb[MDC_GIF_CBLK_SIZE]; if (fread(bb,1,MDC_GIF_CBLK_SIZE,fp) != MDC_GIF_CBLK_SIZE) return(MDC_NO); cb->blocksize = (Uint8) bb[0]; cb->flags = (Uint8) bb[1]; memcpy(&cb->delay,bb+2,2); MdcSWAP(cb->delay); cb->transparent_colour = (Uint8) bb[4]; cb->terminator = (Uint8) bb[5]; return(MDC_YES); } int MdcReadGifPlainTextBlk(FILE *fp, MDC_GIFPLAINTEXT *pt) { char bb[MDC_GIF_TBLK_SIZE]; if (fread(bb,1,MDC_GIF_TBLK_SIZE,fp) != MDC_GIF_TBLK_SIZE) return(MDC_NO); pt->blocksize = (Uint8) bb[0]; memcpy(&pt->left ,bb+1,2); MdcSWAP(pt->left); memcpy(&pt->top ,bb+3,2); MdcSWAP(pt->top); memcpy(&pt->gridwidth ,bb+5,2); MdcSWAP(pt->gridwidth); memcpy(&pt->gridheight,bb+7,2); MdcSWAP(pt->gridheight); pt->cellwidth = (Uint8) bb[9]; pt->cellheight = (Uint8) bb[10]; pt->forecolour = (Uint8) bb[11]; pt->backcolour = (Uint8) bb[12]; return(MDC_YES); } int MdcReadGifApplicationBlk(FILE *fp, MDC_GIFAPPLICATION *ap) { char bb[MDC_GIF_ABLK_SIZE]; if (fread(bb,1,MDC_GIF_ABLK_SIZE,fp) != MDC_GIF_ABLK_SIZE) return(MDC_NO); ap->blocksize = (Uint8) bb[0]; memcpy(ap->applstring ,bb+1,8); memcpy(ap->authentication,bb+9,3); return(MDC_YES); } /* unpack an LZW compressed image */ char *MdcUnpackImage(FILEINFO *fi, Uint32 nr ) { FILE *fp=fi->ifp; Int16 bits=fi->image[nr].bits; IMG_DATA *id=&fi->image[nr]; Uint8 pix; Int16 bits2; /* Bits plus 1 */ Int16 codesize; /* Current code size in bits */ Int16 codesize2; /* Next codesize */ Int16 nextcode; /* Next available table entry */ Int16 thiscode; /* Code being expanded */ Int16 oldtoken; /* Last symbol decoded */ Int16 currentcode; /* Code just read */ Int16 oldcode; /* Code read before this one */ Int16 bitsleft; /* Number of bits left in *p */ Int16 blocksize; /* Bytes in next block */ Int16 line=0; /* next line to write */ Int16 nxtbyte=0; /* next byte to write */ Int16 pass=0; /* pass number for interlaced pictures */ Uint8 *p; /* Pointer to current byte in read buffer */ Uint8 *q; /* Pointer past last byte in read buffer */ Uint8 b[255]; /* Read buffer */ Uint8 *u; /* Stack pointer into firstcodestack */ Uint8 *linebuffer; /* place to store the current line */ static Uint8 firstcodestack[4096]; /* Stack for first codes */ static Uint8 lastcodestack[4096]; /* Statck for previous code */ static Int16 codestack[4096]; /* Stack for links */ static Int16 wordmasktable[] = { 0x0000,0x0001,0x0003,0x0007, 0x000f,0x001f,0x003f,0x007f, 0x00ff,0x01ff,0x03ff,0x07ff, 0x0fff,0x1fff,0x3fff,0x7fff }; static Int16 inctable[] = { 8,8,4,2,0 }; /* interlace increments */ static Int16 startable[] = { 0,4,2,1,0 }; /* interlace starts */ p=q=b; bitsleft = 8; if (bits < 2 || bits > 8) return("GIF Bad symbolsize"); bits2 = 1 << bits; nextcode = bits2 + 2; codesize2 = 1 << (codesize = bits + 1); oldcode=oldtoken=MDC_NO_CODE; if ((linebuffer=(Uint8 *)malloc(id->width)) == NULL) return("GIF Bad malloc linebuffer"); /* loop until something breaks */ for (;;) { if (bitsleft==8) { if (++p >= q && (((blocksize = (Int16)fgetc(fp)) < 1) || (q=(p=b)+fread(b,1,(unsigned)blocksize,fp))< (b+blocksize))) { MdcFree(linebuffer); return("GIF Unexpected EOF (1)"); } bitsleft = 0; } thiscode = *p; if ((currentcode=(codesize+bitsleft)) <= 8) { *p >>= codesize; bitsleft = currentcode; }else { if (++p >= q && (((blocksize = (Int16)fgetc(fp)) < 1) || (q=(p=b)+fread(b,1,(unsigned)blocksize,fp)) < (b+blocksize))) { MdcFree(linebuffer); return("GIF Unexpected EOF (2)"); } thiscode |= *p << (8 - bitsleft); if (currentcode <= 16) *p >>= (bitsleft=currentcode-8); else { if (++p >= q && (((blocksize = (Int16)fgetc(fp)) < 1) || (q=(p=b) + fread(b,1,(unsigned)blocksize,fp)) < (b+blocksize))) { MdcFree(linebuffer); return("GIF Unexpected EOF (3)"); } thiscode |= *p << (16 - bitsleft); *p >>= (bitsleft = currentcode - 16); } } thiscode &= wordmasktable[codesize]; currentcode = thiscode; if (thiscode == (bits2+1)) break; /* found EOI */ if (thiscode > nextcode) { MdcFree(linebuffer); return("GIF Bad compression code"); } if (thiscode == bits2) { nextcode = bits2 + 2; codesize2 = 1 << (codesize = (bits + 1)); oldtoken = oldcode = MDC_NO_CODE; continue; } u = firstcodestack; if (thiscode==nextcode) { if (oldcode==MDC_NO_CODE) { MdcFree(linebuffer); return("GIF Bad first code"); } *u++ = oldtoken; thiscode = oldcode; } while (thiscode >= bits2) { *u++ = lastcodestack[thiscode]; thiscode = codestack[thiscode]; } oldtoken = thiscode; do { pix = (Uint8)thiscode; linebuffer[nxtbyte++]=pix; if (nxtbyte >= id->width) { MdcPutGifLine(id,linebuffer,line); nxtbyte=0; /* check for interlaced image */ if (id->flags & 0x40) { line+=inctable[pass]; if (line >= id->height) line=startable[++pass]; }else ++line; } if (u <= firstcodestack) break; thiscode = *--u; }while(1); if (nextcode < 4096 && oldcode != MDC_NO_CODE) { codestack[nextcode] = oldcode; lastcodestack[nextcode] = oldtoken; if (++nextcode >= codesize2 && codesize < 12) codesize2 = 1 << ++codesize; } oldcode = currentcode; } MdcFree(linebuffer); return(NULL); } /* save one line to memory */ void MdcPutGifLine(IMG_DATA *id, Uint8 *p, Int16 n) { if ( n >= 0 && n < id->height ) memcpy(id->buf+((Uint32)n*id->width),p,id->width); } char *MdcWriteGIF(FILEINFO *fi) { Uint32 nr; Uint8 *buf8; MDC_GIFOPT opt; MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN; /* always for a gif */ /* check supported */ if (fi->type == COLRGB) return("GIF True color files unsupported"); memset(&opt,0,sizeof(MDC_GIFOPT)); if ((MDC_GIF_OPTIONS == MDC_YES) && (XMDC_GUI == MDC_NO)) { MdcGetGifOpt(fi,&opt); }else { opt.transp = MDC_NO; opt.loop = MDC_YES; opt.delay = GIF_DELAY; } if (XMDC_GUI == MDC_NO) { MdcDefaultName(fi,MDC_FRMT_GIF,fi->ofname,fi->ifname); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing GIF:"); if (MDC_VERBOSE) MdcPrntMesg("GIF Writing <%s> ...",fi->ofname); if (MDC_FILE_STDOUT == MDC_YES) { fi->ofp = stdout; }else{ if (MdcKeepFile(fi->ofname)) return("GIF File exists!!"); if ( (fi->ofp=fopen(fi->ofname,"wb")) == NULL ) return ("GIF Couldn't open file"); } if (MDC_FORCE_INT != MDC_NO) { if (MDC_FORCE_INT != BIT8_U) { MdcPrntWarn("GIF Only Uint8 pixels supported"); } } /* check supported things */ if (MDC_QUANTIFY || MDC_CALIBRATE) { MdcPrntWarn("GIF Normalization loses quantified values!"); } if ( MdcWriteGifHeader(fi,&opt) ) return("GIF Bad write screen description"); if ( MdcWriteCommentBlock(fi,MDC_LIBVERS) ) return("GIF Bad write comment block"); if ( fi->acquisition_type != MDC_ACQUISITION_UNKNOWN ) if ( MdcMakeScanInfoStr(fi) ) { if ( MdcWriteCommentBlock(fi,mdcbufr) ) { return("GIF Bad write scan info comment block"); } } if ( (fi->number > 1) && (opt.loop == MDC_YES) ) { if ( MdcWriteLoopBlock(fi,"NETSCAPE","2.0") ) return ("GIF Bad write loop block"); } for ( nr=0; nrnumber; nr++) { if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL); if ( (fi->number > 1) || (opt.transp == MDC_YES)) if ( MdcWriteControlBlock(fi,&opt,nr) ) return("GIF Bad write control block"); if ( MdcWriteImageBlock(fi,nr) ) return("GIF Bad write image block"); if (fi->image[nr].type != BIT8_U) { if ( (buf8=MdcGetImgBIT8_U(fi, nr)) == NULL) return("GIF Bad malloc new image buffer"); if ( MdcWriteImage(buf8,fi,nr) ) { MdcFree(buf8); return("GIF Bad compression (1)"); }else{ MdcFree(buf8); } }else{ if ( MdcWriteImage(fi->image[nr].buf,fi,nr) ) return("GIF Bad compression (2)"); } } if ( MdcWriteApplicationBlock(fi,MDC_PRGR,"NLF")) return("GIF Bad write application block"); if ( fputc(';',fi->ofp) == EOF ) return("GIF Bad write terminator code"); MdcCloseFile(fi->ofp); return NULL; } void MdcGetGifOpt(FILEINFO *fi, MDC_GIFOPT *opt) { opt->loop = MDC_YES; opt->transp = MDC_YES; if (MDC_FILE_STDIN == MDC_YES) return; /* stdin already in use */ MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("\tGIF OPTIONS\t\tORIG FILE: %s\n",fi->ifname); MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("\n\tSelect color map:\n\n"); MdcPrntScrn("\t\t%d -> present\n",MDC_MAP_PRESENT); MdcPrntScrn("\t\t%d -> gray\n",MDC_MAP_GRAY); MdcPrntScrn("\t\t%d -> rainbow\n",MDC_MAP_RAINBOW); MdcPrntScrn("\t\t%d -> combined\n",MDC_MAP_COMBINED); MdcPrntScrn("\t\t%d -> hotmetal\n",MDC_MAP_HOTMETAL); MdcPrntScrn("\t\t%d -> loaded LUT\n",MDC_MAP_LOADED); MdcPrntScrn("\n\tYour choice [%d]? ",(int)fi->map); if (!MdcPutDefault(mdcbufr)) { fi->map = (Uint8)atoi(mdcbufr); MdcGetColorMap((int)fi->map,fi->palette); } if (fi->number > 1) { MdcPrntScrn("\n\tInsert a display loop [yes]? "); mdcbufr[0]='y'; if (!MdcPutDefault(mdcbufr)) if (mdcbufr[0]=='n' || mdcbufr[0]=='N') opt->loop = MDC_NO; MdcPrntScrn("\n\tDelay 1/100ths of a second [%3d]? ", GIF_DELAY); if (!MdcPutDefault(mdcbufr)) { opt->delay = (Uint16)atoi(mdcbufr); }else{ opt->delay = GIF_DELAY; } } MdcPrntScrn("\n\tInsert transparent color [yes]? "); mdcbufr[0]='y'; if (!MdcPutDefault(mdcbufr)) if (mdcbufr[0]=='n' || mdcbufr[0]=='N') opt->transp = MDC_NO; if (opt->transp == MDC_YES) { MdcPrntScrn("\n\tTransparent color [%u]? ",opt->transp_color); if (!MdcPutDefault(mdcbufr)) opt->transp_color = (Uint8)atoi(mdcbufr); } MdcPrntScrn("\n\tBackground color [%u]? ",opt->bground_color); if (!MdcPutDefault(mdcbufr)) opt->bground_color= (Uint8)atoi(mdcbufr); MdcPrntScrn("\n"); MdcPrintLine('-',MDC_FULL_LENGTH); } /* write the header */ int MdcWriteGifHeader(FILEINFO *fi, MDC_GIFOPT *opt) { MDC_GIFHEADER gh; unsigned int bits = 8; /* we only write 8-bit gifs */ /* fill the header struct */ memset(&gh,0,MDC_GIF_GH_SIZE); memcpy(gh.sig,MDC_GIF89_SIG,6); gh.screenwidth=(Int16)fi->mwidth; gh.screenheight=(Int16)fi->mheight; gh.background=opt->bground_color; gh.aspect=0; gh.flags=0; /* set up the global flags */ if (fi->palette == NULL) gh.flags=(((bits-1) & 0x07)<<4); else gh.flags = (0x80 | ((bits-1)<<4) | ((bits-1) & 0x07)); MdcSWAP(gh.screenwidth); MdcSWAP(gh.screenheight); /* write the header */ fwrite((char *)&gh,1,MDC_GIF_GH_SIZE,fi->ofp); /* write the colour map */ if (fi->palette != NULL) fwrite(fi->palette,1U,3U*(1U<ofp); return(ferror(fi->ofp)); } int MdcWriteControlBlock(FILEINFO *fi, MDC_GIFOPT *opt, Uint32 n) { MDC_GIFCONTROLBLOCK cb; memset(&cb,0,MDC_GIF_CBLK_SIZE); fputc('!',fi->ofp); /* say it's an extension block */ fputc(0xf9,fi->ofp); /* say it's a control block */ cb.blocksize=0x04; cb.flags=0x00; if (fi->number > 1 ) { cb.flags^=0x02; cb.flags<<=2; /* dispose to background */ cb.flags^=0x02; /* wait for user input */ cb.delay = opt->delay; /* delay to dispose 1/100 sec */ } if (opt->transp == MDC_YES) { /* transparent color */ cb.flags^=0x01; cb.transparent_colour = opt->transp_color; } MdcSWAP(cb.delay); fwrite((char *)&cb,1,MDC_GIF_CBLK_SIZE,fi->ofp); return(ferror(fi->ofp)); } /* write an image descriptor block */ int MdcWriteImageBlock(FILEINFO *fi, Uint32 n) { MDC_GIFIMAGEBLOCK ib; Uint8 *palette=NULL; int bits = 8; /* we only write 8-bit gifs */ memset(&ib,0,MDC_GIF_IBLK_SIZE); /* fill the image block struct */ fputc(',',fi->ofp); ib.left=0; ib.top=0; ib.width=fi->image[n].width; ib.height=fi->image[n].height; /* set the local flags */ if(palette==NULL) ib.flags=bits-1; else ib.flags=((bits-1) & 0x07) | 0x80; MdcSWAP(ib.left); MdcSWAP(ib.top); MdcSWAP(ib.width); MdcSWAP(ib.height); /* write the block */ fwrite((char *)&ib,1,MDC_GIF_IBLK_SIZE,fi->ofp); return(ferror(fi->ofp)); } /* compress an image */ int MdcWriteImage(Uint8 * buffer, FILEINFO *fi, Uint32 n) { FILE *fp=fi->ofp; Uint8 *pix=buffer; Int16 prefix_code; Int16 suffix_char; Int16 hx,d; Uint16 min_code_size=8; Uint32 i, width=fi->image[n].width, height=fi->image[n].height; /* make sure the initial code size is legal */ if (min_code_size < 2 || min_code_size > 9) { /* monochrome images have two bits in LZW compression */ if (min_code_size == 1) min_code_size = 2; else return(EOF); } /* write initial code size */ fputc(min_code_size,fp); /* initialize the encoder */ bit_offset=0; MdcInitTable(min_code_size); MdcWriteCode(fp,clear_code); if (pix == NULL) return(EOF); suffix_char=(Int16)pix[0]; /* initialize the prefix */ prefix_code = suffix_char; /* get a character to compress */ for ( i=1; i<(width*height); i++) { if ( i == (width*height) ) break; suffix_char = (Int16)pix[i]; /* derive an index into the code table */ hx=(prefix_code ^ (suffix_char << 5)) % table_size; d=1; for (;;) { /* see if the code is in the table */ if (currentcode[hx] == 0) { /* if not, put it there */ MdcWriteCode(fp,prefix_code); d = free_code; /* find the next free code */ if (free_code <= largest_code) { oldcode[hx] = prefix_code; newcode[hx] = suffix_char; currentcode[hx] = free_code; free_code++; } /* expand the code size or scrap the table */ if (d == max_code) { if (code_size < 12) { code_size++; max_code <<= 1; }else{ MdcWriteCode(fp,clear_code); MdcInitTable(min_code_size); } } prefix_code = suffix_char; break; } if (oldcode[hx] == prefix_code && newcode[hx] == suffix_char) { prefix_code = currentcode[hx]; break; } hx += d; d += 2; if(hx >= table_size) hx -= table_size; } } /* write the prefix code */ MdcWriteCode(fp,prefix_code); /* and the end of file code */ MdcWriteCode(fp,eof_code); /* MdcFlush the buffer */ if(bit_offset > 0) MdcFlush(fp,(bit_offset+7)/8); /* write a zero length block */ MdcFlush(fp,0); return(ferror(fp)); } int MdcWriteCommentBlock(FILEINFO *fi, const char *comment) { int n; n=strlen(comment); fputc('!',fi->ofp); /* say it's an extension block */ fputc(0xfe,fi->ofp); /* say it's a comment */ do { if(n > 255) { fputc(255,fi->ofp); fwrite(comment,1,255,fi->ofp); comment +=255; n-=255; } else { fputc(n,fi->ofp); fwrite(comment,1,(unsigned)n,fi->ofp); fputc(0,fi->ofp); n=0; } } while(n); return(ferror(fi->ofp)); } int MdcWriteLoopBlock(FILEINFO *fi, const char *applstr, const char *auth) { MDC_GIFAPPLICATION ap; memset(&ap,0,MDC_GIF_ABLK_SIZE); fputc('!',fi->ofp); fputc(0xff,fi->ofp); ap.blocksize=0x0b; memcpy(ap.applstring,applstr,8); memcpy(ap.authentication,auth,3); fwrite((char *)&ap,1,MDC_GIF_ABLK_SIZE,fi->ofp); fputc(0x03,fi->ofp); fputc(0x01,fi->ofp); fputc(0xe8,fi->ofp); fputc(0x03,fi->ofp); fputc(0,fi->ofp); return(ferror(fi->ofp)); } int MdcWriteApplicationBlock(FILEINFO *fi, const char *applstr, const char *auth) { MDC_GIFAPPLICATION ap; memset(&ap,0,MDC_GIF_ABLK_SIZE); fputc('!',fi->ofp); fputc(0xff,fi->ofp); ap.blocksize=0x0b; memcpy(ap.applstring,applstr,7); memcpy(ap.authentication,auth,3); fwrite((char *)&ap,1,MDC_GIF_ABLK_SIZE,fi->ofp); fputc(0,fi->ofp); return(ferror(fi->ofp)); } /* initialize the code table */ void MdcInitTable(Int16 min_code_size) { Int16 i; code_size=min_code_size+1; clear_code=(1<> 3; bits_left = bit_offset & 7; /* eNlf: BUG?? Problem: */ /* eNlf: BUG?? We found one image that didn't compress well */ /* eNlf: BUG?? with the original code of (byte_offset>=254) */ /* eNlf: BUG?? Decoding gave error "GIF Unexpected EOF (2)" */ /* eNlf: BUG?? Solution: */ /* eNlf: BUG?? We changed de code into (byte_offset>=253) */ /* eNlf: BUG?? and that image got compressed (others too) */ /* eNlf: BUG?? Perhaps our solution implicates a new BUG?? */ /* eNlf: BUG?? for other images! Let's hope it doesn't ... */ /* eNlf:if(byte_offset >= 254) { original code fragment */ if(byte_offset >= 253) { /* new code fragment */ MdcFlush(fp,byte_offset); code_buffer[0] = code_buffer[byte_offset]; bit_offset = bits_left; byte_offset = 0; } if(bits_left > 0) { temp = ((Int32)code << bits_left) | code_buffer[byte_offset]; code_buffer[byte_offset]=temp; code_buffer[byte_offset+1]=(temp >> 8); code_buffer[byte_offset+2]=(temp >> 16); } else { code_buffer[byte_offset] = code; code_buffer[byte_offset+1]=(code >> 8); } bit_offset += code_size; }