cdesktopenv/cde/lib/DtHelp/GifUtils.c

1260 lines
34 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* $XConsortium: GifUtils.c /main/5 1996/05/07 13:21:42 drk $
* Copyright (c) 1993 HAL Computer Systems International, Ltd.
* All rights reserved. Unpublished -- rights reserved under
* the Copyright Laws of the United States. USE OF A COPYRIGHT
* NOTICE IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
* OR DISCLOSURE.
*
* THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND TRADE
* SECRETS OF HAL COMPUTER SYSTEMS INTERNATIONAL, LTD. USE,
* DISCLOSURE, OR REPRODUCTION IS PROHIBITED WITHOUT THE
* PRIOR EXPRESS WRITTEN PERMISSION OF HAL COMPUTER SYSTEMS
* INTERNATIONAL, LTD.
*
* RESTRICTED RIGHTS LEGEND
* Use, duplication, or disclosure by the Government is subject
* to the restrictions as set forth in subparagraph (c)(l)(ii)
* of the Rights in Technical Data and Computer Software clause
* at DFARS 252.227-7013.
*
* HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.
* 1315 Dell Avenue
* Campbell, CA 95008
*
*/
/* +-------------------------------------------------------------------+ */
/* | Portions lifted from giftoppm.c (pbmplus version 10dec91) | */
/* | | */
/* | 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 C_Gif
#define L_Graphics
#define C_MessageMgr
#define L_Managers
/* include files */
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include "GifUtilsI.h"
#ifndef __STDC__
#define debug1(s, x) s << "x" << " = " << (x) << "\n"
#else
#define debug1(s, x) s << #x << " = " << (x) << "\n"
#endif
#define MAX_GHANDS 16 /* maximum # of GRAF handles */
#define PPM_ASSIGN(p,red,grn,blu) (p) = ((pixel) (red) << 20) | ((pixel) (grn) << 10) | (pixel) (blu)
#define PPM_GETR(p) (((p) & 0x3ff00000) >> 20)
#define PPM_GETG(p) (((p) & 0xffc00) >> 10)
#define PPM_GETB(p) ((p) & 0x3ff)
#define MAXCOLORMAPSIZE 256
#define TRUE 1
#define FALSE 0
#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) (((b)<<8)|(a))
typedef struct {
unsigned int Width;
unsigned int Height;
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
unsigned int BitPixel;
unsigned int ColorResolution;
unsigned int Background;
unsigned int AspectRatio;
} GifScreenType;
typedef struct {
int transparent;
int delayTime;
int inputFlag;
int disposal;
} Gif89Type;
/*
* This structure holds variables that were formerly global or static
* within a function in the original giftoppm code. They have been
* moved into this object to ensure thread re-entrancy of the routines
* that use them. A unique instance of this object is created for each
* thread and passed to the appropriate routines.
*/
typedef struct {
/* Formerly global variables */
GifScreenType GifScreen;
Gif89Type Gif89;
int ZeroDataBlock;
/* Formerly static variables declared in DoExtension */
char ext_buf[256];
/* Formerly static variables declared in GetCode */
unsigned char buf[280];
int curbit, lastbit, done, last_byte;
/* Formerly static variables declared in LWZReadByte*/
int fresh;
int code_size, set_code_size;
int max_code, max_code_size;
int firstcode, oldcode;
int clear_code, end_code;
int table[2][(1<< MAX_LWZ_BITS)];
int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
} GifState;
/*********************/
static void
_gif_error( char *format, ... )
{
va_list args;
va_start( args, format );
fprintf( stderr, "GifObject: " );
(void) vfprintf( stderr, format, args );
fputc( '\n', stderr );
va_end( args );
/* throw (Exception()); */
#if 0
abort();
#endif
}
/*********************/
static void
_gif_message( char *format, ... )
{
va_list args;
va_start( args, format );
fprintf( stderr, "GifObject: " );
(void) vfprintf( stderr, format, args );
fputc( '\n', stderr );
va_end( args );
}
/*********************/
static pixel **
_gif_allocarray( int cols, int rows, int size )
{
char** its;
int i;
its = (char**) malloc( rows * sizeof(char*) );
if ( its == (char**) 0 )
_gif_error( "out of memory allocating an array", 0 );
its[0] = (char*) malloc( rows * cols * size );
if ( its[0] == (char*) 0 )
_gif_error( "out of memory allocating an array", 0 );
for ( i = 1; i < rows; ++i )
its[i] = &(its[0][i * cols * size]);
return (pixel **)its;
}
/*********************/
static int
GetDataBlock(byte **inbuf, unsigned char *buf, GifState *g)
{
unsigned char count;
count = (*inbuf)[0];
(*inbuf)++;
g->ZeroDataBlock = count == 0;
if (count) {
memcpy (buf, *inbuf, count);
*inbuf += count;
}
return count;
}
/*********************/
static int
ReadColorMap(byte **inbuf, int number, unsigned char buffer[3][MAXCOLORMAPSIZE])
{
int i;
unsigned char *rgb;
for (i = 0; i < number; ++i) {
rgb = (unsigned char *)*inbuf;
*inbuf += 3;
buffer[CM_RED][i] = rgb[0] ;
buffer[CM_GREEN][i] = rgb[1] ;
buffer[CM_BLUE][i] = rgb[2] ;
}
return FALSE;
}
/*********************/
static int
DoExtension(byte **inbuf, int label, GifState *g)
{
char *str;
char *buf = g->ext_buf;
switch (label) {
case 0x01: /* Plain Text Extension */
str = "Plain Text Extension";
#ifdef notdef
if (GetDataBlock(inbuf, (unsigned char*) buf, g) == 0)
;
lpos = LM_to_uint(buf[0], buf[1]);
tpos = LM_to_uint(buf[2], buf[3]);
width = LM_to_uint(buf[4], buf[5]);
height = LM_to_uint(buf[6], buf[7]);
cellw = buf[8];
cellh = buf[9];
foreground = buf[10];
background = buf[11];
while (GetDataBlock(inbuf, (unsigned char*) buf, g) != 0) {
PPM_ASSIGN(image[ypos][xpos],
cmap[CM_RED][v],
cmap[CM_GREEN][v],
cmap[CM_BLUE][v]);
++index;
}
return FALSE;
#else
break;
#endif
case 0xff: /* Application Extension */
str = "Application Extension";
break;
case 0xfe: /* Comment Extension */
str = "Comment Extension";
while (GetDataBlock(inbuf, (unsigned char*) buf, g) != 0) {
#ifdef DEBUG
_gif_message("gif comment: %s", buf );
#endif
}
return FALSE;
case 0xf9: /* Graphic Control Extension */
str = "Graphic Control Extension";
(void) GetDataBlock(inbuf, (unsigned char*) buf, g);
g->Gif89.disposal = (buf[0] >> 2) & 0x7;
g->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
g->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
if ((buf[0] & 0x1) != 0)
g->Gif89.transparent = buf[3];
while (GetDataBlock(inbuf, (unsigned char*) buf, g) != 0)
;
return FALSE;
default:
str = buf;
sprintf(buf, "UNKNOWN (0x%02x)", label);
break;
}
#ifdef DEBUG
_gif_message("got a '%s' extension", str );
#endif
while (GetDataBlock(inbuf, (unsigned char*) buf, g) != 0)
;
return FALSE;
}
/*********************/
static int
GetCode(byte **inbuf, int code_size, int flag, GifState *g)
{
int i, j, ret;
unsigned char count;
unsigned char *buf = g->buf;
if (flag) {
for (i = 0; i < 280; i++)
buf[i] = 0;
g->last_byte = 0;
g->curbit = 0;
g->lastbit = 0;
g->done = FALSE;
return 0;
}
if ( (g->curbit+code_size) >= g->lastbit) {
if (g->done) {
if (g->curbit >= g->lastbit)
_gif_error("ran off the end of my bits", 0 );
return -1;
}
if (g->last_byte) {
buf[0] = buf[g->last_byte-2];
buf[1] = buf[g->last_byte-1];
}
if ((count = GetDataBlock(inbuf, &buf[2], g)) == 0)
g->done = TRUE;
g->last_byte = 2 + count;
g->curbit = (g->curbit - g->lastbit) + 16;
g->lastbit = (2+count)*8 ;
}
ret = 0;
for (i = g->curbit, j = 0; j < code_size; ++i, ++j)
ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
g->curbit += code_size;
return ret;
}
/*********************/
static int
LWZReadByte(byte **inbuf, int flag, int input_code_size, GifState *g)
{
int code, incode;
register int i;
if (flag) {
g->set_code_size = input_code_size;
g->code_size = g->set_code_size+1;
g->clear_code = 1 << g->set_code_size ;
g->end_code = g->clear_code + 1;
g->max_code_size = 2*g->clear_code;
g->max_code = g->clear_code+2;
GetCode(inbuf, 0, TRUE, g);
g->fresh = TRUE;
for (i = 0; i < g->clear_code; ++i) {
g->table[0][i] = 0;
g->table[1][i] = i;
}
for (; i < (1<<MAX_LWZ_BITS); ++i)
g->table[0][i] = g->table[1][0] = 0;
g->sp = g->stack;
return 0;
} else if (g->fresh) {
g->fresh = FALSE;
do {
g->firstcode = g->oldcode =
GetCode(inbuf, g->code_size, FALSE, g);
} while (g->firstcode == g->clear_code);
return g->firstcode;
}
if (g->sp > g->stack)
return *--(g->sp);
while ((code = GetCode(inbuf, g->code_size, FALSE, g)) >= 0) {
if (code == g->clear_code) {
for (i = 0; i < g->clear_code; ++i) {
g->table[0][i] = 0;
g->table[1][i] = i;
}
for (; i < (1<<MAX_LWZ_BITS); ++i)
g->table[0][i] = g->table[1][i] = 0;
g->code_size = g->set_code_size+1;
g->max_code_size = 2*g->clear_code;
g->max_code = g->clear_code+2;
g->sp = g->stack;
g->firstcode = g->oldcode =
GetCode(inbuf, g->code_size, FALSE, g);
return g->firstcode;
} else if (code == g->end_code) {
int count;
unsigned char buf[260];
if (g->ZeroDataBlock)
return -2;
while ((count = GetDataBlock(inbuf, buf, g)) > 0)
;
#ifdef DEBUG
if (count != 0)
_gif_message("missing EOD in data stream (common occurence)");
#endif
return -2;
}
incode = code;
if (code >= g->max_code) {
*(g->sp)++ = g->firstcode;
code = g->oldcode;
}
while (code >= g->clear_code) {
*(g->sp++) = g->table[1][code];
if (code == g->table[0][code])
_gif_error("circular table entry BIG ERROR", 0);
code = g->table[0][code];
}
*(g->sp)++ = g->firstcode = g->table[1][code];
if ((code = g->max_code) <(1<<MAX_LWZ_BITS)) {
g->table[0][code] = g->oldcode;
g->table[1][code] = g->firstcode;
++(g->max_code);
if ((g->max_code >= g->max_code_size) &&
(g->max_code_size < (1<<MAX_LWZ_BITS))) {
g->max_code_size *= 2;
++(g->code_size);
}
}
g->oldcode = incode;
if (g->sp > g->stack)
return *--(g->sp);
}
return code;
}
/*********************/
pixel **
ReadImage(byte **inbuf, int len, int height, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore, GifState *g)
{
unsigned char c;
int v;
int xpos = 0, ypos = 0, pass = 0;
pixel **image;
/*
** Initialize the Compression routines
*/
c = (*inbuf)[0];
(*inbuf)++;
if (LWZReadByte(inbuf, TRUE, c, g) < 0)
_gif_error("error reading image", 0 );
/*
** If this is an "uninteresting picture" ignore it.
*/
if (ignore) {
#ifdef DEBUG
_gif_message("skipping image..." );
#endif
while (LWZReadByte(inbuf, FALSE, c, g) >= 0)
;
return NULL;
}
if ((image = _gif_allocarray(len, height, sizeof(pixel))) == NULL)
_gif_error("couldn't alloc space for image", 0 );
#ifdef DEBUG
_gif_message("reading %d by %d%s GIF image",
len, height, interlace ? " interlaced" : "" );
#endif
while ((v = LWZReadByte(inbuf,FALSE,c, g)) >= 0 ) {
PPM_ASSIGN(image[ypos][xpos], cmap[CM_RED][v],
cmap[CM_GREEN][v], cmap[CM_BLUE][v]);
++xpos;
if (xpos == len) {
xpos = 0;
if (interlace) {
switch (pass) {
case 0:
case 1:
ypos += 8; break;
case 2:
ypos += 4; break;
case 3:
ypos += 2; break;
}
if (ypos >= height) {
++pass;
switch (pass) {
case 1:
ypos = 4; break;
case 2:
ypos = 2; break;
case 3:
ypos = 1; break;
default:
goto fini;
}
}
} else {
++ypos;
}
}
if (ypos >= height)
break;
}
fini:
#ifdef DEBUG
if (LWZReadByte(inbuf,FALSE,c, g)>=0)
_gif_message("too much input data, ignoring extra...");
#endif
return image;
}
/* ////////////////////////////////////////////////////////////
// class constructor
// ////////////////////////////////////////////////////////// */
enum _DtGrLoadStatus InitGifObject (
GifObj *go,
Display *dpy,
Drawable drawable,
Screen *screen,
int depth,
Colormap colormap,
Visual *visual,
GC gc,
enum _DtGrColorModel colorModel,
Boolean allowReducedColors)
{
int r, g, b, i, visualsMatched;
XVisualInfo vTemplate, *visualList;
/*
** Initialize structure values
*/
go->bits_per_pixel = 2;
go->colors_per_pixel = (int) pow (2, go->bits_per_pixel);
go->total_colors = (int) pow (go->colors_per_pixel, 3);
go->f_color_map_constructed = 0;
go->f_total_greys = 2;
go->f_ximage = NULL;
go->f_dpy = dpy;
go->f_drawable = drawable;
go->f_screen = XScreenNumberOfScreen(screen);
go->f_dft_depth = depth;
go->f_cmap = colormap;
go->f_gc = gc;
go->f_visual = visual;
go->f_ncells = DisplayCells(go->f_dpy, go->f_screen);
go->f_nplanes = DisplayPlanes(go->f_dpy,go->f_screen);
go->f_white = WhitePixel(go->f_dpy, go->f_screen);
go->f_black = BlackPixel(go->f_dpy, go->f_screen);
go->f_allow_reduced_colors = allowReducedColors;
go->f_color_reduction_used = FALSE;
/*
** Initialize color allocation fields according to the color model
** specified by the caller.
*/
switch (colorModel)
{
case _DtGrCOLOR:
go->f_do_visual = DO_COLOR;
go->f_init_total_greys = 32;
break;
case _DtGrGRAY_SCALE:
go->f_do_visual = DO_GREY;
go->f_init_total_greys = 32;
break;
case _DtGrBITONAL:
go->f_do_visual = DO_GREY;
go->f_init_total_greys = 2;
break;
default:
/* Should never get here */
go->f_do_visual = DO_COLOR;
go->f_init_total_greys = 32;
}
/* Return if the colormap is already allocated */
if ( go->f_color_map_constructed )
return (_DtGrSUCCESS);
/* find the visual class code */
vTemplate.screen = go->f_screen;
vTemplate.depth = go->f_dft_depth;
visualList = XGetVisualInfo( go->f_dpy,
VisualScreenMask | VisualDepthMask,
&vTemplate, &visualsMatched );
/* Return failure if we can't find a matching visual */
if ( visualsMatched == 0 )
return (_DtGrCONVERT_FAILURE);
go->f_visual_class = StaticGray;
for ( i=0; i<visualsMatched; i++ )
{
if ( visualList[i].visual == go->f_visual )
{
go->f_visual_class = visualList[i].class;
break;
}
}
XFree(visualList);
/* Construct a 4x4x4 color cube */
i = 0;
for (r = 0; r < go->colors_per_pixel; r++)
for (g = 0; g < go->colors_per_pixel; g++)
for (b = 0; b < go->colors_per_pixel; b++)
{
go->GifCMap[i].red = ((r * 65535)/(go->colors_per_pixel - 1));
go->GifCMap[i].green = ((g * 65535)/(go->colors_per_pixel - 1));
go->GifCMap[i].blue = ((b * 65535)/(go->colors_per_pixel - 1));
i++;
}
/*
** Allocate X pixels, either color or greyscale values depending upon
** visual class and color model.
*/
switch ( go->f_visual_class )
{
case StaticGray:
case GrayScale:
case StaticColor:
{
/*
** Return failure if caller is insisting on color and this
** visual can't provide it.
*/
if ((colorModel == _DtGrCOLOR) && !allowReducedColors)
return (_DtGrCOLOR_FAILED);
if ( allocate_greys(go) != 0 )
return (_DtGrCOLOR_FAILED);
break;
}
case PseudoColor:
case DirectColor:
case TrueColor:
{
if (colorModel == _DtGrCOLOR)
{
if ( allocate_colors(go) != 0 )
return (_DtGrCOLOR_FAILED);
}
else
{
if ( allocate_greys(go) != 0 )
return (_DtGrCOLOR_FAILED);
}
break;
}
default:
return (_DtGrCONVERT_FAILURE);
}
/*
** Colors successfully allocated, return status code indicating
** whether we had to fallback to a degraded color model.
*/
if (go->f_color_reduction_used)
return (_DtGrCOLOR_REDUCE);
else
return (_DtGrSUCCESS);
}
/* /////////////////////////////////////////////////////////////////
// class destructor
// /////////////////////////////////////////////////////////////// */
void DeleteGifObjectResources(GifObj *g)
{
}
int allocate_colors(GifObj *g)
{
int i, j;
/*return allocate_greys(); // use this to test grey-scale */
XColor color;
unsigned long* colors;
color.flags = DoRed | DoGreen | DoBlue;
for (i = 0; i < g->total_colors; i++) {
color.red = g->GifCMap[i].red;
color.green = g->GifCMap[i].green;
color.blue = g->GifCMap[i].blue;
/*printf ("Allocating %3d: ", i); */
if ( !XAllocColor (g->f_dpy, g->f_cmap, &color) ) {
/*puts ("FAILED!!!"); */
colors = (unsigned long *) malloc (sizeof(unsigned long) * i);
for (j = 0; j < i; j++)
colors[j] = g->GifCMap[j].pixel;
/*cerr << "Xfree in allocate_colors(): " << i << "\n"; */
XFreeColors (g->f_dpy, g->f_cmap, colors, i, 0);
free(colors);
/* fallback to greys */
if (g->f_allow_reduced_colors)
{
g->f_color_reduction_used = TRUE;
return allocate_greys(g);
}
else
return (_DtGrCOLOR_FAILED);
}
/*fprintf(stderr, "i=%d pixel=%d\n", i, color.pixel);*/
/*printf ("@ %d\n", color.pixel); */
g->GifCMap[i].pixel = color.pixel;
}
g->f_do_visual = DO_COLOR;
g->f_color_map_constructed = 1;
return 0;
}
int allocate_greys(GifObj *g)
{
XColor color;
int i, j;
unsigned long* colors;
color.flags = DoRed | DoGreen | DoBlue;
for ( i=0; i<g->total_colors; i++ ) {
/*
debug1(cerr, i);
debug1(cerr, GifCMap[i].red);
debug1(cerr, GifCMap[i].green);
debug1(cerr, GifCMap[i].blue);
debug1(cerr, 0.299 * GifCMap[i].red + 0.587 * GifCMap[i].green +
0.114 * GifCMap[i].blue);
debug1(cerr, GifCMap[i].grey);
*/
g->GifCMap[i].grey = (unsigned short)(0.299 * g->GifCMap[i].red +
0.587 * g->GifCMap[i].green +
0.114 * g->GifCMap[i].blue);
}
/*
if ( StaticColor == g->f_visual_class ||
TrueColor == g->f_visual_class )
*/
if ( StaticColor == g->f_visual_class)
{
g->f_do_visual = DO_GREY;
g->f_total_greys = 2;
return 0;
}
for ( g->f_total_greys=g->f_init_total_greys; g->f_total_greys>=2;
g->f_total_greys/=2 )
{
/*fprintf(stderr, "f_total_greys = %d\n", g->f_total_greys); */
/*
** Return failure if we're about to downgrade from greyscale
** to dithered monochrome and we don't allow reduced colors.
*/
if ((g->f_total_greys == 2) && (g->f_init_total_greys > 2) &&
(!g->f_allow_reduced_colors))
return -1;
for (i = 0; i<g->f_total_greys; i++) {
color.red =
color.green =
color.blue = (i*65535)/(g->f_total_greys - 1);
/*fprintf (stderr, "Allocating %3d: ", i);*/
if ( !XAllocColor (g->f_dpy, g->f_cmap, &color) ) {
/*fprintf(stderr, "alloc Grey FAILED!!!");*/
colors = (unsigned long *) malloc (sizeof(unsigned long) * i);
for (j = 0; j < i; j++)
colors[j] = g->GifGMap[j];
/*cerr << "Xfree in allocate_greys()\n"; */
XFreeColors (g->f_dpy, g->f_cmap, colors, i, 0);
free(colors);
break;
}
/*printf ("@ %d\n", color.pixel); */
g->GifGMap[i] = color.pixel;
}
if ( i == g->f_total_greys ) {
/*
for ( int l=0; l<i; l++ )
cerr << "GifGMap[l]= " << GifGMap[l] << "\n";
*/
g->f_color_map_constructed = 1;
g->f_do_visual = DO_GREY;
/* If greyscape was downgraded to bitonal, record the fact */
if ((g->f_total_greys == 2) && (g->f_init_total_greys > 2))
g->f_color_reduction_used = TRUE;
return 0;
}
}
return -1;
}
/* ////////////////////////////////////////////////////////////
// Free allocated raw image data
// ////////////////////////////////////////////////////////// */
void
free_raw_image( pixel **image )
{
free( (char *)image[0] );
free( (char *)image );
}
/* ////////////////////////////////////////////////////////////
// Decompress GIF data into raw bytes
// ////////////////////////////////////////////////////////// */
pixel **
create_raw_image( byte *inbuf, unsigned int buflen, int *width, int *height, int imageNumber )
{
unsigned char *buf;
unsigned char *start_of_buf = inbuf;
unsigned char c;
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
int useGlobalColormap;
int bitPixel;
int imageCount = 0;
char version[4];
pixel **image;
GifState g;
/* Initialize the GIF state object */
g.Gif89.transparent = g.Gif89.delayTime = g.Gif89.inputFlag = -1;
g.Gif89.disposal = 0;
g.ZeroDataBlock = FALSE;
g.fresh = FALSE;
/* XXXSWM -- hack */
if (buflen < 13) {
fprintf (stderr, "Not GIF Data, buffer too small\n");
return NULL;
}
buf = (unsigned char *)inbuf;
inbuf += 6;
if (strncmp((const char *)buf,"GIF",3) != 0) {
fprintf( stderr, "GifObject: not GIF data\n" );
return NULL;
}
strncpy(version, (const char *)(buf + 3), 3);
version[3] = '\0';
if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
fprintf (stderr, "bad version number, not '87a' or '89a'\n" );
return NULL;
}
buf = (unsigned char *)inbuf;
inbuf += 7;
g.GifScreen.Width = LM_to_uint(buf[0],buf[1]);
g.GifScreen.Height = LM_to_uint(buf[2],buf[3]);
g.GifScreen.BitPixel = 2<<(buf[4]&0x07);
g.GifScreen.ColorResolution = (((buf[4]&0x70)>>3)+1);
g.GifScreen.Background = buf[5];
g.GifScreen.AspectRatio = buf[6];
if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
if (ReadColorMap(&inbuf,g.GifScreen.BitPixel,g.GifScreen.ColorMap)) {
fprintf (stderr, "error reading global colormap\n" );
return NULL;
}
}
if (g.GifScreen.AspectRatio != 0 && g.GifScreen.AspectRatio != 49) {
float r;
r = ( (float) g.GifScreen.AspectRatio + 15.0 ) / 64.0;
#ifdef DEBUG
_gif_message("warning - non-square pixels; to fix do a 'pnmscale -%cscale %g'",
r < 1.0 ? 'x' : 'y',
r < 1.0 ? 1.0 / r : r );
#endif
}
image = NULL;
for (;;) {
if (inbuf - start_of_buf >= buflen) {
fprintf (stderr, "Premature EOF in GIF data\n");
return NULL;
}
c = inbuf[0];
inbuf++;
if (c == ';') { /* GIF terminator */
if (imageCount < imageNumber)
_gif_error("only %d image%s found in file",
imageCount, imageCount>1?"s":"" );
return image;
}
if (c == '!') { /* Extension */
if (inbuf - start_of_buf >= buflen) {
fprintf (stderr, "Premature EOF in GIF data\n");
return NULL;
}
c = inbuf[0];
inbuf++;
DoExtension(&inbuf, c, &g);
continue;
}
if (c != ',') { /* Not a valid start character */
#ifdef DEBUG
_gif_message("bogus character 0x%02x, ignoring", (int) c );
#endif
continue;
}
++imageCount;
buf = (unsigned char *)inbuf;
inbuf += 9;
useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
bitPixel = 1<<((buf[8]&0x07)+1);
*width = LM_to_uint(buf[4],buf[5]);
*height = LM_to_uint(buf[6],buf[7]);
if (! useGlobalColormap) {
if (ReadColorMap(&inbuf, bitPixel, localColorMap))
_gif_error("error reading local colormap", 0 );
image = ReadImage(&inbuf, *width, *height, localColorMap,
BitSet(buf[8], INTERLACE), imageCount != imageNumber,
&g);
} else {
image = ReadImage(&inbuf, *width, *height, g.GifScreen.ColorMap,
BitSet(buf[8], INTERLACE), imageCount != imageNumber,
&g);
}
}
}
/* ////////////////////////////////////////////////////////////
// Create X pixmap from raw image data
// ////////////////////////////////////////////////////////// */
/* from "Computer Graphics" by Foley, VanDam, Feiner, Hughes */
/* 2nd edition */
static int dither_8X8[8][8] =
{
{ 0, 32, 8, 40, 2, 34, 10, 42 },
{ 48, 16, 56, 24, 50, 18, 58, 26 },
{ 42, 44, 4, 36, 14, 46, 6, 38 },
{ 60, 28, 52, 20, 62, 30, 54, 22 },
{ 3, 35, 11, 43, 1, 33, 9, 41 },
{ 51, 19, 59, 27, 49, 17, 57, 25 },
{ 15, 47, 7, 39, 13, 45, 5, 37 },
{ 63, 31, 55, 23, 61, 29, 53, 21 }
};
/*static int dither_6X6[6][6] =
//{
// { 24, 32, 16, 26, 34, 18},
// { 4, 0, 12, 6, 2, 14},
// { 20, 8, 28, 22, 10, 30},
// { 27, 35, 19, 25, 33, 17},
// { 7, 3, 15, 5, 1, 13},
// { 23, 11, 31, 21, 9, 29}
//};
*/
static int dither_4X4[4][4] =
{
{ 0, 8, 2, 10 },
{ 12, 4, 14, 6},
{ 3, 11, 1, 9},
{ 15, 7, 13, 5}
};
static int dither_3X3[3][3] =
{
{ 6, 8, 4 },
{ 1, 0, 3},
{ 5, 2, 7}
};
static int dither_2X2[2][2] =
{
{ 0, 2 },
{ 3, 1}
};
/*static int dither_matrix_sz = 2; */
/*static int dither_matrix_sz = 4; */
/*static int dither_matrix_sz = 3; */
static int dither_matrix_sz = 8;
/* call XListPixFormat() to get bits/pix and pads ? */
Pixmap
create_pixmap( GifObj *g, pixel **image, int width, int height, Pixel fg, Pixel bg, float ratio)
{
int nullCount = (4 - (width % 4)) & 0x03;
int ximWidth = width + nullCount;
byte *ximData = 0;
pixel *ipp = *image;
int spacing;
long pixval;
int x, y;
int index;
Pixmap pm;
int scaledWidth, scaledHeight;
if ( g->f_nplanes > 8 )
ximData = (byte *) malloc(ximWidth * height * 4 );
else
ximData = (byte *) malloc(ximWidth * height );
if (!ximData) {
fprintf(stderr, "Could not allocate ximage data\n");
return NULL;
}
/* Monochrome */
if (g->f_nplanes == 1)
g->f_ximage = XCreateImage(g->f_dpy, g->f_visual, g->f_nplanes, XYPixmap,
0, (char *)ximData, width, height, 32, 0);
/* 8 bit color */
/*else if (g->f_nplanes == 8) */
/* non-mono display */
else
g->f_ximage = XCreateImage(g->f_dpy, g->f_visual, g->f_nplanes, ZPixmap,
0, (char *)ximData, width, height, 32, 0);
if (!g->f_ximage) {
fprintf(stderr, "XCreateImage failed\n");
return NULL;
}
/* RGB to Pixel Conversion */
if ( g->f_total_greys == 2 )
spacing = 65536 / (dither_matrix_sz * dither_matrix_sz);
else
spacing = 65536 / g->f_total_greys;
/*cerr << "spacing" << spacing << "\n"; */
for (y=0; y < height; y++) {
for (x=0; x < width; x++) {
pixval = (long)*ipp;
/* XColor cellDef; */
/* cellDef.red = (short)PPM_GETR(pixval); */
/* cellDef.green = (short)PPM_GETG(pixval); */
/* cellDef.blue = (short)PPM_GETB(pixval); */
index = (((short)PPM_GETR(pixval))/64)*16 +
(((short)PPM_GETG(pixval))/64)*4 +
((short)PPM_GETB(pixval))/64;
/*fprintf(stderr, "grey= %d, grey/space=%d\n", g->GifCMap[index].grey, g->GifCMap[index].grey / spacing);*/
switch (g->f_do_visual) {
case DO_GREY:
switch ( g->f_total_greys ) {
case 2:
/*cerr << "index=" << index << "\n"; */
/*cerr << "GifCMap[index].grey" << GifCMap[index].grey << "\n"; */
/*cerr << "GifCMap[index].grey/spacing" << GifCMap[index].grey / spacing << "\n"; */
if ( dither_8X8[x%dither_matrix_sz][y%dither_matrix_sz] < g->GifCMap[index].grey / spacing ) {
XPutPixel(g->f_ximage,x,y,g->f_white);
} else {
XPutPixel(g->f_ximage,x,y,g->f_black);
}
break;
default:
/*cerr << GifCMap[index].grey / spacing << " "; */
XPutPixel(g->f_ximage,x,y, g->GifGMap[g->GifCMap[index].grey / spacing]);
}
break;
case DO_COLOR:
#ifdef FLOOD
fprintf(stderr, "%03d %03d %03d -- %03d %03d %03d ",
(short)PPM_GETR(pixval),(short)PPM_GETG(pixval),(short)PPM_GETB(pixval),
g->GifCMap[index].red>>8, g->GifCMap[index].green>>8, g->GifCMap[index].blue>>8
);
if ((short)PPM_GETR(pixval) != (GifCMap[index].red>>8) ||
(short)PPM_GETG(pixval) != (GifCMap[index].green>>8) ||
(short)PPM_GETB(pixval) != (GifCMap[index].blue>>8))
puts (" *");
else
puts (" -");
#endif
XPutPixel(g->f_ximage,x,y, g->GifCMap[index].pixel);
/*
switch ( g->f_visual_class ) {
case StaticColor:
case PseudoColor:
XPutPixel(g->f_ximage,x,y, g->GifCMap[index].pixel);
break;
case TrueColor:
case DirectColor:
break;
}
*/
}
ipp++;
}
for (x=width;x<ximWidth;x++)
XPutPixel(g->f_ximage,x,y,bg); /* padding */
}
scaledWidth = width * ratio + 0.5;
scaledHeight = height * ratio + 0.5;
if (scaledWidth == 0)
scaledWidth = 1;
if (scaledHeight == 0)
scaledHeight = 1;
pm = XCreatePixmap(g->f_dpy,g->f_drawable,
scaledWidth,scaledHeight,
g->f_nplanes);
if (!pm) {
fprintf(stderr, "could not create pixmap\n");
return NULL;
}
_XmPutScaledImage (g->f_dpy,pm,g->f_gc,g->f_ximage,
0,0,0,0,width,height,
scaledWidth,scaledHeight);
XDestroyImage(g->f_ximage);
g->f_ximage = NULL;
return(pm);
}
/* /////////////////////////////////////////////////////////////////
// Load pixmap from GIF data
// /////////////////////////////////////////////////////////////// */
Pixmap
gif_to_pixmap(GifObj *g, byte *inbuf, unsigned int buflen, Dimension *w, Dimension *h, Pixel fg, Pixel bg, float ratio)
{
Pixmap pixmap;
pixel **raw_image;
int width, height;
/* Create raw image from compress GIF data */
raw_image = create_raw_image (inbuf, buflen, &width, &height, 1);
if (!raw_image) return NULL;
/* Create X pixmap from raw image data */
pixmap = create_pixmap(g, raw_image, width, height, fg, bg, ratio);
/* Free raw image data */
free_raw_image(raw_image);
/* Set X pixmap dimensions */
*w = (Dimension) width * ratio + 0.5;
*h = (Dimension) height * ratio + 0.5;
if (*w == 0)
*w = 1;
if (*h == 0)
*h = 1;
/* Return X pixmap */
return pixmap;
}