1 /************************************************************************/
2 /*									*/
3 /*  Gif writing basic functionality.					*/
4 /*									*/
5 /*  ORIGIN: "Gif-Lib" - Yet another gif library.			*/
6 /*  Written by:  Gershon Elber, Ver 1.1, Aug. 1990			*/
7 /*  Version 3.0 by Eric S. Raymond (Full GIF89 support)			*/
8 /*									*/
9 /*  Modified to what it is now by Mark de Does				*/
10 /*									*/
11 /************************************************************************/
12 
13 #   include	<stdlib.h>
14 #   include	<stdio.h>
15 #   include	<string.h>
16 #   include	"bm_gif_lib.h"
17 
18 #   include	<appDebugon.h>
19 #   include	<sioBlocked.h>
20 #   include	<sioLzw.h>
21 #   include	<sioEndian.h>
22 
23 /************************************************************************/
24 /*									*/
25 /*  Write a color map to file.						*/
26 /*									*/
27 /************************************************************************/
28 
bmGifWriteColorMap(GifFileType * gft,const GifColorMap * gcm)29 static int bmGifWriteColorMap(	GifFileType *		gft,
30 				const GifColorMap *	gcm )
31     {
32     int			i;
33     const RGB8Color *	rgb8;
34     int			count= 1 << gcm->gcmBitsPerPixel;
35 
36     if  ( gcm->gcmColorCount > count )
37 	{ LLDEB(gcm->gcmColorCount,count);	}
38 
39     rgb8= gcm->gcmColors;
40     for ( i= 0; i < gcm->gcmColorCount && i < count; rgb8++, i++ )
41 	{
42 	if  ( sioOutPutByte( rgb8->rgb8Red, gft->gftSos ) < 0 )
43 	    { return -1;	}
44 	if  ( sioOutPutByte( rgb8->rgb8Green, gft->gftSos ) < 0 )
45 	    { return -1;	}
46 	if  ( sioOutPutByte( rgb8->rgb8Blue, gft->gftSos ) < 0 )
47 	    { return -1;	}
48 	}
49 
50     for ( i= gcm->gcmColorCount; i < count; rgb8++, i++ )
51 	{
52 	if  ( sioOutPutByte( 255, gft->gftSos ) < 0 )
53 	    { return -1;	}
54 	if  ( sioOutPutByte( 255, gft->gftSos ) < 0 )
55 	    { return -1;	}
56 	if  ( sioOutPutByte( 255, gft->gftSos ) < 0 )
57 	    { return -1;	}
58 	}
59 
60     return 0;
61     }
62 
63 /************************************************************************/
64 /*									*/
65 /*  Initialize GIF writing to an OutputStream.				*/
66 /*									*/
67 /*  _GifError is cleared if succesfull.					*/
68 /*									*/
69 /************************************************************************/
70 
EGifOpenFileHandle(SimpleOutputStream * sos)71 GifFileType * EGifOpenFileHandle(	SimpleOutputStream *	sos )
72 {
73     GifFileType *		gft;
74 
75     gft= (GifFileType *)malloc(sizeof(GifFileType));
76     if  ( ! gft )
77 	{ XDEB(gft); return (GifFileType *)0;	}
78 
79     memset( gft, '\0', sizeof(GifFileType) );
80 
81     strncpy( gft->gftVersionString, GIF87_STAMP, 6 )[6]= '\0';
82 
83     gft->gftSos= sos;
84     gft->gftSosBlocked= (SimpleOutputStream *)0;
85     gft->gftSosLzw= (SimpleOutputStream *)0;
86 
87     _GifError = 0;
88 
89     return gft;
90 }
91 
92 /******************************************************************************
93 *   Routine to set current GIF version. All files open for write will be      *
94 * using this version until next call to this routine. Version consists of     *
95 * 3 characters as "87a" or "89a". No test is made to validate the version.    *
96 ******************************************************************************/
97 
bmGifSetVersion(GifFileType * gft,const char * version)98 void bmGifSetVersion(	GifFileType *	gft,
99 			const char *	version )
100 {
101     if  ( strcmp( version, GIF87_STAMP+ 3 )	&&
102 	  strcmp( version, GIF89_STAMP+ 3 )	)
103 	{ SDEB(version); return;	}
104 
105     strncpy( gft->gftVersionString+ 3, version, 3 );
106 }
107 
108 /************************************************************************/
109 /*									*/
110 /*  Write the Screen descriptor to the output.				*/
111 /*									*/
112 /*  This routine should be called before any other EGif calls, I.E	*/
113 /*  immediately after the GIF file openning.				*/
114 /*									*/
115 /*  1)  First write the version prefix into the file.			*/
116 /*  2)  Put the screen descriptor into the file				*/
117 /*  3)  If we have Global color map - dump it also			*/
118 /*									*/
119 /************************************************************************/
120 
EGifPutScreenDesc(GifFileType * gft,int Width,int Height,int bitsPerPixel,int BackGround,const GifColorMap * gcm)121 int EGifPutScreenDesc(	GifFileType *		gft,
122 			int			Width,
123 			int			Height,
124 			int			bitsPerPixel,
125 			int			BackGround,
126 			const GifColorMap *	gcm )
127 {
128     GifScreenDescriptor *	gsd= &(gft->gftScreenDescriptor);
129 
130     if  ( ! gft->gftSos )
131 	{
132 	XDEB(gft->gftSos);
133 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
134 	return GIF_ERROR;
135 	}
136 
137     /*  1  */
138     sioOutPutString( gft->gftVersionString, gft->gftSos );
139 
140     gsd->gsdScreenWide= Width;
141     gsd->gsdScreenHigh= Height;
142     gsd->gsdScreenBitsPerPixel= bitsPerPixel;
143     gsd->gsdScreenBackgroundColor= BackGround;
144     gsd->gsdScreenAspectRatio= 0;
145 
146     bmGifInitGifColorMap( &(gsd->gsdScreenColorMap) );
147 
148     if  ( gcm )
149 	{ gsd->gsdScreenColorMap= *gcm;	}
150 
151     /*  2  */
152     sioEndianPutLeInt16( gsd->gsdScreenWide, gft->gftSos );
153     sioEndianPutLeInt16( gsd->gsdScreenHigh, gft->gftSos );
154 
155     gsd->gsdPackedFields= 0;
156     if  ( gcm && gcm->gcmColorCount > 0 )
157 	{
158 	gsd->gsdPackedFields |= 0x80;
159 	gsd->gsdPackedFields |= gcm->gcmBitsPerPixel -1;
160 	}
161     gsd->gsdPackedFields |= ( bitsPerPixel -1 ) << 4;
162 
163     if  ( sioOutPutByte( gsd->gsdPackedFields, gft->gftSos ) < 0 )
164 	{ return GIF_ERROR;	}
165     if  ( sioOutPutByte( gsd->gsdScreenBackgroundColor, gft->gftSos ) < 0 )
166 	{ return GIF_ERROR;	}
167     if  ( sioOutPutByte( gsd->gsdScreenAspectRatio, gft->gftSos ) < 0 )
168 	{ return GIF_ERROR;	}
169 
170     /*  3  */
171     if  ( gcm && gcm->gcmColorCount > 0 )
172 	{
173 	if  ( bmGifWriteColorMap( gft, &(gsd->gsdScreenColorMap) ) )
174 	    { XDEB(gcm); return GIF_ERROR;	}
175 	}
176 
177     return GIF_OK;
178 }
179 
180 /************************************************************************/
181 /*									*/
182 /*  Setup the LZ compression for an image:				*/
183 /*									*/
184 /*  The result is that pixels emitted to the image are LZW compressed,	*/
185 /*  the compressed data is divided in packets and the packets are	*/
186 /*  emitted over the output stream for the image.			*/
187 /*									*/
188 /************************************************************************/
189 
bmGifSetupCompress(GifFileType * gft,int codeSize)190 static int bmGifSetupCompress(	GifFileType *		gft,
191 				int			codeSize )
192     {
193     /*  4  */
194     if  ( gft->gftSosBlocked )
195 	{ XDEB(gft->gftSosBlocked);	}
196 
197     gft->gftSosBlocked= sioOutBlockedOpen( gft->gftSos );
198     if  ( ! gft->gftSosBlocked )
199 	{ XDEB(gft->gftSosBlocked); return -1;	}
200 
201     /*  5  */
202     if  ( gft->gftSosLzw )
203 	{ XDEB(gft->gftSosLzw);	}
204 
205     gft->gftSosLzw= sioOutLzwGifOpen( gft->gftSosBlocked, codeSize );
206     if  ( ! gft->gftSosLzw )
207 	{ XDEB(gft->gftSosLzw); return -1;	}
208 
209     return 0;
210     }
211 
bmGifCleanupCompress(GifFileType * gft)212 static int bmGifCleanupCompress(	GifFileType *	gft )
213     {
214     if  ( gft->gftSosLzw )
215 	{
216 	if  ( sioOutClose( gft->gftSosLzw ) )
217 	    { LDEB(1); return -1;	}
218 	gft->gftSosLzw= (SimpleOutputStream *)0;
219 	}
220 
221     if  ( gft->gftSosBlocked )
222 	{
223 	if  ( sioOutClose( gft->gftSosBlocked ) )
224 	    { LDEB(1); return -1;	}
225 	gft->gftSosBlocked= (SimpleOutputStream *)0;
226 	}
227 
228     return 0;
229     }
230 
231 /************************************************************************/
232 /*									*/
233 /*  Emit an image descriptor to file.					*/
234 /*									*/
235 /*  This routine should be called after a screen descriptor is emmitted	*/
236 /*  and before any of the image data is emitted. If the image has	*/
237 /*  transparent parts, the graphics extension saying so should have	*/
238 /*  been emitted before.						*/
239 /*									*/
240 /*  In general, any extensions and comments relating to an image should	*/
241 /*  be emitted BEFORE the image.					*/
242 /*									*/
243 /*  1)  File writable?							*/
244 /*  2)  Remember parameters.						*/
245 /*  3)  Write descriptor to file.					*/
246 /*  4)  Write the color map to file (If one is given)			*/
247 /*  5)  Remember how many pixels are to be written.			*/
248 /*  6)  Find out what color map is relevant and derive LZW initial code	*/
249 /*	size from the relevant color map.				*/
250 /*  7)  Emit the code size and initialize the compressor and packet	*/
251 /*	streams.							*/
252 /*									*/
253 /************************************************************************/
254 
EGifPutImageDesc(GifFileType * gft,int Left,int Top,int Width,int Height,int Interlace,const GifColorMap * gcm)255 int EGifPutImageDesc(	GifFileType *		gft,
256 			int			Left,
257 			int			Top,
258 			int			Width,
259 			int			Height,
260 			int			Interlace,
261 			const GifColorMap *	gcm )
262 {
263     unsigned char		flags;
264     GifScreenDescriptor *	gsd= &(gft->gftScreenDescriptor);
265     GifImageDesc *		gid= &(gft->gftCurrentImageDescriptor);
266 
267     int				codeSize;
268 
269     /*  1  */
270     if  ( ! gft->gftSos )
271 	{
272 	XDEB(gft->gftSos);
273 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
274 	return GIF_ERROR;
275 	}
276 
277     /*  2  */
278     gid->Left = Left;
279     gid->Top = Top;
280     gid->Width = Width;
281     gid->Height = Height;
282     gid->Interlace = Interlace;
283 
284     bmGifInitGifColorMap( &(gid->gidImageColorMap) );
285 
286     if  ( gcm )
287 	{ gid->gidImageColorMap= *gcm;	}
288 
289     /*  3  */
290     if  ( sioOutPutByte( ',', gft->gftSos ) < 0 )
291 	{ return GIF_ERROR;	}
292     sioEndianPutLeInt16( gid->Left, gft->gftSos );
293     sioEndianPutLeInt16( gid->Top, gft->gftSos );
294     sioEndianPutLeInt16( gid->Width, gft->gftSos );
295     sioEndianPutLeInt16( gid->Height, gft->gftSos );
296 
297     flags= 0;
298     if  ( gcm && gcm->gcmColorCount > 0 )
299 	{
300 	flags |= 0x80;
301 	flags |= gcm->gcmBitsPerPixel - 1;
302 	}
303     if  ( Interlace )
304 	{
305 	flags |= 0x40;
306 	}
307     if  ( sioOutPutByte( flags, gft->gftSos ) < 0 )
308 	{ return GIF_ERROR;	}
309 
310     /*  4  */
311     if  ( gcm && gcm->gcmColorCount > 0 )
312 	{
313 	if  ( bmGifWriteColorMap( gft, gcm ) )
314 	    { XDEB(gcm); return GIF_ERROR;	}
315 	}
316 
317     /*  5  */
318     gft->gftPixelCount= (long) Width * (long) Height;
319 
320     /*  6  */
321     if  ( gcm && gcm->gcmColorCount > 0 )
322 	{ codeSize= gcm->gcmBitsPerPixel;	}
323     else{
324 	if  ( gsd->gsdScreenColorMap.gcmColorCount > 0 )
325 	    { codeSize= gsd->gsdScreenColorMap.gcmBitsPerPixel;	}
326 	else{
327 	    if  ( gcm )
328 		{ LDEB(gcm->gcmColorCount);	}
329 	    else{ XDEB(gcm);			}
330 	    LDEB( gsd->gsdScreenColorMap.gcmBitsPerPixel );
331 
332 	    _GifError = E_GIF_ERR_NO_COLOR_MAP;
333 	    return GIF_ERROR;
334 	    }
335 	}
336 
337     /*  7  */
338     codeSize= ( codeSize < 2 ? 2 : codeSize );
339     if  ( sioOutPutByte( codeSize, gft->gftSos ) < 0 )
340 	{ return GIF_ERROR;	}
341 
342     if  ( bmGifSetupCompress( gft, codeSize ) )
343 	{ LDEB(1); return GIF_ERROR;	}
344 
345     return GIF_OK;
346 }
347 
348 /************************************************************************/
349 /*									*/
350 /*  Emit a series of pixel values to file.. typically a scanline.	*/
351 /*									*/
352 /*  1)  File not writable.. Refuse.					*/
353 /*  2)  Refuse to exceed the number of pixels in the image.		*/
354 /*  3)  Subtract the number of pixels from that left in the file.	*/
355 /*  4)  Actually emit the pixels.					*/
356 /*  5)  When the image is finished, clean compressor.			*/
357 /*									*/
358 /************************************************************************/
359 
bmGifPutPixels(GifFileType * gft,const unsigned char * Line,int LineLen)360 int bmGifPutPixels(		GifFileType *		gft,
361 				const unsigned char *	Line,
362 				int			LineLen )
363 {
364     /*  1  */
365     if  ( ! gft->gftSos )
366 	{
367 	XDEB(gft->gftSos);
368 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
369 	return GIF_ERROR;
370 	}
371 
372     /*  2  */
373     if  ( gft->gftPixelCount < (unsigned)LineLen )
374 	{
375 	LLDEB(gft->gftPixelCount,LineLen);
376 	_GifError = E_GIF_ERR_DATA_TOO_BIG;
377 	return GIF_ERROR;
378 	}
379 
380     /*  3  */
381     gft->gftPixelCount -= LineLen;
382 
383     /*  4  */
384     if  ( sioOutWriteBytes( gft->gftSosLzw, Line, LineLen ) != LineLen )
385 	{
386 	LDEB(LineLen);
387 	_GifError = E_GIF_ERR_DISK_IS_FULL; return GIF_ERROR;
388 	}
389 
390     /*  5  */
391     if  ( gft->gftPixelCount == 0		&&
392 	  bmGifCleanupCompress( gft )		)
393 	{ LDEB(gft->gftPixelCount); return GIF_ERROR;	}
394 
395     return GIF_OK;
396 }
397 
398 /************************************************************************/
399 /*									*/
400 /*  Put a comment into GIF file using the GIF89 comment extension	*/
401 /*  block.								*/
402 /*									*/
403 /************************************************************************/
404 
EGifPutComment(GifFileType * gft,const char * Comment)405 int EGifPutComment(	GifFileType *	gft,
406 			const char *	Comment )
407 {
408     return EGifPutExtension(gft, COMMENT_EXT_FUNC_CODE, strlen(Comment),
409 								Comment);
410 }
411 
412 /******************************************************************************
413 *   Put a first extension block (see GIF manual) into gif file.  Here more    *
414 * extensions can be dumped using EGifPutExtensionMid until		      *
415 * EGifPutExtensionLast is invoked.					      *
416 ******************************************************************************/
417 
EGifPutExtensionFirst(GifFileType * gft,int ExtCode,int ExtLen,const void * Extension)418 int EGifPutExtensionFirst(	GifFileType *	gft,
419 				int		ExtCode,
420 				int		ExtLen,
421 				const void *	Extension)
422 {
423     if  ( ! gft->gftSos )
424 	{
425 	XDEB(gft->gftSos);
426 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
427 	return GIF_ERROR;
428 	}
429 
430     if  ( ExtCode == 0 )
431 	{
432 	if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
433 	    { return GIF_ERROR;	}
434 	}
435     else{
436 	if  ( sioOutPutByte( '!', gft->gftSos ) < 0 )
437 	    { return GIF_ERROR;	}
438 	if  ( sioOutPutByte( ExtCode, gft->gftSos ) < 0 )
439 	    { return GIF_ERROR;	}
440 	if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
441 	    { return GIF_ERROR;	}
442 	}
443 
444     if  ( sioOutWriteBytes( gft->gftSos, (unsigned char *)Extension, ExtLen ) != ExtLen )
445 	{ LDEB(ExtLen); return GIF_ERROR;	}
446 
447     return GIF_OK;
448 }
449 
450 /******************************************************************************
451 *   Put a middle extension block (see GIF manual) into gif file.	      *
452 ******************************************************************************/
453 
EGifPutExtensionNext(GifFileType * gft,int ExtCode,int ExtLen,const void * Extension)454 int EGifPutExtensionNext(	GifFileType *	gft,
455 				int		ExtCode,
456 				int		ExtLen,
457 				const void *	Extension)
458 {
459     if  ( ! gft->gftSos )
460 	{
461 	XDEB(gft->gftSos);
462 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
463 	return GIF_ERROR;
464 	}
465 
466     if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
467 	{ return GIF_ERROR;	}
468 
469     if  ( sioOutWriteBytes( gft->gftSos, (unsigned char *)Extension, ExtLen ) != ExtLen )
470 	{ LDEB(ExtLen); return GIF_ERROR;	}
471 
472     return GIF_OK;
473 }
474 
475 /******************************************************************************
476 *   Put a last extension block (see GIF manual) into gif file.		      *
477 ******************************************************************************/
478 
EGifPutExtensionLast(GifFileType * gft,int ExtCode,int ExtLen,const void * Extension)479 int EGifPutExtensionLast(	GifFileType *	gft,
480 				int		ExtCode,
481 				int		ExtLen,
482 				const void *	Extension )
483 {
484     if  ( ! gft->gftSos )
485 	{
486 	XDEB(gft->gftSos);
487 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
488 	return GIF_ERROR;
489 	}
490 
491     if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
492 	{ return GIF_ERROR;	}
493 
494     if  ( sioOutWriteBytes( gft->gftSos, (unsigned char *)Extension, ExtLen ) != ExtLen )
495 	{ LDEB(ExtLen); return GIF_ERROR;	}
496 
497     if  ( sioOutPutByte( '\0', gft->gftSos ) < 0 )
498 	{ return GIF_ERROR;	}
499 
500     return GIF_OK;
501 }
502 
503 /******************************************************************************
504 *   Put an extension block (see GIF manual) into gif file.		      *
505 ******************************************************************************/
506 
EGifPutExtension(GifFileType * gft,int ExtCode,int ExtLen,const void * Extension)507 int EGifPutExtension(	GifFileType *	gft,
508 			int		ExtCode,
509 			int		ExtLen,
510 			const void *	Extension )
511 {
512     if  ( ! gft->gftSos )
513 	{
514 	XDEB(gft->gftSos);
515 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
516 	return GIF_ERROR;
517 	}
518 
519     if  ( ExtCode == 0 )
520 	{
521 	if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
522 	    { return GIF_ERROR;	}
523 	}
524     else{
525 	if  ( sioOutPutByte( '!', gft->gftSos ) < 0 )
526 	    { return GIF_ERROR;	}
527 	if  ( sioOutPutByte( ExtCode, gft->gftSos ) < 0 )
528 	    { return GIF_ERROR;	}
529 	if  ( sioOutPutByte( ExtLen, gft->gftSos ) < 0 )
530 	    { return GIF_ERROR;	}
531 	}
532 
533     if  ( sioOutWriteBytes( gft->gftSos, (unsigned char *)Extension, ExtLen ) != ExtLen )
534 	{ LDEB(ExtLen); return GIF_ERROR;	}
535 
536     if  ( sioOutPutByte( '\0', gft->gftSos ) < 0 )
537 	{ return GIF_ERROR;	}
538 
539     return GIF_OK;
540 }
541 
542 /************************************************************************/
543 /*									*/
544 /*  Close a gif file for writing.					*/
545 /*									*/
546 /************************************************************************/
547 
EGifCloseFile(GifFileType * gft)548 int EGifCloseFile(	GifFileType *	gft )
549 {
550     int		rval= GIF_OK;
551 
552     if  ( ! gft->gftSos )
553 	{
554 	XDEB(gft->gftSos);
555 	_GifError = E_GIF_ERR_NOT_WRITEABLE;
556 	rval= GIF_ERROR;
557 	}
558 
559     if  ( gft->gftSos )
560 	{
561 	if  ( sioOutPutByte( ';', gft->gftSos ) < 0 )
562 	    { rval= GIF_ERROR;	}
563 	}
564 
565     bmGifCleanupCompress( gft );
566 
567     free( gft );
568 
569     return rval;
570 }
571