comparison spandsp-0.0.6pre17/src/t4_rx.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
comparison
equal deleted inserted replaced
3:c6c5a16ce2f2 4:26cd8f1ef0b1
1 //#define T4_STATE_DEBUGGING
2 /*
3 * SpanDSP - a series of DSP components for telephony
4 *
5 * t4_rx.c - ITU T.4 FAX receive processing
6 *
7 * Written by Steve Underwood <steveu@coppice.org>
8 *
9 * Copyright (C) 2003, 2007 Steve Underwood
10 *
11 * All rights reserved.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License version 2.1,
15 * as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 * $Id: t4_rx.c,v 1.12.2.8 2009/12/21 17:18:39 steveu Exp $
27 */
28
29 /*
30 * Much of this file is based on the T.4 and T.6 support in libtiff, which requires
31 * the following notice in any derived source code:
32 *
33 * Copyright (c) 1990-1997 Sam Leffler
34 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
35 *
36 * Permission to use, copy, modify, distribute, and sell this software and
37 * its documentation for any purpose is hereby granted without fee, provided
38 * that (i) the above copyright notices and this permission notice appear in
39 * all copies of the software and related documentation, and (ii) the names of
40 * Sam Leffler and Silicon Graphics may not be used in any advertising or
41 * publicity relating to the software without the specific, prior written
42 * permission of Sam Leffler and Silicon Graphics.
43 *
44 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
45 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
46 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
47 *
48 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
49 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
50 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
52 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
53 * OF THIS SOFTWARE.
54 *
55 * Decoder support is derived from code in Frank Cringle's viewfax program;
56 * Copyright (C) 1990, 1995 Frank D. Cringle.
57 */
58
59 /*! \file */
60
61 #if defined(HAVE_CONFIG_H)
62 #include "config.h"
63 #endif
64
65 #include <stdlib.h>
66 #include <inttypes.h>
67 #include <limits.h>
68 #include <stdio.h>
69 #include <fcntl.h>
70 #include <unistd.h>
71 #include <time.h>
72 #include <memory.h>
73 #include <string.h>
74 #if defined(HAVE_TGMATH_H)
75 #include <tgmath.h>
76 #endif
77 #if defined(HAVE_MATH_H)
78 #include <math.h>
79 #endif
80 #include "floating_fudge.h"
81 #include <tiffio.h>
82
83 #include "spandsp/telephony.h"
84 #include "spandsp/logging.h"
85 #include "spandsp/bit_operations.h"
86 #include "spandsp/async.h"
87 #include "spandsp/t4_rx.h"
88 #include "spandsp/t4_tx.h"
89 #include "spandsp/version.h"
90
91 #include "spandsp/private/logging.h"
92 #include "spandsp/private/t4_rx.h"
93 #include "spandsp/private/t4_tx.h"
94
95 /*! The number of centimetres in one inch */
96 #define CM_PER_INCH 2.54f
97
98 /*! The number of EOLs to expect at the end of a T.4 page */
99 #define EOLS_TO_END_ANY_RX_PAGE 6
100 /*! The number of EOLs to check at the end of a T.4 page */
101 #define EOLS_TO_END_T4_RX_PAGE 5
102 /*! The number of EOLs to check at the end of a T.6 page */
103 #define EOLS_TO_END_T6_RX_PAGE 2
104
105 #if defined(T4_STATE_DEBUGGING)
106 static void STATE_TRACE(const char *format, ...)
107 {
108 va_list arg_ptr;
109
110 va_start(arg_ptr, format);
111 vprintf(format, arg_ptr);
112 va_end(arg_ptr);
113 }
114 /*- End of function --------------------------------------------------------*/
115 #else
116 #define STATE_TRACE(...) /**/
117 #endif
118
119 /*! T.4 run length table entry */
120 typedef struct
121 {
122 /*! Length of T.4 code, in bits */
123 uint16_t length;
124 /*! T.4 code */
125 uint16_t code;
126 /*! Run length, in bits */
127 int16_t run_length;
128 } t4_run_table_entry_t;
129
130 #include "t4_t6_decode_states.h"
131
132 #if defined(HAVE_LIBTIFF)
133 static int set_tiff_directory_info(t4_state_t *s)
134 {
135 time_t now;
136 struct tm *tm;
137 char buf[256 + 1];
138 uint16_t resunit;
139 float x_resolution;
140 float y_resolution;
141 t4_tiff_state_t *t;
142
143 t = &s->tiff;
144 /* Prepare the directory entry fully before writing the image, or libtiff complains */
145 TIFFSetField(t->tiff_file, TIFFTAG_COMPRESSION, t->output_compression);
146 if (t->output_compression == COMPRESSION_CCITT_T4)
147 {
148 TIFFSetField(t->tiff_file, TIFFTAG_T4OPTIONS, t->output_t4_options);
149 TIFFSetField(t->tiff_file, TIFFTAG_FAXMODE, FAXMODE_CLASSF);
150 }
151 TIFFSetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, s->image_width);
152 TIFFSetField(t->tiff_file, TIFFTAG_BITSPERSAMPLE, 1);
153 TIFFSetField(t->tiff_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
154 TIFFSetField(t->tiff_file, TIFFTAG_SAMPLESPERPIXEL, 1);
155 if (t->output_compression == COMPRESSION_CCITT_T4
156 ||
157 t->output_compression == COMPRESSION_CCITT_T6)
158 {
159 TIFFSetField(t->tiff_file, TIFFTAG_ROWSPERSTRIP, -1L);
160 }
161 else
162 {
163 TIFFSetField(t->tiff_file,
164 TIFFTAG_ROWSPERSTRIP,
165 TIFFDefaultStripSize(t->tiff_file, 0));
166 }
167 TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
168 TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
169 TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
170
171 x_resolution = s->x_resolution/100.0f;
172 y_resolution = s->y_resolution/100.0f;
173 /* Metric seems the sane thing to use in the 21st century, but a lot of lousy software
174 gets FAX resolutions wrong, and more get it wrong using metric than using inches. */
175 #if 0
176 TIFFSetField(t->tiff_file, TIFFTAG_XRESOLUTION, x_resolution);
177 TIFFSetField(t->tiff_file, TIFFTAG_YRESOLUTION, y_resolution);
178 resunit = RESUNIT_CENTIMETER;
179 TIFFSetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, resunit);
180 #else
181 TIFFSetField(t->tiff_file, TIFFTAG_XRESOLUTION, floorf(x_resolution*CM_PER_INCH + 0.5f));
182 TIFFSetField(t->tiff_file, TIFFTAG_YRESOLUTION, floorf(y_resolution*CM_PER_INCH + 0.5f));
183 resunit = RESUNIT_INCH;
184 TIFFSetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, resunit);
185 #endif
186 /* TODO: add the version of spandsp */
187 TIFFSetField(t->tiff_file, TIFFTAG_SOFTWARE, "Spandsp " SPANDSP_RELEASE_DATETIME_STRING);
188 if (gethostname(buf, sizeof(buf)) == 0)
189 TIFFSetField(t->tiff_file, TIFFTAG_HOSTCOMPUTER, buf);
190
191 #if defined(TIFFTAG_FAXDCS)
192 if (t->dcs)
193 TIFFSetField(t->tiff_file, TIFFTAG_FAXDCS, t->dcs);
194 #endif
195 if (t->sub_address)
196 TIFFSetField(t->tiff_file, TIFFTAG_FAXSUBADDRESS, t->sub_address);
197 if (t->far_ident)
198 TIFFSetField(t->tiff_file, TIFFTAG_IMAGEDESCRIPTION, t->far_ident);
199 if (t->vendor)
200 TIFFSetField(t->tiff_file, TIFFTAG_MAKE, t->vendor);
201 if (t->model)
202 TIFFSetField(t->tiff_file, TIFFTAG_MODEL, t->model);
203
204 time(&now);
205 tm = localtime(&now);
206 sprintf(buf,
207 "%4d/%02d/%02d %02d:%02d:%02d",
208 tm->tm_year + 1900,
209 tm->tm_mon + 1,
210 tm->tm_mday,
211 tm->tm_hour,
212 tm->tm_min,
213 tm->tm_sec);
214 TIFFSetField(t->tiff_file, TIFFTAG_DATETIME, buf);
215 TIFFSetField(t->tiff_file, TIFFTAG_FAXRECVTIME, now - s->page_start_time);
216
217 TIFFSetField(t->tiff_file, TIFFTAG_IMAGELENGTH, s->image_length);
218 /* Set the total pages to 1. For any one page document we will get this
219 right. For multi-page documents we will need to come back and fill in
220 the right answer when we know it. */
221 TIFFSetField(t->tiff_file, TIFFTAG_PAGENUMBER, s->current_page++, 1);
222 s->tiff.pages_in_file = s->current_page;
223 if (t->output_compression == COMPRESSION_CCITT_T4)
224 {
225 if (s->t4_t6_rx.bad_rows)
226 {
227 TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->t4_t6_rx.bad_rows);
228 TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED);
229 TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->t4_t6_rx.longest_bad_row_run);
230 }
231 else
232 {
233 TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN);
234 }
235 }
236 TIFFSetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, s->image_width);
237 return 0;
238 }
239 /*- End of function --------------------------------------------------------*/
240
241 static int open_tiff_output_file(t4_state_t *s, const char *file)
242 {
243 if ((s->tiff.tiff_file = TIFFOpen(file, "w")) == NULL)
244 return -1;
245 return 0;
246 }
247 /*- End of function --------------------------------------------------------*/
248
249 static void write_tiff_image(t4_state_t *s)
250 {
251 /* Set up the TIFF directory info... */
252 set_tiff_directory_info(s);
253 /* ..and then write the image... */
254 if (TIFFWriteEncodedStrip(s->tiff.tiff_file, 0, s->image_buffer, s->image_length*s->bytes_per_row) < 0)
255 span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file);
256 /* ...then the directory entry, and libtiff is happy. */
257 TIFFWriteDirectory(s->tiff.tiff_file);
258 }
259 /*- End of function --------------------------------------------------------*/
260
261 static int close_tiff_output_file(t4_state_t *s)
262 {
263 int i;
264 t4_tiff_state_t *t;
265
266 t = &s->tiff;
267 /* Perform any operations needed to tidy up a written TIFF file before
268 closure. */
269 if (s->current_page > 1)
270 {
271 /* We need to edit the TIFF directories. Until now we did not know
272 the total page count, so the TIFF file currently says one. Now we
273 need to set the correct total page count associated with each page. */
274 for (i = 0; i < s->current_page; i++)
275 {
276 TIFFSetDirectory(t->tiff_file, (tdir_t) i);
277 TIFFSetField(t->tiff_file, TIFFTAG_PAGENUMBER, i, s->current_page);
278 TIFFWriteDirectory(t->tiff_file);
279 }
280 }
281 TIFFClose(t->tiff_file);
282 t->tiff_file = NULL;
283 if (t->file)
284 {
285 /* Try not to leave a file behind, if we didn't receive any pages to
286 put in it. */
287 if (s->current_page == 0)
288 remove(t->file);
289 free((char *) t->file);
290 t->file = NULL;
291 }
292 return 0;
293 }
294 /*- End of function --------------------------------------------------------*/
295
296 #else
297
298 static int set_tiff_directory_info(t4_state_t *s)
299 {
300 return 0;
301 }
302 /*- End of function --------------------------------------------------------*/
303
304 static int get_tiff_directory_info(t4_state_t *s)
305 {
306 return 0;
307 }
308 /*- End of function --------------------------------------------------------*/
309
310 static int test_tiff_directory_info(t4_state_t *s)
311 {
312 return 0;
313 }
314 /*- End of function --------------------------------------------------------*/
315
316 static int open_tiff_input_file(t4_state_t *s, const char *file)
317 {
318 return 0;
319 }
320 /*- End of function --------------------------------------------------------*/
321
322 static int read_tiff_image(t4_state_t *s)
323 {
324 return 0;
325 }
326 /*- End of function --------------------------------------------------------*/
327
328 static int close_tiff_input_file(t4_state_t *s)
329 {
330 return 0;
331 }
332 /*- End of function --------------------------------------------------------*/
333
334 static int open_tiff_output_file(t4_state_t *s, const char *file)
335 {
336 return 0;
337 }
338 /*- End of function --------------------------------------------------------*/
339
340 static void write_tiff_image(t4_state_t *s)
341 {
342 return 0;
343 }
344 /*- End of function --------------------------------------------------------*/
345
346 static int close_tiff_output_file(t4_state_t *s)
347 {
348 return 0;
349 }
350 /*- End of function --------------------------------------------------------*/
351 #endif
352
353 static void update_row_bit_info(t4_state_t *s)
354 {
355 if (s->row_bits > s->max_row_bits)
356 s->max_row_bits = s->row_bits;
357 if (s->row_bits < s->min_row_bits)
358 s->min_row_bits = s->row_bits;
359 s->row_bits = 0;
360 }
361 /*- End of function --------------------------------------------------------*/
362
363 #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || defined(__powerpc__)
364 static __inline__ int run_length(unsigned int bits)
365 {
366 return 7 - top_bit(bits);
367 }
368 /*- End of function --------------------------------------------------------*/
369 #else
370 static __inline__ int run_length(unsigned int bits)
371 {
372 static const uint8_t run_len[256] =
373 {
374 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0F */
375 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1F */
376 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2F */
377 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3F */
378 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
379 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5F */
380 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
381 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7F */
382 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
384 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
385 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
386 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
387 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
388 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xEF */
389 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0 - 0xFF */
390 };
391
392 return run_len[bits];
393 }
394 /*- End of function --------------------------------------------------------*/
395 #endif
396
397 static int free_buffers(t4_state_t *s)
398 {
399 if (s->image_buffer)
400 {
401 free(s->image_buffer);
402 s->image_buffer = NULL;
403 s->image_buffer_size = 0;
404 }
405 if (s->cur_runs)
406 {
407 free(s->cur_runs);
408 s->cur_runs = NULL;
409 }
410 if (s->ref_runs)
411 {
412 free(s->ref_runs);
413 s->ref_runs = NULL;
414 }
415 if (s->row_buf)
416 {
417 free(s->row_buf);
418 s->row_buf = NULL;
419 }
420 return 0;
421 }
422 /*- End of function --------------------------------------------------------*/
423
424 static __inline__ void add_run_to_row(t4_state_t *s)
425 {
426 if (s->t4_t6_rx.run_length >= 0)
427 {
428 s->row_len += s->t4_t6_rx.run_length;
429 /* Don't allow rows to grow too long, and overflow the buffers */
430 if (s->row_len <= s->image_width)
431 s->cur_runs[s->t4_t6_rx.a_cursor++] = s->t4_t6_rx.run_length;
432 }
433 s->t4_t6_rx.run_length = 0;
434 }
435 /*- End of function --------------------------------------------------------*/
436
437 static int put_decoded_row(t4_state_t *s)
438 {
439 static const int msbmask[9] =
440 {
441 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
442 };
443 uint8_t *t;
444 uint32_t i;
445 uint32_t *p;
446 int fudge;
447 int row_starts_at;
448 int x;
449 int j;
450
451 if (s->t4_t6_rx.run_length)
452 add_run_to_row(s);
453 #if defined(T4_STATE_DEBUGGING)
454 /* Dump the runs of black and white for analysis */
455 {
456 int total;
457
458 total = 0;
459 for (x = 0; x < s->t4_t6_rx.b_cursor; x++)
460 total += s->ref_runs[x];
461 printf("Ref (%d)", total);
462 for (x = 0; x < s->t4_t6_rx.b_cursor; x++)
463 printf(" %" PRIu32, s->ref_runs[x]);
464 printf("\n");
465 total = 0;
466 for (x = 0; x < s->t4_t6_rx.a_cursor; x++)
467 total += s->cur_runs[x];
468 printf("Cur (%d)", total);
469 for (x = 0; x < s->t4_t6_rx.a_cursor; x++)
470 printf(" %" PRIu32, s->cur_runs[x]);
471 printf("\n");
472 }
473 #endif
474 row_starts_at = s->image_size;
475 /* Make sure there is enough room for another row */
476 if (s->image_size + s->bytes_per_row >= s->image_buffer_size)
477 {
478 if ((t = realloc(s->image_buffer, s->image_buffer_size + 100*s->bytes_per_row)) == NULL)
479 return -1;
480 s->image_buffer_size += 100*s->bytes_per_row;
481 s->image_buffer = t;
482 }
483 if (s->row_len == s->image_width)
484 {
485 STATE_TRACE("%d Good row - %d %s\n", s->image_length, s->row_len, (s->row_is_2d) ? "2D" : "1D");
486 if (s->t4_t6_rx.curr_bad_row_run)
487 {
488 if (s->t4_t6_rx.curr_bad_row_run > s->t4_t6_rx.longest_bad_row_run)
489 s->t4_t6_rx.longest_bad_row_run = s->t4_t6_rx.curr_bad_row_run;
490 s->t4_t6_rx.curr_bad_row_run = 0;
491 }
492 /* Convert the runs to a bit image of the row */
493 /* White/black/white... runs, always starting with white. That means the first run could be
494 zero length. */
495 for (x = 0, fudge = 0; x < s->t4_t6_rx.a_cursor; x++, fudge ^= 0xFF)
496 {
497 i = s->cur_runs[x];
498 if ((int) i >= s->tx_bits)
499 {
500 s->tx_bitstream = (s->tx_bitstream << s->tx_bits) | (msbmask[s->tx_bits] & fudge);
501 for (i += (8 - s->tx_bits); i >= 8; i -= 8)
502 {
503 s->tx_bits = 8;
504 s->image_buffer[s->image_size++] = (uint8_t) s->tx_bitstream;
505 s->tx_bitstream = fudge;
506 }
507 }
508 s->tx_bitstream = (s->tx_bitstream << i) | (msbmask[i] & fudge);
509 s->tx_bits -= i;
510 }
511 s->image_length++;
512 }
513 else
514 {
515 STATE_TRACE("%d Bad row - %d %s\n", s->image_length, s->row_len, (s->row_is_2d) ? "2D" : "1D");
516 /* Try to clean up the bad runs, and produce something reasonable as the reference
517 row for the next row. Use a copy of the previous good row as the actual current
518 row. If the row only fell apart near the end, reusing it might be the best
519 solution. */
520 for (j = 0, fudge = 0; j < s->t4_t6_rx.a_cursor && fudge < s->image_width; j++)
521 fudge += s->cur_runs[j];
522 if (fudge < s->image_width)
523 {
524 /* Try to pad with white, and avoid black, to minimise mess on the image. */
525 if ((s->t4_t6_rx.a_cursor & 1))
526 {
527 /* We currently finish in white. We could extend that, but it is probably of
528 the right length. Changing it would only further mess up what happens in the
529 next row. It seems better to add a black spot, and an extra white run. */
530 s->cur_runs[s->t4_t6_rx.a_cursor++] = 1;
531 fudge++;
532 if (fudge < s->image_width)
533 s->cur_runs[s->t4_t6_rx.a_cursor++] = s->image_width - fudge;
534 }
535 else
536 {
537 /* We currently finish on black, so we add an extra white run to fill out the line. */
538 s->cur_runs[s->t4_t6_rx.a_cursor++] = s->image_width - fudge;
539 }
540 }
541 else
542 {
543 /* Trim the last element to align with the proper image width */
544 s->cur_runs[s->t4_t6_rx.a_cursor] += (s->image_width - fudge);
545 }
546 /* Ensure there is a previous line to copy from. */
547 if (s->image_size != s->t4_t6_rx.last_row_starts_at)
548 {
549 /* Copy the previous row over this one */
550 memcpy(s->image_buffer + s->image_size, s->image_buffer + s->t4_t6_rx.last_row_starts_at, s->bytes_per_row);
551 s->image_size += s->bytes_per_row;
552 s->image_length++;
553 }
554 s->t4_t6_rx.bad_rows++;
555 s->t4_t6_rx.curr_bad_row_run++;
556 }
557
558 /* Pad the row as it becomes the reference row, so there are no odd runs to pick up if we
559 step off the end of the list. */
560 s->cur_runs[s->t4_t6_rx.a_cursor] = 0;
561 s->cur_runs[s->t4_t6_rx.a_cursor + 1] = 0;
562
563 /* Prepare the buffers for the next row. */
564 s->t4_t6_rx.last_row_starts_at = row_starts_at;
565 /* Swap the buffers */
566 p = s->cur_runs;
567 s->cur_runs = s->ref_runs;
568 s->ref_runs = p;
569
570 s->t4_t6_rx.b_cursor = 1;
571 s->t4_t6_rx.a_cursor = 0;
572 s->t4_t6_rx.b1 = s->ref_runs[0];
573 s->t4_t6_rx.a0 = 0;
574
575 s->t4_t6_rx.run_length = 0;
576
577 return 0;
578 }
579 /*- End of function --------------------------------------------------------*/
580
581 SPAN_DECLARE(int) t4_rx_end_page(t4_state_t *s)
582 {
583 int row;
584 int i;
585
586 if (s->line_encoding == T4_COMPRESSION_ITU_T6)
587 {
588 /* Push enough zeros through the decoder to flush out any remaining codes */
589 for (i = 0; i < 13; i++)
590 t4_rx_put_bit(s, 0);
591 }
592 if (s->t4_t6_rx.curr_bad_row_run)
593 {
594 if (s->t4_t6_rx.curr_bad_row_run > s->t4_t6_rx.longest_bad_row_run)
595 s->t4_t6_rx.longest_bad_row_run = s->t4_t6_rx.curr_bad_row_run;
596 s->t4_t6_rx.curr_bad_row_run = 0;
597 }
598
599 if (s->image_size == 0)
600 return -1;
601
602 if (s->t4_t6_rx.row_write_handler)
603 {
604 for (row = 0; row < s->image_length; row++)
605 {
606 if (s->t4_t6_rx.row_write_handler(s->t4_t6_rx.row_write_user_data, s->image_buffer + row*s->bytes_per_row, s->bytes_per_row) < 0)
607 {
608 span_log(&s->logging, SPAN_LOG_WARNING, "Write error at row %d.\n", row);
609 break;
610 }
611 }
612 /* Write a blank row to indicate the end of the image. */
613 if (s->t4_t6_rx.row_write_handler(s->t4_t6_rx.row_write_user_data, NULL, 0) < 0)
614 span_log(&s->logging, SPAN_LOG_WARNING, "Write error at row %d.\n", row);
615 }
616 else
617 {
618 write_tiff_image(s);
619 }
620 s->t4_t6_rx.rx_bits = 0;
621 s->t4_t6_rx.rx_skip_bits = 0;
622 s->t4_t6_rx.rx_bitstream = 0;
623 s->t4_t6_rx.consecutive_eols = EOLS_TO_END_ANY_RX_PAGE;
624
625 s->image_size = 0;
626 return 0;
627 }
628 /*- End of function --------------------------------------------------------*/
629
630 static __inline__ void drop_rx_bits(t4_state_t *s, int bits)
631 {
632 /* Only remove one bit right now. The rest need to be removed step by step,
633 checking for a misaligned EOL along the way. This is time consuming, but
634 if we don't do it a single bit error can severely damage an image. */
635 s->row_bits += bits;
636 s->t4_t6_rx.rx_skip_bits += (bits - 1);
637 s->t4_t6_rx.rx_bits--;
638 s->t4_t6_rx.rx_bitstream >>= 1;
639 }
640 /*- End of function --------------------------------------------------------*/
641
642 static __inline__ void force_drop_rx_bits(t4_state_t *s, int bits)
643 {
644 /* This should only be called to drop the bits of an EOL, as that is the
645 only place where it is safe to drop them all at once. */
646 s->row_bits += bits;
647 s->t4_t6_rx.rx_skip_bits = 0;
648 s->t4_t6_rx.rx_bits -= bits;
649 s->t4_t6_rx.rx_bitstream >>= bits;
650 }
651 /*- End of function --------------------------------------------------------*/
652
653 static int rx_put_bits(t4_state_t *s, uint32_t bit_string, int quantity)
654 {
655 int bits;
656
657 /* We decompress bit by bit, as the data stream is received. We need to
658 scan continuously for EOLs, so we might as well work this way. */
659 s->line_image_size += quantity;
660 s->t4_t6_rx.rx_bitstream |= (bit_string << s->t4_t6_rx.rx_bits);
661 /* The longest item we need to scan for is 13 bits long (a 2D EOL), so we
662 need a minimum of 13 bits in the buffer to proceed with any bit stream
663 analysis. */
664 if ((s->t4_t6_rx.rx_bits += quantity) < 13)
665 return FALSE;
666 if (s->t4_t6_rx.consecutive_eols)
667 {
668 /* Check if the image has already terminated. */
669 if (s->t4_t6_rx.consecutive_eols >= EOLS_TO_END_ANY_RX_PAGE)
670 return TRUE;
671 /* Check if the image hasn't even started. */
672 if (s->t4_t6_rx.consecutive_eols < 0)
673 {
674 /* We are waiting for the very first EOL (1D or 2D only). */
675 /* We need to take this bit by bit, as the EOL could be anywhere,
676 and any junk could preceed it. */
677 while ((s->t4_t6_rx.rx_bitstream & 0xFFF) != 0x800)
678 {
679 s->t4_t6_rx.rx_bitstream >>= 1;
680 if (--s->t4_t6_rx.rx_bits < 13)
681 return FALSE;
682 }
683 /* We have an EOL, so now the page begins and we can proceed to
684 process the bit stream as image data. */
685 s->t4_t6_rx.consecutive_eols = 0;
686 if (s->line_encoding == T4_COMPRESSION_ITU_T4_1D)
687 {
688 s->row_is_2d = FALSE;
689 force_drop_rx_bits(s, 12);
690 }
691 else
692 {
693 s->row_is_2d = !(s->t4_t6_rx.rx_bitstream & 0x1000);
694 force_drop_rx_bits(s, 13);
695 }
696 }
697 }
698
699 while (s->t4_t6_rx.rx_bits >= 13)
700 {
701 /* We need to check for EOLs bit by bit through the whole stream. If
702 we just try looking between code words, we will miss an EOL when a bit
703 error has throw the code words completely out of step. The can mean
704 recovery takes many lines, and the image gets really messed up. */
705 /* Although EOLs are not inserted at the end of each row of a T.6 image,
706 they are still perfectly valid, and can terminate an image. */
707 if ((s->t4_t6_rx.rx_bitstream & 0x0FFF) == 0x0800)
708 {
709 STATE_TRACE("EOL\n");
710 if (s->row_len == 0)
711 {
712 /* A zero length row - i.e. 2 consecutive EOLs - is distinctly
713 the end of page condition. That's all we actually get on a
714 T.6 page. However, there are a minimum of 6 EOLs at the end of
715 any T.4 page. We can look for more than 2 EOLs in case bit
716 errors simulate the end of page condition at the wrong point.
717 Such robust checking is irrelevant for a T.6 page, as it should
718 be error free. */
719 /* Note that for a T.6 page we should get here on the very first
720 EOL, as the row length should be zero at that point. Therefore
721 we should count up both EOLs, unless there is some bogus partial
722 row ahead of them. */
723 s->t4_t6_rx.consecutive_eols++;
724 if (s->line_encoding == T4_COMPRESSION_ITU_T6)
725 {
726 if (s->t4_t6_rx.consecutive_eols >= EOLS_TO_END_T6_RX_PAGE)
727 {
728 s->t4_t6_rx.consecutive_eols = EOLS_TO_END_ANY_RX_PAGE;
729 return TRUE;
730 }
731 }
732 else
733 {
734 if (s->t4_t6_rx.consecutive_eols >= EOLS_TO_END_T4_RX_PAGE)
735 {
736 s->t4_t6_rx.consecutive_eols = EOLS_TO_END_ANY_RX_PAGE;
737 return TRUE;
738 }
739 }
740 }
741 else
742 {
743 /* The EOLs are not back-to-back, so they are not part of the
744 end of page condition. */
745 if (s->t4_t6_rx.run_length > 0)
746 add_run_to_row(s);
747 s->t4_t6_rx.consecutive_eols = 0;
748 if (put_decoded_row(s))
749 return TRUE;
750 update_row_bit_info(s);
751 }
752 if (s->line_encoding == T4_COMPRESSION_ITU_T4_2D)
753 {
754 s->row_is_2d = !(s->t4_t6_rx.rx_bitstream & 0x1000);
755 force_drop_rx_bits(s, 13);
756 }
757 else
758 {
759 force_drop_rx_bits(s, 12);
760 }
761 s->t4_t6_rx.its_black = FALSE;
762 s->t4_t6_rx.black_white = 0;
763 s->t4_t6_rx.run_length = 0;
764 s->row_len = 0;
765 continue;
766 }
767 if (s->t4_t6_rx.rx_skip_bits)
768 {
769 /* We are clearing out the remaining bits of the last code word we
770 absorbed. */
771 s->t4_t6_rx.rx_skip_bits--;
772 s->t4_t6_rx.rx_bits--;
773 s->t4_t6_rx.rx_bitstream >>= 1;
774 continue;
775 }
776 if (s->row_is_2d && s->t4_t6_rx.black_white == 0)
777 {
778 bits = s->t4_t6_rx.rx_bitstream & 0x7F;
779 STATE_TRACE("State %d, %d - ",
780 t4_2d_table[bits].state,
781 t4_2d_table[bits].width);
782 if (s->row_len >= s->image_width)
783 {
784 drop_rx_bits(s, t4_2d_table[bits].width);
785 continue;
786 }
787 if (s->t4_t6_rx.a_cursor)
788 {
789 /* Move past a0, always staying on the current colour */
790 for ( ; s->t4_t6_rx.b1 <= s->t4_t6_rx.a0; s->t4_t6_rx.b_cursor += 2)
791 s->t4_t6_rx.b1 += (s->ref_runs[s->t4_t6_rx.b_cursor] + s->ref_runs[s->t4_t6_rx.b_cursor + 1]);
792 }
793 switch (t4_2d_table[bits].state)
794 {
795 case S_Horiz:
796 STATE_TRACE("Horiz %d %d %d\n",
797 s->image_width,
798 s->t4_t6_rx.a0,
799 s->t4_t6_rx.a_cursor);
800 /* We now need to extract a white/black or black/white pair of runs, using the 1D
801 method. If the first of the pair takes us exactly to the end of the row, there
802 should still be a zero length element for the second of the pair. */
803 s->t4_t6_rx.its_black = s->t4_t6_rx.a_cursor & 1;
804 s->t4_t6_rx.black_white = 2;
805 break;
806 case S_Vert:
807 STATE_TRACE("Vert[%d] %d %d %d %d\n",
808 t4_2d_table[bits].param,
809 s->image_width,
810 s->t4_t6_rx.a0,
811 s->t4_t6_rx.b1,
812 s->t4_t6_rx.run_length);
813 s->t4_t6_rx.run_length += (s->t4_t6_rx.b1 - s->t4_t6_rx.a0 + t4_2d_table[bits].param);
814 s->t4_t6_rx.a0 = s->t4_t6_rx.b1 + t4_2d_table[bits].param;
815 add_run_to_row(s);
816 /* We need to move one step in one direction or the other, to change to the
817 opposite colour */
818 if (t4_2d_table[bits].param >= 0)
819 {
820 s->t4_t6_rx.b1 += s->ref_runs[s->t4_t6_rx.b_cursor++];
821 }
822 else
823 {
824 if (s->t4_t6_rx.b_cursor)
825 s->t4_t6_rx.b1 -= s->ref_runs[--s->t4_t6_rx.b_cursor];
826 }
827 break;
828 case S_Pass:
829 STATE_TRACE("Pass %d %d %d %d %d\n",
830 s->image_width,
831 s->t4_t6_rx.a0,
832 s->t4_t6_rx.b1,
833 s->ref_runs[s->t4_t6_rx.b_cursor],
834 s->ref_runs[s->t4_t6_rx.b_cursor + 1]);
835 s->t4_t6_rx.b1 += s->ref_runs[s->t4_t6_rx.b_cursor++];
836 s->t4_t6_rx.run_length += (s->t4_t6_rx.b1 - s->t4_t6_rx.a0);
837 s->t4_t6_rx.a0 = s->t4_t6_rx.b1;
838 s->t4_t6_rx.b1 += s->ref_runs[s->t4_t6_rx.b_cursor++];
839 break;
840 case S_Ext:
841 /* We do not currently handle any kind of extension */
842 STATE_TRACE("Ext %d %d %d 0x%x\n",
843 s->image_width,
844 s->t4_t6_rx.a0,
845 ((s->t4_t6_rx.rx_bitstream >> t4_2d_table[bits].width) & 0x7),
846 s->t4_t6_rx.rx_bitstream);
847 /* TODO: The uncompressed option should be implemented. */
848 break;
849 case S_Null:
850 STATE_TRACE("Null\n");
851 break;
852 default:
853 STATE_TRACE("Unexpected T.4 state\n");
854 span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected T.4 state %d\n", t4_2d_table[bits].state);
855 break;
856 }
857 drop_rx_bits(s, t4_2d_table[bits].width);
858 }
859 else
860 {
861 if (s->t4_t6_rx.its_black)
862 {
863 bits = s->t4_t6_rx.rx_bitstream & 0x1FFF;
864 STATE_TRACE("State %d, %d - Black %d %d %d\n",
865 t4_1d_black_table[bits].state,
866 t4_1d_black_table[bits].width,
867 s->image_width,
868 s->t4_t6_rx.a0,
869 t4_1d_black_table[bits].param);
870 switch (t4_1d_black_table[bits].state)
871 {
872 case S_MakeUpB:
873 case S_MakeUp:
874 s->t4_t6_rx.run_length += t4_1d_black_table[bits].param;
875 s->t4_t6_rx.a0 += t4_1d_black_table[bits].param;
876 break;
877 case S_TermB:
878 s->t4_t6_rx.its_black = FALSE;
879 if (s->row_len < s->image_width)
880 {
881 s->t4_t6_rx.run_length += t4_1d_black_table[bits].param;
882 s->t4_t6_rx.a0 += t4_1d_black_table[bits].param;
883 add_run_to_row(s);
884 }
885 if (s->t4_t6_rx.black_white)
886 s->t4_t6_rx.black_white--;
887 break;
888 default:
889 /* Bad black */
890 s->t4_t6_rx.black_white = 0;
891 break;
892 }
893 drop_rx_bits(s, t4_1d_black_table[bits].width);
894 }
895 else
896 {
897 bits = s->t4_t6_rx.rx_bitstream & 0xFFF;
898 STATE_TRACE("State %d, %d - White %d %d %d\n",
899 t4_1d_white_table[bits].state,
900 t4_1d_white_table[bits].width,
901 s->image_width,
902 s->t4_t6_rx.a0,
903 t4_1d_white_table[bits].param);
904 switch (t4_1d_white_table[bits].state)
905 {
906 case S_MakeUpW:
907 case S_MakeUp:
908 s->t4_t6_rx.run_length += t4_1d_white_table[bits].param;
909 s->t4_t6_rx.a0 += t4_1d_white_table[bits].param;
910 break;
911 case S_TermW:
912 s->t4_t6_rx.its_black = TRUE;
913 if (s->row_len < s->image_width)
914 {
915 s->t4_t6_rx.run_length += t4_1d_white_table[bits].param;
916 s->t4_t6_rx.a0 += t4_1d_white_table[bits].param;
917 add_run_to_row(s);
918 }
919 if (s->t4_t6_rx.black_white)
920 s->t4_t6_rx.black_white--;
921 break;
922 default:
923 /* Bad white */
924 s->t4_t6_rx.black_white = 0;
925 break;
926 }
927 drop_rx_bits(s, t4_1d_white_table[bits].width);
928 }
929 }
930 if (s->t4_t6_rx.a0 >= s->image_width)
931 s->t4_t6_rx.a0 = s->image_width - 1;
932
933 if (s->line_encoding == T4_COMPRESSION_ITU_T6)
934 {
935 /* T.6 has no EOL markers. We sense the end of a line by its length alone. */
936 /* The last test here is a backstop protection, so a corrupt image cannot
937 cause us to do bad things. Bad encoders have actually been seen, which
938 demand such protection. */
939 if (s->t4_t6_rx.black_white == 0 && s->row_len >= s->image_width)
940 {
941 STATE_TRACE("EOL T.6\n");
942 if (s->t4_t6_rx.run_length > 0)
943 add_run_to_row(s);
944 update_row_bit_info(s);
945 if (put_decoded_row(s))
946 return TRUE;
947 s->t4_t6_rx.its_black = FALSE;
948 s->t4_t6_rx.black_white = 0;
949 s->t4_t6_rx.run_length = 0;
950 s->row_len = 0;
951 }
952 }
953 }
954 return FALSE;
955 }
956 /*- End of function --------------------------------------------------------*/
957
958 SPAN_DECLARE(int) t4_rx_put_bit(t4_state_t *s, int bit)
959 {
960 return rx_put_bits(s, bit & 1, 1);
961 }
962 /*- End of function --------------------------------------------------------*/
963
964 SPAN_DECLARE(int) t4_rx_put_byte(t4_state_t *s, uint8_t byte)
965 {
966 return rx_put_bits(s, byte & 0xFF, 8);
967 }
968 /*- End of function --------------------------------------------------------*/
969
970 SPAN_DECLARE(int) t4_rx_put_chunk(t4_state_t *s, const uint8_t buf[], int len)
971 {
972 int i;
973 uint8_t byte;
974
975 for (i = 0; i < len; i++)
976 {
977 byte = buf[i];
978 if (rx_put_bits(s, byte & 0xFF, 8))
979 return TRUE;
980 }
981 return FALSE;
982 }
983 /*- End of function --------------------------------------------------------*/
984
985 SPAN_DECLARE(int) t4_rx_set_row_write_handler(t4_state_t *s, t4_row_write_handler_t handler, void *user_data)
986 {
987 s->t4_t6_rx.row_write_handler = handler;
988 s->t4_t6_rx.row_write_user_data = user_data;
989 return 0;
990 }
991 /*- End of function --------------------------------------------------------*/
992
993 SPAN_DECLARE(t4_state_t *) t4_rx_init(t4_state_t *s, const char *file, int output_encoding)
994 {
995 if (s == NULL)
996 {
997 if ((s = (t4_state_t *) malloc(sizeof(*s))) == NULL)
998 return NULL;
999 }
1000 memset(s, 0, sizeof(*s));
1001 span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
1002 span_log_set_protocol(&s->logging, "T.4");
1003 s->rx = TRUE;
1004
1005 span_log(&s->logging, SPAN_LOG_FLOW, "Start rx document\n");
1006
1007 if (open_tiff_output_file(s, file) < 0)
1008 return NULL;
1009
1010 /* Save the file name for logging reports. */
1011 s->tiff.file = strdup(file);
1012 /* Only provide for one form of coding throughout the file, even though the
1013 coding on the wire could change between pages. */
1014 switch (output_encoding)
1015 {
1016 case T4_COMPRESSION_ITU_T4_1D:
1017 s->tiff.output_compression = COMPRESSION_CCITT_T4;
1018 s->tiff.output_t4_options = GROUP3OPT_FILLBITS;
1019 break;
1020 case T4_COMPRESSION_ITU_T4_2D:
1021 s->tiff.output_compression = COMPRESSION_CCITT_T4;
1022 s->tiff.output_t4_options = GROUP3OPT_FILLBITS | GROUP3OPT_2DENCODING;
1023 break;
1024 case T4_COMPRESSION_ITU_T6:
1025 s->tiff.output_compression = COMPRESSION_CCITT_T6;
1026 s->tiff.output_t4_options = 0;
1027 break;
1028 }
1029
1030 /* Until we have a valid figure for the bytes per row, we need it to be set to a suitable
1031 value to ensure it will be seen as changing when the real value is used. */
1032 s->bytes_per_row = 0;
1033
1034 s->current_page = 0;
1035 s->tiff.pages_in_file = 0;
1036 s->tiff.start_page = 0;
1037 s->tiff.stop_page = INT_MAX;
1038
1039 s->image_buffer = NULL;
1040 s->image_buffer_size = 0;
1041
1042 /* Set some default values */
1043 s->x_resolution = T4_X_RESOLUTION_R8;
1044 s->y_resolution = T4_Y_RESOLUTION_FINE;
1045 s->image_width = T4_WIDTH_R8_A4;
1046
1047 return s;
1048 }
1049 /*- End of function --------------------------------------------------------*/
1050
1051 SPAN_DECLARE(int) t4_rx_start_page(t4_state_t *s)
1052 {
1053 int bytes_per_row;
1054 int run_space;
1055 uint32_t *bufptr;
1056
1057 span_log(&s->logging, SPAN_LOG_FLOW, "Start rx page - compression %d\n", s->line_encoding);
1058 if (s->tiff.tiff_file == NULL)
1059 return -1;
1060
1061 /* Calculate the scanline/tile width. */
1062 bytes_per_row = (s->image_width + 7)/8;
1063 run_space = (s->image_width + 4)*sizeof(uint32_t);
1064 if (bytes_per_row != s->bytes_per_row)
1065 {
1066 /* Allocate the space required for decoding the new row length. */
1067 s->bytes_per_row = bytes_per_row;
1068 if ((bufptr = (uint32_t *) realloc(s->cur_runs, run_space)) == NULL)
1069 return -1;
1070 s->cur_runs = bufptr;
1071 if ((bufptr = (uint32_t *) realloc(s->ref_runs, run_space)) == NULL)
1072 return -1;
1073 s->ref_runs = bufptr;
1074 }
1075 memset(s->cur_runs, 0, run_space);
1076 memset(s->ref_runs, 0, run_space);
1077
1078 s->t4_t6_rx.rx_bits = 0;
1079 s->t4_t6_rx.rx_skip_bits = 0;
1080 s->t4_t6_rx.rx_bitstream = 0;
1081 s->row_bits = 0;
1082 s->min_row_bits = INT_MAX;
1083 s->max_row_bits = 0;
1084
1085 s->row_is_2d = (s->line_encoding == T4_COMPRESSION_ITU_T6);
1086 /* We start at -1 EOLs for 1D and 2D decoding, as an indication we are waiting for the
1087 first EOL. T.6 coding starts without any preamble. */
1088 s->t4_t6_rx.consecutive_eols = (s->line_encoding == T4_COMPRESSION_ITU_T6) ? 0 : -1;
1089
1090 s->t4_t6_rx.bad_rows = 0;
1091 s->t4_t6_rx.longest_bad_row_run = 0;
1092 s->t4_t6_rx.curr_bad_row_run = 0;
1093 s->image_length = 0;
1094 s->tx_bitstream = 0;
1095 s->tx_bits = 8;
1096 s->image_size = 0;
1097 s->line_image_size = 0;
1098 s->t4_t6_rx.last_row_starts_at = 0;
1099
1100 s->row_len = 0;
1101 s->t4_t6_rx.its_black = FALSE;
1102 s->t4_t6_rx.black_white = 0;
1103
1104 /* Initialise the reference line to all white */
1105 s->ref_runs[0] =
1106 s->ref_runs[1] =
1107 s->ref_runs[2] =
1108 s->ref_runs[3] = s->image_width;
1109
1110 s->t4_t6_rx.b_cursor = 1;
1111 s->t4_t6_rx.a_cursor = 0;
1112 s->t4_t6_rx.b1 = s->ref_runs[0];
1113 s->t4_t6_rx.a0 = 0;
1114
1115 s->t4_t6_rx.run_length = 0;
1116
1117 time (&s->page_start_time);
1118
1119 return 0;
1120 }
1121 /*- End of function --------------------------------------------------------*/
1122
1123 SPAN_DECLARE(int) t4_rx_release(t4_state_t *s)
1124 {
1125 if (!s->rx)
1126 return -1;
1127 if (s->tiff.tiff_file)
1128 close_tiff_output_file(s);
1129 free_buffers(s);
1130 return 0;
1131 }
1132 /*- End of function --------------------------------------------------------*/
1133
1134 SPAN_DECLARE(int) t4_rx_free(t4_state_t *s)
1135 {
1136 int ret;
1137
1138 ret = t4_rx_release(s);
1139 free(s);
1140 return ret;
1141 }
1142 /*- End of function --------------------------------------------------------*/
1143
1144 SPAN_DECLARE(void) t4_rx_set_rx_encoding(t4_state_t *s, int encoding)
1145 {
1146 s->line_encoding = encoding;
1147 }
1148 /*- End of function --------------------------------------------------------*/
1149
1150 SPAN_DECLARE(void) t4_rx_set_image_width(t4_state_t *s, int width)
1151 {
1152 s->image_width = width;
1153 }
1154 /*- End of function --------------------------------------------------------*/
1155
1156 SPAN_DECLARE(void) t4_rx_set_y_resolution(t4_state_t *s, int resolution)
1157 {
1158 s->y_resolution = resolution;
1159 }
1160 /*- End of function --------------------------------------------------------*/
1161
1162 SPAN_DECLARE(void) t4_rx_set_x_resolution(t4_state_t *s, int resolution)
1163 {
1164 s->x_resolution = resolution;
1165 }
1166 /*- End of function --------------------------------------------------------*/
1167
1168 SPAN_DECLARE(void) t4_rx_set_dcs(t4_state_t *s, const char *dcs)
1169 {
1170 s->tiff.dcs = (dcs && dcs[0]) ? dcs : NULL;
1171 }
1172 /*- End of function --------------------------------------------------------*/
1173
1174 SPAN_DECLARE(void) t4_rx_set_sub_address(t4_state_t *s, const char *sub_address)
1175 {
1176 s->tiff.sub_address = (sub_address && sub_address[0]) ? sub_address : NULL;
1177 }
1178 /*- End of function --------------------------------------------------------*/
1179
1180 SPAN_DECLARE(void) t4_rx_set_far_ident(t4_state_t *s, const char *ident)
1181 {
1182 s->tiff.far_ident = (ident && ident[0]) ? ident : NULL;
1183 }
1184 /*- End of function --------------------------------------------------------*/
1185
1186 SPAN_DECLARE(void) t4_rx_set_vendor(t4_state_t *s, const char *vendor)
1187 {
1188 s->tiff.vendor = vendor;
1189 }
1190 /*- End of function --------------------------------------------------------*/
1191
1192 SPAN_DECLARE(void) t4_rx_set_model(t4_state_t *s, const char *model)
1193 {
1194 s->tiff.model = model;
1195 }
1196 /*- End of function --------------------------------------------------------*/
1197
1198 SPAN_DECLARE(void) t4_get_transfer_statistics(t4_state_t *s, t4_stats_t *t)
1199 {
1200 t->pages_transferred = s->current_page - s->tiff.start_page;
1201 t->pages_in_file = s->tiff.pages_in_file;
1202 t->width = s->image_width;
1203 t->length = s->image_length;
1204 t->bad_rows = s->t4_t6_rx.bad_rows;
1205 t->longest_bad_row_run = s->t4_t6_rx.longest_bad_row_run;
1206 t->x_resolution = s->x_resolution;
1207 t->y_resolution = s->y_resolution;
1208 t->encoding = s->line_encoding;
1209 t->line_image_size = s->line_image_size/8;
1210 }
1211 /*- End of function --------------------------------------------------------*/
1212
1213 SPAN_DECLARE(const char *) t4_encoding_to_str(int encoding)
1214 {
1215 switch (encoding)
1216 {
1217 case T4_COMPRESSION_ITU_T4_1D:
1218 return "T.4 1-D";
1219 case T4_COMPRESSION_ITU_T4_2D:
1220 return "T.4 2-D";
1221 case T4_COMPRESSION_ITU_T6:
1222 return "T.6";
1223 }
1224 return "???";
1225 }
1226 /*- End of function --------------------------------------------------------*/
1227 /*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.