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