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.
This commit is contained in:
Steve Underwood 2012-09-14 00:24:19 +08:00
parent 83d7f4282c
commit 831e5dd4d9
6 changed files with 105 additions and 59 deletions

View File

@ -534,7 +534,7 @@ fi
if test "$ac_cv_header_tif_dir_h" = "yes" ; then if test "$ac_cv_header_tif_dir_h" = "yes" ; then
AC_DEFINE([SPANDSP_SUPPORT_TIFF_FX], [1], [Support TIFF/FX in TIFF file handling]) 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 else
SPANDSP_SUPPORT_TIFF_FX="#undef SPANDSP_SUPPORT_TIFF_FX" SPANDSP_SUPPORT_TIFF_FX="#undef SPANDSP_SUPPORT_TIFF_FX"
fi fi

View File

@ -38,7 +38,7 @@ typedef struct
/*! \brief The compression type used in the TIFF file */ /*! \brief The compression type used in the TIFF file */
uint16_t compression; uint16_t compression;
/*! \brief Image type - bilevel, gray, colour */ /*! \brief Image type - bi-level, gray, colour, etc. */
int image_type; int image_type;
/*! \brief The TIFF photometric setting for the current page. */ /*! \brief The TIFF photometric setting for the current page. */
uint16_t photo_metric; uint16_t photo_metric;
@ -96,6 +96,11 @@ struct t4_tx_state_s
int line_encoding_gray; int line_encoding_gray;
int line_encoding_colour; 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. */ /*! \brief The width of the current page, in pixels. */
uint32_t image_width; uint32_t image_width;
/*! \brief The length of the current page, in pixels. */ /*! \brief The length of the current page, in pixels. */

View File

@ -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. */ \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); 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. /*! \brief Get the row-to-row (y) resolution of the current page.
\param s The T.4 context. \param s The T.4 context.
\return The resolution, in pixels per metre. */ \return The resolution, in pixels per metre. */

View File

@ -1308,6 +1308,7 @@ static int build_dcs(t30_state_t *s)
{ {
int i; int i;
int bad; int bad;
int row_squashing_ratio;
/* Make a DCS frame based on local issues and the latest received DIS/DTC frame. Negotiate /* 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. */ 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_ctrl_bit(s->dcs_frame, T30_DCS_BIT_RECEIVE_FAX_DOCUMENT);
/* Set the Y resolution bits */ /* Set the Y resolution bits */
bad = T30_ERR_OK; bad = T30_ERR_OK;
row_squashing_ratio = 1;
switch (s->y_resolution) switch (s->y_resolution)
{ {
case T4_Y_RESOLUTION_1200: case T4_Y_RESOLUTION_1200:
@ -1425,27 +1427,6 @@ static int build_dcs(t30_state_t *s)
break; break;
} }
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: case T4_Y_RESOLUTION_300:
switch (s->x_resolution) switch (s->x_resolution)
{ {
@ -1460,12 +1441,27 @@ static int build_dcs(t30_state_t *s)
break; break;
} }
break; break;
case T4_Y_RESOLUTION_FINE: case T4_Y_RESOLUTION_SUPERFINE:
if (!(s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION)) 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) switch (s->x_resolution)
{ {
@ -1476,8 +1472,10 @@ static int build_dcs(t30_state_t *s)
bad = T30_ERR_NORESSUPPORT; bad = T30_ERR_NORESSUPPORT;
break; break;
} }
break;
} }
break; row_squashing_ratio <<= 1;
/* Fall through */
default: default:
case T4_Y_RESOLUTION_STANDARD: case T4_Y_RESOLUTION_STANDARD:
switch (s->x_resolution) switch (s->x_resolution)
@ -1491,6 +1489,7 @@ static int build_dcs(t30_state_t *s)
} }
break; break;
} }
t4_tx_set_row_squashing_ratio(&s->t4.tx, row_squashing_ratio);
if (bad != T30_ERR_OK) if (bad != T30_ERR_OK)
{ {
t30_set_status(s, bad); t30_set_status(s, bad);
@ -1915,23 +1914,21 @@ static int set_min_scan_time_code(t30_state_t *s)
switch (s->y_resolution) switch (s->y_resolution)
{ {
case T4_Y_RESOLUTION_SUPERFINE: 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); 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];
span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution.\n"); break;
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]; span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n");
break; /* Fall through */
case T4_Y_RESOLUTION_FINE: 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); s->min_scan_time_code = translate_min_scan_time[1][min_bits_field];
span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution.\n"); break;
return -1;
} }
s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n");
break; /* Fall through */
default: default:
case T4_Y_RESOLUTION_STANDARD: case T4_Y_RESOLUTION_STANDARD:
s->min_scan_time_code = translate_min_scan_time[0][min_bits_field]; 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)) switch (s->far_dis_dtc_frame[4] & (DISBIT6 | DISBIT5 | DISBIT4 | DISBIT3))
{ {
case (DISBIT6 | 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; 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)) 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"); 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); send_simple_frame(s, T30_RTP);
break; break;
case T30_COPY_QUALITY_BAD: 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) if (s->phase_d_handler)
s->phase_d_handler(s, s->phase_d_user_data, fcf); s->phase_d_handler(s, s->phase_d_user_data, fcf);
set_state(s, T30_STATE_III_Q_RTN); set_state(s, T30_STATE_III_Q_RTN);
send_simple_frame(s, T30_RTN); send_simple_frame(s, T30_RTN);
break; break;
} }
break; break;
case T30_DCN: case T30_DCN:
t30_set_status(s, T30_ERR_RX_DCNFAX); 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. */ /* Default to the basic modems. */
s->supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17; 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_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; | 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; | T30_SUPPORT_215MM_WIDTH;
/* Set the output encoding to something safe. Most things get 1D and 2D /* Set the output encoding to something safe. Most things get 1D and 2D
encoding right. Quite a lot get other things wrong. */ encoding right. Quite a lot get other things wrong. */

View File

@ -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) static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len)
{ {
t4_tx_state_t *s; t4_tx_state_t *s;
int i;
int j;
s = (t4_tx_state_t *) user_data; s = (t4_tx_state_t *) user_data;
if (s->tiff.row >= s->image_length) if (s->tiff.row >= s->image_length)
return 0; return 0;
memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len);
s->tiff.row++; 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; return len;
} }
/*- End of function --------------------------------------------------------*/ /*- 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) static int row_read(void *user_data, uint8_t buf[], size_t len)
{ {
t4_tx_state_t *s; t4_tx_state_t *s;
s = (t4_tx_state_t *) user_data; s = (t4_tx_state_t *) user_data;
if (s->tiff.raw_row >= s->tiff.image_length) 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) if ((s->header_text = malloc(132 + 1)) == NULL)
return -1; 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 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 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 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; repeats = 1;
break; break;
} }
repeats /= s->row_squashing_ratio;
if (s->header_overlays_image) if (s->header_overlays_image)
{ {
/* Read and dump a row of the real image, allowing for the possibility /* 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; return len;
} }
} }
row = s->header_row/repeats; switch (s->tiff.image_type)
pos = 0;
for (t = s->header_text; *t && pos <= len - 2; t++)
{ {
pattern = header_font[(uint8_t) *t][row]; case T4_IMAGE_TYPE_BILEVEL:
buf[pos++] = (uint8_t) (pattern >> 8); row = s->header_row/repeats;
buf[pos++] = (uint8_t) (pattern & 0xFF); 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; return len;
} }
/*- End of function --------------------------------------------------------*/ /*- 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_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); 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 = tiff_row_read_handler;
s->row_handler_user_data = (void *) s; s->row_handler_user_data = (void *) s;
s->row_squashing_ratio = 1;
if (file) if (file)
{ {
if (open_tiff_input_file(s, file) < 0) if (open_tiff_input_file(s, file) < 0)

View File

@ -575,7 +575,7 @@ int main(int argc, char *argv[])
if (compression < 0 || (block_size == 0 && compression_step >= 3)) if (compression < 0 || (block_size == 0 && compression_step >= 3))
break; 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); t4_rx_set_rx_encoding(&receive_state, compression);
rows_read = 0; rows_read = 0;
@ -716,7 +716,7 @@ int main(int argc, char *argv[])
compression = compression_sequence[compression_step++]; 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); t4_rx_set_rx_encoding(&receive_state, compression);
if (t4_tx_start_page(&send_state)) if (t4_tx_start_page(&send_state))