Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkImgGIF.c
1810 views
1
/*
2
* tkImgGIF.c --
3
*
4
* A photo image file handler for GIF files. Reads 87a and 89a GIF files.
5
* At present there is no write function.
6
*
7
* Derived from the giftoppm code found in the pbmplus package
8
* and tkImgFmtPPM.c in the tk4.0b2 distribution by -
9
*
10
* Reed Wade ([email protected]), University of Tennessee
11
*
12
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
13
*
14
* See the file "license.terms" for information on usage and redistribution
15
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
*
17
* This file also contains code from the giftoppm program, which is
18
* copyrighted as follows:
19
*
20
* +-------------------------------------------------------------------+
21
* | Copyright 1990, David Koblas. |
22
* | Permission to use, copy, modify, and distribute this software |
23
* | and its documentation for any purpose and without fee is hereby |
24
* | granted, provided that the above copyright notice appear in all |
25
* | copies and that both that copyright notice and this permission |
26
* | notice appear in supporting documentation. This software is |
27
* | provided "as is" without express or implied warranty. |
28
* +-------------------------------------------------------------------+
29
*
30
* SCCS: @(#) tkImgGIF.c 1.9 96/07/19 09:57:18
31
*/
32
33
#include "tkInt.h"
34
35
/*
36
* The format record for the GIF file format:
37
*/
38
39
static int FileMatchGIF _ANSI_ARGS_((FILE *f, char *fileName,
40
char *formatString, int *widthPtr, int *heightPtr));
41
static int FileReadGIF _ANSI_ARGS_((Tcl_Interp *interp,
42
FILE *f, char *fileName, char *formatString,
43
Tk_PhotoHandle imageHandle, int destX, int destY,
44
int width, int height, int srcX, int srcY));
45
46
Tk_PhotoImageFormat tkImgFmtGIF = {
47
"GIF", /* name */
48
FileMatchGIF, /* fileMatchProc */
49
NULL, /* stringMatchProc */
50
FileReadGIF, /* fileReadProc */
51
NULL, /* stringReadProc */
52
NULL, /* fileWriteProc */
53
NULL, /* stringWriteProc */
54
};
55
56
#define INTERLACE 0x40
57
#define LOCALCOLORMAP 0x80
58
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
59
#define MAXCOLORMAPSIZE 256
60
#define CM_RED 0
61
#define CM_GREEN 1
62
#define CM_BLUE 2
63
#define MAX_LWZ_BITS 12
64
#define LM_to_uint(a,b) (((b)<<8)|(a))
65
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
66
67
/*
68
* Prototypes for local procedures defined in this file:
69
*/
70
71
static int DoExtension _ANSI_ARGS_((FILE *fd, int label,
72
int *transparent));
73
static int GetCode _ANSI_ARGS_((FILE *fd, int code_size,
74
int flag));
75
static int GetDataBlock _ANSI_ARGS_((FILE *fd,
76
unsigned char *buf));
77
static int LWZReadByte _ANSI_ARGS_((FILE *fd, int flag,
78
int input_code_size));
79
static int ReadColorMap _ANSI_ARGS_((FILE *fd, int number,
80
unsigned char buffer[3][MAXCOLORMAPSIZE]));
81
static int ReadGIFHeader _ANSI_ARGS_((FILE *f, int *widthPtr,
82
int *heightPtr));
83
static int ReadImage _ANSI_ARGS_((Tcl_Interp *interp,
84
char *imagePtr, FILE *fd, int len, int height,
85
unsigned char cmap[3][MAXCOLORMAPSIZE],
86
int interlace, int transparent));
87
88
/*
89
*----------------------------------------------------------------------
90
*
91
* FileMatchGIF --
92
*
93
* This procedure is invoked by the photo image type to see if
94
* a file contains image data in GIF format.
95
*
96
* Results:
97
* The return value is 1 if the first characters in file f look
98
* like GIF data, and 0 otherwise.
99
*
100
* Side effects:
101
* The access position in f may change.
102
*
103
*----------------------------------------------------------------------
104
*/
105
106
static int
107
FileMatchGIF(f, fileName, formatString, widthPtr, heightPtr)
108
FILE *f; /* The image file, open for reading. */
109
char *fileName; /* The name of the image file. */
110
char *formatString; /* User-specified format string, or NULL. */
111
int *widthPtr, *heightPtr; /* The dimensions of the image are
112
* returned here if the file is a valid
113
* raw GIF file. */
114
{
115
return ReadGIFHeader(f, widthPtr, heightPtr);
116
}
117
118
/*
119
*----------------------------------------------------------------------
120
*
121
* FileReadGIF --
122
*
123
* This procedure is called by the photo image type to read
124
* GIF format data from a file and write it into a given
125
* photo image.
126
*
127
* Results:
128
* A standard TCL completion code. If TCL_ERROR is returned
129
* then an error message is left in interp->result.
130
*
131
* Side effects:
132
* The access position in file f is changed, and new data is
133
* added to the image given by imageHandle.
134
*
135
*----------------------------------------------------------------------
136
*/
137
138
static int
139
FileReadGIF(interp, f, fileName, formatString, imageHandle, destX, destY,
140
width, height, srcX, srcY)
141
Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
142
FILE *f; /* The image file, open for reading. */
143
char *fileName; /* The name of the image file. */
144
char *formatString; /* User-specified format string, or NULL. */
145
Tk_PhotoHandle imageHandle; /* The photo image to write into. */
146
int destX, destY; /* Coordinates of top-left pixel in
147
* photo image to be written to. */
148
int width, height; /* Dimensions of block of photo image to
149
* be written to. */
150
int srcX, srcY; /* Coordinates of top-left pixel to be used
151
* in image being read. */
152
{
153
int fileWidth, fileHeight;
154
int nBytes;
155
Tk_PhotoImageBlock block;
156
unsigned char buf[100];
157
int bitPixel;
158
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
159
unsigned char colorMap[3][MAXCOLORMAPSIZE];
160
int useGlobalColormap;
161
int transparent = -1;
162
163
if (!ReadGIFHeader(f, &fileWidth, &fileHeight)) {
164
Tcl_AppendResult(interp, "couldn't read GIF header from file \"",
165
fileName, "\"", NULL);
166
return TCL_ERROR;
167
}
168
if ((fileWidth <= 0) || (fileHeight <= 0)) {
169
Tcl_AppendResult(interp, "GIF image file \"", fileName,
170
"\" has dimension(s) <= 0", (char *) NULL);
171
return TCL_ERROR;
172
}
173
174
if (fread(buf, 1, 3, f) != 3) {
175
return TCL_OK;
176
}
177
bitPixel = 2<<(buf[0]&0x07);
178
179
if (BitSet(buf[0], LOCALCOLORMAP)) { /* Global Colormap */
180
if (!ReadColorMap(f, bitPixel, colorMap)) {
181
Tcl_AppendResult(interp, "error reading color map",
182
(char *) NULL);
183
return TCL_ERROR;
184
}
185
}
186
187
if ((srcX + width) > fileWidth) {
188
width = fileWidth - srcX;
189
}
190
if ((srcY + height) > fileHeight) {
191
height = fileHeight - srcY;
192
}
193
if ((width <= 0) || (height <= 0)
194
|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
195
return TCL_OK;
196
}
197
198
Tk_PhotoExpand(imageHandle, destX + width, destY + height);
199
200
block.width = fileWidth;
201
block.height = fileHeight;
202
block.pixelSize = 3;
203
block.pitch = 3 * fileWidth;
204
block.offset[0] = 0;
205
block.offset[1] = 1;
206
block.offset[2] = 2;
207
nBytes = fileHeight * block.pitch;
208
block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes);
209
210
while (1) {
211
if (fread(buf, 1, 1, f) != 1) {
212
/*
213
* Premature end of image. We should really notify
214
* the user, but for now just show garbage.
215
*/
216
217
break;
218
}
219
220
if (buf[0] == ';') {
221
/*
222
* GIF terminator.
223
*/
224
225
break;
226
}
227
228
if (buf[0] == '!') {
229
/*
230
* This is a GIF extension.
231
*/
232
233
if (fread(buf, 1, 1, f) != 1) {
234
interp->result =
235
"error reading extension function code in GIF image";
236
goto error;
237
}
238
if (DoExtension(f, buf[0], &transparent) < 0) {
239
interp->result = "error reading extension in GIF image";
240
goto error;
241
}
242
continue;
243
}
244
245
if (buf[0] != ',') {
246
/*
247
* Not a valid start character; ignore it.
248
*/
249
continue;
250
}
251
252
if (fread(buf, 1, 9, f) != 9) {
253
interp->result = "couldn't read left/top/width/height in GIF image";
254
goto error;
255
}
256
257
useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
258
259
bitPixel = 1<<((buf[8]&0x07)+1);
260
261
if (!useGlobalColormap) {
262
if (!ReadColorMap(f, bitPixel, localColorMap)) {
263
Tcl_AppendResult(interp, "error reading color map",
264
(char *) NULL);
265
goto error;
266
}
267
if (ReadImage(interp, (char *) block.pixelPtr, f, fileWidth,
268
fileHeight, localColorMap, BitSet(buf[8], INTERLACE),
269
transparent) != TCL_OK) {
270
goto error;
271
}
272
} else {
273
if (ReadImage(interp, (char *) block.pixelPtr, f, fileWidth,
274
fileHeight, colorMap, BitSet(buf[8], INTERLACE),
275
transparent) != TCL_OK) {
276
goto error;
277
}
278
}
279
break;
280
}
281
282
Tk_PhotoPutBlock(imageHandle, &block, destX, destY, fileWidth, fileHeight);
283
ckfree((char *) block.pixelPtr);
284
return TCL_OK;
285
286
error:
287
ckfree((char *) block.pixelPtr);
288
return TCL_ERROR;
289
290
}
291
292
/*
293
*----------------------------------------------------------------------
294
*
295
* ReadGIFHeader --
296
*
297
* This procedure reads the GIF header from the beginning of a
298
* GIF file and returns the dimensions of the image.
299
*
300
* Results:
301
* The return value is 1 if file "f" appears to start with
302
* a valid GIF header, 0 otherwise. If the header is valid,
303
* then *widthPtr and *heightPtr are modified to hold the
304
* dimensions of the image.
305
*
306
* Side effects:
307
* The access position in f advances.
308
*
309
*----------------------------------------------------------------------
310
*/
311
312
static int
313
ReadGIFHeader(f, widthPtr, heightPtr)
314
FILE *f; /* Image file to read the header from */
315
int *widthPtr, *heightPtr; /* The dimensions of the image are
316
* returned here. */
317
{
318
unsigned char buf[7];
319
320
if ((fread(buf, 1, 6, f) != 6)
321
|| ((strncmp("GIF87a", (char *) buf, 6) != 0)
322
&& (strncmp("GIF89a", (char *) buf, 6) != 0))) {
323
return 0;
324
}
325
326
if (fread(buf, 1, 4, f) != 4) {
327
return 0;
328
}
329
330
*widthPtr = LM_to_uint(buf[0],buf[1]);
331
*heightPtr = LM_to_uint(buf[2],buf[3]);
332
return 1;
333
}
334
335
/*
336
*-----------------------------------------------------------------
337
* The code below is copied from the giftoppm program and modified
338
* just slightly.
339
*-----------------------------------------------------------------
340
*/
341
342
static int
343
ReadColorMap(fd,number,buffer)
344
FILE *fd;
345
int number;
346
unsigned char buffer[3][MAXCOLORMAPSIZE];
347
{
348
int i;
349
unsigned char rgb[3];
350
351
for (i = 0; i < number; ++i) {
352
if (! ReadOK(fd, rgb, sizeof(rgb)))
353
return 0;
354
355
buffer[CM_RED][i] = rgb[0] ;
356
buffer[CM_GREEN][i] = rgb[1] ;
357
buffer[CM_BLUE][i] = rgb[2] ;
358
}
359
return 1;
360
}
361
362
363
364
static int
365
DoExtension(fd, label, transparent)
366
FILE *fd;
367
int label;
368
int *transparent;
369
{
370
static unsigned char buf[256];
371
int count = 0;
372
373
switch (label) {
374
case 0x01: /* Plain Text Extension */
375
break;
376
377
case 0xff: /* Application Extension */
378
break;
379
380
case 0xfe: /* Comment Extension */
381
do {
382
count = GetDataBlock(fd, (unsigned char*) buf);
383
} while (count > 0);
384
return count;
385
386
case 0xf9: /* Graphic Control Extension */
387
count = GetDataBlock(fd, (unsigned char*) buf);
388
if (count < 0) {
389
return 1;
390
}
391
if ((buf[0] & 0x1) != 0) {
392
*transparent = buf[3];
393
}
394
395
do {
396
count = GetDataBlock(fd, (unsigned char*) buf);
397
} while (count > 0);
398
return count;
399
}
400
401
do {
402
count = GetDataBlock(fd, (unsigned char*) buf);
403
} while (count > 0);
404
return count;
405
}
406
407
static int ZeroDataBlock = 0;
408
409
static int
410
GetDataBlock(fd, buf)
411
FILE *fd;
412
unsigned char *buf;
413
{
414
unsigned char count;
415
416
if (! ReadOK(fd,&count,1)) {
417
return -1;
418
}
419
420
ZeroDataBlock = count == 0;
421
422
if ((count != 0) && (! ReadOK(fd, buf, count))) {
423
return -1;
424
}
425
426
return count;
427
}
428
429
430
static int
431
ReadImage(interp, imagePtr, fd, len, height, cmap, interlace, transparent)
432
Tcl_Interp *interp;
433
char *imagePtr;
434
FILE *fd;
435
int len, height;
436
unsigned char cmap[3][MAXCOLORMAPSIZE];
437
int interlace;
438
int transparent;
439
{
440
unsigned char c;
441
int v;
442
int xpos = 0, ypos = 0, pass = 0;
443
char *colStr;
444
445
446
/*
447
* Initialize the Compression routines
448
*/
449
if (! ReadOK(fd,&c,1)) {
450
Tcl_AppendResult(interp, "error reading GIF image: ",
451
Tcl_PosixError(interp), (char *) NULL);
452
return TCL_ERROR;
453
}
454
455
if (LWZReadByte(fd, 1, c) < 0) {
456
interp->result = "format error in GIF image";
457
return TCL_ERROR;
458
}
459
460
if (transparent != -1) {
461
colStr = Tcl_GetVar(interp, "TRANSPARENT_GIF_COLOR", 0L);
462
if (colStr != NULL) {
463
XColor *colorPtr;
464
colorPtr = Tk_GetColor(interp, Tk_MainWindow(interp),
465
Tk_GetUid(colStr));
466
if (colorPtr) {
467
cmap[CM_RED][transparent] = colorPtr->red >> 8;
468
cmap[CM_GREEN][transparent] = colorPtr->green >> 8;
469
cmap[CM_BLUE][transparent] = colorPtr->blue >> 8;
470
Tk_FreeColor(colorPtr);
471
}
472
}
473
}
474
475
while ((v = LWZReadByte(fd,0,c)) >= 0 ) {
476
477
imagePtr[ (xpos*3) + (ypos *len*3)] = cmap[CM_RED][v];
478
imagePtr[ (xpos*3) + (ypos *len*3) +1] = cmap[CM_GREEN][v];
479
imagePtr[ (xpos*3) + (ypos *len*3) +2] = cmap[CM_BLUE][v];
480
481
++xpos;
482
if (xpos == len) {
483
xpos = 0;
484
if (interlace) {
485
switch (pass) {
486
case 0:
487
case 1:
488
ypos += 8; break;
489
case 2:
490
ypos += 4; break;
491
case 3:
492
ypos += 2; break;
493
}
494
495
if (ypos >= height) {
496
++pass;
497
switch (pass) {
498
case 1:
499
ypos = 4; break;
500
case 2:
501
ypos = 2; break;
502
case 3:
503
ypos = 1; break;
504
default:
505
return TCL_OK;
506
}
507
}
508
} else {
509
++ypos;
510
}
511
}
512
if (ypos >= height)
513
break;
514
}
515
return TCL_OK;
516
}
517
518
static int
519
LWZReadByte(fd, flag, input_code_size)
520
FILE *fd;
521
int flag;
522
int input_code_size;
523
{
524
static int fresh = 0;
525
int code, incode;
526
static int code_size, set_code_size;
527
static int max_code, max_code_size;
528
static int firstcode, oldcode;
529
static int clear_code, end_code;
530
static int table[2][(1<< MAX_LWZ_BITS)];
531
static int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
532
register int i;
533
534
535
if (flag) {
536
537
set_code_size = input_code_size;
538
code_size = set_code_size+1;
539
clear_code = 1 << set_code_size ;
540
end_code = clear_code + 1;
541
max_code_size = 2*clear_code;
542
max_code = clear_code+2;
543
544
GetCode(fd, 0, 1);
545
546
fresh = 1;
547
548
for (i = 0; i < clear_code; ++i) {
549
table[0][i] = 0;
550
table[1][i] = i;
551
}
552
for (; i < (1<<MAX_LWZ_BITS); ++i) {
553
table[0][i] = table[1][0] = 0;
554
}
555
556
sp = stack;
557
558
return 0;
559
560
} else if (fresh) {
561
562
fresh = 0;
563
do {
564
firstcode = oldcode = GetCode(fd, code_size, 0);
565
} while (firstcode == clear_code);
566
return firstcode;
567
}
568
569
if (sp > stack)
570
return *--sp;
571
572
while ((code = GetCode(fd, code_size, 0)) >= 0) {
573
if (code == clear_code) {
574
for (i = 0; i < clear_code; ++i) {
575
table[0][i] = 0;
576
table[1][i] = i;
577
}
578
579
for (; i < (1<<MAX_LWZ_BITS); ++i) {
580
table[0][i] = table[1][i] = 0;
581
}
582
583
code_size = set_code_size+1;
584
max_code_size = 2*clear_code;
585
max_code = clear_code+2;
586
sp = stack;
587
firstcode = oldcode = GetCode(fd, code_size, 0);
588
return firstcode;
589
590
} else if (code == end_code) {
591
int count;
592
unsigned char buf[260];
593
594
if (ZeroDataBlock)
595
return -2;
596
597
while ((count = GetDataBlock(fd, buf)) > 0)
598
;
599
600
if (count != 0)
601
return -2;
602
}
603
604
incode = code;
605
606
if (code >= max_code) {
607
*sp++ = firstcode;
608
code = oldcode;
609
}
610
611
while (code >= clear_code) {
612
*sp++ = table[1][code];
613
if (code == table[0][code]) {
614
return -2;
615
616
/*
617
* Used to be this instead, Steve Ball suggested
618
* the change to just return.
619
620
printf("circular table entry BIG ERROR\n");
621
*/
622
}
623
code = table[0][code];
624
}
625
626
*sp++ = firstcode = table[1][code];
627
628
if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
629
630
table[0][code] = oldcode;
631
table[1][code] = firstcode;
632
++max_code;
633
if ((max_code>=max_code_size) && (max_code_size < (1<<MAX_LWZ_BITS))) {
634
max_code_size *= 2;
635
++code_size;
636
}
637
}
638
639
oldcode = incode;
640
641
if (sp > stack)
642
return *--sp;
643
}
644
return code;
645
}
646
647
648
static int
649
GetCode(fd, code_size, flag)
650
FILE *fd;
651
int code_size;
652
int flag;
653
{
654
static unsigned char buf[280];
655
static int curbit, lastbit, done, last_byte;
656
int i, j, ret;
657
unsigned char count;
658
659
if (flag) {
660
curbit = 0;
661
lastbit = 0;
662
done = 0;
663
return 0;
664
}
665
666
667
if ( (curbit+code_size) >= lastbit) {
668
if (done) {
669
/* ran off the end of my bits */
670
return -1;
671
}
672
buf[0] = buf[last_byte-2];
673
buf[1] = buf[last_byte-1];
674
675
if ((count = GetDataBlock(fd, &buf[2])) == 0)
676
done = 1;
677
678
last_byte = 2 + count;
679
curbit = (curbit - lastbit) + 16;
680
lastbit = (2+count)*8 ;
681
}
682
683
ret = 0;
684
for (i = curbit, j = 0; j < code_size; ++i, ++j)
685
ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
686
687
688
curbit += code_size;
689
690
return ret;
691
}
692
693