From cdac39a6e4eae9e10eff9767c1beac2147d696c4 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Fri, 5 Apr 2013 00:29:32 +0800 Subject: [PATCH] Addition of T.85 compression to the TIFF file. --- libs/spandsp/src/spandsp/t30.h | 12 +++- libs/spandsp/src/t30.c | 42 ++++++++++--- libs/spandsp/src/t30_api.c | 30 +++++++++- libs/spandsp/src/t4_rx.c | 104 ++++++++++++++++++++++++++++++++- libs/spandsp/src/t4_tx.c | 28 ++++++--- 5 files changed, 194 insertions(+), 22 deletions(-) diff --git a/libs/spandsp/src/spandsp/t30.h b/libs/spandsp/src/spandsp/t30.h index 7970051f73..dabedaa6e8 100644 --- a/libs/spandsp/src/spandsp/t30.h +++ b/libs/spandsp/src/spandsp/t30.h @@ -374,10 +374,20 @@ enum T30_SUPPORT_COMPRESSION_SYCC_T81 = 0x200, /*! T.88 monochrome JBIG2 compression */ T30_SUPPORT_COMPRESSION_T88 = 0x400, + /*! Gray-scale support by multi-level codecs */ + T30_SUPPORT_COMPRESSION_GRAYSCALE = 0x1000000, + /*! Colour support by multi-level codecs */ + T30_SUPPORT_COMPRESSION_COLOUR = 0x2000000, + /*! 12 bit mode for gray scale and colour */ + T30_SUPPORT_COMPRESSION_12BIT = 0x4000000, + /*! Convert a colour image to a gray-scale one */ + T30_SUPPORT_COMPRESSION_COLOUR_TO_GRAY = 0x8000000, /*! Dither a gray scale image down a simple bilevel image, with rescaling to fit a FAX page */ T30_SUPPORT_GRAY_TO_BILEVEL = 0x10000000, /*! Dither a colour image down a simple bilevel image, with rescaling to fit a FAX page */ - T30_SUPPORT_COLOUR_TO_BILEVEL = 0x20000000 + T30_SUPPORT_COLOUR_TO_BILEVEL = 0x20000000, + /*! Rescale an image (except a bi-level image) to fit a permitted FAX width when necessary */ + T30_SUPPORT_COMPRESSION_RESCALING = 0x40000000 }; enum diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index c8a16c3d96..19b8868cce 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -1202,6 +1202,7 @@ int t30_build_dis_or_dtc(t30_state_t *s) { /* ECM allowed */ set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_ECM_CAPABLE); + /* Only offer the option of fancy compression schemes, if we are also offering the ECM option needed to support them. */ if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T6)) @@ -1215,6 +1216,9 @@ int t30_build_dis_or_dtc(t30_state_t *s) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE); } + if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_COLOUR)) + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FULL_COLOUR_CAPABLE); + if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T42_T81)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T81_CAPABLE); if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T43)) @@ -1232,6 +1236,15 @@ int t30_build_dis_or_dtc(t30_state_t *s) } //if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T89)) // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T89_CAPABLE); + + if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_12BIT)) + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE); + + //if ((s->supported_compressions & 30_SUPPORT_COMPRESSION_NO_SUBSAMPLING)) + // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING); + + /* No custom illuminant */ + /* No custom gamut range */ } if ((s->supported_t30_features & T30_SUPPORT_FIELD_NOT_VALID)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FNV_CAPABLE); @@ -1262,12 +1275,6 @@ int t30_build_dis_or_dtc(t30_state_t *s) /* No mode 26 (T.505) */ /* No digital network capability */ /* No duplex operation */ - /* No JPEG */ - /* No full colour */ - /* No 12bits/pel */ - /* No sub-sampling (1:1:1) */ - /* No custom illuminant */ - /* No custom gamut range */ if ((s->supported_image_sizes & T30_SUPPORT_US_LETTER_LENGTH)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LETTER_CAPABLE); if ((s->supported_image_sizes & T30_SUPPORT_US_LEGAL_LENGTH)) @@ -2329,7 +2336,6 @@ static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) return -1; } } - queue_phase(s, T30_PHASE_B_TX); /* Try to send something */ if (s->tx_file[0]) @@ -2439,9 +2445,26 @@ static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) s->y_resolution = -1; //s->current_page_resolution = 0; x = -1; - if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE)) + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE)) { /* Gray scale or colour image */ + + /* Note 35 of Table 2/T.30 */ + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE)) + { + /* We are going to work in full colour mode */ + } + + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_12BIT_COMPONENT)) + { + /* We are going to work in 12 bit mode */ + } + + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING)) + { + //???? = T30_SUPPORT_COMPRESSION_T42_T81_SUBSAMPLING; + } + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200)) { if ((s->supported_colour_resolutions & T30_SUPPORT_RESOLUTION_1200_1200)) @@ -2664,7 +2687,7 @@ static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) /* Check which compression the far end has decided to use. */ #if defined(SPANDSP_SUPPORT_T42) - if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE)) + if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE)) { s->line_encoding = T4_COMPRESSION_T42_T81; } @@ -3905,6 +3928,7 @@ static void process_state_r(t30_state_t *s, const uint8_t *msg, int len) process_rx_dcs(s, msg, len); break; case T30_DCN: + /* Received a DCN while waiting for a DIS or DCN */ t30_set_status(s, T30_ERR_RX_DCNWHY); disconnect(s); break; diff --git a/libs/spandsp/src/t30_api.c b/libs/spandsp/src/t30_api.c index e5326d926e..3a60ac80dc 100644 --- a/libs/spandsp/src/t30_api.c +++ b/libs/spandsp/src/t30_api.c @@ -697,12 +697,12 @@ SPAN_DECLARE(int) t30_set_supported_compressions(t30_state_t *s, int supported_c supported_compressions &= T30_SUPPORT_COMPRESSION_T4_1D | T30_SUPPORT_COMPRESSION_T4_2D | T30_SUPPORT_COMPRESSION_T6 + | T30_SUPPORT_COMPRESSION_T85 + | T30_SUPPORT_COMPRESSION_T85_L0 //| T30_SUPPORT_COMPRESSION_T81 #if defined(SPANDSP_SUPPORT_T43) | T30_SUPPORT_COMPRESSION_T43 #endif - | T30_SUPPORT_COMPRESSION_T85 - | T30_SUPPORT_COMPRESSION_T85_L0 | 0; s->supported_compressions = supported_compressions; t30_build_dis_or_dtc(s); @@ -712,6 +712,23 @@ SPAN_DECLARE(int) t30_set_supported_compressions(t30_state_t *s, int supported_c SPAN_DECLARE(int) t30_set_supported_bilevel_resolutions(t30_state_t *s, int supported_resolutions) { + supported_resolutions &= T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200; + /* Make sure anything needed for colour is enabled as a bi-level image, as that is a + rule from T.30. 100x100 is an exception, as it doesn't exist as a bi-level resolution. */ + supported_resolutions |= (s->supported_colour_resolutions & ~T4_RESOLUTION_100_100); s->supported_bilevel_resolutions = supported_resolutions; t30_build_dis_or_dtc(s); return 0; @@ -720,7 +737,16 @@ SPAN_DECLARE(int) t30_set_supported_bilevel_resolutions(t30_state_t *s, int supp SPAN_DECLARE(int) t30_set_supported_colour_resolutions(t30_state_t *s, int supported_resolutions) { + supported_resolutions &= T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200; s->supported_colour_resolutions = supported_resolutions; + /* Make sure anything needed for colour is enabled as a bi-level image, as that is a + rule from T.30. 100x100 is an exception, as it doesn't exist as a bi-level resolution. */ + s->supported_bilevel_resolutions |= (s->supported_colour_resolutions & ~T4_RESOLUTION_100_100); t30_build_dis_or_dtc(s); return 0; } diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 4e0e37a19b..3c9a5f5e68 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -81,6 +81,12 @@ /*! The number of centimetres in one inch */ #define CM_PER_INCH 2.54f +typedef struct +{ + uint8_t *buf; + int ptr; +} packer_t; + #if defined(SPANDSP_SUPPORT_TIFF_FX) extern TIFFFieldArray tiff_fx_field_array; #endif @@ -336,9 +342,30 @@ static int open_tiff_output_file(t4_rx_state_t *s, const char *file) } /*- End of function --------------------------------------------------------*/ +static int row_read_handler(void *user_data, uint8_t row[], size_t len) +{ + packer_t *s; + + s = (packer_t *) user_data; + memcpy(row, &s->buf[s->ptr], len); + s->ptr += len; + return len; +} +/*- End of function --------------------------------------------------------*/ + static int write_tiff_image(t4_rx_state_t *s) { t4_rx_tiff_state_t *t; + uint8_t *buf; + uint8_t *buf2; + int buf_len; + int len; + int len2; + t85_encode_state_t t85; +#if defined(SPANDSP_SUPPORT_T43) + t43_encode_state_t t43; +#endif + packer_t packer; #if defined(SPANDSP_SUPPORT_TIFF_FX) uint64_t offset; #endif @@ -353,8 +380,79 @@ static int write_tiff_image(t4_rx_state_t *s) if (!TIFFCheckpointDirectory(t->tiff_file)) span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page); /* ...and write out the image... */ - if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + switch (t->output_encoding) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.85 compression.\n", t->file); + buf_len = 0; + buf = NULL; + packer.buf = t->image_buffer; + packer.ptr = 0; + t85_encode_init(&t85, s->image_width, s->image_length, row_read_handler, &packer); + //if (t->output_encoding == T4_COMPRESSION_T85_L0) + // t85_encode_set_options(&t85, 256, -1, -1); + len2 = 0; + do + { + if (buf_len < len2 + 50000) + { + buf_len += 50000; + if ((buf2 = realloc(buf, buf_len)) == NULL) + { + if (buf) + free(buf); + return -1; + } + buf = buf2; + } + len = t85_encode_get(&t85, &buf[len2], 50000); + len2 += len; + } + while (len > 0); + if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + t85_encode_release(&t85); + free(buf); + break; +#if defined(SPANDSP_SUPPORT_T43) + case T4_COMPRESSION_T43: + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.43 compression.\n", t->file); + buf_len = 0; + buf = NULL; + packer.buf = t->image_buffer; + packer.ptr = 0; + t43_encode_init(&t43, s->image_width, s->image_length, row_read_handler, &packer); + len2 = 0; + do + { + if (buf_len < len2 + 50000) + { + buf_len += 50000; + if ((buf2 = realloc(buf, buf_len)) == NULL) + { + if (buf) + free(buf); + return -1; + } + buf = buf2; + } + len = t43_encode_get(&t43, &buf[len2], 50000); + len2 += len; + } + while (len > 0); + if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + t43_encode_release(&t43); + free(buf); + break; +#endif + default: + /* Let libtiff do the compression */ + if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + break; + } /* ...then finalise the directory entry, and libtiff is happy. */ if (!TIFFWriteDirectory(t->tiff_file)) span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", t->file, s->current_page); @@ -365,7 +463,9 @@ static int write_tiff_image(t4_rx_state_t *s) { TIFFSetField(t->tiff_file, TIFFTAG_FAXPROFILE, PROFILETYPE_G3_FAX); TIFFSetField(t->tiff_file, TIFFTAG_PROFILETYPE, FAXPROFILE_F); + TIFFSetField(t->tiff_file, TIFFTAG_CODINGMETHODS, CODINGMETHODS_T4_1D | CODINGMETHODS_T4_2D | CODINGMETHODS_T6); TIFFSetField(t->tiff_file, TIFFTAG_VERSIONYEAR, "1998"); + TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 3); offset = 0; if (!TIFFWriteCustomDirectory(t->tiff_file, &offset)) diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 3bb2a03932..a308f53435 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -86,10 +86,19 @@ /*! The number of centimetres in one inch */ #define CM_PER_INCH 2.54f +typedef struct +{ + uint8_t *buf; + int ptr; + int row; + int bit_mask; +} t85_packer_t; + static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length); #if defined(SPANDSP_SUPPORT_TIFF_FX) /* TIFF-FX related extensions to the tag set supported by libtiff */ + static const TIFFFieldInfo tiff_fx_tiff_field_info[] = { {TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, FIELD_CUSTOM, FALSE, FALSE, (char *) "Indexed"}, @@ -106,6 +115,7 @@ static const TIFFFieldInfo tiff_fx_tiff_field_info[] = {TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "ImageLayer"}, }; +#if 1 static TIFFField tiff_fx_tiff_fields[] = { { TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "Indexed" }, @@ -123,6 +133,7 @@ static TIFFField tiff_fx_tiff_fields[] = }; TIFFFieldArray tiff_fx_field_array = { tfiatOther, 0, 12, tiff_fx_tiff_fields }; +#endif static TIFFExtendProc _ParentExtender = NULL; @@ -177,12 +188,13 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) return -1; /* TODO: This only allows for 8 bit deep maps */ - if ((s->colour_map = realloc(s->colour_map, 3*256)) == NULL) - return -1; span_log(&s->logging, SPAN_LOG_FLOW, "Got a colour map\n"); + s->colour_map_entries = 1 << bits_per_sample; + if ((s->colour_map = realloc(s->colour_map, 3*s->colour_map_entries)) == NULL) + return -1; #if 0 /* Sweep the colormap in the proper order */ - for (i = 0; i < (1 << bits_per_sample); i++) + for (i = 0; i < s->colour_map_entries; i++) { s->colour_map[3*i + 0] = (map_L[i] >> 8) & 0xFF; s->colour_map[3*i + 1] = (map_a[i] >> 8) & 0xFF; @@ -191,15 +203,15 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) } #else /* Sweep the colormap in the order that seems to work for l04x_02x.tif */ - for (i = 0; i < (1 << bits_per_sample); i++) + for (i = 0; i < s->colour_map_entries; i++) { - s->colour_map[0*256 + i] = (map_L[i] >> 8) & 0xFF; - s->colour_map[1*256 + i] = (map_a[i] >> 8) & 0xFF; - s->colour_map[2*256 + i] = (map_b[i] >> 8) & 0xFF; + s->colour_map[0*s->colour_map_entries + i] = (map_L[i] >> 8) & 0xFF; + s->colour_map[1*s->colour_map_entries + i] = (map_a[i] >> 8) & 0xFF; + s->colour_map[2*s->colour_map_entries + i] = (map_b[i] >> 8) & 0xFF; } #endif lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, 256); - for (i = 0; i < (1 << bits_per_sample); i++) + for (i = 0; i < s->colour_map_entries; i++) span_log(&s->logging, SPAN_LOG_FLOW, "Map %3d - %5d %5d %5d\n", i, s->colour_map[3*i], s->colour_map[3*i + 1], s->colour_map[3*i + 2]); return 0; }