diff options
Diffstat (limited to 'dviware/dvi2bitmap/GIFBitmap.cc')
-rw-r--r-- | dviware/dvi2bitmap/GIFBitmap.cc | 807 |
1 files changed, 807 insertions, 0 deletions
diff --git a/dviware/dvi2bitmap/GIFBitmap.cc b/dviware/dvi2bitmap/GIFBitmap.cc new file mode 100644 index 0000000000..d7d2ddf83e --- /dev/null +++ b/dviware/dvi2bitmap/GIFBitmap.cc @@ -0,0 +1,807 @@ +/* This file is part of dvi2bitmap; see README for copyrights and licence */ +/* + * GIFEncode.cc - GIF image compression interface. + * + * This file is based on ppmtogif.c program which is part of the + * pbmplus distribution. + * + * The header at the top of ppmtogif.c is reproduced below. + */ + +/* ppmtogif.c - read a portable pixmap and produce a GIF file +** +** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A +** Lempel-Zim compression based on "compress". +** +** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl> +** +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** The Graphics Interchange Format(c) is the Copyright property of +** CompuServe Incorporated. GIF(sm) is a Service Mark property of +** CompuServe Incorporated. +*/ + +#include <config.h> + +// Minimally converted to a C++ class +#include "Bitmap.h" // for BitmapError exception class +#include "GIFBitmap.h" + +#ifdef HAVE_CSTD_INCLUDE +#include <cmath> +#else +#include <math.h> // for floor() and ceil() in iround +#endif + +using STD::fopen; +using STD::fwrite; +using STD::fputc; +using STD::fclose; +using STD::fflush; +using STD::fprintf; +using STD::floor; +using STD::ceil; +using STD::sqrt; + +const unsigned long GIFBitmap::masks[] + = { 0x0000, + 0x0001, 0x0003, 0x0007, 0x000F, + 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + + +GIFBitmap::GIFBitmap (const int w, const int h, const int bpp) + : BitmapImage (w, h, bpp), + Pass(0), maxbits(BITS), hsize(HSIZE), free_ent(0), clear_flg(0), + in_count(1), out_count(0), cur_accum(0), cur_bits(0) +{ + maxmaxcode = (code_int)1 << BITS; + if (verbosity_ > normal) + fprintf (stderr, "GIFBitmap: w=%d h=%d\n", w, h); +} + +GIFBitmap::~GIFBitmap() +{ +} + +inline int iround (float f) +{ + return static_cast<int>(f>0 ? floor(f+0.5) : ceil(f-0.5)); +} + +void GIFBitmap::write (const string filename) +{ + if (bitmapRows_ != h_) + throw DviBug ("attempt to GIFBitmap::write with incomplete bitmap"); + + // make a colour table, appropriate for bpp_ bits-per-pixel + if (bpp_ > 8) + throw DviBug ("max of 8 bits-per-pixel"); + const int ncolours = 1<<bpp_; + + int *RedCT = new int[ncolours]; + int *GreenCT = new int[ncolours]; + int *BlueCT = new int[ncolours]; + int diffred = fg_.red -bg_.red; + int diffgreen = fg_.green-bg_.green; + int diffblue = fg_.blue -bg_.blue; + for (int i=0; i<ncolours; i++) + { + // Following is a rather `steeper' colour table than the + // obvious linear one, which should plausibly therefore be + // less prone to the `halo' you can get when transparent + // images are viewed against a substantially different + // background from the one they were intended for. Having + // said that, however, I can't see much difference when I + // examine them. +#if 1 + double rat = sqrt(static_cast<double>(i)/(ncolours-1)); +#else + float rat = static_cast<float>(i)/(ncolours-1); +#endif + RedCT[i] = bg_.red + iround(rat*diffred); + GreenCT[i] = bg_.green + iround(rat*diffgreen); + BlueCT[i] = bg_.blue + iround(rat*diffblue); + } + + if (verbosity_ > normal) + { + fprintf (stderr, "GIFBitmap colour tables:\n"); + for (int i=0; i<ncolours; i++) + fprintf (stderr, "%d\t%d\t%d\t%d\n", + i, RedCT[i], GreenCT[i], BlueCT[i]); + } + + //const int step = 256 >> bpp_; + //int *CT = new int[ncolours]; + //for (int i=0, val=255; i<ncolours-1; i++, val-=step) + //CT[i] = val; + //CT[ncolours-1] = 0; + + FILE *F = fopen (filename.c_str(), "wb"); + if (F == 0) + throw BitmapError ("can't open GIF file "+filename+" to write"); + GIFEncode (F, // open file + w_, h_, // width and height of bitmap + 1, // interlace? + 0, // which CT entry is the background? + (isTransparent_ ? 0 : -1), // make entry 0 transparent, if req'd + bpp_, // bits-per-pixel + //CT, CT, CT // colour tables + RedCT, GreenCT, BlueCT + ); + // GIFEncode closes the stream when it's finished + delete[] RedCT; + delete[] GreenCT; + delete[] BlueCT; +} + + + + + +/***************************************************************************** + * + * GIFENCODE.C - GIF Image compression interface + * + * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent, + * BitsPerPixel, Red, Green, Blue, GetPixel ) + * + *****************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +//static int Width, Height; +//static int curx, cury; +//static long CountDown; +//static int Pass = 0; +//static int Interlace; + +/* + * Bump the 'curx' and 'cury' to point to the next pixel + */ +void GIFBitmap::BumpPixel(void) +{ + /* + * Bump the current X position + */ + ++curx; + + /* + * If we are at the end of a scan line, set curx back to the beginning + * If we are interlaced, bump the cury to the appropriate spot, + * otherwise, just increment it. + */ + if( curx == Width ) { + curx = 0; + + if( !Interlace ) + ++cury; + else { + switch( Pass ) { + + case 0: + cury += 8; + if( cury >= Height ) { + ++Pass; + cury = 4; + } + break; + + case 1: + cury += 8; + if( cury >= Height ) { + ++Pass; + cury = 2; + } + break; + + case 2: + cury += 4; + if( cury >= Height ) { + ++Pass; + cury = 1; + } + break; + + case 3: + cury += 2; + break; + } + } + } +} + +/* + * Return the next pixel from the image + */ +int GIFBitmap::GIFNextPixel(void) +{ + int r; + + if( CountDown == 0 ) + return EOF; + + --CountDown; + + //r = ( * getpixel )( curx, cury ); + r = static_cast<int>(bitmap_[cury*w_+curx]); + + //fputc ((bitmap_[cury*w_+curx] ? '*' : '.'), stderr); + //if (curx == w_-1) + // fputc ('\n', stderr); + + BumpPixel(); + + return r; +} + +/* public */ + +void GIFBitmap::GIFEncode(FILE* fp, + int GWidth, int GHeight, + int GInterlace, + int Background, + int Transparent, + int BitsPerPixel, + int *Red, int *Green, int *Blue) +{ + int B; + int RWidth, RHeight; + int LeftOfs, TopOfs; + int Resolution; + int ColorMapSize; + int InitCodeSize; + int i; + + Interlace = GInterlace; + + ColorMapSize = 1 << BitsPerPixel; + + RWidth = Width = GWidth; + RHeight = Height = GHeight; + LeftOfs = TopOfs = 0; + + Resolution = BitsPerPixel; + + /* + * Calculate number of bits we are expecting + */ + CountDown = (long)Width * (long)Height; + + /* + * Indicate which pass we are on (if interlace) + */ + Pass = 0; + + /* + * The initial code size + */ + if( BitsPerPixel <= 1 ) + InitCodeSize = 2; + else + InitCodeSize = BitsPerPixel; + + /* + * Set up the current x and y position + */ + curx = cury = 0; + + /* + * Write the Magic header + */ + fwrite( Transparent < 0 ? "GIF87a" : "GIF89a", 1, 6, fp ); + + /* + * Write out the screen width and height + */ + Putword( RWidth, fp ); + Putword( RHeight, fp ); + + /* + * Indicate that there is a global colour map + */ + B = 0x80; /* Yes, there is a color map */ + + /* + * OR in the resolution + */ + B |= (Resolution - 1) << 5; + + /* + * OR in the Bits per Pixel + */ + B |= (BitsPerPixel - 1); + + /* + * Write it out + */ + fputc( B, fp ); + + /* + * Write out the Background colour + */ + fputc( Background, fp ); + + /* + * Byte of 0's (future expansion) + */ + fputc( 0, fp ); + + /* + * Write out the Global Colour Map + */ + for( i=0; i<ColorMapSize; ++i ) { + fputc( Red[i], fp ); + fputc( Green[i], fp ); + fputc( Blue[i], fp ); + } + + /* + * Write out extension for transparent colour index, if necessary. + */ + if ( Transparent >= 0 ) { + fputc( '!', fp ); + fputc( 0xf9, fp ); + fputc( 4, fp ); + fputc( 1, fp ); + fputc( 0, fp ); + fputc( 0, fp ); + fputc( Transparent, fp ); + fputc( 0, fp ); + } + + /* + * Write an Image separator + */ + fputc( ',', fp ); + + /* + * Write the Image header + */ + + Putword( LeftOfs, fp ); + Putword( TopOfs, fp ); + Putword( Width, fp ); + Putword( Height, fp ); + + /* + * Write out whether or not the image is interlaced + */ + if( Interlace ) + fputc( 0x40, fp ); + else + fputc( 0x00, fp ); + + /* + * Write out the initial code size + */ + fputc( InitCodeSize, fp ); + + /* + * Go and actually compress the data + */ + compress( InitCodeSize+1, fp); + + /* + * Write out a Zero-length packet (to end the series) + */ + fputc( 0, fp ); + + /* + * Write the GIF file terminator + */ + fputc( ';', fp ); + + /* + * And close the file + */ + fclose( fp ); +} + +/* + * Write out a word to the GIF file + */ +void GIFBitmap::Putword(int w, FILE* fp) +{ + fputc( w & 0xff, fp ); + fputc( (w / 256) & 0xff, fp ); +} + + +/*************************************************************************** + * + * GIFCOMPR.C - GIF Image compression routines + * + * Lempel-Ziv compression based on 'compress'. GIF modifications by + * David Rowley (mgardi@watdcsu.waterloo.edu) + * + ***************************************************************************/ + +/* + * General DEFINEs + */ + +// These #defines moved to class constants +//#define BITS 12 +//#define HSIZE 5003 /* 80% occupancy */ + +#ifdef NO_UCHAR + typedef char char_type; +#else /*NO_UCHAR*/ + typedef unsigned char char_type; +#endif /*NO_UCHAR*/ + +/* + * + * 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) + * + */ +#include <ctype.h> + +#define ARGVAL() (*++(*argv) || (--argc && *++argv)) + +//static int n_bits; /* number of bits/code */ +//static int maxbits = BITS; /* user settable max # bits/code */ +//static code_int maxcode; /* maximum code, given n_bits */ +//static code_int maxmaxcode = (code_int)1 << BITS; /* should NEVER generate this code */ +#ifdef COMPATIBLE /* But wrong! */ +# define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1) +#else /*COMPATIBLE*/ +# define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) +#endif /*COMPATIBLE*/ + +//static count_int htab [HSIZE]; +//static unsigned short codetab [HSIZE]; +#define HashTabOf(i) htab[i] +#define CodeTabOf(i) codetab[i] + +//static code_int hsize = HSIZE; /* for dynamic table sizing */ + +/* + * 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**BITS 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) CodeTabOf(i) +#define tab_suffixof(i) ((char_type*)(htab))[i] +#define de_stack ((char_type*)&tab_suffixof((code_int)1<<BITS)) + +//static code_int free_ent = 0; /* first unused entry */ + +/* + * block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ +//static int clear_flg = 0; + +//static int offset; +//static long int in_count = 1; /* length of input */ +//static long int out_count = 0; /* # of codes output (for debugging) */ + +/* + * compress stdin to stdout + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ + +//static int g_init_bits; +//static FILE* g_outfile; + +//static int ClearCode; +//static int EOFCode; + +void GIFBitmap::compress(int init_bits, FILE* outfile) +{ + register long fcode; + register code_int i /* = 0 */; + register int c; + register code_int ent; + register code_int disp; + register code_int hsize_reg; + register int hshift; + + /* + * Set up the globals: g_init_bits - initial number of bits + * g_outfile - pointer to output file + */ + g_init_bits = init_bits; + g_outfile = outfile; + + /* + * Set up the necessary values + */ + offset = 0; + out_count = 0; + clear_flg = 0; + in_count = 1; + maxcode = MAXCODE(n_bits = g_init_bits); + + ClearCode = (1 << (init_bits - 1)); + EOFCode = ClearCode + 1; + free_ent = ClearCode + 2; + + char_init(); + + ent = GIFNextPixel(); + + hshift = 0; + for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L ) + ++hshift; + hshift = 8 - hshift; /* set hash code range bound */ + + hsize_reg = hsize; + cl_hash( (count_int) hsize_reg); /* clear hash table */ + + output( (code_int)ClearCode ); + +#ifdef SIGNED_COMPARE_SLOW + while ( (c = GIFNextPixel()) != (unsigned) EOF ) { +#else /*SIGNED_COMPARE_SLOW*/ + while ( (c = GIFNextPixel()) != EOF ) { /* } */ +#endif /*SIGNED_COMPARE_SLOW*/ + + ++in_count; + + fcode = (long) (((long) c << maxbits) + ent); + i = (((code_int)c << hshift) ^ ent); /* xor hashing */ + + if ( HashTabOf (i) == fcode ) { + ent = CodeTabOf (i); + continue; + } else if ( (long)HashTabOf (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 ( HashTabOf (i) == fcode ) { + ent = CodeTabOf (i); + continue; + } + if ( (long)HashTabOf (i) > 0 ) + goto probe; +nomatch: + output ( (code_int) ent ); + ++out_count; + ent = c; +#ifdef SIGNED_COMPARE_SLOW + if ( (unsigned) free_ent < (unsigned) maxmaxcode) { +#else /*SIGNED_COMPARE_SLOW*/ + if ( free_ent < maxmaxcode ) { /* } */ +#endif /*SIGNED_COMPARE_SLOW*/ + CodeTabOf (i) = (short)free_ent++; /* code -> hashtable */ + HashTabOf (i) = fcode; + } else + cl_block(); + } + /* + * Put out the final code. + */ + output( (code_int)ent ); + ++out_count; + output( (code_int) EOFCode ); +} + +/***************************************************************** + * 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 BITS 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 unsigned long cur_accum = 0; + //static int cur_bits = 0; + + //static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, + // 0x001F, 0x003F, 0x007F, 0x00FF, + // 0x01FF, 0x03FF, 0x07FF, 0x0FFF, + // 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +void GIFBitmap::output(code_int code) +{ + 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 = g_init_bits); + 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 ); + + if( ferror( g_outfile ) ) + throw BitmapError ("error in GIFBitmap"); + } +} + +/* + * Clear out the hash table + */ +void GIFBitmap::cl_block () /* table clear for block compress */ +{ + + cl_hash ( (count_int) hsize ); + free_ent = ClearCode + 2; + clear_flg = 1; + + output( (code_int)ClearCode ); +} + +void GIFBitmap::cl_hash(count_int p_hsize) /* reset code table */ + /*register count_int hsize;*/ +{ + + register count_int *htab_p = htab+p_hsize; + + register long i; + register long m1 = -1; + + i = p_hsize - 16; + do { /* might use Sys V memset(3) here */ + *(htab_p-16) = m1; + *(htab_p-15) = m1; + *(htab_p-14) = m1; + *(htab_p-13) = m1; + *(htab_p-12) = m1; + *(htab_p-11) = m1; + *(htab_p-10) = m1; + *(htab_p-9) = m1; + *(htab_p-8) = m1; + *(htab_p-7) = m1; + *(htab_p-6) = m1; + *(htab_p-5) = m1; + *(htab_p-4) = m1; + *(htab_p-3) = m1; + *(htab_p-2) = m1; + *(htab_p-1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + + for ( i += 16; i > 0; --i ) + *--htab_p = m1; +} + +/* +static void +writeerr() +{ + pm_error( "error writing output file" ); +} +*/ + +/****************************************************************************** + * + * GIF Specific routines + * + ******************************************************************************/ + +/* + * Number of characters so far in this 'packet' + */ +//static int a_count; + +/* + * Set up the 'byte output' routine + */ +void GIFBitmap::char_init() +{ + a_count = 0; +} + +/* + * Define the storage for the packet accumulator + */ +//static char accum[ 256 ]; + +/* + * Add a character to the end of the current packet, and if it is 254 + * characters, flush the packet to disk. + */ +void GIFBitmap::char_out( int c ) +{ + accum[ a_count++ ] = (char)c; + if( a_count >= 254 ) + flush_char(); +} + +/* + * Flush the packet to disk, and reset the accumulator + */ +void GIFBitmap::flush_char() +{ + if( a_count > 0 ) { + fputc( a_count, g_outfile ); + fwrite( accum, 1, a_count, g_outfile ); + a_count = 0; + } +} + +/* The End */ |