From 831e5dd4d9a85feb5f8810654cc0fecf9dccbe7d Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Fri, 14 Sep 2012 00:24:19 +0800 Subject: [PATCH] Add image squashing feature to the FAX engine so a superfine image or fine image can be sent to a machine which has those resolutions inhibited. --- libs/spandsp/configure.ac | 2 +- libs/spandsp/src/spandsp/private/t4_tx.h | 7 +- libs/spandsp/src/spandsp/t4_tx.h | 6 ++ libs/spandsp/src/t30.c | 91 +++++++++++++----------- libs/spandsp/src/t4_tx.c | 54 ++++++++++---- libs/spandsp/tests/t4_tests.c | 4 +- 6 files changed, 105 insertions(+), 59 deletions(-) diff --git a/libs/spandsp/configure.ac b/libs/spandsp/configure.ac index 22fccd6d25..9928098765 100644 --- a/libs/spandsp/configure.ac +++ b/libs/spandsp/configure.ac @@ -534,7 +534,7 @@ fi if test "$ac_cv_header_tif_dir_h" = "yes" ; then AC_DEFINE([SPANDSP_SUPPORT_TIFF_FX], [1], [Support TIFF/FX in TIFF file handling]) - SPANDSP_SUPPORT_TIFF_FX="#define SPANDSP_SUPPORT_TIFF_FX" + SPANDSP_SUPPORT_TIFF_FX="#define SPANDSP_SUPPORT_TIFF_FX 1" else SPANDSP_SUPPORT_TIFF_FX="#undef SPANDSP_SUPPORT_TIFF_FX" fi diff --git a/libs/spandsp/src/spandsp/private/t4_tx.h b/libs/spandsp/src/spandsp/private/t4_tx.h index dee5f84b92..078ac97b8a 100644 --- a/libs/spandsp/src/spandsp/private/t4_tx.h +++ b/libs/spandsp/src/spandsp/private/t4_tx.h @@ -38,7 +38,7 @@ typedef struct /*! \brief The compression type used in the TIFF file */ uint16_t compression; - /*! \brief Image type - bilevel, gray, colour */ + /*! \brief Image type - bi-level, gray, colour, etc. */ int image_type; /*! \brief The TIFF photometric setting for the current page. */ uint16_t photo_metric; @@ -96,6 +96,11 @@ struct t4_tx_state_s int line_encoding_gray; int line_encoding_colour; + /*! \brief When superfine and fine resolution images need to be squahed vertically + to a lower resolution, this value sets the number of source rows which + must be squashed to form each row on the wire. */ + int row_squashing_ratio; + /*! \brief The width of the current page, in pixels. */ uint32_t image_width; /*! \brief The length of the current page, in pixels. */ diff --git a/libs/spandsp/src/spandsp/t4_tx.h b/libs/spandsp/src/spandsp/t4_tx.h index 3eccc679c8..3bf942ca88 100644 --- a/libs/spandsp/src/spandsp/t4_tx.h +++ b/libs/spandsp/src/spandsp/t4_tx.h @@ -342,6 +342,12 @@ SPAN_DECLARE(void) t4_tx_set_header_overlays_image(t4_tx_state_t *s, int header_ \return 0 for success, otherwise -1. */ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, void *user_data); +/*! \brief Set the row squashing ratio, for adjusting row-to-row (y) resolution of bi-level + images for a T.4 transmit context. + \param s The T.4 transmit context. + \param row_squashing_ratio Vertical squashing ratio. */ +SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio); + /*! \brief Get the row-to-row (y) resolution of the current page. \param s The T.4 context. \return The resolution, in pixels per metre. */ diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index e22f60b01f..a12d357f76 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -1308,6 +1308,7 @@ static int build_dcs(t30_state_t *s) { int i; int bad; + int row_squashing_ratio; /* Make a DCS frame based on local issues and the latest received DIS/DTC frame. Negotiate the result based on what both parties can do. */ @@ -1369,6 +1370,7 @@ static int build_dcs(t30_state_t *s) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT); /* Set the Y resolution bits */ bad = T30_ERR_OK; + row_squashing_ratio = 1; switch (s->y_resolution) { case T4_Y_RESOLUTION_1200: @@ -1425,27 +1427,6 @@ static int build_dcs(t30_state_t *s) break; } break; - case T4_Y_RESOLUTION_SUPERFINE: - if (!(s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION)) - { - bad = T30_ERR_NORESSUPPORT; - } - else - { - switch (s->x_resolution) - { - case T4_X_RESOLUTION_R8: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); - break; - case T4_X_RESOLUTION_R16: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); - break; - default: - bad = T30_ERR_NORESSUPPORT; - break; - } - } - break; case T4_Y_RESOLUTION_300: switch (s->x_resolution) { @@ -1460,12 +1441,27 @@ static int build_dcs(t30_state_t *s) break; } break; - case T4_Y_RESOLUTION_FINE: - if (!(s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION)) + case T4_Y_RESOLUTION_SUPERFINE: + if ((s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION)) { - bad = T30_ERR_NORESSUPPORT; + switch (s->x_resolution) + { + case T4_X_RESOLUTION_R8: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); + break; + case T4_X_RESOLUTION_R16: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); + break; + default: + bad = T30_ERR_NORESSUPPORT; + break; + } + break; } - else + row_squashing_ratio <<= 1; + /* Fall through */ + case T4_Y_RESOLUTION_FINE: + if ((s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION)) { switch (s->x_resolution) { @@ -1476,8 +1472,10 @@ static int build_dcs(t30_state_t *s) bad = T30_ERR_NORESSUPPORT; break; } + break; } - break; + row_squashing_ratio <<= 1; + /* Fall through */ default: case T4_Y_RESOLUTION_STANDARD: switch (s->x_resolution) @@ -1491,6 +1489,7 @@ static int build_dcs(t30_state_t *s) } break; } + t4_tx_set_row_squashing_ratio(&s->t4.tx, row_squashing_ratio); if (bad != T30_ERR_OK) { t30_set_status(s, bad); @@ -1915,23 +1914,21 @@ static int set_min_scan_time_code(t30_state_t *s) switch (s->y_resolution) { case T4_Y_RESOLUTION_SUPERFINE: - if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE)) + if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE)) { - t30_set_status(s, T30_ERR_NORESSUPPORT); - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution.\n"); - return -1; + s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; + break; } - s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; - break; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n"); + /* Fall through */ case T4_Y_RESOLUTION_FINE: - if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE)) + if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE)) { - t30_set_status(s, T30_ERR_NORESSUPPORT); - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution.\n"); - return -1; + s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; + break; } - s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; - break; + span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n"); + /* Fall through */ default: case T4_Y_RESOLUTION_STANDARD: s->min_scan_time_code = translate_min_scan_time[0][min_bits_field]; @@ -2122,7 +2119,7 @@ static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) } } } - span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding); + span_log(&s->logging, SPAN_LOG_FLOW, "Choose compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding); switch (s->far_dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3)) { case (DISBIT6 | DISBIT4 | DISBIT3): @@ -2410,7 +2407,7 @@ static int process_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) { s->line_encoding = T4_COMPRESSION_ITU_T4_1D; } - span_log(&s->logging, SPAN_LOG_FLOW, "Selected compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding); + span_log(&s->logging, SPAN_LOG_FLOW, "Far end selected compression %s (%d)\n", t4_encoding_to_str(s->line_encoding), s->line_encoding); if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT)) span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Remote is not requesting receive in DCS\n"); @@ -3389,12 +3386,18 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg, send_simple_frame(s, T30_RTP); break; case T30_COPY_QUALITY_BAD: +#if 0 + /* Some people want to keep even the bad pages */ + if (s->keep_bad_pages) + rx_end_page(s); +#endif if (s->phase_d_handler) s->phase_d_handler(s, s->phase_d_user_data, fcf); set_state(s, T30_STATE_III_Q_RTN); send_simple_frame(s, T30_RTN); break; } + break; case T30_DCN: t30_set_status(s, T30_ERR_RX_DCNFAX); @@ -6306,9 +6309,13 @@ SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s, /* Default to the basic modems. */ s->supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17; s->supported_compressions = T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION; - s->supported_resolutions = T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION + s->supported_resolutions = T30_SUPPORT_STANDARD_RESOLUTION + | T30_SUPPORT_FINE_RESOLUTION + | T30_SUPPORT_SUPERFINE_RESOLUTION | T30_SUPPORT_R8_RESOLUTION; - s->supported_image_sizes = T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH + s->supported_image_sizes = T30_SUPPORT_US_LETTER_LENGTH + | T30_SUPPORT_US_LEGAL_LENGTH + | T30_SUPPORT_UNLIMITED_LENGTH | T30_SUPPORT_215MM_WIDTH; /* Set the output encoding to something safe. Most things get 1D and 2D encoding right. Quite a lot get other things wrong. */ diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 6aeef44d22..2e2f25c706 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -484,12 +484,23 @@ static int open_tiff_input_file(t4_tx_state_t *s, const char *file) static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; + int i; + int j; s = (t4_tx_state_t *) user_data; if (s->tiff.row >= s->image_length) return 0; memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); s->tiff.row++; + + /* If this is a bi-level image which has more vertical resolution than the + far end will accept, we need to squash it down to size. */ + for (i = 1; i < s->row_squashing_ratio && s->tiff.row < s->image_length; i++) + { + for (j = 0; j < s->image_width/8; j++) + buf[j] |= s->tiff.image_buffer[s->tiff.row*len + j]; + s->tiff.row++; + } return len; } /*- End of function --------------------------------------------------------*/ @@ -497,7 +508,7 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) static int row_read(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; - + s = (t4_tx_state_t *) user_data; if (s->tiff.raw_row >= s->tiff.image_length) @@ -650,7 +661,7 @@ static int make_header(t4_tx_state_t *s) if ((s->header_text = malloc(132 + 1)) == NULL) return -1; } - /* This is very English oriented, but then most FAX machines are, too. Some + /* This is very English oriented, but then most FAX machines are. Some measure of i18n in the time and date, and even the header_info string, is entirely possible, although the font area would need some serious work to properly deal with East Asian script. There is no spec for what the header @@ -711,6 +722,7 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) repeats = 1; break; } + repeats /= s->row_squashing_ratio; if (s->header_overlays_image) { /* Read and dump a row of the real image, allowing for the possibility @@ -721,23 +733,37 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) return len; } } - row = s->header_row/repeats; - pos = 0; - for (t = s->header_text; *t && pos <= len - 2; t++) + switch (s->tiff.image_type) { - pattern = header_font[(uint8_t) *t][row]; - buf[pos++] = (uint8_t) (pattern >> 8); - buf[pos++] = (uint8_t) (pattern & 0xFF); + case T4_IMAGE_TYPE_BILEVEL: + row = s->header_row/repeats; + pos = 0; + for (t = s->header_text; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + buf[pos++] = (uint8_t) (pattern >> 8); + buf[pos++] = (uint8_t) (pattern & 0xFF); + } + while (pos < len) + buf[pos++] = 0; + s->header_row++; + if (s->header_row >= 16*repeats) + { + /* End of header. Change to normal image row data. */ + set_row_read_handler(s, s->row_handler, s->row_handler_user_data); + } + break; } - while (pos < len) - buf[pos++] = 0; - s->header_row++; - if (s->header_row >= 16*repeats) - set_row_read_handler(s, s->row_handler, s->row_handler_user_data); return len; } /*- End of function --------------------------------------------------------*/ +SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio) +{ + s->row_squashing_ratio = row_squashing_ratio; +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(int) t4_tx_next_page_has_different_format(t4_tx_state_t *s) { span_log(&s->logging, SPAN_LOG_FLOW, "Checking for the existence of page %d\n", s->current_page + 1); @@ -1143,6 +1169,8 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int s->row_handler = tiff_row_read_handler; s->row_handler_user_data = (void *) s; + s->row_squashing_ratio = 1; + if (file) { if (open_tiff_input_file(s, file) < 0) diff --git a/libs/spandsp/tests/t4_tests.c b/libs/spandsp/tests/t4_tests.c index db9581aaee..b159ad81a8 100644 --- a/libs/spandsp/tests/t4_tests.c +++ b/libs/spandsp/tests/t4_tests.c @@ -575,7 +575,7 @@ int main(int argc, char *argv[]) if (compression < 0 || (block_size == 0 && compression_step >= 3)) break; } - t4_tx_set_tx_encoding(&send_state, compression, T4_COMPRESSION_NONE); + t4_tx_set_tx_encoding(&send_state, compression); t4_rx_set_rx_encoding(&receive_state, compression); rows_read = 0; @@ -716,7 +716,7 @@ int main(int argc, char *argv[]) compression = compression_sequence[compression_step++]; } } - t4_tx_set_tx_encoding(&send_state, compression, T4_COMPRESSION_NONE); + t4_tx_set_tx_encoding(&send_state, compression); t4_rx_set_rx_encoding(&receive_state, compression); if (t4_tx_start_page(&send_state))