1 /*
2  * tiff.c --
3  *
4  * A photo image file handler for TIFF files.
5  *
6  * Uses the libtiff.so library, which is dynamically
7  * loaded only when used.
8  */
9 
10 /* Author : Jan Nijtmans */
11 /* Date   : 7/16/97      */
12 
13 /*
14  * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
15  */
16 
17 #ifdef _WIN32
18 #   define HAVE_BOOLEAN
19 #   include <Windows.h>
20 #endif
21 #include "tkimg.h"
22 #ifdef EXTERN
23 #	undef EXTERN
24 #endif
25 #include <tiff.h>
26 #include <tiffio.h>
27 #include <zlib.h>
28 
29 #ifdef HAVE_STDLIB_H
30 #undef HAVE_STDLIB_H
31 #endif
32 #include <jpeglib.h>
33 
34 static int SetupTiffLibrary(Tcl_Interp *interp);
35 
36 #define MORE_INITIALIZATION \
37     if (SetupTiffLibrary (interp) != TCL_OK) { return TCL_ERROR; }
38 
39 #include "init.c"
40 
41 #include "tiffInt.h"
42 
43 /*
44  * Prototypes for local procedures defined in this file:
45  */
46 
47 static int getint(unsigned char *buf, TIFFDataType format, int order);
48 
49 static int CommonMatch(tkimg_MFile *handle, int *widhtPtr, int *heightPtr);
50 
51 static int CommonRead(Tcl_Interp *interp, TIFF *tif,
52        Tcl_Obj *format, Tk_PhotoHandle imageHandle, int destX, int destY,
53        int width, int height, int srcX, int srcY);
54 
55 static int CommonWrite(Tcl_Interp *interp, TIFF *tif,
56        int comp, Tk_PhotoImageBlock *blockPtr);
57 
58 static int ParseWriteFormat(Tcl_Interp *interp, Tcl_Obj *format,
59        int *comp, const char **mode);
60 
61 static void _TIFFerr(const char *, const char *, va_list);
62 static void _TIFFwarn(const char *, const char *, va_list);
63 
64 /*
65  * The functions for the TIFF input handler
66  */
67 
68 static int mapDummy(thandle_t, tdata_t *, toff_t *);
69 static void unMapDummy(thandle_t, tdata_t, toff_t);
70 static int closeDummy(thandle_t);
71 static tsize_t writeDummy(thandle_t, tdata_t, tsize_t);
72 
73 static tsize_t readMFile(thandle_t, tdata_t, tsize_t);
74 static toff_t seekMFile(thandle_t, toff_t, int);
75 static toff_t sizeMFile(thandle_t);
76 
77 static tsize_t readString(thandle_t, tdata_t, tsize_t);
78 static tsize_t writeString(thandle_t, tdata_t, tsize_t);
79 static toff_t seekString(thandle_t, toff_t, int);
80 static toff_t sizeString(thandle_t);
81 
82 static char *errorMessage = NULL;
83 
84 static int
SetupTiffLibrary(Tcl_Interp * interp)85 SetupTiffLibrary (Tcl_Interp *interp)
86 {
87     TIFFSetErrorHandler(_TIFFerr);
88     TIFFSetWarningHandler(_TIFFwarn);
89 
90     return TCL_OK;
91 }
92 
93 static int
getint(unsigned char * buf,TIFFDataType format,int order)94 getint(
95     unsigned char *buf,
96     TIFFDataType format,
97     int order
98 ) {
99     int result;
100 
101     switch (format) {
102         case TIFF_BYTE:
103             result = buf[0]; break;
104         case TIFF_SHORT:
105             result = (buf[order]<<8) + buf[1-order]; break;
106         case TIFF_LONG:
107             if (order) {
108                 result = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0];
109             } else {
110                 result = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
111             }; break;
112         default:
113             result = -1;
114     }
115     return result;
116 }
117 
118 static void
_TIFFerr(const char * module,const char * fmt,va_list ap)119 _TIFFerr(
120      const char *module,
121      const char *fmt,
122      va_list     ap
123 ) {
124     char buf [2048];
125     char *cp = buf;
126 
127     if (module != NULL) {
128         sprintf(cp, "%s: ", module);
129         cp += strlen(module) + 2;
130     }
131 
132     vsprintf(cp, fmt, ap);
133     if (errorMessage) {
134         ckfree(errorMessage);
135     }
136     if (strstr(buf, "Null count for")) {
137         return;
138     }
139     errorMessage = (char *) ckalloc(strlen(buf)+1);
140     strcpy(errorMessage, buf);
141 }
142 
143 /* warnings are not processed in Tcl */
144 static void
_TIFFwarn(const char * module,const char * fmt,va_list ap)145 _TIFFwarn(
146      const char *module,
147      const char *fmt,
148      va_list     ap
149 ) {
150 }
151 
152 static int
mapDummy(thandle_t fd,tdata_t * base,toff_t * size)153 mapDummy(
154     thandle_t fd,
155     tdata_t *base,
156     toff_t *size
157 ) {
158     return (toff_t) 0;
159 }
160 
161 static void
unMapDummy(thandle_t fd,tdata_t base,toff_t size)162 unMapDummy(
163     thandle_t fd,
164     tdata_t base,
165     toff_t size
166 ) {
167 }
168 
169 static int
closeDummy(thandle_t fd)170 closeDummy(thandle_t fd)
171 {
172     return 0;
173 }
174 
175 static tsize_t
writeDummy(thandle_t fd,tdata_t data,tsize_t size)176 writeDummy(
177     thandle_t fd,
178     tdata_t data,
179     tsize_t size
180 ) {
181    return size;
182 }
183 
184 static tsize_t
readMFile(thandle_t fd,tdata_t data,tsize_t size)185 readMFile(
186     thandle_t fd,
187     tdata_t data,
188     tsize_t size
189 ) {
190     return (tsize_t) tkimg_Read2((tkimg_MFile *) fd, (char *) data, size) ;
191 }
192 
193 static toff_t
seekMFile(thandle_t fd,toff_t off,int whence)194 seekMFile(
195     thandle_t fd,
196     toff_t off,
197     int whence
198 ) {
199     return Tcl_Seek((Tcl_Channel) ((tkimg_MFile *) fd)->data, (int) off, whence);
200 }
201 
202 static toff_t
sizeMFile(thandle_t fd)203 sizeMFile(thandle_t fd)
204 {
205     int fsize;
206     return (fsize = Tcl_Seek((Tcl_Channel) ((tkimg_MFile *) fd)->data,
207            (int) 0, SEEK_END)) < 0 ? 0 : (toff_t) fsize;
208 }
209 
210 /*
211  * In the following functions "handle" is used differently for speed reasons:
212  *
213  *      handle.buffer   (writing only) dstring used for writing.
214  *      handle.data     pointer to first character
215  *      handle.length   size of data
216  *      handle.state    "file" position pointer.
217  *
218  * After a read, only the position pointer is adapted, not the other fields.
219  */
220 
221 static tsize_t
readString(thandle_t fd,tdata_t data,tsize_t size)222 readString(
223     thandle_t fd,
224     tdata_t data,
225     tsize_t size
226 ) {
227     tkimg_MFile *handle = (tkimg_MFile *) fd;
228 
229     if (((size_t)size + handle->state) > handle->length) {
230         size = handle->length - handle->state;
231     }
232     if (size) {
233         memcpy((char *) data, handle->data + handle->state, (size_t) size);
234         handle->state += size;
235     }
236     return size;
237 }
238 
239 static tsize_t
writeString(thandle_t fd,tdata_t data,tsize_t size)240 writeString(
241     thandle_t fd,
242     tdata_t data,
243     tsize_t size
244 ) {
245     tkimg_MFile *handle = (tkimg_MFile *) fd;
246 
247     if (handle->state + (size_t)size > handle->length) {
248         handle->length = handle->state + size;
249         Tcl_DStringSetLength(handle->buffer, handle->length);
250         handle->data = Tcl_DStringValue(handle->buffer);
251     }
252     memcpy(handle->data + handle->state, (char *) data, (size_t) size);
253     handle->state += size;
254     return size;
255 }
256 
257 static toff_t
seekString(thandle_t fd,toff_t off,int whence)258 seekString(
259     thandle_t fd,
260     toff_t off,
261     int whence
262 ) {
263     tkimg_MFile *handle = (tkimg_MFile *) fd;
264 
265     switch (whence) {
266         case SEEK_SET:
267             handle->state = (int) off;
268             break;
269         case SEEK_CUR:
270             handle->state += (int) off;
271             break;
272         case SEEK_END:
273             handle->state = handle->length + (int) off;
274             break;
275     }
276     if (handle->state < 0) {
277         handle->state = 0;
278         return -1;
279     }
280     return (toff_t) handle->state;
281 }
282 
283 static toff_t
sizeString(thandle_t fd)284 sizeString(thandle_t fd)
285 {
286     return ((tkimg_MFile *) fd)->length;
287 }
288 
289 
290 /*
291  *----------------------------------------------------------------------
292  *
293  * ObjMatchTIFF --
294  *
295  *  This procedure is invoked by the photo image type to see if
296  *  a string contains image data in TIFF format.
297  *
298  * Results:
299  *  The return value is 1 if the first characters in the string
300  *  is like TIFF data, and 0 otherwise.
301  *
302  * Side effects:
303  *  the size of the image is placed in widthPre and heightPtr.
304  *
305  *----------------------------------------------------------------------
306  */
307 
308 static int
ObjMatch(Tcl_Obj * data,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)309 ObjMatch(
310     Tcl_Obj *data,              /* the object containing the image data */
311     Tcl_Obj *format,            /* the image format string */
312     int *widthPtr,              /* where to put the string width */
313     int *heightPtr,             /* where to put the string height */
314     Tcl_Interp *interp
315 ) {
316     tkimg_MFile handle;
317 
318     if (!tkimg_ReadInit(data, '\111', &handle) &&
319         !tkimg_ReadInit(data, '\115', &handle)) {
320         return 0;
321     }
322 
323     return CommonMatch(&handle, widthPtr, heightPtr);
324 }
325 
ChnMatch(Tcl_Channel chan,const char * fileName,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)326 static int ChnMatch(
327     Tcl_Channel chan,
328     const char *fileName,
329     Tcl_Obj *format,
330     int *widthPtr,
331     int *heightPtr,
332     Tcl_Interp *interp
333 ) {
334     tkimg_MFile handle;
335 
336     handle.data = (char *) chan;
337     handle.state = IMG_CHAN;
338 
339     return CommonMatch(&handle, widthPtr, heightPtr);
340 }
341 
342 static int
CommonMatch(tkimg_MFile * handle,int * widthPtr,int * heightPtr)343 CommonMatch(
344     tkimg_MFile *handle,
345     int *widthPtr, int *heightPtr
346 ) {
347     unsigned char buf[4096];
348     int i, j, order, w = 0, h = 0;
349 
350     i = tkimg_Read2(handle, (char *) buf, 8);
351     order = (buf[0] == '\111');
352     if ((i != 8) || (buf[0] != buf[1])
353         || ((buf[0] != '\111') && (buf[0] != '\115'))
354         || (getint(buf+2,TIFF_SHORT,order) != 42)) {
355         return 0;
356     }
357     i = getint(buf+4,TIFF_LONG,order);
358 
359     while (i > 4104) {
360         i -= 4096;
361         tkimg_Read2(handle, (char *) buf, 4096);
362     }
363     if (i>8) {
364         tkimg_Read2(handle, (char *) buf, i-8);
365     }
366     tkimg_Read2(handle, (char *) buf, 2);
367     i = getint(buf,TIFF_SHORT,order);
368     while (i--) {
369         tkimg_Read2(handle, (char *) buf, 12);
370         if (buf[order]!=1) continue;
371         j = getint(buf+2,TIFF_SHORT,order);
372         j = getint(buf+8, (TIFFDataType) j, order);
373         if (buf[1-order]==0) {
374             w = j;
375             if (h>0) break;
376         } else if (buf[1-order]==1) {
377             h = j;
378             if (w>0) break;
379         }
380     }
381 
382     if ((w <= 0) || (h <= 0)) {
383         return 0;
384     }
385     *widthPtr  = w;
386     *heightPtr = h;
387     return 1;
388 }
389 
390 static int
ObjRead(Tcl_Interp * interp,Tcl_Obj * data,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)391 ObjRead(
392     Tcl_Interp *interp,
393     Tcl_Obj *data,                      /* object containing the image */
394     Tcl_Obj *format,
395     Tk_PhotoHandle imageHandle,
396     int destX, int destY,
397     int width, int height,
398     int srcX, int srcY
399 ) {
400     TIFF *tif;
401     int result;
402     tkimg_MFile handle;
403     char *dataPtr = NULL;
404 
405     if (!tkimg_ReadInit(data, '\115', &handle)) {
406         tkimg_ReadInit(data, '\111', &handle);
407     }
408 
409     if (handle.state != IMG_STRING) {
410         dataPtr = ckalloc((handle.length*3)/4 + 2);
411         handle.length = tkimg_Read2(&handle, dataPtr, handle.length);
412         handle.data = dataPtr;
413     }
414     handle.state = 0;
415     tif = TIFFClientOpen("inline data", "r", (thandle_t) &handle,
416               readString, writeString, seekString, closeDummy,
417               sizeString, mapDummy, unMapDummy);
418 
419     if (tif != NULL) {
420         result = CommonRead(interp, tif, format, imageHandle,
421                  destX, destY, width, height, srcX, srcY);
422     } else {
423         result = TCL_ERROR;
424     }
425     if (result == TCL_ERROR) {
426         Tcl_AppendResult(interp, errorMessage, (char *) NULL);
427         ckfree(errorMessage);
428         errorMessage = NULL;
429     }
430     if (dataPtr) {
431         ckfree(dataPtr);
432     }
433     return result;
434 }
435 
436 static int
ChnRead(Tcl_Interp * interp,Tcl_Channel chan,const char * fileName,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)437 ChnRead(
438     Tcl_Interp *interp,
439     Tcl_Channel chan,
440     const char *fileName,
441     Tcl_Obj *format,
442     Tk_PhotoHandle imageHandle,
443     int destX, int destY,
444     int width, int height,
445     int srcX, int srcY
446 ) {
447     TIFF *tif;
448     int result;
449 
450     tkimg_MFile handle;
451     handle.data = (char *) chan;
452     handle.state = IMG_CHAN;
453     tif = TIFFClientOpen(fileName, "r", (thandle_t) &handle,
454           readMFile, writeDummy, seekMFile, closeDummy,
455           sizeMFile, mapDummy, unMapDummy);
456 
457     if (tif) {
458         result = CommonRead(interp, tif, format, imageHandle,
459                 destX, destY, width, height, srcX, srcY);
460     } else {
461         result = TCL_ERROR;
462     }
463     if (result == TCL_ERROR) {
464         Tcl_AppendResult(interp, errorMessage, (char *) NULL);
465         ckfree(errorMessage);
466         errorMessage = 0;
467     }
468     return result;
469 }
470 
471 static int
CommonRead(Tcl_Interp * interp,TIFF * tif,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)472 CommonRead(
473     Tcl_Interp *interp,
474     TIFF *tif,
475     Tcl_Obj *format,
476     Tk_PhotoHandle imageHandle,
477     int destX, int destY,
478     int width, int height,
479     int srcX, int srcY
480 ) {
481     Tk_PhotoImageBlock block;
482     uint32 w, h;
483     size_t npixels;
484     uint32 *raster;
485     int result = TCL_OK;
486     int nBytes, index = 0, objc = 0;
487     Tcl_Obj **objv = NULL;
488 
489     if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
490         return TCL_ERROR;
491     }
492     if (objc > 1) {
493         char *c = Tcl_GetStringFromObj(objv[1], &nBytes);
494         if ((objc > 3) || ((objc == 3) && ((c[0] != '-') ||
495                 (c[1] != 'i') || strncmp(c, "-index", strlen(c))))) {
496             Tcl_AppendResult(interp, "invalid format: \"",
497                     tkimg_GetStringFromObj2(format, NULL), "\"", (char *) NULL);
498             return TCL_ERROR;
499         }
500         if (Tcl_GetIntFromObj(interp, objv[objc-1], &index) != TCL_OK) {
501             return TCL_ERROR;
502         }
503     }
504     while (index-- != 0) {
505         if (TIFFReadDirectory(tif) != 1) {
506             Tcl_AppendResult(interp,"no image data for this index",
507                     (char *) NULL);
508             return TCL_ERROR;
509         }
510     }
511 #ifdef WORDS_BIGENDIAN
512     block.offset[0] = 3;
513     block.offset[1] = 2;
514     block.offset[2] = 1;
515     block.offset[3] = 0;
516 #else
517     block.offset[0] = 0;
518     block.offset[1] = 1;
519     block.offset[2] = 2;
520     block.offset[3] = 3;
521 #endif
522     block.pixelSize = sizeof (uint32);
523 
524     TIFFGetField(tif, TIFFTAG_IMAGEWIDTH,  &w);
525     TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
526     npixels = w * h;
527 
528     raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
529     block.width = w;
530     block.height = h;
531     block.pitch = - (block.pixelSize * (int) w);
532     block.pixelPtr = ((unsigned char *) raster) + ((1-h) * block.pitch);
533     if (raster == NULL) {
534         printf("cannot malloc\n");
535         return TCL_ERROR;
536     }
537 
538     if (!TIFFReadRGBAImage(tif, w, h, raster, 0) || errorMessage) {
539         _TIFFfree (raster);
540         if (errorMessage) {
541             Tcl_AppendResult(interp, errorMessage, (char *) NULL);
542             ckfree(errorMessage);
543             errorMessage = NULL;
544         }
545         return TCL_ERROR;
546     }
547 
548     block.pixelPtr += srcY * block.pitch + srcX * block.pixelSize;
549     block.offset[3] = block.offset[0]; /* don't use transparency */
550     if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX,
551                         destY, width, height, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
552         result = TCL_ERROR;
553     }
554 
555     _TIFFfree (raster);
556     TIFFClose(tif);
557     return result;
558 }
559 
StringWrite(Tcl_Interp * interp,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)560 static int StringWrite(
561     Tcl_Interp *interp,
562     Tcl_Obj *format,
563     Tk_PhotoImageBlock *blockPtr
564 ) {
565     TIFF *tif;
566     int result, comp;
567     tkimg_MFile handle;
568     Tcl_DString dstring;
569     const char *mode;
570     Tcl_DString data;
571 
572     Tcl_DStringInit(&data);
573     if (ParseWriteFormat(interp, format, &comp, &mode) != TCL_OK) {
574         return TCL_ERROR;
575     }
576 
577     Tcl_DStringInit(&dstring);
578     tkimg_WriteInit(&dstring, &handle);
579     tif = TIFFClientOpen("inline data", mode, (thandle_t) &handle,
580             readString, writeString, seekString, closeDummy,
581             sizeString, mapDummy, unMapDummy);
582 
583     result = CommonWrite(interp, tif, comp, blockPtr);
584     TIFFClose(tif);
585 
586     if (result != TCL_OK) {
587         Tcl_AppendResult(interp, errorMessage, (char *) NULL);
588         ckfree(errorMessage);
589         errorMessage = NULL;
590         return TCL_ERROR;
591     }
592 
593     int length = handle.length;
594     tkimg_WriteInit(&data, &handle);
595     tkimg_Write2(&handle, Tcl_DStringValue(&dstring), length);
596     Tcl_DStringFree(&dstring);
597 
598     tkimg_Putc(IMG_DONE, &handle);
599     Tcl_DStringResult(interp, &data);
600 
601     return TCL_OK;
602 }
603 
604 static int
ChnWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)605 ChnWrite(
606     Tcl_Interp *interp,
607     const char *filename,
608     Tcl_Obj *format,
609     Tk_PhotoImageBlock *blockPtr
610 ) {
611     TIFF *tif;
612     int result, comp;
613     Tcl_DString nameBuffer;
614     const char *fullname, *mode;
615 
616     if (!(fullname=Tcl_TranslateFileName(interp, filename, &nameBuffer))) {
617         return TCL_ERROR;
618     }
619 
620     if (ParseWriteFormat(interp, format, &comp, &mode) != TCL_OK) {
621         Tcl_DStringFree(&nameBuffer);
622         return TCL_ERROR;
623     }
624 
625     if (!(tif = TIFFOpen(fullname, mode))) {
626         Tcl_AppendResult(interp, filename, ": ", Tcl_PosixError(interp),
627                 (char *)NULL);
628         Tcl_DStringFree(&nameBuffer);
629         return TCL_ERROR;
630     }
631 
632     Tcl_DStringFree(&nameBuffer);
633 
634     result = CommonWrite(interp, tif, comp, blockPtr);
635     TIFFClose(tif);
636     return result;
637 }
638 
639 static int
ParseWriteFormat(Tcl_Interp * interp,Tcl_Obj * format,int * comp,const char ** mode)640 ParseWriteFormat(
641     Tcl_Interp *interp,
642     Tcl_Obj *format,
643     int *comp,
644     const char **mode
645 ) {
646     static const char *const tiffWriteOptions[] = {
647       "-compression",
648       "-byteorder",
649       NULL
650     };
651     int objc, length, c, i, index;
652     Tcl_Obj **objv;
653     const char *compression, *byteorder;
654 
655     *comp = COMPRESSION_NONE;
656     *mode = "w";
657     if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK)
658         return TCL_ERROR;
659     if (objc) {
660         compression = "none";
661         byteorder = "";
662         for (i=1; i<objc; i++) {
663             if (Tcl_GetIndexFromObj(interp, objv[i], (const char *CONST86 *)tiffWriteOptions,
664                     "format option", 0, &index) !=TCL_OK) {
665                 return TCL_ERROR;
666             }
667             if (++i >= objc) {
668                 Tcl_AppendResult(interp, "No value for option \"",
669                         Tcl_GetStringFromObj(objv[--i], (int *) NULL),
670                         "\"", (char *) NULL);
671                 return TCL_ERROR;
672             }
673             switch(index) {
674                 case 0:
675                     compression = Tcl_GetStringFromObj(objv[i], (int *) NULL); break;
676                 case 1:
677                     byteorder = Tcl_GetStringFromObj(objv[i], (int *) NULL); break;
678             }
679         }
680         c = compression[0]; length = strlen(compression);
681         if ((c == 'n') && (!strncmp(compression, "none", length))) {
682             *comp = COMPRESSION_NONE;
683         } else if ((c == 'd') && (!strncmp(compression, "deflate", length))) {
684             *comp = COMPRESSION_DEFLATE;
685         } else if ((c == 'j') && (!strncmp(compression, "jpeg", length))) {
686             *comp = COMPRESSION_JPEG;
687         } else if ((c == 'l') && (length>1) && (!strncmp(compression, "logluv", length))) {
688             *comp = COMPRESSION_SGILOG;
689         } else if ((c == 'l') && (length>1) && (!strncmp(compression, "lzw", length))) {
690             *comp = COMPRESSION_LZW;
691         } else if ((c == 'p') && (length>1) && (!strncmp(compression, "packbits", length))) {
692             *comp = COMPRESSION_PACKBITS;
693         } else if ((c == 'p') && (length>1) && (!strncmp(compression, "pixarlog", length))) {
694             *comp = COMPRESSION_PIXARLOG;
695         } else {
696             Tcl_AppendResult(interp, "invalid compression mode \"",
697                  compression,"\": should be deflate, jpeg, logluv, lzw, ",
698                         "packbits, pixarlog, or none", (char *) NULL);
699             return TCL_ERROR;
700         }
701         c = byteorder[0]; length = strlen(byteorder);
702         if (c == 0 || ((c == 'n') && (!strncmp(byteorder, "none", length)))) {
703             *mode = "w";
704         } else if ((c == 's') && (!strncmp(byteorder, "smallendian", length))) {
705             *mode = "wl";
706         } else if ((c == 'l') && (!strncmp(byteorder, "littleendian", length))) {
707             *mode = "wl";
708         } else if ((c == 'b') && (!strncmp(byteorder, "bigendian", length))) {
709             *mode = "wb";
710         } else if ((c == 'n') && (!strncmp(byteorder, "network", length))) {
711             *mode = "wb";
712         } else {
713             Tcl_AppendResult(interp, "invalid byteorder \"",
714                  byteorder,"\": should be bigendian, littleendian, ",
715                  "network, smallendian, or none", (char *) NULL);
716             return TCL_ERROR;
717         }
718     }
719     return TCL_OK;
720 }
721 
722 static int
CommonWrite(Tcl_Interp * interp,TIFF * tif,int comp,Tk_PhotoImageBlock * blockPtr)723 CommonWrite(
724     Tcl_Interp *interp,
725     TIFF *tif,
726     int comp,
727     Tk_PhotoImageBlock *blockPtr
728 ) {
729     int numsamples;
730     unsigned char *data = NULL;
731 
732     TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,  blockPtr->width);
733     TIFFSetField(tif, TIFFTAG_IMAGELENGTH, blockPtr->height);
734     TIFFSetField(tif, TIFFTAG_COMPRESSION, comp);
735 
736     TIFFSetField(tif, TIFFTAG_PLANARCONFIG,    PLANARCONFIG_CONTIG);
737     TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
738     TIFFSetField(tif, TIFFTAG_ORIENTATION,     ORIENTATION_TOPLEFT);
739     TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,    blockPtr->height);
740 
741     TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (int)2);
742     TIFFSetField(tif, TIFFTAG_XRESOLUTION,    (float)1200.0);
743     TIFFSetField(tif, TIFFTAG_YRESOLUTION,    (float)1200.0);
744 
745     TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
746     if ((blockPtr->offset[0] == blockPtr->offset[1])
747          && (blockPtr->offset[0] == blockPtr->offset[2])) {
748         numsamples = 1;
749         TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
750         TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,     PHOTOMETRIC_MINISBLACK);
751     } else {
752         numsamples = 3;
753         TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
754         TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,     PHOTOMETRIC_RGB);
755     }
756 
757     if ((blockPtr->pitch == numsamples * blockPtr->width)
758         && (blockPtr->pixelSize == numsamples)) {
759         data = blockPtr->pixelPtr;
760     } else {
761         unsigned char *srcPtr, *dstPtr, *rowPtr;
762         int greenOffset, blueOffset, alphaOffset, x, y;
763         dstPtr = data = (unsigned char *) ckalloc(numsamples *
764                 blockPtr->width * blockPtr->height);
765         rowPtr = blockPtr->pixelPtr + blockPtr->offset[0];
766         greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
767         blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
768         alphaOffset =  blockPtr->offset[0];
769         if (alphaOffset < blockPtr->offset[2]) {
770             alphaOffset = blockPtr->offset[2];
771         }
772         if (++alphaOffset < blockPtr->pixelSize) {
773             alphaOffset -= blockPtr->offset[0];
774         } else {
775             alphaOffset = 0;
776         }
777         if (blueOffset || greenOffset) {
778             for (y = blockPtr->height; y > 0; y--) {
779                 srcPtr = rowPtr;
780                 for (x = blockPtr->width; x>0; x--) {
781                     if (alphaOffset && !srcPtr[alphaOffset]) {
782                         *dstPtr++ = 0xd9;
783                         *dstPtr++ = 0xd9;
784                         *dstPtr++ = 0xd9;
785                     } else {
786                         *dstPtr++ = srcPtr[0];
787                         *dstPtr++ = srcPtr[greenOffset];
788                         *dstPtr++ = srcPtr[blueOffset];
789                     }
790                     srcPtr += blockPtr->pixelSize;
791                 }
792                 rowPtr += blockPtr->pitch;
793             }
794         } else {
795             for (y = blockPtr->height; y > 0; y--) {
796                 srcPtr = rowPtr;
797                 for (x = blockPtr->width; x>0; x--) {
798                     *dstPtr++ = srcPtr[0];
799                     srcPtr += blockPtr->pixelSize;
800                 }
801                 rowPtr += blockPtr->pitch;
802             }
803         }
804     }
805 
806     TIFFWriteEncodedStrip(tif, 0, data,
807             numsamples * blockPtr->width * blockPtr->height);
808     if (data != blockPtr->pixelPtr) {
809         ckfree((char *) data);
810     }
811 
812     return TCL_OK;
813 }
814 
815 void
TkimgTIFFfree(tdata_t data)816 TkimgTIFFfree(tdata_t data)
817 {
818     _TIFFfree(data);
819 }
820 
821 tdata_t
TkimgTIFFmalloc(tsize_t size)822 TkimgTIFFmalloc(tsize_t size)
823 {
824     return _TIFFmalloc(size);
825 }
826 
827 tdata_t
TkimgTIFFrealloc(tdata_t data,tsize_t size)828 TkimgTIFFrealloc(
829     tdata_t data,
830     tsize_t size
831 ) {
832     return _TIFFrealloc(data, size);
833 }
834