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