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