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