1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2015 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2016-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"		/* restrict */
22 #endif
23 
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <tiffio.h>
27 
28 #include "resources.h"
29 #include "object.h"
30 #include "f_picobj.h"		/* image_size() */
31 #include "f_util.h"		/* map_to_palette(), map_to_mono() */
32 #include "w_msgpanel.h"
33 
34 
35 static void
error_handler(const char * module,const char * fmt,va_list ap)36 error_handler(const char *module, const char *fmt, va_list ap)
37 {
38 	char	buffer[510];	/* usable size of tmpstr[] in w_msgpanel.c */
39 
40 	vsnprintf(buffer, sizeof buffer, fmt, ap);
41 	file_msg("%s: %s", module, buffer);
42 }
43 
44 /* return codes:  PicSuccess (1) : success
45 		  FileInvalid (-2) : invalid file
46 */
47 int
read_tif(F_pic * pic,struct xfig_stream * restrict pic_stream)48 read_tif(F_pic *pic, struct xfig_stream *restrict pic_stream)
49 {
50 	int		stat = FileInvalid;
51 	uint16		unit;
52 	uint32		w, h;
53 	float		res_x, res_y;
54 	TIFF		*tif;
55 
56 	if (uncompressed_content(pic_stream))
57 		return FileInvalid;
58 
59 	/* re-direct TIFF errors to file_msg() */
60 	(void)TIFFSetErrorHandler(error_handler);
61 	/* ignore warnings */
62 	(void)TIFFSetWarningHandler(NULL);
63 
64 	if ((tif = TIFFOpen(pic_stream->content, "r")) == NULL)
65 		return stat;
66 
67 	/* read image width and height */
68 	if (TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w) != 1) {
69 		TIFFClose(tif);
70 		return stat;
71 	}
72         if (TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h) != 1) {
73 		TIFFClose(tif);
74 		return stat;
75 	}
76 	if (w == 0 || h == 0) {
77 		TIFFClose(tif);
78 		return stat;
79 	}
80 
81 	/* allocate memory for image data */
82 	if ((pic->pic_cache->bitmap = malloc(w * h * sizeof(uint32))) == NULL) {
83 		file_msg("Out of memory");
84 		TIFFClose(tif);
85 		return stat;
86 	}
87 
88 	/* get image resolution, alias density */
89 	if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &unit) != 1)
90 		unit = 1;
91         if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_x) != 1 ||
92 			TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_y) != 1) {
93 		res_x = 0.0f;		/* image_size() will use a default */
94 		res_y = 0.0f;
95 		unit = 1;	/* 1 - none, 2 - per inch, 3 - per cm */
96 	}
97 	/* set pixmap properties */
98 	pic->pixmap = None;
99 	pic->pic_cache->subtype = T_PIC_TIF;
100 	pic->pic_cache->bit_size.x = (int)w;	/* needed in */
101 	pic->pic_cache->bit_size.y = (int)h;	/* map_to_palette() below */
102 	pic->hw_ratio = (float)h / w;
103 	image_size(&pic->pic_cache->size_x, &pic->pic_cache->size_y,
104 			pic->pic_cache->bit_size.x, pic->pic_cache->bit_size.y,
105 			unit == 2u ? 'i': (unit == 3u ? 'c': 'u'), res_x,res_y);
106 
107 	if (appres.DEBUG)
108 		fprintf(stderr, "Reading TIFF image, size %d x %d, resolution "
109 				"%.0f x %.0f%s.\n", pic->pic_cache->bit_size.x,
110 				pic->pic_cache->bit_size.y, res_x, res_y,
111 				unit == 2u ? " pixel per inch" :
112 					(unit == 3u ? " pixel per cm" : "" ));
113 
114 	/* read the image */
115 	stat = TIFFReadRGBAImageOriented(tif, w, h,
116 		      (uint32 *)pic->pic_cache->bitmap, ORIENTATION_TOPLEFT, 0);
117 	TIFFClose(tif);
118 	if (stat == 0) {
119 		if (pic->pic_cache->bitmap)
120 			free(pic->pic_cache->bitmap);
121 		return FileInvalid;
122 	}
123 
124 	/* provide the pixmap */
125 	if (tool_vclass == TrueColor && image_bpp == 4 && !appres.monochrome) {
126 		/* either a full-color pixmap, format ARGB */
127 		unsigned char	*p;
128 		unsigned char	tmp;
129 #ifdef WORDS_BIGENDIAN
130 		/* change RGBA to ARGB */
131 		/* as long as the alpha channel is not used, the fastest would
132 		   be to read the image with an offset of one */
133 		p = pic->pic_cache->bitmap;
134 		while (p < pic->pic_cache->bitmap + w * h * sizeof(uint32)) {
135 			/* tmp = *(p + 3); alpha channel not used */
136 			*(p + 3) = *(p + 2);
137 			*(p + 2) = *(p + 1);
138 			*(p + 1) = *p;
139 			p += sizeof(uint32);	/* must be 4 */
140 		}
141 #else
142 		/* swap RGBA to BGRA */
143 		p = pic->pic_cache->bitmap;
144 		while (p < pic->pic_cache->bitmap + w * h * sizeof(uint32)) {
145 			tmp = *p;  *p = *(p + 2);  *(p + 2) = tmp;
146 			p += sizeof(uint32);	/* must be 4 */
147 		}
148 #endif
149 		/* indicate, that this is a TrueColor pixmap */
150 		pic->pic_cache->numcols = -1;
151 
152 	} else { /* tool_vclass != TrueColor || .. */
153 		/* or a pixmap with a colormap, one byte per pixel */
154 		/* write BGR triples for map_to_palette */
155 		unsigned char	*src;
156 		unsigned char	*dst;
157 		unsigned char	tmp;
158 
159 		/* write the first two triples */
160 		tmp = *pic->pic_cache->bitmap;
161 		*pic->pic_cache->bitmap = *(pic->pic_cache->bitmap + 2);
162 		*(pic->pic_cache->bitmap + 2) = tmp;
163 
164 		src = pic->pic_cache->bitmap + 4;	/* RGBA RGBA */
165 		dst = pic->pic_cache->bitmap + 3;	/* BGRB GR   */
166 		tmp = *src;
167 		*dst++ = *(src + 2);
168 		*dst++ = *(src + 1);
169 		*dst++ = tmp;
170 		src += 4;		/* sizeof(uint32) */
171 		while (src < pic->pic_cache->bitmap + w * h * sizeof(uint32)) {
172 			*dst++ = *(src + 2);
173 			*dst++ = *(src + 1);
174 			*dst++ = *src;
175 			src += 4;	/* sizeof(uint32) */
176 		}
177 		pic->pic_cache->bitmap = realloc(pic->pic_cache->bitmap,
178 						w * h * 3 * sizeof(char));
179 
180 		/* map_to_palette() reduces to 256 colors */
181 		pic->pic_cache->numcols = 256;
182 		if (!map_to_palette(pic)) {
183 			/* map_to_palette() frees pic->pic_cache->bitmap */
184 			return FileInvalid;
185 		}
186 		if (tool_cells <= 2 || appres.monochrome)
187 			map_to_mono(pic);
188 	}
189 
190 	return PicSuccess;
191 }
192