From cc795b87c4009bfdf953a3b5dcaa4eb372f14c82 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Sat, 13 Apr 2013 01:46:14 +0800 Subject: [PATCH] Allow writing of T.85 format TIFF files --- libs/spandsp/README | 2 +- libs/spandsp/src/t4_tx.c | 321 ++++++++++++++++++++++++++------------- 2 files changed, 220 insertions(+), 103 deletions(-) diff --git a/libs/spandsp/README b/libs/spandsp/README index 093960b429..3e900525e8 100644 --- a/libs/spandsp/README +++ b/libs/spandsp/README @@ -1,4 +1,4 @@ -spandsp 0.0.5 - A DSP library for telephony +spandsp 0.0.6 - A DSP library for telephony ------------------------------------------- SpanDSP is a library of DSP functions for telephony, in the 8000 sample per diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 7c404f23c9..ff5b5fd349 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -446,13 +446,13 @@ static int test_tiff_directory_info(t4_tx_state_t *s) { uint16_t res_unit; uint32_t parm32; + uint16_t bits_per_sample; + uint16_t samples_per_pixel; + int image_type; int best_x_entry; int best_y_entry; float x_resolution; float y_resolution; - uint16_t bits_per_sample; - uint16_t samples_per_pixel; - int image_type; t4_tx_tiff_state_t *t; t = &s->tiff; @@ -574,12 +574,43 @@ static int row_read(void *user_data, uint8_t buf[], size_t len) } /*- End of function --------------------------------------------------------*/ +static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t len) +{ + t85_packer_t *s; + + s = (t85_packer_t *) user_data; + memcpy(&s->buf[s->ptr], buf, len); + s->ptr += len; + s->row++; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static int t85_comment_handler(void *user_data, const uint8_t buf[], size_t len) +{ + t4_tx_state_t *s; + + s = (t4_tx_state_t *) user_data; + if (buf) + span_log(&s->logging, SPAN_LOG_WARNING, "T.85 comment (%d): %s\n", (int) len, buf); + else + span_log(&s->logging, SPAN_LOG_WARNING, "T.85 comment (%d): ---\n", (int) len); + return 0; +} +/*- End of function --------------------------------------------------------*/ + static int read_tiff_image(t4_tx_state_t *s) { int total_len; int len; + int biggest; int i; + int num_strips; + int result; uint8_t *t; + uint8_t *raw_data; + t85_decode_state_t t85; + t85_packer_t t85_pack; image_translate_state_t *translator; if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL) @@ -625,31 +656,101 @@ static int read_tiff_image(t4_tx_state_t *s) } else { - s->tiff.image_size = s->image_length*TIFFScanlineSize(s->tiff.tiff_file); - if (s->tiff.image_size >= s->tiff.image_buffer_size) + /* The original image is a bi-level one. We can't really rescale it, as that works out + really poorly for a bi-level image. It has to be used in its original form. The only + practical exception is to conver a superfine resolution image to a fine resolution one, + or a fine image to a standard resolution one. We could pad slightly short rows or crop + slightly long one, but lets not bother. */ + switch (s->tiff.compression) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) - return -1; - s->tiff.image_buffer_size = s->tiff.image_size; - s->tiff.image_buffer = t; - } - - for (i = 0, total_len = 0; total_len < s->tiff.image_size; i++, total_len += len) - { - if ((len = TIFFReadEncodedStrip(s->tiff.tiff_file, i, &s->tiff.image_buffer[total_len], s->tiff.image_size - total_len)) < 0) + case COMPRESSION_T85: + /* Decode the whole image into a buffer */ + /* libtiff probably cannot decompress T.85, so we must handle it ourselves */ + /* Size up and allocate the buffer for the raw data */ + num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); + biggest = TIFFRawStripSize(s->tiff.tiff_file, 0); + for (i = 1; i < num_strips; i++) { - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error.\n", s->tiff.file); - return -1; + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if (len > biggest) + biggest = len; } + if ((raw_data = malloc(biggest)) == NULL) + return -1; + + s->tiff.image_size = s->image_length*((s->image_width + 7)/8); + if (s->tiff.image_size >= s->tiff.image_buffer_size) + { + if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + return -1; + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; + } + + t85_pack.buf = s->tiff.image_buffer; + t85_pack.ptr = 0; + t85_pack.row = 0; + t85_decode_init(&t85, t85_row_write_handler, &t85_pack); + t85_decode_set_comment_handler(&t85, 1000, t85_comment_handler, s); + + total_len = 0; + result = -1; + for (i = 0; i < num_strips; i++, total_len += len) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: ReadRaw error.\n", s->tiff.file); + return -1; + } + result = t85_decode_put(&t85, raw_data, len); + if (result != T4_DECODE_MORE_DATA) + break; + } + if (result == T4_DECODE_MORE_DATA) + result = t85_decode_put(&t85, NULL, 0); + + len = t85_decode_get_compressed_image_size(&t85); + span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->image_length); + t85_decode_release(&t85); + free(raw_data); + break; + default: + /* Decode the whole image into a buffer */ + /* Let libtiff handle the decompression */ + s->tiff.image_size = s->image_length*TIFFScanlineSize(s->tiff.tiff_file); + if (s->tiff.image_size >= s->tiff.image_buffer_size) + { + if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + return -1; + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; + } + + /* Allow for the image being stored in multiple strips, although it is rare to find + a stripped image in a T.4 or T.6 encoded file. */ + num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); + for (i = 0, total_len = 0; i < num_strips; i++, total_len += len) + { + if ((len = TIFFReadEncodedStrip(s->tiff.tiff_file, i, &s->tiff.image_buffer[total_len], s->tiff.image_size - total_len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Read error.\n", s->tiff.file); + return -1; + } + } + /* We might need to flip all the bits, so 1 = black and 0 = white. */ + if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) + { + span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file); + for (i = 0; i < s->tiff.image_size; i++) + s->tiff.image_buffer[i] = ~s->tiff.image_buffer[i]; + s->tiff.photo_metric = PHOTOMETRIC_MINISWHITE; + } + /* We might need to bit reverse each of the bytes of the image. */ + if (s->tiff.fill_order != FILLORDER_LSB2MSB) + bit_reverse(s->tiff.image_buffer, s->tiff.image_buffer, s->tiff.image_size); + break; } - if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) - { - span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file); - for (i = 0; i < s->tiff.image_size; i++) - s->tiff.image_buffer[i] = ~s->tiff.image_buffer[i]; - } - if (s->tiff.fill_order != FILLORDER_LSB2MSB) - bit_reverse(s->tiff.image_buffer, s->tiff.image_buffer, s->tiff.image_size); } s->tiff.row = 0; return s->image_length; @@ -684,15 +785,15 @@ static int set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: return t4_t6_encode_set_row_read_handler(&s->encoder.t4_t6, handler, user_data); + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + return t85_encode_set_row_read_handler(&s->encoder.t85, handler, user_data); case T4_COMPRESSION_T42_T81: return t42_encode_set_row_read_handler(&s->encoder.t42, handler, user_data); #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: return t43_encode_set_row_read_handler(&s->encoder.t43, handler, user_data); #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - return t85_encode_set_row_read_handler(&s->encoder.t85, handler, user_data); } return -1; } @@ -836,61 +937,65 @@ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handl SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding) { - switch (encoding) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: - switch (s->line_encoding) + switch (encoding) { case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - break; - default: - t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->image_width, s->row_handler, s->row_handler_user_data); - t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); - break; - } - s->line_encoding = encoding; - return t4_t6_encode_set_encoding(&s->encoder.t4_t6, encoding); - case T4_COMPRESSION_T42_T81: - switch (s->line_encoding) - { - case T4_COMPRESSION_T42_T81: - break; - default: - t42_encode_init(&s->encoder.t42, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); - break; - } - s->line_encoding = encoding; - return 0; -#if defined(SPANDSP_SUPPORT_T43) - case T4_COMPRESSION_T43: - switch (s->line_encoding) - { - case T4_COMPRESSION_T43: - break; - default: - t43_encode_init(&s->encoder.t43, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); - break; - } - s->line_encoding = encoding; - return 0; -#endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - switch (s->line_encoding) - { + switch (s->line_encoding) + { + case T4_COMPRESSION_T4_1D: + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T6: + break; + default: + t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->image_width, s->row_handler, s->row_handler_user_data); + t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); + break; + } + s->line_encoding = encoding; + if (t4_t6_encode_set_encoding(&s->encoder.t4_t6, encoding)) + return -1; + return s->line_encoding; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: - break; - default: - t85_encode_init(&s->encoder.t85, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); - break; + switch (s->line_encoding) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + break; + default: + t85_encode_init(&s->encoder.t85, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); + break; + } + s->line_encoding = encoding; + return s->line_encoding; + case T4_COMPRESSION_T42_T81: + switch (s->line_encoding) + { + case T4_COMPRESSION_T42_T81: + break; + default: + t42_encode_init(&s->encoder.t42, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); + break; + } + s->line_encoding = encoding; + return s->line_encoding; +#if defined(SPANDSP_SUPPORT_T43) + case T4_COMPRESSION_T43: + switch (s->line_encoding) + { + case T4_COMPRESSION_T43: + break; + default: + t43_encode_init(&s->encoder.t43, s->image_width, s->image_length, s->row_handler, s->row_handler_user_data); + break; + } + s->line_encoding = encoding; + return s->line_encoding; +#endif } - s->line_encoding = encoding; - return 0; } return -1; } @@ -919,6 +1024,10 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) case T4_COMPRESSION_T6: t4_t6_encode_set_image_width(&s->encoder.t4_t6, image_width); break; + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + t85_encode_set_image_width(&s->encoder.t85, image_width); + break; case T4_COMPRESSION_T42_T81: t42_encode_set_image_width(&s->encoder.t42, image_width); break; @@ -927,10 +1036,6 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) t43_encode_set_image_width(&s->encoder.t43, image_width); break; #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - t85_encode_set_image_width(&s->encoder.t85, image_width); - break; } } /*- End of function --------------------------------------------------------*/ @@ -940,6 +1045,10 @@ static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length) s->image_length = image_length; switch (s->line_encoding) { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + t85_encode_set_image_length(&s->encoder.t85, image_length); + break; case T4_COMPRESSION_T42_T81: t42_encode_set_image_length(&s->encoder.t42, image_length); break; @@ -948,10 +1057,6 @@ static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length) t43_encode_set_image_length(&s->encoder.t43, image_length); break; #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - t85_encode_set_image_length(&s->encoder.t85, image_length); - break; } } /*- End of function --------------------------------------------------------*/ @@ -1011,6 +1116,18 @@ SPAN_DECLARE(int) t4_tx_get_x_resolution(t4_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ +SPAN_DECLARE(int) t4_tx_get_resolution(t4_tx_state_t *s) +{ + return s->metadata.resolution_code; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t4_tx_get_image_type(t4_tx_state_t *s) +{ + return s->metadata.image_type; +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(int) t4_tx_get_pages_in_file(t4_tx_state_t *s) { int max; @@ -1056,6 +1173,13 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6)/s->row_squashing_ratio; t->line_image_size = t4_t6_encode_get_compressed_image_size(&s->encoder.t4_t6)/8; break; + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + t->type = T4_IMAGE_TYPE_BILEVEL; + t->width = t85_encode_get_image_width(&s->encoder.t85); + t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio; + t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8; + break; case T4_COMPRESSION_T42_T81: t->type = 0; t->width = t42_encode_get_image_width(&s->encoder.t42); @@ -1070,13 +1194,6 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t t->line_image_size = t43_encode_get_compressed_image_size(&s->encoder.t43)/8; break; #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - t->type = T4_IMAGE_TYPE_BILEVEL; - t->width = t85_encode_get_image_width(&s->encoder.t85); - t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio; - t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8; - break; } } /*- End of function --------------------------------------------------------*/ @@ -1089,15 +1206,15 @@ SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s) case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: return t4_t6_encode_image_complete(&s->encoder.t4_t6); + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + return t85_encode_image_complete(&s->encoder.t85); case T4_COMPRESSION_T42_T81: return t42_encode_image_complete(&s->encoder.t42); #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: return t43_encode_image_complete(&s->encoder.t43); #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - return t85_encode_image_complete(&s->encoder.t85); } return SIG_STATUS_END_OF_DATA; } @@ -1118,15 +1235,15 @@ SPAN_DECLARE(int) t4_tx_get(t4_tx_state_t *s, uint8_t buf[], size_t max_len) case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: return t4_t6_encode_get(&s->encoder.t4_t6, buf, max_len); + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + return t85_encode_get(&s->encoder.t85, buf, max_len); case T4_COMPRESSION_T42_T81: return t42_encode_get(&s->encoder.t42, buf, max_len); #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: return t43_encode_get(&s->encoder.t43, buf, max_len); #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - return t85_encode_get(&s->encoder.t85, buf, max_len); } return 0; } @@ -1157,6 +1274,10 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s) case T4_COMPRESSION_T6: t4_t6_encode_restart(&s->encoder.t4_t6, s->image_width); break; + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + t85_encode_restart(&s->encoder.t85, s->image_width, s->image_length); + break; case T4_COMPRESSION_T42_T81: t42_encode_restart(&s->encoder.t42, s->image_width, s->image_length); break; @@ -1165,10 +1286,6 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s) t43_encode_restart(&s->encoder.t43, s->image_width, s->image_length); break; #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - t85_encode_restart(&s->encoder.t85, s->image_width, s->image_length); - break; } /* If there is a page header, create that first */ if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0) @@ -1279,15 +1396,15 @@ SPAN_DECLARE(int) t4_tx_release(t4_tx_state_t *s) case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: return t4_t6_encode_release(&s->encoder.t4_t6); + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + return t85_encode_release(&s->encoder.t85); case T4_COMPRESSION_T42_T81: return t42_encode_release(&s->encoder.t42); #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: return t43_encode_release(&s->encoder.t43); #endif - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - return t85_encode_release(&s->encoder.t85); } return -1; }