Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginG3.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#define G3_DEFAULT_WIDTH  1728
39
40
0
#define TIFFhowmany8(x) (((x)&0x07)?((uint32_t)(x)>>3)+1:(uint32_t)(x)>>3)
41
42
// ==========================================================
43
//   libtiff interface 
44
// ==========================================================
45
46
static tmsize_t 
47
0
_g3ReadProc(thandle_t handle, void *buf, tmsize_t size) {
48
  // returns an error when reading the TIFF header
49
0
  return 0;
50
0
}
51
52
static tmsize_t
53
0
_g3WriteProc(thandle_t handle, void *buf, tmsize_t size) {
54
  // returns ok when writing the TIFF header
55
0
  return size;
56
0
}
57
58
static toff_t
59
0
_g3SeekProc(thandle_t handle, toff_t off, int whence) {
60
0
  return 0;
61
0
}
62
63
static int
64
0
_g3CloseProc(thandle_t handle) {
65
0
  return 0;
66
0
}
67
68
static toff_t
69
0
_g3SizeProc(thandle_t handle) {
70
0
  return 0;
71
0
}
72
73
static int
74
0
_g3MapProc(thandle_t, void** base, toff_t* size) {
75
0
  return 0;
76
0
}
77
78
static void
79
0
_g3UnmapProc(thandle_t, void* base, toff_t size) {
80
0
}
81
82
// --------------------------------------------------------------
83
84
static tmsize_t
85
0
G3GetFileSize(FreeImageIO *io, fi_handle handle) {
86
0
    long currentPos = io->tell_proc(handle);
87
0
    io->seek_proc(handle, 0, SEEK_END);
88
0
    long fileSize = io->tell_proc(handle);
89
0
    io->seek_proc(handle, currentPos, SEEK_SET);
90
0
    return fileSize;
91
0
}
92
93
static BOOL 
94
0
G3ReadFile(FreeImageIO *io, fi_handle handle, uint8_t *tif_rawdata, tmsize_t tif_rawdatasize) {
95
0
  return ((tmsize_t)(io->read_proc(tif_rawdata, (unsigned)tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize);
96
0
}
97
98
// ==========================================================
99
// Internal functions
100
// ==========================================================
101
102
static int 
103
0
copyFaxFile(FreeImageIO *io, fi_handle handle, TIFF* tifin, uint32_t xsize, int stretch, FIMEMORY *memory) {
104
0
  BYTE *rowbuf = NULL;
105
0
  BYTE *refbuf = NULL;
106
0
  uint32_t row;
107
0
  uint16_t badrun;
108
0
  uint16_t badfaxrun;
109
0
  uint32_t badfaxlines;
110
0
  int ok;
111
112
0
  try {
113
114
0
    uint32_t linesize = TIFFhowmany8(xsize);
115
0
    rowbuf = (BYTE*) _TIFFmalloc(linesize);
116
0
    refbuf = (BYTE*) _TIFFmalloc(linesize);
117
0
    if (rowbuf == NULL || refbuf == NULL) {
118
0
      throw FI_MSG_ERROR_MEMORY;
119
0
    }
120
121
0
    tifin->tif_rawdatasize = G3GetFileSize(io, handle);
122
0
    tifin->tif_rawdata = (tidata_t) _TIFFmalloc(tifin->tif_rawdatasize);
123
0
    if (tifin->tif_rawdata == NULL) {
124
0
      throw FI_MSG_ERROR_MEMORY;
125
0
    }
126
      
127
0
    if(!G3ReadFile(io, handle, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
128
0
      throw "Read error at scanline 0";
129
0
    }
130
0
    tifin->tif_rawcp = tifin->tif_rawdata;
131
0
    tifin->tif_rawcc = tifin->tif_rawdatasize;
132
133
0
    (*tifin->tif_setupdecode)(tifin);
134
0
    (*tifin->tif_predecode)(tifin, (uint16_t) 0);
135
0
    tifin->tif_row = 0;
136
0
    badfaxlines = 0;
137
0
    badfaxrun = 0;
138
139
0
    _TIFFmemset(refbuf, 0, linesize);
140
0
    row = 0;
141
0
    badrun = 0;   // current run of bad lines 
142
0
    while (tifin->tif_rawcc > 0) {
143
0
      ok = (*tifin->tif_decoderow)(tifin, rowbuf, linesize, 0);
144
0
      if (!ok) {
145
0
        badfaxlines++;
146
0
        badrun++;
147
        // regenerate line from previous good line 
148
0
        _TIFFmemcpy(rowbuf, refbuf, linesize);
149
0
      } else {
150
0
        if (badrun > badfaxrun)
151
0
          badfaxrun = badrun;
152
0
        badrun = 0;
153
0
        _TIFFmemcpy(refbuf, rowbuf, linesize);
154
0
      }
155
0
      tifin->tif_row++;
156
157
0
      FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
158
0
      row++;
159
0
      if (stretch) {
160
0
        FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
161
0
        row++;
162
0
      }
163
0
    }
164
0
    if (badrun > badfaxrun)
165
0
      badfaxrun = badrun;
166
167
0
    _TIFFfree(tifin->tif_rawdata);
168
0
    tifin->tif_rawdata = NULL;
169
170
0
    _TIFFfree(rowbuf);
171
0
    _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
0
  } catch(const char *message) {
182
0
    if(rowbuf) _TIFFfree(rowbuf);
183
0
    if(refbuf) _TIFFfree(refbuf);
184
0
    if(tifin->tif_rawdata) {
185
0
      _TIFFfree(tifin->tif_rawdata);
186
0
      tifin->tif_rawdata = NULL;
187
0
    }
188
0
    FreeImage_OutputMessageProc(s_format_id, message);
189
190
0
    return -1;
191
0
  }
192
193
0
  return (row);
194
0
}
195
196
197
// ==========================================================
198
// Plugin Implementation
199
// ==========================================================
200
201
static const char * DLL_CALLCONV
202
2
Format() {
203
2
  return "G3";
204
2
}
205
206
static const char * DLL_CALLCONV 
207
0
Description() {
208
0
  return "Raw fax format CCITT G.3";
209
0
}
210
211
static const char * DLL_CALLCONV 
212
0
Extension() {
213
0
  return "g3";
214
0
}
215
216
static const char * DLL_CALLCONV 
217
0
RegExpr() {
218
0
  return NULL; // there is now reasonable regexp for raw G3
219
0
}
220
221
static const char * DLL_CALLCONV 
222
0
MimeType() {
223
0
  return "image/fax-g3";
224
0
}
225
226
static BOOL DLL_CALLCONV 
227
0
SupportsExportDepth(int depth) {
228
0
  return  FALSE;
229
0
}
230
231
// ----------------------------------------------------------
232
233
static FIBITMAP * DLL_CALLCONV
234
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
235
0
  TIFF *faxTIFF = NULL;
236
0
  FIBITMAP *dib = NULL;
237
0
  FIMEMORY *memory = NULL;
238
239
  //int verbose = 0;
240
0
  int stretch = 0;
241
0
  int rows;
242
0
  float resX = 204.0;
243
0
  float resY = 196.0;
244
245
0
  uint32_t xsize = G3_DEFAULT_WIDTH;
246
0
  int compression_in = COMPRESSION_CCITTFAX3;
247
0
  int fillorder_in = FILLORDER_LSB2MSB;
248
0
  uint32_t group3options_in = 0;  // 1d-encoded 
249
0
  uint32_t group4options_in = 0;  // compressed 
250
0
  int photometric_in = PHOTOMETRIC_MINISWHITE;
251
252
0
  if(handle==NULL) return NULL;
253
254
0
  try {
255
    // set default load options
256
257
0
    compression_in = COMPRESSION_CCITTFAX3;     // input is g3-encoded 
258
0
    group3options_in &= ~GROUP3OPT_2DENCODING;   // input is 1d-encoded (g3 only) 
259
0
    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_t) 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
0
    memory = FreeImage_OpenMemory();
322
0
    if(!memory) throw FI_MSG_ERROR_MEMORY;
323
    
324
    // wrap the raw fax file
325
0
    faxTIFF = TIFFClientOpen("(FakeInput)", "w",
326
      // TIFFClientOpen() fails if we don't set existing value here 
327
0
      NULL,
328
0
      _g3ReadProc, _g3WriteProc,
329
0
      _g3SeekProc, _g3CloseProc,
330
0
      _g3SizeProc, _g3MapProc,
331
0
      _g3UnmapProc);
332
333
0
    if (faxTIFF == NULL) {
334
0
      throw "Can not create fake input file";
335
0
    }
336
0
    TIFFSetMode(faxTIFF, O_RDONLY);
337
0
    TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
338
0
    TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
339
0
    TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
340
0
    TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
341
0
    TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
342
0
    TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
343
0
    TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
344
0
    TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
345
346
    // NB: this must be done after directory info is setup 
347
0
    TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
348
0
    if (compression_in == COMPRESSION_CCITTFAX3)
349
0
      TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
350
0
    else if (compression_in == COMPRESSION_CCITTFAX4)
351
0
      TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
352
    
353
0
    resX = 204;
354
0
    if (!stretch) {
355
0
      TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
356
0
    } else {
357
0
      resY = 196;
358
0
    }
359
360
    // decode the raw fax data
361
0
    rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory);
362
0
    if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options";
363
364
365
    // allocate the output dib
366
0
    dib = FreeImage_Allocate(xsize, rows, 1);
367
0
    unsigned pitch = FreeImage_GetPitch(dib);
368
0
    uint32_t linesize = TIFFhowmany8(xsize);
369
370
    // fill the bitmap structure ...
371
    // ... palette
372
0
    RGBQUAD *pal = FreeImage_GetPalette(dib);
373
0
    if(photometric_in == PHOTOMETRIC_MINISWHITE) {
374
0
      pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
375
0
      pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
376
0
    } else {
377
0
      pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
378
0
      pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
379
0
    }
380
    // ... resolution
381
0
    FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5));
382
0
    FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5));
383
384
    // read the decoded scanline and fill the bitmap data
385
0
    FreeImage_SeekMemory(memory, 0, SEEK_SET);
386
0
    BYTE *bits = FreeImage_GetScanLine(dib, rows - 1);
387
0
    for(int k = 0; k < rows; k++) {
388
0
      FreeImage_ReadMemory(bits, linesize, 1, memory);
389
0
      bits -= pitch;
390
0
    }
391
392
    // free the TIFF wrapper
393
0
    TIFFClose(faxTIFF);
394
395
    // free the memory buffer
396
0
    FreeImage_CloseMemory(memory);
397
398
0
  } catch(const char *message) {
399
0
    if(memory) FreeImage_CloseMemory(memory);
400
0
    if(faxTIFF) TIFFClose(faxTIFF);
401
0
    if(dib) FreeImage_Unload(dib);
402
0
    FreeImage_OutputMessageProc(s_format_id, message);
403
0
    return NULL;
404
0
  }
405
406
0
  return dib;
407
408
0
}
409
410
// ==========================================================
411
//   Init
412
// ==========================================================
413
414
void DLL_CALLCONV
415
2
InitG3(Plugin *plugin, int format_id) {
416
2
  s_format_id = format_id;
417
418
2
  plugin->format_proc = Format;
419
2
  plugin->description_proc = Description;
420
2
  plugin->extension_proc = Extension;
421
2
  plugin->regexpr_proc = RegExpr;
422
2
  plugin->open_proc = NULL;
423
2
  plugin->close_proc = NULL;
424
2
  plugin->pagecount_proc = NULL;
425
2
  plugin->pagecapability_proc = NULL;
426
2
  plugin->load_proc = Load;
427
2
  plugin->save_proc = NULL;
428
2
  plugin->validate_proc = NULL;
429
2
  plugin->mime_proc = MimeType;
430
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
431
2
  plugin->supports_export_type_proc = NULL;
432
2
  plugin->supports_icc_profiles_proc = NULL;
433
2
}