1 #include <stdio.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include "gd.h"
6
7 /* Code drawn from ppmtogif.c, from the pbmplus package
8 **
9 ** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
10 ** Lempel-Zim compression based on "compress".
11 **
12 ** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
13 **
14 ** Copyright (C) 1989 by Jef Poskanzer.
15 **
16 ** Permission to use, copy, modify, and distribute this software and its
17 ** documentation for any purpose and without fee is hereby granted, provided
18 ** that the above copyright notice appear in all copies and that both that
19 ** copyright notice and this permission notice appear in supporting
20 ** documentation. This software is provided "as is" without express or
21 ** implied warranty.
22 **
23 ** The Graphics Interchange Format(c) is the Copyright property of
24 ** CompuServe Incorporated. GIF(sm) is a Service Mark property of
25 ** CompuServe Incorporated.
26 */
27
28 /*
29 * a code_int must be able to hold 2**GIFBITS values of type int, and also -1
30 */
31 typedef int code_int;
32
33 #ifdef SIGNED_COMPARE_SLOW
34 typedef unsigned long int count_int;
35 typedef unsigned short int count_short;
36 #else /*SIGNED_COMPARE_SLOW*/
37 typedef long int count_int;
38 #endif /*SIGNED_COMPARE_SLOW*/
39
40 /* 2.0.28: threadsafe */
41
42 #define maxbits GIFBITS
43
44 /* should NEVER generate this code */
45 #define maxmaxcode ((code_int)1 << GIFBITS)
46
47 #define HSIZE 5003 /* 80% occupancy */
48 #define hsize HSIZE /* Apparently invariant, left over from
49 compress */
50
51 typedef struct {
52 int Width, Height;
53 int curx, cury;
54 long CountDown;
55 int Pass;
56 int Interlace;
57 int n_bits; /* number of bits/code */
58 code_int maxcode; /* maximum code, given n_bits */
59 count_int htab [HSIZE];
60 unsigned short codetab [HSIZE];
61 code_int free_ent; /* first unused entry */
62 /*
63 * block compression parameters -- after all codes are used up,
64 * and compression rate changes, start over.
65 */
66 int clear_flg;
67 int offset;
68 long int in_count; /* length of input */
69 long int out_count; /* # of codes output (for debugging) */
70
71 int g_init_bits;
72 gdIOCtx * g_outfile;
73
74 int ClearCode;
75 int EOFCode;
76 unsigned long cur_accum;
77 int cur_bits;
78 /*
79 * Number of characters so far in this 'packet'
80 */
81 int a_count;
82 /*
83 * Define the storage for the packet accumulator
84 */
85 char accum[ 256 ];
86 } GifCtx;
87
88 static int gifPutWord(int w, gdIOCtx *out);
89 static int colorstobpp(int colors);
90 static void BumpPixel (GifCtx *ctx);
91 static int GIFNextPixel (gdImagePtr im, GifCtx *ctx);
92 static void GIFEncode (gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
93 static void compress (int init_bits, gdIOCtx *outfile, gdImagePtr im, GifCtx *ctx);
94 static void output (code_int code, GifCtx *ctx);
95 static void cl_block (GifCtx *ctx);
96 static void cl_hash (register count_int chsize, GifCtx *ctx);
97 static void char_init (GifCtx *ctx);
98 static void char_out (int c, GifCtx *ctx);
99 static void flush_char (GifCtx *ctx);
gdImageGifPtr(gdImagePtr im,int * size)100 void * gdImageGifPtr (gdImagePtr im, int *size)
101 {
102 void *rv;
103 gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
104 gdImageGifCtx (im, out);
105 rv = gdDPExtractData (out, size);
106 out->gd_free (out);
107 return rv;
108 }
109
gdImageGif(gdImagePtr im,FILE * outFile)110 void gdImageGif (gdImagePtr im, FILE * outFile)
111 {
112 gdIOCtx *out = gdNewFileCtx (outFile);
113 gdImageGifCtx (im, out);
114 out->gd_free (out);
115 }
116
gdImageGifCtx(gdImagePtr im,gdIOCtxPtr out)117 void gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out)
118 {
119 gdImagePtr pim = 0, tim = im;
120 int interlace, BitsPerPixel;
121 interlace = im->interlace;
122 if (im->trueColor) {
123 /* Expensive, but the only way that produces an
124 acceptable result: mix down to a palette
125 based temporary image. */
126 pim = gdImageCreatePaletteFromTrueColor(im, 1, 256);
127 if (!pim) {
128 return;
129 }
130 tim = pim;
131 }
132 BitsPerPixel = colorstobpp(tim->colorsTotal);
133 /* All set, let's do it. */
134 GIFEncode(
135 out, tim->sx, tim->sy, interlace, 0, tim->transparent, BitsPerPixel,
136 tim->red, tim->green, tim->blue, tim);
137 if (pim) {
138 /* Destroy palette based temporary image. */
139 gdImageDestroy( pim);
140 }
141 }
142
143 static int
colorstobpp(int colors)144 colorstobpp(int colors)
145 {
146 int bpp = 0;
147
148 if ( colors <= 2 )
149 bpp = 1;
150 else if ( colors <= 4 )
151 bpp = 2;
152 else if ( colors <= 8 )
153 bpp = 3;
154 else if ( colors <= 16 )
155 bpp = 4;
156 else if ( colors <= 32 )
157 bpp = 5;
158 else if ( colors <= 64 )
159 bpp = 6;
160 else if ( colors <= 128 )
161 bpp = 7;
162 else if ( colors <= 256 )
163 bpp = 8;
164 return bpp;
165 }
166
167 /*****************************************************************************
168 *
169 * GIFENCODE.C - GIF Image compression interface
170 *
171 * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
172 * BitsPerPixel, Red, Green, Blue, gdImagePtr )
173 *
174 *****************************************************************************/
175
176 #define TRUE 1
177 #define FALSE 0
178 /*
179 * Bump the 'curx' and 'cury' to point to the next pixel
180 */
181 static void
BumpPixel(GifCtx * ctx)182 BumpPixel(GifCtx *ctx)
183 {
184 /*
185 * Bump the current X position
186 */
187 ++(ctx->curx);
188
189 /*
190 * If we are at the end of a scan line, set curx back to the beginning
191 * If we are interlaced, bump the cury to the appropriate spot,
192 * otherwise, just increment it.
193 */
194 if( ctx->curx == ctx->Width ) {
195 ctx->curx = 0;
196
197 if( !ctx->Interlace )
198 ++(ctx->cury);
199 else {
200 switch( ctx->Pass ) {
201
202 case 0:
203 ctx->cury += 8;
204 if( ctx->cury >= ctx->Height ) {
205 ++(ctx->Pass);
206 ctx->cury = 4;
207 }
208 break;
209
210 case 1:
211 ctx->cury += 8;
212 if( ctx->cury >= ctx->Height ) {
213 ++(ctx->Pass);
214 ctx->cury = 2;
215 }
216 break;
217
218 case 2:
219 ctx->cury += 4;
220 if( ctx->cury >= ctx->Height ) {
221 ++(ctx->Pass);
222 ctx->cury = 1;
223 }
224 break;
225
226 case 3:
227 ctx->cury += 2;
228 break;
229 }
230 }
231 }
232 }
233
234 /*
235 * Return the next pixel from the image
236 */
237 static int
GIFNextPixel(gdImagePtr im,GifCtx * ctx)238 GIFNextPixel(gdImagePtr im, GifCtx *ctx)
239 {
240 int r;
241
242 if( ctx->CountDown == 0 )
243 return EOF;
244
245 --(ctx->CountDown);
246
247 r = gdImageGetPixel(im, ctx->curx, ctx->cury);
248
249 BumpPixel(ctx);
250
251 return r;
252 }
253
254 /* public */
255
256 static void
GIFEncode(gdIOCtxPtr fp,int GWidth,int GHeight,int GInterlace,int Background,int Transparent,int BitsPerPixel,int * Red,int * Green,int * Blue,gdImagePtr im)257 GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
258 {
259 int B;
260 int RWidth, RHeight;
261 int LeftOfs, TopOfs;
262 int Resolution;
263 int ColorMapSize;
264 int InitCodeSize;
265 int i;
266 GifCtx ctx;
267
268 memset(&ctx, 0, sizeof(ctx));
269 ctx.Interlace = GInterlace;
270 ctx.in_count = 1;
271
272 ColorMapSize = 1 << BitsPerPixel;
273
274 RWidth = ctx.Width = GWidth;
275 RHeight = ctx.Height = GHeight;
276 LeftOfs = TopOfs = 0;
277
278 Resolution = BitsPerPixel;
279
280 /*
281 * Calculate number of bits we are expecting
282 */
283 ctx.CountDown = (long)ctx.Width * (long)ctx.Height;
284
285 /*
286 * Indicate which pass we are on (if interlace)
287 */
288 ctx.Pass = 0;
289
290 /*
291 * The initial code size
292 */
293 if( BitsPerPixel <= 1 )
294 InitCodeSize = 2;
295 else
296 InitCodeSize = BitsPerPixel;
297
298 /*
299 * Set up the current x and y position
300 */
301 ctx.curx = ctx.cury = 0;
302
303 /*
304 * Write the Magic header
305 */
306 gdPutBuf(Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp );
307
308 /*
309 * Write out the screen width and height
310 */
311 gifPutWord( RWidth, fp );
312 gifPutWord( RHeight, fp );
313
314 /*
315 * Indicate that there is a global colour map
316 */
317 B = 0x80; /* Yes, there is a color map */
318
319 /*
320 * OR in the resolution
321 */
322 B |= (Resolution - 1) << 5;
323
324 /*
325 * OR in the Bits per Pixel
326 */
327 B |= (BitsPerPixel - 1);
328
329 /*
330 * Write it out
331 */
332 gdPutC( B, fp );
333
334 /*
335 * Write out the Background colour
336 */
337 gdPutC( Background, fp );
338
339 /*
340 * Byte of 0's (future expansion)
341 */
342 gdPutC( 0, fp );
343
344 /*
345 * Write out the Global Colour Map
346 */
347 for( i=0; i<ColorMapSize; ++i ) {
348 gdPutC( Red[i], fp );
349 gdPutC( Green[i], fp );
350 gdPutC( Blue[i], fp );
351 }
352
353 /*
354 * Write out extension for transparent colour index, if necessary.
355 */
356 if ( Transparent >= 0 ) {
357 gdPutC( '!', fp );
358 gdPutC( 0xf9, fp );
359 gdPutC( 4, fp );
360 gdPutC( 1, fp );
361 gdPutC( 0, fp );
362 gdPutC( 0, fp );
363 gdPutC( (unsigned char) Transparent, fp );
364 gdPutC( 0, fp );
365 }
366
367 /*
368 * Write an Image separator
369 */
370 gdPutC( ',', fp );
371
372 /*
373 * Write the Image header
374 */
375
376 gifPutWord( LeftOfs, fp );
377 gifPutWord( TopOfs, fp );
378 gifPutWord( ctx.Width, fp );
379 gifPutWord( ctx.Height, fp );
380
381 /*
382 * Write out whether or not the image is interlaced
383 */
384 if( ctx.Interlace )
385 gdPutC( 0x40, fp );
386 else
387 gdPutC( 0x00, fp );
388
389 /*
390 * Write out the initial code size
391 */
392 gdPutC( InitCodeSize, fp );
393
394 /*
395 * Go and actually compress the data
396 */
397 compress( InitCodeSize+1, fp, im, &ctx );
398
399 /*
400 * Write out a Zero-length packet (to end the series)
401 */
402 gdPutC( 0, fp );
403
404 /*
405 * Write the GIF file terminator
406 */
407 gdPutC( ';', fp );
408 }
409
410 /***************************************************************************
411 *
412 * GIFCOMPR.C - GIF Image compression routines
413 *
414 * Lempel-Ziv compression based on 'compress'. GIF modifications by
415 * David Rowley (mgardi@watdcsu.waterloo.edu)
416 *
417 ***************************************************************************/
418
419 /*
420 * General DEFINEs
421 */
422
423 #define GIFBITS 12
424
425 #ifdef NO_UCHAR
426 typedef char char_type;
427 #else /*NO_UCHAR*/
428 typedef unsigned char char_type;
429 #endif /*NO_UCHAR*/
430
431 /*
432 *
433 * GIF Image compression - modified 'compress'
434 *
435 * Based on: compress.c - File compression ala IEEE Computer, June 1984.
436 *
437 * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
438 * Jim McKie (decvax!mcvax!jim)
439 * Steve Davies (decvax!vax135!petsd!peora!srd)
440 * Ken Turkowski (decvax!decwrl!turtlevax!ken)
441 * James A. Woods (decvax!ihnp4!ames!jaw)
442 * Joe Orost (decvax!vax135!petsd!joe)
443 *
444 */
445 #include <ctype.h>
446
447 #define ARGVAL() (*++(*argv) || (--argc && *++argv))
448
449 #ifdef COMPATIBLE /* But wrong! */
450 # define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1)
451 #else /*COMPATIBLE*/
452 # define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
453 #endif /*COMPATIBLE*/
454
455 #define HashTabOf(i) ctx->htab[i]
456 #define CodeTabOf(i) ctx->codetab[i]
457
458
459 /*
460 * To save much memory, we overlay the table used by compress() with those
461 * used by decompress(). The tab_prefix table is the same size and type
462 * as the codetab. The tab_suffix table needs 2**GIFBITS characters. We
463 * get this from the beginning of htab. The output stack uses the rest
464 * of htab, and contains characters. There is plenty of room for any
465 * possible stack (stack used to be 8000 characters).
466 */
467
468 #define tab_prefixof(i) CodeTabOf(i)
469 #define tab_suffixof(i) ((char_type*)(htab))[i]
470 #define de_stack ((char_type*)&tab_suffixof((code_int)1<<GIFBITS))
471
472 /*
473 * compress stdin to stdout
474 *
475 * Algorithm: use open addressing double hashing (no chaining) on the
476 * prefix code / next character combination. We do a variant of Knuth's
477 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
478 * secondary probe. Here, the modular division first probe is gives way
479 * to a faster exclusive-or manipulation. Also do block compression with
480 * an adaptive reset, whereby the code table is cleared when the compression
481 * ratio decreases, but after the table fills. The variable-length output
482 * codes are re-sized at this point, and a special CLEAR code is generated
483 * for the decompressor. Late addition: construct the table according to
484 * file size for noticeable speed improvement on small files. Please direct
485 * questions about this implementation to ames!jaw.
486 */
487
488 static void
489 output(code_int code, GifCtx *ctx);
490
491 static void
compress(int init_bits,gdIOCtxPtr outfile,gdImagePtr im,GifCtx * ctx)492 compress(int init_bits, gdIOCtxPtr outfile, gdImagePtr im, GifCtx *ctx)
493 {
494 register long fcode;
495 register code_int i /* = 0 */;
496 register int c;
497 register code_int ent;
498 register code_int disp;
499 register code_int hsize_reg;
500 register int hshift;
501
502 /*
503 * Set up the globals: g_init_bits - initial number of bits
504 * g_outfile - pointer to output file
505 */
506 ctx->g_init_bits = init_bits;
507 ctx->g_outfile = outfile;
508
509 /*
510 * Set up the necessary values
511 */
512 ctx->offset = 0;
513 ctx->out_count = 0;
514 ctx->clear_flg = 0;
515 ctx->in_count = 1;
516 ctx->maxcode = MAXCODE(ctx->n_bits = ctx->g_init_bits);
517
518 ctx->ClearCode = (1 << (init_bits - 1));
519 ctx->EOFCode = ctx->ClearCode + 1;
520 ctx->free_ent = ctx->ClearCode + 2;
521
522 char_init(ctx);
523
524 ent = GIFNextPixel( im, ctx );
525
526 hshift = 0;
527 for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
528 ++hshift;
529 hshift = 8 - hshift; /* set hash code range bound */
530
531 hsize_reg = hsize;
532 cl_hash( (count_int) hsize_reg, ctx ); /* clear hash table */
533
534 output( (code_int)ctx->ClearCode, ctx );
535
536 #ifdef SIGNED_COMPARE_SLOW
537 while ( (c = GIFNextPixel( im, ctx )) != (unsigned) EOF ) {
538 #else /*SIGNED_COMPARE_SLOW*/
539 while ( (c = GIFNextPixel( im, ctx )) != EOF ) { /* } */
540 #endif /*SIGNED_COMPARE_SLOW*/
541
542 ++(ctx->in_count);
543
544 fcode = (long) (((long) c << maxbits) + ent);
545 i = (((code_int)c << hshift) ^ ent); /* xor hashing */
546
547 if ( HashTabOf (i) == fcode ) {
548 ent = CodeTabOf (i);
549 continue;
550 } else if ( (long)HashTabOf (i) < 0 ) /* empty slot */
551 goto nomatch;
552 disp = hsize_reg - i; /* secondary hash (after G. Knott) */
553 if ( i == 0 )
554 disp = 1;
555 probe:
556 if ( (i -= disp) < 0 )
557 i += hsize_reg;
558
559 if ( HashTabOf (i) == fcode ) {
560 ent = CodeTabOf (i);
561 continue;
562 }
563 if ( (long)HashTabOf (i) > 0 )
564 goto probe;
565 nomatch:
566 output ( (code_int) ent, ctx );
567 ++(ctx->out_count);
568 ent = c;
569 #ifdef SIGNED_COMPARE_SLOW
570 if ( (unsigned) ctx->free_ent < (unsigned) maxmaxcode) {
571 #else /*SIGNED_COMPARE_SLOW*/
572 if ( ctx->free_ent < maxmaxcode ) { /* } */
573 #endif /*SIGNED_COMPARE_SLOW*/
574 CodeTabOf (i) = ctx->free_ent++; /* code -> hashtable */
575 HashTabOf (i) = fcode;
576 } else
577 cl_block(ctx);
578 }
579 /*
580 * Put out the final code.
581 */
582 output( (code_int)ent, ctx );
583 ++(ctx->out_count);
584 output( (code_int) ctx->EOFCode, ctx );
585 }
586
587 /*****************************************************************
588 * TAG( output )
589 *
590 * Output the given code.
591 * Inputs:
592 * code: A n_bits-bit integer. If == -1, then EOF. This assumes
593 * that n_bits =< (long)wordsize - 1.
594 * Outputs:
595 * Outputs code to the file.
596 * Assumptions:
597 * Chars are 8 bits long.
598 * Algorithm:
599 * Maintain a GIFBITS character long buffer (so that 8 codes will
600 * fit in it exactly). Use the VAX insv instruction to insert each
601 * code in turn. When the buffer fills up empty it and start over.
602 */
603
604 static const unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
605 0x001F, 0x003F, 0x007F, 0x00FF,
606 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
607 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
608
609
610 /* Arbitrary value to mark output is done. When we see EOFCode, then we don't
611 * expect to see any more data. If we do (e.g. corrupt image inputs), cur_bits
612 * might be negative, so flag it to return early.
613 */
614 #define CUR_BITS_FINISHED -1000
615
616
617 static void
618 output(code_int code, GifCtx *ctx)
619 {
620 if (ctx->cur_bits == CUR_BITS_FINISHED) {
621 return;
622 }
623
624 ctx->cur_accum &= masks[ ctx->cur_bits ];
625
626 if( ctx->cur_bits > 0 )
627 ctx->cur_accum |= ((long)code << ctx->cur_bits);
628 else
629 ctx->cur_accum = code;
630
631 ctx->cur_bits += ctx->n_bits;
632
633 while( ctx->cur_bits >= 8 ) {
634 char_out( (unsigned int)(ctx->cur_accum & 0xff), ctx );
635 ctx->cur_accum >>= 8;
636 ctx->cur_bits -= 8;
637 }
638
639 /*
640 * If the next entry is going to be too big for the code size,
641 * then increase it, if possible.
642 */
643 if ( ctx->free_ent > ctx->maxcode || ctx->clear_flg ) {
644
645 if( ctx->clear_flg ) {
646
647 ctx->maxcode = MAXCODE (ctx->n_bits = ctx->g_init_bits);
648 ctx->clear_flg = 0;
649
650 } else {
651
652 ++(ctx->n_bits);
653 if ( ctx->n_bits == maxbits )
654 ctx->maxcode = maxmaxcode;
655 else
656 ctx->maxcode = MAXCODE(ctx->n_bits);
657 }
658 }
659
660 if( code == ctx->EOFCode ) {
661 /*
662 * At EOF, write the rest of the buffer.
663 */
664 while( ctx->cur_bits > 0 ) {
665 char_out( (unsigned int)(ctx->cur_accum & 0xff), ctx);
666 ctx->cur_accum >>= 8;
667 ctx->cur_bits -= 8;
668 }
669
670 /* Flag that it's done to prevent re-entry. */
671 ctx->cur_bits = CUR_BITS_FINISHED;
672
673 flush_char(ctx);
674 }
675 }
676
677 /*
678 * Clear out the hash table
679 */
680 static void
681 cl_block (GifCtx *ctx) /* table clear for block compress */
682 {
683
684 cl_hash ( (count_int) hsize, ctx );
685 ctx->free_ent = ctx->ClearCode + 2;
686 ctx->clear_flg = 1;
687
688 output( (code_int)ctx->ClearCode, ctx);
689 }
690
691 static void
692 cl_hash(register count_int chsize, GifCtx *ctx) /* reset code table */
693
694 {
695
696 register count_int *htab_p = ctx->htab+chsize;
697
698 register long i;
699 register long m1 = -1;
700
701 i = chsize - 16;
702 do { /* might use Sys V memset(3) here */
703 *(htab_p-16) = m1;
704 *(htab_p-15) = m1;
705 *(htab_p-14) = m1;
706 *(htab_p-13) = m1;
707 *(htab_p-12) = m1;
708 *(htab_p-11) = m1;
709 *(htab_p-10) = m1;
710 *(htab_p-9) = m1;
711 *(htab_p-8) = m1;
712 *(htab_p-7) = m1;
713 *(htab_p-6) = m1;
714 *(htab_p-5) = m1;
715 *(htab_p-4) = m1;
716 *(htab_p-3) = m1;
717 *(htab_p-2) = m1;
718 *(htab_p-1) = m1;
719 htab_p -= 16;
720 } while ((i -= 16) >= 0);
721
722 for ( i += 16; i > 0; --i )
723 *--htab_p = m1;
724 }
725
726 /******************************************************************************
727 *
728 * GIF Specific routines
729 *
730 ******************************************************************************/
731
732 /*
733 * Set up the 'byte output' routine
734 */
735 static void
736 char_init(GifCtx *ctx)
737 {
738 ctx->a_count = 0;
739 }
740
741 /*
742 * Add a character to the end of the current packet, and if it is 254
743 * characters, flush the packet to disk.
744 */
745 static void
746 char_out(int c, GifCtx *ctx)
747 {
748 ctx->accum[ ctx->a_count++ ] = c;
749 if( ctx->a_count >= 254 )
750 flush_char(ctx);
751 }
752
753 /*
754 * Flush the packet to disk, and reset the accumulator
755 */
756 static void
757 flush_char(GifCtx *ctx)
758 {
759 if( ctx->a_count > 0 ) {
760 gdPutC( ctx->a_count, ctx->g_outfile );
761 gdPutBuf( ctx->accum, ctx->a_count, ctx->g_outfile );
762 ctx->a_count = 0;
763 }
764 }
765
766 static int gifPutWord(int w, gdIOCtx *out)
767 {
768 /* Byte order is little-endian */
769 gdPutC(w & 0xFF, out);
770 gdPutC((w >> 8) & 0xFF, out);
771 return 0;
772 }
773
774
775