]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Add image squashing feature to the FAX engine so a superfine image or fine
authorSteve Underwood <steveu@x100e.coppice.org>
Thu, 13 Sep 2012 16:24:19 +0000 (00:24 +0800)
committerSteve Underwood <steveu@x100e.coppice.org>
Thu, 13 Sep 2012 16:24:19 +0000 (00:24 +0800)
image can be sent to a machine which has those resolutions inhibited.

libs/spandsp/configure.ac
libs/spandsp/src/spandsp/private/t4_tx.h
libs/spandsp/src/spandsp/t4_tx.h
libs/spandsp/src/t30.c
libs/spandsp/src/t4_tx.c
libs/spandsp/tests/t4_tests.c

index 22fccd6d25a98c0d2cfe4426a8f24f805e259d01..9928098765d767ea8c468a86927267576ee436a6 100644 (file)
@@ -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
index dee5f84b92215db364ba40d565b14b1b2b05c237..078ac97b8ad59a2307628f61462c68376dda3a7b 100644 (file)
@@ -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. */
index 3eccc679c8a0e52d39b9eb132d9d9ef39ef3a6a4..3bf942ca880c7c7cbc66eba868511c8fab27c4bb 100644 (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. */
 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. */
index e22f60b01ffee2866dcd59639065fab3fcfb7c8d..a12d357f76c58b343cc71bfa4f732add11d32508 100644 (file)
@@ -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,12 +1427,22 @@ static int build_dcs(t30_state_t *s)
             break;
         }
         break;
-    case T4_Y_RESOLUTION_SUPERFINE:
-        if (!(s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION))
+    case T4_Y_RESOLUTION_300:
+        switch (s->x_resolution)
         {
+        case T4_X_RESOLUTION_300:
+            if (!(s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION))
+                bad = T30_ERR_NORESSUPPORT;
+            else
+                set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300);
+            break;
+        default:
             bad = T30_ERR_NORESSUPPORT;
+            break;
         }
-        else
+        break;
+    case T4_Y_RESOLUTION_SUPERFINE:
+        if ((s->supported_resolutions & T30_SUPPORT_SUPERFINE_RESOLUTION))
         {
             switch (s->x_resolution)
             {
@@ -1444,28 +1456,12 @@ static int build_dcs(t30_state_t *s)
                 bad = T30_ERR_NORESSUPPORT;
                 break;
             }
-        }
-        break;
-    case T4_Y_RESOLUTION_300:
-        switch (s->x_resolution)
-        {
-        case T4_X_RESOLUTION_300:
-            if (!(s->supported_resolutions & T30_SUPPORT_300_300_RESOLUTION))
-                bad = T30_ERR_NORESSUPPORT;
-            else
-                set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300);
-            break;
-        default:
-            bad = T30_ERR_NORESSUPPORT;
             break;
         }
-        break;
+        row_squashing_ratio <<= 1;
+        /* Fall through */
     case T4_Y_RESOLUTION_FINE:
-        if (!(s->supported_resolutions & T30_SUPPORT_FINE_RESOLUTION))
-        {
-            bad = T30_ERR_NORESSUPPORT;
-        }
-        else
+        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. */
index 6aeef44d22a0cd4c83e3c0f77c3522d8a0146e85..2e2f25c7066b024b3f54e72eb99165783a5672e4 100644 (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)
 {
     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)
index db9581aaee4851f60179ef52d87e0baf25355418..b159ad81a8b36f7f58fce6680ddfba88f0cd33df 100644 (file)
@@ -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))