From cc795b87c4009bfdf953a3b5dcaa4eb372f14c82 Mon Sep 17 00:00:00 2001
From: Steve Underwood <steveu@coppice.org>
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;
 }