/***** * readGIFplc.c : GIF/GZF progressive loading interfaces * * This file Version $Revision: 1.6 $ * * Creation date: Fri Jun 13 16:31:35 GMT+0100 1997 * Last modification: $Date: 1999/07/29 01:26:29 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * * Copyright (C) 1994-1997 by Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: readGIFplc.c,v $ * Revision 1.6 1999/07/29 01:26:29 sopwith * * * Fix all warnings. * * Revision 1.5 1998/02/12 03:09:46 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.4 1998/01/07 01:45:41 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include #endif #include #include #if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ) #include /* unlink */ #include #endif #include "XmHTMLP.h" #include "XmHTMLfuncs.h" #include "LZWStream.h" #include "plc.h" /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ #define INTERLACE 0x40 #define LOCALCOLORMAP 0x80 /*** Private Function Prototype Declarations ****/ #define BitSet(byte, bit) (((byte) & (bit)) == (bit)) #define LM_to_uint(a,b) ((((b)&0xFF) << 8) | ((a)&0xFF)) static Boolean DoExtension(PLC *plc, int label); static Boolean ReadColormap(PLC *plc, PLCImageGIF *gif); static Boolean DoImage(PLCImage *gif, Byte *input); /*** Private Variable Declarations ***/ /***** * Name: _PLC_GIF_Init * Return Type: void * Description: image initializer for GIF images * In: * plc: current PLC * Returns: * Nothing but PLC is updated. * Note: * As this routine must be fully re-entrant, it needs a lot of checks * to make sure we have the data we want fully available. * The drawback is that if we are being suspended while doing this * initialization, everything must be reset and repeated the next time * this routine is called. *****/ void _PLC_GIF_Init(PLC *plc) { Byte buf[16], c; PLCImageGIF *gif; gif = &(plc->object->plc_gif_image); _XmHTMLDebug(15, ("plc.c: _PLC_GIF_Init for %s\n", plc->url)); /* this plc is active */ plc->plc_status = PLC_ACTIVE; /***** * When this routine is called, the init method of this PLC has already * been called to determine the type of this PLC Image object. Therefore * we already have data available and we need to rewind the input buffer * back to the beginning. *****/ _PLCRewindInputBuffer(plc); /* we know this is a gif image, so skip magic */ gif->info->type = IMAGE_GIF; (void)_PLCReadOK(plc, buf, 6); /* read logical screen descriptor */ (void)_PLCReadOK(plc, buf, 7); /* image dimensions */ gif->width = LM_to_uint(buf[0],buf[1]); gif->height = LM_to_uint(buf[2],buf[3]); /* set colorspace and allocate a colormap */ gif->colorclass = XmIMAGE_COLORSPACE_INDEXED; gif->cmapsize = 2<<(buf[4]&0x07); /* * We may have been called before (but returned 'cause not enough data * was available). */ if(gif->cmap == NULL) gif->cmap = (TColor*)calloc(gif->cmapsize, sizeof(TColor)); /* image is initially fully opaque */ gif->transparency = XmHTML_NONE; gif->bg_pixel = -1; /* * Incoming data buffer. This is *way* too much as the incoming data * will be compressed (but it does make sure there is enough room) */ gif->buf_size = gif->width*gif->height; gif->buf_pos = 0; /* current pos in data received so far */ gif->byte_count = 0; /* size of data received so far */ if(gif->buffer == NULL) gif->buffer = (Byte*)calloc(gif->buf_size + 1, sizeof(Byte)); /* check if a global colormap is available */ if(BitSet(buf[4], LOCALCOLORMAP)) { if(!(ReadColormap(plc, gif))) { /* premature end of data. */ if(plc->plc_data_status == STREAM_END) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: bad image\n Could not read " "global colormap.", plc->url); plc->plc_status = PLC_ABORT; } return; /* no global colormap! */ } } /* process all extensions */ c = 0; while(c != ',') { if(!_PLCReadOK(plc, &c, 1)) return; if (c == ';') /* GIF terminator */ { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: corrupt image\n Image contains " "no data.", plc->url); plc->plc_status = PLC_ABORT; return; } if(c == '!') /* Extension */ { if(!_PLCReadOK(plc,&c,1)) { if(plc->plc_data_status == STREAM_END) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: corrupt image\n Could not" "read extension block type.", plc->url); plc->plc_status = PLC_ABORT; } return; } if(!(DoExtension(plc, c))) { if(plc->plc_data_status == STREAM_END) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: corrupt image\n Empty " "extension block.", plc->url); plc->plc_status = PLC_ABORT; } return; } continue; } if (c != ',') continue; /* Not a valid start character */ } /* get image descriptor */ if(!_PLCReadOK(plc, buf, 9)) return; /* see if we are to use a local colormap */ if(BitSet(buf[8], LOCALCOLORMAP)) { /* local colormap size */ gif->ncolors = 1<<((buf[8]&0x07)+1); /* do we also have a glocal colormap? */ if(gif->cmap) free(gif->cmap); gif->cmapsize = gif->ncolors; gif->cmap = (TColor*)calloc(gif->cmapsize, sizeof(TColor)); if(!(ReadColormap(plc, gif))) { /* premature end of data. */ if(plc->plc_data_status == STREAM_END) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: bad image\n Could not read " "local colormap.", plc->url); plc->plc_status = PLC_ABORT; } return; /* no global colormap! */ } } gif->ncolors = gif->cmapsize; /* sanity check: image *must* have a colormap */ if(gif->cmap == NULL) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "%s: bad image\n Image has no colormap " "(global nor local)!", plc->url); plc->plc_status = PLC_ABORT; return; /* no global colormap! */ } /* image depth (= codeSize in GIF images, unused in GZF images) */ if(!(_PLCReadOK(plc, &c, 1))) return; gif->depth = (int)(c & 0xff); /* check interlacing */ if(BitSet(buf[8], INTERLACE)) { /* interlaced gifs require 4 passes and use an initial rowstride of 8 */ gif->npasses = 4; gif->stride = 8; } else { /* regular gif, 1 pass will get us the entire image */ gif->npasses = 1; gif->stride = 0; } gif->curr_pass = 0; gif->curr_scanline = 0; /***** * This routine is also used for GZF images, so before initializing * the LZWStream object we need to make sure we have been called for * a true GIF image. *****/ if(plc->object->type == plcGIF) { XmHTMLWidget html = plc->object->plc_any.owner; if(html->html.gif_proc != NULL) { gif->external_codec = True; gif->inflate = html->html.gif_proc; if((gif->gstream = (XmHTMLGIFStream*)malloc(sizeof(XmHTMLGIFStream))) == NULL) { /* out of memory, too bad then */ _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "Can't load image %s:\n Failed to " "allocate %i bytes for external gif decoder.", plc->url, sizeof(XmHTMLGIFStream)); plc->plc_status = PLC_ABORT; return; } /* initialize GIFStream object */ memset(gif->gstream, 0, sizeof(XmHTMLGIFStream)); gif->gstream->codesize = (int)c; gif->gstream->state = GIF_STREAM_INIT; gif->gstream->next_out = gif->buffer; gif->gstream->avail_out = gif->buf_size + 1; gif->gstream->is_progressive = True; /* * and call external decoder so it can initialize its own data * structures */ if((gif->inflate(gif->gstream)) != GIF_STREAM_OK) { if(gif->gstream->msg != NULL) { _XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"), "XmNdecodeGIFProc initalization " "failed for image\n %s:\n %s", plc->url, gif->gstream->msg ? gif->gstream->msg : "(unknown error)"); } /* external decoder initalization failed, abort and return */ plc->plc_status = PLC_ABORT; return; } gif->gstream->state = GIF_STREAM_OK; } else { /* initialize local data buffer */ gif->ib.file = plc->url; gif->ib.buffer = gif->buffer; gif->ib.size = 0; gif->ib.next = 0; gif->ib.type = IMAGE_GIF; gif->ib.depth = gif->depth; gif->ib.may_free = False; /* initialize LZWStream object */ if(gif->lstream == NULL) { if((gif->lstream = LZWStreamCreate(&(gif->ib), html->html.zCmd)) == NULL) { /* couldn't create stream, abort and return */ plc->plc_status = PLC_ABORT; return; } /* set read functions */ gif->lstream->readOK = _XmHTMLGifReadOK; gif->lstream->getData = _XmHTMLGifGetDataBlock; } /* first byte in buffer is gif codesize */ gif->ib.buffer[0] = c; gif->ib.size = 1; } /* allocate room for final image data */ if(gif->data == NULL) { gif->data = (Byte*)calloc(gif->buf_size + 1, sizeof(Byte)); /* don't allocate clipmask yet, it's done in the plc code */ } gif->data_size = gif->buf_size; gif->data_pos = 0; } /* object has been initialized */ plc->initialized = True; plc->curr_obj_func = 0; /* move to GIF scanline reader */ return; } /***** * Name: _PLC_GIF_ScanlineProc * Return Type: void * Description: GZF scanline processor (decompresses data and orders it) * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_GIF_ScanlineProc(PLC *plc) { Byte *input = NULL; PLCImageGIF *gif; Boolean done = False; int bytes_avail; _XmHTMLDebug(15, ("plc.c: _PLC_GIF_ScanlineProc for %s\n", plc->url)); gif = &(plc->object->plc_gif_image); /***** * We can choose between two types of LZW decoders: the internal one or * an externally installed progressive decoder. * The internal decoder is fairly efficient for direct gif loading, but is * *HUGELY* slow for progressive gif loading: it forks of uncompress (or * gzip) each time LZWStreamUncompress is called. Therefore we collect * all data and call the internal decoder when all image data has been * received. * When an external gif decoder has been installed, we just proceed in the * same way as with the GZF scanline procedure: decode a chunk of data * each time and allow it to be transfered to the display. *****/ if(gif->external_codec == False) { int len; Boolean have_all = False; bytes_avail = plc->left; #ifndef PLC_WORKPROCS do { #endif /* !PLC_WORKPROCS */ /***** * get a new block of compressed data. This will automatically make * a new request for input data when there isn't enough data left * in the current input buffer. *****/ if((len = _PLCGetDataBlock(plc, gif->gbuf)) != 0) { /* store block size */ gif->ib.buffer[gif->ib.size++] = (Byte)len; /* append newly received data */ (void)memcpy(gif->ib.buffer + gif->ib.size, gif->gbuf, len); /* new buffer size */ gif->ib.size += len; /* bytes left in current buffer (+1 for block header) */ bytes_avail -= (len + 1); /* prevent image transfer function from doing anything */ gif->prev_pos = gif->data_pos = 0; } else { if(plc->plc_status == PLC_SUSPEND || plc->plc_status == PLC_ABORT) return; /* * if plc_status == PLC_ACTIVE, we have a zero data block which * indicates the raster data end. For now we just consider this * as the end signal and start decoding the raster data. * For gif animations this would be the place to move to the * next frame. */ have_all = True; /* plug in a zero length data block and GIF terminator */ gif->ib.buffer[gif->ib.size++] = (Byte)0; gif->ib.buffer[gif->ib.size++] = (Byte)1; gif->ib.buffer[gif->ib.size++] = (Byte)';'; } #ifndef PLC_WORKPROCS } /* This test will be false if we made a new data request */ while(bytes_avail == plc->left && have_all == False); #endif /* !PLC_WORKPROCS */ /* we have got all image data, now decode it */ if(have_all) { /* rewind image buffer */ gif->ib.next = 0; /* now (re-)initialize the stream object */ if((LZWStreamInit(gif->lstream)) <= 0) { /* this is an error */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_GIF_ScanlineProc"), gif->lstream->err_msg); plc->plc_status = PLC_ABORT; return; } /* convert data */ LZWStreamConvert(gif->lstream); /* get uncompressed data */ if((input = LZWStreamUncompress(gif->lstream, (int*)&gif->byte_count)) == NULL) { _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_GIF_ScanlineProc"), gif->lstream->err_msg); /* rather fatal... */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_GIF_ScanlineProc"), "LZWStreamUncompress failed for image %s", plc->url); plc->plc_status = PLC_ABORT; return; } /* convert input data to raw image data */ (void)DoImage((PLCImage*)gif, input); /* free input buffer */ free(input); /* we are finished here */ plc->obj_funcs_complete = True; } } else { /***** * External GIF decoder installed, proceeds in the same way as the * GZF decoder, except that now the external decoder is called * instead of the zlib routines. *****/ XmHTMLGIFStream *gifstream = gif->gstream; int err; bytes_avail = plc->left; #ifndef PLC_WORKPROCS do { #endif /* !PLC_WORKPROCS */ /***** * get a new block of compressed data. This will automatically make * a new request for input data when there isn't enough data left * in the current input buffer. *****/ if((gifstream->avail_in = _PLCGetDataBlock(plc, gif->gbuf)) == 0) { if(plc->plc_status == PLC_SUSPEND || plc->plc_status == PLC_ABORT) return; /* * if plc_status == PLC_ACTIVE, we have a zero data block which * indicates the raster data end. Put in a 0 length data * block, the gif terminator, set the gifstream state so the * caller knows to wrap things up and proceed. */ gif->gbuf[0] = (Byte)0; gif->gbuf[1] = (Byte)1; gif->gbuf[2] = (Byte)';'; gifstream->avail_in = 3; gifstream->state = GIF_STREAM_FINAL; } gifstream->next_in = gif->gbuf; /* bytes left in current buffer (+1 for block header) */ bytes_avail -= (gifstream->avail_in + 1); /* adjust output buffer */ gifstream->next_out = gif->buffer + gifstream->total_out; gifstream->avail_out = gif->buf_size - gifstream->total_out; /* uncompress it */ err = gif->inflate(gifstream); /* check return value */ if(err != GIF_STREAM_OK && err != GIF_STREAM_END) { _XmHTMLWarning(__WFUNC__(NULL, "_PLC_GIF_ScanlineProc"), "Error while decoding GIF image %s:\n external decoder " "failed: %s\n", plc->url, gifstream->msg ? gifstream->msg : "(unknown error)"); plc->plc_status = PLC_ABORT; return; } /* this many uncompressed bytes available now */ gif->byte_count = gifstream->total_out; /* convert input data to raw image data */ done = DoImage((PLCImage*)gif, gif->buffer); /* and break if inflate has finished uncompressing our data. */ if(err == GIF_STREAM_END || done == True) plc->obj_funcs_complete = True; #ifndef PLC_WORKPROCS } /* This test will be false if we made a new data request */ while(bytes_avail == plc->left); #endif /* !PLC_WORKPROCS */ } } /***** * Name: _PLC_GIF_Destructor * Return Type: void * Description: GIF PLC virtual destructor method. * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_GIF_Destructor(PLC *plc) { PLCImageGIF *gif; _XmHTMLDebug(15, ("plc.c: _PLC_GIF_Destructor for %s\n", plc->url)); gif = &(plc->object->plc_gif_image); if(gif->external_codec) { /* we have an external decoder, tell it to destroy itself */ gif->gstream->state = GIF_STREAM_END; gif->gstream->next_out = NULL; gif->gstream->avail_out = 0; gif->gstream->next_in = NULL; gif->gstream->avail_in = 0; /* call external decoder */ (void)gif->inflate(gif->gstream); /* destroy the stream object */ free(gif->gstream); } else { /* destroy the stream object */ LZWStreamDestroy(gif->lstream); } } #if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ) /***** * Name: _PLC_GZF_Init * Return Type: void * Description: image initializer for GZF images * In: * plc: current PLC * Returns: * nothing. * Note: * As GZF images are GIF images in which only the compressed raster data * differs, we can simply use the gif initializer for this. *****/ void _PLC_GZF_Init(PLC *plc) { _XmHTMLDebug(15, ("plc.c: _PLC_GZF_Init for %s\n", plc->url)); _PLC_GIF_Init(plc); if(plc->plc_status == PLC_ACTIVE) { PLCImageGZF *gzf; /* get GZF image and zstream object */ gzf = &(plc->object->plc_gzf_image); /* this is a GZF image */ gzf->info->type = IMAGE_GZF; /* initialize z_stream */ gzf->zstream.zalloc = Z_NULL; gzf->zstream.zfree = Z_NULL; gzf->zstream.opaque = Z_NULL; /* abort if this fails: incorrect library version/out of memory */ if((inflateInit(&gzf->zstream)) != Z_OK) { _XmHTMLWarning(__WFUNC__(gzf->owner, "_PLC_GZF_Init"), "inflateInit failed: %s\n", gzf->zstream.msg ? gzf->zstream.msg :"(unknown zlib " "error)"); plc->plc_status = PLC_ABORT; return; } /* allocate room for uncompressed data */ gzf->data = (Byte*)calloc(gzf->buf_size + 1, sizeof(Byte)); gzf->data_size = gzf->buf_size; gzf->data_pos = 0; /* don't allocate clipmask yet, it's done in the plc code */ } } /***** * Name: _PLC_GZF_ScanlineProc * Return Type: void * Description: GZF scanline processor (decompresses data and orders it) * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_GZF_ScanlineProc(PLC *plc) { int err, bytes_avail; PLCImageGZF *gzf; Boolean done = False; _XmHTMLDebug(15, ("plc.c: _PLC_GZF_ScanlineProc for %s\n", plc->url)); gzf = &(plc->object->plc_gzf_image); /* bytes left in current input buffer */ bytes_avail = plc->left; #ifndef PLC_WORKPROCS do { #endif /* !PLC_WORKPROCS */ /***** * get a new block of compressed data. This will automatically make * a new request for input data when there isn't enough data left * in the current input buffer. *****/ if((gzf->zstream.avail_in = _PLCGetDataBlock(plc, gzf->zbuf)) == 0) { /* * if plc_status == PLC_ACTIVE, we have a zero data block which * indicates the raster data end. For now we just consider this * as the end signal and return without further processing. * For gif animations this would be the place to move to the next * frame. */ return; /* aborted, date ended or plc suspended */ } gzf->zstream.next_in = gzf->zbuf; /* bytes left in current buffer (+1 for block header) */ bytes_avail -= (gzf->zstream.avail_in + 1); /* adjust output buffer */ gzf->zstream.next_out = gzf->buffer + gzf->zstream.total_out; gzf->zstream.avail_out = gzf->buf_size - gzf->zstream.total_out; /* uncompress it */ err = inflate(&gzf->zstream, Z_PARTIAL_FLUSH); /* check return value */ if(err != Z_OK && err != Z_STREAM_END) { _XmHTMLWarning(__WFUNC__(NULL, "_PLC_GZF_ScanlineProc"), "Error while decoding image %s:\n inflate failed: %s\n", plc->url, gzf->zstream.msg); plc->plc_status = PLC_ABORT; return; } /* this many uncompressed bytes available now */ gzf->byte_count = gzf->zstream.total_out; /* convert input data to raw image data */ done = DoImage((PLCImage*)gzf, gzf->buffer); /* and break if inflate has finished uncompressing our data. */ if(err == Z_STREAM_END || done == True) plc->obj_funcs_complete = True; #ifndef PLC_WORKPROCS } /* This test will be false if we made a new data request */ while(bytes_avail == plc->left); #endif /* !PLC_WORKPROCS */ } /***** * Name: _PLC_GZF_Destructor * Return Type: void * Description: GZF PLC virtual destructor method. * In: * plc: current PLC * Returns: * nothing. * Note: * GZF images are virtually equal to GIF images, so we can safely use the * GIF destructor method to destroy a GZF-plc. *****/ void _PLC_GZF_Destructor(PLC *plc) { PLCImageGZF *gzf; _XmHTMLDebug(15, ("plc.c: _PLC_GZF_Destructor for %s\n", plc->url)); gzf = &(plc->object->plc_gzf_image); /* clean up zlib */ if((inflateEnd(&gzf->zstream)) != Z_OK) { _XmHTMLWarning(__WFUNC__(NULL, "_PLC_GZF_Destructor"), "inflateEnd failed: %s\n", gzf->zstream.msg); } } #else /***** * Dummy functions when GZF support has not been selected. * All will abort the PLC when a GZF image is encountered (although the * dummy ScanlineProc and Destructor will *never* be used). *****/ void _PLC_GZF_Init(PLC *plc) { plc->plc_status = PLC_ABORT; } void _PLC_GZF_ScanlineProc(PLC *plc) { plc->plc_status = PLC_ABORT; } void _PLC_GZF_Destructor(PLC *plc) { plc->plc_status = PLC_ABORT; } #endif /* !HAVE_LIBPNG && !HAVE_LIBZ */ /********* *** Private functions used by both GIF and GZF progressive loaders. *********/ /***** * Name: DoExtension * Return Type: int * Description: process a gif extension block * In: * plc: current PLC * label: extension block identifier; * Returns: * True when extension was valid. False otherwise. *****/ static Boolean DoExtension(PLC *plc, int label) { static char buf[256]; PLCImageGIF *gif; gif = &(plc->object->plc_gif_image); _XmHTMLDebug(15, ("plc.c: DoExtension for %s\n", plc->url)); switch(label) { case 0x01: /* Plain Text Extension */ break; case 0xff: /* Application Extension */ /* * Netscape Looping extension * Get first block */ (void)_PLCGetDataBlock(plc, (Byte*)buf); if(!(strncmp((char*)buf, "NETSCAPE2.0", 11))) { if((_PLCGetDataBlock(plc, (Byte*)buf)) <= 0) return(False); /* corrupt animation/end of data */ } break; case 0xfe: /* Comment Extension */ while(_PLCGetDataBlock(plc, (Byte*) buf) > 0); return(True); case 0xf9: /* Graphic Control Extension */ (void)_PLCGetDataBlock(plc, (Byte*) buf); if ((buf[0] & 0x1) != 0) { PLCImageGIF *gif; gif = &(plc->object->plc_gif_image); gif->bg_pixel = (int)((Byte)buf[3]); gif->transparency = XmIMAGE_TRANSPARENCY_BG; } while(_PLCGetDataBlock(plc, (Byte*) buf) > 0); return(True); default: break; } while(_PLCGetDataBlock(plc, (Byte*) buf) > 0); return(True); } /***** * Name: ReadColormap * Return Type: Boolean * Description: reads the colormap of a GIF/GZF image. * In: * plc: current PLC * gif: plc image object. * Returns: * True when colormap has been read, False if not. *****/ static Boolean ReadColormap(PLC *plc, PLCImageGIF *gif) { register int i; Byte rgb[3]; _XmHTMLDebug(15, ("plc.c: ReadColormap for %s\n", plc->url)); /* read it */ for(i = 0; i < gif->cmapsize; ++i) { if(!_PLCReadOK(plc, rgb, sizeof(rgb))) return(False); gif->cmap[i].red = rgb[0]; gif->cmap[i].green = rgb[1]; gif->cmap[i].blue = rgb[2]; } return(True); } /***** * Name: DoImage * Return Type: Boolean * Description: transforms raw gif raster data to final gif image data. * In: * gif: PLC image object data; * input: raw raster data. * Returns: * True when all raw image data has been processed, False if not. *****/ static Boolean DoImage(PLCImage *gif, Byte *input) { Boolean done = False; Byte *inPtr, *outPtr; register int i; /* convert data to actual image data */ if(gif->npasses > 1) /* image is interlaced */ { int pass, stride, xpos, ypos, skip; int nfill; Byte *src, *dest, *data; register int j; /* last known position in uncompressed data stream */ inPtr = input; /* Interlaced images are always fully recomputed */ data = gif->data; /* image data */ ypos = 0; /* current scanline */ pass = 0; /* current interlacing pass */ stride = 8; /* interlacing data stride */ skip = gif->width; /* initial data index */ xpos = 0; for(i = j = 0; i < gif->height && j < gif->byte_count; i++, j += skip) { if(ypos < gif->height) { outPtr = &data[skip * ypos]; for(xpos = 0; xpos < skip; xpos++) *outPtr++ = *inPtr++; } if((ypos += stride) >= gif->height) { if(pass++ > 0) stride /= 2; ypos = stride / 2; } } /* * tidy things up by filling in empty spaces between the * interlacing passes. When we have completed a pass we have * data available for expanding the entire image. */ if(pass) { gif->prev_pos = 0; gif->data_pos = gif->data_size; ypos = gif->height; } else /* current image data ends at this position */ gif->data_pos = ypos * gif->width; /***** * This piece of code will copy the current line to a number of * following, unfilled lines. * For pass 0, it will copy a line 7 times (stride = 8), for pass * 1 it will copy a line 3 times (stride = 4), and for pass 2 it * will copy a line only once (stride = 2). The last pass is a * no-op. *****/ for(i = 0; i < ypos; i += stride) { src = &data[skip * i]; for(nfill = 1; nfill < stride && i + nfill < gif->height; nfill++) { dest = &data[skip * (i + nfill)]; memmove(dest, src, skip); } } /* Break out if we have performed all passes and processed all data */ done = (pass == gif->npasses && j >= gif->data_size); } else { int max_byte; /* last known position in uncompressed data stream */ inPtr = input + gif->prev_pos; /* last known position in image data stream */ outPtr= gif->data + gif->prev_pos; /* last byte to use */ max_byte = (int)(gif->byte_count/gif->width)*gif->width; /* direct data copy */ for(i = gif->prev_pos; i < max_byte; i++) *outPtr++ = *inPtr++; /* last full scanline */ gif->data_pos = max_byte; /* Break out if we have performed all passes and processed all data */ done = (gif->data_pos >= gif->data_size); } return(done); }