Blame src/gd_gif_out.c

Packit ed3af9
/**
Packit ed3af9
 * File: GIF Output
Packit ed3af9
 *
Packit ed3af9
 * Write GIF images.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include <stdio.h>
Packit ed3af9
#include <math.h>
Packit ed3af9
#include <string.h>
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gdhelpers.h"
Packit ed3af9
Packit ed3af9
/* Code drawn from ppmtogif.c, from the pbmplus package
Packit ed3af9
**
Packit ed3af9
** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
Packit ed3af9
** Lempel-Zim compression based on "compress".
Packit ed3af9
**
Packit ed3af9
** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
Packit ed3af9
**
Packit ed3af9
** Copyright (C) 1989 by Jef Poskanzer.
Packit ed3af9
**
Packit ed3af9
** Permission to use, copy, modify, and distribute this software and its
Packit ed3af9
** documentation for any purpose and without fee is hereby granted, provided
Packit ed3af9
** that the above copyright notice appear in all copies and that both that
Packit ed3af9
** copyright notice and this permission notice appear in supporting
Packit ed3af9
** documentation.  This software is provided "as is" without express or
Packit ed3af9
** implied warranty.
Packit ed3af9
**
Packit ed3af9
** The Graphics Interchange Format(c) is the Copyright property of
Packit ed3af9
** CompuServe Incorporated.  GIF(sm) is a Service Mark property of
Packit ed3af9
** CompuServe Incorporated.
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
/* a code_int must be able to hold 2**GIFBITS values of type int, and also -1 */
Packit ed3af9
typedef int code_int;
Packit ed3af9
Packit ed3af9
#ifdef SIGNED_COMPARE_SLOW
Packit ed3af9
typedef unsigned long int count_int;
Packit ed3af9
typedef unsigned short int count_short;
Packit ed3af9
#else /* SIGNED_COMPARE_SLOW */
Packit ed3af9
typedef long int count_int;
Packit ed3af9
#endif /* SIGNED_COMPARE_SLOW */
Packit ed3af9
Packit ed3af9
/* 2.0.28: threadsafe */
Packit ed3af9
Packit ed3af9
#define maxbits GIFBITS
Packit ed3af9
Packit ed3af9
/* should NEVER generate this code */
Packit ed3af9
#define maxmaxcode ((code_int)1 << GIFBITS)
Packit ed3af9
Packit ed3af9
#define HSIZE	5003	/* 80% occupancy */
Packit ed3af9
#define hsize	HSIZE	/* Apparently invariant, left over from compress */
Packit ed3af9
Packit ed3af9
typedef struct {
Packit ed3af9
	int Width, Height;
Packit ed3af9
	int curx, cury;
Packit ed3af9
	long CountDown;
Packit ed3af9
	int Pass;
Packit ed3af9
	int Interlace;
Packit ed3af9
	int n_bits;
Packit ed3af9
	code_int maxcode;
Packit ed3af9
	count_int htab [HSIZE];
Packit ed3af9
	unsigned short codetab [HSIZE];
Packit ed3af9
	/* first unused entry */
Packit ed3af9
	code_int free_ent;
Packit ed3af9
	/* block compression parameters -- after all codes are used up,
Packit ed3af9
	 * and compression rate changes, start over. */
Packit ed3af9
	int clear_flg;
Packit ed3af9
	int offset;
Packit ed3af9
	long int in_count;
Packit ed3af9
	/* # of codes output (for debugging) */
Packit ed3af9
	long int out_count;
Packit ed3af9
	int g_init_bits;
Packit ed3af9
	gdIOCtx * g_outfile;
Packit ed3af9
	int ClearCode;
Packit ed3af9
	int EOFCode;
Packit ed3af9
	unsigned long cur_accum;
Packit ed3af9
	int cur_bits;
Packit ed3af9
	int a_count;
Packit ed3af9
	char accum[ 256 ];
Packit ed3af9
} GifCtx;
Packit ed3af9
Packit ed3af9
static int gifPutWord(int w, gdIOCtx *out);
Packit ed3af9
static int colorstobpp(int colors);
Packit ed3af9
static void BumpPixel(GifCtx *ctx);
Packit ed3af9
static int GIFNextPixel(gdImagePtr im, GifCtx *ctx);
Packit ed3af9
static void GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
Packit ed3af9
static void GIFAnimEncode(gdIOCtxPtr fp, int IWidth, int IHeight, int LeftOfs, int TopOfs, int GInterlace, int Transparent, int Delay, int Disposal, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
Packit ed3af9
static void compress(int init_bits, gdIOCtx *outfile, gdImagePtr im, GifCtx *ctx);
Packit ed3af9
static void output(code_int code, GifCtx *ctx);
Packit ed3af9
static void cl_block(GifCtx *ctx);
Packit ed3af9
static void cl_hash(register count_int chsize, GifCtx *ctx);
Packit ed3af9
static void char_init(GifCtx *ctx);
Packit ed3af9
static void char_out(int c, GifCtx *ctx);
Packit ed3af9
static void flush_char(GifCtx *ctx);
Packit ed3af9
Packit Service 8509e6
static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out);
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifPtr
Packit ed3af9
Packit ed3af9
    Identical to <gdImageGif> except that it returns a pointer to a
Packit ed3af9
    memory area with the GIF data. This memory must be freed by the
Packit ed3af9
    caller when it is no longer needed.
Packit ed3af9
Packit ed3af9
    The caller *must* invoke <gdFree>, not _free()_.  This is because
Packit ed3af9
    it is not guaranteed that libgd will use the same implementation
Packit ed3af9
    of malloc, free, etc. as your proggram.
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to write
Packit ed3af9
    size    - Output: the size of the resulting image.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    A pointer to the GIF data or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImageGifPtr(gdImagePtr im, int *size)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit Service 8509e6
	if (!_gdImageGifCtx(im, out)) {
Packit Service 8509e6
        rv = gdDPExtractData(out, size);
Packit Service 8509e6
    } else {
Packit Service 8509e6
        rv = NULL;
Packit Service 8509e6
    }
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGif
Packit ed3af9
Packit ed3af9
    <gdImageGif> outputs the specified image to the specified file in
Packit ed3af9
    GIF format. The file must be open for binary writing. (Under MSDOS
Packit ed3af9
    and all versions of Windows, it is important to use "wb" as
Packit ed3af9
    opposed to simply "w" as the mode when opening the file; under
Packit ed3af9
    Unix there is no penalty for doing so). <gdImageGif> does not close
Packit ed3af9
    the file; your code must do so.
Packit ed3af9
Packit ed3af9
    GIF does not support true color; GIF images can contain a maximum
Packit ed3af9
    of 256 colors. If the image to be written is a truecolor image,
Packit ed3af9
    such as those created with gdImageCreateTrueColor or loaded from a
Packit ed3af9
    JPEG or a truecolor PNG image file, a palette-based temporary
Packit ed3af9
    image will automatically be created internally using the
Packit ed3af9
    <gdImageCreatePaletteFromTrueColor> function. The original image
Packit ed3af9
    pixels are not modified. This conversion produces high quality
Packit ed3af9
    palettes but does require some CPU time. If you are regularly
Packit ed3af9
    converting truecolor to palette in this way, you should consider
Packit ed3af9
    creating your image as a palette-based image in the first place.
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageGifCtx> outputs the image via a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageGifPtr> stores the image in a large array of bytes.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to write
Packit ed3af9
    outFile - The FILE pointer to write the image to.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
Packit ed3af9
    > gdImagePtr im;
Packit ed3af9
    > int black, white;
Packit ed3af9
    > FILE *out;
Packit ed3af9
    > // Create the image
Packit ed3af9
    > im = gdImageCreate(100, 100);
Packit ed3af9
    > // Allocate background
Packit ed3af9
    > white = gdImageColorAllocate(im, 255, 255, 255);
Packit ed3af9
    > // Allocate drawing color
Packit ed3af9
    > black = gdImageColorAllocate(im, 0, 0, 0);
Packit ed3af9
    > // Draw rectangle
Packit ed3af9
    > gdImageRectangle(im, 0, 0, 99, 99, black);
Packit ed3af9
    > // Open output file in binary mode
Packit ed3af9
    > out = fopen("rect.gif", "wb");
Packit ed3af9
    > // Write GIF
Packit ed3af9
    > gdImageGif(im, out);
Packit ed3af9
    > // Close file
Packit ed3af9
    > fclose(out);
Packit ed3af9
    > // Destroy image
Packit ed3af9
    > gdImageDestroy(im);
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageGif(gdImagePtr im, FILE *outFile)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageGifCtx(im, out);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifCtx
Packit ed3af9
Packit ed3af9
    Writes a GIF image via a <gdIOCtx>.  See <gdImageGif>.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im      - The image to write
Packit ed3af9
    out     - The <gdIOCtx> struct used to do the writing.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out)
Packit ed3af9
{
Packit Service 8509e6
    _gdImageGifCtx(im, out);
Packit Service 8509e6
}
Packit Service 8509e6
Packit Service 8509e6
/* returns 0 on success, 1 on failure */
Packit Service 8509e6
static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out)
Packit Service 8509e6
{
Packit ed3af9
	gdImagePtr pim = 0, tim = im;
Packit ed3af9
	int interlace, BitsPerPixel;
Packit ed3af9
	interlace = im->interlace;
Packit ed3af9
Packit ed3af9
	if(im->trueColor) {
Packit ed3af9
		/* Expensive, but the only way that produces an
Packit ed3af9
		acceptable result: mix down to a palette
Packit ed3af9
		based temporary image. */
Packit ed3af9
		pim = gdImageCreatePaletteFromTrueColor(im, 1, 256);
Packit ed3af9
		if(!pim) {
Packit Service 8509e6
			return 1;
Packit ed3af9
		}
Packit ed3af9
		tim = pim;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	BitsPerPixel = colorstobpp(tim->colorsTotal);
Packit ed3af9
Packit ed3af9
	/* All set, let's do it. */
Packit ed3af9
	GIFEncode(
Packit ed3af9
	    out, tim->sx, tim->sy, interlace, 0, tim->transparent, BitsPerPixel,
Packit ed3af9
	    tim->red, tim->green, tim->blue, tim);
Packit ed3af9
Packit ed3af9
	if(pim) {
Packit ed3af9
		/* Destroy palette based temporary image. */
Packit ed3af9
		gdImageDestroy(	pim);
Packit ed3af9
	}
Packit Service 8509e6
Packit Service 8509e6
    return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimBeginPtr
Packit ed3af9
Packit ed3af9
    Like <gdImageGifAnimBegin> except that it outputs to a memory
Packit ed3af9
    buffer.  See <gdImageGifAnimBegin>.
Packit ed3af9
Packit ed3af9
    The returned memory must be freed by the caller when it is no
Packit ed3af9
    longer needed. **The caller must invoke <gdFree>(), not free()**,
Packit ed3af9
    unless the caller is absolutely certain that the same
Packit ed3af9
    implementations of malloc, free, etc. are used both at library
Packit ed3af9
    build time and at application build time (but don't; it could
Packit ed3af9
    always change).
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The reference image
Packit ed3af9
    size        - Output: the size in bytes of the result.
Packit ed3af9
    GlobalCM    - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default
Packit ed3af9
    Loops       - Loop count; 0 -> infinite, -1 means no loop
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
   A pointer to the resulting data (the contents of the start of the
Packit ed3af9
   GIF) or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void *) gdImageGifAnimBeginPtr(gdImagePtr im, int *size, int GlobalCM, int Loops)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	gdImageGifAnimBeginCtx(im, out, GlobalCM, Loops);
Packit ed3af9
	rv = gdDPExtractData(out, size);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimBegin
Packit ed3af9
Packit ed3af9
    This function must be called as the first function when creating a
Packit ed3af9
    GIF animation. It writes the correct GIF file headers to selected
Packit ed3af9
    file output, and prepares for frames to be added for the
Packit ed3af9
    animation. The image argument is not used to produce an image
Packit ed3af9
    frame to the file, it is only used to establish the GIF animation
Packit ed3af9
    frame size, interlacing options and the color
Packit ed3af9
    palette. <gdImageGifAnimAdd> is used to add the first and
Packit ed3af9
    subsequent frames to the animation, and the animation must be
Packit ed3af9
    terminated by writing a semicolon character (;) to it or by using
Packit ed3af9
    gdImageGifAnimEnd to do that.
Packit ed3af9
Packit ed3af9
    The GlobalCM flag indicates if a global color map (or palette) is
Packit ed3af9
    used in the GIF89A header. A nonzero value specifies that a global
Packit ed3af9
    color map should be used to reduce the size of the animation. Of
Packit ed3af9
    course, if the color maps of individual frames differ greatly, a
Packit ed3af9
    global color map may not be a good idea. GlobalCM=1 means write
Packit ed3af9
    global color map, GlobalCM=0 means do not, and GlobalCM=-1 means
Packit ed3af9
    to do the default, which currently is to use a global color map.
Packit ed3af9
Packit ed3af9
    If Loops is 0 or greater, the Netscape 2.0 extension for animation
Packit ed3af9
    loop count is written. 0 means infinite loop count. -1 means that
Packit ed3af9
    the extension is not added which results in no looping. -1 is the
Packit ed3af9
    default.
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimBeginCtx> outputs the image via a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimBeginPtr> stores the image in a large array of bytes.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The reference image
Packit ed3af9
    outfile     - The output FILE*.
Packit ed3af9
    GlobalCM    - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default
Packit ed3af9
    Loops       - Loop count; 0 -> infinite, -1 means no loop
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
Packit ed3af9
    See <gdImageGifAnimBegin>.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimBegin(gdImagePtr im, FILE *outFile, int GlobalCM, int Loops)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageGifAnimBeginCtx(im, out, GlobalCM, Loops);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimBeginCtx
Packit ed3af9
Packit ed3af9
    Like <gdImageGifAnimBegin> except that it outputs to <gdIOCtx>.
Packit ed3af9
    See <gdImageGifAnimBegin>.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The reference image
Packit ed3af9
    out         - Pointer to the output <gdIOCtx>.
Packit ed3af9
    GlobalCM    - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default
Packit ed3af9
    Loops       - Loop count; 0 -> infinite, -1 means no loop
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimBeginCtx(gdImagePtr im, gdIOCtxPtr out, int GlobalCM, int Loops)
Packit ed3af9
{
Packit ed3af9
	int B;
Packit ed3af9
	int RWidth, RHeight;
Packit ed3af9
	int Resolution;
Packit ed3af9
	int ColorMapSize;
Packit ed3af9
	int BitsPerPixel;
Packit ed3af9
	int Background = 0;
Packit ed3af9
	int i;
Packit ed3af9
Packit ed3af9
	/* Default is to use global color map */
Packit ed3af9
	if (GlobalCM < 0) {
Packit ed3af9
		GlobalCM = 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	BitsPerPixel = colorstobpp(im->colorsTotal);
Packit ed3af9
	ColorMapSize = 1 << BitsPerPixel;
Packit ed3af9
Packit ed3af9
	RWidth = im->sx;
Packit ed3af9
	RHeight = im->sy;
Packit ed3af9
Packit ed3af9
	Resolution = BitsPerPixel;
Packit ed3af9
Packit ed3af9
	/* Write the Magic header */
Packit ed3af9
	gdPutBuf("GIF89a", 6, out);
Packit ed3af9
Packit ed3af9
	/* Write out the screen width and height */
Packit ed3af9
	gifPutWord(RWidth, out);
Packit ed3af9
	gifPutWord(RHeight, out);
Packit ed3af9
Packit ed3af9
	/* Indicate that there is a global colour map */
Packit ed3af9
	B = GlobalCM ? 0x80 : 0;
Packit ed3af9
Packit ed3af9
	/* OR in the resolution */
Packit ed3af9
	B |= (Resolution - 1) << 4;
Packit ed3af9
Packit ed3af9
	/* OR in the Bits per Pixel */
Packit ed3af9
	B |= (BitsPerPixel - 1);
Packit ed3af9
Packit ed3af9
	/* Write it out */
Packit ed3af9
	gdPutC(B, out);
Packit ed3af9
Packit ed3af9
	/* Write out the Background colour */
Packit ed3af9
	gdPutC(Background, out);
Packit ed3af9
Packit ed3af9
	/* Byte of 0's (future expansion) */
Packit ed3af9
	gdPutC(0, out);
Packit ed3af9
Packit ed3af9
	/* Write out the Global Colour Map */
Packit ed3af9
	if(GlobalCM) {
Packit ed3af9
		for(i = 0; i < ColorMapSize; ++i) {
Packit ed3af9
			gdPutC(im->red[i], out);
Packit ed3af9
			gdPutC(im->green[i], out);
Packit ed3af9
			gdPutC(im->blue[i], out);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(Loops >= 0) {
Packit ed3af9
		gdPutBuf("!\377\13NETSCAPE2.0\3\1", 16, out);
Packit ed3af9
		gifPutWord(Loops, out);
Packit ed3af9
		gdPutC(0, out);
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimAddPtr
Packit ed3af9
Packit ed3af9
    Like <gdImageGifAnimAdd> (which contains more information) except
Packit ed3af9
    that it stores the data to write into memory and returns a pointer
Packit ed3af9
    to it.
Packit ed3af9
Packit ed3af9
    This memory must be freed by the caller when it is no longer
Packit ed3af9
    needed. **The caller must invoke <gdFree>(), not free(),** unless
Packit ed3af9
    the caller is absolutely certain that the same implementations of
Packit ed3af9
    malloc, free, etc. are used both at library build time and at
Packit ed3af9
    application build time (but don't; it could always change).
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The image to add.
Packit ed3af9
    size        - Output: the size of the resulting buffer.
Packit ed3af9
    LocalCM     - Flag.  If 1, use a local color map for this frame.
Packit ed3af9
    LeftOfs     - Left offset of image in frame.
Packit ed3af9
    TopOfs      - Top offset of image in frame.
Packit ed3af9
    Delay       - Delay before next frame (in 1/100 seconds)
Packit ed3af9
    Disposal    - MODE: How to treat this frame when the next one loads.
Packit ed3af9
    previm      - NULL or a pointer to the previous image written.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Pointer to the resulting data or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void *) gdImageGifAnimAddPtr(gdImagePtr im, int *size, int LocalCM,
Packit ed3af9
                                         int LeftOfs, int TopOfs, int Delay,
Packit ed3af9
                                         int Disposal, gdImagePtr previm)
Packit ed3af9
{
Packit ed3af9
	void *rv;
Packit ed3af9
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
Packit ed3af9
	if (out == NULL) return NULL;
Packit ed3af9
	gdImageGifAnimAddCtx(im, out, LocalCM, LeftOfs, TopOfs, Delay, Disposal, previm);
Packit ed3af9
	rv = gdDPExtractData(out, size);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
	return rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimAdd
Packit ed3af9
Packit ed3af9
    This function writes GIF animation frames to GIF animation, which
Packit ed3af9
    was initialized with <gdImageGifAnimBegin>. With _LeftOfs_ and
Packit ed3af9
    _TopOfs_ you can place this frame in different offset than (0,0)
Packit ed3af9
    inside the image screen as defined in <gdImageGifAnimBegin>. Delay
Packit ed3af9
    between the previous frame and this frame is in 1/100s
Packit ed3af9
    units. _Disposal_ is usually <gdDisposalNone>, meaning that the
Packit ed3af9
    pixels changed by this frame should remain on the display when the
Packit ed3af9
    next frame begins to render, but can also be <gdDisposalUnknown>
Packit ed3af9
    (not recommended), <gdDisposalRestoreBackground> (restores the
Packit ed3af9
    first allocated color of the global palette), or
Packit ed3af9
    <gdDisposalRestorePrevious> (restores the appearance of the
Packit ed3af9
    affected area before the frame was rendered). Only
Packit ed3af9
    <gdDisposalNone> is a sensible choice for the first frame. If
Packit ed3af9
    _previm_ is passed, the built-in GIF optimizer will always use
Packit ed3af9
    <gdDisposalNone> regardless of the Disposal parameter.
Packit ed3af9
Packit ed3af9
    Setting the _LocalCM_ flag to 1 adds a local palette for this
Packit ed3af9
    image to the animation. Otherwise the global palette is assumed
Packit ed3af9
    and the user must make sure the palettes match. Use
Packit ed3af9
    <gdImagePaletteCopy> to do that.
Packit ed3af9
Packit ed3af9
    Automatic optimization is activated by giving the previous image
Packit ed3af9
    as a parameter. This function then compares the images and only
Packit ed3af9
    writes the changed pixels to the new frame in animation. The
Packit ed3af9
    _Disposal_ parameter for optimized animations must be set to 1,
Packit ed3af9
    also for the first frame. _LeftOfs_ and _TopOfs_ parameters are
Packit ed3af9
    ignored for optimized frames. To achieve good optimization, it is
Packit ed3af9
    usually best to use a single global color map. To allow
Packit ed3af9
    <gdImageGifAnimAdd> to compress unchanged pixels via the use of a
Packit ed3af9
    transparent color, the image must include a transparent color.
Packit ed3af9
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimAddCtx> outputs its data via a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimAddPtr> outputs its data to a memory buffer which
Packit ed3af9
    it returns.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The image to add.
Packit ed3af9
    outfile     - The output FILE* being written.
Packit ed3af9
    LocalCM     - Flag.  If 1, use a local color map for this frame.
Packit ed3af9
    LeftOfs     - Left offset of image in frame.
Packit ed3af9
    TopOfs      - Top offset of image in frame.
Packit ed3af9
    Delay       - Delay before next frame (in 1/100 seconds)
Packit ed3af9
    Disposal    - MODE: How to treat this frame when the next one loads.
Packit ed3af9
    previm      - NULL or a pointer to the previous image written.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
  Example:
Packit ed3af9
    (start code)
Packit ed3af9
Packit ed3af9
    {
Packit ed3af9
    gdImagePtr im, im2, im3;
Packit ed3af9
    int black, white, trans;
Packit ed3af9
    FILE *out;
Packit ed3af9
    
Packit ed3af9
    im = gdImageCreate(100, 100);     // Create the image
Packit ed3af9
    white = gdImageColorAllocate(im, 255, 255, 255); // Allocate background
Packit ed3af9
    black = gdImageColorAllocate(im, 0, 0, 0); // Allocate drawing color
Packit ed3af9
    trans = gdImageColorAllocate(im, 1, 1, 1); // trans clr for compression
Packit ed3af9
    gdImageRectangle(im, 0, 0, 10, 10, black); // Draw rectangle
Packit ed3af9
    
Packit ed3af9
    out = fopen("anim.gif", "wb");// Open output file in binary mode
Packit ed3af9
    gdImageGifAnimBegin(im, out, 1, 3);// Write GIF hdr, global clr map,loops
Packit ed3af9
    // Write the first frame.  No local color map.  Delay = 1s
Packit ed3af9
    gdImageGifAnimAdd(im, out, 0, 0, 0, 100, 1, NULL);
Packit ed3af9
    
Packit ed3af9
    // construct the second frame
Packit ed3af9
    im2 = gdImageCreate(100, 100);
Packit ed3af9
    (void)gdImageColorAllocate(im2, 255, 255, 255); // White background
Packit ed3af9
    gdImagePaletteCopy (im2, im);  // Make sure the palette is identical
Packit ed3af9
    gdImageRectangle(im2, 0, 0, 15, 15, black);    // Draw something
Packit ed3af9
    // Allow animation compression with transparent pixels
Packit ed3af9
    gdImageColorTransparent (im2, trans);
Packit ed3af9
    gdImageGifAnimAdd(im2, out, 0, 0, 0, 100, 1, im);  // Add second frame
Packit ed3af9
    
Packit ed3af9
    // construct the third frame
Packit ed3af9
    im3 = gdImageCreate(100, 100);
Packit ed3af9
    (void)gdImageColorAllocate(im3, 255, 255, 255); // white background
Packit ed3af9
    gdImagePaletteCopy (im3, im); // Make sure the palette is identical
Packit ed3af9
    gdImageRectangle(im3, 0, 0, 15, 20, black); // Draw something
Packit ed3af9
    // Allow animation compression with transparent pixels
Packit ed3af9
    gdImageColorTransparent (im3, trans);
Packit ed3af9
    // Add the third frame, compressing against the second one
Packit ed3af9
    gdImageGifAnimAdd(im3, out, 0, 0, 0, 100, 1, im2);
Packit ed3af9
    gdImageGifAnimEnd(out);  // End marker, same as putc(';', out);
Packit ed3af9
    fclose(out); // Close file
Packit ed3af9
    
Packit ed3af9
    // Destroy images
Packit ed3af9
    gdImageDestroy(im);
Packit ed3af9
    gdImageDestroy(im2);
Packit ed3af9
    gdImageDestroy(im3);
Packit ed3af9
    }
Packit ed3af9
Packit ed3af9
    (end code)
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimAdd(gdImagePtr im, FILE *outFile, int LocalCM,
Packit ed3af9
                                    int LeftOfs, int TopOfs, int Delay,
Packit ed3af9
                                    int Disposal, gdImagePtr previm)
Packit ed3af9
{
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageGifAnimAddCtx(im, out, LocalCM, LeftOfs, TopOfs, Delay, Disposal, previm);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int comparewithmap(gdImagePtr im1, gdImagePtr im2, int c1, int c2, int *colorMap)
Packit ed3af9
{
Packit ed3af9
	if(!colorMap) {
Packit ed3af9
		return c1 == c2;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(-2 != colorMap[c1]) {
Packit ed3af9
		return colorMap[c1] == c2;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return (colorMap[c1] = gdImageColorExactAlpha(im2, im1->red[c1], im1->green[c1], im1->blue[c1], im1->alpha[c1])) == c2;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimAddCtx
Packit ed3af9
Packit ed3af9
    Adds an animation frame via a <gdIOCtxPtr>.  See gdImageGifAnimAdd>.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    im          - The image to add.
Packit ed3af9
    out         - The output <gdIOCtxPtr>.
Packit ed3af9
    LocalCM     - Flag.  If 1, use a local color map for this frame.
Packit ed3af9
    LeftOfs     - Left offset of image in frame.
Packit ed3af9
    TopOfs      - Top offset of image in frame.
Packit ed3af9
    Delay       - Delay before next frame (in 1/100 seconds)
Packit ed3af9
    Disposal    - MODE: How to treat this frame when the next one loads.
Packit ed3af9
    previm      - NULL or a pointer to the previous image written.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimAddCtx(gdImagePtr im, gdIOCtxPtr out,
Packit ed3af9
                                       int LocalCM, int LeftOfs, int TopOfs,
Packit ed3af9
                                       int Delay, int Disposal,
Packit ed3af9
                                       gdImagePtr previm)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr pim = NULL, tim = im;
Packit ed3af9
	int interlace, transparent, BitsPerPixel;
Packit ed3af9
	interlace = im->interlace;
Packit ed3af9
	transparent = im->transparent;
Packit ed3af9
Packit ed3af9
	/* Default is no local color map */
Packit ed3af9
	if(LocalCM < 0) {
Packit ed3af9
		LocalCM = 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(im->trueColor) {
Packit ed3af9
		/* Expensive, but the only way that produces an
Packit ed3af9
			acceptable result: mix down to a palette
Packit ed3af9
			based temporary image. */
Packit ed3af9
		pim = gdImageCreatePaletteFromTrueColor(im, 1, 256);
Packit ed3af9
		if (!pim) {
Packit ed3af9
			return;
Packit ed3af9
		}
Packit ed3af9
		tim = pim;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (previm) {
Packit ed3af9
		/* create optimized animation.  Compare this image to
Packit ed3af9
		   the previous image and crop the temporary copy of
Packit ed3af9
		   current image to include only changed rectangular
Packit ed3af9
		   area.  Also replace unchanged pixels inside this
Packit ed3af9
		   area with transparent color.  Transparent color
Packit ed3af9
		   needs to be already allocated!
Packit ed3af9
		   Preconditions:
Packit ed3af9
		   TopOfs, LeftOfs are assumed 0
Packit ed3af9
Packit ed3af9
		   Images should be of same size.  If not, a temporary
Packit ed3af9
		   copy is made with the same size as previous image.
Packit ed3af9
Packit ed3af9
		*/
Packit ed3af9
		gdImagePtr prev_pim = 0, prev_tim = previm;
Packit ed3af9
		int x, y;
Packit ed3af9
		int min_x = 0;
Packit ed3af9
		int min_y = tim->sy;
Packit ed3af9
		int max_x = 0;
Packit ed3af9
		int max_y = 0;
Packit ed3af9
		int colorMap[256];
Packit ed3af9
Packit ed3af9
		if (previm->trueColor) {
Packit ed3af9
			prev_pim = gdImageCreatePaletteFromTrueColor(previm, 1, 256);
Packit ed3af9
			if (!prev_pim) {
Packit ed3af9
				goto fail_end;
Packit ed3af9
			}
Packit ed3af9
			prev_tim = prev_pim;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		for (x = 0; x < 256; ++x) {
Packit ed3af9
			colorMap[x] = -2;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		/* First find bounding box of changed areas. */
Packit ed3af9
		/* first find the top changed row */
Packit ed3af9
		for (y = 0; y < tim->sy; ++y) {
Packit ed3af9
			for (x = 0; x < tim->sx; ++x) {
Packit ed3af9
				if (!comparewithmap(prev_tim, tim,
Packit ed3af9
				                    prev_tim->pixels[y][x],
Packit ed3af9
				                    tim->pixels[y][x],
Packit ed3af9
				                    colorMap)) {
Packit ed3af9
					min_y = max_y = y;
Packit ed3af9
					min_x = max_x = x;
Packit ed3af9
					goto break_top;
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
break_top:
Packit ed3af9
		if (tim->sy == min_y) {
Packit ed3af9
			/* No changes in this frame!! Encode empty image. */
Packit ed3af9
			transparent = 0;
Packit ed3af9
			min_x = min_y = 1;
Packit ed3af9
			max_x = max_y = 0;
Packit ed3af9
		} else {
Packit ed3af9
			/* Then the bottom row */
Packit ed3af9
			for (y = tim->sy - 1; y > min_y; --y) {
Packit ed3af9
				for (x = 0; x < tim->sx; ++x) {
Packit ed3af9
					if (!comparewithmap
Packit ed3af9
					        (prev_tim, tim,
Packit ed3af9
					         prev_tim->pixels[y][x],
Packit ed3af9
					         tim->pixels[y][x],
Packit ed3af9
					         colorMap)) {
Packit ed3af9
						max_y = y;
Packit ed3af9
						if(x < min_x) {
Packit ed3af9
							min_x = x;
Packit ed3af9
						}
Packit ed3af9
						if(x > max_x) {
Packit ed3af9
							max_x = x;
Packit ed3af9
						}
Packit ed3af9
						goto break_bot;
Packit ed3af9
					}
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
break_bot:
Packit ed3af9
			/* left side */
Packit ed3af9
			for (x = 0; x < min_x; ++x) {
Packit ed3af9
				for (y = min_y; y <= max_y; ++y) {
Packit ed3af9
					if (!comparewithmap
Packit ed3af9
					        (prev_tim, tim,
Packit ed3af9
					         prev_tim->pixels[y][x],
Packit ed3af9
					         tim->pixels[y][x],
Packit ed3af9
					         colorMap)) {
Packit ed3af9
						min_x = x;
Packit ed3af9
						goto break_left;
Packit ed3af9
					}
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
break_left:
Packit ed3af9
			/* right side */
Packit ed3af9
			for (x = tim->sx - 1; x > max_x; --x) {
Packit ed3af9
				for (y = min_y; y <= max_y; ++y) {
Packit ed3af9
					if (!comparewithmap
Packit ed3af9
					        (prev_tim, tim,
Packit ed3af9
					         prev_tim->pixels[y][x],
Packit ed3af9
					         tim->pixels[y][x],
Packit ed3af9
					         colorMap)) {
Packit ed3af9
						max_x = x;
Packit ed3af9
						goto break_right;
Packit ed3af9
					}
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
break_right:
Packit ed3af9
			;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		LeftOfs = min_x;
Packit ed3af9
		TopOfs = min_y;
Packit ed3af9
		Disposal = 1;
Packit ed3af9
Packit ed3af9
		/* Make a copy of the image with the new offsets.
Packit ed3af9
		   But only if necessary. */
Packit ed3af9
		if (min_x != 0 || max_x != tim->sx - 1
Packit ed3af9
		        || min_y != 0 || max_y != tim->sy - 1
Packit ed3af9
		        || transparent >= 0) {
Packit ed3af9
Packit ed3af9
			gdImagePtr pim2 = gdImageCreate(max_x-min_x + 1, max_y-min_y + 1);
Packit ed3af9
Packit ed3af9
			if (!pim2) {
Packit ed3af9
				if (prev_pim) {
Packit ed3af9
					gdImageDestroy(prev_pim);
Packit ed3af9
				}
Packit ed3af9
				goto fail_end;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			gdImagePaletteCopy(pim2, LocalCM ? tim : prev_tim);
Packit ed3af9
			gdImageCopy(pim2, tim, 0, 0, min_x, min_y,
Packit ed3af9
			            max_x - min_x + 1, max_y - min_y + 1);
Packit ed3af9
Packit ed3af9
			if (pim) {
Packit ed3af9
				gdImageDestroy(pim);
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			tim = pim = pim2;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		/* now let's compare pixels for transparent
Packit ed3af9
		   optimization.  But only if transparent is set. */
Packit ed3af9
		if (transparent >= 0) {
Packit ed3af9
			for(y = 0; y < tim->sy; ++y) {
Packit ed3af9
				for (x = 0; x < tim->sx; ++x) {
Packit ed3af9
					if(comparewithmap
Packit ed3af9
					        (prev_tim, tim,
Packit ed3af9
					         prev_tim->pixels[min_y + y][min_x + x],
Packit ed3af9
					         tim->pixels[y][x], 0)) {
Packit ed3af9
						gdImageSetPixel(tim, x, y, transparent);
Packit ed3af9
						break;
Packit ed3af9
					}
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if(prev_pim) {
Packit ed3af9
			gdImageDestroy(prev_pim);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	BitsPerPixel = colorstobpp(tim->colorsTotal);
Packit ed3af9
Packit ed3af9
	/* All set, let's do it. */
Packit ed3af9
	GIFAnimEncode(
Packit ed3af9
	    out, tim->sx, tim->sy, LeftOfs, TopOfs, interlace, transparent,
Packit ed3af9
	    Delay, Disposal, BitsPerPixel,
Packit ed3af9
	    LocalCM ? tim->red : 0, tim->green, tim->blue, tim);
Packit ed3af9
Packit ed3af9
fail_end:
Packit ed3af9
	if(pim) {
Packit ed3af9
		/* Destroy palette based temporary image. */
Packit ed3af9
		gdImageDestroy(pim);
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimEnd
Packit ed3af9
Packit ed3af9
    Terminates the GIF file properly.
Packit ed3af9
Packit ed3af9
    (Previous versions of this function's documentation suggested just
Packit ed3af9
    manually writing a semicolon (';') instead since that is all this
Packit ed3af9
    function does.  While that has no longer changed, we now suggest
Packit ed3af9
    that you do not do this and instead always call
Packit ed3af9
    <gdImageGifAnimEnd> (or equivalent) since later versions could
Packit ed3af9
    possibly do more or different things.)
Packit ed3af9
Packit ed3af9
  Variants:
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimEndCtx> outputs its data via a <gdIOCtx> struct.
Packit ed3af9
Packit ed3af9
    <gdImageGifAnimEndPtr> outputs its data to a memory buffer which
Packit ed3af9
    it returns.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    outfile     - the destination FILE*.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimEnd(FILE *outFile)
Packit ed3af9
{
Packit ed3af9
#if 1
Packit ed3af9
	putc(';', outFile);
Packit ed3af9
#else
Packit ed3af9
	gdIOCtx *out = gdNewFileCtx(outFile);
Packit ed3af9
	if (out == NULL) return;
Packit ed3af9
	gdImageGifAnimEndCtx(out);
Packit ed3af9
	out->gd_free(out);
Packit ed3af9
#endif
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimEndPtr
Packit ed3af9
Packit ed3af9
    Like <gdImageGifAnimEnd> (which contains more information) except
Packit ed3af9
    that it stores the data to write into memory and returns a pointer
Packit ed3af9
    to it.
Packit ed3af9
Packit ed3af9
    This memory must be freed by the caller when it is no longer
Packit ed3af9
    needed. **The caller must invoke <gdFree>(), not free(),** unless
Packit ed3af9
    the caller is absolutely certain that the same implementations of
Packit ed3af9
    malloc, free, etc. are used both at library build time and at
Packit ed3af9
    application build time (but don't; it could always change).
Packit ed3af9
Packit ed3af9
    The 'size' parameter receives the total size of the block of
Packit ed3af9
    memory.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    size        - Output: the size of the resulting buffer.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Pointer to the resulting data or NULL if an error occurred.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void *) gdImageGifAnimEndPtr(int *size)
Packit ed3af9
{
Packit ed3af9
	char *rv = (char *) gdMalloc(1);
Packit ed3af9
	if(!rv) {
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
	*rv = ';';
Packit ed3af9
	*size = 1;
Packit ed3af9
	return (void *)rv;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
  Function: gdImageGifAnimEndCtx
Packit ed3af9
Packit ed3af9
    Like <gdImageGifAnimEnd>, but writes its data via a <gdIOCtx>.
Packit ed3af9
Packit ed3af9
  Parameters:
Packit ed3af9
Packit ed3af9
    out         - the destination <gdIOCtx>.
Packit ed3af9
Packit ed3af9
  Returns:
Packit ed3af9
Packit ed3af9
    Nothing.
Packit ed3af9
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
BGD_DECLARE(void) gdImageGifAnimEndCtx(gdIOCtx *out)
Packit ed3af9
{
Packit ed3af9
	/*
Packit ed3af9
	 * Write the GIF file terminator
Packit ed3af9
	 */
Packit ed3af9
	gdPutC(';', out);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int colorstobpp(int colors)
Packit ed3af9
{
Packit ed3af9
	int bpp = 0;
Packit ed3af9
Packit ed3af9
	if(colors <= 2)
Packit ed3af9
		bpp = 1;
Packit ed3af9
	else if(colors <= 4)
Packit ed3af9
		bpp = 2;
Packit ed3af9
	else if(colors <= 8)
Packit ed3af9
		bpp = 3;
Packit ed3af9
	else if(colors <= 16)
Packit ed3af9
		bpp = 4;
Packit ed3af9
	else if(colors <= 32)
Packit ed3af9
		bpp = 5;
Packit ed3af9
	else if(colors <= 64)
Packit ed3af9
		bpp = 6;
Packit ed3af9
	else if(colors <= 128)
Packit ed3af9
		bpp = 7;
Packit ed3af9
	else if(colors <= 256)
Packit ed3af9
		bpp = 8;
Packit ed3af9
Packit ed3af9
	return bpp;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*****************************************************************************
Packit ed3af9
 *
Packit ed3af9
 * GIFENCODE.C    - GIF Image compression interface
Packit ed3af9
 *
Packit ed3af9
 * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
Packit ed3af9
 *            BitsPerPixel, Red, Green, Blue, gdImagePtr )
Packit ed3af9
 *
Packit ed3af9
 *****************************************************************************/
Packit ed3af9
Packit ed3af9
#define TRUE 1
Packit ed3af9
#define FALSE 0
Packit ed3af9
Packit ed3af9
/* Bump the 'curx' and 'cury' to point to the next pixel */
Packit ed3af9
static void BumpPixel(GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	/* Bump the current X position */
Packit ed3af9
	++(ctx->curx);
Packit ed3af9
Packit ed3af9
	/* If we are at the end of a scan line, set curx back to the beginning
Packit ed3af9
	 * If we are interlaced, bump the cury to the appropriate spot,
Packit ed3af9
	 * otherwise, just increment it. */
Packit ed3af9
	if(ctx->curx == ctx->Width) {
Packit ed3af9
		ctx->curx = 0;
Packit ed3af9
Packit ed3af9
		if(!ctx->Interlace) {
Packit ed3af9
			++(ctx->cury);
Packit ed3af9
		} else {
Packit ed3af9
			switch(ctx->Pass) {
Packit ed3af9
Packit ed3af9
			case 0:
Packit ed3af9
				ctx->cury += 8;
Packit ed3af9
				if(ctx->cury >= ctx->Height) {
Packit ed3af9
					++(ctx->Pass);
Packit ed3af9
					ctx->cury = 4;
Packit ed3af9
				}
Packit ed3af9
				break;
Packit ed3af9
Packit ed3af9
			case 1:
Packit ed3af9
				ctx->cury += 8;
Packit ed3af9
				if(ctx->cury >= ctx->Height) {
Packit ed3af9
					++(ctx->Pass);
Packit ed3af9
					ctx->cury = 2;
Packit ed3af9
				}
Packit ed3af9
				break;
Packit ed3af9
Packit ed3af9
			case 2:
Packit ed3af9
				ctx->cury += 4;
Packit ed3af9
				if(ctx->cury >= ctx->Height) {
Packit ed3af9
					++(ctx->Pass);
Packit ed3af9
					ctx->cury = 1;
Packit ed3af9
				}
Packit ed3af9
				break;
Packit ed3af9
Packit ed3af9
			case 3:
Packit ed3af9
				ctx->cury += 2;
Packit ed3af9
				break;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* Return the next pixel from the image */
Packit ed3af9
static int GIFNextPixel(gdImagePtr im, GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	int r;
Packit ed3af9
Packit ed3af9
	if(ctx->CountDown == 0) {
Packit ed3af9
		return EOF;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	--(ctx->CountDown);
Packit ed3af9
Packit ed3af9
	r = gdImageGetPixel(im, ctx->curx, ctx->cury);
Packit ed3af9
Packit ed3af9
	BumpPixel(ctx);
Packit ed3af9
Packit ed3af9
	return r;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* public */
Packit ed3af9
Packit ed3af9
static void GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
Packit ed3af9
{
Packit ed3af9
	int B;
Packit ed3af9
	int RWidth, RHeight;
Packit ed3af9
	int LeftOfs, TopOfs;
Packit ed3af9
	int Resolution;
Packit ed3af9
	int ColorMapSize;
Packit ed3af9
	int InitCodeSize;
Packit ed3af9
	int i;
Packit ed3af9
	GifCtx ctx;
Packit ed3af9
Packit ed3af9
	memset(&ctx, 0, sizeof(ctx));
Packit ed3af9
Packit ed3af9
	ctx.Interlace = GInterlace;
Packit ed3af9
	ctx.in_count = 1;
Packit ed3af9
Packit ed3af9
	ColorMapSize = 1 << BitsPerPixel;
Packit ed3af9
Packit ed3af9
	RWidth = ctx.Width = GWidth;
Packit ed3af9
	RHeight = ctx.Height = GHeight;
Packit ed3af9
	LeftOfs = TopOfs = 0;
Packit ed3af9
Packit ed3af9
	Resolution = BitsPerPixel;
Packit ed3af9
Packit ed3af9
	/* Calculate number of bits we are expecting */
Packit ed3af9
	ctx.CountDown = (long)ctx.Width * (long)ctx.Height;
Packit ed3af9
Packit ed3af9
	/* Indicate which pass we are on (if interlace) */
Packit ed3af9
	ctx.Pass = 0;
Packit ed3af9
Packit ed3af9
	/* The initial code size */
Packit ed3af9
	if(BitsPerPixel <= 1) {
Packit ed3af9
		InitCodeSize = 2;
Packit ed3af9
	} else {
Packit ed3af9
		InitCodeSize = BitsPerPixel;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Set up the current x and y position */
Packit ed3af9
	ctx.curx = ctx.cury = 0;
Packit ed3af9
Packit ed3af9
	/* Write the Magic header */
Packit ed3af9
	gdPutBuf(Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp);
Packit ed3af9
Packit ed3af9
	/* Write out the screen width and height */
Packit ed3af9
	gifPutWord(RWidth, fp);
Packit ed3af9
	gifPutWord(RHeight, fp);
Packit ed3af9
Packit ed3af9
	/* Indicate that there is a global colour map */
Packit ed3af9
	/* Yes, there is a color map */
Packit ed3af9
	B = 0x80;
Packit ed3af9
Packit ed3af9
	/* OR in the resolution */
Packit ed3af9
	B |= (Resolution - 1) << 4;
Packit ed3af9
Packit ed3af9
	/* OR in the Bits per Pixel */
Packit ed3af9
	B |= (BitsPerPixel - 1);
Packit ed3af9
Packit ed3af9
	/* Write it out */
Packit ed3af9
	gdPutC(B, fp);
Packit ed3af9
Packit ed3af9
	/* Write out the Background colour */
Packit ed3af9
	gdPutC(Background, fp);
Packit ed3af9
Packit ed3af9
	/* Byte of 0's (future expansion) */
Packit ed3af9
	gdPutC(0, fp);
Packit ed3af9
Packit ed3af9
	/* Write out the Global Colour Map */
Packit ed3af9
	for(i = 0; i < ColorMapSize; ++i) {
Packit ed3af9
		gdPutC(Red[i], fp);
Packit ed3af9
		gdPutC(Green[i], fp);
Packit ed3af9
		gdPutC(Blue[i], fp);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Write out extension for transparent colour index, if necessary. */
Packit ed3af9
	if(Transparent >= 0) {
Packit ed3af9
		gdPutC('!', fp);
Packit ed3af9
		gdPutC(0xf9, fp);
Packit ed3af9
		gdPutC(4, fp);
Packit ed3af9
		gdPutC(1, fp);
Packit ed3af9
		gdPutC(0, fp);
Packit ed3af9
		gdPutC(0, fp);
Packit ed3af9
		gdPutC((unsigned char) Transparent, fp);
Packit ed3af9
		gdPutC(0, fp);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Write an Image separator */
Packit ed3af9
	gdPutC(',', fp);
Packit ed3af9
Packit ed3af9
	/* Write the Image header */
Packit ed3af9
	gifPutWord(LeftOfs, fp);
Packit ed3af9
	gifPutWord(TopOfs, fp);
Packit ed3af9
	gifPutWord(ctx.Width, fp);
Packit ed3af9
	gifPutWord(ctx.Height, fp);
Packit ed3af9
Packit ed3af9
	/* Write out whether or not the image is interlaced */
Packit ed3af9
	if(ctx.Interlace) {
Packit ed3af9
		gdPutC(0x40, fp);
Packit ed3af9
	} else {
Packit ed3af9
		gdPutC(0x00, fp);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Write out the initial code size */
Packit ed3af9
	gdPutC(InitCodeSize, fp);
Packit ed3af9
Packit ed3af9
	/* Go and actually compress the data */
Packit ed3af9
	compress(InitCodeSize + 1, fp, im, &ctx;;
Packit ed3af9
Packit ed3af9
	/* Write out a Zero-length packet (to end the series) */
Packit ed3af9
	gdPutC(0, fp);
Packit ed3af9
Packit ed3af9
	/* Write the GIF file terminator */
Packit ed3af9
	gdPutC(';', fp);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void GIFAnimEncode(gdIOCtxPtr fp, int IWidth, int IHeight, int LeftOfs, int TopOfs, int GInterlace, int Transparent, int Delay, int Disposal, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
Packit ed3af9
{
Packit ed3af9
	int B;
Packit ed3af9
	int ColorMapSize;
Packit ed3af9
	int InitCodeSize;
Packit ed3af9
	int i;
Packit ed3af9
	GifCtx ctx;
Packit ed3af9
Packit ed3af9
	memset(&ctx, 0, sizeof(ctx));
Packit ed3af9
Packit ed3af9
	ctx.Interlace = GInterlace;
Packit ed3af9
	ctx.in_count = 1;
Packit ed3af9
Packit ed3af9
	ColorMapSize = 1 << BitsPerPixel;
Packit ed3af9
Packit ed3af9
	if(LeftOfs < 0) {
Packit ed3af9
		LeftOfs = 0;
Packit ed3af9
	}
Packit ed3af9
	if(TopOfs < 0) {
Packit ed3af9
		TopOfs = 0;
Packit ed3af9
	}
Packit ed3af9
	if(Delay < 0) {
Packit ed3af9
		Delay = 100;
Packit ed3af9
	}
Packit ed3af9
	if(Disposal < 0) {
Packit ed3af9
		Disposal = 1;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	ctx.Width = IWidth;
Packit ed3af9
	ctx.Height = IHeight;
Packit ed3af9
Packit ed3af9
	/* Calculate number of bits we are expecting */
Packit ed3af9
	ctx.CountDown = (long)ctx.Width * (long)ctx.Height;
Packit ed3af9
Packit ed3af9
	/* Indicate which pass we are on (if interlace) */
Packit ed3af9
	ctx.Pass = 0;
Packit ed3af9
Packit ed3af9
	/* The initial code size */
Packit ed3af9
	if(BitsPerPixel <= 1) {
Packit ed3af9
		InitCodeSize = 2;
Packit ed3af9
	} else {
Packit ed3af9
		InitCodeSize = BitsPerPixel;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Set up the current x and y position */
Packit ed3af9
	ctx.curx = ctx.cury = 0;
Packit ed3af9
Packit ed3af9
	/* Write out extension for image animation and looping */
Packit ed3af9
	gdPutC('!', fp);
Packit ed3af9
	gdPutC(0xf9, fp);
Packit ed3af9
	gdPutC(4, fp);
Packit ed3af9
	gdPutC((Transparent >= 0 ? 1 : 0) | (Disposal << 2), fp);
Packit ed3af9
	gdPutC((unsigned char)(Delay & 255), fp);
Packit ed3af9
	gdPutC((unsigned char)((Delay >> 8) & 255), fp);
Packit ed3af9
	gdPutC((unsigned char) Transparent, fp);
Packit ed3af9
	gdPutC(0, fp);
Packit ed3af9
Packit ed3af9
	/* Write an Image separator */
Packit ed3af9
	gdPutC(',', fp);
Packit ed3af9
Packit ed3af9
	/* Write out the Image header */
Packit ed3af9
	gifPutWord(LeftOfs, fp);
Packit ed3af9
	gifPutWord(TopOfs, fp);
Packit ed3af9
	gifPutWord(ctx.Width, fp);
Packit ed3af9
	gifPutWord(ctx.Height, fp);
Packit ed3af9
Packit ed3af9
	/* Indicate that there is a local colour map */
Packit ed3af9
	B = (Red && Green && Blue) ? 0x80 : 0;
Packit ed3af9
Packit ed3af9
	/* OR in the interlacing */
Packit ed3af9
	B |= ctx.Interlace ? 0x40 : 0;
Packit ed3af9
Packit ed3af9
	/* OR in the Bits per Pixel */
Packit ed3af9
	B |= (Red && Green && Blue) ? (BitsPerPixel - 1) : 0;
Packit ed3af9
Packit ed3af9
	/* Write it out */
Packit ed3af9
	gdPutC(B, fp);
Packit ed3af9
Packit ed3af9
	/* Write out the Local Colour Map */
Packit ed3af9
	if(Red && Green && Blue) {
Packit ed3af9
		for(i = 0; i < ColorMapSize; ++i) {
Packit ed3af9
			gdPutC(Red[i], fp);
Packit ed3af9
			gdPutC(Green[i], fp);
Packit ed3af9
			gdPutC(Blue[i], fp);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Write out the initial code size */
Packit ed3af9
	gdPutC(InitCodeSize, fp);
Packit ed3af9
Packit ed3af9
	/* Go and actually compress the data */
Packit ed3af9
	compress(InitCodeSize + 1, fp, im, &ctx;;
Packit ed3af9
Packit ed3af9
	/* Write out a Zero-length packet (to end the series) */
Packit ed3af9
	gdPutC(0, fp);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/***************************************************************************
Packit ed3af9
 *
Packit ed3af9
 *  GIFCOMPR.C       - GIF Image compression routines
Packit ed3af9
 *
Packit ed3af9
 *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
Packit ed3af9
 *  David Rowley (mgardi@watdcsu.waterloo.edu)
Packit ed3af9
 *
Packit ed3af9
 ***************************************************************************/
Packit ed3af9
Packit ed3af9
/* General DEFINEs */
Packit ed3af9
Packit ed3af9
#define GIFBITS	12
Packit ed3af9
Packit ed3af9
#ifdef NO_UCHAR
Packit ed3af9
typedef char char_type;
Packit ed3af9
#else /* NO_UCHAR */
Packit ed3af9
typedef unsigned char char_type;
Packit ed3af9
#endif /* NO_UCHAR */
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 *
Packit ed3af9
 * GIF Image compression - modified 'compress'
Packit ed3af9
 *
Packit ed3af9
 * Based on: compress.c - File compression ala IEEE Computer, June 1984.
Packit ed3af9
 *
Packit ed3af9
 * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
Packit ed3af9
 *              Jim McKie               (decvax!mcvax!jim)
Packit ed3af9
 *              Steve Davies            (decvax!vax135!petsd!peora!srd)
Packit ed3af9
 *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
Packit ed3af9
 *              James A. Woods          (decvax!ihnp4!ames!jaw)
Packit ed3af9
 *              Joe Orost               (decvax!vax135!petsd!joe)
Packit ed3af9
 *
Packit ed3af9
 */
Packit ed3af9
#include <ctype.h>
Packit ed3af9
Packit ed3af9
#define ARGVAL() (*++(*argv) || (--argc && *++argv))
Packit ed3af9
Packit ed3af9
#ifdef COMPATIBLE /* But wrong! */
Packit ed3af9
#	define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1)
Packit ed3af9
#else /* COMPATIBLE */
Packit ed3af9
#	define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
Packit ed3af9
#endif /* COMPATIBLE */
Packit ed3af9
Packit ed3af9
#define HashTabOf(i) ctx->htab[i]
Packit ed3af9
#define CodeTabOf(i) ctx->codetab[i]
Packit ed3af9
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * To save much memory, we overlay the table used by compress() with those
Packit ed3af9
 * used by decompress().  The tab_prefix table is the same size and type
Packit ed3af9
 * as the codetab.  The tab_suffix table needs 2**GIFBITS characters.  We
Packit ed3af9
 * get this from the beginning of htab.  The output stack uses the rest
Packit ed3af9
 * of htab, and contains characters.  There is plenty of room for any
Packit ed3af9
 * possible stack (stack used to be 8000 characters).
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#define tab_prefixof(i) CodeTabOf(i)
Packit ed3af9
#define tab_suffixof(i) ((char_type*)(htab))[i]
Packit ed3af9
#define de_stack ((char_type*)&tab_suffixof((code_int)1 << GIFBITS))
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * compress stdin to stdout
Packit ed3af9
 *
Packit ed3af9
 * Algorithm:  use open addressing double hashing (no chaining) on the
Packit ed3af9
 * prefix code / next character combination.  We do a variant of Knuth's
Packit ed3af9
 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
Packit ed3af9
 * secondary probe.  Here, the modular division first probe is gives way
Packit ed3af9
 * to a faster exclusive-or manipulation.  Also do block compression with
Packit ed3af9
 * an adaptive reset, whereby the code table is cleared when the compression
Packit ed3af9
 * ratio decreases, but after the table fills.  The variable-length output
Packit ed3af9
 * codes are re-sized at this point, and a special CLEAR code is generated
Packit ed3af9
 * for the decompressor.  Late addition:  construct the table according to
Packit ed3af9
 * file size for noticeable speed improvement on small files.  Please direct
Packit ed3af9
 * questions about this implementation to ames!jaw.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
static void output(code_int code, GifCtx *ctx);
Packit ed3af9
Packit ed3af9
static void compress(int init_bits, gdIOCtxPtr outfile, gdImagePtr im, GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	register long fcode;
Packit ed3af9
	register code_int i;
Packit ed3af9
	register int c;
Packit ed3af9
	register code_int ent;
Packit ed3af9
	register code_int disp;
Packit ed3af9
	register code_int hsize_reg;
Packit ed3af9
	register int hshift;
Packit ed3af9
Packit ed3af9
	/* Set up the globals:
Packit ed3af9
	 *	g_init_bits - initial number of bits
Packit ed3af9
	 *	g_outfile   - pointer to output file */
Packit ed3af9
	ctx->g_init_bits = init_bits;
Packit ed3af9
	ctx->g_outfile = outfile;
Packit ed3af9
Packit ed3af9
	/* Set up the necessary values */
Packit ed3af9
	ctx->offset = 0;
Packit ed3af9
	ctx->out_count = 0;
Packit ed3af9
	ctx->clear_flg = 0;
Packit ed3af9
	ctx->in_count = 1;
Packit ed3af9
	ctx->maxcode = MAXCODE(ctx->n_bits = ctx->g_init_bits);
Packit ed3af9
Packit ed3af9
	ctx->ClearCode = (1 << (init_bits - 1));
Packit ed3af9
	ctx->EOFCode = ctx->ClearCode + 1;
Packit ed3af9
	ctx->free_ent = ctx->ClearCode + 2;
Packit ed3af9
Packit ed3af9
	char_init(ctx);
Packit ed3af9
Packit ed3af9
	ent = GIFNextPixel(im, ctx);
Packit ed3af9
Packit ed3af9
	hshift = 0;
Packit ed3af9
	for(fcode = (long)hsize; fcode < 65536L; fcode *= 2L) {
Packit ed3af9
		++hshift;
Packit ed3af9
	}
Packit ed3af9
	hshift = 8 - hshift; /* set hash code range bound */
Packit ed3af9
Packit ed3af9
	hsize_reg = hsize;
Packit ed3af9
	cl_hash((count_int) hsize_reg, ctx); /* clear hash table */
Packit ed3af9
Packit ed3af9
	output((code_int)ctx->ClearCode, ctx);
Packit ed3af9
Packit ed3af9
#ifdef SIGNED_COMPARE_SLOW
Packit ed3af9
	while((c = GIFNextPixel(im)) != (unsigned) EOF) {
Packit ed3af9
#else /* SIGNED_COMPARE_SLOW */
Packit ed3af9
	while((c = GIFNextPixel(im, ctx)) != EOF) {
Packit ed3af9
#endif /* SIGNED_COMPARE_SLOW */
Packit ed3af9
Packit ed3af9
		++(ctx->in_count);
Packit ed3af9
Packit ed3af9
		fcode = (long) (((long) c << maxbits) + ent);
Packit ed3af9
		i = (((code_int)c << hshift) ^ ent); /* xor hashing */
Packit ed3af9
Packit ed3af9
		if(HashTabOf(i) == fcode) {
Packit ed3af9
			ent = CodeTabOf (i);
Packit ed3af9
			continue;
Packit ed3af9
		} else if ((long)HashTabOf (i) < 0) {/* empty slot */
Packit ed3af9
			goto nomatch;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		disp = hsize_reg - i; /* secondary hash (after G. Knott) */
Packit ed3af9
Packit ed3af9
		if(i == 0) {
Packit ed3af9
			disp = 1;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
probe:
Packit ed3af9
		if((i -= disp) < 0) {
Packit ed3af9
			i += hsize_reg;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if(HashTabOf(i) == fcode) {
Packit ed3af9
			ent = CodeTabOf (i);
Packit ed3af9
			continue;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		if((long)HashTabOf(i) > 0) {
Packit ed3af9
			goto probe;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
nomatch:
Packit ed3af9
		output((code_int) ent, ctx);
Packit ed3af9
		++(ctx->out_count);
Packit ed3af9
		ent = c;
Packit ed3af9
#ifdef SIGNED_COMPARE_SLOW
Packit ed3af9
		if((unsigned) ctx->free_ent < (unsigned) maxmaxcode) {
Packit ed3af9
#else /*SIGNED_COMPARE_SLOW*/
Packit ed3af9
		if (ctx->free_ent < maxmaxcode) {  /* } */
Packit ed3af9
#endif /*SIGNED_COMPARE_SLOW*/
Packit ed3af9
			CodeTabOf(i) = ctx->free_ent++; /* code -> hashtable */
Packit ed3af9
			HashTabOf(i) = fcode;
Packit ed3af9
		} else {
Packit ed3af9
			cl_block(ctx);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Put out the final code. */
Packit ed3af9
	output((code_int)ent, ctx);
Packit ed3af9
	++(ctx->out_count);
Packit ed3af9
	output((code_int) ctx->EOFCode, ctx);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*****************************************************************
Packit ed3af9
 * TAG( output )
Packit ed3af9
 *
Packit ed3af9
 * Output the given code.
Packit ed3af9
 * Inputs:
Packit ed3af9
 *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
Packit ed3af9
 *              that n_bits =< (long)wordsize - 1.
Packit ed3af9
 * Outputs:
Packit ed3af9
 *      Outputs code to the file.
Packit ed3af9
 * Assumptions:
Packit ed3af9
 *      Chars are 8 bits long.
Packit ed3af9
 * Algorithm:
Packit ed3af9
 *      Maintain a GIFBITS character long buffer (so that 8 codes will
Packit ed3af9
 * fit in it exactly).  Use the VAX insv instruction to insert each
Packit ed3af9
 * code in turn.  When the buffer fills up empty it and start over.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
static const unsigned long masks[] = {
Packit ed3af9
	0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
Packit ed3af9
	0x001F, 0x003F, 0x007F, 0x00FF,
Packit ed3af9
	0x01FF, 0x03FF, 0x07FF, 0x0FFF,
Packit ed3af9
	0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
Packit ed3af9
};
Packit ed3af9
Packit ed3af9
/* Arbitrary value to mark output is done.  When we see EOFCode, then we don't
Packit ed3af9
 * expect to see any more data.  If we do (e.g. corrupt image inputs), cur_bits
Packit ed3af9
 * might be negative, so flag it to return early.
Packit ed3af9
 */
Packit ed3af9
#define CUR_BITS_FINISHED -1000
Packit ed3af9
Packit ed3af9
static void output(code_int code, GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	if (ctx->cur_bits == CUR_BITS_FINISHED)
Packit ed3af9
		return;
Packit ed3af9
	ctx->cur_accum &= masks[ctx->cur_bits];
Packit ed3af9
Packit ed3af9
	if(ctx->cur_bits > 0) {
Packit ed3af9
		ctx->cur_accum |= ((long)code << ctx->cur_bits);
Packit ed3af9
	} else {
Packit ed3af9
		ctx->cur_accum = code;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	ctx->cur_bits += ctx->n_bits;
Packit ed3af9
Packit ed3af9
	while(ctx->cur_bits >= 8) {
Packit ed3af9
		char_out((unsigned int)(ctx->cur_accum & 0xff), ctx);
Packit ed3af9
		ctx->cur_accum >>= 8;
Packit ed3af9
		ctx->cur_bits -= 8;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/*
Packit ed3af9
	 * If the next entry is going to be too big for the code size,
Packit ed3af9
	 * then increase it, if possible.
Packit ed3af9
	 */
Packit ed3af9
	if(ctx->free_ent > ctx->maxcode || ctx->clear_flg) {
Packit ed3af9
		if(ctx->clear_flg) {
Packit ed3af9
			ctx->maxcode = MAXCODE (ctx->n_bits = ctx->g_init_bits);
Packit ed3af9
			ctx->clear_flg = 0;
Packit ed3af9
		} else {
Packit ed3af9
			++(ctx->n_bits);
Packit ed3af9
			if(ctx->n_bits == maxbits) {
Packit ed3af9
				ctx->maxcode = maxmaxcode;
Packit ed3af9
			} else {
Packit ed3af9
				ctx->maxcode = MAXCODE(ctx->n_bits);
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(code == ctx->EOFCode) {
Packit ed3af9
		/* At EOF, write the rest of the buffer. */
Packit ed3af9
		while(ctx->cur_bits > 0) {
Packit ed3af9
			char_out((unsigned int)(ctx->cur_accum & 0xff), ctx);
Packit ed3af9
			ctx->cur_accum >>= 8;
Packit ed3af9
			ctx->cur_bits -= 8;
Packit ed3af9
		}
Packit ed3af9
		/* Flag that it's done to prevent re-entry. */
Packit ed3af9
		ctx->cur_bits = CUR_BITS_FINISHED;
Packit ed3af9
Packit ed3af9
		flush_char(ctx);
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Clear out the hash table
Packit ed3af9
 */
Packit ed3af9
static void cl_block (GifCtx *ctx) /* table clear for block compress */
Packit ed3af9
{
Packit ed3af9
	cl_hash((count_int) hsize, ctx);
Packit ed3af9
	ctx->free_ent = ctx->ClearCode + 2;
Packit ed3af9
	ctx->clear_flg = 1;
Packit ed3af9
Packit ed3af9
	output((code_int)ctx->ClearCode, ctx);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void cl_hash(register count_int chsize, GifCtx *ctx) /* reset code table */
Packit ed3af9
{
Packit ed3af9
	register count_int *htab_p = ctx->htab+chsize;
Packit ed3af9
	register long i;
Packit ed3af9
	register long m1 = -1;
Packit ed3af9
Packit ed3af9
	i = chsize - 16;
Packit ed3af9
	do { /* might use Sys V memset(3) here */
Packit ed3af9
		*(htab_p - 16) = m1;
Packit ed3af9
		*(htab_p - 15) = m1;
Packit ed3af9
		*(htab_p - 14) = m1;
Packit ed3af9
		*(htab_p - 13) = m1;
Packit ed3af9
		*(htab_p - 12) = m1;
Packit ed3af9
		*(htab_p - 11) = m1;
Packit ed3af9
		*(htab_p - 10) = m1;
Packit ed3af9
		*(htab_p - 9) = m1;
Packit ed3af9
		*(htab_p - 8) = m1;
Packit ed3af9
		*(htab_p - 7) = m1;
Packit ed3af9
		*(htab_p - 6) = m1;
Packit ed3af9
		*(htab_p - 5) = m1;
Packit ed3af9
		*(htab_p - 4) = m1;
Packit ed3af9
		*(htab_p - 3) = m1;
Packit ed3af9
		*(htab_p - 2) = m1;
Packit ed3af9
		*(htab_p - 1) = m1;
Packit ed3af9
		htab_p -= 16;
Packit ed3af9
	} while((i -= 16) >= 0);
Packit ed3af9
Packit ed3af9
	for(i += 16; i > 0; --i) {
Packit ed3af9
		*--htab_p = m1;
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/******************************************************************************
Packit ed3af9
 *
Packit ed3af9
 * GIF Specific routines
Packit ed3af9
 *
Packit ed3af9
 ******************************************************************************/
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Set up the 'byte output' routine
Packit ed3af9
 */
Packit ed3af9
static void char_init(GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	ctx->a_count = 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Add a character to the end of the current packet, and if it is 254
Packit ed3af9
 * characters, flush the packet to disk.
Packit ed3af9
 */
Packit ed3af9
static void char_out(int c, GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	ctx->accum[ctx->a_count++] = c;
Packit ed3af9
	if(ctx->a_count >= 254) {
Packit ed3af9
		flush_char(ctx);
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * Flush the packet to disk, and reset the accumulator
Packit ed3af9
 */
Packit ed3af9
static void flush_char(GifCtx *ctx)
Packit ed3af9
{
Packit ed3af9
	if(ctx->a_count > 0) {
Packit ed3af9
		gdPutC(ctx->a_count, ctx->g_outfile);
Packit ed3af9
		gdPutBuf(ctx->accum, ctx->a_count, ctx->g_outfile);
Packit ed3af9
		ctx->a_count = 0;
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static int gifPutWord(int w, gdIOCtx *out)
Packit ed3af9
{
Packit ed3af9
	/* Byte order is little-endian */
Packit ed3af9
	gdPutC(w & 0xFF, out);
Packit ed3af9
	gdPutC((w >> 8) & 0xFF, out);
Packit ed3af9
	return 0;
Packit ed3af9
}