loaders: JPG: Fix bussy loop on corrupted file.
[gfxprim.git] / libs / loaders / GP_TIFF.c
blobfad41caf9af1cf9173fb86951b92fdb254c4d182
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 TIFF image support using libtiff.
29 #include <stdint.h>
30 #include <inttypes.h>
32 #include <errno.h>
33 #include <string.h>
35 #include "../../config.h"
37 #include "core/GP_Pixel.h"
38 #include "core/GP_GetPutPixel.h"
39 #include "core/GP_Debug.h"
41 #include "GP_TIFF.h"
43 #define TIFF_HEADER_LITTLE "II\x2a\0"
44 #define TIFF_HEADER_BIG "MM\0\x2a"
46 int GP_MatchTIFF(const void *buf)
48 if (!memcmp(buf, TIFF_HEADER_LITTLE, 4))
49 return 1;
51 if (!memcmp(buf, TIFF_HEADER_BIG, 4))
52 return 1;
54 return 0;
57 #ifdef HAVE_TIFF
59 #include <tiffio.h>
61 static const char *compression_name(uint16_t compression)
63 switch (compression) {
64 case COMPRESSION_NONE:
65 return "None";
66 case COMPRESSION_CCITTRLE:
67 return "CCITT modified Huffman RLE";
68 /* COMPRESSION_CCITTFAX3 == COMPRESSION_CCITT_T4 */
69 case COMPRESSION_CCITTFAX3:
70 return "CCITT Group 3 fax encoding / CCITT T.4 (TIFF 6 name)";
71 /* COMPRESSION_CCITTFAX4 == COMPRESSION_CCITT_T6 */
72 case COMPRESSION_CCITTFAX4:
73 return "CCITT Group 4 fax encoding / CCITT T.6 (TIFF 6 name)";
74 case COMPRESSION_LZW:
75 return "LZW";
76 case COMPRESSION_OJPEG:
77 return "JPEG 6.0";
78 case COMPRESSION_JPEG:
79 return "JPEG DCT";
80 case COMPRESSION_NEXT:
81 return "NeXT 2 bit RLE";
82 case COMPRESSION_CCITTRLEW:
83 return "#1 w/ word alignment";
84 case COMPRESSION_PACKBITS:
85 return "Macintosh RLE";
86 case COMPRESSION_THUNDERSCAN:
87 return "ThunderScan RLE";
90 return "Unknown";
93 static const char *photometric_name(uint16_t photometric)
95 switch (photometric) {
96 case PHOTOMETRIC_MINISWHITE:
97 return "Min is White";
98 case PHOTOMETRIC_MINISBLACK:
99 return "Min is black";
100 case PHOTOMETRIC_RGB:
101 return "RGB";
102 case PHOTOMETRIC_PALETTE:
103 return "Palette";
104 case PHOTOMETRIC_MASK:
105 return "Mask";
106 case PHOTOMETRIC_SEPARATED:
107 return "Separated";
108 case PHOTOMETRIC_YCBCR:
109 return "YCBCR";
110 case PHOTOMETRIC_CIELAB:
111 return "CIELAB";
112 case PHOTOMETRIC_ICCLAB:
113 return "ICCLAB";
114 case PHOTOMETRIC_ITULAB:
115 return "ITULAB";
116 case PHOTOMETRIC_LOGL:
117 return "LOGL";
118 case PHOTOMETRIC_LOGLUV:
119 return "LOGLUV";
120 default:
121 return "Unknown";
125 struct tiff_header {
126 /* compulsory tiff data */
127 uint32_t w, h;
128 uint16_t compress;
129 uint16_t bits_per_sample;
131 /* either strips or tiles should be set */
132 uint32_t rows_per_strip;
134 uint32_t tile_w;
135 uint32_t tile_h;
137 /* pixel type related values */
138 uint16_t photometric;
141 static int read_header(TIFF *tiff, struct tiff_header *header)
143 /* all these fields are compulsory in tiff image */
144 if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &header->w)) {
145 GP_DEBUG(1, "Failed to read Width");
146 return EIO;
149 if (!TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &header->h)) {
150 GP_DEBUG(1, "Failed to read Height");
151 return EIO;
154 if (!TIFFGetField(tiff, TIFFTAG_COMPRESSION, &header->compress)) {
155 GP_DEBUG(1, "Failed to read Compression Type");
156 return EIO;
159 if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE,
160 &header->bits_per_sample)) {
161 GP_DEBUG(1, "Failed to read Bits Per Sample");
162 return EIO;
165 GP_DEBUG(1, "TIFF image %ux%u Compression: %s, Bits Per Sample: %u",
166 header->w, header->h,
167 compression_name(header->compress), header->bits_per_sample);
169 /* If set tiff is saved in tiles */
170 if (TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &header->tile_w) &&
171 TIFFGetField(tiff, TIFFTAG_TILELENGTH, &header->tile_h)) {
172 GP_DEBUG(1, "TIFF is tiled in %ux%u",
173 header->tile_w, header->tile_h);
174 header->rows_per_strip = 0;
175 return 0;
178 if (!TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &header->rows_per_strip)) {
179 GP_DEBUG(1, "TIFF not saved in tiles nor strips");
180 return ENOSYS;
183 GP_DEBUG(1, "TIFF is saved in strips");
185 return 0;
188 enum rec_type {
189 REC_STRING,
190 REC_SHORT,
191 REC_FLOAT,
194 struct tag {
195 int tag;
196 const char *name;
197 enum rec_type type;
200 static struct tag tags[] = {
201 {TIFFTAG_IMAGEDESCRIPTION, "Image Description", REC_STRING},
202 {TIFFTAG_MAKE, "Make", REC_STRING},
203 {TIFFTAG_MODEL, "Model", REC_STRING},
204 {TIFFTAG_ORIENTATION, "Orientation", REC_SHORT},
205 {TIFFTAG_SAMPLESPERPIXEL, "Samples per Pixel", REC_SHORT},
206 {TIFFTAG_XRESOLUTION, "X Resolution", REC_FLOAT},
207 {TIFFTAG_YRESOLUTION, "Y Resolution", REC_FLOAT},
208 {TIFFTAG_RESOLUTIONUNIT, "Resolution Unit", REC_SHORT},
209 {TIFFTAG_SOFTWARE, "Software", REC_STRING},
210 {TIFFTAG_DATETIME, "Date Time", REC_STRING},
211 {TIFFTAG_ARTIST, "Artist", REC_STRING},
212 {TIFFTAG_HOSTCOMPUTER, "Host Computer", REC_STRING},
213 {TIFFTAG_COPYRIGHT, "Copyright", REC_STRING},
214 {0, NULL, 0},
217 static void fill_metadata(TIFF *tiff, struct tiff_header *header,
218 GP_DataStorage *storage)
220 unsigned int i;
221 int flag;
222 uint16_t s;
223 float f;
224 GP_DataNode val;
226 GP_DataStorageAddInt(storage, NULL, "Width", header->w);
227 GP_DataStorageAddInt(storage, NULL, "Height", header->h);
228 GP_DataStorageAddString(storage, NULL, "Compression",
229 compression_name(header->compress));
230 GP_DataStorageAddInt(storage, NULL, "Bits per Sample",
231 header->bits_per_sample);
233 for (i = 0; tags[i].name; i++) {
235 val.id = tags[i].name;
237 switch (tags[i].type) {
238 case REC_STRING:
239 val.type = GP_DATA_STRING;
240 flag = TIFFGetField(tiff, tags[i].tag, &(val.value.str));
241 break;
242 case REC_SHORT:
243 val.type = GP_DATA_INT;
244 flag = TIFFGetField(tiff, tags[i].tag, &s);
245 val.value.i = s;
246 break;
247 case REC_FLOAT:
248 val.type = GP_DATA_DOUBLE;
249 flag = TIFFGetField(tiff, tags[i].tag, &f);
250 val.value.d = f;
251 break;
252 default:
253 GP_WARN("Unhandled type %i", tags[i].type);
256 if (flag) {
257 GP_DataStorageAdd(storage, NULL, &val);
258 flag = 0;
263 static GP_PixelType match_grayscale_pixel_type(TIFF *tiff,
264 struct tiff_header *header)
266 if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE,
267 &header->bits_per_sample)) {
268 GP_DEBUG(1, "Have 1bit Bitmap");
269 return GP_PIXEL_G1;
272 switch (header->bits_per_sample) {
273 case 1:
274 GP_DEBUG(1, "Have 1bit Bitmap");
275 return GP_PIXEL_G1;
276 case 2:
277 GP_DEBUG(1, "Have 2bit Grayscale");
278 return GP_PIXEL_G2;
279 case 4:
280 GP_DEBUG(1, "Have 4bit Grayscale");
281 return GP_PIXEL_G4;
282 case 8:
283 GP_DEBUG(1, "Have 8bit Grayscale");
284 return GP_PIXEL_G8;
285 case 16:
286 GP_DEBUG(1, "Have 16bit Grayscale");
287 return GP_PIXEL_G16;
288 default:
289 GP_DEBUG(1, "Unimplemented bits per sample %u",
290 (unsigned) header->bits_per_sample);
291 return GP_PIXEL_UNKNOWN;
295 static GP_PixelType match_rgb_pixel_type(TIFF *tiff, struct tiff_header *header)
297 uint16_t samples;
299 if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samples)) {
300 GP_DEBUG(1, "Failed to get Samples Per Pixel");
301 return EINVAL;
304 GP_DEBUG(1, "Have %u samples per pixel", (unsigned) samples);
306 uint16_t bps = header->bits_per_sample;
308 /* Mach RGB pixel type with given pixel sizes */
309 if (samples == 3)
310 return GP_PixelRGBLookup(bps, 0, bps, bps,
311 bps, 2*bps, 0, 0, 3*bps);
313 GP_DEBUG(1, "Unsupported");
314 return GP_PIXEL_UNKNOWN;
317 static GP_PixelType match_pixel_type(TIFF *tiff, struct tiff_header *header)
319 if (!TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &header->photometric))
320 return GP_PIXEL_UNKNOWN;
322 GP_DEBUG(1, "Have photometric %s",
323 photometric_name(header->photometric));
325 switch (header->photometric) {
326 /* 1-bit or 4, 8-bit grayscale */
327 case PHOTOMETRIC_MINISWHITE:
328 case PHOTOMETRIC_MINISBLACK:
329 return match_grayscale_pixel_type(tiff, header);
330 case PHOTOMETRIC_RGB:
331 return match_rgb_pixel_type(tiff, header);
332 /* The palete is RGB161616 map it to BGR888 for now */
333 case PHOTOMETRIC_PALETTE:
334 return GP_PIXEL_RGB888;
335 default:
336 GP_DEBUG(1, "Unimplemented photometric interpretation %u",
337 (unsigned) header->photometric);
338 return GP_PIXEL_UNKNOWN;
342 static uint16_t get_idx(uint8_t *row, uint32_t x, uint16_t bps)
344 switch (bps) {
345 case 1:
346 return !!(row[x/8] & (1<<(7 - x%8)));
347 case 2:
348 return (row[x/4] >> (2*(3 - x%4))) & 0x03;
349 case 4:
350 return (row[x/2] >> (4*(!(x%2)))) & 0x0f;
351 case 8:
352 return row[x];
353 case 16:
354 return ((uint16_t*)row)[x];
357 GP_DEBUG(1, "Unsupported bits per sample %u", (unsigned) bps);
358 return 0;
361 static int tiff_read_palette(TIFF *tiff, GP_Pixmap *res,
362 struct tiff_header *header,
363 GP_ProgressCallback *callback)
365 if (TIFFIsTiled(tiff)) {
366 //TODO
367 return ENOSYS;
370 if (header->bits_per_sample > 48) {
371 GP_DEBUG(1, "Bits per sample too big %u",
372 (unsigned)header->bits_per_sample);
373 return EINVAL;
376 unsigned int palette_size = (1<<header->bits_per_sample);
377 uint16_t *palette_r, *palette_g, *palette_b;
378 uint32_t x, y, scanline_size;
380 GP_DEBUG(1, "Pallete size %u", palette_size);
382 if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &palette_r, &palette_g, &palette_b)) {
383 GP_DEBUG(1, "Failed to read palette");
384 return EIO;
387 scanline_size = TIFFScanlineSize(tiff);
389 GP_DEBUG(1, "Scanline size %u", (unsigned) scanline_size);
391 uint8_t buf[scanline_size];
393 /* Read image strips scanline by scanline */
394 for (y = 0; y < header->h; y++) {
395 if (TIFFReadScanline(tiff, buf, y, 0) != 1) {
396 //TODO: Make use of TIFF ERROR
397 GP_DEBUG(1, "Error reading scanline");
398 return EIO;
401 for (x = 0; x < header->w; x++) {
402 uint16_t i = get_idx(buf, x, header->bits_per_sample);
404 if (i >= palette_size) {
405 GP_WARN("Invalid palette index %u",
406 (unsigned) i);
407 i = 0;
410 GP_Pixel p = GP_Pixel_CREATE_RGB888(palette_r[i]>>8,
411 palette_g[i]>>8,
412 palette_b[i]>>8);
414 GP_PutPixel_Raw_24BPP(res, x, y, p);
417 if (GP_ProgressCallbackReport(callback, y, res->h, res->w)) {
418 GP_DEBUG(1, "Operation aborted");
419 return ECANCELED;
423 GP_ProgressCallbackDone(callback);
424 return 0;
427 //Temporary, the bitendians strikes again
428 #include "core/GP_BitSwap.h"
431 * Direct read -> data in image are in right format.
433 static int tiff_read(TIFF *tiff, GP_Pixmap *res, struct tiff_header *header,
434 GP_ProgressCallback *callback)
436 uint32_t i, y;
437 uint16_t planar_config, samples, s;
439 GP_DEBUG(1, "Reading tiff data");
441 if (TIFFIsTiled(tiff)) {
442 //TODO
443 return ENOSYS;
446 //ASSERT ScanlineSize == w!
448 /* Figure out number of planes */
449 if (!TIFFGetField(tiff, TIFFTAG_PLANARCONFIG, &planar_config))
450 planar_config = 1;
452 switch (planar_config) {
453 case 1:
454 GP_DEBUG(1, "Planar config = 1, all samples are in one plane");
455 samples = 1;
456 break;
457 case 2:
458 if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samples)) {
459 GP_DEBUG(1, "Planar config = 2, samples per pixel undefined");
460 return EINVAL;
462 GP_DEBUG(1, "Have %u samples per pixel", (unsigned)samples);
463 break;
464 default:
465 GP_DEBUG(1, "Unimplemented planar config = %u",
466 (unsigned)planar_config);
467 return EINVAL;
470 /* Read image strips scanline by scanline */
471 for (y = 0; y < header->h; y++) {
472 uint8_t *addr = GP_PIXEL_ADDR(res, 0, y);
474 //TODO: Does not work with RowsPerStrip > 1 -> needs StripOrientedIO
475 for (s = 0; s < samples; s++) {
476 if (TIFFReadScanline(tiff, addr, y, s) != 1) {
477 //TODO: Make use of TIFF ERROR
478 GP_DEBUG(1, "Error reading scanline");
479 return EIO;
482 //Temporary, till bitendians are fixed
483 switch (res->pixel_type) {
484 case GP_PIXEL_G1:
485 GP_BitSwapRow_B1(addr, res->bytes_per_row);
486 break;
487 case GP_PIXEL_G2:
488 GP_BitSwapRow_B2(addr, res->bytes_per_row);
489 break;
490 case GP_PIXEL_G4:
491 GP_BitSwapRow_B4(addr, res->bytes_per_row);
492 break;
493 default:
494 break;
497 /* We need to negate the values when Min is White */
498 if (header->photometric == PHOTOMETRIC_MINISWHITE)
499 for (i = 0; i < res->bytes_per_row; i++)
500 addr[i] = ~addr[i];
503 if (GP_ProgressCallbackReport(callback, y, res->h, res->w)) {
504 GP_DEBUG(1, "Operation aborted");
505 return ECANCELED;
509 GP_ProgressCallbackDone(callback);
510 return 0;
513 static tsize_t tiff_io_read(thandle_t io, void *buf, tsize_t size)
515 return GP_IORead(io, buf, size);
518 static tsize_t tiff_io_write(thandle_t io, void *buf, tsize_t size)
520 return GP_IOWrite(io, buf, size);
523 static toff_t tiff_io_seek(thandle_t io, toff_t offset, int whence)
525 return GP_IOSeek(io, offset, whence);
528 static int tiff_io_close(thandle_t GP_UNUSED(io))
530 return 0;
533 static toff_t tiff_io_size(thandle_t io)
535 return GP_IOSize(io);
539 static int tiff_io_map(thandle_t io, void **base, toff_t *size)
541 GP_WARN("stub called");
542 return 0;
545 static void tiff_io_unmap(thandle_t io, void *base, toff_t size)
547 GP_WARN("stub called");
548 return 0;
552 int GP_ReadTIFFEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *storage,
553 GP_ProgressCallback *callback)
555 TIFF *tiff;
556 struct tiff_header header;
557 GP_Pixmap *res;
558 GP_PixelType pixel_type;
559 int err;
561 tiff = TIFFClientOpen("GFXprim IO", "r", io, tiff_io_read,
562 tiff_io_write, tiff_io_seek, tiff_io_close,
563 tiff_io_size, NULL, NULL);
565 if (!tiff) {
566 GP_DEBUG(1, "TIFFClientOpen failed");
567 err = EIO;
568 goto err0;
571 if ((err = read_header(tiff, &header)))
572 goto err1;
574 if (storage)
575 fill_metadata(tiff, &header, storage);
577 if (!img)
578 return 0;
580 pixel_type = match_pixel_type(tiff, &header);
582 if (pixel_type == GP_PIXEL_UNKNOWN) {
583 err = ENOSYS;
584 goto err1;
587 res = GP_PixmapAlloc(header.w, header.h, pixel_type);
589 if (res == NULL) {
590 err = errno;
591 GP_DEBUG(1, "Malloc failed");
592 goto err1;
595 switch (header.photometric) {
596 case PHOTOMETRIC_PALETTE:
597 err = tiff_read_palette(tiff, res, &header, callback);
598 break;
599 default:
600 err = tiff_read(tiff, res, &header, callback);
603 if (err)
604 goto err2;
606 TIFFClose(tiff);
608 *img = res;
609 return 0;
610 err2:
611 GP_PixmapFree(res);
612 err1:
613 TIFFClose(tiff);
614 err0:
615 errno = err;
616 return 1;
619 static int save_grayscale(TIFF *tiff, const GP_Pixmap *src,
620 GP_ProgressCallback *callback)
622 uint32_t x, y;
623 uint8_t buf[src->bytes_per_row];
624 tsize_t ret;
626 TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, src->bpp);
627 TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
628 TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
629 TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
631 for (y = 0; y < src->h; y++) {
632 uint8_t *addr = GP_PIXEL_ADDR(src, 0, y);
634 if (src->bpp < 8 && !src->bit_endian) {
635 for (x = 0; x < src->bytes_per_row; x++) {
636 switch (src->pixel_type) {
637 case GP_PIXEL_G1:
638 buf[x] = GP_BIT_SWAP_B1(addr[x]);
639 break;
640 case GP_PIXEL_G2:
641 buf[x] = GP_BIT_SWAP_B2(addr[x]);
642 break;
643 case GP_PIXEL_G4:
644 buf[x] = GP_BIT_SWAP_B4(addr[x]);
645 break;
646 default:
647 GP_BUG("Uh, oh, do we need swap?");
650 addr = buf;
653 ret = TIFFWriteEncodedStrip(tiff, y, addr, src->bytes_per_row);
655 if (ret == -1) {
656 //TODO TIFF ERROR
657 GP_DEBUG(1, "TIFFWriteEncodedStrip failed");
658 return EIO;
661 if (GP_ProgressCallbackReport(callback, y, src->h, src->w)) {
662 GP_DEBUG(1, "Operation aborted");
663 return ECANCELED;
667 return 0;
670 static int save_rgb(TIFF *tiff, const GP_Pixmap *src,
671 GP_ProgressCallback *callback)
673 uint8_t buf[src->w * 3];
674 uint32_t x, y;
676 TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
677 TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3);
678 TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
680 for (y = 0; y < src->h; y++) {
681 uint8_t *addr = GP_PIXEL_ADDR(src, 0, y);
683 switch (src->pixel_type) {
684 case GP_PIXEL_RGB888:
685 for (x = 0; x < src->bytes_per_row; x+=3) {
686 buf[x + 2] = addr[x];
687 buf[x + 1] = addr[x + 1];
688 buf[x] = addr[x + 2];
690 addr = buf;
691 break;
692 case GP_PIXEL_xRGB8888:
693 for (x = 0; x < src->bytes_per_row; x+=4) {
694 buf[3*(x/4) + 2] = addr[x];
695 buf[3*(x/4) + 1] = addr[x + 1];
696 buf[3*(x/4)] = addr[x + 2];
698 addr = buf;
699 break;
700 default:
701 break;
704 TIFFWriteEncodedStrip(tiff, y, addr, src->w * 3);
706 if (GP_ProgressCallbackReport(callback, y, src->h, src->w)) {
707 GP_DEBUG(1, "Operation aborted");
708 return ECANCELED;
712 return 0;
715 static GP_PixelType save_ptypes[] = {
716 GP_PIXEL_BGR888,
717 GP_PIXEL_RGB888,
718 GP_PIXEL_xRGB8888,
719 GP_PIXEL_G1,
720 GP_PIXEL_G2,
721 GP_PIXEL_G4,
722 GP_PIXEL_G8,
723 GP_PIXEL_UNKNOWN,
726 int GP_WriteTIFF(const GP_Pixmap *src, GP_IO *io,
727 GP_ProgressCallback *callback)
729 TIFF *tiff;
730 int err = 0;
732 GP_DEBUG(1, "Writing TIFF to I/O (%p)", io);
734 if (GP_PixelHasFlags(src->pixel_type, GP_PIXEL_HAS_ALPHA)) {
735 GP_DEBUG(1, "Alpha channel not supported yet");
736 errno = ENOSYS;
737 return 1;
740 switch (src->pixel_type) {
741 case GP_PIXEL_G1:
742 case GP_PIXEL_G2:
743 case GP_PIXEL_G4:
744 case GP_PIXEL_G8:
745 break;
746 case GP_PIXEL_RGB888:
747 case GP_PIXEL_BGR888:
748 case GP_PIXEL_xRGB8888:
749 break;
750 default:
751 GP_DEBUG(1, "Unsupported pixel type %s",
752 GP_PixelTypeName(src->pixel_type));
753 errno = ENOSYS;
754 return 1;
757 /* Open TIFF image */
758 tiff = TIFFClientOpen("GFXprim IO", "w", io, tiff_io_read,
759 tiff_io_write, tiff_io_seek, tiff_io_close,
760 tiff_io_size, NULL, NULL);
762 if (!tiff) {
763 GP_DEBUG(1, "TIFFClientOpen failed");
764 errno = EIO;
765 return 1;
768 /* Set required fields */
769 TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, src->w);
770 TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, src->h);
771 TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
772 TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
774 switch (src->pixel_type) {
775 case GP_PIXEL_RGB888:
776 case GP_PIXEL_BGR888:
777 case GP_PIXEL_xRGB8888:
778 err = save_rgb(tiff, src, callback);
779 break;
780 case GP_PIXEL_G1:
781 case GP_PIXEL_G2:
782 case GP_PIXEL_G4:
783 case GP_PIXEL_G8:
784 err = save_grayscale(tiff, src, callback);
785 break;
786 default:
787 GP_BUG("Wrong pixel type");
788 break;
791 if (err) {
792 TIFFClose(tiff);
793 errno = err;
794 return 1;
797 TIFFClose(tiff);
798 GP_ProgressCallbackDone(callback);
799 return 0;
802 #else
804 int GP_ReadTIFFEx(GP_IO GP_UNUSED(*io), GP_Pixmap GP_UNUSED(**img),
805 GP_DataStorage GP_UNUSED(*storage),
806 GP_ProgressCallback GP_UNUSED(*callback))
808 errno = ENOSYS;
809 return 1;
812 int GP_WriteTIFF(const GP_Pixmap GP_UNUSED(*src), GP_IO GP_UNUSED(*io),
813 GP_ProgressCallback GP_UNUSED(*callback))
815 errno = ENOSYS;
816 return 1;
819 #endif /* HAVE_TIFF */
821 GP_Pixmap *GP_LoadTIFF(const char *src_path, GP_ProgressCallback *callback)
823 return GP_LoaderLoadImage(&GP_TIFF, src_path, callback);
826 GP_Pixmap *GP_ReadTIFF(GP_IO *io, GP_ProgressCallback *callback)
828 return GP_LoaderReadImage(&GP_TIFF, io, callback);
831 int GP_LoadTIFFEx(const char *src_path, GP_Pixmap **img,
832 GP_DataStorage *storage, GP_ProgressCallback *callback)
834 return GP_LoaderLoadImageEx(&GP_TIFF, src_path, img, storage, callback);
837 int GP_SaveTIFF(const GP_Pixmap *src, const char *dst_path,
838 GP_ProgressCallback *callback)
840 return GP_LoaderSaveImage(&GP_TIFF, src, dst_path, callback);
843 struct GP_Loader GP_TIFF = {
844 #ifdef HAVE_TIFF
845 .Read = GP_ReadTIFFEx,
846 .Write = GP_WriteTIFF,
847 .save_ptypes = save_ptypes,
848 #endif
849 .Match = GP_MatchTIFF,
851 .fmt_name = "Tag Image File Format",
852 .extensions = {"tif", "tiff", NULL},