mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3262 lines
95 KiB
3262 lines
95 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
Jim Seidman [email protected]
|
|
Scott Piette [email protected]
|
|
Paul Rohr [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
|
|
/* Stream Object
|
|
** ------------
|
|
*/
|
|
|
|
/* ****************************************************************** */
|
|
/* External Prototypes */
|
|
|
|
int HT_CreateDeviceImageMap(struct Mwin *tw, struct ImageInfo *pImg);
|
|
BOOL W3Doc_CheckForImageLoadElement (struct _www *w3doc, int element);
|
|
void GTR_DrawProgessiveImage (struct Mwin* tw, int ndx);
|
|
/* ****************************************************************** */
|
|
|
|
#define BLOCK_SIZE 32768
|
|
|
|
#ifdef UNIX
|
|
#include "gui/x_dither.h"
|
|
#define NEW_DITHERER
|
|
#define EXTRAFUDGE 128
|
|
#endif /* UNIX */
|
|
|
|
struct _HTStream
|
|
{
|
|
CONST HTStreamClass *isa;
|
|
HTRequest *request;
|
|
struct Mwin *tw;
|
|
|
|
/* state info for "push"-model decompressor */
|
|
struct buf_in *src;
|
|
struct _GIFinfo *gif;
|
|
int state;
|
|
int first_time;
|
|
|
|
/* platform-specific stuff */
|
|
#ifdef WIN32
|
|
RGBQUAD colors[256];
|
|
#endif /* WIN32 */
|
|
#ifdef MAC
|
|
GWorldPtr gw;
|
|
BitMap *mask;
|
|
CTabHandle colors;
|
|
#endif
|
|
#ifdef UNIX
|
|
XColor *colors;
|
|
#endif
|
|
};
|
|
|
|
/* Image streams
|
|
*/
|
|
static void HTGIF_free(HTStream * me);
|
|
static void HTGIF_abort(HTStream * me, HTError e);
|
|
static BOOL HTGIF_put_character(HTStream * me, char c);
|
|
static BOOL HTGIF_put_string(HTStream * me, CONST char *s);
|
|
static BOOL HTGIF_write(HTStream * me, CONST char *s, int l);
|
|
|
|
#ifdef MAC
|
|
static CONST HTStreamClass HTGIFClass =
|
|
{
|
|
"GIF",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
HTGIF_free,
|
|
HTGIF_abort,
|
|
HTGIF_put_character, HTGIF_put_string,
|
|
HTGIF_write
|
|
};
|
|
void HTGIF_InitStaticStrings(void);
|
|
void HTGIF_InitStaticStrings(void)
|
|
{
|
|
HTGIFClass.szStatusNoLength = GTR_GetString(HTGIF_RECEIVING_INLINE_S);
|
|
HTGIFClass.szStatusWithLength = GTR_GetString(HTGIF_RECEIVING_INLINE_S_S);
|
|
}
|
|
#else
|
|
static CONST HTStreamClass HTGIFClass =
|
|
{
|
|
"GIF",
|
|
SID_HTGIF_RECEIVING_INLINE_S,
|
|
SID_HTGIF_RECEIVING_INLINE_S_S,
|
|
NULL,
|
|
HTGIF_free,
|
|
HTGIF_abort,
|
|
HTGIF_put_character, HTGIF_put_string,
|
|
HTGIF_write
|
|
};
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
#define HARDWARE_BIT_ORDER MSBFirst
|
|
#define HARDWARE_BYTE_ORDER MSBFirst
|
|
#else
|
|
#define HARDWARE_BIT_ORDER LSBFirst
|
|
#define HARDWARE_BYTE_ORDER LSBFirst
|
|
#endif
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
"push"-model GIF decoder (used to be GIF.C)
|
|
|
|
***************************************************************************/
|
|
|
|
/* +-------------------------------------------------------------------+ */
|
|
/* | Copyright 1990, David Koblas. | */
|
|
/* | 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. | */
|
|
/* +-------------------------------------------------------------------+ */
|
|
|
|
|
|
#define MAXCOLORMAPSIZE 256
|
|
|
|
#ifndef MAC
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif /* !MAC */
|
|
|
|
#define CM_RED 0
|
|
#define CM_GREEN 1
|
|
#define CM_BLUE 2
|
|
|
|
#define MAX_LWZ_BITS 12
|
|
|
|
#define INTERLACE 0x40
|
|
#define LOCALCOLORMAP 0x80
|
|
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
|
|
|
|
#define LM_to_uint(a,b) ((((unsigned int) b)<<8)|((unsigned int)a))
|
|
|
|
|
|
/* input buffer: to be completely consumed */
|
|
struct buf_in
|
|
{
|
|
unsigned char *pData;
|
|
long len; /* total length */
|
|
long offset; /* first unconsumed byte */
|
|
BOOL bForceFlush; /* forces **output** buffer to be flushed */
|
|
};
|
|
|
|
|
|
/* output buffer: asynchronously filled */
|
|
struct buf_out
|
|
{
|
|
unsigned char *pData;
|
|
long offset; /* amount received so far */
|
|
|
|
/* the following fields are only used by FillBlock */
|
|
long blockSize;
|
|
int iBlockState; /* see below */
|
|
};
|
|
|
|
#define BLOCK_INIT 0
|
|
#define BLOCK_READ 1
|
|
|
|
|
|
/* state info for push-model LWZ decompressor */
|
|
struct _LWZinfo
|
|
{
|
|
/*
|
|
** Pulled out of nextCode
|
|
*/
|
|
unsigned char buf[280];
|
|
struct buf_out async_buf; /* points to buf */
|
|
|
|
long curbit, lastbit, get_done;
|
|
long last_byte;
|
|
long return_clear;
|
|
|
|
/*
|
|
** Out of nextLWZ
|
|
*/
|
|
long *stack;
|
|
long stacksize;
|
|
long *sp;
|
|
long code_size, set_code_size;
|
|
long max_code, max_code_size;
|
|
long clear_code, end_code;
|
|
|
|
long *table[2];
|
|
long firstcode, oldcode;
|
|
BOOL suspended;
|
|
};
|
|
|
|
|
|
struct _rgb
|
|
{
|
|
unsigned char r,g,b;
|
|
};
|
|
|
|
enum {DITHER_CUBE, DITHER_VGA, DITHER_NONE} dither_enum;
|
|
|
|
|
|
/* state info for push-model GIF decoder */
|
|
struct _GIFinfo
|
|
{
|
|
/* screen characteristics (file scope) */
|
|
unsigned char rawColorMapBytes[3*MAXCOLORMAPSIZE];
|
|
unsigned long ScreenBitPixel;
|
|
unsigned long AspectRatio;
|
|
|
|
/* image characteristics (for current image) */
|
|
unsigned char rawLocalColorMapBytes[3*MAXCOLORMAPSIZE];
|
|
long useGlobalColormap;
|
|
long ImageBitPixel;
|
|
long imageCount;
|
|
long transparent;
|
|
long interlace;
|
|
long width, height; /* of image, in pixels */
|
|
long rowbytes; /* width of image row (in bytes) */
|
|
BOOL bGotHeader; /* true, AFTER GIF_write has extracted header */
|
|
|
|
struct ImageInfo *pIInfo;
|
|
|
|
int dither_type;
|
|
unsigned char *image; /* the decompressed image */
|
|
unsigned char *predither_image; /* the decompressed image */
|
|
long decoded_pass; /* image decoded as of this pass */
|
|
long decoded_ypos; /* image filled through this row */
|
|
BOOL bFirstPass; /* is this the first pass? */
|
|
|
|
/* gPrefs.* settings for duration of image */
|
|
BOOL bDitherColors; /* don't change dithering method halfway thru */
|
|
|
|
/* async state info */
|
|
int state;
|
|
struct _LWZinfo lwz;
|
|
unsigned char ext_type; /* extension type */
|
|
unsigned char buf[256]; /* scratch buffer */
|
|
struct buf_out async_buf; /* points to buf, others as needed */
|
|
|
|
/* progressive display */
|
|
long xpos, ypos; /* for current (ie, next) pixel */
|
|
|
|
/* applies iff interlaced */
|
|
long line; /* input line 0 -> height-1 */
|
|
long pass; /* output pass 0 -> 3 */
|
|
long step; /* increment 8, 8, 4, 2 */
|
|
int fill; /* number of lines to fill interlace */
|
|
|
|
struct _rgb gif_colors[MAXCOLORMAPSIZE];
|
|
long cmapSize;
|
|
|
|
#if defined(WIN32) || defined(UNIX)
|
|
/*
|
|
Only the Windows and UNIX versions do their own
|
|
dithering.
|
|
*/
|
|
|
|
#ifdef UNIX
|
|
/*
|
|
The UNIX version can dither to a color space other than
|
|
6x6x6. The Windows version only dithers to a 6x6x6 cube,
|
|
so the level increments and number of levels for Windows
|
|
are constant.
|
|
*/
|
|
int red_level_incr;
|
|
int grn_level_incr;
|
|
int blu_level_incr;
|
|
|
|
int red_color_levels;
|
|
int grn_color_levels;
|
|
int blu_color_levels;
|
|
|
|
int calc_matrix0; /* Added to pre calculate some numbers */
|
|
int calc_matrix1; /* Added to pre calculate some numbers */
|
|
int calc_matrix2; /* Added to pre calculate some numbers */
|
|
int calc_matrix3; /* Added to pre calculate some numbers */
|
|
int calc_matrix4; /* Added to pre calculate some numbers */
|
|
int calc_matrix5; /* Added to pre calculate some numbers */
|
|
int calc_matrix6; /* Added to pre calculate some numbers */
|
|
|
|
#endif /* UNIX */
|
|
|
|
struct
|
|
{
|
|
int *v_red;
|
|
int *v_grn;
|
|
int *v_blu;
|
|
int *v_red_mem;
|
|
int *v_grn_mem;
|
|
int *v_blu_mem;
|
|
int h_red;
|
|
int h_grn;
|
|
int h_blu;
|
|
int red_next_error;
|
|
int grn_next_error;
|
|
int blu_next_error;
|
|
} dither;
|
|
#endif /* WIN32 || UNIX */
|
|
|
|
/* platform-specific stuff */
|
|
#ifdef WIN32
|
|
RGBQUAD * colrs; /* colormap */
|
|
HPALETTE hPalette; /* derived from colormap */
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
XColor * colrs; /* colormap */
|
|
unsigned char *mask;
|
|
#endif
|
|
|
|
#ifdef MAC
|
|
CTabHandle colrs; /* colormap */
|
|
char IndexMap[256]; /* indexes shifted colormap */
|
|
GWorldPtr gw;
|
|
BitMap *mask;
|
|
#endif /* MAC */
|
|
};
|
|
|
|
#ifdef NEW_DITHERER
|
|
int dither_row (struct _GIFinfo *gif, unsigned char * from, unsigned char * to,
|
|
int row, int ncols);
|
|
void dither_init ( void );
|
|
#endif
|
|
|
|
/*************************************************************
|
|
|
|
prototypes
|
|
|
|
**************************************************************/
|
|
|
|
static int DecodeGIF(struct buf_in *src, struct _GIFinfo *gif);
|
|
static BOOL GIF_DoExtension(struct buf_in *src, struct _GIFinfo *gif);
|
|
static BOOL GIF_ReadImage(struct buf_in *src, struct _GIFinfo *gif);
|
|
static struct ImageInfo * GIF_DoSetImage (HTStream *me);
|
|
|
|
static BOOL GIF_StorePlatformColorMap(struct _GIFinfo *gif);
|
|
static BOOL GIF_ReadColorMap(struct _GIFinfo *gif, long cmapSize, unsigned char cmap[3*MAXCOLORMAPSIZE]);
|
|
|
|
#ifdef WIN32
|
|
static BOOL GIF_AllocImage(unsigned char **image, long w, long h, long *rowbytes, long transparent);
|
|
#endif /* WIN32 */
|
|
|
|
#ifdef UNIX
|
|
static BOOL GIF_AllocImage(unsigned char **image, long w, long h, long *rowbytes, long transparent, unsigned char **mask);
|
|
#endif /* UNIX */
|
|
|
|
#ifdef MAC
|
|
static BOOL GIF_StoreMacintoshColorMap(CTabHandle *colrs,long cmapSize,unsigned char cmap[3*MAXCOLORMAPSIZE],
|
|
struct _GIFinfo *gif);
|
|
static BOOL GIF_AllocImage(long w, long h, CTabHandle colrs, long transparent,
|
|
GWorldPtr *gw, long *rowbytes, BitMap **mask);
|
|
#endif /* MAC */
|
|
|
|
static int xx_Dither(unsigned char *pdata, unsigned char *dest,
|
|
struct _GIFinfo *gif, int yLast, int yThis);
|
|
#ifdef NEW_DITHERER
|
|
static int xx_NewDither(unsigned char *pdata, unsigned char *dest,
|
|
struct _GIFinfo *gif, int yLast, int yThis);
|
|
#endif
|
|
static int (*dither_func)(unsigned char *pdata, unsigned char *dest,
|
|
struct _GIFinfo *gif, int yLast, int yThis) = xx_Dither;
|
|
|
|
static bNewDitherer = 0;
|
|
|
|
|
|
|
|
|
|
/*************************************************************
|
|
|
|
buffer-handling stuff
|
|
|
|
**************************************************************/
|
|
|
|
|
|
/*
|
|
FillBuf: "push"-model way of getting more bytes
|
|
*/
|
|
static BOOL FillBuf(struct buf_in *src, struct buf_out *buf, long bytesNeeded)
|
|
{
|
|
long avail, needed, len;
|
|
BOOL bOutputFull;
|
|
|
|
avail = src->len - src->offset; /* input bytes available */
|
|
needed = bytesNeeded - buf->offset; /* output bytes still needed */
|
|
|
|
bOutputFull = (needed <= avail);
|
|
|
|
/* how many of them should we copy? */
|
|
if (bOutputFull)
|
|
len = needed; /* enuf to fill the output buffer */
|
|
else
|
|
len = avail; /* all -- empty the input buffer */
|
|
|
|
if (len > 0)
|
|
{
|
|
memcpy(buf->pData + buf->offset, src->pData + src->offset, (int) len);
|
|
|
|
src->offset += len;
|
|
buf->offset += len;
|
|
}
|
|
|
|
if (bOutputFull)
|
|
{
|
|
/* initialize for next use */
|
|
buf->offset = 0;
|
|
buf->iBlockState = BLOCK_INIT;
|
|
}
|
|
else if (src->bForceFlush)
|
|
{
|
|
/*
|
|
HACK: force partial output buffer to be flushed.
|
|
this only makes sense at EOF, when we want to process any
|
|
lingering bytes that we've been buffering.
|
|
|
|
NOTE: buf->offset is *NOT* reset for next use, since there won't
|
|
be one for this stream. it's kept around for callers who
|
|
want to know exactly how many bytes are available in the
|
|
partial buffer.
|
|
*/
|
|
bOutputFull = TRUE;
|
|
}
|
|
|
|
return bOutputFull;
|
|
}
|
|
|
|
|
|
/*
|
|
FillChar: "push"-model way of getting a single byte
|
|
*/
|
|
static BOOL FillChar(struct buf_in *src, unsigned char *c)
|
|
{
|
|
long avail = src->len - src->offset; /* input bytes available */
|
|
|
|
if (avail == 0)
|
|
return FALSE;
|
|
else
|
|
{
|
|
struct buf_out tmp;
|
|
|
|
tmp.pData = c;
|
|
tmp.offset = 0;
|
|
|
|
return (FillBuf(src, &tmp, 1));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
FillBlock: "push"-model way of getting GIF data block
|
|
*/
|
|
static long FillBlock(struct buf_in *src, struct buf_out *buf)
|
|
{
|
|
switch (buf->iBlockState)
|
|
{
|
|
case BLOCK_INIT: /* how many bytes in block? */
|
|
{
|
|
unsigned char count = 0;
|
|
|
|
if (!FillChar(src, &count))
|
|
return -1; /* suspend and try again */
|
|
|
|
buf->offset = 0;
|
|
buf->blockSize = (long) count;
|
|
|
|
if (buf->blockSize == 0)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: this is a ZeroDataBlock.\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
buf->iBlockState = BLOCK_READ;
|
|
/* fall through */
|
|
|
|
case BLOCK_READ: /* ie, offset < blockSize */
|
|
if (!FillBuf(src, buf, buf->blockSize))
|
|
return -1; /* suspend and try again */
|
|
|
|
/* all done */
|
|
break;
|
|
}
|
|
|
|
if (src->bForceFlush && (src->len == src->offset))
|
|
{
|
|
/* flush whatever we've got */
|
|
long bytes = buf->offset;
|
|
|
|
buf->offset = 0;
|
|
return bytes;
|
|
}
|
|
else
|
|
{
|
|
/* entire block available */
|
|
return buf->blockSize;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
|
|
LWZ stuff
|
|
|
|
**************************************************************/
|
|
|
|
/*
|
|
initLWZ: initialize LWZ decompressor state
|
|
*/
|
|
static void initLWZ(struct _LWZinfo *lwz, long input_code_size)
|
|
{
|
|
lwz->oldcode = -99;
|
|
lwz->set_code_size = input_code_size;
|
|
lwz->code_size = lwz->set_code_size + 1;
|
|
lwz->clear_code = 1 << lwz->set_code_size;
|
|
lwz->end_code = lwz->clear_code + 1;
|
|
lwz->max_code_size = 2 * lwz->clear_code;
|
|
lwz->max_code = lwz->clear_code + 2;
|
|
|
|
lwz->curbit = lwz->lastbit = 0;
|
|
lwz->last_byte = 2;
|
|
lwz->get_done = FALSE;
|
|
|
|
lwz->return_clear = TRUE;
|
|
|
|
lwz->sp = lwz->stack;
|
|
|
|
lwz->suspended = FALSE;
|
|
}
|
|
|
|
/*
|
|
nextCode: get next code's worth of bits
|
|
*/
|
|
static long nextCode(struct _LWZinfo *lwz, struct buf_in *src)
|
|
{
|
|
static long maskTbl[16] =
|
|
{
|
|
0x0000, 0x0001, 0x0003, 0x0007,
|
|
0x000f, 0x001f, 0x003f, 0x007f,
|
|
0x00ff, 0x01ff, 0x03ff, 0x07ff,
|
|
0x0fff, 0x1fff, 0x3fff, 0x7fff,
|
|
};
|
|
long i, j, ret, end;
|
|
|
|
if (lwz->return_clear)
|
|
{
|
|
lwz->return_clear = FALSE;
|
|
return lwz->clear_code;
|
|
}
|
|
|
|
/* see if we have enough bits to emit nextCode */
|
|
end = lwz->curbit + lwz->code_size;
|
|
|
|
if (end >= lwz->lastbit)
|
|
{
|
|
/* nope. get some more */
|
|
long count;
|
|
|
|
if (lwz->get_done)
|
|
{
|
|
/** Added by SP for checking **/
|
|
if (lwz->curbit >= lwz->lastbit)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: ran off the end of my bits\n"));
|
|
}
|
|
|
|
return -1; /* HYP: all done */
|
|
}
|
|
|
|
/* try to get a whole block in the buffer */
|
|
if (!lwz->suspended)
|
|
{
|
|
/* don't do any of this on restart */
|
|
lwz->buf[0] = lwz->buf[lwz->last_byte - 2];
|
|
lwz->buf[1] = lwz->buf[lwz->last_byte - 1];
|
|
|
|
lwz->async_buf.pData = &lwz->buf[2];
|
|
lwz->async_buf.offset = 0;
|
|
lwz->async_buf.iBlockState = BLOCK_INIT;
|
|
}
|
|
|
|
if ((count = FillBlock(src, &lwz->async_buf)) == 0)
|
|
lwz->get_done = TRUE;
|
|
|
|
if (count == -1)
|
|
{
|
|
lwz->suspended = TRUE;
|
|
return -1; /* suspend and try again */
|
|
}
|
|
|
|
lwz->suspended = FALSE;
|
|
|
|
lwz->last_byte = 2 + count;
|
|
lwz->curbit = (lwz->curbit - lwz->lastbit) + 16;
|
|
lwz->lastbit = (2 + count) * 8;
|
|
|
|
end = lwz->curbit + lwz->code_size;
|
|
}
|
|
|
|
j = end / 8;
|
|
i = lwz->curbit / 8;
|
|
|
|
if (i == j)
|
|
ret = lwz->buf[i];
|
|
else if (i + 1 == j)
|
|
ret = lwz->buf[i] | (((long) lwz->buf[i + 1]) << 8);
|
|
else
|
|
ret = lwz->buf[i] | (((long) lwz->buf[i + 1]) << 8) | (((long) lwz->buf[i + 2]) << 16);
|
|
|
|
ret = (ret >> (lwz->curbit % 8)) & maskTbl[lwz->code_size];
|
|
|
|
lwz->curbit += lwz->code_size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
readLWZ: pops code for next pixel from stack or buffer
|
|
*/
|
|
#define readLWZ(lwz, src) ((lwz->sp > lwz->stack) ? *--lwz->sp : nextLWZ(lwz, src))
|
|
|
|
|
|
/*
|
|
nextLWZ: gets code for next pixel from buffer
|
|
*/
|
|
static long nextLWZ(struct _LWZinfo *lwz, struct buf_in *src)
|
|
{
|
|
long code, incode, count;
|
|
long i;
|
|
|
|
while ((code = nextCode(lwz, src)) >= 0) /* NOTE: may suspend (code = -1) */
|
|
{
|
|
if (code == lwz->clear_code)
|
|
{
|
|
/* corrupt GIFs can make this happen */
|
|
if (lwz->clear_code >= (1 << MAX_LWZ_BITS))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: probable corrupt gif.\n"));
|
|
return -2;
|
|
}
|
|
|
|
if (lwz->oldcode != -1)
|
|
{
|
|
/* only do this once (ie, for first clear_code in seq.)
|
|
& not on restart after suspended clear_code consumption */
|
|
for (i = 0; i < lwz->clear_code; ++i)
|
|
{
|
|
lwz->table[0][i] = 0;
|
|
lwz->table[1][i] = i;
|
|
}
|
|
for (; i < (1 << MAX_LWZ_BITS); ++i)
|
|
lwz->table[0][i] = lwz->table[1][i] = 0;
|
|
lwz->code_size = lwz->set_code_size + 1;
|
|
lwz->max_code_size = 2 * lwz->clear_code;
|
|
lwz->max_code = lwz->clear_code + 2;
|
|
lwz->sp = lwz->stack;
|
|
}
|
|
do
|
|
{
|
|
/* consume subsequent clear_codes */
|
|
lwz->firstcode = lwz->oldcode = nextCode(lwz, src);
|
|
|
|
if (lwz->oldcode < 0)
|
|
return -1; /* suspend and try again */
|
|
}
|
|
while (lwz->firstcode == lwz->clear_code);
|
|
|
|
return lwz->firstcode;
|
|
}
|
|
if (lwz->oldcode == -1)
|
|
{
|
|
/* restart after suspended clear_code consumption */
|
|
lwz->firstcode = lwz->oldcode = code;
|
|
|
|
return lwz->firstcode;
|
|
}
|
|
if (code == lwz->end_code)
|
|
{
|
|
/* was previous block at end of block sequence? */
|
|
if ((lwz->async_buf.blockSize == 0) &&
|
|
(lwz->async_buf.iBlockState == BLOCK_INIT))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: Error?: ZeroDataBlock\n"));
|
|
return -2;
|
|
}
|
|
|
|
/* consume rest of blocks in this sequence */
|
|
while ((count = FillBlock(src, &lwz->async_buf)) > 0)
|
|
;
|
|
|
|
if (count != 0)
|
|
return -1; /* suspend and try again */
|
|
|
|
XX_DMsg(DBG_IMAGE, ("GIF: code==lwz->end_code\n"));
|
|
return -2;
|
|
}
|
|
|
|
incode = code;
|
|
|
|
if (code >= lwz->max_code)
|
|
{
|
|
*lwz->sp++ = lwz->firstcode;
|
|
code = lwz->oldcode;
|
|
}
|
|
|
|
while (code >= lwz->clear_code)
|
|
{
|
|
*lwz->sp++ = lwz->table[1][code];
|
|
if (code == lwz->table[0][code])
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: circular table entry BIG ERROR\n"));
|
|
return (code);
|
|
}
|
|
|
|
/** Added by SP for checks **/
|
|
if ((long)lwz->sp >= ((long)lwz->stack + lwz->stacksize))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: Error circular table STACK OVERFLOW in ReadGIF\n"));
|
|
return(code);
|
|
}
|
|
|
|
code = lwz->table[0][code];
|
|
}
|
|
|
|
*lwz->sp++ = lwz->firstcode = lwz->table[1][code];
|
|
|
|
if ((code = lwz->max_code) < (1 << MAX_LWZ_BITS))
|
|
{
|
|
lwz->table[0][code] = lwz->oldcode;
|
|
lwz->table[1][code] = lwz->firstcode;
|
|
++lwz->max_code;
|
|
if ((lwz->max_code >= lwz->max_code_size) && (lwz->max_code_size < (1 << MAX_LWZ_BITS)))
|
|
{
|
|
lwz->max_code_size *= 2;
|
|
++lwz->code_size;
|
|
}
|
|
}
|
|
|
|
lwz->oldcode = incode;
|
|
|
|
if (lwz->sp > lwz->stack)
|
|
return (*--lwz->sp);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
|
|
GIF-specific stuff
|
|
|
|
**************************************************************/
|
|
|
|
#define STATE_FILE_TYPE (STATE_OTHER)
|
|
#define STATE_FILE_HEADER (STATE_OTHER + 1)
|
|
#define STATE_FILE_COLORMAP (STATE_OTHER + 2)
|
|
#define STATE_FILE_SECTION (STATE_OTHER + 3)
|
|
#define STATE_FILE_EXTENSION (STATE_OTHER + 4)
|
|
#define STATE_FILE_EXTENSION_DATA (STATE_OTHER + 5)
|
|
#define STATE_IMAGE_HEADER (STATE_OTHER + 6)
|
|
#define STATE_IMAGE_COLORMAP (STATE_OTHER + 7)
|
|
#define STATE_IMAGE_CREATE (STATE_OTHER + 8)
|
|
#define STATE_IMAGE_LWZINIT (STATE_OTHER + 9)
|
|
#define STATE_IMAGE_DATA (STATE_OTHER + 10)
|
|
#define STATE_IMAGE_DONE (STATE_OTHER + 11)
|
|
|
|
/*
|
|
DecodeGIF: "push"-model version of ReadGIF
|
|
|
|
-------------------------------------------------------------------
|
|
|
|
consumes ENTIRE input buffer, processes that buffer as far as it
|
|
can, and then suspends.
|
|
|
|
each state has the following logic:
|
|
|
|
1. try to consume a given amount of input
|
|
2. if not enough, buffer it and suspend (restart w/same state)
|
|
3. otherwise, process entire buffer and go to next state
|
|
|
|
on restart, we jump to the same state we left and keep adding to
|
|
whatever we already have buffered.
|
|
*/
|
|
static int DecodeGIF(struct buf_in *src, struct _GIFinfo *gif)
|
|
{
|
|
unsigned char c;
|
|
char version[4];
|
|
unsigned char *buf;
|
|
struct buf_out *async_buf;
|
|
struct _LWZinfo *lwz;
|
|
BOOL bLoop;
|
|
|
|
/* the following is for convenience */
|
|
buf = gif->buf;
|
|
async_buf = &gif->async_buf;
|
|
lwz = &gif->lwz;
|
|
|
|
/* can loop over state machine, if needed */
|
|
do
|
|
{
|
|
/* assume not */
|
|
bLoop = FALSE;
|
|
|
|
switch (gif->state)
|
|
{
|
|
case STATE_INIT:
|
|
gif->image = NULL;
|
|
#ifdef MAC
|
|
gif->gw = NULL;
|
|
#endif /* MAC */
|
|
|
|
gif->imageCount = 0;
|
|
gif->transparent = -1;
|
|
gif->bDitherColors = gPrefs.bDitherColors;
|
|
|
|
/* start with scratch buffer */
|
|
async_buf->pData = buf;
|
|
async_buf->offset = 0;
|
|
async_buf->iBlockState = BLOCK_INIT;
|
|
|
|
gif->state = STATE_FILE_TYPE;
|
|
|
|
/* special case: simple initialization */
|
|
if (src->len == 0)
|
|
break; /* suspend and try again */
|
|
|
|
/* fall through */
|
|
|
|
case STATE_FILE_TYPE:
|
|
if (!FillBuf(src, async_buf, 6))
|
|
break; /* suspend and try again */
|
|
|
|
if (strncmp((char *) buf, "GIF", 3) != 0)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: not a GIF file\n"));
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
|
|
version[0] = *(buf + 3);
|
|
version[1] = *(buf + 4);
|
|
version[2] = *(buf + 5);
|
|
version[3] = '\0';
|
|
|
|
/* ALT: take Tom Lane's approach */
|
|
if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: bad version number, not 87a or 89a\n"));
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
|
|
gif->state = STATE_FILE_HEADER;
|
|
/* fall through */
|
|
|
|
case STATE_FILE_HEADER:
|
|
if (!FillBuf(src, async_buf, 7))
|
|
break; /* suspend and try again */
|
|
|
|
gif->ScreenBitPixel = 2 << (buf[4] & 0x07);
|
|
gif->AspectRatio = buf[6];
|
|
|
|
if (gif->AspectRatio != 0 && gif->AspectRatio != 49)
|
|
{
|
|
float r;
|
|
r = ((float) (gif->AspectRatio) + (float) 15.0) / (float) 64.0;
|
|
XX_DMsg(DBG_IMAGE, ("Warning: non-square pixels!\n"));
|
|
}
|
|
|
|
if (!BitSet(buf[4], LOCALCOLORMAP))
|
|
gif->state = STATE_FILE_SECTION; /* skip next state */
|
|
else
|
|
{
|
|
/* switch buffers to read colormap */
|
|
async_buf->pData = gif->rawColorMapBytes;
|
|
async_buf->offset = 0;
|
|
async_buf->iBlockState = BLOCK_INIT;
|
|
|
|
gif->state = STATE_FILE_COLORMAP;
|
|
}
|
|
/* fall through */
|
|
|
|
case STATE_FILE_COLORMAP:
|
|
if (gif->state == STATE_FILE_COLORMAP)
|
|
{
|
|
if (!FillBuf(src, async_buf, (gif->ScreenBitPixel * 3)))
|
|
break; /* suspend and try again */
|
|
|
|
/* switch back to scratch buffer */
|
|
async_buf->pData = buf;
|
|
async_buf->offset = 0;
|
|
async_buf->iBlockState = BLOCK_INIT;
|
|
}
|
|
|
|
gif->state = STATE_FILE_SECTION;
|
|
/* fall through */
|
|
|
|
case STATE_FILE_SECTION:
|
|
if (!FillChar(src, &c))
|
|
break; /* suspend and try again */
|
|
|
|
if (c == ';') /* GIF terminator */
|
|
{
|
|
|
|
if (gif->imageCount < 1)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("No images found in file\n"));
|
|
|
|
gif->state = STATE_ABORT;
|
|
}
|
|
else
|
|
{
|
|
gif->state = STATE_DONE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (c == '!') /* Extension */
|
|
{
|
|
gif->state = STATE_FILE_EXTENSION;
|
|
}
|
|
else if (c == ',') /* Valid start character */
|
|
{
|
|
++gif->imageCount;
|
|
|
|
gif->state = STATE_IMAGE_HEADER;
|
|
}
|
|
else
|
|
{
|
|
#ifdef GIF_FIX_THIS
|
|
/* TODO: exit here? */
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
#else
|
|
/* ALT: just loop and keep trying? */
|
|
gif->state = STATE_FILE_SECTION;
|
|
bLoop = TRUE; /* restart */
|
|
break;
|
|
#endif /* GIF_FIX_THIS */
|
|
}
|
|
/* fall through */
|
|
|
|
case STATE_FILE_EXTENSION:
|
|
if (gif->state == STATE_FILE_EXTENSION)
|
|
{
|
|
if (!FillChar(src, &c))
|
|
break; /* suspend and try again */
|
|
|
|
gif->ext_type = c;
|
|
gif->state = STATE_FILE_EXTENSION_DATA;
|
|
}
|
|
/* fall through */
|
|
|
|
case STATE_FILE_EXTENSION_DATA:
|
|
if (gif->state == STATE_FILE_EXTENSION_DATA)
|
|
{
|
|
if (!GIF_DoExtension(src, gif))
|
|
break; /* suspend and try again */
|
|
|
|
gif->state = STATE_FILE_SECTION;
|
|
bLoop = TRUE; /* restart */
|
|
break;
|
|
}
|
|
|
|
case STATE_IMAGE_HEADER:
|
|
if (!FillBuf(src, async_buf, 9))
|
|
break; /* suspend and try again */
|
|
|
|
gif->useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
|
|
gif->interlace = (long) BitSet(buf[8], INTERLACE);
|
|
|
|
gif->ImageBitPixel = 1 << ((buf[8] & 0x07) + 1);
|
|
|
|
gif->width = (long) LM_to_uint(buf[4], buf[5]);
|
|
gif->height = (long) LM_to_uint(buf[6], buf[7]);
|
|
|
|
if (gif->useGlobalColormap)
|
|
gif->state = STATE_IMAGE_CREATE; /* skip next state */
|
|
else
|
|
{
|
|
/* switch buffers to read local colormap */
|
|
async_buf->pData = gif->rawLocalColorMapBytes;
|
|
async_buf->offset = 0;
|
|
async_buf->iBlockState = BLOCK_INIT;
|
|
|
|
gif->state = STATE_IMAGE_COLORMAP;
|
|
}
|
|
/* fall through */
|
|
|
|
case STATE_IMAGE_COLORMAP:
|
|
if (gif->state == STATE_IMAGE_COLORMAP)
|
|
{
|
|
if (!FillBuf(src, async_buf, (gif->ImageBitPixel * 3)))
|
|
break; /* suspend and try again */
|
|
|
|
/* switch back to scratch buffer */
|
|
async_buf->pData = buf;
|
|
async_buf->offset = 0;
|
|
async_buf->iBlockState = BLOCK_INIT;
|
|
}
|
|
|
|
gif->state = STATE_IMAGE_CREATE;
|
|
/* fall through */
|
|
|
|
case STATE_IMAGE_CREATE:
|
|
if (!gif->useGlobalColormap)
|
|
{
|
|
GIF_ReadColorMap(gif, gif->ImageBitPixel,
|
|
gif->rawLocalColorMapBytes);
|
|
#ifdef MAC
|
|
GIF_StoreMacintoshColorMap (&gif->colrs,
|
|
gif->ImageBitPixel, gif->rawLocalColorMapBytes, gif);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
GIF_ReadColorMap (gif, gif->ScreenBitPixel,
|
|
gif->rawColorMapBytes);
|
|
#ifdef MAC
|
|
GIF_StoreMacintoshColorMap (&gif->colrs,
|
|
gif->ScreenBitPixel, gif->rawColorMapBytes, gif);
|
|
#endif
|
|
}
|
|
|
|
#ifndef MAC
|
|
GIF_StorePlatformColorMap(gif);
|
|
#endif
|
|
|
|
/* create device-specific image */
|
|
|
|
#ifdef UNIX
|
|
if (!GIF_AllocImage (&gif->image, gif->width, gif->height,
|
|
&gif->rowbytes, gif->transparent, &gif->mask))
|
|
#endif
|
|
#ifdef WIN32
|
|
if (!GIF_AllocImage (&gif->image, gif->width, gif->height,
|
|
&gif->rowbytes, gif->transparent))
|
|
#endif
|
|
#ifdef MAC
|
|
if (!GIF_AllocImage (gif->width, gif->height, gif->colrs,
|
|
gif->transparent, &gif->gw, &gif->rowbytes, &gif->mask))
|
|
#endif
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
|
|
#ifndef MAC
|
|
if ((gif->interlace) &&
|
|
(gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
{
|
|
/* create temporary pre-dithered copy of image */
|
|
#ifdef UNIX
|
|
if (!GIF_AllocImage(&gif->predither_image, gif->width,
|
|
gif->height, &gif->rowbytes, gif->transparent,
|
|
NULL))
|
|
#endif
|
|
#ifdef WIN32
|
|
if (!GIF_AllocImage (&gif->predither_image, gif->width,
|
|
gif->height, &gif->rowbytes, gif->transparent))
|
|
#endif
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gif->predither_image = NULL;
|
|
}
|
|
#endif /* !MAC */
|
|
|
|
gif->state = STATE_IMAGE_LWZINIT;
|
|
/* fall through */
|
|
|
|
case STATE_IMAGE_LWZINIT:
|
|
if (!FillChar(src, &c))
|
|
break; /* suspend and try again */
|
|
|
|
/*
|
|
** Initialize the Compression routines
|
|
*/
|
|
lwz->stacksize = (1 << MAX_LWZ_BITS) * 2 * sizeof(long);
|
|
|
|
lwz->stack = (long *)GTR_MALLOC(lwz->stacksize);
|
|
lwz->table[0] = (long *)GTR_MALLOC((1 << MAX_LWZ_BITS) * sizeof(long));
|
|
lwz->table[1] = (long *)GTR_MALLOC((1 << MAX_LWZ_BITS) * sizeof(long));
|
|
|
|
if (!lwz->stack || !lwz->table[0] || !lwz->table[1])
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
|
|
initLWZ(lwz, c);
|
|
|
|
/* initialize loops */
|
|
gif->xpos = gif->ypos = 0;
|
|
gif->decoded_pass = 0;
|
|
gif->decoded_ypos = 0;
|
|
gif->bFirstPass = TRUE;
|
|
|
|
if (gif->interlace)
|
|
{
|
|
gif->line = 0;
|
|
gif->pass = 0;
|
|
gif->step = 8;
|
|
gif->fill = 7;
|
|
}
|
|
|
|
gif->state = STATE_IMAGE_DATA;
|
|
|
|
/* initialize dithering stuff */
|
|
#ifdef WIN32
|
|
if (wg.eColorMode == 8)
|
|
gif->dither_type = DITHER_CUBE;
|
|
else
|
|
gif->dither_type = DITHER_NONE; /* true color screen */
|
|
|
|
/* TODO: (Eric) what should happen for VGA? */
|
|
#endif /* WIN32 */
|
|
|
|
#ifdef MAC
|
|
gif->dither_type = DITHER_NONE;
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
/* if ((display_depth == 8) && gif->bDitherColors) */
|
|
if (display_depth == 8)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Init lzw dither info.\n");
|
|
#endif
|
|
gif->dither_type = DITHER_CUBE;
|
|
gif->red_color_levels = dither_info.red_levels;
|
|
gif->grn_color_levels = dither_info.green_levels;
|
|
gif->blu_color_levels = dither_info.blue_levels;
|
|
gif->red_level_incr = 255 / (gif->red_color_levels - 1);
|
|
gif->grn_level_incr = 255 / (gif->grn_color_levels - 1);
|
|
gif->blu_level_incr = 255 / (gif->blu_color_levels - 1);
|
|
gif->calc_matrix0 = gif->red_level_incr / 2;
|
|
gif->calc_matrix1 = gif->red_color_levels - 1;
|
|
gif->calc_matrix2 = gif->grn_level_incr / 2;
|
|
gif->calc_matrix3 = gif->grn_color_levels - 1;
|
|
gif->calc_matrix4 = gif->blu_level_incr / 2;
|
|
gif->calc_matrix5 = gif->blu_color_levels - 1;
|
|
gif->calc_matrix6 = gif->grn_color_levels * gif->blu_color_levels;
|
|
}
|
|
else
|
|
gif->dither_type = DITHER_NONE;
|
|
|
|
#endif /* UNIX */
|
|
|
|
#if defined(WIN32) || defined(UNIX)
|
|
/* TODO:
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
*/
|
|
#ifdef UNIX
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
#endif
|
|
{
|
|
memset(&gif->dither, 0, sizeof(gif->dither));
|
|
gif->dither.v_red_mem =
|
|
(int *) GTR_CALLOC(gif->width + 2, sizeof(int));
|
|
if (!gif->dither.v_red_mem)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
gif->dither.v_grn_mem =
|
|
(int *) GTR_CALLOC(gif->width + 2, sizeof(int));
|
|
if (!gif->dither.v_grn_mem) {
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
gif->dither.v_blu_mem =
|
|
(int *) GTR_CALLOC(gif->width + 2, sizeof(int));
|
|
if (!gif->dither.v_blu_mem)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
gif->state = STATE_ABORT;
|
|
break;
|
|
}
|
|
|
|
/* Initially, the vertical errors are 0, set by calloc */
|
|
gif->dither.v_red = gif->dither.v_red_mem + 1;
|
|
gif->dither.v_grn = gif->dither.v_grn_mem + 1;
|
|
gif->dither.v_blu = gif->dither.v_blu_mem + 1;
|
|
}
|
|
#endif /* WIN32 || UNIX */
|
|
|
|
/* fall through */
|
|
|
|
case STATE_IMAGE_DATA:
|
|
if (!GIF_ReadImage(src, gif))
|
|
{
|
|
/*
|
|
TODO: update display w/partial image (progressively)
|
|
*/
|
|
break; /* suspend and try again */
|
|
}
|
|
|
|
gif->state = STATE_IMAGE_DONE;
|
|
/* fall through */
|
|
|
|
case STATE_IMAGE_DONE:
|
|
/*
|
|
NOTE: if DecodeGIF gets rewritten to handle multi-image GIFs,
|
|
then we'll need to clean up the lwz memory each time
|
|
we hit this state.
|
|
*/
|
|
|
|
gif->state = STATE_DONE;
|
|
/* fall through */
|
|
|
|
case STATE_DONE:
|
|
/*
|
|
we don't care about the rest of the file, so just eat input
|
|
*/
|
|
src->offset = src->len;
|
|
|
|
/*
|
|
TODO: update display w/final image (if needed)
|
|
*/
|
|
break;
|
|
|
|
case STATE_ABORT:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
while (bLoop);
|
|
|
|
if ((gif->state == STATE_DONE) ||
|
|
(gif->state == STATE_ABORT))
|
|
{
|
|
/* clean up lwz memory */
|
|
if (lwz->stack)
|
|
{
|
|
GTR_FREE(lwz->stack);
|
|
lwz->stack = NULL;
|
|
}
|
|
if (lwz->table[0])
|
|
{
|
|
GTR_FREE(lwz->table[0]);
|
|
lwz->table[0] = NULL;
|
|
}
|
|
if (lwz->table[1])
|
|
{
|
|
GTR_FREE(lwz->table[1]);
|
|
lwz->table[1] = NULL;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(UNIX)
|
|
#ifdef UNIX
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
#endif
|
|
{
|
|
/* clean up dither memory */
|
|
if (gif->dither.v_red_mem)
|
|
{
|
|
GTR_FREE(gif->dither.v_red_mem);
|
|
gif->dither.v_red_mem = NULL;
|
|
}
|
|
if (gif->dither.v_grn_mem)
|
|
{
|
|
GTR_FREE(gif->dither.v_grn_mem);
|
|
gif->dither.v_grn_mem = NULL;
|
|
}
|
|
if (gif->dither.v_blu_mem)
|
|
{
|
|
GTR_FREE(gif->dither.v_blu_mem);
|
|
gif->dither.v_blu_mem = NULL;
|
|
}
|
|
|
|
if (gif->predither_image)
|
|
{
|
|
GTR_FREE(gif->predither_image);
|
|
gif->predither_image = NULL;
|
|
}
|
|
}
|
|
#endif /* WIN32 || UNIX */
|
|
|
|
}
|
|
|
|
return gif->state;
|
|
}
|
|
|
|
/*
|
|
ReadGIF: convert entire GIF into properly-oriented image, colors, & info
|
|
*/
|
|
#ifdef WIN32
|
|
unsigned char *
|
|
ReadGIF(unsigned char *pMem, long imagesize, long *w, long *h, RGBQUAD * colrs, long *bg)
|
|
#endif /* WIN32 */
|
|
#ifdef UNIX
|
|
unsigned char *
|
|
ReadGIF(unsigned char *pMem, long imagesize, long *w, long *h, XColor *colrs, long *bg)
|
|
#endif /* UNIX */
|
|
#ifdef MAC
|
|
GWorldPtr
|
|
MacReadGIF(struct Mwin *tw, Handle hMem, long imagesize, int *w, int *h)
|
|
#endif /* MAC */
|
|
{
|
|
struct buf_in *src;
|
|
struct _GIFinfo *gif;
|
|
int state;
|
|
|
|
#ifdef MAC
|
|
GWorldPtr gw = (void *) 0xdeadbeef;
|
|
unsigned char *pMem = (void *) 0xdeadbeef;
|
|
#endif /* MAC */
|
|
|
|
/*
|
|
IDEA: for the moment, hang onto this API (w/addition of len) &
|
|
process the entire image as currently.
|
|
|
|
next, return a whole new image, etc, for each pass.
|
|
*/
|
|
|
|
/* alloc, initialize state variables */
|
|
src = (struct buf_in *) GTR_CALLOC(sizeof(struct buf_in), 1);
|
|
gif = (struct _GIFinfo *) GTR_CALLOC(sizeof(struct _GIFinfo), 1);
|
|
|
|
if (!src || !gif)
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
|
|
|
|
GTR_FREE(src);
|
|
GTR_FREE(gif);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
#ifdef MAC
|
|
HLock(hMem);
|
|
pMem = *hMem;
|
|
#endif /* MAC */
|
|
|
|
src->len = imagesize;
|
|
src->offset = 0;
|
|
src->pData = pMem;
|
|
src->bForceFlush = TRUE;
|
|
|
|
#ifndef MAC
|
|
gif->colrs = colrs;
|
|
#endif
|
|
gif->state = STATE_INIT;
|
|
|
|
/*
|
|
TODO: do we care about the suspension state (if any) here?
|
|
HYP: probably not. depends on whether it's all handled already.
|
|
*/
|
|
state = DecodeGIF(src, gif);
|
|
|
|
/*
|
|
TODO: do we need to pass h/w out any more?
|
|
HYP: calls to update image cache should prob. happen in state machine
|
|
*/
|
|
*w = gif->width;
|
|
*h = gif->height;
|
|
|
|
/*
|
|
TODO: what about colrs & bg?
|
|
HYP: not sure
|
|
*/
|
|
|
|
#ifndef MAC
|
|
if (bg)
|
|
*bg = gif->transparent;
|
|
|
|
return gif->image;
|
|
#else
|
|
HUnlock (hMem);
|
|
return gif->gw;
|
|
#endif /* !MAC */
|
|
}
|
|
|
|
/*
|
|
GIF_DoExtension: read/skip GIF extension blocks
|
|
*/
|
|
static BOOL GIF_DoExtension(struct buf_in *src, struct _GIFinfo *gif)
|
|
{
|
|
char *str;
|
|
unsigned char *buf;
|
|
struct buf_out *async_buf;
|
|
long count;
|
|
|
|
/* the following are for convenience */
|
|
buf = gif->buf;
|
|
async_buf = &gif->async_buf;
|
|
|
|
switch (gif->ext_type)
|
|
{
|
|
case 0x01: /* Plain Text Extension */
|
|
str = "Plain Text Extension";
|
|
break;
|
|
case 0xff: /* Application Extension */
|
|
str = "Application Extension";
|
|
break;
|
|
case 0xfe: /* Comment Extension */
|
|
str = "Comment Extension";
|
|
break;
|
|
case 0xf9: /* Graphic Control Extension */
|
|
str = "Graphic Control Extension";
|
|
if (FillBlock(src, async_buf) < 0)
|
|
return FALSE; /* suspend and try again */
|
|
|
|
if (((unsigned char)buf[0] & 0x1) != 0)
|
|
gif->transparent = (unsigned char)buf[3];
|
|
|
|
break;
|
|
default:
|
|
str = buf;
|
|
sprintf(buf, "UNKNOWN (0x%02x)", gif->ext_type);
|
|
break;
|
|
}
|
|
|
|
/* consume rest of blocks in this sequence */
|
|
while ((count = FillBlock(src, async_buf)) > 0)
|
|
;
|
|
|
|
if (count != 0)
|
|
return FALSE; /* suspend and try again */
|
|
|
|
/* done with extension */
|
|
return TRUE;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(UNIX)
|
|
|
|
#ifdef UNIX
|
|
#define HTGIF_RED_COLOR_LEVELS (gif->red_color_levels)
|
|
#define HTGIF_GREEN_COLOR_LEVELS (gif->grn_color_levels)
|
|
#define HTGIF_BLUE_COLOR_LEVELS (gif->blu_color_levels)
|
|
#define HTGIF_RED_LEVEL_INCR (gif->red_level_incr)
|
|
#define HTGIF_GREEN_LEVEL_INCR (gif->grn_level_incr)
|
|
#define HTGIF_BLUE_LEVEL_INCR (gif->blu_level_incr)
|
|
|
|
#define HTGIF_RED_LEVEL_CALC1 (gif->calc_matrix0)
|
|
#define HTGIF_RED_LEVEL_CALC2 (gif->calc_matrix1)
|
|
#define HTGIF_GREEN_LEVEL_CALC1 (gif->calc_matrix2)
|
|
#define HTGIF_GREEN_LEVEL_CALC2 (gif->calc_matrix3)
|
|
#define HTGIF_BLUE_LEVEL_CALC1 (gif->calc_matrix4)
|
|
#define HTGIF_BLUE_LEVEL_CALC2 (gif->calc_matrix5)
|
|
#define HTGIF_GREEN_BLUE_CALC1 (gif->calc_matrix6)
|
|
|
|
#endif /* UNIX */
|
|
|
|
#ifdef WIN32
|
|
#define HTGIF_RED_COLOR_LEVELS RED_COLOR_LEVELS
|
|
#define HTGIF_GREEN_COLOR_LEVELS GREEN_COLOR_LEVELS
|
|
#define HTGIF_BLUE_COLOR_LEVELS BLUE_COLOR_LEVELS
|
|
#define HTGIF_RED_LEVEL_INCR RED_LEVEL_INCR
|
|
#define HTGIF_GREEN_LEVEL_INCR GREEN_LEVEL_INCR
|
|
#define HTGIF_BLUE_LEVEL_INCR BLUE_LEVEL_INCR
|
|
|
|
#define HTGIF_RED_LEVEL_CALC1 (HTGIF_RED_LEVEL_INCR / 2)
|
|
#define HTGIF_RED_LEVEL_CALC2 (HTGIF_RED_COLOR_LEVELS - 1)
|
|
#define HTGIF_GREEN_LEVEL_CALC1 (HTGIF_GREEN_LEVEL_INCR / 2)
|
|
#define HTGIF_GREEN_LEVEL_CALC2 (HTGIF_GREEN_COLOR_LEVELS - 1)
|
|
#define HTGIF_BLUE_LEVEL_CALC1 (HTGIF_BLUE_LEVEL_INCR / 2)
|
|
#define HTGIF_BLUE_LEVEL_CALC2 (HTGIF_BLUE_COLOR_LEVELS - 1)
|
|
#define HTGIF_GREEN_BLUE_CALC1 (HTGIF_GREEN_COLOR_LEVELS * HTGIF_BLUE_COLOR_LEVELS)
|
|
|
|
#endif /* WIN32 */
|
|
|
|
static int x_MapToClosestColor(struct _GIFinfo *gif, int pixel, struct _rgb *pRGB)
|
|
{
|
|
int r_level, g_level, b_level;
|
|
|
|
if (gif->transparent != -1 && pixel == gif->transparent)
|
|
return BACKGROUND_COLOR_INDEX;
|
|
|
|
/* adjust r to the closest available value */
|
|
r_level = (pRGB->r + HTGIF_RED_LEVEL_CALC1) / HTGIF_RED_LEVEL_INCR;
|
|
if (r_level < 0)
|
|
{
|
|
r_level = 0;
|
|
}
|
|
else if (r_level >= HTGIF_RED_COLOR_LEVELS)
|
|
{
|
|
r_level = HTGIF_RED_LEVEL_CALC2;
|
|
}
|
|
|
|
/* adjust g to the closest available value */
|
|
g_level = (pRGB->g + HTGIF_GREEN_LEVEL_CALC1) / HTGIF_GREEN_LEVEL_INCR;
|
|
if (g_level < 0)
|
|
{
|
|
g_level = 0;
|
|
}
|
|
else if (g_level >= HTGIF_GREEN_COLOR_LEVELS)
|
|
{
|
|
g_level = HTGIF_GREEN_LEVEL_CALC2;
|
|
}
|
|
|
|
/* adjust b to the closest available value */
|
|
b_level = (pRGB->b + HTGIF_BLUE_LEVEL_CALC1) / HTGIF_BLUE_LEVEL_INCR;
|
|
if (b_level < 0)
|
|
{
|
|
b_level = 0;
|
|
}
|
|
else if (b_level >= HTGIF_BLUE_COLOR_LEVELS)
|
|
{
|
|
b_level = HTGIF_BLUE_LEVEL_CALC2;
|
|
}
|
|
|
|
/*
|
|
Having calculated new r, g, and b values
|
|
now we calculate the new pixel value
|
|
*/
|
|
#ifdef UNIX
|
|
return (unsigned char)dither_info.cmap[(r_level*HTGIF_GREEN_BLUE_CALC1 + g_level*HTGIF_BLUE_COLOR_LEVELS + b_level)];
|
|
#else
|
|
return (r_level*HTGIF_GREEN_BLUE_CALC1 + g_level*HTGIF_BLUE_COLOR_LEVELS + b_level);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Floyd-Steinberg error diffusion dithering routine
|
|
|
|
(progressive variant: dithers rows from yLast+1 to yThis)
|
|
*/
|
|
static int xx_Dither(unsigned char *pdata, unsigned char *dest,
|
|
struct _GIFinfo *gif, int yLast, int yThis)
|
|
{
|
|
int x;
|
|
int y;
|
|
int r,g,b;
|
|
int r2,g2,b2;
|
|
int r_level, g_level, b_level;
|
|
int total_error;
|
|
unsigned char *p;
|
|
unsigned char *q;
|
|
unsigned char v;
|
|
int yRow;
|
|
|
|
#ifdef DEBUG
|
|
printf ("Dither last = %d, this = %d.\n", yLast, yThis);
|
|
#endif
|
|
|
|
for (y=yLast+1; y<=yThis; y++)
|
|
{
|
|
/* Beginning each new row, the horizontal errors are 0 */
|
|
gif->dither.h_red = 0;
|
|
gif->dither.h_grn = 0;
|
|
gif->dither.h_blu = 0;
|
|
gif->dither.red_next_error = 0;
|
|
gif->dither.grn_next_error = 0;
|
|
gif->dither.blu_next_error = 0;
|
|
|
|
#ifdef WIN32
|
|
yRow = gif->height - y - 1; /* the DIB is stored upside down */
|
|
#else
|
|
yRow = y; /* our DIB is right side up */
|
|
#endif /* WIN32 */
|
|
|
|
p = pdata + gif->rowbytes * yRow;
|
|
q = dest + gif->rowbytes * yRow;
|
|
|
|
for (x = 0; x != gif->width; x++)
|
|
{
|
|
if ((gif->transparent != -1) && (*p == gif->transparent))
|
|
{
|
|
gif->dither.h_red = 0;
|
|
gif->dither.h_grn = 0;
|
|
gif->dither.h_blu = 0;
|
|
gif->dither.v_red[x] = 0;
|
|
gif->dither.v_grn[x] = 0;
|
|
gif->dither.v_blu[x] = 0;
|
|
*q = BACKGROUND_COLOR_INDEX;
|
|
}
|
|
else
|
|
{
|
|
/* RED */
|
|
/* r is the original value adjusted with the previous vertical and horizontal errors */
|
|
r = gif->gif_colors[*p].r + (gif->dither.h_red + gif->dither.v_red[x]);
|
|
|
|
/* adjust r to the closest available value */
|
|
r_level = (r + HTGIF_RED_LEVEL_CALC1) / HTGIF_RED_LEVEL_INCR;
|
|
if (r_level < 0)
|
|
{
|
|
r_level = 0;
|
|
}
|
|
else if (r_level >= HTGIF_RED_COLOR_LEVELS)
|
|
{
|
|
r_level = HTGIF_RED_LEVEL_CALC2;
|
|
}
|
|
r2 = r_level * HTGIF_RED_LEVEL_INCR;
|
|
|
|
/* calculate the errors for the current pixel, total error is (r - r2) */
|
|
total_error = (r - r2); /* 1/16 */
|
|
gif->dither.v_red[x-1] += (total_error * 3 / 16);
|
|
gif->dither.v_red[x] = (total_error * 5 / 16) + gif->dither.red_next_error; /* from the previous column */
|
|
gif->dither.red_next_error = (total_error / 16);
|
|
gif->dither.h_red = (r - r2) - (total_error * 9 / 16);
|
|
|
|
/* GREEN */
|
|
/* g is the original value adjusted with the previous vertical and horizontal errors */
|
|
|
|
g = gif->gif_colors[*p].g + (gif->dither.h_grn + gif->dither.v_grn[x]);
|
|
|
|
/* adjust g to the closest available value */
|
|
g_level = (g + HTGIF_GREEN_LEVEL_CALC1) / HTGIF_GREEN_LEVEL_INCR;
|
|
if (g_level < 0)
|
|
{
|
|
g_level = 0;
|
|
}
|
|
else if (g_level >= HTGIF_GREEN_COLOR_LEVELS)
|
|
{
|
|
g_level = HTGIF_GREEN_LEVEL_CALC2;
|
|
}
|
|
g2 = g_level * HTGIF_GREEN_LEVEL_INCR;
|
|
/* calculate the errors for the current pixel, total error is (g - g2) */
|
|
total_error = (g - g2); /* 1/16 */
|
|
gif->dither.v_grn[x-1] += (total_error * 3 / 16);
|
|
gif->dither.v_grn[x] = (total_error * 5 / 16) + gif->dither.grn_next_error; /* from the previous column */
|
|
gif->dither.grn_next_error = (total_error / 16);
|
|
gif->dither.h_grn = (g - g2) - (total_error * 9 / 16);
|
|
|
|
/* BLUE */
|
|
/* b is the original value adjusted with the previous vertical and horizontal errors */
|
|
b = gif->gif_colors[*p].b + (gif->dither.h_blu + gif->dither.v_blu[x]);
|
|
|
|
/* adjust b to the closest available value */
|
|
b_level = (b + HTGIF_BLUE_LEVEL_CALC1) / HTGIF_BLUE_LEVEL_INCR;
|
|
if (b_level < 0)
|
|
{
|
|
b_level = 0;
|
|
}
|
|
else if (b_level >= HTGIF_BLUE_COLOR_LEVELS)
|
|
{
|
|
b_level = HTGIF_BLUE_LEVEL_CALC2;
|
|
}
|
|
b2 = b_level * HTGIF_BLUE_LEVEL_INCR;
|
|
|
|
/* calculate the errors for the current pixel, total error is (b - b2) */
|
|
total_error = (b - b2); /* 1/16 */
|
|
gif->dither.v_blu[x-1] += (total_error * 3 / 16);
|
|
gif->dither.v_blu[x] = (total_error * 5 / 16) + gif->dither.blu_next_error; /* from the previous column */
|
|
gif->dither.blu_next_error = (total_error / 16);
|
|
gif->dither.h_blu = (b - b2) - (total_error * 9 / 16);
|
|
|
|
/*
|
|
Having calculated new r, g, and b values (along with their errors),
|
|
now we calculate the new pixel value
|
|
*/
|
|
v = r_level*HTGIF_GREEN_BLUE_CALC1 + g_level*HTGIF_BLUE_COLOR_LEVELS + b_level;
|
|
#ifdef UNIX
|
|
*q = (unsigned char) dither_info.cmap[v];
|
|
#else
|
|
*q = v;
|
|
#endif
|
|
}
|
|
p++;
|
|
q++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NEW_DITHERER
|
|
static int xx_NewDither(unsigned char *pdata, unsigned char *dest,
|
|
struct _GIFinfo *gif, int yLast, int yThis)
|
|
{
|
|
int row;
|
|
unsigned char *from, *to;
|
|
|
|
for (row = yLast+1 ; row <= yThis ; row++)
|
|
{
|
|
from = pdata + gif->rowbytes * row;
|
|
to = dest + gif->rowbytes * row;
|
|
dither_row (gif, from, to, row, gif->width);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* NEW_DITHERER */
|
|
|
|
|
|
#endif /* WIN32 || UNIX */
|
|
|
|
/*
|
|
GIF_ReadImage: process a single image stream out of a GIF file
|
|
|
|
TODO: (John) check Mac logic which replaces ImageToGW
|
|
*/
|
|
static BOOL GIF_ReadImage(struct buf_in *src, struct _GIFinfo *gif)
|
|
{
|
|
unsigned char *dp;
|
|
long v, yRow;
|
|
BOOL bDone;
|
|
long start_ypos;
|
|
#if defined (UNIX) || defined (MAC)
|
|
unsigned char *mp;
|
|
int mpi;
|
|
#endif
|
|
#ifdef MAC
|
|
int mm;
|
|
#endif /* MAC */
|
|
|
|
#ifdef MAC
|
|
PixMapHandle thePix = GetGWorldPixMap(gif->gw);
|
|
static int bitMask[]
|
|
= {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
|
#endif
|
|
|
|
struct _LWZinfo *lwz;
|
|
|
|
/* the following is for convenience */
|
|
lwz = &gif->lwz;
|
|
|
|
/* remember how much was already decoded when we started */
|
|
start_ypos = gif->decoded_ypos;
|
|
|
|
if ((gif->decoded_pass == 0) && (start_ypos == 0))
|
|
start_ypos = -1; /* get right arg.s for xx_Dither */
|
|
|
|
/* assume that we will suspend (ie, just jump out) */
|
|
bDone = FALSE;
|
|
|
|
#ifdef MAC
|
|
LockPixels(thePix);
|
|
gif->image = (*thePix)->baseAddr;
|
|
#endif /* MAC */
|
|
|
|
if (gif->interlace)
|
|
{
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
unsigned char *dp2;
|
|
int y;
|
|
#endif /* FEATURE_PROGRESSIVE_IMAGE */
|
|
|
|
for (; gif->line < gif->height; gif->line++)
|
|
{
|
|
/** Added by SP for fix to some images **/
|
|
if (gif->ypos < gif->height)
|
|
{
|
|
#ifdef WIN32
|
|
yRow = gif->height - gif->ypos - 1; /* the DIB is stored upside down */
|
|
#else
|
|
yRow = gif->ypos; /* our DIB is right side up */
|
|
#endif /* WIN32 */
|
|
|
|
#ifndef MAC
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
{
|
|
/* need a working copy to dither each pass from */
|
|
dp = &gif->predither_image[gif->rowbytes * yRow + gif->xpos];
|
|
}
|
|
else
|
|
#endif /* !MAC */
|
|
{
|
|
dp = &gif->image[gif->rowbytes * yRow + gif->xpos];
|
|
#ifdef MAC
|
|
if (gif->mask)
|
|
{
|
|
mp = gif->mask->baseAddr + gif->mask->rowBytes * yRow + gif->xpos / 8;
|
|
mm = gif->xpos % 8;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef UNIX
|
|
if (gif->mask)
|
|
mp = gif->mask + ((gif->width+7)/8) * yRow + gif->xpos / 8;
|
|
#endif
|
|
|
|
for (; gif->xpos < gif->width; gif->xpos++)
|
|
{
|
|
if ((v = readLWZ(lwz, src)) < 0)
|
|
goto fini;
|
|
|
|
#ifndef MAC
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(!gif->bDitherColors))
|
|
{
|
|
*dp++ = (unsigned char) x_MapToClosestColor(gif, v, &(gif->gif_colors[v]));
|
|
}
|
|
else
|
|
{
|
|
*dp++ = (unsigned char) v;
|
|
}
|
|
#ifdef UNIX
|
|
if (gif->transparent >= 0)
|
|
{
|
|
mpi = (gif->xpos & 7);
|
|
if (v != gif->transparent)
|
|
*mp |= (1 << (mpi));
|
|
if (mpi>=7)
|
|
mp++;
|
|
}
|
|
#endif /* UNIX */
|
|
#else /* !MAC */
|
|
*dp++ = gif->IndexMap[(unsigned char) v];
|
|
if (gif->transparent >= 0)
|
|
{
|
|
if (v != gif->transparent)
|
|
*mp |= bitMask[mm];
|
|
mm++;
|
|
if (mm >= 8)
|
|
{
|
|
mp++;
|
|
mm = 0;
|
|
}
|
|
}
|
|
#endif /* MAC */
|
|
}
|
|
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
if (gPrefs.bProgressiveImageDisplay)
|
|
{
|
|
/* how much of image is now decoded? */
|
|
gif->decoded_pass = gif->pass; /* this pass */
|
|
gif->decoded_ypos = gif->ypos + gif->fill; /* thru filled lines */
|
|
|
|
if (gif->decoded_ypos >= gif->height)
|
|
gif->decoded_ypos = gif->height - 1;
|
|
|
|
/* Copy line to fill blank */
|
|
#ifndef MAC
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->bDitherColors))
|
|
{
|
|
dp = &gif->predither_image[gif->rowbytes * yRow];
|
|
}
|
|
else
|
|
#endif /* !MAC */
|
|
{
|
|
dp = &gif->image[gif->rowbytes * yRow];
|
|
}
|
|
|
|
dp2 = dp;
|
|
|
|
for ( y = gif->ypos+1 ; y <= gif->decoded_ypos ; y++)
|
|
{
|
|
#ifdef WIN32
|
|
dp2 -= gif->rowbytes; /* the DIB is stored upside down */
|
|
#else
|
|
dp2 += gif->rowbytes; /* our DIB is right side up */
|
|
#endif /* WIN32 */
|
|
memcpy (dp2, dp, gif->rowbytes);
|
|
}
|
|
}
|
|
else
|
|
#endif /* FEATURE_PROGRESSIVE_IMAGE */
|
|
{
|
|
gif->decoded_ypos = gif->ypos;
|
|
}
|
|
|
|
}
|
|
#ifndef UNIX_BUG
|
|
/*
|
|
TODO: confirm that this code can be dropped
|
|
ALT: if not, should be needed on Unix, too
|
|
*/
|
|
else
|
|
{
|
|
/* Throw line away */
|
|
for (; gif->xpos < gif->width; gif->xpos++)
|
|
{
|
|
if ((v = readLWZ(lwz, src)) < 0)
|
|
goto fini;
|
|
}
|
|
}
|
|
#endif /* !UNIX */
|
|
|
|
if ((gif->ypos += gif->step) >= gif->height)
|
|
{
|
|
#ifndef MAC
|
|
/* dither any new rows this pass */
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->decoded_ypos > start_ypos) &&
|
|
(gif->bDitherColors))
|
|
{
|
|
int dith_size;
|
|
|
|
(*dither_func)(gif->predither_image, gif->image, gif, start_ypos, gif->decoded_ypos);
|
|
|
|
dith_size = (gif->width + 2)*sizeof(int);
|
|
|
|
/* reset errors before dithering next pass */
|
|
memset(gif->dither.v_red_mem, 0, dith_size);
|
|
memset(gif->dither.v_grn_mem, 0, dith_size);
|
|
memset(gif->dither.v_blu_mem, 0, dith_size);
|
|
}
|
|
#endif /* !MAC */
|
|
|
|
/* initialize for next pass */
|
|
if (gif->pass++ > 0)
|
|
gif->step /= 2;
|
|
gif->ypos = gif->step / 2;
|
|
gif->fill = gif->ypos - 1;
|
|
gif->bFirstPass = FALSE;
|
|
|
|
start_ypos = -1;
|
|
}
|
|
|
|
/* re-init for next loop */
|
|
gif->xpos = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* not interlaced */
|
|
for (; gif->ypos < gif->height; gif->ypos++)
|
|
{
|
|
#ifdef WIN32
|
|
yRow = gif->height - gif->ypos - 1; /* the DIB is stored upside down */
|
|
#else
|
|
yRow = gif->ypos; /* our DIB is right side up */
|
|
#endif /* WIN32 */
|
|
|
|
dp = &gif->image[gif->rowbytes * yRow + gif->xpos];
|
|
#ifdef UNIX
|
|
if (gif->mask)
|
|
mp = gif->mask + ((gif->width+7)/8) * yRow + gif->xpos / 8;
|
|
#endif
|
|
#ifdef MAC
|
|
if (gif->mask)
|
|
{
|
|
mp = gif->mask->baseAddr + gif->mask->rowBytes * yRow + gif->xpos / 8;
|
|
mm = gif->xpos % 8;
|
|
}
|
|
#endif
|
|
for (; gif->xpos < gif->width; gif->xpos++)
|
|
{
|
|
if ((v = readLWZ(lwz, src)) < 0)
|
|
goto fini;
|
|
|
|
#ifndef MAC
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(!gif->bDitherColors))
|
|
{
|
|
*dp++ = (unsigned char) x_MapToClosestColor(gif, v, &(gif->gif_colors[v]));
|
|
}
|
|
else
|
|
{
|
|
*dp++ = (unsigned char) v;
|
|
}
|
|
#ifdef UNIX
|
|
if (gif->transparent >= 0)
|
|
{
|
|
mpi = (gif->xpos & 7);
|
|
if (v != gif->transparent)
|
|
*mp |= (1 << (mpi));
|
|
if (mpi>=7)
|
|
mp++;
|
|
}
|
|
#endif /* UNIX */
|
|
#else /* !MAC */
|
|
*dp++ = gif->IndexMap[(unsigned char) v];
|
|
if (gif->transparent >= 0)
|
|
{
|
|
if (v != gif->transparent)
|
|
*mp |= bitMask[mm];
|
|
mm++;
|
|
if (mm >= 8)
|
|
{
|
|
mp++;
|
|
mm = 0;
|
|
}
|
|
}
|
|
#endif /* MAC */
|
|
}
|
|
|
|
/* image is decoded through this line */
|
|
gif->decoded_ypos = gif->ypos;
|
|
|
|
/* re-init for next loop */
|
|
gif->xpos = 0;
|
|
}
|
|
}
|
|
|
|
/* made it all the way through the image */
|
|
bDone = TRUE;
|
|
|
|
fini:
|
|
|
|
#ifndef MAC
|
|
if ((gif->dither_type != DITHER_NONE) &&
|
|
(gif->decoded_ypos > start_ypos) &&
|
|
(gif->bDitherColors))
|
|
{
|
|
/* dither any new rows this pass */
|
|
if (gif->interlace)
|
|
(*dither_func)(gif->predither_image, gif->image, gif, start_ypos, gif->decoded_ypos);
|
|
else
|
|
(*dither_func)(gif->image, gif->image, gif, start_ypos, gif->decoded_ypos);
|
|
}
|
|
#endif /* MAC */
|
|
|
|
#ifdef MAC
|
|
UnlockPixels(gif->gw->portPixMap);
|
|
#endif /* MAC */
|
|
|
|
/* HACK: make sure that last line gets progressively painted */
|
|
if (bDone)
|
|
gif->decoded_ypos = gif->height;
|
|
|
|
return bDone;
|
|
}
|
|
|
|
static BOOL
|
|
GIF_ReadColorMap(struct _GIFinfo *gif,long cmapSize,unsigned char cmap[3*MAXCOLORMAPSIZE])
|
|
{
|
|
long v;
|
|
|
|
gif->cmapSize = cmapSize;
|
|
|
|
for (v = 0; v < MAXCOLORMAPSIZE; v++)
|
|
{
|
|
gif->gif_colors[v].r = gif->gif_colors[v].g = gif->gif_colors[v].b = 0;
|
|
}
|
|
for (v = 0; v < cmapSize; v++)
|
|
{
|
|
gif->gif_colors[v].r = cmap[v*3 + CM_RED] * 0x101;
|
|
gif->gif_colors[v].g = cmap[v*3 + CM_GREEN] * 0x101;
|
|
gif->gif_colors[v].b = cmap[v*3 + CM_BLUE] * 0x101;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************
|
|
|
|
platform-specific stuff
|
|
|
|
**************************************************************/
|
|
|
|
/*
|
|
GIF_StorePlatformColorMap: transform raw GIF bytes into platform-specific colormap
|
|
*/
|
|
#ifdef WIN32
|
|
static BOOL
|
|
GIF_StorePlatformColorMap(struct _GIFinfo *gif)
|
|
{
|
|
long v;
|
|
LOGPALETTE *lp;
|
|
|
|
for (v = 0; v < MAXCOLORMAPSIZE; v++)
|
|
{
|
|
gif->colrs[v].rgbRed = gif->gif_colors[v].r;
|
|
gif->colrs[v].rgbGreen = gif->gif_colors[v].g;
|
|
gif->colrs[v].rgbBlue = gif->gif_colors[v].b;
|
|
}
|
|
|
|
/* convert to a palette right here, to speed up progressive draw code */
|
|
|
|
/*
|
|
TODO: gif->colrs could be (HPALETTE) instead of (RGBQUAD *)
|
|
ie, gif->colrs and gif->hPalette are redundant
|
|
PQ: need to fix ReadGIF to match (so image viewer will still work)
|
|
*/
|
|
|
|
lp = (LOGPALETTE *) GTR_CALLOC(1, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256);
|
|
if (lp)
|
|
{
|
|
int i;
|
|
|
|
lp->palVersion = 0x300;
|
|
lp->palNumEntries = 256;
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
lp->palPalEntry[i].peRed = gif->colrs[i].rgbRed;
|
|
lp->palPalEntry[i].peGreen = gif->colrs[i].rgbGreen;
|
|
lp->palPalEntry[i].peBlue = gif->colrs[i].rgbBlue;
|
|
lp->palPalEntry[i].peFlags = 0;
|
|
}
|
|
gif->hPalette = CreatePalette(lp);
|
|
GTR_FREE(lp);
|
|
}
|
|
else
|
|
{
|
|
gif->hPalette = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* WIN32 */
|
|
#ifdef UNIX
|
|
static BOOL
|
|
GIF_StorePlatformColorMap(struct _GIFinfo *gif)
|
|
{
|
|
long v;
|
|
|
|
#ifdef DEBUG
|
|
printf ("Store platform color map.\n");
|
|
#endif
|
|
for (v = 0; v < MAXCOLORMAPSIZE; v++)
|
|
{
|
|
gif->colrs[v].red = gif->gif_colors[v].r * 257;
|
|
gif->colrs[v].green = gif->gif_colors[v].g * 257;
|
|
gif->colrs[v].blue = gif->gif_colors[v].b * 257;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* UNIX */
|
|
#ifdef MAC
|
|
static BOOL
|
|
GIF_StoreMacintoshColorMap(CTabHandle *colrs,long cmapSize,unsigned char buffer[3*MAXCOLORMAPSIZE],
|
|
struct _GIFinfo *gif)
|
|
{
|
|
int nBlackIndex, nWhiteIndex;
|
|
int nBlackTotal, nWhiteTotal;
|
|
RGBColor rgbSwap;
|
|
int cm1;
|
|
int n;
|
|
long v;
|
|
unsigned char cmap[3][MAXCOLORMAPSIZE];
|
|
|
|
/* old ReadColorMap logic: parse buffer into array */
|
|
for (v = 0; v < cmapSize; ++v)
|
|
{
|
|
cmap[CM_RED][v] = buffer[v*3 + CM_RED];
|
|
cmap[CM_GREEN][v] = buffer[v*3 + CM_GREEN];
|
|
cmap[CM_BLUE][v] = buffer[v*3 + CM_BLUE];
|
|
}
|
|
|
|
/* platform-specific logic */
|
|
*colrs = MacGlobals.sysctab;
|
|
HandToHand((Handle *)colrs);
|
|
if (!*colrs)
|
|
{
|
|
return FALSE;
|
|
}
|
|
(***colrs).ctSeed = GetCTSeed();
|
|
(***colrs).ctSize = cmapSize - 1;
|
|
|
|
if (gif->transparent != -1)
|
|
{
|
|
cmap[0][gif->transparent] = 0xff;
|
|
cmap[1][gif->transparent] = 0xff;
|
|
cmap[2][gif->transparent] = 0xff;
|
|
}
|
|
|
|
/* We need to find the colors closest to black and white so that we can
|
|
make sure that they appear in the correct places in the color table.
|
|
Otherwise we'll print out incorrectly on LaserWriters (see develop
|
|
issue 13, page 75). */
|
|
nBlackTotal = 0x0fffffff;
|
|
nWhiteTotal = 0;
|
|
nBlackIndex = 254;
|
|
nWhiteIndex = 255;
|
|
cm1 = cmapSize - 1;
|
|
|
|
for (n = 0; n < cmapSize; n++)
|
|
{
|
|
int nColorTotal;
|
|
|
|
/* Remember that GIF only uses 8 bits-per-gun, while Mac uses 16 bpg */
|
|
(***colrs).ctTable[n].rgb.red = (cmap[0][n] << 8) + cmap[0][n];
|
|
(***colrs).ctTable[n].rgb.green = (cmap[1][n] << 8) + cmap[1][n];
|
|
(***colrs).ctTable[n].rgb.blue = (cmap[2][n] << 8) + cmap[2][n];
|
|
|
|
nColorTotal = cmap[0][n] + cmap[1][n] + cmap[2][n];
|
|
if (nColorTotal < nBlackTotal)
|
|
{
|
|
nBlackTotal = nColorTotal;
|
|
nBlackIndex = n;
|
|
}
|
|
if (nColorTotal > nWhiteTotal)
|
|
{
|
|
nWhiteTotal = nColorTotal;
|
|
nWhiteIndex = n;
|
|
}
|
|
|
|
gif->IndexMap[n] = n;
|
|
}
|
|
|
|
/* Switch the color table around so that white is at 0 black at the last
|
|
position */
|
|
if (nWhiteIndex != 0)
|
|
{
|
|
if (nBlackIndex == 0)
|
|
{
|
|
if (nWhiteIndex == cm1)
|
|
{
|
|
/* We just need to swap black and white */
|
|
rgbSwap = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nBlackIndex] = cm1;
|
|
gif->IndexMap[nWhiteIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
/* We need to do a three-way swap. White goes where black
|
|
was, black goes to the end, and whatever color was at
|
|
the end goes to where white was. We accomplish this
|
|
by first swapping black and white, then swapping black
|
|
(which is now at nWhiteIndex) with the third color. */
|
|
rgbSwap = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = rgbSwap;
|
|
|
|
rgbSwap = (***colrs).ctTable[cm1].rgb;
|
|
(***colrs).ctTable[cm1].rgb = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nBlackIndex] = cm1;
|
|
gif->IndexMap[nWhiteIndex] = 0;
|
|
gif->IndexMap[cm1] = nWhiteIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (nWhiteIndex == cm1)
|
|
{
|
|
/* We need to do a three-way swap. Black goes where white
|
|
was, white moves to the beginning, and whatever color
|
|
was at 0 goes to where black started. We first swap
|
|
black and white, then whatever is at 0 with white (which
|
|
is now at nBlackIndex) */
|
|
rgbSwap = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = rgbSwap;
|
|
|
|
rgbSwap = (***colrs).ctTable[0].rgb;
|
|
(***colrs).ctTable[0].rgb = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nBlackIndex] = cm1;
|
|
gif->IndexMap[nWhiteIndex] = 0;
|
|
gif->IndexMap[0] = nBlackIndex;
|
|
}
|
|
else if (nBlackIndex != cm1)
|
|
{
|
|
/* We need to swap both white and black with other colors */
|
|
rgbSwap = (***colrs).ctTable[0].rgb;
|
|
(***colrs).ctTable[0].rgb = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = rgbSwap;
|
|
|
|
rgbSwap = (***colrs).ctTable[cm1].rgb;
|
|
(***colrs).ctTable[cm1].rgb = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nBlackIndex] = cm1;
|
|
gif->IndexMap[nWhiteIndex] = 0;
|
|
gif->IndexMap[cm1] = nBlackIndex;
|
|
gif->IndexMap[0] = nWhiteIndex;
|
|
}
|
|
else
|
|
{
|
|
/* We just need to swap white with another color */
|
|
rgbSwap = (***colrs).ctTable[0].rgb;
|
|
(***colrs).ctTable[0].rgb = (***colrs).ctTable[nWhiteIndex].rgb;
|
|
(***colrs).ctTable[nWhiteIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nWhiteIndex] = 0;
|
|
gif->IndexMap[0] = nWhiteIndex;
|
|
}
|
|
}
|
|
}
|
|
else if (nBlackIndex != cm1)
|
|
{
|
|
/* We just need to swap black with another color */
|
|
rgbSwap = (***colrs).ctTable[cm1].rgb;
|
|
(***colrs).ctTable[cm1].rgb = (***colrs).ctTable[nBlackIndex].rgb;
|
|
(***colrs).ctTable[nBlackIndex].rgb = rgbSwap;
|
|
|
|
gif->IndexMap[nBlackIndex] = cm1;
|
|
gif->IndexMap[cm1] = nBlackIndex;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* MAC */
|
|
|
|
|
|
/*
|
|
GIF_AllocImage: calculate row width (in bytes) & allocate image
|
|
*/
|
|
#ifdef WIN32
|
|
static BOOL GIF_AllocImage(unsigned char **image, long w, long h, long *rowbytes, long transparent)
|
|
{
|
|
int size;
|
|
|
|
*rowbytes = w; /* each pixel is one color entry */
|
|
|
|
if (*rowbytes%4)
|
|
{
|
|
*rowbytes = *rowbytes + 4 - (*rowbytes%4);
|
|
}
|
|
|
|
size = *rowbytes * h * sizeof(char);
|
|
*image = (unsigned char *) GTR_MALLOC(size);
|
|
|
|
memset(*image, (transparent == -1) ? 0 : (char) transparent, size);
|
|
|
|
return (*image != NULL);
|
|
}
|
|
#endif /* WIN32 */
|
|
|
|
#ifdef UNIX
|
|
static BOOL GIF_AllocImage(unsigned char **image, long w, long h, long *rowbytes, long transparent, unsigned char **mask)
|
|
{
|
|
int size;
|
|
|
|
*rowbytes = w; /* each pixel is one color entry */
|
|
|
|
*image = (unsigned char *)
|
|
GTR_CALLOC (w * h * sizeof(char) + EXTRAFUDGE, 1);
|
|
|
|
if (mask != NULL)
|
|
if (transparent != -1)
|
|
{
|
|
/* we later depend on all-bits zero */
|
|
/* Actually for speedup, we should init this to all 1s
|
|
** and then just unset the transparent bits
|
|
*/
|
|
*mask = GTR_CALLOC ((w+7)/8, h);
|
|
}
|
|
else
|
|
*mask = 0;
|
|
|
|
return (*image != NULL);
|
|
}
|
|
#endif /* UNIX */
|
|
|
|
#define MASK_SLOP 4
|
|
|
|
#ifdef MAC
|
|
static BOOL GIF_AllocImage(long w, long h, CTabHandle colrs, long transparent,
|
|
GWorldPtr *gw, long *rowbytes, BitMap **mask)
|
|
{
|
|
Rect r;
|
|
PixMapHandle thePix;
|
|
|
|
SetRect(&r, 0, 0, w, h);
|
|
*gw = GTR_ALLOCGWORLD(8, &r, colrs);
|
|
|
|
if (!*gw)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
thePix = GetGWorldPixMap(*gw);
|
|
*rowbytes = (*thePix)->rowBytes & 0x7fff;
|
|
}
|
|
|
|
if (transparent != -1 && (*mask = GTR_CALLOC(sizeof (BitMap), 1)))
|
|
{
|
|
(*mask)->bounds = r;
|
|
(*mask)->rowBytes = w/8; (*mask)->rowBytes += (*mask)->rowBytes%4;
|
|
/* use calloc because we later depend on all-bits zero */
|
|
(*mask)->baseAddr = GTR_CALLOC((*mask)->rowBytes * h + MASK_SLOP, 1);
|
|
if ((*mask)->baseAddr == NULL)
|
|
{
|
|
GTR_FREE(*mask);
|
|
*mask = NULL;
|
|
}
|
|
}
|
|
else
|
|
{ *mask = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* !MAC */
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
HTGIF stream stuff
|
|
|
|
***************************************************************************/
|
|
|
|
static BOOL HTGIF_put_character(HTStream * me, char c)
|
|
{
|
|
return HTGIF_write(me, &c, 1);
|
|
}
|
|
|
|
static BOOL HTGIF_put_string(HTStream * me, CONST char *s)
|
|
{
|
|
/* This never gets called */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void GIF_DoProgressiveStuff (HTStream * me)
|
|
{
|
|
/* check if 1st time AND has header AND has colormap
|
|
** AND had allocated the image buffer
|
|
*/
|
|
/* Note this depends on all states being increasing numerically */
|
|
if (!me->gif->bGotHeader && me->state > STATE_IMAGE_CREATE)
|
|
{
|
|
me->gif->bGotHeader = 1;
|
|
|
|
me->gif->pIInfo = GIF_DoSetImage (me); /* set up image header stuff */
|
|
}
|
|
|
|
|
|
if (me->gif->pIInfo &&
|
|
(me->gif->decoded_ypos-1) >= 0) /* Got some data, lets get to it */
|
|
{
|
|
HTList *cur;
|
|
wImageEleP p;
|
|
struct Mwin *mw;
|
|
BOOL bDrawMe;
|
|
|
|
#ifdef UNIX
|
|
struct _element *pel;
|
|
BOOL clear_below = 1;
|
|
#endif
|
|
|
|
me->gif->pIInfo->nPass = me->gif->decoded_pass;
|
|
me->gif->pIInfo->nLastRow = me->gif->decoded_ypos;
|
|
me->gif->pIInfo->bFirstPass = me->gif->bFirstPass;
|
|
|
|
/* And finally request that it be updated on the display */
|
|
HT_CreateDeviceImageMap (me->tw, me->gif->pIInfo);
|
|
|
|
/*
|
|
** Walk through list of elements which reference this
|
|
** image and update them.
|
|
*/
|
|
for (cur = me->gif->pIInfo->llElements ;
|
|
(p = (wImageEleP) HTList_nextObject(cur)) && p->w3doc ; )
|
|
{
|
|
|
|
for (mw = Mlist; mw; mw = mw->next)
|
|
{
|
|
if (p->w3doc == mw->w3doc)
|
|
{
|
|
RECT rUpdate;
|
|
#ifdef UNIX
|
|
clear_below = 1;
|
|
|
|
#endif
|
|
bDrawMe = TRUE;
|
|
|
|
if ( gPrefs.ReformatHandling >= 2 &&
|
|
W3Doc_CheckForImageLoadElement (p->w3doc, p->element))
|
|
{
|
|
/* We're in "high-flicker" mode - reformat the document */
|
|
#ifdef DEBUG
|
|
printf("Reformatting for image %s.\n",
|
|
p->w3doc->aElements[p->element].portion.img.myImage->src);
|
|
#endif
|
|
TW_Reformat (mw);
|
|
#ifdef UNIX
|
|
clear_below = 0;
|
|
#endif
|
|
}
|
|
|
|
#ifdef UNIX
|
|
/* needed below for clear */
|
|
pel = &p->w3doc->aElements[p->element];
|
|
rUpdate = pel->r;
|
|
#endif
|
|
if ( gPrefs.ReformatHandling < 2 )
|
|
{
|
|
#ifndef UNIX
|
|
struct _element *pel;
|
|
#endif
|
|
|
|
/* This is "no-flicker" mode - don't update the display unless the placeholder
|
|
is already the right size (because we had image hints) */
|
|
pel = &p->w3doc->aElements[p->element];
|
|
rUpdate = pel->r;
|
|
|
|
if (pel->iBorder > 0)
|
|
{
|
|
GTR_InsetRect(&rUpdate, pel->iBorder, pel->iBorder);
|
|
}
|
|
|
|
if (!pel->portion.img.height || !pel->portion.img.width)
|
|
{
|
|
bDrawMe = FALSE;
|
|
}
|
|
}
|
|
|
|
/* TODO don't call if not resized or off screen */
|
|
if (bDrawMe)
|
|
{
|
|
|
|
#ifdef UNIX
|
|
#if 1
|
|
/* hack. this is to force area under transparent
|
|
** gif to redraw. ideally this would only happen
|
|
** once.
|
|
*/
|
|
if (me->first_time && me->gif->transparent >= 0)
|
|
{
|
|
if (clear_below)
|
|
{
|
|
/* TW_Draw(mw, &rUpdate); */
|
|
/*DEBUG printf ( "Erasing %d,%d - %d,%d\n", rUpdate.left, rUpdate.bottom, rUpdate.right, rUpdate.top); */
|
|
OffsetRect(&rUpdate, -mw->offl, -mw->offt);
|
|
x_EraseBackground (mw, &rUpdate, XtWindow(mw->win));
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
GTR_DrawProgessiveImage (mw, p->element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
me->first_time = 0;
|
|
me->gif->pIInfo->nPreviousLastRow = me->gif->pIInfo->nLastRow;
|
|
me->gif->pIInfo->nPreviousPass = me->gif->pIInfo->nPass;
|
|
}
|
|
}
|
|
|
|
static BOOL HTGIF_write(HTStream * me, CONST char *s, int l)
|
|
{
|
|
me->src->len = l;
|
|
me->src->offset = 0;
|
|
me->src->pData = (unsigned char *) s;
|
|
|
|
/* let state machine consume input */
|
|
me->state = DecodeGIF (me->src, me->gif);
|
|
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
if (gPrefs.bProgressiveImageDisplay )
|
|
GIF_DoProgressiveStuff (me);
|
|
#endif
|
|
|
|
/* else just return state. */
|
|
|
|
/*
|
|
TODO: (progressive display)
|
|
depending on state, may want to call Image_SetImageData to add
|
|
updated image data to image cache.
|
|
|
|
then call ??? to draw new delta, if any
|
|
|
|
ALT: just make calls directly in state machine
|
|
*/
|
|
|
|
return (me->state != STATE_ABORT);
|
|
}
|
|
|
|
|
|
static void HTGIF_free(HTStream * me)
|
|
{
|
|
me->src->len = 0;
|
|
me->src->offset = 0;
|
|
me->src->pData = NULL;
|
|
me->src->bForceFlush = TRUE;
|
|
|
|
/* force state machine to consume rest of its buffers */
|
|
if (me->state != STATE_DONE)
|
|
me->state = DecodeGIF(me->src, me->gif);
|
|
|
|
/* see what happened */
|
|
if ( (me->state == STATE_IMAGE_DATA) ||
|
|
(me->state == STATE_IMAGE_DONE) ||
|
|
(me->state == STATE_DONE) )
|
|
{
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
if (gPrefs.bProgressiveImageDisplay)
|
|
{
|
|
/* have to do it one more time in some cases */
|
|
GIF_DoProgressiveStuff (me);
|
|
#ifdef UNIX
|
|
/* now call it one last time so it can fix up pixmaps */
|
|
if ( me->gif->pIInfo)
|
|
{
|
|
me->gif->pIInfo->bComplete = 1;
|
|
HT_CreateDeviceImageMap (me->tw, me->gif->pIInfo);
|
|
}
|
|
else
|
|
XX_DMsg(DBG_IMAGE, ("GIF ImageSetInfo failed\n"));
|
|
#endif
|
|
}
|
|
else /* display at end (i.e. the old way) */
|
|
{
|
|
#endif
|
|
/* hack. don't let image get destroyed on
|
|
** inlined image viewers.
|
|
*/
|
|
#ifdef DEBUG
|
|
if (me->gif->pIInfo)
|
|
printf("Warning image info not free'd.\n");
|
|
#endif
|
|
me->gif->pIInfo = GIF_DoSetImage (me);
|
|
#ifdef FEATURE_INLINED_IMAGES
|
|
if (me->tw->w3doc->bIsImage)
|
|
HT_CreateDeviceImageMap (me->tw, me->gif->pIInfo);
|
|
#endif
|
|
#ifdef UNIX
|
|
me->gif->pIInfo->bComplete = 1;
|
|
/* HT_CreateDeviceImageMap (me->tw, me->gif->pIInfo); */
|
|
#endif
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{ /* image incomplete */
|
|
#ifdef WIN32
|
|
Image_SetImageData (me->request, NULL, 0, IMG_ERROR, NULL, -1, 0);
|
|
#endif
|
|
#ifdef MAC
|
|
Image_SetImageData (me->request, NULL, NULL, 0, IMG_ERROR);
|
|
#endif
|
|
#ifdef UNIX
|
|
Image_SetImageData (me->request, NULL, NULL, 0, IMG_ERROR, NULL, -1, 0, 0);
|
|
if (me->gif->colrs)
|
|
GTR_FREE (me->gif->colrs);
|
|
#endif
|
|
}
|
|
|
|
cleanup:
|
|
|
|
/* clean up */
|
|
if (me->src)
|
|
GTR_FREE(me->src);
|
|
if (me->gif)
|
|
{
|
|
#ifdef DEBUG
|
|
if (me->gif->pIInfo)
|
|
{
|
|
printf("Need to free image info struct.\n");
|
|
}
|
|
#endif
|
|
GTR_FREE(me->gif);
|
|
}
|
|
|
|
GTR_FREE(me);
|
|
}
|
|
|
|
static void HTGIF_abort(HTStream * me, HTError e)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Aborting transfer of %s, e = %d", me->request->destination->szActualURL, e));
|
|
|
|
me->src->len = 0;
|
|
me->src->offset = 0;
|
|
me->src->pData = NULL;
|
|
me->src->bForceFlush = TRUE;
|
|
|
|
me->gif->state = STATE_ABORT;
|
|
|
|
/* force state machine to clean up after itself */
|
|
if (me->state != STATE_ABORT)
|
|
me->state = DecodeGIF(me->src, me->gif);
|
|
|
|
|
|
#ifdef FEATURE_PROGRESSIVE_IMAGE
|
|
if ((gPrefs.bProgressiveImageDisplay) &&
|
|
(e == HTERROR_CANCELLED) &&
|
|
((me->gif->decoded_pass > 0) || (me->gif->decoded_ypos > 0)))
|
|
{
|
|
/* HACK: user cancelled during LOADING, so set PARTIAL, rather than NOTLOADED */
|
|
me->gif->pIInfo->flags |= IMG_PARTIAL;
|
|
}
|
|
else
|
|
#endif /* FEATURE_PROGRESSIVE_IMAGE */
|
|
{
|
|
#ifdef WIN32
|
|
Image_SetImageData(me->request, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED, NULL, -1, 0);
|
|
#endif
|
|
#ifdef MAC
|
|
Image_SetImageData(me->request, NULL, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED);
|
|
#endif
|
|
#ifdef UNIX
|
|
Image_SetImageData(me->request, NULL, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED, NULL, -1, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
if (me->src)
|
|
GTR_FREE(me->src);
|
|
if (me->gif)
|
|
{
|
|
#ifdef DEBUG
|
|
if (me->gif->pIInfo)
|
|
{
|
|
printf("Need to free image info struct.\n");
|
|
}
|
|
#endif
|
|
GTR_FREE(me->gif);
|
|
}
|
|
|
|
GTR_FREE(me);
|
|
}
|
|
|
|
/*
|
|
** This is just to break out the calling of Image_SetImageData in the
|
|
** case of having a complete header cuz this code is so clutzy and
|
|
** it is currently called more than once.
|
|
*/
|
|
static struct ImageInfo * GIF_DoSetImage (HTStream *me)
|
|
{
|
|
struct ImageInfo *img;
|
|
int flags = 0;
|
|
|
|
me->gif->bGotHeader = 1;
|
|
|
|
#ifdef DEBUG
|
|
if (me->gif->pIInfo)
|
|
{
|
|
printf("Need to free image info struct in GIF_DoSetImage.\n");
|
|
}
|
|
#endif
|
|
#ifdef UNIX
|
|
#ifdef FEATURE_INLINED_IMAGES
|
|
if (me->tw->w3doc->bIsImage)
|
|
flags |= IMG_ISIMAGE;
|
|
#endif
|
|
img = Image_SetImageData(me->request, me->gif->image, me->gif->mask, me->gif->width,
|
|
me->gif->height, me->gif->colrs, me->gif->transparent, 8, flags);
|
|
|
|
#endif
|
|
#ifdef MAC
|
|
|
|
img = Image_SetImageData(me->request, me->gif->gw, me->gif->mask,
|
|
(int)me->gif->width, (int)me->gif->height);
|
|
|
|
#endif
|
|
#ifdef WIN32
|
|
|
|
img = Image_SetImageData(me->request, me->gif->image, (int)me->gif->width,
|
|
(int)me->gif->height, me->gif->hPalette, me->gif->transparent, 0);
|
|
#endif /* WIN32 */
|
|
|
|
|
|
return img;
|
|
}
|
|
|
|
/* Image creation
|
|
*/
|
|
|
|
PUBLIC HTStream *Image_GIF(struct Mwin *tw, HTRequest * request, void *param, HTFormat input_format, HTFormat output_format, HTStream * output_stream)
|
|
{
|
|
BOOL bOK;
|
|
HTStream *me = (HTStream *) GTR_CALLOC(sizeof(*me), 1);
|
|
|
|
XX_DMsg(DBG_IMAGE, ("Creating new GIF stream for %s\n", request->destination->szActualURL));
|
|
|
|
/* for convenience, assume failure */
|
|
bOK = FALSE;
|
|
|
|
if (!me)
|
|
goto fini;
|
|
|
|
me->isa = &HTGIFClass;
|
|
me->request = request;
|
|
me->first_time = 1; /* only used by unix */
|
|
|
|
/* alloc, initialize platform-specific state */
|
|
#ifdef MAC
|
|
/* TODO: (John) alloc colors here? */
|
|
#endif
|
|
#ifdef UNIX
|
|
#ifdef DEBUG
|
|
if (me->colors)
|
|
printf("Need to free old colors in Image_GIF.\n");
|
|
#endif
|
|
|
|
if (!(me->colors = GTR_MALLOC (sizeof (XColor) * 256)))
|
|
goto fini;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
if (me->gif)
|
|
printf("Need to free gif data in Image_GIF.\n");
|
|
if (me->src)
|
|
printf("Need to free src data in Image_GIF.\n");
|
|
#endif
|
|
/* alloc, initialize decoder state variables */
|
|
me->src = (struct buf_in *)GTR_CALLOC(1, sizeof(struct buf_in));
|
|
me->gif = (struct _GIFinfo *)GTR_CALLOC(1, sizeof(struct _GIFinfo));
|
|
|
|
if (!me->src || !me->gif)
|
|
goto fini;
|
|
|
|
me->src->len = 0;
|
|
me->src->offset = 0;
|
|
me->src->pData = NULL;
|
|
me->src->bForceFlush = FALSE;
|
|
|
|
me->gif->colrs = me->colors;
|
|
me->gif->state = STATE_INIT;
|
|
|
|
/* start state machine */
|
|
me->state = DecodeGIF(me->src, me->gif);
|
|
|
|
/* see what happened */
|
|
if (me->state == STATE_ABORT)
|
|
goto fini;
|
|
|
|
if (me->state != STATE_FILE_TYPE)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GIF: unexpected state %s after initialization\n", me->state));
|
|
goto fini;
|
|
}
|
|
|
|
/* made it through, so everything worked */
|
|
bOK = TRUE;
|
|
|
|
fini:
|
|
if (!bOK)
|
|
{
|
|
/* couldn't initialize: clean up */
|
|
#ifdef WIN32
|
|
Image_SetImageData(request, NULL, 0, IMG_NOTLOADED, NULL, -1, 0);
|
|
#endif
|
|
#ifdef MAC
|
|
Image_SetImageData(request, NULL, NULL, 0, IMG_NOTLOADED);
|
|
#endif
|
|
#ifdef UNIX
|
|
Image_SetImageData(request, NULL, NULL, 0, IMG_NOTLOADED, NULL, -1, 0, 0);
|
|
#endif
|
|
|
|
if (me)
|
|
{
|
|
if (me->src)
|
|
GTR_FREE(me->src);
|
|
if (me->gif)
|
|
{
|
|
#ifdef DEBUG
|
|
if (me->gif->pIInfo)
|
|
printf("Need to free gif pIInfo data in Image_GIF.\n");
|
|
#endif
|
|
GTR_FREE(me->gif);
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
me->tw = tw;
|
|
return me;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
#ifdef NEW_DITHERER
|
|
/*
|
|
|
|
This code is originally from the public domain GIS GRASS
|
|
written by Michael Shapiro, US Army CERL
|
|
|
|
These functions are for the new color logic.
|
|
They convert the printer color levels and multiplier factors
|
|
into lookup tables for 0-255 rgb GRASS colors.
|
|
|
|
They also calculate how much color should be carried to the next pixel.
|
|
For example, if a color value of 135 translates to a color level of 3
|
|
which represents a color value of 127, then the printed color is 8 too
|
|
low, so 8 must be carried over to the neighboring pixels.
|
|
*/
|
|
|
|
#define COLORMODE_APPROX 0
|
|
#define COLORMODE_DIFFUSION 1
|
|
#define COLORMODE_DITHER 2
|
|
|
|
static void mix ( short *low, short *hi, short *extra, int *level_to_255,
|
|
int nlevels, int steps );
|
|
static dither ( unsigned char *color, int row, int col, int ncols,
|
|
short *low, short *hi, short *extra );
|
|
static build ( int nlevels, int mult, int *level, int *value,
|
|
int *level_to_255 );
|
|
|
|
|
|
static int colormode = COLORMODE_DITHER;
|
|
|
|
static int red_nlevels, red_mult,
|
|
grn_nlevels, grn_mult,
|
|
blu_nlevels, blu_mult;
|
|
static int *red_level_to_255, red_level[256], red_value[256],
|
|
*grn_level_to_255, grn_level[256], grn_value[256],
|
|
*blu_level_to_255, blu_level[256], blu_value[256];
|
|
|
|
/* for dithering! */
|
|
static short red_low[256], red_hi[256], red_extra[256],
|
|
grn_low[256], grn_hi[256], grn_extra[256],
|
|
blu_low[256], blu_hi[256], blu_extra[256];
|
|
|
|
#define DITHER_CUBE_SIZE 8
|
|
#define DITHER_ROWS DITHER_CUBE_SIZE
|
|
#define DITHER_COLS DITHER_CUBE_SIZE
|
|
|
|
#if (DITHER_CUBE_SIZE == 8)
|
|
static short dither_matrix[DITHER_ROWS][DITHER_COLS] =
|
|
{
|
|
{ 0, 24, 36, 60, 2, 26, 38, 62},
|
|
{44, 52, 8, 16, 46, 54, 10, 18},
|
|
{28, 4, 56, 32, 30, 6, 58, 34},
|
|
{48, 40, 20, 12, 50, 42, 22, 14},
|
|
{ 3, 27, 39, 63, 1, 25, 37, 61},
|
|
{47, 55, 11, 19, 45, 53, 9, 17},
|
|
{31, 7, 59, 35, 29, 5, 57, 33},
|
|
{51, 43, 23, 15, 49, 41, 21, 13}
|
|
|
|
};
|
|
#endif
|
|
|
|
static short dither_matrix16[16][16] = {
|
|
/* Bayer's order-4 dither array. Generated by the code given in
|
|
* Stephen Hawley's article "Ordered Dithering" in Graphics Gems I.
|
|
* The values in this array must range from 0 to ODITHER_CELLS-1.
|
|
*/
|
|
{ 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
|
|
{ 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
|
|
{ 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
|
|
{ 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
|
|
{ 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
|
|
{ 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
|
|
{ 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
|
|
{ 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
|
|
{ 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
|
|
{ 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
|
|
{ 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
|
|
{ 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
|
|
{ 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
|
|
{ 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
|
|
{ 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
|
|
{ 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
|
|
};
|
|
|
|
#define DITHER_SIZE DITHER_ROWS * DITHER_COLS
|
|
|
|
int
|
|
set_colormode (int cmode)
|
|
{
|
|
colormode = cmode;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
build_color_tables (void)
|
|
{
|
|
|
|
/* get the info from the printer driver */
|
|
red_nlevels = dither_info.red_levels;
|
|
grn_nlevels = dither_info.green_levels;
|
|
blu_nlevels = dither_info.blue_levels;
|
|
|
|
red_mult = blu_nlevels * grn_nlevels;
|
|
grn_mult = blu_nlevels;
|
|
blu_mult = 1;
|
|
|
|
|
|
/* these next tables will be used to
|
|
convert a printer level to a number in the range 0-255
|
|
*/
|
|
red_level_to_255 = (int *) GTR_CALLOC (red_nlevels, sizeof (int));
|
|
grn_level_to_255 = (int *) GTR_CALLOC (grn_nlevels, sizeof (int));
|
|
blu_level_to_255 = (int *) GTR_CALLOC (blu_nlevels, sizeof (int));
|
|
|
|
build (red_nlevels, red_mult, red_level, red_value, red_level_to_255);
|
|
build (grn_nlevels, grn_mult, grn_level, grn_value, grn_level_to_255);
|
|
build (blu_nlevels, blu_mult, blu_level, blu_value, blu_level_to_255);
|
|
}
|
|
|
|
void
|
|
build_dither_tables(void)
|
|
{
|
|
mix(red_low, red_hi, red_extra, red_level_to_255, red_nlevels, DITHER_SIZE);
|
|
mix(grn_low, grn_hi, grn_extra, grn_level_to_255, grn_nlevels, DITHER_SIZE);
|
|
mix(blu_low, blu_hi, blu_extra, blu_level_to_255, blu_nlevels, DITHER_SIZE);
|
|
}
|
|
|
|
void
|
|
dump_color_tables(void)
|
|
{
|
|
int i;
|
|
printf ("levels: red=%d, grn=%d, blu=%d\n",
|
|
red_nlevels, grn_nlevels, blu_nlevels);
|
|
printf ("mults: red=%d, grn=%d, blu=%d\n",
|
|
red_mult, grn_mult, blu_mult);
|
|
for (i = 0; i < red_nlevels; i++)
|
|
printf ("level %d -> %3d\n", i, red_level_to_255[i]);
|
|
for (i = 0; i < 256; i++)
|
|
printf ("%3d -> level %d\n", i, red_level[i]);
|
|
}
|
|
|
|
static
|
|
build (int nlevels, int mult, int *level, int *value, int *level_to_255)
|
|
{
|
|
int i;
|
|
int first, last;
|
|
|
|
first = 0;
|
|
for (i = 0; i < nlevels; i++)
|
|
{
|
|
last = (255.0/nlevels) * (i+1);
|
|
while (first <= last)
|
|
{
|
|
level[first] = i;
|
|
value[first] = i*mult;
|
|
first++;
|
|
}
|
|
level_to_255[i] = i * 255.0/(nlevels-1);
|
|
}
|
|
while (first <= 255)
|
|
{
|
|
level[first] = (nlevels-1);
|
|
value[first] = (nlevels-1) * mult;
|
|
first++;
|
|
}
|
|
}
|
|
|
|
/* this routine converts a GRASS rgb color to a printer color number
|
|
the rgb are from 0-255.
|
|
*/
|
|
int
|
|
printer_color_number (int red, int grn, int blu)
|
|
{
|
|
if (red < 0) red = 0;
|
|
else if (red > 255) red = 255;
|
|
|
|
if (grn < 0) grn = 0;
|
|
else if (grn > 255) grn = 255;
|
|
|
|
if (blu < 0) blu = 0;
|
|
else if (blu > 255) blu = 255;
|
|
|
|
return red_value[red] + grn_value[grn] + blu_value[blu];
|
|
}
|
|
|
|
int
|
|
red_carryover (int color)
|
|
{
|
|
if (color < 0) color = 0;
|
|
else if (color > 255) color = 255;
|
|
|
|
return color - red_level_to_255 [ red_level[color]];
|
|
}
|
|
|
|
int
|
|
grn_carryover (int color)
|
|
{
|
|
if (color < 0) color = 0;
|
|
else if (color > 255) color = 255;
|
|
|
|
return color - grn_level_to_255 [ grn_level[color]];
|
|
}
|
|
|
|
int
|
|
blu_carryover (int color)
|
|
{
|
|
if (color < 0) color = 0;
|
|
else if (color > 255) color = 255;
|
|
|
|
return color - blu_level_to_255 [ blu_level[color]];
|
|
}
|
|
|
|
/* this next routine supports matrix dithering
|
|
* given an input color intensity, it computes the exact printer
|
|
* color intensities above and below, plus a ratio to mix the
|
|
* two to get the input colors.
|
|
*
|
|
*/
|
|
static
|
|
void
|
|
mix (short *low, short *hi, short *extra, int *level_to_255,
|
|
int nlevels, int steps)
|
|
{
|
|
int color;
|
|
int i;
|
|
|
|
for (color = 0; color < 256; color++, low++, hi++, extra++)
|
|
{
|
|
for (i = 1; i < nlevels; i++)
|
|
{
|
|
if (color >= level_to_255[i-1] && color < level_to_255[i])
|
|
break;
|
|
}
|
|
if (i < nlevels)
|
|
{
|
|
*low = level_to_255[i-1];
|
|
*hi = level_to_255[i];
|
|
*extra = (color - *low) * steps / (*hi - *low);
|
|
}
|
|
else
|
|
{
|
|
*hi = *low = 255;
|
|
*extra = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
dither (unsigned char *color, int row, int col, int ncols,
|
|
short *low, short *hi, short *extra)
|
|
{
|
|
short *dp;
|
|
|
|
dp = dither_matrix[row%DITHER_ROWS];
|
|
col = DITHER_COLS - (col%DITHER_COLS);
|
|
|
|
while (--ncols >= 0)
|
|
{
|
|
if (extra[*color] > dp[--col])
|
|
*color = hi[*color];
|
|
else
|
|
*color = low[*color];
|
|
if (col == 0) col = DITHER_COLS;
|
|
color++;
|
|
}
|
|
}
|
|
|
|
void
|
|
red_dither (unsigned char *color, int row, int col, int ncols)
|
|
{
|
|
dither (color, row, col, ncols, red_low, red_hi, red_extra);
|
|
}
|
|
|
|
void
|
|
grn_dither (unsigned char *color, int row, int col, int ncols)
|
|
{
|
|
dither (color, row, col, ncols, grn_low, grn_hi, grn_extra);
|
|
}
|
|
|
|
void
|
|
blu_dither (unsigned char *color, int row, int col, int ncols)
|
|
{
|
|
dither (color, row, col, ncols, blu_low, blu_hi, blu_extra);
|
|
}
|
|
|
|
|
|
void
|
|
dither_init (void)
|
|
{
|
|
if (getenv ("OLD_DITHERER"))
|
|
{
|
|
bNewDitherer = 1;
|
|
dither_func = xx_Dither;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
bNewDitherer = 1;
|
|
dither_func = xx_NewDither;
|
|
}
|
|
|
|
set_colormode (COLORMODE_DITHER);
|
|
build_color_tables ();
|
|
build_dither_tables ();
|
|
}
|
|
|
|
|
|
/*
|
|
** Dither a row of color values
|
|
**
|
|
** from and to can be the same buffer.
|
|
**
|
|
** row is actual row in image and is requred for the matrix indexing
|
|
*/
|
|
dither_row (struct _GIFinfo *gif, unsigned char * from, unsigned char * to,
|
|
int row, int ncols)
|
|
{
|
|
short *dp;
|
|
int tmp;
|
|
int mat_col;
|
|
int red, blu, grn;
|
|
int r, g, b;
|
|
|
|
dp = dither_matrix[row%DITHER_ROWS];
|
|
mat_col = DITHER_COLS-1; /* - (mat_col%DITHER_COLS); */
|
|
|
|
while (--ncols >= 0)
|
|
{
|
|
if ((gif->transparent != -1) && (*from == gif->transparent))
|
|
{
|
|
*to = BACKGROUND_COLOR_INDEX;
|
|
goto cont;
|
|
}
|
|
|
|
/* RED */
|
|
r = gif->gif_colors[*from].r;
|
|
if (red_extra[r] > dp[mat_col])
|
|
red = red_hi[r];
|
|
else
|
|
red = red_low[r];
|
|
|
|
/* GREEN */
|
|
g = gif->gif_colors[*from].g;
|
|
if (grn_extra[g] > dp[mat_col])
|
|
grn = grn_hi[g];
|
|
else
|
|
grn = grn_low[g];
|
|
|
|
/* BLUE */
|
|
b = gif->gif_colors[*from].b;
|
|
if (blu_extra[b] > dp[mat_col])
|
|
blu = blu_hi[b];
|
|
else
|
|
blu = blu_low[b];
|
|
|
|
#ifdef UNIX
|
|
tmp = red_value[red] + grn_value[grn] + blu_value[blu];
|
|
*to = (unsigned char) dither_info.cmap[tmp];
|
|
#else
|
|
*to = red_value[red] + grn_value[grn] + blu_value[blu];
|
|
#endif
|
|
|
|
cont:
|
|
from++; to++;
|
|
mat_col--;
|
|
if (mat_col == 0)
|
|
mat_col = DITHER_COLS-1;
|
|
}
|
|
}
|
|
|
|
/* if 128
|
|
#include "shared/dmatrix.h"
|
|
*/
|
|
#define BM_SET1(buf,x) ((buf)[(x)/8] |= (1<<(7-((x)%8))))
|
|
#define BM_SET0(buf,x) ((buf)[(x)/8] &= ~(1<<(7-((x)%8))))
|
|
|
|
#define MONO_MATRIX_SIZE 16
|
|
|
|
/* given a row of gray scale bytes, return a dithered row of B/W bits */
|
|
int mono_dither (unsigned char *gray, unsigned char *mono, int row, int len, int invert)
|
|
{
|
|
int i;
|
|
short *dp;
|
|
|
|
dp = dither_matrix16[row%MONO_MATRIX_SIZE];
|
|
for (i = 0 ; i < len ; ++i, ++gray)
|
|
{
|
|
if (*gray >= dp[i%MONO_MATRIX_SIZE])
|
|
if (invert)
|
|
BM_SET0 (mono, i);
|
|
else
|
|
BM_SET1(mono, i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#else
|
|
void dither_init ( void )
|
|
{
|
|
}
|
|
|
|
#endif /* NEW_DITHERER */
|