/* encoder.c - GIF Encoder and associated routines */ #include #include #ifndef XFRACT #include #endif /* see Fractint.c for a description of the "include" hierarchy */ #include "port.h" #include "prototyp.h" #include "fractype.h" static int compress(int rowlimit); static int _fastcall shftwrite(BYTE far * color, int numcolors); static int _fastcall extend_blk_len(int datalen); static int _fastcall put_extend_blk(int block_id, int block_len, char far * block_data); static int _fastcall store_item_name(char *); static void _fastcall setup_save_info(struct fractal_info far * save_info); #ifdef XFRACT int fake_lut = 0; #endif /* Save-To-Disk Routines (GIF) The following routines perform the GIF encoding when the 's' key is pressed. The compression logic in this file has been replaced by the classic UNIX compress code. We have extensively modified the sources to fit Fractint's needs, but have left the original credits where they appear. Thanks to the original authors for making available these classic and reliable sources. Of course, they our not responsible for all the changes we have made to integrate their sources into Fractint. MEMORY ALLOCATION There are two large arrays: long htab[HSIZE] (5003*4 = 20012 bytes) unsigned short codetab[HSIZE] (5003*2 = 10006 bytes) At the moment these arrays reuse extraseg and strlocn, respectively. */ static int numsaves = 0; /* For adjusting 'save-to-disk' filenames */ static FILE *g_outfile; static int last_colorbar; static int save16bit; static int outcolor1s, outcolor2s; static int startbits; static BFCODE paletteBW[] = { /* B&W palette */ 0, 0, 0, 63, 63, 63, }; #ifndef XFRACT static BFCODE paletteCGA[] = { /* 4-color (CGA) palette */ 0, 0, 0, 21, 63, 63, 63, 21, 63, 63, 63, 63, }; #endif static BFCODE paletteEGA[] = { /* 16-color (EGA/CGA) pal */ 0, 0, 0, 0, 0, 42, 0, 42, 0, 0, 42, 42, 42, 0, 0, 42, 0, 42, 42, 21, 0, 42, 42, 42, 21, 21, 21, 21, 21, 63, 21, 63, 21, 21, 63, 63, 63, 21, 21, 63, 21, 63, 63, 63, 21, 63, 63, 63, }; int savetodisk(char *filename) /* save-to-disk routine */ { char tmpmsg[41]; /* before openfile in case of overrun */ char openfile[FILE_MAX_PATH], openfiletype[10]; char tmpfile[FILE_MAX_PATH]; char *period; int newfile; int i, j, interrupted, outcolor1, outcolor2; restart: save16bit = disk16bit; if (gif87a_flag) /* not storing non-standard fractal info */ save16bit = 0; strcpy(openfile, filename); /* decode and open the filename */ strcpy(openfiletype, DEFAULTFRACTALTYPE); /* determine the file * extension */ if (save16bit) strcpy(openfiletype, ".pot"); if ((period = has_ext(openfile)) != NULL) { strcpy(openfiletype, period); *period = 0; } if (resave_flag != 1) updatesavename(filename); /* for next time */ strcat(openfile, openfiletype); strcpy(tmpfile, openfile); if (access(openfile, 0) != 0)/* file doesn't exist */ newfile = 1; else { /* file already exists */ static FCODE fractint_tmp[] = {"fractint.tmp"}; if (overwrite == 0) { if (resave_flag == 0) goto restart; if (started_resaves == 0) { /* first save of a savetime set */ updatesavename(filename); goto restart; } } if (access(openfile, 2) != 0) { sprintf(tmpmsg, s_cantwrite, openfile); stopmsg(0, tmpmsg); return -1; } newfile = 0; i = strlen(tmpfile); while (--i >= 0 && tmpfile[i] != SLASHC) tmpfile[i] = 0; far_strcat(tmpfile, fractint_tmp); } started_resaves = (resave_flag == 1) ? 1 : 0; if (resave_flag == 2) /* final save of savetime set? */ resave_flag = 0; if ((g_outfile = fopen(tmpfile, "wb")) == NULL) { sprintf(tmpmsg, s_cantcreate, tmpfile); stopmsg(0, tmpmsg); return -1; } if (dotmode == 11) { /* disk-video */ char buf[61]; extract_filename(tmpmsg, openfile); sprintf(buf, "Saving %s", tmpmsg); dvid_status(1, buf); } #ifdef XFRACT else { putstring(3, 0, 0, "Saving to:"); putstring(4, 0, 0, openfile); putstring(5, 0, 0, " "); } #endif busy = 1; if (debugflag != 200) interrupted = encoder(); else interrupted = timer(2, NULL); /* invoke encoder() via timer */ busy = 0; fclose(g_outfile); if (interrupted) { static FCODE s_delete[] = {"delete the file,\ncontinue to keep the partial image."}; static FCODE s_retain[] = {"retain the original file,\ncontinue to replace original with new partial image."}; char buf[200]; sprintf(buf, "Save of %s interrupted.\nCancel to ", openfile); if (newfile) far_strcat(buf,s_delete); else far_strcat(buf,s_retain); interrupted = 1; if (stopmsg(2, buf) < 0) { interrupted = -1; unlink(tmpfile); } } if (newfile == 0 && interrupted >= 0) { /* replace the real file */ unlink(openfile); /* success assumed since we checked */ rename(tmpfile, openfile);/* earlier with access */ } if (dotmode != 11) { /* supress this on disk-video */ if (active_system == 0) { /* no bars in Windows version */ outcolor1 = outcolor1s; outcolor2 = outcolor2s; for (j = 0; j <= last_colorbar; j++) { if ((j & 4) == 0) { if (++outcolor1 >= colors) outcolor1 = 0; if (++outcolor2 >= colors) outcolor2 = 0; } for (i = 0; 250 * i < xdots; i++) { /* clear vert status bars */ putcolor(i, j, getcolor(i, j) ^ outcolor1); putcolor(xdots - 1 - i, j, getcolor(xdots - 1 - i, j) ^ outcolor2); } } } #ifdef XFRACT putstring(5, 0, 0, "Saving done\n"); #endif } else /* disk-video */ dvid_status(1, ""); if (interrupted) { static FCODE msg[] = {" *interrupted* save "}; texttempmsg(msg); if (initbatch >= 1) initbatch = 3; /* if batch mode, set error level */ return -1; } if (timedsave == 0) { buzzer(0); if (initbatch == 0) { extract_filename(tmpfile, openfile); sprintf(tmpmsg, " File saved as %s ", tmpfile); texttempmsg(tmpmsg); } } if (initsavetime < 0) goodbye(); return 0; } int encoder() { int i, width, rowlimit, interrupted; BYTE bitsperpixel, x; struct fractal_info save_info; if (initbatch) /* flush any impending keystrokes */ while (keypressed()) getakey(); setup_save_info(&save_info); #ifndef XFRACT bitsperpixel = 0; /* calculate bits / pixel */ for (i = colors; i >= 2; i /= 2) bitsperpixel++; startbits = bitsperpixel + 1;/* start coding with this many bits */ if (colors == 2) startbits++; /* B&W Klooge */ #else if (colors == 2) { bitsperpixel = 1; startbits = 3; } else { bitsperpixel = 8; startbits = 9; } #endif if (gif87a_flag == 1) { if (fwrite("GIF87a", 6, 1, g_outfile) != 1) goto oops; /* old GIF Signature */ } else { if (fwrite("GIF89a", 6, 1, g_outfile) != 1) goto oops; /* new GIF Signature */ } width = xdots; rowlimit = ydots; if (save16bit) { /* pot16bit info is stored as: file: double width rows, right side * of row is low 8 bits diskvid: ydots rows of colors followed by ydots * rows of low 8 bits decoder: returns (row of color info then row of * low 8 bits) * ydots */ rowlimit <<= 1; width <<= 1; } if (write2(&width, 2, 1, g_outfile) != 1) goto oops; /* screen descriptor */ if (write2(&ydots, 2, 1, g_outfile) != 1) goto oops; x = (BYTE) (128 + ((6 - 1) << 4) + (bitsperpixel - 1)); /* color resolution == 6 * bits worth */ if (write1(&x, 1, 1, g_outfile) != 1) goto oops; if (fputc(0, g_outfile) != 0) goto oops; /* background color */ i = 0; if (viewwindow /* less than full screen? */ && (viewxdots == 0 || viewydots == 0)) /* and we picked the dots? */ i = (int) (((double) sydots / (double) sxdots) * 64.0 / screenaspect - 14.5); else /* must risk loss of precision if numbers low */ i = (int) ((((double) ydots / (double) xdots) / finalaspectratio) * 64 - 14.5); if (i < 1) i = 1; if (i > 255) i = 255; if (gif87a_flag) i = 0; /* for some decoders which can't handle * aspect */ if (fputc(i, g_outfile) != i) goto oops; /* pixel aspect ratio */ #ifndef XFRACT if (colors == 256) { /* write out the 256-color palette */ if (gotrealdac) { /* got a DAC - must be a VGA */ if (!shftwrite((BYTE far *) dacbox, colors)) goto oops; #else if (colors > 2) { if (gotrealdac || fake_lut) { /* got a DAC - must be a VGA */ if (!shftwrite((BYTE far *) dacbox, 256)) goto oops; #endif } else { /* uh oh - better fake it */ for (i = 0; i < 256; i += 16) if (!shftwrite((BYTE far *)paletteEGA, 16)) goto oops; } } if (colors == 2) { /* write out the B&W palette */ if (!shftwrite((BYTE far *)paletteBW, colors)) goto oops; } #ifndef XFRACT if (colors == 4) { /* write out the CGA palette */ if (!shftwrite((BYTE far *)paletteCGA, colors)) goto oops; } if (colors == 16) { /* Either EGA or VGA */ if (gotrealdac) { if (!shftwrite((BYTE *) dacbox, colors)) goto oops; } else { /* no DAC - must be an EGA */ if (!shftwrite((BYTE far *)paletteEGA, colors)) goto oops; } } #endif if (fwrite(",", 1, 1, g_outfile) != 1) goto oops; /* Image Descriptor */ i = 0; if (write2(&i, 2, 1, g_outfile) != 1) goto oops; if (write2(&i, 2, 1, g_outfile) != 1) goto oops; if (write2(&width, 2, 1, g_outfile) != 1) goto oops; if (write2(&ydots, 2, 1, g_outfile) != 1) goto oops; if (write1(&i, 1, 1, g_outfile) != 1) goto oops; bitsperpixel = (BYTE) (startbits - 1); if (write1(&bitsperpixel, 1, 1, g_outfile) != 1) goto oops; interrupted = compress(rowlimit); if(ferror(g_outfile)) goto oops; if (fputc(0, g_outfile) != 0) goto oops; if (gif87a_flag == 0) { /* store non-standard fractal info */ /* loadfile.c has notes about extension block structure */ if (interrupted) save_info.calc_status = 0; /* partial save is not resumable */ save_info.tot_extend_len = 0; if (resume_info != 0 && save_info.calc_status == 2) { /* resume info block, 002 */ save_info.tot_extend_len += extend_blk_len(resume_len); MoveFromMemory((BYTE *)block,(U16)1,(long)resume_len,0,resume_info); if (!put_extend_blk(2, resume_len, (char far *)block)) goto oops; } /* save_info.fractal_type gets modified in setup_save_info() in float only version, so we need to use fractype. JCO 06JAN01 */ /* if (save_info.fractal_type == FORMULA || save_info.fractal_type == FFORMULA) */ if (fractype == FFORMULA) save_info.tot_extend_len += store_item_name(FormName); /* if (save_info.fractal_type == LSYSTEM) */ if (fractype == LSYSTEM) save_info.tot_extend_len += store_item_name(LName); /* if (save_info.fractal_type == IFS || save_info.fractal_type == IFS3D) */ if (fractype == IFS || fractype == IFS3D) save_info.tot_extend_len += store_item_name(IFSName); if (display3d <= 0 && rangeslen) { /* ranges block, 004 */ save_info.tot_extend_len += extend_blk_len(rangeslen * 2); #ifdef XFRACT fix_ranges(ranges, rangeslen, 0); put_extend_blk(4, rangeslen * 2, (char far *) ranges); #else if (!put_extend_blk(4, rangeslen * 2, (char far *) ranges)) goto oops; #endif } /* Extended parameters block 005 */ if (bf_math) { save_info.tot_extend_len += extend_blk_len(22 * (bflength + 2)); /* note: this assumes variables allocated in order starting with * bfxmin in init_bf2() in BIGNUM.C */ if (!put_extend_blk(5, 22 * (bflength + 2), (char far *) bfxmin)) goto oops; } /* Extended parameters block 006 */ if (evolving&1) { struct evolution_info esave_info; int i; struct evolution_info resume_e_info; GENEBASE gene[NUMGENES]; MoveFromMemory((BYTE *)&gene, (U16)sizeof(gene), 1L, 0L, gene_handle); if (evolve_handle == 0 || calc_status == 4) { esave_info.paramrangex = paramrangex; esave_info.paramrangey = paramrangey; esave_info.opx = opx; esave_info.opy = opy; esave_info.odpx = (short)odpx; esave_info.odpy = (short)odpy; esave_info.px = (short)px; esave_info.py = (short)py; esave_info.sxoffs = (short)sxoffs; esave_info.syoffs = (short)syoffs; esave_info.xdots = (short)xdots; esave_info.ydots = (short)ydots; esave_info.gridsz = (short)gridsz; esave_info.evolving = (short)evolving; esave_info.this_gen_rseed = (unsigned short)this_gen_rseed; esave_info.fiddlefactor = fiddlefactor; esave_info.ecount = (short) (gridsz * gridsz); /* flag for done */ } else { /* we will need the resuming information */ MoveFromMemory((BYTE *)&resume_e_info,(U16)sizeof(resume_e_info),1L,0L,evolve_handle); esave_info.paramrangex = resume_e_info.paramrangex; esave_info.paramrangey = resume_e_info.paramrangey; esave_info.opx = resume_e_info.opx; esave_info.opy = resume_e_info.opy; esave_info.odpx = (short)resume_e_info.odpx; esave_info.odpy = (short)resume_e_info.odpy; esave_info.px = (short)resume_e_info.px; esave_info.py = (short)resume_e_info.py; esave_info.sxoffs = (short)resume_e_info.sxoffs; esave_info.syoffs = (short)resume_e_info.syoffs; esave_info.xdots = (short)resume_e_info.xdots; esave_info.ydots = (short)resume_e_info.ydots; esave_info.gridsz = (short)resume_e_info.gridsz; esave_info.evolving = (short)resume_e_info.evolving; esave_info.this_gen_rseed = (unsigned short)resume_e_info.this_gen_rseed; esave_info.fiddlefactor = resume_e_info.fiddlefactor; esave_info.ecount = resume_e_info.ecount; } for (i = 0; i < NUMGENES; i++) esave_info.mutate[i] = (short)gene[i].mutate; for (i = 0; i < sizeof(esave_info.future) / sizeof(short); i++) esave_info.future[i] = 0; /* some XFRACT logic for the doubles needed here */ #ifdef XFRACT decode_evolver_info(&esave_info, 0); #endif /* evolution info block, 006 */ put_extend_blk(6, sizeof(esave_info), (char far *) &esave_info); save_info.tot_extend_len += extend_blk_len(sizeof(esave_info)); } /* main and last block, 001 */ save_info.tot_extend_len += extend_blk_len(FRACTAL_INFO_SIZE); #ifdef XFRACT decode_fractal_info(&save_info, 0); #endif if (!put_extend_blk(1, FRACTAL_INFO_SIZE, (char far *) &save_info)) { goto oops; } } if (fwrite(";", 1, 1, g_outfile) != 1) goto oops; /* GIF Terminator */ return (interrupted); oops: { static FCODE msg[] = {"Error Writing to disk (Disk full?)"}; fflush(g_outfile); stopmsg(0,msg); return 1; } } /* shift IBM colors to GIF */ static int _fastcall shftwrite(BYTE far * color, int numcolors) { BYTE thiscolor; int i, j; for (i = 0; i < numcolors; i++) for (j = 0; j < 3; j++) { thiscolor = color[3 * i + j]; thiscolor = (BYTE) (thiscolor << 2); thiscolor = (BYTE) (thiscolor + (BYTE) (thiscolor >> 6)); if (fputc(thiscolor, g_outfile) != (int) thiscolor) return (0); } return (1); } static int _fastcall extend_blk_len(int datalen) { return (datalen + (datalen + 254) / 255 + 15); /* data + 1.per.block + 14 for id + 1 for null at end */ } static int _fastcall put_extend_blk(int block_id, int block_len, char far * block_data) { int i, j; char header[15]; strcpy(header, "!\377\013fractint"); sprintf(&header[11], "%03u", block_id); if (fwrite(header, 14, 1, g_outfile) != 1) return (0); i = (block_len + 254) / 255; while (--i >= 0) { block_len -= (j = min(block_len, 255)); if (fputc(j, g_outfile) != j) return (0); while (--j >= 0) fputc(*(block_data++), g_outfile); } if (fputc(0, g_outfile) != 0) return (0); return (1); } static int _fastcall store_item_name(char *nameptr) { struct formula_info fsave_info; int i; for (i = 0; i < 40; i++) fsave_info.form_name[i] = 0; /* initialize string */ strcpy(fsave_info.form_name, nameptr); if (fractype == FFORMULA) { fsave_info.uses_p1 = (short) uses_p1; fsave_info.uses_p2 = (short) uses_p2; fsave_info.uses_p3 = (short) uses_p3; fsave_info.uses_ismand = (short) uses_ismand; fsave_info.ismand = (short) ismand; fsave_info.uses_p4 = (short) uses_p4; fsave_info.uses_p5 = (short) uses_p5; } else { fsave_info.uses_p1 = 0; fsave_info.uses_p2 = 0; fsave_info.uses_p3 = 0; fsave_info.uses_ismand = 0; fsave_info.ismand = 0; fsave_info.uses_p4 = 0; fsave_info.uses_p5 = 0; } for (i = 0; i < sizeof(fsave_info.future) / sizeof(short); i++) fsave_info.future[i] = 0; /* formula/lsys/ifs info block, 003 */ put_extend_blk(3, sizeof(fsave_info), (char far *) &fsave_info); return (extend_blk_len(sizeof(fsave_info))); } static void _fastcall setup_save_info(struct fractal_info far * save_info) { /* converts from new to old fractal types */ static SIFCODE new2old[] = { 4, 67, 66, 6, 7, 8, 9, 11, 18, 26, 27, 31, 32, 33, 35, 37, 39, 40, 42, 47, 48, 49, 53, 54, 55, 56, 59, 60, 61, 63, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 86, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 102, 103, 104, 105, 107, 108, 110, 112, 114, 118, 120, 121, 122, 123, 124, 125, 126, 128, 130, 132, 134, 136, 138, 139, 140, 141, 142, 143, 145, 146, 148, 150, 151, 152, 154, 155, 156, 157, 158, 159, 160, 162, 164, 165, 166, 167, 168, 169, 170, 171 }; int i; if (fractype != FFORMULA) maxfn = 0; /* set save parameters in save structure */ far_strcpy(save_info->info_id, INFO_ID); save_info->version = 15; /* file version, independent of system */ /* increment this EVERY time the fractal_info structure changes */ if (maxit <= SHRT_MAX) save_info->iterationsold = (short) maxit; else save_info->iterationsold = (short) SHRT_MAX; save_info->fractal_type = (short) new2old[fractype]; save_info->xmin = xxmin; save_info->xmax = xxmax; save_info->ymin = yymin; save_info->ymax = yymax; save_info->creal = param[0]; save_info->cimag = param[1]; save_info->videomodeax = (short) videoentry.videomodeax; save_info->videomodebx = (short) videoentry.videomodebx; save_info->videomodecx = (short) videoentry.videomodecx; save_info->videomodedx = (short) videoentry.videomodedx; save_info->dotmode = (short) (videoentry.dotmode % 100); save_info->xdots = (short) videoentry.xdots; save_info->ydots = (short) videoentry.ydots; save_info->colors = (short) videoentry.colors; save_info->parm3 = 0; /* pre version==7 fields */ save_info->parm4 = 0; save_info->dparm3 = param[2]; save_info->dparm4 = param[3]; save_info->dparm5 = param[4]; save_info->dparm6 = param[5]; save_info->dparm7 = param[6]; save_info->dparm8 = param[7]; save_info->dparm9 = param[8]; save_info->dparm10 = param[9]; save_info->fillcolor = (short) fillcolor; save_info->potential[0] = (float) potparam[0]; save_info->potential[1] = (float) potparam[1]; save_info->potential[2] = (float) potparam[2]; save_info->rflag = (short) rflag; save_info->rseed = (short) rseed; save_info->inside = (short) inside; if (LogFlag <= SHRT_MAX) save_info->logmapold = (short) LogFlag; else save_info->logmapold = (short) SHRT_MAX; save_info->invert[0] = (float) inversion[0]; save_info->invert[1] = (float) inversion[1]; save_info->invert[2] = (float) inversion[2]; save_info->decomp[0] = (short) decomp[0]; save_info->biomorph = (short) usr_biomorph; save_info->symmetry = (short) forcesymmetry; for (i = 0; i < 16; i++) save_info->init3d[i] = (short) init3d[i]; save_info->previewfactor = (short) previewfactor; save_info->xtrans = (short) xtrans; save_info->ytrans = (short) ytrans; save_info->red_crop_left = (short) red_crop_left; save_info->red_crop_right = (short) red_crop_right; save_info->blue_crop_left = (short) blue_crop_left; save_info->blue_crop_right = (short) blue_crop_right; save_info->red_bright = (short) red_bright; save_info->blue_bright = (short) blue_bright; save_info->xadjust = (short) xadjust; save_info->yadjust = (short) yadjust; save_info->eyeseparation = (short) eyeseparation; save_info->glassestype = (short) glassestype; save_info->outside = (short) outside; save_info->x3rd = xx3rd; save_info->y3rd = yy3rd; save_info->calc_status = (short) calc_status; save_info->stdcalcmode = (char) ((three_pass && stdcalcmode == '3') ? 127 : stdcalcmode); if (distest <= 32000) save_info->distestold = (short) distest; else save_info->distestold = 32000; save_info->floatflag = 1; if (bailout >= 4 && bailout <= 32000) save_info->bailoutold = (short) bailout; else save_info->bailoutold = 0; save_info->calctime = calctime; save_info->trigndx[0] = trigndx[0]; save_info->trigndx[1] = trigndx[1]; save_info->trigndx[2] = trigndx[2]; save_info->trigndx[3] = trigndx[3]; save_info->finattract = (short) finattract; save_info->initorbit[0] = initorbit.x; save_info->initorbit[1] = initorbit.y; save_info->useinitorbit = useinitorbit; save_info->periodicity = (short) periodicitycheck; save_info->pot16bit = (short) disk16bit; save_info->faspectratio = finalaspectratio; save_info->system = (short) save_system; if (check_back()) save_info->release = (short) min(save_release, release); else save_info->release = (short) release; save_info->flag3d = (short) display3d; save_info->ambient = (short) Ambient; save_info->randomize = (short) RANDOMIZE; save_info->haze = (short) haze; save_info->transparent[0] = (short) transparent[0]; save_info->transparent[1] = (short) transparent[1]; save_info->rotate_lo = (short) rotate_lo; save_info->rotate_hi = (short) rotate_hi; save_info->distestwidth = (short) distestwidth; save_info->mxmaxfp = mxmaxfp; save_info->mxminfp = mxminfp; save_info->mymaxfp = mymaxfp; save_info->myminfp = myminfp; save_info->zdots = (short) zdots; save_info->originfp = originfp; save_info->depthfp = depthfp; save_info->heightfp = heightfp; save_info->widthfp = widthfp; save_info->distfp = distfp; save_info->eyesfp = eyesfp; save_info->orbittype = (short) neworbittype; save_info->juli3Dmode = (short) juli3Dmode; save_info->maxfn = maxfn; save_info->inversejulia = (short) ((major_method << 8) + minor_method); /* MVS */ save_info->bailout = bailout; save_info->bailoutest = (short) bailoutest; save_info->iterations = maxit; save_info->bflength = (short) bnlength; save_info->bf_math = (short) bf_math; save_info->old_demm_colors = (short) old_demm_colors; save_info->logmap = LogFlag; save_info->distest = distest; save_info->dinvert[0] = inversion[0]; save_info->dinvert[1] = inversion[1]; save_info->dinvert[2] = inversion[2]; save_info->logcalc = (short) Log_Fly_Calc; save_info->stoppass = (short) stoppass; save_info->quick_calc = (short) quick_calc; save_info->closeprox = closeprox; save_info->nobof = (short) nobof; for (i = 0; i < sizeof(save_info->future) / sizeof(short); i++) save_info->future[i] = 0; } /*************************************************************************** * * GIFENCOD.C - GIF Image compression routines * * Lempel-Ziv compression based on 'compress'. GIF modifications by * David Rowley (mgardi@watdcsu.waterloo.edu). * Thoroughly massaged by the Stone Soup team for Fractint's purposes. * ***************************************************************************/ #define BITSF 12 #define HSIZE 5003 /* 80% occupancy */ /* * * GIF Image compression - modified 'compress' * * Based on: compress.c - File compression ala IEEE Computer, June 1984. * * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) * Jim McKie (decvax!mcvax!jim) * Steve Davies (decvax!vax135!petsd!peora!srd) * Ken Turkowski (decvax!decwrl!turtlevax!ken) * James A. Woods (decvax!ihnp4!ames!jaw) * Joe Orost (decvax!vax135!petsd!joe) * */ /* prototypes */ static void _fastcall output(int code); static void _fastcall char_out(int c); static void _fastcall flush_char(void); static void _fastcall cl_block(void); static int n_bits; /* number of bits/code */ static int maxbits = BITSF; /* user settable max # bits/code */ static int maxcode; /* maximum code, given n_bits */ static int maxmaxcode = (int)1 << BITSF; /* should NEVER generate this code */ # define MAXCODE(n_bits) (((int) 1 << (n_bits)) - 1) #ifdef XFRACT unsigned int strlocn[10240]; BYTE block[4096]; #endif static long far *htab; static unsigned short *codetab = (unsigned short *)strlocn; /* * To save much memory, we overlay the table used by compress() with those * used by decompress(). The tab_prefix table is the same size and type * as the codetab. The tab_suffix table needs 2**BITSF characters. We * get this from the beginning of htab. The output stack uses the rest * of htab, and contains characters. There is plenty of room for any * possible stack (stack used to be 8000 characters). */ #define tab_prefixof(i) codetab[i] #define tab_suffixof(i) ((char_type far *)(htab))[i] #define de_stack ((char_type far *)&tab_suffixof((int)1< 2) { outcolor1 = 2; outcolor2 = 3; } if (((++numsaves) & 1) == 0) { /* reverse the colors on alt saves */ i = outcolor1; outcolor1 = outcolor2; outcolor2 = i; } outcolor1s = outcolor1; outcolor2s = outcolor2; /* * Set up the necessary values */ cur_accum = 0; cur_bits = 0; clear_flg = 0; ydot = 0; ent = 0; maxcode = MAXCODE(n_bits = startbits); ClearCode = (1 << (startbits - 1)); EOFCode = ClearCode + 1; free_ent = ClearCode + 2; a_count = 0; hshift = 0; for (fcode = (long) HSIZE; fcode < 65536L; fcode *= 2L) hshift++; hshift = 8 - hshift; /* set hash code range bound */ far_memset(htab,0xff,(unsigned)HSIZE*sizeof(long)); hsize_reg = HSIZE; output((int)ClearCode); for (rownum = 0; rownum < ydots; rownum++) { /* scan through the dots */ for (ydot = rownum; ydot < rowlimit; ydot += ydots) { for (xdot = 0; xdot < xdots; xdot++) { if (save16bit == 0 || ydot < ydots) color = getcolor(xdot, ydot); else color = readdisk(xdot + sxoffs, ydot + syoffs); if(in_count == 0) { in_count = 1; ent = color; continue; } fcode = (long) (((long) color << maxbits) + ent); i = (((int)color << hshift) ^ ent); /* xor hashing */ if (htab[i] == fcode) { ent = codetab[i]; continue; } else if ((long)htab[i] < 0) /* empty slot */ goto nomatch; disp = hsize_reg - i; /* secondary hash (after G. Knott) */ if (i == 0) disp = 1; probe: if ((i -= disp) < 0) i += hsize_reg; if (htab[i] == fcode) { ent = codetab[i]; continue; } if ((long)htab[i] > 0) goto probe; nomatch: output ((int) ent); ent = color; if (free_ent < maxmaxcode) { /* code -> hashtable */ codetab[i] = (unsigned short)free_ent++; htab[i] = fcode; } else cl_block(); } /* end for xdot */ if (dotmode != 11 /* supress this on disk-video */ && active_system == 0 /* and in Windows version */ && ydot == rownum) { if ((ydot & 4) == 0) { if (++outcolor1 >= colors) outcolor1 = 0; if (++outcolor2 >= colors) outcolor2 = 0; } for (i = 0; 250 * i < xdots; i++) { /* display vert status bars */ /* (this is NOT GIF-related) */ putcolor(i, ydot, getcolor(i, ydot) ^ outcolor1); putcolor(xdots - 1 - i, ydot, getcolor(xdots - 1 - i, ydot) ^ outcolor2); } last_colorbar = ydot; } /* end if dotmode != 11 */ tempkey = keypressed(); if (tempkey && (tempkey != (int)'s')) /* keyboard hit - bail out */ { interrupted = 1; rownum = ydots; break; } if (tempkey == (int)'s') getakey(); /* eat the keystroke */ } /* end for ydot */ } /* end for rownum */ /* * Put out the final code. */ output((int)ent); output((int) EOFCode); return (interrupted); } /***************************************************************** * TAG(output) * * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits =< (long)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits long. * Algorithm: * Maintain a BITSF character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static void _fastcall output(int code) { static ULFCODE masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; cur_accum &= masks[ cur_bits ]; if(cur_bits > 0) cur_accum |= ((long)code << cur_bits); else cur_accum = code; cur_bits += n_bits; while(cur_bits >= 8) { char_out((unsigned int)(cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if (free_ent > maxcode || clear_flg) { if(clear_flg) { maxcode = MAXCODE (n_bits = startbits); clear_flg = 0; } else { n_bits++; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } if(code == EOFCode) { /* * At EOF, write the rest of the buffer. */ while(cur_bits > 0) { char_out((unsigned int)(cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } flush_char(); fflush(g_outfile); } } /* * Clear out the hash table */ static void _fastcall cl_block(void) /* table clear for block compress */ { far_memset(htab,0xff,(unsigned)HSIZE*sizeof(long)); free_ent = ClearCode + 2; clear_flg = 1; output((int)ClearCode); } /* * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. */ static void _fastcall char_out(int c) { accum[ a_count++ ] = (char)c; if(a_count >= 254) flush_char(); } /* * Flush the packet to disk, and reset the accumulator */ static void _fastcall flush_char(void) { if(a_count > 0) { fputc(a_count, g_outfile); fwrite(accum, 1, a_count, g_outfile); a_count = 0; } }