forked from uobikiemukot/recterm
-
Notifications
You must be signed in to change notification settings - Fork 11
/
gifsave89.c
2377 lines (2317 loc) · 136 KB
/
gifsave89.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright(c) 2012-2012, John Forkosh Associates, Inc. All rights reserved.
* http://www.forkosh.com mailto: john@forkosh.com
* Copyright (C) 2014 haru <uobikiemukot at gmail dot com>
* Copyright (C) 2014 Hayaki Saito <user@zuse.jp>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------
standard headers...
-------------------------------------------------------------------------- */
#include "config.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_CTYPE_H
# include <ctype.h>
#endif
#if defined(GSTESTDRIVE)
/* --- only needed for main() test driver below --- */
#include <math.h>
#endif
#include "malloc_stub.h"
#include "gifsave89.h"
/* -------------------------------------------------------------------------
additional gifsave89 parameters and macros...
-------------------------------------------------------------------------- */
/* ---
* bitfield macros (byte_bits=76543210, with lsb=bit#0 and 128=bit#7set)
* --------------------------------------------------------------------- */
#define getbit(x,bit) ( ((x)>>(bit)) & 1 ) /* get bit-th bit of x */
#define setbit(x,bit) ( (x) |= (1<<(bit)) ) /* set bit-th bit of x */
#define clearbit(x,bit) ( (x) &= ~(1<<(bit)) ) /* clear bit-th bit of x */
#define putbit(x,bit,val) \
if(((int)(val))==0) clearbit((x),(bit)); else setbit((x),(bit))
#define bitmask(nbits) ((1<<(nbits))-1) /* a mask of nbits 1's */
#define getbitfield(x,bit1,nbits) (((x)>>(bit1)) & (bitmask(nbits)))
#define putbitfield(x,bit1,nbits,val) /* x:bit1...bit1+nbits-1 = val */ \
if ( (nbits)>0 && (bit1)>=0 ) { /* check input */ \
(x) &= (~((bitmask((nbits))) << (bit1))); /*set field=0's*/ \
(x) |= (((val)&(bitmask((nbits)))) << (bit1)); /*set field=val*/ \
} /* let user supply final ; */
/* ---
* debugging, etc
* ----------------- */
/* --- debugging --- */
#define ismsgprint(gs,n) (gs==NULL?0:gs->msglevel>=(n)&&gs->msgfp!=NULL)
#define OKAY ((status)>=0?1:0) /*shortcut for funcs to check status */
static int msglevel = /* message level / verbosity */
#if defined(MSGLEVEL) /* if compiled with -DMSGLEVEL=n */
MSGLEVEL; /* use default msglevel=n */
#else /* otherwise */
0; /* use 0 */
#endif
static char msgfile[132] = "\000"; /* file for msgs (stdout if "\0") */
#define MSGFP stdout /* default msgfp */
/* --- etc --- */
#define min2(x,y) ((x)<=(y)?(x):(y)) /* lesser of two values */
#define max2(x,y) ((x)>=(y)?(x):(y)) /* larger of two values */
#define absval(x) ((x)>=0?(x):(-(x))) /* absolute value */
/* -------------------------------------------------------------------------
* Comments and structs below provide detailed GIF89a format documentation
* -------------------------------------------------------------------------
* Structs for gif87a/89a block formats...
* from: http://www.matthewflickinger.com/lab/whatsinagif/
* http://www.fileformat.info/format/gif/egff.htm
* also see: http://www.w3.org/Graphics/GIF/spec-gif89a.txt
* Several discrepancies between spec-gif89a.txt and egff.htm
* are "Note:"'ed below.
* If you prefer printed books, see Chapter 26 in
* The File Formats Handbook, Gunter Born, ITP Publishing 1995,
* ISBN 1-850-32128-0. From its publication date, you can see
* it's old, but its gif format treatment remains unchanged.
* -------------------------------------------------------------------------
* gif file layout:
* Illustrated below is the basic gif file layout.
* o Each file begins with a Header and Logical Screen Descriptor.
* An optional Global Color Table follows the Logical Screen Descriptor.
* Each of these blocks is always found at the same offset in the file.
* o Each image stored in the file contains a Local Image Descriptor, an
* optional Local Color Table, a single byte containing the LZW
* minimum codesize, image data comprised of one or more sub-blocks,
* followed by a terminating 0 byte.
* o The detailed byte-by-byte (and bit-by-bit where necessary) internal
* structure for each type of block is laid out in the structs and
* their accompanying comments below.
* o The last field in every gif file is a Terminator character (hex 3B).
* -------------------------------------------------------------------------
* +---
* Header and Color | Header
* Table Information | Logical Screen Descriptor
* | Global Color Table
* +---
* +---
* Optional Extension | Comment Extension
* information (valid | Application Extension
* only for gif89a) | Graphic Control Extension
* +---
* Either...
* +---
* (gif89a only) | Plain Text Extension
* +---
* Or...
* +---
* | Local Image Descriptor
* Image 1 | Local Color Table
* | Image Data (preceded by LZW min codesize byte)
* +---
* ... optional extensions for ...
* ... Image 2, etc ...
* +---
* Optional Extension | Comment Extension
* information (valid | Application Extension
* only for gif89a) | Graphic Control Extension
* +---
* Either...
* +---
* (gif89a only) | Plain Text Extension
* +---
* Or...
* +---
* | Local Image Descriptor
* Last Image | Local Color Table
* | Image Data (preceded by LZW min codesize byte)
* +---
* +---
* Trailer | Trailer Byte (always hex 3B)
* +---
* -------------------------------------------------------------------------
* Notes:
* o WORD's are 16-bit (2-byte) integers, ordered little-endian,
* regardless of cpu architecture byte ordering.
* o Image Data is preceded by a byte containing the minimum LZW codesize,
* as stated above, and must also be followed by a byte containing 0
* to terminate the sequence of sub-blocks.
* o gif89a added "Control Extensions" to gif87a, to control the
* rendering of image data. 87a only allowed one image at a time
* to be displayed in "slide show" fashion. Control Extensions allow
* both textual and graphical data to be displayed, overlaid, and
* deleted in "animated multimedia presentation" fashion.
* o Control Extension blocks may appear almost anywhere following the
* Global Color Table. Extension blocks begin with the Extension
* Introducer hex 21, followed by a Block Label (hex 00 to FF)
* identifying the type of extension.
* -------------------------------------------------------------------------
* Flow diagram for assembling gif blocks...
* ------------------+------------------------------------------------------
* key: +----------+ | +--------+
* | required | | | Header |
* +----------+ | +--------+
* | |
* +- - - - - + | +---------------------------+
* | optional | | | Logical Screen Descriptor |
* + - - - - -+ | +---------------------------+
* ------------------+ |
* +- - - - - - - - - - +
* | Global Color Table |
* + - - - - - - - - - -+
* |
* |<--------------<------------------+
* | |
* +------------------+---------------+ |
* | | |
* | +- - - - - - - - -+ |
* | | Graphic Control | ^
* | | extension | |
* | + - - - - - - - - + |
* | | |
* | +--------+--------+ |
* | | | ^
* | | +------------+ |
* | | | Image | |
* | | | Descriptor | |
* +-------+------+ | +------------+ |
* | | | | ^
* +-------------+ +-----------+ +------------+ +- - - - - - + |
* | Application | | Comment | | Plain Text | | Local | |
* | extension | | extension | | extension | | Color Table| |
* +-------------+ +-----------+ +------------+ + - - - - - -+ |
* | | | | ^
* +-------+------+ | +-------+ |
* | | | Image | |
* | | | Data | |
* | | +-------+ |
* | | | ^
* | +--------+--------+ |
* | | |
* +---------------+------------------+ |
* | |
* +------------------>------------------+
* |
* +---------+
* | Trailer |
* +---------+
* -------------------------------------------------------------------------
* PS:
* o The structs defined below,
* GIFHEADER, GIFCOLORTABLE, GIFIMAGEDESC,
* GIFGRAPHICCONTROL, GIFPLAINTEXT, GIFAPPLICATION, GIFCOMMENT
* aren't actually used by the gifsave89 (except for GIFAPPLICATION).
* They're provided mostly to help document the gif file format.
* But they're not actually used because structs can't typically
* just be output as, e.g.,
* memcpy((void *)image,(void *)&struct,sizeof(struct));
* because we can't usually be sure structs are packed in memory.
* Syntax for specifying packed structs to C compilers isn't
* completely portably possible, so gifsave89 outputs each
* member element separately.
* ----------------------------------------------------------------------- */
/* ---
* Gif header and logical screen descriptor:
* -------------------------------------------- */
#define GIFHEADER struct _GifHeader
GIFHEADER {
/* ---
* Header
* --------- */
BYTE Signature[3]; /* Header Signature (always "GIF") */
BYTE Version[3]; /* GIF format version ("87a" or "89a") */
/* ---
* Logical Screen Descriptor
* ---------------------------- */
WORD ScreenWidth; /* Width of Display Screen in Pixels */
WORD ScreenHeight; /* Height of Display Screen in Pixels */
/* ScreenHeight and Width are the minimum resolution to display
the image (if the display device does not support this resolution,
some sort of scaling is done to display the image). */
BYTE Packed; /* Screen and Color Map Information */
/* Packed contains four subfields, with bit 0 the
least significant bit, or LSB...
Bits 0-2: Size of each Global Color Table entry
the number of bits in each Global Color Table entry minus 1,
e.g., 7 if the image contains 8 bits per pixel. And the
number of Global Color Table entries is then
#GlobalColorTableEntries = 1 << (SizeOfTheGlobalColorTable + 1)
Bit 3: Global Color Table Sort Flag (always 0 for 87a)
if 1, then Global Color Table entries are sorted from the most
important (most frequently occurring) to the least (sorting
aids applications in choosing colors to use with displays that
have fewer colors than the image). Valid only for 89a, 0 for 87a.
Bits 4-6: Color Resolution
the number of bits in an entry of the original color palette
minus 1 (e.g., 7 if the image originally contained 8 bits per
primary color)
Bit 7: Global Color Table Flag
1 if a Global Color Table is present in the GIF file, and 0 if
not (if present, Global Color Table data always follows the
Logical Screen Descriptor header) */
BYTE BackgroundColor; /* Background Color Index */
/* the Global Color Table index of the color to use for the image's
border and background */
BYTE AspectRatio; /* Pixel Aspect Ratio (usually 0) */
/* not used if 0, else the aspect ratio value of image pixels
(pixel width divided by the height, in the range of 1 to 255,
used as PixelAspectRatio = (AspectRatio+15)/64) */
}; /* --- end-of-struct GIFHEADER --- */
/* ---
* Gif color table:
* the number of entries in the Global Color Table is a power of two
* (2, 4, 8, 16, etc), up to 256. Its size in bytes is calculated using
* bits 0, 1, and 2 in the Packed field of the Logical Screen Descriptor:
* ColorTableSize(in bytes) = 3 * (1 << (SizeOfGlobalColorTable + 1))
* ----------------------------------------------------------------------- */
#define GIFCOLORTABLE struct _GifColorTable
GIFCOLORTABLE {
BYTE Red; /* Red Color Element */
BYTE Green; /* Green Color Element */
BYTE Blue; /* Blue Color Element */
}; /* --- end-of-struct GIFCOLORTABLE --- */
/* ---
* Gif local image descriptor:
* the Local Image Descriptor appears before each section of image data
* and has the following structure...
* ----------------------------------------------------------------------- */
#define GIFIMAGEDESC struct _GifImageDescriptor
GIFIMAGEDESC {
BYTE Separator; /* Image Descriptor identifier */
/* hex value 2C denoting the beginning of the Image Descriptor */
WORD Left; /* X position of image on the display */
WORD Top; /* Y position of image on the display */
/* Left, Top are pixel coords of the upper-left corner of the image
on the logical screen (the screen's upper-left corner is 0,0) */
WORD Width; /* Width of the image in pixels */
WORD Height; /* Height of the image in pixels */
/* Width, Height are the image size in pixels */
BYTE Packed; /* Image and Color Table Data Information */
/* Packed contains five subfields, with bit 0 the LSB...
(Note/warning: egff.htm has this documented "backwards",
whereas spec-gif89a.txt is correct. The correct version is below.)
Bits 0-2: Size of each Local Color Table Entry
the number of bits in each Local Color Table entry minus 1
Bits 3-4: Reserved
Bit 5: Local Color Table Sort Flag (always 0 for 87a)
1 if entries in the Local Color Table are sorted in order of
importance, or 0 if unsorted (valid only for 89a, 0 for 87a)
Bit 6: Interlace Flag
1 if the image is interlaced, or 0 if it is non-interlaced
Bit 7: Local Color Table Flag
1 if a Local Color Table is present, or 0 to use
the Global Color Table */
}; /* --- end-of-struct GIFIMAGEDESC --- */
/* ---
* Gif graphics control extension block:
* A Graphics Control Extension block modifies how (either bitmap or text)
* data in the Graphics Rendering block that immediately follows it is
* displayed, e.g., whether the graphic is overlaid transparent or opaque
* over another graphic, whether it is to be restored or deleted, whether
* user input is expected before continuing with the display of data, etc.
* A Graphics Control block must occur before the data it modifies, and
* only one Graphics Control block may appear per Graphics Rendering block.
* ----------------------------------------------------------------------- */
#define GIFGRAPHICCONTROL struct _GifGraphicsControlExtension
GIFGRAPHICCONTROL {
BYTE Introducer; /* Extension Introducer (always hex 21) */
BYTE Label; /* Graphic Control Label (always hex F9) */
BYTE BlockSize; /* Size of remaining fields (always hex 04) */
BYTE Packed; /* Method of graphics disposal to use */
/* Packed contains four subfields, with bit 0 the LSB...
Bit 0: Transparent Color Flag
if 1, the ColorIndex field contains a color transparency index
Bit 1: User Input Flag
if 1, user input (key press, mouse click, etc) is expected
before continuing to the next graphic sequence
Bits 2-4: Disposal Method
(Note: spec-gif89a.txt says values may be 0,1,2,3 rather
than 0,1,2,4 in egff.htm. 0,1,2,3 appears to be correct.)
Indicates how the graphic is to be disposed of after display...
hex 00 = disposal method not specified, hex 01 = do not dispose
of graphic, hex 02 = overwrite graphic with background color,
and hex 03 = restore with previous graphic
Bits 5-7: Reserved */
WORD DelayTime; /* Hundredths of seconds to wait */
/* hundredths of a second before graphics presentation continues,
or 0 for no delay */
BYTE ColorIndex; /* Transparent Color Index */
/* color transparency index (if the Transparent Color Flag is 1) */
BYTE Terminator; /* Block Terminator (always 0) */
}; /* --- end-of-struct GIFGRAPHICCONTROL --- */
/* ---
* Gif plain text extension block:
* Plain Text Extension blocks allow mixing ASCII text, displayed as
* graphics, with bitmapped image data, e.g., captions that are not
* part of the bitmapped image may be overlaid on it.
* ----------------------------------------------------------------------- */
#define GIFPLAINTEXT struct _GifPlainTextExtension
GIFPLAINTEXT {
BYTE Introducer; /* Extension Introducer (always hex 21) */
BYTE Label; /* Extension Label (always hex 01) */
BYTE BlockSize; /* Size of Extension Block (always hex 0C) */
WORD TextGridLeft; /* X position of text grid in pixels */
WORD TextGridTop; /* Y position of text grid in pixels */
/* X,Y coords of the text grid with respect to the upper-left
corner of the display screen (0,0) */
WORD TextGridWidth; /* Width of the text grid in pixels */
WORD TextGridHeight; /* Height of the text grid in pixels */
/* the size of the text grid in pixels */
BYTE CellWidth; /* Width of a grid cell in pixels */
BYTE CellHeight; /* Height of a grid cell in pixels */
/* the size in pixels of each character cell in the grid */
BYTE TextFgColorIndex; /* Text foreground color index value */
BYTE TextBgColorIndex; /* Text background color index value */
/* indexes into the (Global or Local) Color Table for the color
of the text, and for the color of the background */
BYTE *PlainTextData; /* The Plain Text data */
/* the text to be rendered as a graphic, comprised of one or more
sub-blocks, each beginning with a byte containing the number of
text bytes (from 1 to 255) that follow */
BYTE Terminator; /* Block Terminator (always 0) */
}; /* --- end-of-struct GIFPLAINTEXT --- */
/* ---
* Gif application extension block:
* Application Extension blocks store data understood only by the application
* reading the gif file (similar to how tags are used in TIFF and TGA image
* file formats), e.g., instructions on changing video modes, on applying
* special processing to displayed image data, etc.
* ----------------------------------------------------------------------- */
#define GIFAPPLICATION struct _GifApplicationExtension
GIFAPPLICATION {
BYTE Introducer; /* Extension Introducer (always hex 21) */
BYTE Label; /* Extension Label (always hex FF) */
BYTE BlockSize; /* Size of Extension Block (always hex 0B) */
BYTE Identifier[8]; /* Application Identifier */
/* up to eight printable 7-bit ASCII chars, used to identify the
application that wrote the Application Extension block */
BYTE AuthentCode[3]; /* Application Authentication Code */
/* a value used to uniquely identify a software application that
created the Application Extension block, e.g., a serial number,
a version number, etc */
BYTE *ApplicationData; /* Point to Application Data sub-blocks */
/* the data used by the software application, comprised of a series
of sub-blocks identical to data in Plain Text Extension blocks */
BYTE Terminator; /* Block Terminator (always 0) */
}; /* --- end-of-struct GIFAPPLICATION --- */
/* ---
* Gif comment extension block:
* data stored in Comment Extension blocks is to be read only by the user
* examining a gif file, and should be ignored by the application.
* ----------------------------------------------------------------------- */
#define GIFCOMMENT struct _GifCommentExtension
GIFCOMMENT {
BYTE Introducer; /* Extension Introducer (always hex 21) */
BYTE Label; /* Comment Label (always hex FE) */
/*no BYTE BlockSize; *//* Size of Extension Block (always hex ??) */
BYTE *CommentData; /* Pointer to Comment Data sub-blocks */
/* one or more sub-blocks of ASCII string data (strings not
required to be NULL-terminated */
BYTE Terminator; /* Block Terminator (always 0) */
}; /* --- end-of-struct GIFCOMMENT --- */
/* ==========================================================================
* Functions: newgif(gifimage,width,height,colors,bgindex)
* animategif(gs,nrepetitions) *optional*
* plaintxtgif(gs,left,top,width,height,fg,bg,data) *optional*
* controlgif(gs,tcolor,delay,userinput,disposal) *optional*
* putgif(gs,pixels) or *short form*
* fputgif(gs,left,top,width,height,pixels,colors) *long form*
* endgif(gs)
* --- alternative "short form" for a single gif image ---
* makegif(nbytes,width,height,pixels,colors,bgindex)*short form*
* Purpose: construct gif -- user-callable entry points for gifsave89
* --------------------------------------------------------------------------
* Arguments:
* For newgif(), called once to intialize gif...
* gifimage (O) (void **) where ptr to buffer (that will
* be malloc'ed by gifsave89) containing
* constructed gif image is stored
* width (I) int containing screen (total image)
* width (#columns) in pixels
* height (I) int containing screen (total image)
* height (#rows) in pixels
* colors (I) int * to r,g,b, r,g,b, ..., -1 containing
* image colors for gif global color table
* (see putgifcolortable() comments for more
* details)
* bgindex (I) int containing colors[] index (0=first color)
* for background
* For all subsequent function() calls...
* gs (I) void * returned by newgif() is always the
* first argument to every other function
* For putgif(), called once or looped to render each frame of gif...
* +------------------------------------------------------------
* | Note: several variables removed as putgif() args; the more
* | detailed entry point fputgif, below, includes top,left,
* | width,height,colors args, to provide "full" functionality
* | putgif() takes top,left of frame, relative to screen
* | origin, as 0,0, and takes width,height as screen dimensions
* +------------------------------------------------------------
* gs (I) as described above
* pixels (I) unsigned char * to width*height bytes,
* from upper-left to lower-right,
* each byte containing a colors[] index
* For fputgif(), "full" putgif with additional args...
* top, left (I) ints containing row,col of upper-left
* corner pixel, with 0,0=upper-left of screen
* width, height (I) ints containing width,height of pixels
* image (which must be less than width,height
* of screen if top,left>0,0)
* colors (I) int * to local color table for this frame,
* or NULL to use global color table
* For endgif(), called once after all frames rendered...
* gs (I) as described above
* For animategif(), optionally called once after newgif...
* gs (I) as described above
* nrepetitions (I) int containing 0 to loop continuously,
* or containing #repetitions, 0 to 65535.
* For plaintxtgif(), optionally called preceding put & control...
* +------------------------------------------------------------
* | Note: issues wget to mimeTeX to render image of plaintext
* +------------------------------------------------------------
* For controlgif(), optionally called immediately before putgif...
* gs (I) as described above
* tcolor (I) int containing colors[] index of
* transparent color, or -1 to not use
* delay (I) int containing delay in hundredths-of-second
* between frames, or 0 for no delay
* userinput (I) int containing 1 to wait for user keystroke
* or mouseclick before displaying next frame,
* or containing 0 if no user input required
* disposal (I) int containing disposition of frame after
* display
* void (*gifimage=) *makegif(&nbytes_in_gifimage,
* width,height,pixels,colors,bgindex)
* --------------------------------------------------------------------------
* Returns: ( void * ) newgif() returns a ptr that must be passed
* as the first argument to every subsequent
* gifsave89 function called,
* ( void * ) makegif() returns (unsigned char *)gifimage
* or both return NULL for any error.
* ( int ) putgif() returns current #bytes in gifimage,
* endgif() returns total #bytes in gifimage,
* all other functions (extension block funcs)
* return 1 if successful,
* and all return -1 for any error.
* --------------------------------------------------------------------------
* Notes: o sequence of gifsave function calls...
* First, call gs=newgif(&gifimage,etc) once.
* Next, optionally call animategif(gs,nrepetitions) once.
* Now, once or in a loop {
* optionally call controlgif(gs,etc)
* call putgif(gs,pixels) }
* Finally, call nbytes=endgif(gs) once.
* Do what you want with gifimage[nbytes], then free(gifimage)
* o minimal usage example/template
* (see main() Notes for additional info)...
* void *gs=NULL, *newgif();
* int nbytes=0, putgif(), endgif();
* int width=255, height=255,
* *colors = { 255,255,255, 0,0,0, -1 },
* bgindex=0;
* unsigned char *pixels = { 0,1,0,1, ..., width*height };
* unsigned char *gifimage = NULL;
* gs = newgif(&gifimage,width,height,colors,bgindex);
* if ( gs != NULL ) {
* putgif(gs,pixels);
* nbytes = endgif(gs); }
* if ( nbytes > 0 ) {
* do_what_you_want_with(gifimage,nbytes);
* free(gifimage); }
* ======================================================================= */
/* ---
* entry point
* -------------- */
void *newgif(void **gifimage, int width, int height,
int *colors, int bgindex)
{
/* ---
* allocations and declarations
* ------------------------------- */
GS *newgifstruct(), /*allocate and init gif data struct */
*gs = newgifstruct((void **) gifimage, width, height); /*new gif data */
int version = 89; /* default is GIF89a */
int putgifcolortable(), /* emit (global) color table */
ncolorbits = putgifcolortable(NULL, colors); /*#bits for color index */
BYTE Signature[4] = "GIF", /* header always "GIF" */
Version[4] = "89a"; /* and either 87a or 89a */
BYTE Packed = (BYTE) 0; /* packed byte */
int AspectRatio = 0; /* aspect ratio unused */
BK *bk = (gs == NULL ? NULL : &(gs->gifimage)); /* ptr to gif image block */
int putblkbytes(), putblkbyte(), putblkword(); /* write to image block */
int status = 1; /* init okay status */
/* ---
* check input
* -------------- */
if (gs == NULL)
goto end_of_job; /* failed to malloc gif data struct */
width = min2(9999, max2(1, width)); /* 1 <= width <= 9999 */
height = min2(9999, max2(1, height)); /* 1 <= height <= 9999 */
gs->width = width;
gs->height = height; /* reset values stored in gs */
bgindex = (max2(0, bgindex)) % 256; /* 0<=bgindex<=255 */
gs->bgindex = bgindex; /* global&local colortable bgindex */
gs->version = (version == 87 ? 87 : 89); /* default to 89a if not 87 */
if (version == 87)
memcpy(Version, "87a", 4); /* reset to 87a if it is 87 */
if (!OKAY)
goto end_of_job; /* quit if input check failed */
/* ---
* header and screen descriptor
* ------------------------------- */
Packed = 0; /* zero out Packed byte */
if (ncolorbits > 0) { /* have colors[] color table */
putbitfield(Packed, 0, 3, ncolorbits - 1); /* #bits for color index - 1 */
clearbit(Packed, 3); /* clear color table sort flag bit */
putbitfield(Packed, 4, 3, 8 - 1); /* assume 8-bit color resolution */
setbit(Packed, 7); /* set global color table bit */
}
/* --- Header ---- */
status = putblkbytes(bk, Signature, 3);
if (OKAY)
status = putblkbytes(bk, Version, 3);
/* --- Logical Screen Descriptor --- */
if (OKAY)
status = putblkword(bk, width);
if (OKAY)
status = putblkword(bk, height);
if (OKAY)
status = putblkbyte(bk, Packed);
if (OKAY)
status = putblkbyte(bk, bgindex);
if (OKAY)
status = putblkbyte(bk, AspectRatio);
if (!OKAY)
goto end_of_job;
/* ---
* global color table
* --------------------- */
gs->ncolorbits_gct = /* store #colorbits in global table */
gs->ncolorbits = max2(0, ncolorbits); /* and default if no local table */
if (ncolorbits > 0) { /* caller provided global colors[] */
putgifcolortable(gs, colors);
} /* emit global color table */
end_of_job:
if (!OKAY) /* something fouled up */
if (gs != NULL) { /* but we have an allocated gs */
free((void *) gs);
gs = NULL;
} /* free it and signal error */
return (((void *) gs)); /* return data struct as "blob" */
} /* --- end-of-function newgif() --- */
/* ---
* entry point
* -------------- */
int animategif(GS * gs, int nrepetitions,
int delay, int tcolor, int disposal)
{
/* --- allocations and declarations --- */
int status = (-1); /* init for error */
int putgifapplication(); /* emit application extension */
GIFAPPLICATION gifapplication, *ga = (&gifapplication);
BK *bk = (gs == NULL ? NULL : &(gs->gifimage)); /* ptr to gif image block */
int putblkbytes(), putblkbyte(), putblkword(); /* write to image block */
/* ---
* check input
* -------------- */
if (bk == NULL)
goto end_of_job; /* no gif data struct supplied */
if (gs->version == 87)
goto end_of_job; /* no extensions for GIF87a */
if (gs->isanimated > 0)
goto end_of_job; /* just one animategif() per gif */
if (nrepetitions < 0 || nrepetitions > 65535)
nrepetitions = 0; /*0=continuous */
/* ---
* construct graphic control extension and emit it
* -------------------------------------------------- */
memset((void *) ga, 0, sizeof(GIFAPPLICATION)); /* zero out the whole thing */
memcpy(ga->Identifier, "NETSCAPE", 8); /* netscape */
memcpy(ga->AuthentCode, "2.0", 3); /* 2.0 */
ga->ApplicationData = NULL; /* no data */
status = putgifapplication(gs, ga); /* emit application extension hdr */
/* --- emit the data and terminator ourselves (for the time being) --- */
if (OKAY)
status = putblkbyte(bk, 3); /* 3 more bytes of data */
if (OKAY)
status = putblkbyte(bk, 1); /* data sub-block index (always 1) */
if (OKAY)
status = putblkword(bk, nrepetitions); /* #repetitions */
if (OKAY)
status = putblkbyte(bk, 0); /* sub-block sequence terminator */
if (OKAY) {
gs->delay = delay; /* set default frame delay */
gs->tcolor = tcolor; /* set default transparent index */
gs->disposal = disposal; /* set default disposal method */
gs->isanimated++;
} /* set isanimated flag true */
end_of_job:
return (status);
} /* --- end-of-function animategif() --- */
/* ---
* entry point
* -------------- */
int plaintxtgif(GS * gs, int left, int top, int width, int height,
int fg, int bg, char *data)
{
/* --- allocations and declarations --- */
int status = (-1); /* init signalling error */
int Introducer = 0x21; /* always 0x21 */
int Label = 0x01; /* always 0x01 */
int BlockSize = 0x0C; /* always 0x0C */
int Terminator = 0x00; /* always 0x00 */
BK *bk = (gs == NULL ? NULL : &(gs->gifimage)); /* ptr to gif image block */
SB *sb = (gs == NULL ? NULL : &(gs->gifsubblock)); /* ptr to gif subblock */
int putblkbytes(), putblkbyte(), putblkword(); /* write to image block */
int putsubbytes(), flushsubblock(); /* data subblock */
int nchars = (data == NULL ? 0 : strlen(data)); /* #plaintext chars */
int cellwidth = 0, cellheight = height; /* #pixels per char */
int ismimetex = 1; /* true to use mimetex for text */
/* ---
* check input
* -------------- */
if (bk == NULL || sb == NULL)
goto end_of_job; /*no gif data struct supplied */
if (gs->version == 87)
goto end_of_job; /* no extensions for GIF87a */
cellwidth = (nchars < 1 ? 0 : width / nchars); /* #pixels/char width */
if (!ismimetex) { /* irrelevant args if using mimetex */
if (cellwidth < 2 || height < 2)
goto end_of_job; /* won't work */
if (nchars < 1)
goto end_of_job;
}
/* no data won't work, either */
/* ---
* reset (turn off) any preceding "persistent" plaintext
* -------------------------------------------------------- */
if (ismimetex) /* but for mimeTeX... */
if (nchars < 1) { /* ...no data signals reset */
gs->isplaintext = 0; /* reset plaintext flag */
if (gs->pt_pixels != NULL) { /* have previous allocated image */
free(gs->pt_pixels);
gs->pt_pixels = NULL;
} /* free it and reset ptr */
gs->pt_left = gs->pt_top = 0; /* reset top-left corner col;row */
gs->pt_bg = gs->pt_fg = 0; /* reset bg,fg colortable indexes */
gs->pt_width = gs->pt_height = 0; /*reset pixelized text width,height */
memset(gs->pt_data, 0, 1023); /* reset text data */
status = 1; /* set successful status */
goto end_of_job; /* plaintext reset */
}
/* --- end-of-if(nchars<1) --- */
/* ---
* save mimetex data for subsequent putgif()
* -------------------------------------------- */
if (ismimetex) { /* use mimeTeX to render plaintext */
gs->isplaintext = 1; /* set plaintext flag */
if (width < 0 || height < 0) { /* set persistent text flag */
gs->isplaintext = 2; /* display same text on all frames */
width = absval(width);
height = absval(height);
} /* reset neg signal */
gs->pt_left = left;
gs->pt_top = top; /* save top-left corner col;row */
gs->pt_bg = bg;
gs->pt_fg = fg; /* save bg,fg colortable indexes */
gs->pt_width = gs->pt_height = 0; /*reset pixelized text width,height */
nchars = min2(1023, nchars); /* don't overflow data[] buffer */
strncpy(gs->pt_data, data, nchars); /* local copy of text data */
(gs->pt_data)[nchars] = '\000'; /* make sure it's null-terminated */
if (gs->pt_pixels != NULL) { /* have previous allocated image */
free(gs->pt_pixels);
gs->pt_pixels = NULL;
} /* free it and reset ptr */
status = 1; /* set successful status */
}
/* --- end-of-if(ismimetex) --- */
/* ---
* or construct plain text extension and emit it
* ------------------------------------------------ */
if (!ismimetex) { /* emit gif89a plaintent extension */
status = putblkbyte(bk, Introducer);
if (OKAY)
status = putblkbyte(bk, Label);
if (OKAY)
status = putblkbyte(bk, BlockSize);
if (OKAY)
status = putblkword(bk, left);
if (OKAY)
status = putblkword(bk, top);
if (OKAY)
status = putblkword(bk, width);
if (OKAY)
status = putblkword(bk, height);
if (OKAY)
status = putblkbyte(bk, cellwidth);
if (OKAY)
status = putblkbyte(bk, cellheight);
if (OKAY)
status = putblkbyte(bk, fg);
if (OKAY)
status = putblkbyte(bk, bg);
if (OKAY)
status = putsubbytes(sb, (BYTE *) data, nchars);
if (OKAY)
status = flushsubblock(sb);
if (OKAY)
status = putblkbyte(bk, Terminator);
} /* --- end-of-if(!ismimetex) --- */
end_of_job:
return (status);
} /* --- end-of-function plaintxtgif() --- */
/* ---
* entry point
* -------------- */
int controlgif(GS * gs, int tcolor, int delay, int userinput, int disposal)
{
/* --- allocations and declarations --- */
int status = (-1); /* init signalling error */
int Introducer = 0x21; /* always 0x21 */
int Label = 0xF9; /* always 0xF9 */
int BlockSize = 0x04; /* always 0x04 */
int Terminator = 0x00; /* always 0x00 */
BYTE Packed = (BYTE) 0; /* packed byte */
BK *bk = (gs == NULL ? NULL : &(gs->gifimage)); /* ptr to gif image block */
int putblkbytes(), putblkbyte(), putblkword(); /* write to image block */
/* ---
* check input
* -------------- */
if (bk == NULL)
goto end_of_job; /* no gif data struct supplied */
if (gs->version == 87)
goto end_of_job; /* no extensions for GIF87a */
if (gs->ncontrol > 0)
goto end_of_job; /* just one control per image */
if (tcolor < 0 || tcolor > 255)
tcolor = (-1); /* not transparent if illegal */
if (delay < 0 || delay > 65535)
delay = 0; /* 0 if illegal (<0 or >2**16-1) */
userinput = (userinput <= 0 ? 0 : 1); /* "yes" if positive */
if (disposal != 1 && disposal != 2 /* 0,1,2,3 are okay */
&& disposal != 3)
disposal = 0; /* default to 0 otherwise */
/* ---
* construct graphic control extension and emit it
* -------------------------------------------------- */
putbit(Packed, 0, (tcolor >= 0 ? 1 : 0)); /* set transparent color flag */
putbit(Packed, 1, userinput); /* set user input flag */
putbitfield(Packed, 2, 3, disposal); /* set requested disposal method */
putbitfield(Packed, 5, 3, 0); /* clear reserved bits */
status = putblkbyte(bk, Introducer);
if (OKAY)
status = putblkbyte(bk, Label);
if (OKAY)
status = putblkbyte(bk, BlockSize);
if (OKAY)
status = putblkbyte(bk, Packed);
if (OKAY)
status = putblkword(bk, delay);
if (OKAY)
status = putblkbyte(bk, (tcolor < 0 ? 0 : tcolor));
if (OKAY)
status = putblkbyte(bk, Terminator);
if (OKAY)
gs->ncontrol++; /* count (another?) graphic control */
end_of_job:
return (status);
} /* --- end-of-function controlgif() --- */
/* ---
* entry point
* -------------- */
int putgif(GS * gs, void *pixels)
{
/* --- allocations and declarations --- */
int status = (-1), fputgif(); /* fputgif() does all the work */
/* --- call fputgif() --- */
if (gs != NULL && pixels != NULL) /* caller passed required args */
status = fputgif(gs, 0, 0, gs->width, gs->height, pixels, NULL);
return (status); /* return current #bytes in image */
} /* --- end-of-function putgif() --- */
/* ---
* entry point
* -------------- */
int fputgif(GS * gs, int left, int top, int width, int height,
void *pixels, int *colors)
{
/* --- allocations and declarations --- */
#if 0
int width = (gs == NULL ? 0 : gs->width), /* default width: image = screen */
height = (gs == NULL ? 0 : gs->height); /* default height: image = screen */
int left = 0, top = 0; /* default left,top coords */
#endif
int Separator = 0x2C; /* image descrip separator is 0x2C */
BYTE Packed = (BYTE) 0; /* packed byte */
BK *bk = (gs == NULL ? NULL : &(gs->gifimage)); /* ptr to gif image block */
int npixels = (gs == NULL ? (-1) : gs->npixels); /* width*height */
int putblkbytes(), putblkbyte(), putblkword(); /* write to image block */
int codesize; /* write min lzw codesize byte */
int nbytes = 0, encodelzw(); /* lzw encode the image pixels */
int putgifcolortable(), /* emit (local) color table */
ncolorbits = putgifcolortable(NULL, colors); /*#bits for color index */
BYTE *plainmimetext(), /* pixelize plaintext data, */
*overpix = NULL, *overlay(); /* and overlay that text on pixels */
int controlgif(); /* to emit animation controlgif() */
int isextensionallowedbetween = 0; /* true for descrip+extension+image */
int fprintpixels(); /* user-requested debug display */
int status = (-1); /* init for error */
/* ---
* check input
* ----------- */
if (bk == NULL)
goto end_of_job; /* no gs data provided */
if (!isextensionallowedbetween) /*allow extension between descrip/image? */
if (pixels == NULL || width < 1 || height < 1) /* if not allowed, then */
goto end_of_job; /*signal error for any missing args */
/* --- debug pixel display --- */
if (ismsgprint(gs, 32)) /* msglevel >= 32 */
fprintpixels(gs, (gs->msglevel >= 99 ? 2 : 1), pixels); /* hex format if >=99 */
/* ---
* emit default controlgif() for animations if not already done by user
* ----------------------------------------------------------------------- */
if (gs->isanimated > 0) /* rendering gif animation */
if (gs->ncontrol < 1) { /* but no graphic control for frame */
status = controlgif(gs, /* default graphic control... */
gs->tcolor, /* default animation transparency */
gs->delay, /* default animation frame delay */
0, /* userinput always 0 */
gs->disposal); /* default animation disposal */
if (!OKAY)
goto end_of_job; /* quit if failed */
gs->ncontrol++;
}
/* count controlgif() call */
/* ---
* image descriptor
* ------------------- */
if (width >= 1 && height >= 1) {
gs->npixels = npixels = width * height; /* set for encodelzw() */
Packed = (BYTE) 0; /* zero out Packed byte */
putbit(Packed, 7, max2(0, ncolorbits)); /* set local color table bit */
clearbit(Packed, 6); /* clear interlace flag bit */
clearbit(Packed, 5); /* clear sort flag bit */
putbitfield(Packed, 3, 2, 0); /* clear reserved bits */
if (ncolorbits > 0) { /* have colors[] color table */
putbitfield(Packed, 0, 3, ncolorbits - 1);
} /* #bits for color index - 1 */
status = putblkbyte(bk, Separator);
if (OKAY)
status = putblkword(bk, left);
if (OKAY)
status = putblkword(bk, top);
if (OKAY)
status = putblkword(bk, width);
if (OKAY)
status = putblkword(bk, height);
if (OKAY)
status = putblkbyte(bk, Packed);
if (!OKAY)
goto end_of_job;
/* ---
* local color table
* -------------------- */
if (ncolorbits > 0) { /* caller provided local colors[] */
putgifcolortable(gs, colors); /* emit local color table */
gs->ncolorbits = ncolorbits;
} /* and set #bits for local table */
else
gs->ncolorbits = gs->ncolorbits_gct; /*else default to global table */
}
/* end-of-if(width>1&&height>1) --- */
/* ---
* lzw-encode the pixels
* ------------------------ */
if (pixels != NULL && npixels > 0) { /* have pixels to be encoded */
/* ---
* first overlay plaintext, if present
* ------------------------------------- */
if (gs->isplaintext > 0) { /* have plaintext data */
if (gs->pt_pixels == NULL) /* but need new pixel image */
gs->pt_pixels =
plainmimetext(gs->pt_data, &gs->pt_width,
&gs->pt_height);
if (gs->pt_pixels != NULL) /* got new image or still have old */
overpix = overlay(pixels, gs->width, gs->height, /* so overlay it */
gs->pt_pixels, gs->pt_width,
gs->pt_height, gs->pt_left, gs->pt_top,
gs->pt_bg, gs->pt_fg);
if (overpix != NULL) /*got text pixels overlaid on image */
pixels = overpix; /* so that's what we want to see */
}
/* --- end-of-if(gs->isplaintext>0) --- */
/* ---
* LZW minimum codesize byte
* ------------------------- */
codesize = max2(2, (gs->ncolorbits /*-1*/ )); /*local/global table, but >=2 */
status = putblkbyte(bk, codesize); /* emit min codesize byte */
if (!OKAY)
goto end_of_job; /* quit if failed */
/* ---
* lzw encode the image
* ----------------------- */
nbytes = encodelzw(gs, codesize, npixels, (BYTE *) pixels); /* encode pixels */
status = (nbytes < 1 ? (-1) : putblkbyte(bk, 0)); /* 0 terminates sub-blocks */
/* ---
* reset for next image
* ----------------------- */
gs->npixels = (-1); /* new image descrip for next image */
gs->ncontrol = 0; /* no controlgif() calls issued yet */
/* --- reset (plain)text overlay --- */
if (overpix != NULL)
free((void *) overpix); /*free text overlay pixels */
if (gs->isplaintext == 1) { /*plaintext was for this frame only */
if (gs->pt_pixels != NULL) { /*should have allocated pixel image */
free((void *) (gs->pt_pixels)); /* ...free it */
gs->pt_pixels = NULL;
} /* ...and reset its ptr */
gs->isplaintext = 0;
} /* always reset plaintext flag */
} /* --- end-of-if(pixels!=NULL&&npixels>0) --- */
end_of_job:
return ((OKAY ? bk->nblkbytes : status)); /* return current #bytes in image */