1 #ifndef production
2 static char rcsId[]="$Header$";
3 #endif
4 /*****
5 * readGIFplc.c :		GIF/GZF progressive loading interfaces
6 *
7 * This file Version	$Revision$
8 *
9 * Creation date:		Fri Jun 13 16:31:35 GMT+0100 1997
10 * Last modification: 	$Date$
11 * By:					$Author$
12 * Current State:		$State$
13 *
14 * Author:				newt
15 *
16 * Copyright (C) 1994-1997 by Ripley Software Development
17 * All Rights Reserved
18 *
19 * This file is part of the XmHTML Widget Library.
20 *
21 * This library is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Library General Public
23 * License as published by the Free Software Foundation; either
24 * version 2 of the License, or (at your option) any later version.
25 *
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29 * Library General Public License for more details.
30 *
31 * You should have received a copy of the GNU Library General Public
32 * License along with this library; if not, write to the Free
33 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 *****/
36 /*****
37 * ChangeLog
38 * $Log$
39 * Revision 1.1  2011/06/30 16:10:38  rwcox
40 * Cadd
41 *
42 * Revision 1.6  1998/04/27 07:02:56  newt
43 * tka stuff
44 *
45 * Revision 1.5  1998/04/04 06:28:32  newt
46 * XmHTML Beta 1.1.3
47 *
48 * Revision 1.4  1997/10/23 00:25:21  newt
49 * XmHTML Beta 1.1.0 release
50 *
51 * Revision 1.3  1997/08/31 17:41:48  newt
52 * cast fix on LZWStreamUncompress. Debug level change.
53 *
54 * Revision 1.2  1997/08/30 01:29:39  newt
55 * _XmHTMLWarning proto and LZWStream changes.
56 *
57 * Revision 1.1  1997/08/01 12:51:55  newt
58 * Initial Revision
59 *
60 *****/
61 #ifdef HAVE_CONFIG_H
62 #include "config.h"
63 #endif
64 
65 #include <stdio.h>
66 #include <stdlib.h>
67 
68 #if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ)
69 #include <unistd.h>	/* unlink */
70 #include <zlib.h>
71 #endif
72 
73 #include "toolkit.h"
74 #include XmHTMLPrivateHeader
75 #include "LZWStream.h"
76 #include "plc.h"
77 
78 /*** External Function Prototype Declarations ***/
79 
80 /*** Public Variable Declarations ***/
81 
82 /*** Private Datatype Declarations ****/
83 #define INTERLACE		0x40
84 #define LOCALCOLORMAP	0x80
85 
86 /*** Private Function Prototype Declarations ****/
87 #define BitSet(byte, bit)		(((byte) & (bit)) == (bit))
88 #define LM_to_uint(a,b)			((((b)&0xFF) << 8) | ((a)&0xFF))
89 
90 static Boolean DoExtension(PLC *plc, int label);
91 static Boolean ReadColormap(PLC *plc, PLCImageGIF *gif);
92 static Boolean DoImage(PLCImage *gif, Byte *input);
93 
94 /*** Private Variable Declarations ***/
95 
96 /*****
97 * Name: 		_PLC_GIF_Init
98 * Return Type: 	void
99 * Description: 	image initializer for GIF images
100 * In:
101 *	plc:		current PLC
102 * Returns:
103 *	Nothing but PLC is updated.
104 * Note:
105 *	As this routine must be fully re-entrant, it needs a lot of checks
106 *	to make sure we have the data we want fully available.
107 *	The drawback is that if we are being suspended while doing this
108 *	initialization, everything must be reset and repeated the next time
109 *	this routine is called.
110 *****/
111 void
_PLC_GIF_Init(PLC * plc)112 _PLC_GIF_Init(PLC *plc)
113 {
114 	Byte buf[16], c;
115 	PLCImageGIF *gif;
116 
117 	gif = &(plc->object->plc_gif_image);
118 
119 	_XmHTMLDebug(15, ("plc.c: _PLC_GIF_Init for %s\n", plc->url));
120 
121 	/* this plc is active */
122 	plc->plc_status = PLC_ACTIVE;
123 
124 	/*****
125 	* When this routine is called, the init method of this PLC has already
126 	* been called to determine the type of this PLC Image object. Therefore
127 	* we already have data available and we need to rewind the input buffer
128 	* back to the beginning.
129 	*****/
130 	_PLCRewindInputBuffer(plc);
131 
132 	/* we know this is a gif image, so skip magic */
133 	gif->info->type = IMAGE_GIF;
134 	(void)_PLCReadOK(plc, buf, 6);
135 
136 	/* read logical screen descriptor */
137 	(void)_PLCReadOK(plc, buf, 7);
138 
139 	/* image dimensions */
140 	gif->width   = LM_to_uint(buf[0],buf[1]);
141 	gif->height  = LM_to_uint(buf[2],buf[3]);
142 
143 	/* set colorspace and allocate a colormap */
144 	gif->colorclass = XmIMAGE_COLORSPACE_INDEXED;
145 	gif->cmapsize   = 2<<(buf[4]&0x07);
146 
147 	/*
148 	* We may have been called before (but returned 'cause not enough data
149 	* was available).
150 	*/
151 	if(gif->cmap == NULL)
152 		gif->cmap = (XCOLOR*)calloc(gif->cmapsize, sizeof(XCOLOR));
153 
154 	/* image is initially fully opaque */
155 	gif->transparency = XmNONE;
156 	gif->bg_pixel = -1;
157 
158 	/*
159 	* Incoming data buffer. This is *way* too much as the incoming data
160 	* will be compressed (but it does make sure there is enough room)
161 	*/
162 	gif->buf_size   = gif->width*gif->height;
163 	gif->buf_pos    = 0;	/* current pos in data received so far */
164 	gif->byte_count = 0;	/* size of data received so far */
165 	if(gif->buffer == NULL)
166 		gif->buffer = (Byte*)calloc(gif->buf_size + 1, sizeof(Byte));
167 
168 	/* check if a global colormap is available */
169 	if(BitSet(buf[4], LOCALCOLORMAP))
170 	{
171 		if(!(ReadColormap(plc, gif)))
172 		{
173 			/* premature end of data. */
174 			if(plc->plc_data_status == STREAM_END)
175 			{
176 				_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
177 					XMHTML_MSG_106, plc->url, "global");
178 				plc->plc_status = PLC_ABORT;
179 			}
180 			return;	/* no global colormap! */
181 		}
182 	}
183 
184 	/* process all extensions */
185 	c = 0;
186 	while(c != ',')
187 	{
188 		if(!_PLCReadOK(plc, &c, 1))
189 			return;
190 
191 		if (c == ';') /* GIF terminator */
192 		{
193 			_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
194 				XMHTML_MSG_107, plc->url, "pixel data");
195 			plc->plc_status = PLC_ABORT;
196 			return;
197 		}
198 
199 		if(c == '!') /* Extension */
200 		{
201 			if(!_PLCReadOK(plc,&c,1))
202 			{
203 				if(plc->plc_data_status == STREAM_END)
204 				{
205 					_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
206 						XMHTML_MSG_107, plc->url, "extension block type");
207 					plc->plc_status = PLC_ABORT;
208 				}
209 				return;
210 			}
211 			if(!(DoExtension(plc, c)))
212 			{
213 				if(plc->plc_data_status == STREAM_END)
214 				{
215 					_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
216 						XMHTML_MSG_107, plc->url, "extension block");
217 					plc->plc_status = PLC_ABORT;
218 				}
219 				return;
220 			}
221 			continue;
222 		}
223 		if (c != ',')
224 			continue; /* Not a valid start character */
225 	}
226 	/* get image descriptor */
227 	if(!_PLCReadOK(plc, buf, 9))
228 		return;
229 
230 	/* see if we are to use a local colormap */
231 	if(BitSet(buf[8], LOCALCOLORMAP))
232 	{
233 		/* local colormap size */
234 		gif->ncolors = 1<<((buf[8]&0x07)+1);
235 
236 		/* do we also have a glocal colormap? */
237 		if(gif->cmap)
238 			free(gif->cmap);
239 
240 		gif->cmapsize = gif->ncolors;
241 		gif->cmap = (XCOLOR*)calloc(gif->cmapsize, sizeof(XCOLOR));
242 
243 		if(!(ReadColormap(plc, gif)))
244 		{
245 			/* premature end of data. */
246 			if(plc->plc_data_status == STREAM_END)
247 			{
248 				_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
249 					XMHTML_MSG_106, plc->url, "local");
250 				plc->plc_status = PLC_ABORT;
251 			}
252 			return;	/* no global colormap! */
253 		}
254 	}
255 	gif->ncolors = gif->cmapsize;
256 
257 	/* sanity check: image *must* have a colormap */
258 	if(gif->cmap == NULL)
259 	{
260 		_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
261 			XMHTML_MSG_106, plc->url, "global or local");
262 		plc->plc_status = PLC_ABORT;
263 		return;	/* no global colormap! */
264 	}
265 
266 	/* image depth (= codeSize in GIF images, unused in GZF images) */
267 	if(!(_PLCReadOK(plc, &c, 1)))
268 		return;
269 
270 	gif->depth = (int)(c & 0xff);
271 
272 	/* check interlacing */
273 	if(BitSet(buf[8], INTERLACE))
274 	{
275 		/* interlaced gifs require 4 passes and use an initial rowstride of 8 */
276 		gif->npasses = 4;
277 		gif->stride  = 8;
278 	}
279 	else
280 	{
281 		/* regular gif, 1 pass will get us the entire image */
282 		gif->npasses = 1;
283 		gif->stride  = 0;
284 	}
285 	gif->curr_pass = 0;
286 	gif->curr_scanline = 0;
287 
288 	/*****
289 	* This routine is also used for GZF images, so before initializing
290 	* the LZWStream object we need to make sure we have been called for
291 	* a true GIF image.
292 	*****/
293 	if(plc->object->type == plcGIF)
294 	{
295 		XmHTMLWidget html  = plc->object->plc_any.owner;
296 
297 		if(HTML_ATTR(gif_proc) != NULL)
298 		{
299 			gif->external_codec = True;
300 			gif->inflate = HTML_ATTR(gif_proc);
301 			if((gif->gstream =
302 				(XmHTMLGIFStream*)malloc(sizeof(XmHTMLGIFStream))) == NULL)
303 			{
304 				/* out of memory, too bad then */
305 				_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
306 					XMHTML_MSG_113, plc->url, sizeof(XmHTMLGIFStream));
307 				plc->plc_status = PLC_ABORT;
308 				return;
309 			}
310 
311 			/* initialize GIFStream object */
312 			memset(gif->gstream, 0, sizeof(XmHTMLGIFStream));
313 
314 			gif->gstream->codesize  = (int)c;
315 			gif->gstream->state     = GIF_STREAM_INIT;
316 			gif->gstream->next_out  = gif->buffer;
317 			gif->gstream->avail_out = gif->buf_size + 1;
318 			gif->gstream->is_progressive = True;
319 			/*
320 			* and call external decoder so it can initialize its own data
321 			* structures
322 			*/
323 			if((gif->inflate(gif->gstream)) != GIF_STREAM_OK)
324 			{
325 				if(gif->gstream->msg != NULL)
326 				{
327 					_XmHTMLWarning(__WFUNC__(gif->owner, "_PLC_GIF_Init"),
328 						XMHTML_MSG_109, plc->url,
329 						gif->gstream->msg ? gif->gstream->msg :
330 						"(unknown error)");
331 				}
332 				/* external decoder initalization failed, abort and return */
333 				plc->plc_status = PLC_ABORT;
334 				return;
335 			}
336 			gif->gstream->state = GIF_STREAM_OK;
337 		}
338 		else
339 		{
340 			/* initialize local data buffer */
341 			gif->ib.file   = plc->url;
342 			gif->ib.buffer = gif->buffer;
343 			gif->ib.size   = 0;
344 			gif->ib.next   = 0;
345 			gif->ib.type   = IMAGE_GIF;
346 			gif->ib.depth  = gif->depth;
347 			gif->ib.may_free = False;
348 
349 			/* initialize LZWStream object */
350 			if(gif->lstream == NULL)
351 			{
352 				if((gif->lstream = LZWStreamCreate(&(gif->ib),
353 									html->html.zCmd)) == NULL)
354 				{
355 					/* couldn't create stream, abort and return */
356 					plc->plc_status = PLC_ABORT;
357 					return;
358 				}
359 				/* set read functions */
360 				gif->lstream->readOK  = _XmHTMLGifReadOK;
361 				gif->lstream->getData = _XmHTMLGifGetDataBlock;
362 			}
363 			/* first byte in buffer is gif codesize */
364 			gif->ib.buffer[0] = c;
365 			gif->ib.size = 1;
366 		}
367 		/* allocate room for final image data */
368 		if(gif->data == NULL)
369 		{
370 			gif->data = (Byte*)calloc(gif->buf_size + 1, sizeof(Byte));
371 
372 			/* don't allocate clipmask yet, it's done in the plc code */
373 		}
374 		gif->data_size = gif->buf_size;
375 		gif->data_pos  = 0;
376 	}
377 
378 	/* object has been initialized */
379 	plc->initialized = True;
380 
381 	plc->curr_obj_func = 0;	/* move to GIF scanline reader */
382 	return;
383 }
384 
385 /*****
386 * Name: 		_PLC_GIF_ScanlineProc
387 * Return Type: 	void
388 * Description: 	GZF scanline processor (decompresses data and orders it)
389 * In:
390 *	plc:		current PLC
391 * Returns:
392 *	nothing.
393 *****/
394 void
_PLC_GIF_ScanlineProc(PLC * plc)395 _PLC_GIF_ScanlineProc(PLC *plc)
396 {
397 	Byte *input = NULL;
398 	PLCImageGIF *gif;
399 	Boolean done = False;
400 	int bytes_avail;
401 
402 	_XmHTMLDebug(15, ("plc.c: _PLC_GIF_ScanlineProc for %s\n", plc->url));
403 
404 	gif = &(plc->object->plc_gif_image);
405 
406 	/*****
407 	* We can choose between two types of LZW decoders: the internal one or
408 	* an externally installed progressive decoder.
409 	* The internal decoder is fairly efficient for direct gif loading, but is
410 	* *HUGELY* slow for progressive gif loading: it forks of uncompress (or
411 	* gzip) each time LZWStreamUncompress is called. Therefore we collect
412 	* all data and call the internal decoder when all image data has been
413 	* received.
414 	* When an external gif decoder has been installed, we just proceed in the
415 	* same way as with the GZF scanline procedure: decode a chunk of data
416 	* each time and allow it to be transfered to the display.
417 	*****/
418 	if(gif->external_codec == False)
419 	{
420 		int len;
421 		Boolean have_all = False;
422 		bytes_avail = plc->left;
423 
424 #ifndef PLC_WORKPROCS
425 		do
426 		{
427 #endif	/* !PLC_WORKPROCS */
428 
429 			/*****
430 			* get a new block of compressed data. This will automatically make
431 			* a new request for input data when there isn't enough data left
432 			* in the current input buffer.
433 			*****/
434 			if((len = _PLCGetDataBlock(plc, gif->gbuf)) != 0)
435 			{
436 				/* store block size */
437 				gif->ib.buffer[gif->ib.size++] = (Byte)len;
438 
439 				/* append newly received data */
440 				(void)memcpy(gif->ib.buffer + gif->ib.size,
441 					gif->gbuf, len);
442 
443 				/* new buffer size */
444 				gif->ib.size += len;
445 
446 				/* bytes left in current buffer (+1 for block header) */
447 				bytes_avail -= (len + 1);
448 
449 				/* prevent image transfer function from doing anything */
450 				gif->prev_pos = gif->data_pos = 0;
451 			}
452 			else
453 			{
454 				if(plc->plc_status == PLC_SUSPEND ||
455 					plc->plc_status == PLC_ABORT)
456 					return;
457 				/*
458 				* if plc_status == PLC_ACTIVE, we have a zero data block which
459 				* indicates the raster data end. For now we just consider this
460 				* as the end signal and start decoding the raster data.
461 				* For gif animations this would be the place to move to the
462 				* next frame.
463 				*/
464 				have_all = True;
465 
466 				/* plug in a zero length data block and GIF terminator */
467 				gif->ib.buffer[gif->ib.size++] = (Byte)0;
468 				gif->ib.buffer[gif->ib.size++] = (Byte)1;
469 				gif->ib.buffer[gif->ib.size++] = (Byte)';';
470 
471 			}
472 #ifndef PLC_WORKPROCS
473 		}
474 		/* This test will be false if we made a new data request */
475 		while(bytes_avail == plc->left && have_all == False);
476 #endif	/* !PLC_WORKPROCS */
477 
478 		/* we have got all image data, now decode it */
479 		if(have_all)
480 		{
481 			/* rewind image buffer */
482 			gif->ib.next = 0;
483 
484 			/* now (re-)initialize the stream object */
485 			if((LZWStreamInit(gif->lstream)) <= 0)
486 			{
487 				/* this is an error */
488 				_XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner,
489 					"_PLC_GIF_ScanlineProc"), gif->lstream->err_msg);
490 				plc->plc_status = PLC_ABORT;
491 				return;
492 			}
493 
494 			/* convert data */
495 			LZWStreamConvert(gif->lstream);
496 
497 			/* get uncompressed data */
498 			if((input = LZWStreamUncompress(gif->lstream,
499 				(int*)&gif->byte_count)) == NULL)
500 			{
501 				_XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner,
502 					"_PLC_GIF_ScanlineProc"), gif->lstream->err_msg);
503 				/* rather fatal... */
504 				_XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner,
505 					"_PLC_GIF_ScanlineProc"), XMHTML_MSG_114, plc->url);
506 				plc->plc_status = PLC_ABORT;
507 				return;
508 			}
509 
510 			/* convert input data to raw image data */
511 			(void)DoImage((PLCImage*)gif, input);
512 
513 			/* free input buffer */
514 			free(input);
515 
516 			/* we are finished here */
517 			plc->obj_funcs_complete = True;
518 		}
519 	}
520 	else
521 	{
522 		/*****
523 		* External GIF decoder installed, proceeds in the same way as the
524 		* GZF decoder, except that now the external decoder is called
525 		* instead of the zlib routines.
526 		*****/
527 		XmHTMLGIFStream *gifstream = gif->gstream;
528 		int err;
529 
530 		bytes_avail = plc->left;
531 
532 #ifndef PLC_WORKPROCS
533 		do
534 		{
535 #endif	/* !PLC_WORKPROCS */
536 			/*****
537 			* get a new block of compressed data. This will automatically make
538 			* a new request for input data when there isn't enough data left
539 			* in the current input buffer.
540 			*****/
541 			if((gifstream->avail_in =
542 					_PLCGetDataBlock(plc, gif->gbuf)) == 0)
543 			{
544 				if(plc->plc_status == PLC_SUSPEND ||
545 					plc->plc_status == PLC_ABORT)
546 					return;
547 				/*
548 				* if plc_status == PLC_ACTIVE, we have a zero data block which
549 				* indicates the raster data end. Put in a 0 length data
550 				* block, the gif terminator, set the gifstream state so the
551 				* caller knows to wrap things up and proceed.
552 				*/
553 				gif->gbuf[0] = (Byte)0;
554 				gif->gbuf[1] = (Byte)1;
555 				gif->gbuf[2] = (Byte)';';
556 				gifstream->avail_in = 3;
557 				gifstream->state = GIF_STREAM_FINAL;
558 			}
559 
560 			gifstream->next_in = gif->gbuf;
561 
562 			/* bytes left in current buffer (+1 for block header) */
563 			bytes_avail -= (gifstream->avail_in + 1);
564 
565 			/* adjust output buffer */
566 			gifstream->next_out  = gif->buffer + gifstream->total_out;
567 			gifstream->avail_out = gif->buf_size - gifstream->total_out;
568 
569 			/* uncompress it */
570 			err = gif->inflate(gifstream);
571 
572 			/* check return value */
573 			if(err != GIF_STREAM_OK && err != GIF_STREAM_END)
574 			{
575 				_XmHTMLWarning(__WFUNC__(NULL, "_PLC_GIF_ScanlineProc"),
576 					XMHTML_MSG_115, plc->url,
577 					gifstream->msg ? gifstream->msg : "(unknown error)");
578 				plc->plc_status = PLC_ABORT;
579 				return;
580 			}
581 
582 			/* this many uncompressed bytes available now */
583 			gif->byte_count = gifstream->total_out;
584 
585 			/* convert input data to raw image data */
586 			done = DoImage((PLCImage*)gif, gif->buffer);
587 
588 			/* and break if inflate has finished uncompressing our data. */
589 			if(err == GIF_STREAM_END || done == True)
590 				plc->obj_funcs_complete = True;
591 #ifndef PLC_WORKPROCS
592 		}
593 		/* This test will be false if we made a new data request */
594 		while(bytes_avail == plc->left);
595 #endif	/* !PLC_WORKPROCS */
596 	}
597 }
598 
599 /*****
600 * Name: 		_PLC_GIF_Destructor
601 * Return Type: 	void
602 * Description: 	GIF PLC virtual destructor method.
603 * In:
604 *	plc:		current PLC
605 * Returns:
606 *	nothing.
607 *****/
608 void
_PLC_GIF_Destructor(PLC * plc)609 _PLC_GIF_Destructor(PLC *plc)
610 {
611 	PLCImageGIF *gif;
612 
613 	_XmHTMLDebug(15, ("plc.c: _PLC_GIF_Destructor for %s\n", plc->url));
614 
615 	gif = &(plc->object->plc_gif_image);
616 
617 	if(gif->external_codec)
618 	{
619 		/* we have an external decoder, tell it to destroy itself */
620 		gif->gstream->state     = GIF_STREAM_END;
621 		gif->gstream->next_out  = NULL;
622 		gif->gstream->avail_out = 0;
623 		gif->gstream->next_in   = NULL;
624 		gif->gstream->avail_in  = 0;
625 
626 		/* call external decoder */
627 		(void)gif->inflate(gif->gstream);
628 
629 		/* destroy the stream object */
630 		free(gif->gstream);
631 	}
632 	else
633 	{
634 		/* destroy the stream object */
635 		LZWStreamDestroy(gif->lstream);
636 	}
637 }
638 
639 #if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ)
640 /*****
641 * Name: 		_PLC_GZF_Init
642 * Return Type: 	void
643 * Description: 	image initializer for GZF images
644 * In:
645 *	plc:		current PLC
646 * Returns:
647 *	nothing.
648 * Note:
649 *	As GZF images are GIF images in which only the compressed raster data
650 *	differs, we can simply use the gif initializer for this.
651 *****/
652 void
_PLC_GZF_Init(PLC * plc)653 _PLC_GZF_Init(PLC *plc)
654 {
655 	_XmHTMLDebug(15, ("plc.c: _PLC_GZF_Init for %s\n", plc->url));
656 
657 	_PLC_GIF_Init(plc);
658 
659 	if(plc->plc_status == PLC_ACTIVE)
660 	{
661 		PLCImageGZF *gzf;
662 
663 		/* get GZF image and zstream object */
664 		gzf = &(plc->object->plc_gzf_image);
665 
666 		/* this is a GZF image */
667 		gzf->info->type = IMAGE_GZF;
668 
669 		/* initialize z_stream */
670 		gzf->zstream.zalloc = Z_NULL;
671 		gzf->zstream.zfree  = Z_NULL;
672 		gzf->zstream.opaque = Z_NULL;
673 
674 		/* abort if this fails: incorrect library version/out of memory */
675 		if((inflateInit(&gzf->zstream)) != Z_OK)
676 		{
677 			_XmHTMLWarning(__WFUNC__(gzf->owner, "_PLC_GZF_Init"),
678 				XMHTML_MSG_108, plc->url, "Init",
679 				gzf->zstream.msg ? gzf->zstream.msg :"(unknown zlib error)");
680 			plc->plc_status = PLC_ABORT;
681 			return;
682 		}
683 		/* allocate room for uncompressed data */
684 		gzf->data = (Byte*)calloc(gzf->buf_size + 1, sizeof(Byte));
685 		gzf->data_size = gzf->buf_size;
686 		gzf->data_pos  = 0;
687 
688 		/* don't allocate clipmask yet, it's done in the plc code */
689 	}
690 }
691 
692 /*****
693 * Name: 		_PLC_GZF_ScanlineProc
694 * Return Type: 	void
695 * Description: 	GZF scanline processor (decompresses data and orders it)
696 * In:
697 *	plc:		current PLC
698 * Returns:
699 *	nothing.
700 *****/
701 void
_PLC_GZF_ScanlineProc(PLC * plc)702 _PLC_GZF_ScanlineProc(PLC *plc)
703 {
704 	int err, bytes_avail;
705 	PLCImageGZF *gzf;
706 	Boolean done = False;
707 
708 	_XmHTMLDebug(15, ("plc.c: _PLC_GZF_ScanlineProc for %s\n", plc->url));
709 
710 	gzf = &(plc->object->plc_gzf_image);
711 
712 	/* bytes left in current input buffer */
713 	bytes_avail = plc->left;
714 
715 #ifndef PLC_WORKPROCS
716 	do
717 	{
718 #endif	/* !PLC_WORKPROCS */
719 		/*****
720 		* get a new block of compressed data. This will automatically make
721 		* a new request for input data when there isn't enough data left
722 		* in the current input buffer.
723 		*****/
724 		if((gzf->zstream.avail_in = _PLCGetDataBlock(plc, gzf->zbuf)) == 0)
725 		{
726 			/*
727 			* if plc_status == PLC_ACTIVE, we have a zero data block which
728 			* indicates the raster data end. For now we just consider this
729 			* as the end signal and return without further processing.
730 			* For gif animations this would be the place to move to the next
731 			* frame.
732 			*/
733 			return;	/* aborted, date ended or plc suspended */
734 		}
735 		gzf->zstream.next_in = gzf->zbuf;
736 
737 		/* bytes left in current buffer (+1 for block header) */
738 		bytes_avail -= (gzf->zstream.avail_in + 1);
739 
740 		/* adjust output buffer */
741 		gzf->zstream.next_out  = gzf->buffer + gzf->zstream.total_out;
742 		gzf->zstream.avail_out = gzf->buf_size - gzf->zstream.total_out;
743 
744 		/* uncompress it */
745 		err = inflate(&gzf->zstream, Z_PARTIAL_FLUSH);
746 
747 		/* check return value */
748 		if(err != Z_OK && err != Z_STREAM_END)
749 		{
750 			_XmHTMLWarning(__WFUNC__(NULL, "_PLC_GZF_ScanlineProc"),
751 				XMHTML_MSG_108, plc->url, "", gzf->zstream.msg);
752 			plc->plc_status = PLC_ABORT;
753 			return;
754 		}
755 
756 		/* this many uncompressed bytes available now */
757 		gzf->byte_count = gzf->zstream.total_out;
758 
759 		/* convert input data to raw image data */
760 		done = DoImage((PLCImage*)gzf, gzf->buffer);
761 
762 		/* and break if inflate has finished uncompressing our data. */
763 		if(err == Z_STREAM_END || done == True)
764 			plc->obj_funcs_complete = True;
765 #ifndef PLC_WORKPROCS
766 	}
767 	/* This test will be false if we made a new data request */
768 	while(bytes_avail == plc->left);
769 #endif	/* !PLC_WORKPROCS */
770 }
771 
772 /*****
773 * Name: 		_PLC_GZF_Destructor
774 * Return Type: 	void
775 * Description: 	GZF PLC virtual destructor method.
776 * In:
777 *	plc:		current PLC
778 * Returns:
779 *	nothing.
780 * Note:
781 *	GZF images are virtually equal to GIF images, so we can safely use the
782 *	GIF destructor method to destroy a GZF-plc.
783 *****/
784 void
_PLC_GZF_Destructor(PLC * plc)785 _PLC_GZF_Destructor(PLC *plc)
786 {
787 	PLCImageGZF *gzf;
788 
789 	_XmHTMLDebug(15, ("plc.c: _PLC_GZF_Destructor for %s\n", plc->url));
790 
791 	gzf = &(plc->object->plc_gzf_image);
792 
793 	/* clean up zlib */
794 	if((inflateEnd(&gzf->zstream)) != Z_OK)
795 	{
796 		_XmHTMLWarning(__WFUNC__(NULL, "_PLC_GZF_Destructor"),
797 			XMHTML_MSG_108, plc->url, "End", gzf->zstream.msg);
798 	}
799 }
800 
801 #else
802 
803 /*****
804 * Dummy functions when GZF support has not been selected.
805 * All will abort the PLC when a GZF image is encountered (although the
806 * dummy ScanlineProc and Destructor will *never* be used).
807 *****/
808 void
_PLC_GZF_Init(PLC * plc)809 _PLC_GZF_Init(PLC *plc)
810 {
811 	plc->plc_status = PLC_ABORT;
812 }
813 
814 void
_PLC_GZF_ScanlineProc(PLC * plc)815 _PLC_GZF_ScanlineProc(PLC *plc)
816 {
817 	plc->plc_status = PLC_ABORT;
818 }
819 
820 void
_PLC_GZF_Destructor(PLC * plc)821 _PLC_GZF_Destructor(PLC *plc)
822 {
823 	plc->plc_status = PLC_ABORT;
824 }
825 
826 #endif	/* !HAVE_LIBPNG && !HAVE_LIBZ */
827 
828 /*********
829 *** Private functions used by both GIF and GZF progressive loaders.
830 *********/
831 
832 /*****
833 * Name: 		DoExtension
834 * Return Type: 	int
835 * Description: 	process a gif extension block
836 * In:
837 *	plc:		current PLC
838 *	label:		extension block identifier;
839 * Returns:
840 *	True when extension was valid. False otherwise.
841 *****/
842 static Boolean
DoExtension(PLC * plc,int label)843 DoExtension(PLC *plc, int label)
844 {
845 	static char	buf[256];
846 	PLCImageGIF *gif;
847 	gif = &(plc->object->plc_gif_image);
848 
849 	_XmHTMLDebug(15, ("plc.c: DoExtension for %s\n", plc->url));
850 
851 	switch(label)
852 	{
853 		case 0x01:		/* Plain Text Extension */
854 			break;
855 		case 0xff:		/* Application Extension */
856 			/*
857 			* Netscape Looping extension
858 			* Get first block
859 			*/
860 			(void)_PLCGetDataBlock(plc, (Byte*)buf);
861 			if(!(strncmp((char*)buf, "NETSCAPE2.0", 11)))
862 			{
863 				if((_PLCGetDataBlock(plc, (Byte*)buf)) <= 0)
864 					return(False); /* corrupt animation/end of data */
865 			}
866 			break;
867 		case 0xfe:		/* Comment Extension */
868 			while(_PLCGetDataBlock(plc, (Byte*) buf) > 0);
869 			return(True);
870 		case 0xf9:		/* Graphic Control Extension */
871 			(void)_PLCGetDataBlock(plc, (Byte*) buf);
872 			if ((buf[0] & 0x1) != 0)
873 			{
874 				PLCImageGIF *gif;
875 				gif = &(plc->object->plc_gif_image);
876 				gif->bg_pixel = (int)((Byte)buf[3]);
877 				gif->transparency = XmIMAGE_TRANSPARENCY_BG;
878 			}
879 
880 			while(_PLCGetDataBlock(plc, (Byte*) buf) > 0);
881 			return(True);
882 		default:
883 			break;
884 	}
885 	while(_PLCGetDataBlock(plc, (Byte*) buf) > 0);
886 
887 	return(True);
888 }
889 
890 /*****
891 * Name: 		ReadColormap
892 * Return Type: 	Boolean
893 * Description: 	reads the colormap of a GIF/GZF image.
894 * In:
895 *	plc:		current PLC
896 *	gif:		plc image object.
897 * Returns:
898 *	True when colormap has been read, False if not.
899 *****/
900 static Boolean
ReadColormap(PLC * plc,PLCImageGIF * gif)901 ReadColormap(PLC *plc, PLCImageGIF *gif)
902 {
903 	register int i;
904 	Byte rgb[3];
905 
906 	_XmHTMLDebug(15, ("plc.c: ReadColormap for %s\n", plc->url));
907 
908 	/* read it */
909 	for(i = 0; i < gif->cmapsize; ++i)
910 	{
911 		if(!_PLCReadOK(plc, rgb, sizeof(rgb)))
912 			return(False);
913 
914 		GETR(gif->cmap[i]) = rgb[0];
915 		GETG(gif->cmap[i]) = rgb[1];
916 		GETB(gif->cmap[i]) = rgb[2];
917 	}
918 	return(True);
919 }
920 
921 /*****
922 * Name: 		DoImage
923 * Return Type: 	Boolean
924 * Description:	transforms raw gif raster data to final gif image data.
925 * In:
926 *	gif:		PLC image object data;
927 *	input:		raw raster data.
928 * Returns:
929 *	True when all raw image data has been processed, False if not.
930 *****/
931 static Boolean
DoImage(PLCImage * gif,Byte * input)932 DoImage(PLCImage *gif, Byte *input)
933 {
934 	Boolean done = False;
935 	Byte *inPtr, *outPtr;
936 	register int i;
937 
938 	/* convert data to actual image data */
939 	if(gif->npasses > 1)	/* image is interlaced */
940 	{
941 		int pass, stride, xpos, ypos, skip;
942 		int nfill;
943 		Byte *src, *dest, *data;
944 		register int j;
945 
946 		/* last known position in uncompressed data stream */
947 		inPtr = input;
948 
949 		/* Interlaced images are always fully recomputed */
950 		data   = gif->data;		/* image data */
951 		ypos   = 0;				/* current scanline */
952 		pass   = 0;				/* current interlacing pass */
953 		stride = 8;				/* interlacing data stride */
954 		skip   = gif->width;	/* initial data index */
955 		xpos   = 0;
956 
957 		for(i = j = 0; i < gif->height && j < gif->byte_count; i++, j += skip)
958 		{
959 			if(ypos < gif->height)
960 			{
961 				outPtr = &data[skip * ypos];
962 				for(xpos = 0; xpos < skip; xpos++)
963 					*outPtr++ = *inPtr++;
964 			}
965 			if((ypos += stride) >= gif->height)
966 			{
967 				if(pass++ > 0)
968 					stride /= 2;
969 				ypos = stride / 2;
970 			}
971 		}
972 		/*
973 		* tidy things up by filling in empty spaces between the
974 		* interlacing passes. When we have completed a pass we have
975 		* data available for expanding the entire image.
976 		*/
977 		if(pass)
978 		{
979 			gif->prev_pos = 0;
980 			gif->data_pos = gif->data_size;
981 			ypos = gif->height;
982 		}
983 		else
984 			/* current image data ends at this position */
985 			gif->data_pos = ypos * gif->width;
986 
987 		/*****
988 		* This piece of code will copy the current line to a number of
989 		* following, unfilled lines.
990 		* For pass 0, it will copy a line 7 times (stride = 8), for pass
991 		* 1 it will copy a line 3 times (stride = 4), and for pass 2 it
992 		* will copy a line only once (stride = 2). The last pass is a
993 		* no-op.
994 		*****/
995 		for(i = 0; i < ypos; i += stride)
996 		{
997 			src = &data[skip * i];
998 			for(nfill = 1; nfill < stride &&
999 				i + nfill < gif->height; nfill++)
1000 			{
1001 				dest = &data[skip * (i + nfill)];
1002 				memmove(dest, src, skip);
1003 			}
1004 		}
1005 		/* Break out if we have performed all passes and processed all data */
1006 		done = (pass == gif->npasses && j >= gif->data_size);
1007 	}
1008 	else
1009 	{
1010 		int max_byte;
1011 
1012 		/* last known position in uncompressed data stream */
1013 		inPtr = input + gif->prev_pos;
1014 		/* last known position in image data stream */
1015 		outPtr= gif->data + gif->prev_pos;
1016 
1017 		/* last byte to use */
1018 		max_byte = (int)(gif->byte_count/gif->width)*gif->width;
1019 
1020 		/* direct data copy */
1021 		for(i = gif->prev_pos; i < max_byte; i++)
1022 			*outPtr++ = *inPtr++;
1023 
1024 		/* last full scanline */
1025 		gif->data_pos = max_byte;
1026 
1027 		/* Break out if we have performed all passes and processed all data */
1028 		done = (gif->data_pos >= gif->data_size);
1029 	}
1030 	return(done);
1031 }
1032 
1033