123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /* This file is part of the GNU plotutils package. Copyright (C) 1995,
- 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
- The GNU plotutils package is free software. You may 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.
- The GNU plotutils package 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 the GNU plotutils package; see the file COPYING. If not, write to
- the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
- Boston, MA 02110-1301, USA. */
- /* This file (i_rle.c) is a module that does run-length encoding on
- a sequence of integers ("pixel values"), and writes the resulting
- encoded sequence to an output stream. The accompanying header file
- (i_rle.h) defines the external interface. The encoded sequence
- should be GIF-compatible, even though the compression technique
- is not LZW.
- This module encapsulates the miGIF compression routines, originally
- written by der Mouse and ivo. Their copyright notice is reproduced
- below. */
-
- /*-----------------------------------------------------------------------
- *
- * miGIF Compression - mouse and ivo's GIF-compatible compression
- *
- * -run length encoding compression routines-
- *
- * Copyright (C) 1998 Hutchison Avenue Software Corporation
- * http://www.hasc.com
- * info@hasc.com
- *
- * 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." The Hutchison Avenue
- * Software Corporation disclaims all warranties, either express or implied,
- * including but not limited to implied warranties of merchantability and
- * fitness for a particular purpose, with respect to this code and accompanying
- * documentation.
- *
- * The miGIF compression routines do not, strictly speaking, generate files
- * conforming to the GIF spec, since the image data is not LZW-compressed
- * (this is the point: in order to avoid transgression of the Unisys patent
- * on the LZW algorithm.) However, miGIF generates data streams that any
- * reasonably sane LZW decompresser will decompress to what we want.
- *
- * miGIF compression uses run length encoding. It compresses horizontal runs
- * of pixels of the same color. This type of compression gives good results
- * on images with many runs, for example images with lines, text and solid
- * shapes on a solid-colored background. It gives little or no compression
- * on images with few runs, for example digital or scanned photos.
- *
- * der Mouse
- * mouse@rodents.montreal.qc.ca
- * 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
- *
- * ivo@hasc.com
- *
- * The Graphics Interchange Format(c) is the Copyright property of
- * CompuServe Incorporated. GIF(sm) is a Service Mark property of
- * CompuServe Incorporated.
- *
- */
- #include "sys-defines.h" /* libplot-specific */
- #include "extern.h" /* libplot-specific */
- #include "i_rle.h"
- /* forward references */
- static void _block_out (rle_out *rle, unsigned char c);
- static void
- _write_block (rle_out *rle)
- {
- if (rle->ofile)
- {
- fputc (rle->oblen, rle->ofile);
- fwrite ((void *) &(rle->oblock[0]), 1, rle->oblen, rle->ofile);
- }
- #ifdef LIBPLOTTER
- else if (rle->outstream)
- {
- rle->outstream->put ((unsigned char)(rle->oblen));
- rle->outstream->write ((const char *)(&(rle->oblock[0])), rle->oblen);
- }
- #endif
- rle->oblen = 0;
- }
- static void
- _block_out (rle_out *rle, unsigned char c)
- {
- rle->oblock[(rle->oblen)++] = c;
- if (rle->oblen >= 255)
- _write_block (rle);
- }
- static void
- _block_flush (rle_out *rle)
- {
- if (rle->oblen > 0)
- _write_block (rle);
- }
- static void
- _output (rle_out *rle, int val)
- {
- rle->obuf |= val << rle->obits;
- rle->obits += rle->out_bits;
- while (rle->obits >= 8)
- {
- _block_out (rle, (unsigned char)(rle->obuf & 0xff));
- rle->obuf >>= 8;
- rle->obits -= 8;
- }
- }
- static void
- _output_flush (rle_out *rle)
- {
- if (rle->obits > 0)
- _block_out (rle, (unsigned char)(rle->obuf));
- _block_flush (rle);
- }
- static void
- _did_clear (rle_out *rle)
- {
- rle->out_bits = rle->out_bits_init;
- rle->out_bump = rle->out_bump_init;
- rle->out_clear = rle->out_clear_init;
- rle->out_count = 0;
- rle->rl_table_max = 0;
- rle->just_cleared = true;
- }
- static void
- _output_plain (rle_out *rle, int c)
- {
- rle->just_cleared = false;
- _output (rle, c);
- rle->out_count++;
- if (rle->out_count >= rle->out_bump)
- {
- rle->out_bits++;
- rle->out_bump += 1 << (rle->out_bits - 1);
- }
- if (rle->out_count >= rle->out_clear)
- {
- _output (rle, rle->code_clear);
- _did_clear (rle);
- }
- }
- static unsigned int
- _isqrt (unsigned int x)
- {
- unsigned int r;
- unsigned int v;
- if (x < 2)
- return x;
- for (v=x, r=1; v; v>>=2, r<<=1)
- ;
- for ( ; ; )
- {
- v = ((x / r) + r) / 2;
- if ((v == r) || (v == r+1))
- return r;
- r = v;
- }
- }
- static unsigned int
- _compute_triangle_count (unsigned int count, unsigned int nrepcodes)
- {
- unsigned int perrep, cost;
- cost = 0;
- perrep = (nrepcodes * (nrepcodes+1)) / 2;
- while (count >= perrep)
- {
- cost += nrepcodes;
- count -= perrep;
- }
- if (count > 0)
- {
- unsigned int n;
- n = _isqrt (count);
- while ((n*(n+1)) >= 2*count)
- n--;
- while ((n*(n+1)) < 2*count)
- n++;
- cost += n;
- }
- return cost;
- }
- static void
- _max_out_clear (rle_out *rle)
- {
- rle->out_clear = rle->max_ocodes;
- }
- static void
- _reset_out_clear (rle_out *rle)
- {
- rle->out_clear = rle->out_clear_init;
- if (rle->out_count >= rle->out_clear)
- {
- _output (rle, rle->code_clear);
- _did_clear (rle);
- }
- }
- static void
- _rl_flush_fromclear (rle_out *rle, int count)
- {
- int n;
- _max_out_clear (rle);
- rle->rl_table_pixel = rle->rl_pixel;
- n = 1;
- while (count > 0)
- {
- if (n == 1)
- {
- rle->rl_table_max = 1;
- _output_plain (rle, rle->rl_pixel);
- count--;
- }
- else if (count >= n)
- {
- rle->rl_table_max = n;
- _output_plain (rle, rle->rl_basecode + n - 2);
- count -= n;
- }
- else if (count == 1)
- {
- (rle->rl_table_max)++;
- _output_plain (rle, rle->rl_pixel);
- count = 0;
- }
- else
- {
- rle->rl_table_max++;
- _output_plain (rle, rle->rl_basecode+count-2);
- count = 0;
- }
- if (rle->out_count == 0)
- n = 1;
- else
- n++;
- }
- _reset_out_clear (rle);
- }
- static void
- _rl_flush_clearorrep (rle_out *rle, int count)
- {
- int withclr;
- withclr = 1 + _compute_triangle_count ((unsigned int)count,
- (unsigned int)(rle->max_ocodes));
- if (withclr < count)
- {
- _output (rle, rle->code_clear);
- _did_clear (rle);
- _rl_flush_fromclear (rle, count);
- }
- else
- for ( ; count>0; count--)
- _output_plain (rle, rle->rl_pixel);
- }
- static void
- _rl_flush_withtable (rle_out *rle, int count)
- {
- int repmax;
- int repleft;
- int leftover;
- repmax = count / rle->rl_table_max;
- leftover = count % rle->rl_table_max;
- repleft = (leftover ? 1 : 0);
- if (rle->out_count + repmax + repleft > rle->max_ocodes)
- {
- repmax = rle->max_ocodes - rle->out_count;
- leftover = count - (repmax * rle->rl_table_max);
- repleft = 1 + _compute_triangle_count ((unsigned int)leftover,
- (unsigned int)(rle->max_ocodes));
- }
- if (1 + _compute_triangle_count ((unsigned int)count,
- (unsigned int)(rle->max_ocodes))
- < repmax + repleft)
- {
- _output (rle, rle->code_clear);
- _did_clear (rle);
- _rl_flush_fromclear (rle, count);
- return;
- }
- _max_out_clear (rle);
- for ( ; repmax>0; repmax--)
- _output_plain (rle, rle->rl_basecode + rle->rl_table_max - 2);
- if (leftover)
- {
- if (rle->just_cleared)
- _rl_flush_fromclear (rle, leftover);
- else if (leftover == 1)
- _output_plain (rle, rle->rl_pixel);
- else
- _output_plain (rle, rle->rl_basecode + leftover - 2);
- }
- _reset_out_clear (rle);
- }
- /* end a run in progress */
- static void
- _rl_flush (rle_out *rle)
- {
- if (rle->rl_count == 1) /* not a real run, just output pixel */
- _output_plain (rle, rle->rl_pixel);
- else
- {
- if (rle->just_cleared)
- _rl_flush_fromclear (rle, rle->rl_count);
- else if ((rle->rl_table_max < 2)
- || (rle->rl_table_pixel != rle->rl_pixel))
- _rl_flush_clearorrep (rle, rle->rl_count);
- else
- _rl_flush_withtable (rle, rle->rl_count);
- }
- rle->rl_count = 0;
- }
- /***********************************************************************/
- /* EXTERNAL INTERFACE */
- /***********************************************************************/
- /* create new RLE struct, which writes to a specified stream */
- rle_out *
- #ifdef LIBPLOTTER
- _rle_init (FILE *fp, ostream *out, int bit_depth)
- #else
- _rle_init (FILE *fp, int bit_depth)
- #endif
- {
- int init_bits;
- rle_out *rle;
- /* Initial length for compression codes, one bit longer than the minimum
- number of bits needed to represent the set of pixel values. The
- IMAX() and the addition of 1 bit are "because of some algorithmic
- constraints". */
- init_bits = IMAX(bit_depth, 2) + 1;
- rle = (rle_out *)_pl_xmalloc(sizeof(rle_out));
- rle->ofile = fp;
- #ifdef LIBPLOTTER
- rle->outstream = out;
- #endif
- rle->obuf = 0;
- rle->obits = 0;
- rle->oblen = 0;
- rle->code_clear = 1 << (init_bits - 1); /* 100..000 */
- rle->code_eof = rle->code_clear + 1; /* 100..001 */
- rle->rl_basecode = rle->code_eof + 1; /* 100..010 */
- rle->out_bump_init = (1 << (init_bits - 1)) - 1; /* 011..111 */
- /* for images with a lot of runs, making out_clear_init larger will
- give better compression. */
- /* 011..110 */
- rle->out_clear_init = (init_bits <= 3) ? 9 : (rle->out_bump_init - 1);
- rle->out_bits_init = init_bits;
- rle->max_ocodes = (1 << GIFBITS) - ((1 << (rle->out_bits_init - 1)) + 3);
- _did_clear (rle);
- _output (rle, rle->code_clear);
- rle->rl_count = 0;
- return rle;
- }
- /* send one pixel to the RLE */
- void
- _rle_do_pixel (rle_out *rle, int c)
- {
- /* if a run needs to be terminated by being written out, do so */
- if ((rle->rl_count > 0) && (c != rle->rl_pixel))
- _rl_flush (rle);
- /* if current run can be continued, do so (internally) */
- if (rle->rl_pixel == c)
- rle->rl_count++;
- /* otherwise start a new one */
- else
- {
- rle->rl_pixel = c;
- rle->rl_count = 1;
- }
- }
- /* flush out any data remaining in RLE; write EOF and deallocate RLE */
- void
- _rle_terminate (rle_out *rle)
- {
- /* if a run in progress, end it */
- if (rle->rl_count > 0)
- _rl_flush (rle);
- _output (rle, rle->code_eof);
- _output_flush (rle);
- /* deallocate */
- free (rle);
- }
|