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