1 // ==========================================================
2 // G3 Fax Loader
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 // - Petr Pytelka (pyta@lightcomp.com)
7 //
8 // This file is part of FreeImage 3
9 //
10 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
11 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
12 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
13 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
14 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
15 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
16 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
17 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
18 // THIS DISCLAIMER.
19 //
20 // Use at your own risk!
21 // ==========================================================
22 
23 #include "../LibTIFF4/tiffiop.h"
24 
25 #include "FreeImage.h"
26 #include "Utilities.h"
27 
28 // ==========================================================
29 // Plugin Interface
30 // ==========================================================
31 
32 static int s_format_id;
33 
34 // ==========================================================
35 //   Constant/Macro declarations
36 // ==========================================================
37 
38 #define G3_DEFAULT_WIDTH	1728
39 
40 #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
41 
42 // ==========================================================
43 //   libtiff interface
44 // ==========================================================
45 
46 static tmsize_t
_g3ReadProc(thandle_t handle,void * buf,tmsize_t size)47 _g3ReadProc(thandle_t handle, void *buf, tmsize_t size) {
48 	// returns an error when reading the TIFF header
49 	return 0;
50 }
51 
52 static tmsize_t
_g3WriteProc(thandle_t handle,void * buf,tmsize_t size)53 _g3WriteProc(thandle_t handle, void *buf, tmsize_t size) {
54 	// returns ok when writing the TIFF header
55 	return size;
56 }
57 
58 static toff_t
_g3SeekProc(thandle_t handle,toff_t off,int whence)59 _g3SeekProc(thandle_t handle, toff_t off, int whence) {
60 	return 0;
61 }
62 
63 static int
_g3CloseProc(thandle_t handle)64 _g3CloseProc(thandle_t handle) {
65 	return 0;
66 }
67 
68 static toff_t
_g3SizeProc(thandle_t handle)69 _g3SizeProc(thandle_t handle) {
70 	return 0;
71 }
72 
73 static int
_g3MapProc(thandle_t,void ** base,toff_t * size)74 _g3MapProc(thandle_t, void** base, toff_t* size) {
75 	return 0;
76 }
77 
78 static void
_g3UnmapProc(thandle_t,void * base,toff_t size)79 _g3UnmapProc(thandle_t, void* base, toff_t size) {
80 }
81 
82 // --------------------------------------------------------------
83 
84 static tmsize_t
G3GetFileSize(FreeImageIO * io,fi_handle handle)85 G3GetFileSize(FreeImageIO *io, fi_handle handle) {
86     long currentPos = io->tell_proc(handle);
87     io->seek_proc(handle, 0, SEEK_END);
88     long fileSize = io->tell_proc(handle);
89     io->seek_proc(handle, currentPos, SEEK_SET);
90     return fileSize;
91 }
92 
93 static BOOL
G3ReadFile(FreeImageIO * io,fi_handle handle,uint8 * tif_rawdata,tmsize_t tif_rawdatasize)94 G3ReadFile(FreeImageIO *io, fi_handle handle, uint8 *tif_rawdata, tmsize_t tif_rawdatasize) {
95 	return ((tmsize_t)(io->read_proc(tif_rawdata, (unsigned)tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize);
96 }
97 
98 // ==========================================================
99 // Internal functions
100 // ==========================================================
101 
102 static int
copyFaxFile(FreeImageIO * io,fi_handle handle,TIFF * tifin,uint32 xsize,int stretch,FIMEMORY * memory)103 copyFaxFile(FreeImageIO *io, fi_handle handle, TIFF* tifin, uint32 xsize, int stretch, FIMEMORY *memory) {
104 	BYTE *rowbuf = NULL;
105 	BYTE *refbuf = NULL;
106 	uint32 row;
107 	uint16 badrun;
108 	uint16	badfaxrun;
109 	uint32	badfaxlines;
110 	int ok;
111 
112 	try {
113 
114 		uint32 linesize = TIFFhowmany8(xsize);
115 		rowbuf = (BYTE*) _TIFFmalloc(linesize);
116 		refbuf = (BYTE*) _TIFFmalloc(linesize);
117 		if (rowbuf == NULL || refbuf == NULL) {
118 			throw FI_MSG_ERROR_MEMORY;
119 		}
120 
121 		tifin->tif_rawdatasize = G3GetFileSize(io, handle);
122 		tifin->tif_rawdata = (tidata_t) _TIFFmalloc(tifin->tif_rawdatasize);
123 		if (tifin->tif_rawdata == NULL) {
124 			throw FI_MSG_ERROR_MEMORY;
125 		}
126 
127 		if(!G3ReadFile(io, handle, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
128 			throw "Read error at scanline 0";
129 		}
130 		tifin->tif_rawcp = tifin->tif_rawdata;
131 		tifin->tif_rawcc = tifin->tif_rawdatasize;
132 
133 		(*tifin->tif_setupdecode)(tifin);
134 		(*tifin->tif_predecode)(tifin, (uint16) 0);
135 		tifin->tif_row = 0;
136 		badfaxlines = 0;
137 		badfaxrun = 0;
138 
139 		_TIFFmemset(refbuf, 0, linesize);
140 		row = 0;
141 		badrun = 0;		// current run of bad lines
142 		while (tifin->tif_rawcc > 0) {
143 			ok = (*tifin->tif_decoderow)(tifin, rowbuf, linesize, 0);
144 			if (!ok) {
145 				badfaxlines++;
146 				badrun++;
147 				// regenerate line from previous good line
148 				_TIFFmemcpy(rowbuf, refbuf, linesize);
149 			} else {
150 				if (badrun > badfaxrun)
151 					badfaxrun = badrun;
152 				badrun = 0;
153 				_TIFFmemcpy(refbuf, rowbuf, linesize);
154 			}
155 			tifin->tif_row++;
156 
157 			FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
158 			row++;
159 			if (stretch) {
160 				FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
161 				row++;
162 			}
163 		}
164 		if (badrun > badfaxrun)
165 			badfaxrun = badrun;
166 
167 		_TIFFfree(tifin->tif_rawdata);
168 		tifin->tif_rawdata = NULL;
169 
170 		_TIFFfree(rowbuf);
171 		_TIFFfree(refbuf);
172 
173 		/*
174 		if (verbose) {
175 			fprintf(stderr, "%d rows in input\n", rows);
176 			fprintf(stderr, "%ld total bad rows\n", (long) badfaxlines);
177 			fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
178 		}
179 		*/
180 
181 	} catch(const char *message) {
182 		if(rowbuf) _TIFFfree(rowbuf);
183 		if(refbuf) _TIFFfree(refbuf);
184 		if(tifin->tif_rawdata) {
185 			_TIFFfree(tifin->tif_rawdata);
186 			tifin->tif_rawdata = NULL;
187 		}
188 		FreeImage_OutputMessageProc(s_format_id, message);
189 
190 		return -1;
191 	}
192 
193 	return (row);
194 }
195 
196 
197 // ==========================================================
198 // Plugin Implementation
199 // ==========================================================
200 
201 static const char * DLL_CALLCONV
Format()202 Format() {
203 	return "G3";
204 }
205 
206 static const char * DLL_CALLCONV
Description()207 Description() {
208 	return "Raw fax format CCITT G.3";
209 }
210 
211 static const char * DLL_CALLCONV
Extension()212 Extension() {
213 	return "g3";
214 }
215 
216 static const char * DLL_CALLCONV
RegExpr()217 RegExpr() {
218 	return NULL; // there is now reasonable regexp for raw G3
219 }
220 
221 static const char * DLL_CALLCONV
MimeType()222 MimeType() {
223 	return "image/fax-g3";
224 }
225 
226 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)227 SupportsExportDepth(int depth) {
228 	return	FALSE;
229 }
230 
231 // ----------------------------------------------------------
232 
233 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)234 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
235 	TIFF *faxTIFF = NULL;
236 	FIBITMAP *dib = NULL;
237 	FIMEMORY *memory = NULL;
238 
239 	//int verbose = 0;
240 	int	stretch = 0;
241 	int rows;
242 	float resX = 204.0;
243 	float resY = 196.0;
244 
245 	uint32 xsize = G3_DEFAULT_WIDTH;
246 	int compression_in = COMPRESSION_CCITTFAX3;
247 	int fillorder_in = FILLORDER_LSB2MSB;
248 	uint32 group3options_in = 0;	// 1d-encoded
249 	uint32 group4options_in = 0;	// compressed
250 	int photometric_in = PHOTOMETRIC_MINISWHITE;
251 
252 	if(handle==NULL) return NULL;
253 
254 	try {
255 		// set default load options
256 
257 		compression_in = COMPRESSION_CCITTFAX3;			// input is g3-encoded
258 		group3options_in &= ~GROUP3OPT_2DENCODING;		// input is 1d-encoded (g3 only)
259 		fillorder_in = FILLORDER_MSB2LSB;				// input has msb-to-lsb fillorder
260 
261 		/*
262 		Original input-related fax2tiff options
263 
264 		while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) {
265 			switch (c) {
266 					// input-related options
267 				case '3':		// input is g3-encoded
268 					compression_in = COMPRESSION_CCITTFAX3;
269 					break;
270 				case '4':		// input is g4-encoded
271 					compression_in = COMPRESSION_CCITTFAX4;
272 					break;
273 				case 'U':		// input is uncompressed (g3 and g4)
274 					group3options_in |= GROUP3OPT_UNCOMPRESSED;
275 					group4options_in |= GROUP4OPT_UNCOMPRESSED;
276 					break;
277 				case '1':		// input is 1d-encoded (g3 only)
278 					group3options_in &= ~GROUP3OPT_2DENCODING;
279 					break;
280 				case '2':		// input is 2d-encoded (g3 only)
281 					group3options_in |= GROUP3OPT_2DENCODING;
282 					break;
283 				case 'P':	// input has not-aligned EOL (g3 only)
284 					group3options_in &= ~GROUP3OPT_FILLBITS;
285 					break;
286 				case 'A':		// input has aligned EOL (g3 only)
287 					group3options_in |= GROUP3OPT_FILLBITS;
288 					break;
289 				case 'W':		// input has 0 mean white
290 					photometric_in = PHOTOMETRIC_MINISWHITE;
291 					break;
292 				case 'B':		// input has 0 mean black
293 					photometric_in = PHOTOMETRIC_MINISBLACK;
294 					break;
295 				case 'L':		// input has lsb-to-msb fillorder
296 					fillorder_in = FILLORDER_LSB2MSB;
297 					break;
298 				case 'M':		// input has msb-to-lsb fillorder
299 					fillorder_in = FILLORDER_MSB2LSB;
300 					break;
301 				case 'R':		// input resolution
302 					resY = (float) atof(optarg);
303 					break;
304 				case 'X':		// input width
305 					xsize = (uint32) atoi(optarg);
306 					break;
307 
308 					// output-related options
309 				case 's':		// stretch image by dup'ng scanlines
310 					stretch = 1;
311 					break;
312 				case 'v':		// -v for info
313 					verbose++;
314 					break;
315 			}
316 		}
317 
318 		*/
319 
320 		// open a temporary memory buffer to save decoded scanlines
321 		memory = FreeImage_OpenMemory();
322 		if(!memory) throw FI_MSG_ERROR_MEMORY;
323 
324 		// wrap the raw fax file
325 		faxTIFF = TIFFClientOpen("(FakeInput)", "w",
326 			// TIFFClientOpen() fails if we don't set existing value here
327 			NULL,
328 			_g3ReadProc, _g3WriteProc,
329 			_g3SeekProc, _g3CloseProc,
330 			_g3SizeProc, _g3MapProc,
331 			_g3UnmapProc);
332 
333 		if (faxTIFF == NULL) {
334 			throw "Can not create fake input file";
335 		}
336 		TIFFSetMode(faxTIFF, O_RDONLY);
337 		TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
338 		TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
339 		TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
340 		TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
341 		TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
342 		TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
343 		TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
344 		TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
345 
346 		// NB: this must be done after directory info is setup
347 		TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
348 		if (compression_in == COMPRESSION_CCITTFAX3)
349 			TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
350 		else if (compression_in == COMPRESSION_CCITTFAX4)
351 			TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
352 
353 		resX = 204;
354 		if (!stretch) {
355 			TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
356 		} else {
357 			resY = 196;
358 		}
359 
360 		// decode the raw fax data
361 		rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory);
362 		if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options";
363 
364 
365 		// allocate the output dib
366 		dib = FreeImage_Allocate(xsize, rows, 1);
367 		unsigned pitch = FreeImage_GetPitch(dib);
368 		uint32 linesize = TIFFhowmany8(xsize);
369 
370 		// fill the bitmap structure ...
371 		// ... palette
372 		RGBQUAD *pal = FreeImage_GetPalette(dib);
373 		if(photometric_in == PHOTOMETRIC_MINISWHITE) {
374 			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
375 			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
376 		} else {
377 			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
378 			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
379 		}
380 		// ... resolution
381 		FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5));
382 		FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5));
383 
384 		// read the decoded scanline and fill the bitmap data
385 		FreeImage_SeekMemory(memory, 0, SEEK_SET);
386 		BYTE *bits = FreeImage_GetScanLine(dib, rows - 1);
387 		for(int k = 0; k < rows; k++) {
388 			FreeImage_ReadMemory(bits, linesize, 1, memory);
389 			bits -= pitch;
390 		}
391 
392 		// free the TIFF wrapper
393 		TIFFClose(faxTIFF);
394 
395 		// free the memory buffer
396 		FreeImage_CloseMemory(memory);
397 
398 	} catch(const char *message) {
399 		if(memory) FreeImage_CloseMemory(memory);
400 		if(faxTIFF) TIFFClose(faxTIFF);
401 		if(dib) FreeImage_Unload(dib);
402 		FreeImage_OutputMessageProc(s_format_id, message);
403 		return NULL;
404 	}
405 
406 	return dib;
407 
408 }
409 
410 // ==========================================================
411 //   Init
412 // ==========================================================
413 
414 void DLL_CALLCONV
InitG3(Plugin * plugin,int format_id)415 InitG3(Plugin *plugin, int format_id) {
416 	s_format_id = format_id;
417 
418 	plugin->format_proc = Format;
419 	plugin->description_proc = Description;
420 	plugin->extension_proc = Extension;
421 	plugin->regexpr_proc = RegExpr;
422 	plugin->open_proc = NULL;
423 	plugin->close_proc = NULL;
424 	plugin->pagecount_proc = NULL;
425 	plugin->pagecapability_proc = NULL;
426 	plugin->load_proc = Load;
427 	plugin->save_proc = NULL;
428 	plugin->validate_proc = NULL;
429 	plugin->mime_proc = MimeType;
430 	plugin->supports_export_bpp_proc = SupportsExportDepth;
431 	plugin->supports_export_type_proc = NULL;
432 	plugin->supports_icc_profiles_proc = NULL;
433 }
434