From 5d7e26141f9e01f31032808248d9279058202f9b Mon Sep 17 00:00:00 2001
From: Steve Underwood <steveu@coppice.org>
Date: Sat, 14 Jul 2012 23:59:01 +0800
Subject: [PATCH] Introducing T.85 encode and decodei to spandsp. This is not
 connected into the FAX engine in this update.

---
 libs/spandsp/src/Makefile.am                  |   7 +
 libs/spandsp/src/spandsp.h.in                 |   4 +-
 libs/spandsp/src/spandsp/expose.h             |   4 +-
 .../spandsp/private/t81_t82_arith_coding.h    |  79 ++
 libs/spandsp/src/spandsp/private/t85.h        | 212 +++++
 .../src/spandsp/t81_t82_arith_coding.h        |  82 ++
 libs/spandsp/src/spandsp/t85.h                | 288 ++++++
 libs/spandsp/src/t81_t82_arith_coding.c       | 509 +++++++++++
 libs/spandsp/src/t85_decode.c                 | 864 ++++++++++++++++++
 libs/spandsp/src/t85_encode.c                 | 747 +++++++++++++++
 libs/spandsp/tests/Makefile.am                |   8 +
 .../tests/t81_t82_arith_coding_tests.c        | 238 +++++
 libs/spandsp/tests/t85_tests.c                | 369 ++++++++
 13 files changed, 3407 insertions(+), 4 deletions(-)
 create mode 100644 libs/spandsp/src/spandsp/private/t81_t82_arith_coding.h
 create mode 100644 libs/spandsp/src/spandsp/private/t85.h
 create mode 100644 libs/spandsp/src/spandsp/t81_t82_arith_coding.h
 create mode 100644 libs/spandsp/src/spandsp/t85.h
 create mode 100644 libs/spandsp/src/t81_t82_arith_coding.c
 create mode 100644 libs/spandsp/src/t85_decode.c
 create mode 100644 libs/spandsp/src/t85_encode.c
 create mode 100644 libs/spandsp/tests/t81_t82_arith_coding_tests.c
 create mode 100644 libs/spandsp/tests/t85_tests.c

diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am
index a6846151eb..c6d5229d86 100644
--- a/libs/spandsp/src/Makefile.am
+++ b/libs/spandsp/src/Makefile.am
@@ -153,6 +153,9 @@ libspandsp_la_SOURCES = ademco_contactid.c \
                         t38_core.c \
                         t38_gateway.c \
                         t38_non_ecm_buffer.c \
+                        t81_t82_arith_coding.c \
+                        t85_decode.c \
+                        t85_encode.c \
                         t38_terminal.c \
                         testcpuid.c \
                         time_scale.c \
@@ -241,6 +244,8 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
                          spandsp/t4_tx.h \
                          spandsp/t4_t6_decode.h \
                          spandsp/t4_t6_encode.h \
+                         spandsp/t81_t82_arith_coding.h \
+                         spandsp/t85.h \
                          spandsp/telephony.h \
                          spandsp/time_scale.h \
                          spandsp/timezone.h \
@@ -305,6 +310,8 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
                          spandsp/private/t4_tx.h \
                          spandsp/private/t4_t6_decode.h \
                          spandsp/private/t4_t6_encode.h \
+                         spandsp/private/t81_t82_arith_coding.h \
+                         spandsp/private/t85.h \
                          spandsp/private/time_scale.h \
                          spandsp/private/timezone.h \
                          spandsp/private/tone_detect.h \
diff --git a/libs/spandsp/src/spandsp.h.in b/libs/spandsp/src/spandsp.h.in
index e3c0d4f477..90e77cd5ef 100644
--- a/libs/spandsp/src/spandsp.h.in
+++ b/libs/spandsp/src/spandsp.h.in
@@ -108,8 +108,8 @@
 #include <spandsp/image_translate.h>
 #include <spandsp/t4_t6_decode.h>
 #include <spandsp/t4_t6_encode.h>
-/*#include <spandsp/t81_t82_arith_coding.h>*/
-/*#include <spandsp/t85.h>*/
+#include <spandsp/t81_t82_arith_coding.h>
+#include <spandsp/t85.h>
 /*#include <spandsp/t42.h>*/
 /*#include <spandsp/t43.h>*/
 #include <spandsp/t30.h>
diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h
index 75c16f2405..f4696d2688 100644
--- a/libs/spandsp/src/spandsp/expose.h
+++ b/libs/spandsp/src/spandsp/expose.h
@@ -78,8 +78,8 @@
 #include <spandsp/private/image_translate.h>
 #include <spandsp/private/t4_t6_decode.h>
 #include <spandsp/private/t4_t6_encode.h>
-/*#include <spandsp/private/t81_t82_arith_coding.h>*/
-/*#include <spandsp/private/t85.h>*/
+#include <spandsp/private/t81_t82_arith_coding.h>
+#include <spandsp/private/t85.h>
 /*#include <spandsp/private/t42.h>*/
 /*#include <spandsp/private/t43.h>*/
 #include <spandsp/private/t4_rx.h>
diff --git a/libs/spandsp/src/spandsp/private/t81_t82_arith_coding.h b/libs/spandsp/src/spandsp/private/t81_t82_arith_coding.h
new file mode 100644
index 0000000000..0efe3f00ca
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/t81_t82_arith_coding.h
@@ -0,0 +1,79 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t81_t82_arith_coding.h - ITU T.81 and T.82 QM-coder arithmetic encoding
+ *                          and decoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_PRIVATE_T81_T82_ARITH_CODING_H_)
+#define _SPANDSP_PRIVATE_T81_T82_ARITH_CODING_H_
+
+/* State of a working instance of the QM-coder arithmetic encoder */
+struct t81_t82_arith_encode_state_s
+{
+    /*! A register - see T.82 Table 23 */
+    uint32_t a;
+    /*! C register - see T.82 Table 23 */
+    uint32_t c;
+    /*! Probability status for contexts. MSB = MPS */
+    uint8_t st[4096];
+    /*! Number of buffered 0xFF values that might still overflow */
+    int32_t sc;
+    /*! Bit shift counter. This determines when the next byte will be written */
+    int ct;
+    /*! Buffer for the most recent output byte which is not 0xFF */
+    int buffer;
+    /*! Callback function to deliver the encoded data, byte by byte */
+    void (*output_byte_handler)(void *, int);
+    /*! Opaque pointer passed to byte_out */
+    void *user_data;
+};
+
+/* State of a working instance of the QM-coder arithmetic decoder */
+struct t81_t82_arith_decode_state_s
+{
+    /*! A register - see T.82 Table 25 */
+    uint32_t a;
+    /*! C register - see T.82 Table 25 */
+    uint32_t c;
+    /*! Probability status for contexts. MSB = MPS */
+    uint8_t st[4096];
+    /*! Bit-shift counter. Determines when next byte will be read.
+        Special value -1 signals that zero-padding has started */
+    int ct;
+    /*! Pointer to next PSCD data byte */
+    const uint8_t *pscd_ptr;
+    /*! Pointer to byte after PSCD */
+    const uint8_t *pscd_end;
+    /*! Boolean flag that controls initial fill of s->c */
+    int startup;
+    /*! Boolean flag that triggers return -2 between reaching PSCD end
+        and decoding the first symbol that might never have been encoded
+        in the first place */
+    int nopadding;
+};
+
+#endif
+
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/t85.h b/libs/spandsp/src/spandsp/private/t85.h
new file mode 100644
index 0000000000..dac7a3340a
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/t85.h
@@ -0,0 +1,212 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t85.h - ITU T.85 JBIG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2008, 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_T85_H_)
+#define _SPANDSP_PRIVATE_T85_H_
+
+/* Maximum number of ATMOVEs per stripe that the decoder can handle */
+#define T85_ATMOVES_MAX  1
+
+/* TP special pixels */
+#define TPB2CX 0x195
+#define TPB3CX 0x0E5
+
+/* T.82 table 2 - symbolic constants */
+enum
+{
+    T82_STUFF = 0x00,
+    T82_RESERVE = 0x01,
+    T82_SDNORM = 0x02,
+    T82_SDRST = 0x03,
+    T82_ABORT = 0x04,
+    T82_NEWLEN = 0x05,
+    T82_ATMOVE = 0x06,
+    T82_COMMENT = 0x07,
+    T82_ESC = 0xFF
+};
+
+/* State of a working instance of the T.85 JBIG FAX encoder */
+struct t85_encode_state_s
+{
+    /*! \brief Callback function to read a row of pixels from the image source. */
+    t4_row_read_handler_t row_read_handler;
+    /*! \brief Opaque pointer passed to row_read_handler. */
+    void *row_read_user_data;
+
+    /*! The number of bit planes. Always 1 for true T.85 */
+    uint8_t bit_planes;
+    uint8_t current_bit_plane;
+    /*! The width of the full image, in pixels */
+    uint32_t xd;
+    /*! The height of the full image, in pixels */
+    uint32_t yd;
+    /*! Then number of rows per stripe */
+    uint32_t l0;
+    /*! Maximum ATMOVE window size (0 - 127) */
+    int mx;
+    /*! Encoding parameters */
+    int options;
+    /*! The contents for a COMMENT marker segment, to be added to the
+        image at the next opportunity. This is set to NULL when nothing is
+        pending. */
+    const uint8_t *comment;
+    /*! Length of data pointed to by comment */
+    size_t comment_len;
+
+    /*! Next row number to be encoded */
+    uint32_t y;
+    /*! Next row within current stripe */
+    uint32_t i;
+    /*! Flag for handling NEWLEN processing. */
+    int newlen;
+    /*! X-offset of adaptive template pixel */
+    int32_t tx;
+    /*! Adaptive template algorithm variables */
+    uint32_t c_all;
+    /*! Adaptive template algorithm variables */
+    uint32_t c[128];
+    /*! New TX value, or <0 for analysis in progress */
+    int32_t new_tx;
+    /*! TRUE if previous row was typical */
+    int prev_ltp;
+    /*! Pointers to the 3 row buffers */
+    uint8_t *prev_row[3];
+    /*! Pointer to a block of allocated memory 3 rows long, which
+        we divide up for the 3 row buffers. */
+    uint8_t *row_buf;
+    uint8_t *bitstream;
+    int bitstream_len;
+    int bitstream_iptr;
+    int bitstream_optr;
+    int fill_with_white;
+
+    /*! \brief The size of the compressed image, in bytes. */
+    int compressed_image_size;
+
+    /*! Arithmetic encoder state */
+    t81_t82_arith_encode_state_t s;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+/* State of a working instance of the T.85 JBIG FAX decoder */
+struct t85_decode_state_s
+{
+    /*! A callback routine to handle decoded pixel rows */
+    t4_row_write_handler_t row_write_handler;
+    /*! An opaque pointer passed to row_write_handler() */
+    void *row_write_user_data;
+    /*! A callback routine to handle decoded comments */
+    t4_row_write_handler_t comment_handler;
+    /*! An opaque pointer passed to comment_handler() */
+    void *comment_user_data;
+
+    uint8_t min_bit_planes;
+    uint8_t max_bit_planes;
+    /*! The maximum length of comment to be passed to the comment handler */
+    uint32_t max_comment_len;
+    /*! The maximum permitted width of the full image, in pixels */
+    uint32_t max_xd;
+    /*! The maximum permitted height of the full image, in pixels */
+    uint32_t max_yd;
+
+    /*! The number of bit planes expected, according to the header. Always 1 for true T.85 */
+    uint8_t bit_planes;
+    uint8_t current_bit_plane;
+    
+    /*! The width of the full image, in pixels */
+    uint32_t xd;
+    /*! The height of the full image, in pixels */
+    uint32_t yd;
+    /*! The number of rows per stripe */
+    uint32_t l0;
+    /*! Maximum ATMOVE window size */
+    int mx;
+    /*! Encoding parameters */
+    int options;
+
+    /*! The current row and the previous 2 rows of image data */
+    int p[3];
+    /*! Pointers to the 3 row buffers */
+    uint8_t *prev_row[3];
+    /*! Pointer to a block of allocated memory 3 rows long, which
+        we divide up for the 3 row buffers. */
+    uint8_t *row_buf;
+    /*! The length of the row buffer */
+    int row_buf_len;
+    /*! Bytes per pixel row */
+    size_t bytes_per_row;
+    /*! X-offset of AT pixel */
+    int32_t tx;
+    /*! Number of bytes read so far */
+    uint32_t bie_len;
+    /*! Buffer space for the BIH or marker segments fragments */
+    uint8_t buffer[20];
+    /*! Number of bytes in buffer. */
+    int buf_len;
+    /*! Required number of bytes in buffer to proceed with processing
+        its contents. */
+    int buf_needed;
+    /*! The content of a decoded COMMENT marker segment. */
+    uint8_t *comment;
+    /*! The expected length of a decoded COMMENT segment */
+    uint32_t comment_len;
+    /*! The length of COMMENT decoded to date */
+    uint32_t comment_progress;
+    /*! Current column */
+    uint32_t x;
+    /*! Current row */
+    uint32_t y;
+    /*! Current row within the current stripe */
+    uint32_t i;
+    /*! Number of AT moves in the current stripe */
+    int at_moves;
+    /*! Rows at which an AT move will happen */
+    uint32_t at_row[T85_ATMOVES_MAX];
+    /*! ATMOVE x-offsets in current stripe */
+    int at_tx[T85_ATMOVES_MAX];
+    /*! Working data for decode_pscd() */
+    uint32_t row_h[3];
+    /*! Flag for TPBON/TPDON: next pixel is a pseudo pixel */
+    int pseudo;
+    /*! Line is not typical flag. */
+    int lntp;
+    /*! Flag that row_write_handler() requested an interrupt. */
+    int interrupt;
+    /*! Flag that the data to be decoded has run out. */
+    int end_of_data;
+    /*! Arithmetic decoder state */
+    t81_t82_arith_decode_state_t s;
+
+    /*! \brief The size of the compressed image, in bytes. */
+    int compressed_image_size;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/t81_t82_arith_coding.h b/libs/spandsp/src/spandsp/t81_t82_arith_coding.h
new file mode 100644
index 0000000000..905e0491be
--- /dev/null
+++ b/libs/spandsp/src/spandsp/t81_t82_arith_coding.h
@@ -0,0 +1,82 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t81_t82_arith_coding.h - ITU T.81 and T.82 QM-coder arithmetic encoding
+ *                          and decoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_T81_T82_ARITH_CODING_H_)
+#define _SPANDSP_T81_T82_ARITH_CODING_H_
+
+/*! \page t81_t82_arith_coding_page T.81 and T.82 QM-coder arithmetic encoding and decoding
+
+\section t81_t82_arith_coding_page_sec_1 What does it do?
+A similar arithmetic coder, called the QM-coder, is used by several image compression
+schemes. These routines implement this coder in a (hopefully) reusable way.
+
+\section t81_t82_arith_coding_page_sec_1 How does it work?
+*/
+
+/* State of a working instance of the arithmetic encoder */
+typedef struct t81_t82_arith_encode_state_s  t81_t82_arith_encode_state_t;
+
+/* State of a working instance of the arithmetic decoder */
+typedef struct t81_t82_arith_decode_state_s  t81_t82_arith_decode_state_t;
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_arith_encode_state_t *s,
+                                                                       void (*output_byte_handler)(void *, int),
+                                                                       void *user_data);
+
+SPAN_DECLARE(int) t81_t82_arith_encode_restart(t81_t82_arith_encode_state_t *s, int reuse_st);
+
+SPAN_DECLARE(int) t81_t82_arith_encode_release(t81_t82_arith_encode_state_t *s);
+
+SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s);
+
+SPAN_DECLARE(void) t81_t82_arith_encode(t81_t82_arith_encode_state_t *s, int cx, int pix);
+
+SPAN_DECLARE(void) t81_t82_arith_encode_flush(t81_t82_arith_encode_state_t *s);
+
+    
+SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_arith_decode_state_t *s);
+
+SPAN_DECLARE(int) t81_t82_arith_decode_restart(t81_t82_arith_decode_state_t *s, int reuse_st);
+
+SPAN_DECLARE(int) t81_t82_arith_decode_release(t81_t82_arith_decode_state_t *s);
+
+SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s);
+
+SPAN_DECLARE(int) t81_t82_arith_decode(t81_t82_arith_decode_state_t *s, int cx);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/t85.h b/libs/spandsp/src/spandsp/t85.h
new file mode 100644
index 0000000000..516f6ee0e7
--- /dev/null
+++ b/libs/spandsp/src/spandsp/t85.h
@@ -0,0 +1,288 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t85.h - ITU T.85 JBIG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2008, 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_T85_H_)
+#define _SPANDSP_T85_H_
+
+/*! \page t85_page T.85 (JBIG for FAX) image compression and decompression
+
+\section t85_page_sec_1 What does it do?
+The T.85 image compression and decompression routines implement the variant of the
+JBIG encoding method defined in ITU specification T.85. This is an image compression
+algorithm used for black and white FAX transmission. T.85 defines a subset of the
+full JBIG spec (T.82), which only handled a single progressively scanned bit plane.
+This results in a great deal of simplification, and results in the ability to
+compress or decompress progressively, while only buffering the latest 3 pixel rows
+of the image.
+
+\section t85_page_sec_1 How does it work?
+*/
+
+/*! Bits in the option byte of the T.82 BIH which are valid for T.85 */
+enum
+{
+    /*! Enable typical prediction (bottom) */
+    T85_TPBON = 0x08,
+    /*! Variable length image */
+    T85_VLENGTH = 0x20,
+    /*! Lowest-resolution-layer is a two-line template */
+    T85_LRLTWO = 0x40
+};
+
+/*! Return values from the T.85 decoder */
+enum
+{
+    /*! More image data is needed */
+    T85_MORE_DATA = 0,
+    /*! Image completed successfully */
+    T85_OK = -1,
+    /*! The decoder has interrupted */
+    T85_INTERRUPT = -2,
+    /*! An abort was found in the image data */
+    T85_ABORTED = -3,
+    /*! A memory allocation error occurred */
+    T85_NOMEM = -4,
+    /*! The image data is invalid. This includes finding values
+        in the BIH which lie outside the T.85 domain */
+    T85_INVALID_DATA = -5
+};
+
+/*! State of a working instance of the T.85 encoder */
+typedef struct t85_encode_state_s t85_encode_state_t;
+
+/*! State of a working instance of the T.85 decoder */
+typedef struct t85_decode_state_s t85_decode_state_t;
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/*! \brief Get the next byte of the current document page. The document will
+           be padded for the current minimum scan line time.
+    \param s The T.85 context.
+    \return The next byte. For the last byte of data, bit 8 is
+            set. In this case, one or more bits of the byte may be padded with
+            zeros, to complete the byte. */
+SPAN_DECLARE(int) t85_encode_get_byte(t85_encode_state_t *s);
+
+/*! \brief Get the next chunk of the current document page. The document will
+           be padded for the current minimum scan line time.
+    \param s The T.85 context.
+    \param buf The buffer into which the chunk is to written.
+    \param max_len The maximum length of the chunk.
+    \return The actual length of the chunk. If this is less than max_len it 
+            indicates that the end of the document has been reached. */
+SPAN_DECLARE(int) t85_encode_get_chunk(t85_encode_state_t *s, uint8_t buf[], int max_len);
+
+/*! \brief Set the row read handler for a T.85 encode context.
+    \param s The T.85 context.
+    \param handler A pointer to the handler routine.
+    \param user_data An opaque pointer passed to the handler routine.
+    \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) t85_encode_set_row_read_handler(t85_encode_state_t *s,
+                                                  t4_row_read_handler_t handler,
+                                                  void *user_data);
+
+/*! \brief Prepare to encode an image in T.85 format.
+    \param s The T.85 context.
+    \param image_width Image width, in pixels.
+    \param image_length Image length, in pixels.
+    \param handler A callback routine to handle encoded image rows.
+    \param user_data An opaque pointer passed to handler.
+    \return A pointer to the context, or NULL if there was a problem. */
+SPAN_DECLARE(t85_encode_state_t *) t85_encode_init(t85_encode_state_t *s,
+                                                   uint32_t image_width,
+                                                   uint32_t image_length,
+                                                   t4_row_read_handler_t handler,
+                                                   void *user_data);
+
+/*! \brief Restart a T.85 encode context.
+    \param s The T.85 context.
+    \param image width The image width, in pixels.
+    \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) t85_encode_restart(t85_encode_state_t *s,
+                                     uint32_t image_width,
+                                     uint32_t image_length);
+    
+SPAN_DECLARE(int) t85_encode_release(t85_encode_state_t *s);
+
+SPAN_DECLARE(int) t85_encode_free(t85_encode_state_t *s);
+
+/*! \brief Set the T.85 options
+    \param s The T.85 context.
+    \brief l0 ???
+    \brief mx ???
+    \brief options ???. */
+SPAN_DECLARE(void) t85_encode_set_options(t85_encode_state_t *s,
+                                          uint32_t l0,
+                                          int mx,
+                                          int options);
+
+/*! \brief Insert a comment in the encoded file.
+    \param s The T.85 context.
+    \param comment The comment. Note that this is not a C string, and may contain any bytes.
+    \param len The length of the comment. */
+SPAN_DECLARE(void) t85_encode_comment(t85_encode_state_t *s,
+                                      const uint8_t comment[],
+                                      size_t len);
+
+/*! \brief Set the image width.
+    \param s The T.85 context.
+    \param width The width of the image.
+    \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) t85_encode_set_image_width(t85_encode_state_t *s, uint32_t image_width);
+
+/*! \brief Alter the length of a T.85 encoded image. The new length cannot be greater than the
+           originally specified length. If the new length is less than the current length it
+           will be silently adjusted to the current length. Therefore, adjust the length to 1
+           will make the currently encoded length the final length.
+    \param s The T.85 context.
+    \param length The new image length, in pixels.
+    \return 0 if OK, or -1 if the request was not valid. */
+SPAN_DECLARE(int) t85_encode_set_image_length(t85_encode_state_t *s, uint32_t length);
+
+/*! \brief Get the width of the image.
+    \param s The T.85 context.
+    \return The width of the image, in pixels. */
+SPAN_DECLARE(uint32_t) t85_encode_get_image_width(t85_encode_state_t *s);
+
+/*! \brief Get the length of the image.
+    \param s The T.85 context.
+    \return The length of the image, in pixels. */
+SPAN_DECLARE(uint32_t) t85_encode_get_image_length(t85_encode_state_t *s);
+
+/*! \brief Get the size of the compressed image, in bits.
+    \param s The T.85 context.
+    \return The size of the compressed image, in bits. */
+SPAN_DECLARE(int) t85_encode_get_compressed_image_size(t85_encode_state_t *s);
+
+/*! \brief Stop image encoding prematurely.
+    \param s The T.85 context. */
+SPAN_DECLARE(void) t85_encode_abort(t85_encode_state_t *s);
+
+/*! \brief Prepare to decode an image in T.85 format.
+    \param s The T.85 context.
+    \param handler A callback routine to handle decoded image rows.
+    \param user_data An opaque pointer passed to handler.
+    \return A pointer to the context, or NULL if there was a problem. */
+SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data);
+
+SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s);
+
+SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s);
+
+SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s);
+
+SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s);
+
+/*! \brief Get the width of the image.
+    \param s The T.85 context.
+    \return The width of the image, in pixels. */
+SPAN_DECLARE(uint32_t) t85_decode_get_image_width(t85_decode_state_t *s);
+
+/*! \brief Get the length of the image.
+    \param s The T.85 context.
+    \return The length of the image, in pixels. */
+SPAN_DECLARE(uint32_t) t85_decode_get_image_length(t85_decode_state_t *s);
+
+/*! \brief Get the size of the compressed image, in bits.
+    \param s The T.85 context.
+    \return The size of the compressed image, in bits. */
+SPAN_DECLARE(int) t85_decode_get_compressed_image_size(t85_decode_state_t *s);
+
+/*! \brief Set the row handler routine.
+    \param s The T.85 context.
+    \param handler A callback routine to handle decoded image rows.
+    \param user_data An opaque pointer passed to handler.
+    \return 0 for OK. */
+SPAN_DECLARE(int) t85_decode_set_row_write_handler(t85_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data);
+
+/*! \brief Set the comment handler routine.
+    \param s The T.85 context.
+    \param max_comment_len The maximum length of comment to be passed to the handler.
+    \param handler A callback routine to handle decoded comment.
+    \param user_data An opaque pointer passed to handler.
+    \return 0 for OK. */
+SPAN_DECLARE(int) t85_decode_set_comment_handler(t85_decode_state_t *s,
+                                                 uint32_t max_comment_len,
+                                                 t4_row_write_handler_t handler,
+                                                 void *user_data);
+
+/*! A maliciously constructed T.85 image could consume too much memory, and constitute
+    a denial of service attack on the system. This function allows constraints to be
+    applied.
+    \brief Set constraints on the received image size.
+    \param s The T.85 context.
+    \param max_xd The maximum permitted width of the full image, in pixels
+    \param max_yd The maximum permitted height of the full image, in pixels
+    \return 0 for OK */
+SPAN_DECLARE(int) t85_decode_set_image_size_constraints(t85_decode_state_t *s,
+                                                        uint32_t max_xd,
+                                                        uint32_t max_yd);
+
+/*! After the final BIE byte has been delivered to t85_decode_put_xx(), it may still
+    return T85_MORE_DATA when the T85_VLENGTH option was used, and no NEWLEN
+    marker section has appeared yet. This is because such a BIE is not
+    self-terminating (i.e. there could still be a NEWLEN followed by an SDNORM
+    or SDRST at the very end of the final stripe, which needs to be processed
+    before the final row is output. See ITU-T Recommendation T.85, Appendix I).
+    Therefore, after the last byte has been delivered, call this routine to
+    signal the end of the BIE. This is necessary to allow the routine to finish
+    processing BIEs with option T85_VLENGTH that do not actually contain any
+    NEWLEN marker section.
+    \brief Inform the T.85 decode engine of a status change in the signal source (end
+           of tx, rx signal change, etc.).
+    \param s The T.85 context.
+    \param status The type of status change which occured. */
+SPAN_DECLARE(void) t85_decode_rx_status(t85_decode_state_t *s, int status);
+
+/*! \brief Decode a byte of T.85 data.
+    \param s The T.85 context.
+    \param byte The data to be decoded.
+    \return 0 for OK. */
+SPAN_DECLARE(int) t85_decode_put_byte(t85_decode_state_t *s, int byte);
+
+/*! \brief Decode a chunk of T.85 data.
+    \param s The T.85 context.
+    \param data The data to be decoded.
+    \param len The length of the data to be decoded.
+    \return 0 for OK. */
+SPAN_DECLARE(int) t85_decode_put_chunk(t85_decode_state_t *s,
+                                       const uint8_t data[],
+                                       size_t len);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/t81_t82_arith_coding.c b/libs/spandsp/src/t81_t82_arith_coding.c
new file mode 100644
index 0000000000..86e7e0271f
--- /dev/null
+++ b/libs/spandsp/src/t81_t82_arith_coding.c
@@ -0,0 +1,509 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t81_t82_arith_coding.c - ITU T.81 and T.82 QM-coder arithmetic encoding
+ *                          and decoding
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/t81_t82_arith_coding.h"
+
+#include "spandsp/private/t81_t82_arith_coding.h"
+
+/* T.82 defines the QM-coder at a level very close to actual code. Therefore
+   this file closely mirrors the routine names, variable names, and flow
+   described in T.82. QM-Coder is supposed to be the same in some other image
+   compression schemes, such as T.81. However, this code has not been checked
+   to see if it follows the letter of any spec other than T.82. */
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+/* Code bytes which must trigger stuffing */
+enum
+{
+    T81_T82_STUFF = 0x00,
+    T81_T82_ESC = 0xFF
+};
+
+/* This table is from T.82 table 24 - Probability estimation table */
+static const struct probability_estimation_s
+{
+    uint16_t lsz;
+    uint8_t nlps;   /* The SWITCH bit is packed into the top of this byte */
+    uint8_t nmps;
+} prob[113] =
+{
+    {0x5A1D,   1 + 128,   1},
+    {0x2586,  14,         2},
+    {0x1114,  16,         3},
+    {0x080B,  18,         4},
+    {0x03D8,  20,         5},
+    {0x01DA,  23,         6},
+    {0x00E5,  25,         7},
+    {0x006F,  28,         8},
+    {0x0036,  30,         9},
+    {0x001A,  33,        10},
+    {0x000D,  35,        11},
+    {0x0006,   9,        12},
+    {0x0003,  10,        13},
+    {0x0001,  12,        13},
+    {0x5A7F,  15 + 128,  15},
+    {0x3F25,  36,        16},
+    {0x2CF2,  38,        17},
+    {0x207C,  39,        18},
+    {0x17B9,  40,        19},
+    {0x1182,  42,        20},
+    {0x0CEF,  43,        21},
+    {0x09A1,  45,        22},
+    {0x072F,  46,        23},
+    {0x055C,  48,        24},
+    {0x0406,  49,        25},
+    {0x0303,  51,        26},
+    {0x0240,  52,        27},
+    {0x01B1,  54,        28},
+    {0x0144,  56,        29},
+    {0x00F5,  57,        30},
+    {0x00B7,  59,        31},
+    {0x008A,  60,        32},
+    {0x0068,  62,        33},
+    {0x004E,  63,        34},
+    {0x003B,  32,        35},
+    {0x002C,  33,         9},
+    {0x5AE1,  37 + 128,  37},
+    {0x484C,  64,        38},
+    {0x3A0D,  65,        39},
+    {0x2EF1,  67,        40},
+    {0x261F,  68,        41},
+    {0x1F33,  69,        42},
+    {0x19A8,  70,        43},
+    {0x1518,  72,        44},
+    {0x1177,  73,        45},
+    {0x0E74,  74,        46},
+    {0x0BFB,  75,        47},
+    {0x09F8,  77,        48},
+    {0x0861,  78,        49},
+    {0x0706,  79,        50},
+    {0x05CD,  48,        51},
+    {0x04DE,  50,        52},
+    {0x040F,  50,        53},
+    {0x0363,  51,        54},
+    {0x02D4,  52,        55},
+    {0x025C,  53,        56},
+    {0x01F8,  54,        57},
+    {0x01A4,  55,        58},
+    {0x0160,  56,        59},
+    {0x0125,  57,        60},
+    {0x00F6,  58,        61},
+    {0x00CB,  59,        62},
+    {0x00AB,  61,        63},
+    {0x008F,  61,        32},
+    {0x5B12,  65 + 128,  65},
+    {0x4D04,  80,        66},
+    {0x412C,  81,        67},
+    {0x37D8,  82,        68},
+    {0x2FE8,  83,        69},
+    {0x293C,  84,        70},
+    {0x2379,  86,        71},
+    {0x1EDF,  87,        72},
+    {0x1AA9,  87,        73},
+    {0x174E,  72,        74},
+    {0x1424,  72,        75},
+    {0x119C,  74,        76},
+    {0x0F6B,  74,        77},
+    {0x0D51,  75,        78},
+    {0x0BB6,  77,        79},
+    {0x0A40,  77,        48},
+    {0x5832,  80 + 128,  81},
+    {0x4D1C,  88,        82},
+    {0x438E,  89,        83},
+    {0x3BDD,  90,        84},
+    {0x34EE,  91,        85},
+    {0x2EAE,  92,        86},
+    {0x299A,  93,        87},
+    {0x2516,  86,        71},
+    {0x5570,  88 + 128,  89},
+    {0x4CA9,  95,        90},
+    {0x44D9,  96,        91},
+    {0x3E22,  97,        92},
+    {0x3824,  99,        93},
+    {0x32B4,  99,        94},
+    {0x2E17,  93,        86},
+    {0x56A8,  95 + 128,  96},
+    {0x4F46, 101,        97},
+    {0x47E5, 102,        98},
+    {0x41CF, 103,        99},
+    {0x3C3D, 104,       100},
+    {0x375E,  99,        93},
+    {0x5231, 105,       102},
+    {0x4C0F, 106,       103},
+    {0x4639, 107,       104},
+    {0x415E, 103,        99},
+    {0x5627, 105 + 128, 106},
+    {0x50E7, 108,       107},
+    {0x4B85, 109,       103},
+    {0x5597, 110,       109},
+    {0x504F, 111,       107},
+    {0x5A10, 110 + 128, 111},
+    {0x5522, 112,       109},
+    {0x59EB, 112 + 128, 111}
+};
+
+static __inline__ void output_stuffed_byte(t81_t82_arith_encode_state_t *s, int byte)
+{
+    s->output_byte_handler(s->user_data, byte);
+    if (byte == T81_T82_ESC)
+        s->output_byte_handler(s->user_data, T81_T82_STUFF);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void byteout(t81_t82_arith_encode_state_t *s)
+{
+    uint32_t temp;
+
+    /* T.30 figure 26 - BYTEOUT */
+    temp = s->c >> 19;
+    if (temp > 0xFF)
+    {
+        if (s->buffer >= 0)
+            output_stuffed_byte(s, s->buffer + 1);
+        while (s->sc)
+        {
+            s->output_byte_handler(s->user_data, 0x00);
+            s->sc--;
+        }
+        s->buffer = temp & 0xFF;
+    }
+    else if (temp == 0xFF)
+    {
+        s->sc++;
+    }
+    else
+    {
+        if (s->buffer >= 0)
+            output_stuffed_byte(s, s->buffer);
+        while (s->sc)
+        {
+            output_stuffed_byte(s, T81_T82_ESC);
+            s->sc--;
+        }
+        s->buffer = temp;
+    }
+    s->c &= 0x7FFFF;
+    s->ct = 8;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void renorme(t81_t82_arith_encode_state_t *s)
+{
+    /* T.82 figure 25 - RENORME */
+    do
+    {
+        s->a <<= 1;
+        s->c <<= 1;
+        s->ct--;
+        if (s->ct == 0)
+            byteout(s);
+    }
+    while (s->a < 0x8000);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t81_t82_arith_encode(t81_t82_arith_encode_state_t *s, int cx, int pix)
+{
+    uint32_t ss;
+
+    /* T.82 figure 22 - ENCODE */
+    ss = s->st[cx] & 0x7F;
+    if (((pix << 7) ^ s->st[cx]) & 0x80)
+    {
+        /* T.82 figure 23 - CODELPS */
+        s->a -= prob[ss].lsz;
+        if (s->a >= prob[ss].lsz)
+        {
+            s->c += s->a;
+            s->a = prob[ss].lsz;
+        }
+        s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
+        renorme(s);
+    }
+    else
+    {
+        /* T.82 figure 24 - CODEMPS */
+        s->a -= prob[ss].lsz;
+        if (s->a < 0x8000)
+        {
+            if (s->a < prob[ss].lsz)
+            {
+                s->c += s->a;
+                s->a = prob[ss].lsz;
+            }
+            s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
+            renorme(s);
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t81_t82_arith_encode_flush(t81_t82_arith_encode_state_t *s)
+{
+    uint32_t temp;
+
+    /* T.82 figure 28 - FLUSH */
+    /* T.82 figure 29 - CLEARBITS */
+    temp = (s->c + s->a - 1) & 0xFFFF0000;
+    s->c = (temp < s->c)  ?  (temp + 0x8000)  :  temp;
+    /* T.82 figure 30 - FINALWRITES */
+    s->c <<= s->ct;
+    if ((s->c > 0x7FFFFFF))
+    {
+        if (s->buffer >= 0)
+            output_stuffed_byte(s, s->buffer + 1);
+        /* Only output 0x00 bytes if something non-0x00 will follow */
+        if ((s->c & 0x7FFF800))
+        {
+            while (s->sc)
+            {
+                output_stuffed_byte(s, 0x00);
+                s->sc--;
+            }
+        }
+    }
+    else
+    {
+        /* The next bit says s->buffer + 1 in T.82, but that makes no sense. It doesn't
+           agree with how we code things away from the flush condition, and it gives
+           answers which don't seem to match other JBIG coders. */
+        if (s->buffer >= 0)
+            output_stuffed_byte(s, s->buffer);
+        while (s->sc)
+        {
+            output_stuffed_byte(s, 0xFF);
+            s->sc--;
+        }
+    }
+    /* Only output final bytes if they are not 0x00 */
+    if ((s->c & 0x7FFF800))
+    {
+        output_stuffed_byte(s, (s->c >> 19) & 0xFF);
+        if ((s->c & 0x7F800))
+            output_stuffed_byte(s, (s->c >> 11) & 0xFF);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_encode_restart(t81_t82_arith_encode_state_t *s, int reuse_st)
+{
+    /* T.82 figure 27 - INITENC */
+    if (!reuse_st)
+        memset(s->st, 0, sizeof(s->st));
+    s->c = 0;
+    s->a = 0x10000;
+    s->sc = 0;
+    s->ct = 11;
+    s->buffer = -1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_arith_encode_state_t *s,
+                                                                       void (*output_byte_handler)(void *, int),
+                                                                       void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (t81_t82_arith_encode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    s->output_byte_handler = output_byte_handler;
+    s->user_data = user_data;
+
+    t81_t82_arith_encode_restart(s, FALSE);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_encode_release(t81_t82_arith_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s)
+{
+    int ret;
+
+    ret = t81_t82_arith_encode_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_decode(t81_t82_arith_decode_state_t *s, int cx)
+{
+    uint32_t ss;
+    int pix;
+
+    /* T.82 figure 35 - RENORMD */
+    while (s->a < 0x8000  ||  s->startup)
+    {
+        while (s->ct <= 8  &&  s->ct >= 0)
+        {
+            /* First we can move a new byte into s->c */
+            if (s->pscd_ptr >= s->pscd_end)
+                return -1;
+            if (s->pscd_ptr[0] == T81_T82_ESC)
+            {
+                if (s->pscd_ptr + 1 >= s->pscd_end)
+                    return -1;
+                if (s->pscd_ptr[1] == T81_T82_STUFF)
+                {
+                    s->c |= (0xFF << (8 - s->ct));
+                    s->ct += 8;
+                    s->pscd_ptr += 2;
+                }
+                else
+                {
+                    /* Start padding with zero bytes */
+                    s->ct = -1;
+                    if (s->nopadding)
+                    {
+                        /* Subsequent symbols might depend on zero padding */
+                        s->nopadding = FALSE;
+                        return -2;
+                    }
+                }
+            }
+            else
+            {
+                s->c |= (int32_t) *(s->pscd_ptr++) << (8 - s->ct);
+                s->ct += 8;
+            }
+        }
+        s->a <<= 1;
+        s->c <<= 1;
+        if (s->ct >= 0)
+            s->ct--;
+        if (s->a == 0x10000)
+            s->startup = FALSE;
+    }
+
+    /* T.82 figure 32 - DECODE */
+    ss = s->st[cx] & 0x7F;
+    if ((s->c >> 16) >= (s->a -= prob[ss].lsz))
+    {
+        /* T.82 figure 33 - LPS_EXCHANGE */
+        if (s->a < prob[ss].lsz)
+        {
+            s->c -= (s->a << 16);
+            s->a = prob[ss].lsz;
+            pix = s->st[cx] >> 7;
+            s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
+        }
+        else
+        {
+            s->c -= (s->a << 16);
+            s->a = prob[ss].lsz;
+            pix = 1 - (s->st[cx] >> 7);
+            s->st[cx] = (s->st[cx]& 0x80) ^ prob[ss].nlps;
+        }
+    }
+    else
+    {
+        if (s->a < 0x8000)
+        {
+            /* T.82 figure 34 - MPS_EXCHANGE */
+            if (s->a < prob[ss].lsz)
+            {
+                pix = 1 - (s->st[cx] >> 7);
+                s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
+            }
+            else
+            {
+                pix = s->st[cx] >> 7;
+                s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
+            }
+        }
+        else
+        {
+            pix = s->st[cx] >> 7;
+        }
+    }
+
+    return pix;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_decode_restart(t81_t82_arith_decode_state_t *s, int reuse_st)
+{
+    if (!reuse_st)
+        memset(s->st, 0, sizeof(s->st));
+    s->c = 0;
+    s->a = 1;
+    s->ct = 0;
+    s->startup = TRUE;
+    s->nopadding = FALSE;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_arith_decode_state_t *s)
+{
+    if (s == NULL)
+    {
+        if ((s = (t81_t82_arith_decode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+
+    t81_t82_arith_decode_restart(s, FALSE);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_decode_release(t81_t82_arith_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s)
+{
+    int ret;
+
+    ret = t81_t82_arith_decode_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/t85_decode.c b/libs/spandsp/src/t85_decode.c
new file mode 100644
index 0000000000..ff348a5fda
--- /dev/null
+++ b/libs/spandsp/src/t85_decode.c
@@ -0,0 +1,864 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t85_decode.c - ITU T.85 JBIG for FAX image decompression
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009, 2010 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/async.h"
+#include "spandsp/timezone.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#include "spandsp/t81_t82_arith_coding.h"
+#include "spandsp/t85.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/t81_t82_arith_coding.h"
+#include "spandsp/private/t85.h"
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static __inline__ int32_t pack_32(uint8_t *s)
+{
+    int32_t value;
+
+    value = (((int32_t) s[0] << 24) | ((int32_t) s[1] << 16) | ((int32_t) s[2] << 8) | (int32_t) s[3]);
+    return value;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Decode some PSCD bytes, output the decoded rows as they are completed. Return
+   the number of bytes which have actually been read. This will be less than len
+   if a marker segment was part of the data or if the final byte was 0xFF, meaning
+   that this code can not determine whether we have a marker segment. */
+static size_t decode_pscd(t85_decode_state_t *s, const uint8_t data[], size_t len)
+{
+    uint8_t *hp[3];
+    int32_t o;
+    int cx;
+    int i;
+    int pix;
+    int slntp;
+    int buffered_rows;
+
+    buffered_rows = (s->options & T85_LRLTWO)  ?  2  :  3;
+    /* Forward data to the arithmetic decoder */
+    s->s.pscd_ptr = data;
+    s->s.pscd_end = data + len;
+
+    for (s->interrupt = FALSE;  s->i < s->l0  &&  s->y < s->yd  &&  !s->interrupt;  s->i++, s->y++)
+    {
+        /* Point to the current image bytes */
+        for (i = 0;  i < 3;  i++)
+            hp[i] = s->row_buf + s->p[i]*s->bytes_per_row + (s->x >> 3);
+
+        /* Adaptive template changes */
+        if (s->x == 0  &&  s->pseudo)
+        {
+            for (i = 0;  i < s->at_moves;  i++)
+            {
+                if (s->at_row[i] == s->i)
+                    s->tx = s->at_tx[i];
+            }
+        }
+
+        /* Typical prediction */
+        if ((s->options & T85_TPBON)  &&  s->pseudo)
+        {
+            slntp = t81_t82_arith_decode(&s->s, (s->options & T85_LRLTWO)  ?  TPB2CX  :  TPB3CX);
+            if (slntp < 0)
+                return s->s.pscd_ptr - data;
+            s->lntp = !(slntp ^ s->lntp);
+            if (!s->lntp)
+            {
+                /* This row is 'typical' (i.e. identical to the previous one) */
+                if (s->p[1] < 0)
+                {
+                    /* First row of page or (following SDRST) of stripe */
+                    for (i = 0;  i < s->bytes_per_row;  i++)
+                        hp[0][i] = 0;
+                    s->interrupt = s->row_write_handler(s->row_write_user_data, hp[0], s->bytes_per_row);
+                    /* Rotate the ring buffer that holds the last few rows */
+                    s->p[2] = s->p[1];
+                    s->p[1] = s->p[0];
+                    if (++(s->p[0]) >= buffered_rows)
+                        s->p[0] = 0;
+                }
+                else
+                {
+                    s->interrupt = s->row_write_handler(s->row_write_user_data, hp[1], s->bytes_per_row);
+                    /* Duplicate the last row in the ring buffer */
+                    s->p[2] = s->p[1];
+                }
+                continue;
+            }
+            /* This row is 'not typical' and has to be coded completely */
+        }
+        s->pseudo = FALSE;
+
+        if (s->x == 0)
+        {
+            s->row_h[0] = 0;
+            s->row_h[1] = (s->p[1] >= 0)  ?  ((int32_t) hp[1][0] << 8)  :  0;
+            s->row_h[2] = (s->p[2] >= 0)  ?  ((int32_t) hp[2][0] << 8)  :  0;
+        }
+
+        /* Decode row */
+        while (s->x < s->xd)
+        {
+            if ((s->x & 7) == 0)
+            {
+                if (s->x < (s->bytes_per_row - 1)*8  &&  s->p[1] >= 0)
+                {
+                    s->row_h[1] |= hp[1][1];
+                    if (s->p[2] >= 0)
+                        s->row_h[2] |= hp[2][1];
+                }
+            }
+            if ((s->options & T85_LRLTWO))
+            {
+                /* Two row template */
+                do
+                {
+                    cx = (s->row_h[0] & 0x00F);
+                    if (s->tx)
+                    {
+                        cx |= ((s->row_h[1] >> 9) & 0x3E0);
+                        if (s->x >= (uint32_t) s->tx)
+                        {
+                            if (s->tx < 8)
+                            {
+                                cx |= ((s->row_h[0] >> (s->tx - 5)) & 0x010);
+                            }
+                            else
+                            {
+                                o = (s->x - s->tx) - (s->x & ~7);
+                                cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 4);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        cx |= ((s->row_h[1] >> 9) & 0x3F0);
+                    }
+                    pix = t81_t82_arith_decode(&s->s, cx);
+                    if (pix < 0)
+                        return s->s.pscd_ptr - data;
+                    s->row_h[0] = (s->row_h[0] << 1) | pix;
+                    s->row_h[1] <<= 1;
+                }
+                while ((++s->x & 7)  &&  s->x < s->xd);
+            }
+            else
+            {
+                /* Three row template */
+                do
+                {
+                    cx = ((s->row_h[2] >> 7) & 0x380) | (s->row_h[0] & 0x003);
+                    if (s->tx)
+                    {
+                        cx |= ((s->row_h[1] >> 11) & 0x078);
+                        if (s->x >= (uint32_t) s->tx)
+                        {
+                            if (s->tx < 8)
+                            {
+                                cx |= ((s->row_h[0] >> (s->tx - 3)) & 0x004);
+                            }
+                            else
+                            {
+                                o = (s->x - s->tx) - (s->x & ~7);
+                                cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 2);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        cx |= ((s->row_h[1] >> 11) & 0x07C);
+                    }
+                    pix = t81_t82_arith_decode(&s->s, cx);
+                    if (pix < 0)
+                        return s->s.pscd_ptr - data;
+                    s->row_h[0] = (s->row_h[0] << 1) | pix;
+                    s->row_h[1] <<= 1;
+                    s->row_h[2] <<= 1;
+                }
+                while ((++s->x & 7)  &&  s->x < s->xd);
+            }
+            *hp[0]++ = s->row_h[0];
+            hp[1]++;
+            hp[2]++;
+        }
+        *(hp[0] - 1) <<= (s->bytes_per_row*8 - s->xd);
+        s->interrupt = s->row_write_handler(s->row_write_user_data, &s->row_buf[s->p[0]*s->bytes_per_row], s->bytes_per_row);
+        s->x = 0;
+        s->pseudo = TRUE;
+        /* Shuffle the row buffers */
+        s->p[2] = s->p[1];
+        s->p[1] = s->p[0];
+        if (++(s->p[0]) >= buffered_rows)
+            s->p[0] = 0;
+    }
+    return s->s.pscd_ptr - data;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int finish_sde(t85_decode_state_t *s)
+{
+    /* Decode final pixels based on trailing zero bytes */
+    s->s.nopadding = FALSE;
+    if (decode_pscd(s, s->buffer, 2) != 2  &&  s->interrupt)
+        return 1;
+
+    /* Prepare decoder for next SDE */
+    t81_t82_arith_decode_restart(&s->s, s->buffer[1] == T82_SDNORM);
+    s->s.nopadding = s->options & T85_VLENGTH;
+
+    s->x = 0;
+    s->i = 0;
+    s->pseudo = TRUE;
+    s->at_moves = 0;
+    if (s->buffer[1] == T82_SDRST)
+    {
+        s->tx = 0;
+        s->lntp = TRUE;
+        s->p[0] = 0;
+        s->p[1] = -1;
+        s->p[2] = -1;
+    }
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int check_bih(t85_decode_state_t *s)
+{
+    /* Check that the fixed parameters have the values they are expected to be
+       fixed at - see T.85/Table 1 */
+    /* DL - Initial layer to be transmitted */
+    /* D - Number of differential layers */
+    /* Unspecified byte */
+    /* MY - Maximum vertical offset allowed for AT pixel */
+    /* Order byte */
+    if (s->buffer[0] != 0
+        ||
+        s->buffer[1] != 0
+        ||
+        s->buffer[3] != 0
+        ||
+        s->buffer[17] != 0
+        ||
+#if T85_STRICT_ORDER_BITS
+        s->buffer[18] != 0)
+#else
+        (s->buffer[18] & 0xF0) != 0)
+#endif
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Fixed bytes do not contain expected values.\n");
+        return T85_INVALID_DATA;
+    }
+    /* P - Number of bit planes */
+    if (s->buffer[2] < s->min_bit_planes  ||  s->buffer[2] > s->max_bit_planes)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. %d bit planes. Should be %d to %d.\n", s->buffer[2], s->min_bit_planes, s->max_bit_planes);
+        return T85_INVALID_DATA;
+    }
+    s->bit_planes = s->buffer[2];
+    s->current_bit_plane = 0;
+    /* Now look at the stuff which actually counts in a T.85 header. */
+    /* XD - Horizontal image size at layer D */
+    s->xd = pack_32(&s->buffer[4]);
+    if (s->xd == 0  ||  (s->max_xd  &&  s->xd > s->max_xd))
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Width is %" PRIu32 "\n", s->xd);
+        return T85_INVALID_DATA;
+    }
+    /* YD - Vertical image size at layer D */
+    s->yd = pack_32(&s->buffer[8]);
+    if (s->yd == 0  ||  (s->max_yd  &&  s->yd > s->max_yd))
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Length is %" PRIu32 "\n", s->yd);
+        return T85_INVALID_DATA;
+    }
+    /* L0 - Rows per stripe, at the lowest resolution */
+    s->l0 = pack_32(&s->buffer[12]);
+    if (s->l0 == 0)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. L0 is %" PRIu32 "\n", s->l0);
+        return T85_INVALID_DATA;
+    }
+    /* MX - Maximum horizontal offset allowed for AT pixel */
+    s->mx = s->buffer[16];
+    if (s->mx > 127)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. MX is %d\n", s->mx);
+        return T85_INVALID_DATA;
+    }
+    /* Options byte */
+    s->options = s->buffer[19];
+    if ((s->options & 0x97))
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Options are 0x%X\n", s->options);
+        return T85_INVALID_DATA;
+    }
+    span_log(&s->logging, SPAN_LOG_FLOW, "BIH is OK. Image is %" PRIu32 "x%" PRIu32 " pixels\n", s->xd, s->yd);
+    return T85_OK;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t85_decode_rx_status(t85_decode_state_t *s, int status)
+{
+    span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status);
+    switch (status)
+    {
+    case SIG_STATUS_TRAINING_IN_PROGRESS:
+    case SIG_STATUS_TRAINING_FAILED:
+    case SIG_STATUS_TRAINING_SUCCEEDED:
+    case SIG_STATUS_CARRIER_UP:
+        /* Ignore these */
+        break;
+    case SIG_STATUS_CARRIER_DOWN:
+    case SIG_STATUS_END_OF_DATA:
+        /* Finalise the image */
+        s->end_of_data = 1;
+        t85_decode_put_chunk(s, NULL, 0);
+        break;
+    default:
+        span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status);
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_put_byte(t85_decode_state_t *s, int byte)
+{
+    uint8_t data[1];
+
+    if (byte < 0)
+    {
+        t85_decode_rx_status(s, byte);
+        return (s->y >= s->yd)  ?  T85_OK  :  T85_MORE_DATA;
+    }
+    data[0] = byte;
+    return t85_decode_put_chunk(s, data, 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_put_chunk(t85_decode_state_t *s,
+                                       const uint8_t data[],
+                                       size_t len)
+{
+    int ret;
+    uint32_t y;
+    uint8_t *buf;
+    size_t bytes_per_row;
+    size_t min_len;
+    size_t chunk;
+    size_t cnt;
+    int i;
+
+    s->compressed_image_size += len;
+    cnt = 0;
+
+    if (s->bie_len < 20)
+    {
+        /* Read in the 20-byte BIH */
+        i = (s->bie_len + len > 20)  ?  (20 - s->bie_len)  :  len;
+        memcpy(&s->buffer[s->bie_len], data, i);
+        s->bie_len += i;
+        cnt = i;
+        if (s->bie_len < 20)
+            return T85_MORE_DATA;
+        if ((ret = check_bih(s)) != T85_OK)
+            return ret;
+        /* Set up the two/three row buffer */
+        bytes_per_row = (s->xd + 7) >> 3;
+        min_len = ((s->options & T85_LRLTWO)  ?  2  :  3)*bytes_per_row;
+        if (min_len > s->row_buf_len)
+        {
+            /* We need to expand the 3 row buffer */
+            if ((buf = (uint8_t *) realloc(s->row_buf, min_len)) == NULL)
+                return T85_NOMEM;
+            s->row_buf = buf;
+            s->row_buf_len = min_len;
+        }
+
+        t81_t82_arith_decode_init(&s->s);
+        s->s.nopadding = s->options & T85_VLENGTH;
+        if (s->comment)
+        {
+            free(s->comment);
+            s->comment = NULL;
+        }
+        s->comment_len = 0;
+        s->comment_progress = 0;
+        s->buf_len = 0;
+        s->buf_needed = 2;
+        s->x = 0;
+        s->y = 0;
+        s->i = 0;
+        s->pseudo = TRUE;
+        s->at_moves = 0;
+        s->tx = 0;
+        s->lntp = TRUE;
+        s->bytes_per_row = bytes_per_row;
+        s->p[0] = 0;
+        s->p[1] = -1;
+        s->p[2] = -1;
+    }
+
+    /* BID processing loop */
+    while (cnt < len  ||  s->end_of_data == 1)
+    {
+        if (s->end_of_data == 1)
+        {
+            s->buf_needed = 2;
+            s->options &= ~T85_VLENGTH;
+            s->end_of_data = 2;
+        }
+        if (s->comment_len)
+        {
+            /* We are in a COMMENT. Absorb its contents */
+            chunk = len - cnt;
+            if ((s->comment_progress + chunk) >= s->comment_len)
+            {
+                /* Finished */
+                chunk = s->comment_len - s->comment_progress;
+                /* If the comment was too long to be passed to the handler, we still
+                   call the handler with the buffer set to NULL, so it knows a large
+                   comment has occurred. */
+                if (s->comment)
+                    memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
+                if (s->comment_handler)
+                    s->interrupt = s->comment_handler(s->comment_user_data, s->comment, s->comment_len);
+                if (s->comment)
+                {
+                    free(s->comment);
+                    s->comment = NULL;
+                }
+                s->comment_len = 0;
+                s->comment_progress = 0;
+            }
+            else
+            {
+                if (s->comment)
+                    memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
+                s->comment_progress += chunk;
+            }
+            cnt += chunk;
+            continue;
+        }
+
+        /* Load marker segments into s->buffer for processing */
+        if (s->buf_len > 0)
+        {
+            /* We are in a marker of some kind. Load the first 2 bytes of
+               the marker, so we can determine its type, and hence its full
+               length. */
+            while (s->buf_len < s->buf_needed  &&  cnt < len)
+                s->buffer[s->buf_len++] = data[cnt++];
+            /* Check we have enough bytes to see the message type */
+            if (s->buf_len < s->buf_needed)
+                continue;
+            switch (s->buffer[1])
+            {
+            case T82_STUFF:
+                /* Forward stuffed 0xFF to arithmetic decoder. This is likely to be
+                   the commonest thing for us to hit here. */
+                decode_pscd(s, s->buffer, 2);
+                s->buf_len = 0;
+
+                if (s->interrupt)
+                    return T85_INTERRUPT;
+                break;
+            case T82_ABORT:
+                s->buf_len = 0;
+                return T85_ABORTED;
+            case T82_COMMENT:
+                s->buf_needed = 6;
+                if (s->buf_len < 6)
+                    continue;
+                s->buf_needed = 2;
+                s->buf_len = 0;
+
+                s->comment_len = pack_32(&s->buffer[2]);
+                /* Only try to buffer and process the comment's contents if we have
+                   a defined callback routine to do something with it. */
+                /* If this malloc fails we carry on working just fine, and don't try to
+                   process the contents of the comment. That is fairly benign, as
+                   the comments are not generally of critical importance, so let's
+                   not worry. */
+                if (s->comment_handler  &&  s->comment_len > 0  &&  s->comment_len <= s->max_comment_len)
+                    s->comment = malloc(s->comment_len);
+                s->comment_progress = 0;
+                continue;
+            case T82_ATMOVE:
+                s->buf_needed = 8;
+                if (s->buf_len < 8)
+                    continue;
+                s->buf_needed = 2;
+                s->buf_len = 0;
+
+                if (s->at_moves >= T85_ATMOVES_MAX)
+                    return T85_INVALID_DATA;
+                s->at_row[s->at_moves] = pack_32(&s->buffer[2]);
+                s->at_tx[s->at_moves] = s->buffer[6];
+                if (s->at_tx[s->at_moves] > s->mx
+                    ||
+                    (s->at_tx[s->at_moves] > 0  &&  s->at_tx[s->at_moves] < ((s->options & T85_LRLTWO)  ?  5  :  3))
+                    ||
+                    s->buffer[7] != 0)
+                {
+                    return T85_INVALID_DATA;
+                }
+                s->at_moves++;
+                break;
+            case T82_NEWLEN:
+                s->buf_needed = 6;
+                if (s->buf_len < 6)
+                    continue;
+                s->buf_needed = 2;
+                s->buf_len = 0;
+
+                if (!(s->options & T85_VLENGTH))
+                    return T85_INVALID_DATA;
+                s->options &= ~T85_VLENGTH;
+                y = pack_32(&s->buffer[2]);
+                /* An update to the image length is not allowed to stretch it. */
+                if (y > s->yd)
+                    return T85_INVALID_DATA;
+                s->yd = y;
+                break;
+            case T82_SDNORM:
+            case T82_SDRST:
+                if (!(s->options & T85_VLENGTH))
+                {
+                    /* A plain SDNORM or SDRST with no peek ahead required */
+                    s->buf_len = 0;
+                    if (finish_sde(s))
+                        return T85_INTERRUPT;
+                    /* Check whether this was the last SDE */
+                    if (s->y >= s->yd)
+                    {
+                        s->compressed_image_size -= (len - cnt);
+                        return T85_OK;
+                    }
+                    break;
+                }
+                /* This is the messy case. We need to peek ahead, as this element
+                   might be immediately followed by a T82_NEWLEN which affects the
+                   limit of what we decode here. */
+                if (s->buf_needed < 3)
+                    s->buf_needed = 3;
+                if (s->buf_len < 3)
+                    continue;
+                /* Peek ahead to see whether a NEWLEN marker segment follows */
+                if (s->buffer[2] != T82_ESC)
+                {
+                    /* This is not an escape sequence, so push the single peek-ahead
+                       byte back into the buffer. We should always have just grabbed
+                       at least one byte, so this should be safe. */
+                    s->buf_needed = 2;
+                    s->buf_len = 0;
+                    cnt--;
+                    /* Process the T82_SDNORM or T82_SDRST */
+                    if (finish_sde(s))
+                        return T85_INTERRUPT;
+                    /* Check whether this was the last SDE */
+                    if (s->y >= s->yd)
+                    {
+                        s->compressed_image_size -= (len - cnt);
+                        return T85_OK;
+                    }
+                    break;
+                }
+                if (s->buf_needed < 4)
+                    s->buf_needed = 4;
+                if (s->buf_len < 4)
+                    continue;
+                if (s->buffer[3] != T82_NEWLEN)
+                {
+                    s->buf_needed = 2;
+
+                    /* Process the T82_SDNORM or T82_SDRST */
+                    if (finish_sde(s))
+                        return T85_INTERRUPT;
+                    /* Check whether this was the last SDE */
+                    if (s->y >= s->yd)
+                    {
+                        s->compressed_image_size -= (len - cnt);
+                        return T85_OK;
+                    }
+                    /* Recycle the two peek-ahead marker sequence bytes to
+                       be processed later. */
+                    s->buffer[0] = s->buffer[2];
+                    s->buffer[1] = s->buffer[3];
+                    s->buf_len = 2;
+                    break;
+                }
+                if (s->buf_needed < 8)
+                    s->buf_needed = 8;
+                if (s->buf_len < 8)
+                    continue;
+                s->buf_needed = 2;
+                s->buf_len = 0;
+                /* We must have a complete T82_NEWLEN to be here, which we need
+                   to process immediately. */
+                s->options &= ~T85_VLENGTH;
+                y = pack_32(&s->buffer[4]);
+                /* An update to the image length is not allowed to stretch it. */
+                if (y > s->yd)
+                    return T85_INVALID_DATA;
+                /* Things look OK, so accept this new length, and proceed. */
+                s->yd = y;
+                /* Now process the T82_SDNORM or T82_SDRST */
+                if (finish_sde(s))
+                    return T85_INTERRUPT;
+                /* We might be at the end of the image now, but even if we are
+                   there should still be a final training T82_SDNORM or T82_SDRST
+                   that we should pick up. When we do, we won't wait for further
+                   T82_NEWLEN entries, so we should stop crisply on the last byte
+                   of the image. */
+                break;
+            default:
+                s->buf_len = 0;
+                return T85_INVALID_DATA;
+            }
+        }
+        else if (cnt < len  &&  data[cnt] == T82_ESC)
+        {
+            s->buffer[s->buf_len++] = data[cnt++];
+        }
+        else
+        {
+            /* We have found PSCD bytes */
+            cnt += decode_pscd(s, data + cnt, len - cnt);
+            if (s->interrupt)
+                return T85_INTERRUPT;
+            /* We should only have stopped processing PSCD if
+               we ran out of data, or hit a T82_ESC */
+            if (cnt < len  &&  data[cnt] != T82_ESC)
+                return T85_INVALID_DATA;
+        }
+    }
+
+    return T85_MORE_DATA;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_set_row_write_handler(t85_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data)
+{
+    s->row_write_handler = handler;
+    s->row_write_user_data = user_data;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_set_comment_handler(t85_decode_state_t *s,
+                                                 uint32_t max_comment_len,
+                                                 t4_row_write_handler_t handler,
+                                                 void *user_data)
+{
+    s->max_comment_len = max_comment_len;
+    s->comment_handler = handler;
+    s->comment_user_data = user_data;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_set_image_size_constraints(t85_decode_state_t *s,
+                                                        uint32_t max_xd,
+                                                        uint32_t max_yd)
+{
+    s->max_xd = max_xd;
+    s->max_yd = max_yd;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t85_decode_get_image_width(t85_decode_state_t *s)
+{
+    return s->xd;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t85_decode_get_image_length(t85_decode_state_t *s)
+{
+    return s->yd;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_get_compressed_image_size(t85_decode_state_t *s)
+{
+    return s->compressed_image_size*8;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s)
+{
+    if (s->current_bit_plane >= s->bit_planes - 1)
+        return -1;
+    
+    s->current_bit_plane++;
+    s->tx = 0;
+    memset(s->buffer, 0, sizeof(s->buffer));
+    s->buf_len = 0;
+    s->buf_needed = 0;
+    s->at_moves = 0;
+    memset(s->at_row, 0, sizeof(s->at_row));
+    memset(s->at_tx, 0, sizeof(s->at_tx));
+    memset(s->row_h, 0, sizeof(s->row_h));
+    s->pseudo = FALSE;
+    s->lntp = FALSE;
+    s->interrupt = FALSE;
+    s->end_of_data = 0;
+    if (s->comment)
+    {
+        free(s->comment);
+        s->comment = NULL;
+    }
+    s->comment_len = 0;
+    s->comment_progress = 0;
+    s->compressed_image_size = 0;
+
+    t81_t82_arith_decode_restart(&s->s, FALSE);
+    s->s.nopadding = s->options & T85_VLENGTH;
+
+    s->buf_len = 0;
+    s->buf_needed = 2;
+    s->x = 0;
+    s->y = 0;
+    s->i = 0;
+    s->pseudo = TRUE;
+    s->at_moves = 0;
+    s->tx = 0;
+    s->lntp = TRUE;
+    s->p[0] = 0;
+    s->p[1] = -1;
+    s->p[2] = -1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s)
+{
+    s->xd = 0;
+    s->yd = 0;
+    s->l0 = 0;
+    s->mx = 0;
+    s->bytes_per_row = 0;
+    s->tx = 0;
+    s->bie_len = 0;
+    memset(s->buffer, 0, sizeof(s->buffer));
+    s->buf_len = 0;
+    s->buf_needed = 0;
+    s->at_moves = 0;
+    memset(s->at_row, 0, sizeof(s->at_row));
+    memset(s->at_tx, 0, sizeof(s->at_tx));
+    memset(s->row_h, 0, sizeof(s->row_h));
+    s->pseudo = FALSE;
+    s->lntp = FALSE;
+    s->interrupt = FALSE;
+    s->end_of_data = 0;
+    if (s->comment)
+    {
+        free(s->comment);
+        s->comment = NULL;
+    }
+    s->comment_len = 0;
+    s->comment_progress = 0;
+    s->compressed_image_size = 0;
+
+    t81_t82_arith_decode_restart(&s->s, FALSE);
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (t85_decode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.85");
+
+    s->row_write_handler = handler;
+    s->row_write_user_data = user_data;
+
+    s->min_bit_planes = 1;
+    s->max_bit_planes = 1;
+
+    s->max_xd = 0;
+    s->max_yd = 0;
+
+    t81_t82_arith_decode_init(&s->s);
+    t85_decode_restart(s);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s)
+{
+    if (s->row_buf)
+    {
+        free(s->row_buf);
+        s->row_buf = NULL;
+    }
+    if (s->comment)
+    {
+        free(s->comment);
+        s->comment = NULL;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s)
+{
+    int ret;
+
+    ret = t85_decode_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/t85_encode.c b/libs/spandsp/src/t85_encode.c
new file mode 100644
index 0000000000..6bcb3441f5
--- /dev/null
+++ b/libs/spandsp/src/t85_encode.c
@@ -0,0 +1,747 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t85_encode.c - ITU T.85 JBIG for FAX image compression
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009, 2010 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/timezone.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#include "spandsp/t81_t82_arith_coding.h"
+#include "spandsp/t85.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/t81_t82_arith_coding.h"
+#include "spandsp/private/t85.h"
+
+/* Image length update status */
+enum
+{
+    NEWLEN_NONE = 0,
+    NEWLEN_PENDING = 1,
+    NEWLEN_HANDLED = 2
+};
+
+static __inline__ void unpack_32(uint8_t *s, int32_t value)
+{
+    s[3] = value & 0xFF;
+    value >>= 8;
+    s[2] = value & 0xFF;
+    value >>= 8;
+    s[1] = value & 0xFF;
+    value >>= 8;
+    s[0] = value & 0xFF;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void put_stuff(t85_encode_state_t *s, const uint8_t buf[], int len)
+{
+    uint8_t *new_buf;
+    uint32_t bytes_per_row;
+
+    if (s->bitstream_iptr + len >= s->bitstream_len)
+    {
+        /* TODO: Handle memory allocation errors properly */
+        /* The number of uncompressed bytes per row seems like a reasonable measure
+           of what to expect as a poor case for a compressed row. */
+        bytes_per_row = (s->xd + 7) >> 3;
+        if ((new_buf = realloc(s->bitstream, s->bitstream_len + len + bytes_per_row)) == NULL)
+            return;
+        s->bitstream = new_buf;
+        s->bitstream_len += (len + bytes_per_row);
+    }
+    memcpy(&s->bitstream[s->bitstream_iptr], buf, len);
+    s->bitstream_iptr += len;
+    s->compressed_image_size += len;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Callback function for the arithmetic encoder */
+static void output_byte(void *user_data, int byte)
+{
+    t85_encode_state_t *s;
+    uint8_t c = byte;
+
+    s = (t85_encode_state_t *) user_data;
+    c = (uint8_t) byte;
+    put_stuff(s, &c, 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void output_esc_code(t85_encode_state_t *s, int code)
+{
+    uint8_t buf[2];
+
+    buf[0] = T82_ESC;
+    buf[1] = code;
+    put_stuff(s, buf, 2);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void output_newlen(t85_encode_state_t *s)
+{
+    uint8_t buf[6];
+
+    if (s->newlen == NEWLEN_PENDING)
+    {
+        buf[0] = T82_ESC;
+        buf[1] = T82_NEWLEN;
+        unpack_32(&buf[2], s->yd);
+        put_stuff(s, buf, 6);
+        if (s->y == s->yd)
+        {
+            /* See T.82/6.2.6.2 */
+            output_esc_code(s, T82_SDNORM);
+        }
+        s->newlen = NEWLEN_HANDLED;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void output_comment(t85_encode_state_t *s)
+{
+    uint8_t buf[6];
+
+    if (s->comment)
+    {
+        buf[0] = T82_ESC;
+        buf[1] = T82_COMMENT;
+        unpack_32(&buf[2], s->comment_len);
+        put_stuff(s, buf, 6);
+        put_stuff(s, s->comment, s->comment_len);
+        s->comment = NULL;
+        s->comment_len = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void output_atmove(t85_encode_state_t *s)
+{
+    uint8_t buf[8];
+
+    if (s->new_tx >= 0  &&  s->new_tx != s->tx)
+    {
+        s->tx = s->new_tx;
+        buf[0] = T82_ESC;
+        buf[1] = T82_ATMOVE;
+        unpack_32(&buf[2], 0);
+        buf[6] = s->tx;
+        buf[7] = 0;
+        put_stuff(s, buf, 8);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void generate_bih(t85_encode_state_t *s, uint8_t *buf)
+{
+    /* DL - Initial layer to be transmitted */
+    buf[0] = 0;
+    /* D - Number of differential layers */
+    buf[1] = 0;
+    /* P - Number of bit planes */
+    buf[2] = s->bit_planes;
+    /* Unspecified */
+    buf[3] = 0;
+    /* XD - Horizontal image size at layer D */
+    unpack_32(&buf[4], s->xd);
+    /* YD - Vertical image size at layer D */
+    unpack_32(&buf[8], s->yd);
+    /* L0 - Rows per stripe, at the lowest resolution */
+    unpack_32(&buf[12], s->l0);
+    /* MX - Maximum horizontal offset allowed for AT pixel */
+    buf[16] = s->mx;
+    /* MY - Maximum vertical offset allowed for AT pixel */
+    buf[17] = 0;
+    /* Order byte: */
+    /*  4 unused bits */
+    /*  HITOLO - transmission order of differential layers */
+    /*  SEQ - indication of progressive-compatible sequential coding */
+    /*  ILEAVE - interleaved transmission order of multiple bit plane */
+    /*  SMID - transmission order of stripes */
+    /* Note that none of these are relevant to T.85 */
+    buf[18] = 0;
+    /* Options byte: */
+    /*  1 unused bit */
+    /*  LRLTWO - number of reference rows */
+    /*  VLENGTH - indication of possible use of NEWLEN marker segment */
+    /*  TPDON - use of TP for Typical Prediction for differential layers */
+    /*  TPBON - use of TP for base layer */
+    /*  DPON - use of Deterministic Prediction */
+    /*  DPPRIV - use of private DP table */
+    /*  DPLAST - use of last DP table */
+    /* Note that only T85_TPBON, T85_VLENGTH, and T85_LRLTWO are relevant to T.85 */
+    buf[19] = s->options;
+}
+/*- End of function --------------------------------------------------------*/
+    
+SPAN_DECLARE(void) t85_encode_set_options(t85_encode_state_t *s,
+                                          uint32_t l0,
+                                          int mx,
+                                          int options)
+{
+    if (s->y > 0)
+        return;
+
+    /* Its still OK to change things */
+    if (l0 >= 1  &&  l0 <= s->yd)
+        s->l0 = l0;
+    if (mx >= 0  &&  mx <= 127)
+        s->mx = mx;
+    if (options >= 0)
+        s->options = options & (T85_TPBON | T85_VLENGTH | T85_LRLTWO);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int get_next_row(t85_encode_state_t *s)
+{
+    uint8_t buf[20];
+    uint32_t bytes_per_row;
+    const uint8_t *hp[3];
+    uint8_t *z;
+    uint32_t row_h[3];
+    uint32_t j;
+    int32_t o;
+    uint32_t a;
+    uint32_t p;
+    uint32_t t;
+    uint32_t c_min;
+    uint32_t c_max;
+    uint32_t cl_min;
+    uint32_t cl_max;
+    int ltp;
+    int cx;
+    int t_max;
+    int i;
+
+    if (s->y >= s->yd)
+    {
+        /* We have already finished pumping out the image */
+        return -1;
+    }
+
+    s->bitstream_iptr = 0;
+    s->bitstream_optr = 0;
+    bytes_per_row = (s->xd + 7) >> 3;
+    /* Rotate the three rows which we buffer */
+    z = s->prev_row[2];
+    s->prev_row[2] = s->prev_row[1];
+    s->prev_row[1] = s->prev_row[0];
+    s->prev_row[0] = z;
+
+    /* Copy the new row to our buffer, and ensure the last byte of the row is
+       zero padded. The need to tweak this is actually the only reason for
+       storing a third row. We do not want to tamper with the source buffer. */
+    if (s->fill_with_white)
+    {
+        memset(s->prev_row[0], 0, bytes_per_row);
+    }
+    else
+    {
+        if (s->row_read_handler(s->row_read_user_data, s->prev_row[0], bytes_per_row) <= 0)
+        {
+            /* The source has stopped feeding us rows early. Try to clip the image
+               to the current size. */
+            if (t85_encode_set_image_length(s, 1) == 0)
+                return 0;
+            /* We can't clip the image to the current length. We will have to
+               continue up to the original length with blank (all white) rows. */
+            s->fill_with_white = TRUE;
+            memset(s->prev_row[0], 0, bytes_per_row);
+        }
+    }
+    if ((s->xd & 7))
+        s->prev_row[0][bytes_per_row - 1] &= ~((1 << (8 - (s->xd & 7))) - 1);
+
+    if (s->current_bit_plane == 0  &&  s->y == 0)
+    {
+        /* Things that need to be done before the first row is encoded */
+        generate_bih(s, buf);
+        put_stuff(s, buf, 20);
+    }
+
+    if (s->i == 0)
+    {
+        /* Things that need to be done before the next SDE is encoded */
+        output_newlen(s);
+        output_comment(s);
+        output_atmove(s);
+        if (s->mx == 0)
+        {
+            /* Disable ATMOVE analysis */
+            s->new_tx = 0;
+        }
+        else
+        {
+            /* Enable ATMOVE analysis */
+            s->new_tx = -1;
+            s->c_all = 0;
+            for (i = 0;  i <= s->mx;  i++)
+                s->c[i] = 0;
+        }
+        t81_t82_arith_encode_restart(&s->s, TRUE);
+    }
+
+    /* Typical prediction */
+    ltp = FALSE;
+    if ((s->options & T85_TPBON))
+    {
+        /* Look for a match between the rows */
+        ltp = (memcmp(s->prev_row[0], s->prev_row[1], bytes_per_row) == 0);
+        t81_t82_arith_encode(&s->s,
+                             (s->options & T85_LRLTWO)  ?  TPB2CX  :  TPB3CX,
+                             (ltp == s->prev_ltp));
+        s->prev_ltp = ltp;
+    }
+
+    if (!ltp)
+    {
+        /* Pointer to the first image byte in each the three rows of interest */
+        hp[0] = s->prev_row[0];
+        hp[1] = s->prev_row[1];
+        hp[2] = s->prev_row[2];
+
+        row_h[0] = 0;
+        row_h[1] = (uint32_t) hp[1][0] << 8;
+        row_h[2] = (uint32_t) hp[2][0] << 8;
+
+        /* Encode row */
+        for (j = 0;  j < s->xd;  )
+        {
+            row_h[0] |= hp[0][0];
+            if (j < (bytes_per_row - 1)*8)
+            {
+                row_h[1] |= hp[1][1];
+                row_h[2] |= hp[2][1];
+            }
+            if ((s->options & T85_LRLTWO))
+            {
+                /* Two row template */
+                do
+                {
+                    row_h[0] <<= 1;
+                    row_h[1] <<= 1;
+                    row_h[2] <<= 1;
+                    cx = (row_h[0] >> 9) & 0x00F;
+                    if (s->tx)
+                    {
+                        cx |= ((row_h[1] >> 10) & 0x3E0);
+                        if (j >= (uint32_t) s->tx)
+                        {
+                            o = (j - s->tx) - (j & ~7);
+                            cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 4);
+                        }
+                    }
+                    else
+                    {
+                        cx |= ((row_h[1] >> 10) & 0x3F0);
+                    }
+                    p = (row_h[0] >> 8) & 1;
+                    t81_t82_arith_encode(&s->s, cx, p);
+
+                    /* Update the statistics for adaptive template changes,
+                       if this analysis is in progress. */
+                    if (s->new_tx < 0  &&  j >= s->mx  &&  j < s->xd - 2)
+                    {
+                        if (p == ((row_h[1] >> 14) & 1))
+                            s->c[0]++;
+                        for (t = 5;  t <= s->mx  &&  t <= j;  t++)
+                        {
+                            o = (j - t) - (j & ~7);
+                            a = (hp[0][o >> 3] >> (7 - (o & 7))) & 1;
+                            if (a == p)
+                                s->c[t]++;
+                        }
+                        if (p == 0)
+                        {
+                            for (  ;  t <= s->mx;  t++)
+                                s->c[t]++;
+                        }
+                        ++s->c_all;
+                    }
+                }
+                while ((++j & 7)  &&  j < s->xd);
+            }
+            else
+            {
+                /* Three row template */
+                do
+                {
+                    row_h[0] <<= 1;
+                    row_h[1] <<= 1;
+                    row_h[2] <<= 1;
+                    cx = ((row_h[2] >> 8) & 0x380) | ((row_h[0] >> 9) & 0x003);
+                    if (s->tx)
+                    {
+                        cx |= ((row_h[1] >> 12) & 0x078);
+                        if (j >= (uint32_t) s->tx)
+                        {
+                            o = (j - s->tx) - (j & ~7);
+                            cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 2);
+                        }
+                    }
+                    else
+                    {
+                        cx |= ((row_h[1] >> 12) & 0x07C);
+                    }
+                    p = (row_h[0] >> 8) & 1;
+                    t81_t82_arith_encode(&s->s, cx, p);
+
+                    /* Update the statistics for adaptive template changes,
+                       if this analysis is in progress. */
+                    if (s->new_tx < 0  &&  j >= s->mx  &&  j < s->xd - 2)
+                    {
+                        if (p == ((row_h[1] >> 14) & 1))
+                            s->c[0]++;
+                        for (t = 3;  t <= s->mx  &&  t <= j;  t++)
+                        {
+                            o = (j - t) - (j & ~7);
+                            a = (hp[0][o >> 3] >> (7 - (o & 7))) & 1;
+                            if (a == p)
+                                s->c[t]++;
+                        }
+                        if (p == 0)
+                        {
+                            for (  ;  t <= s->mx;  t++)
+                                s->c[t]++;
+                        }
+                        ++s->c_all;
+                    }
+                }
+                while ((++j & 7)  &&  j < s->xd);
+            }
+            hp[0]++;
+            hp[1]++;
+            hp[2]++;
+        }
+    }
+
+    s->i++;
+    s->y++;
+    if (s->i == s->l0  ||  s->y == s->yd)
+    {
+        /* We are at the end of the stripe */
+        t81_t82_arith_encode_flush(&s->s);
+        output_esc_code(s, T82_SDNORM);
+        s->i = 0;
+        output_newlen(s);
+    }
+
+    /* T.82/Annex C - is it time for an adaptive template change? */
+    if (s->new_tx < 0  &&  s->c_all > 2048)
+    {
+        c_min =
+        cl_min = UINT32_MAX;
+        c_max =
+        cl_max = 0;
+        t_max = 0;
+        for (i = (s->options & T85_LRLTWO)  ?  5  :  3;  i <= s->mx;  i++)
+        {
+            if (s->c[i] > c_max)
+                c_max = s->c[i];
+            if (s->c[i] < c_min)
+                c_min = s->c[i];
+            if (s->c[i] > s->c[t_max])
+                t_max = i;
+        }
+        cl_min = (s->c[0] < c_min)  ?  s->c[0]  :  c_min;
+        cl_max = (s->c[0] > c_max)  ?  s->c[0]  :  c_max;
+        /* This test is straight from T.82/Figure C.2 */
+        if ((s->c_all - c_max) < (s->c_all >> 3)
+            &&
+            (c_max - s->c[s->tx]) > (s->c_all - c_max)
+            &&
+            (c_max - s->c[s->tx]) > (s->c_all >> 4)
+            &&
+            (c_max - (s->c_all - s->c[s->tx])) > (s->c_all - c_max)
+            &&
+            (c_max - (s->c_all - s->c[s->tx])) > (s->c_all >> 4)
+            &&
+            (c_max - c_min) > (s->c_all >> 2)
+            &&
+            (s->tx  ||  (cl_max - cl_min) > (s->c_all >> 3)))
+        {
+            /* It is time to perform an ATMOVE */
+            s->new_tx = t_max;
+        }
+        else
+        {
+            /* Disable further analysis */
+            s->new_tx = s->tx;
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_set_image_width(t85_encode_state_t *s, uint32_t image_width)
+{
+    int bytes_per_row;
+    uint8_t *t;
+
+    if (s->xd == image_width)
+        return 0;
+    /* Are we too late to change the width for this page? */
+    if (s->y > 0)
+        return -1;
+    s->xd = image_width;
+    bytes_per_row = (s->xd + 7) >> 3;
+    if ((t = (uint8_t *) realloc(s->row_buf, 3*bytes_per_row)) == NULL)
+        return -1;
+    s->row_buf = t;
+    memset(s->row_buf, 0, 3*bytes_per_row);
+    s->prev_row[0] = s->row_buf;
+    s->prev_row[1] = s->row_buf + bytes_per_row;
+    s->prev_row[2] = s->row_buf + 2*bytes_per_row;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_set_image_length(t85_encode_state_t *s, uint32_t length)
+{
+    /* We must have variable length enabled.
+       We do not allow the length to be changed multiple times.
+       We only allow an image to be shrunk, and not stretched.
+       We do not allow the length to become zero. */
+    if (!(s->options & T85_VLENGTH)  ||  s->newlen == NEWLEN_HANDLED  ||  length >= s->yd  ||  length < 1)
+    {
+        /* Invalid parameter */
+        return -1;
+    }
+    if (s->y > 0)
+    {
+        /* TODO: If we are already beyond the new length, we scale back the new length silently.
+                 Is there any downside to this? */
+        if (length < s->y)
+            length = s->y;
+        if (s->yd != length)
+            s->newlen = NEWLEN_PENDING;
+    }
+    s->yd = length;
+    if (s->y == s->yd)
+    {
+        /* We are already at the end of the image, so finish it off. */
+        if (s->i > 0)
+        {
+            t81_t82_arith_encode_flush(&s->s);
+            output_esc_code(s, T82_SDNORM);
+            s->i = 0;
+        }
+        output_newlen(s);
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t85_encode_abort(t85_encode_state_t *s)
+{
+    output_esc_code(s, T82_ABORT);
+    /* Make the image appear to be complete, so the encoder stops outputting.
+       Take care, as this means s->y is now telling lies. */
+    s->y = s->yd;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t85_encode_comment(t85_encode_state_t *s, const uint8_t comment[], size_t len)
+{
+    s->comment = comment;
+    s->comment_len = len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_get_byte(t85_encode_state_t *s)
+{
+    if (s->bitstream_optr >= s->bitstream_iptr)
+    {
+        do
+        {
+            if (get_next_row(s) < 0)
+                return 0x100;
+        }
+        while (s->bitstream_iptr == 0);
+    }
+    return s->bitstream[s->bitstream_optr++];
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_get_chunk(t85_encode_state_t *s, uint8_t buf[], int max_len)
+{
+    int len;
+    int n;
+
+    for (len = 0;  len < max_len;  len += n)
+    {
+        if (s->bitstream_optr >= s->bitstream_iptr)
+        {
+            if (get_next_row(s) < 0)
+                return len;
+        }
+        n = s->bitstream_iptr - s->bitstream_optr;
+        if (n > max_len - len)
+            n = max_len - len;
+        memcpy(&buf[len], &s->bitstream[s->bitstream_optr], n);
+        s->bitstream_optr += n;
+    }
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t85_encode_get_image_width(t85_encode_state_t *s)
+{
+    return s->xd;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t85_encode_get_image_length(t85_encode_state_t *s)
+{
+    return s->yd;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_get_compressed_image_size(t85_encode_state_t *s)
+{
+    return s->compressed_image_size*8;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_set_row_read_handler(t85_encode_state_t *s,
+                                                  t4_row_read_handler_t handler,
+                                                  void *user_data)
+{
+    s->row_read_handler = handler;
+    s->row_read_user_data = user_data;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_restart(t85_encode_state_t *s, uint32_t image_width, uint32_t image_length)
+{
+    int bytes_per_row;
+
+    /* Allow the image width to be anything, although only a few values are actually
+       permitted by the T.85 and T.4 specs. Higher levels in the stack need to impose
+       these restrictions. */
+    t85_encode_set_image_width(s, image_width);
+    bytes_per_row = (s->xd + 7) >> 3;
+    memset(s->row_buf, 0, 3*bytes_per_row);
+    s->yd = image_length;
+
+    s->comment = NULL;
+    s->comment_len = 0;
+    s->y = 0;
+    s->i = 0;
+    s->newlen = NEWLEN_NONE;
+    s->new_tx = -1;
+    s->tx = 0;
+    s->prev_ltp = FALSE;
+    s->bitstream_iptr = 0;
+    s->bitstream_optr = 0;
+    if (s->bitstream)
+    {
+        free(s->bitstream);
+        s->bitstream = NULL;
+    }
+    s->bitstream_len = 0;
+    s->fill_with_white = FALSE;
+    s->compressed_image_size = 0;
+
+    t81_t82_arith_encode_init(&s->s, output_byte, s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t85_encode_state_t *) t85_encode_init(t85_encode_state_t *s,
+                                                   uint32_t image_width,
+                                                   uint32_t image_length,
+                                                   t4_row_read_handler_t handler,
+                                                   void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (t85_encode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.85");
+
+    s->row_read_handler = handler;
+    s->row_read_user_data = user_data;
+
+    /* T.85 BASIC setting for L0. In T.85 this may only be changed if T.30 negotiation permits. */
+    s->l0 = 128;
+    /* No ATMOVE pending */
+    s->mx = 127;
+    /* Default options */
+    s->options = T85_TPBON | T85_VLENGTH;
+
+    s->bitstream = NULL;
+    s->bitstream_len = 0;
+
+    s->bit_planes = 1;
+    s->current_bit_plane = 0;
+
+    t85_encode_restart(s, image_width, image_length);
+    
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_release(t85_encode_state_t *s)
+{
+    if (s->row_buf)
+    {
+        free(s->row_buf);
+        s->row_buf = NULL;
+    }
+    if (s->bitstream)
+    {
+        free(s->bitstream);
+        s->bitstream = NULL;
+        s->bitstream_len = 0;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t85_encode_free(t85_encode_state_t *s)
+{
+    int ret;
+
+    ret = t85_encode_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am
index c370f20a46..d4820d81d3 100644
--- a/libs/spandsp/tests/Makefile.am
+++ b/libs/spandsp/tests/Makefile.am
@@ -109,6 +109,8 @@ noinst_PROGRAMS =   ademco_contactid_tests \
                     t38_decode \
                     t38_non_ecm_buffer_tests \
                     t4_tests \
+                    t81_t82_arith_coding_tests \
+                    t85_tests \
                     time_scale_tests \
                     timezone_tests \
                     tone_detect_tests \
@@ -312,6 +314,12 @@ t38_non_ecm_buffer_tests_LDADD = $(LIBDIR) -lspandsp
 t4_tests_SOURCES = t4_tests.c
 t4_tests_LDADD = $(LIBDIR) -lspandsp
 
+t81_t82_arith_coding_tests_SOURCES = t81_t82_arith_coding_tests.c
+t81_t82_arith_coding_tests_LDADD = $(LIBDIR) -lspandsp
+
+t85_tests_SOURCES = t85_tests.c
+t85_tests_LDADD = $(LIBDIR) -lspandsp
+
 time_scale_tests_SOURCES = time_scale_tests.c
 time_scale_tests_LDADD = $(LIBDIR) -lspandsp
 
diff --git a/libs/spandsp/tests/t81_t82_arith_coding_tests.c b/libs/spandsp/tests/t81_t82_arith_coding_tests.c
new file mode 100644
index 0000000000..8d5bab5808
--- /dev/null
+++ b/libs/spandsp/tests/t81_t82_arith_coding_tests.c
@@ -0,0 +1,238 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t81_t82_arith_coding_tests.c - Tests for the ITU T.81 and T.82 arithmetic
+ *                                encoder/decoder, based on the test description
+ *                                in T.82
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/*! \page t81_t82_arith_coding_tests_page T.81 and T.82 Arithmetic encoder/decoder tests
+\section t81_t82_arith_coding_tests_pagesec_1 What does it do
+These tests exercise the arithmetic encoder and decoder for T.81 and T.82. As T.85 is based
+on T.82, this is also the arithmetic coder for T.85.
+
+These tests are based on T.82 section 7. Nothing beyond the prescibed tests is performed at
+the present time.
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#define MSG_SIZE 10000
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+uint8_t msg[MSG_SIZE];
+
+int32_t msg_len;
+
+static void write_byte(void *user_data, int byte)
+{
+    if (msg_len < MSG_SIZE)
+        msg[msg_len++] = byte;
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+    t81_t82_arith_encode_state_t *se;
+    t81_t82_arith_decode_state_t *sd;
+    int i;
+    int j;
+    int test_failed;
+    int pix;
+    const uint8_t *pp;
+    /* Test data from T.82 7.1 */
+    static const uint16_t pix_7_1[16] =
+    {
+        0x05E0, 0x0000, 0x8B00, 0x01C4, 0x1700, 0x0034, 0x7FFF, 0x1A3F,
+        0x951B, 0x05D8, 0x1D17, 0xE770, 0x0000, 0x0000, 0x0656, 0x0E6A
+    };
+    /* Test data from T.82 7.1 */
+    static const uint16_t cx_7_1[16] =
+    {
+        0x0FE0, 0x0000, 0x0F00, 0x00F0, 0xFF00, 0x0000, 0x0000, 0x0000,
+        0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+    };
+    /* Test data from T.82 7.1 - scd with stuffing and SDNORM termination */
+    static const uint8_t sde_7_1[32] =
+    {
+        0x69, 0x89, 0x99, 0x5C, 0x32, 0xEA, 0xFA, 0xA0,
+        0xD5, 0xFF, 0x00, 0x52, 0x7F, 0xFF, 0x00, 0xFF,
+        0x00, 0xFF, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x3F,
+        0xFF, 0x00, 0x2D, 0x20, 0x82, 0x91, 0xFF, 0x02
+    };
+#define SDE_7_1_LEN         30  /* Don't include the termination SDNORM */
+#define SDE_7_1_FULL_LEN    32  /* Include the termination SDNORM */
+
+    printf("T.81/T.82 arithmetic encoder tests, from ITU-T T.82\n\n");
+
+    printf("Arithmetic encoder tests from ITU-T T.82/7.1\n");
+    if ((se = t81_t82_arith_encode_init(NULL, write_byte, NULL)) == NULL)
+    {
+        fprintf(stderr, "Failed to allocate arithmetic encoder!\n");
+        exit(2);
+    }
+    msg_len = 0;
+    for (i = 0;  i < 16;  i++)
+    {
+        for (j = 0;  j < 16;  j++)
+        {
+            t81_t82_arith_encode(se,
+                                 (cx_7_1[i] >> (15 - j)) & 1,
+                                 (pix_7_1[i] >> (15 - j)) & 1);
+        }
+    }
+    t81_t82_arith_encode_flush(se);
+    if (msg_len != SDE_7_1_LEN  ||  memcmp(msg, sde_7_1, SDE_7_1_LEN))
+    {
+        printf("Encoded data:  ");
+        for (i = 0;  i < msg_len;  i++)
+            printf("%02X", msg[i]);
+        printf("\n");
+        printf("Expected data: ");
+        for (i = 0;  i < SDE_7_1_LEN;  i++)
+            printf("%02X", sde_7_1[i]);
+        printf("\n");
+        printf("Test failed\n");
+        exit(2);
+    }
+    printf("Test passed\n");
+
+    printf("Arithmetic decoder tests from ITU-T T.82/7.1\n");
+    printf("Decoding byte by byte...\n");
+    test_failed = FALSE;
+    if ((sd = t81_t82_arith_decode_init(NULL)) == NULL)
+    {
+        fprintf(stderr, "Failed to allocate arithmetic decoder!\n");
+        exit(2);
+    }
+    pp = sde_7_1;
+    sd->pscd_ptr = pp;
+    sd->pscd_end = pp + 1;
+    for (i = 0;  i < 16;  i++)
+    {
+        for (j = 0;  j < 16;  j++)
+        {
+            for (;;)
+            {
+                pix = t81_t82_arith_decode(sd, (cx_7_1[i] >> (15 - j)) & 1);
+                if ((pix >= 0  ||  sd->pscd_end >= sde_7_1 + SDE_7_1_FULL_LEN))
+                    break;
+                pp++;
+                if (sd->pscd_ptr != pp - 1)
+                    sd->pscd_ptr = pp;
+                sd->pscd_end = pp + 1;
+            }
+            if (pix < 0)
+            {
+                printf("Bad pixel %d, byte %" PRIdPTR ".\n\n",
+                       i*16 + j + 1,
+                       sd->pscd_ptr - sd->pscd_end);
+                test_failed = TRUE;
+                break;
+            }
+            if (pix != ((pix_7_1[i] >> (15 - j)) & 1))
+            {
+                printf("Bad PIX (%d) at pixel %d.\n\n",
+                       pix,
+                       i*16 + j + 1);
+                test_failed = TRUE;
+                break;
+            }
+        }
+    }
+    if (sd->pscd_ptr != sd->pscd_end - 2)
+    {
+        printf("%" PRIdPTR " bytes left after decoder finished.\n\n",
+               sd->pscd_end - sd->pscd_ptr - 2);
+        test_failed = TRUE;
+    }
+    if (test_failed)
+    {
+        printf("Test failed\n");
+        exit(2);
+    }
+    printf("Test passed\n");
+
+    printf("Decoding chunk by chunk...\n");
+    test_failed = FALSE;
+    t81_t82_arith_decode_init(sd);
+    sd->pscd_ptr = sde_7_1;
+    sd->pscd_end = sde_7_1 + SDE_7_1_FULL_LEN;
+    for (i = 0;  i < 16;  i++)
+    {
+        for (j = 0;  j < 16;  j++)
+        {
+            pix = t81_t82_arith_decode(sd, (cx_7_1[i] >> (15 - j)) & 1);
+            if (pix < 0)
+            {
+                printf("Bad pixel %d, byte %" PRIdPTR ".\n\n",
+                       i*16 + j + 1,
+                       sd->pscd_ptr - sd->pscd_end);
+                test_failed = TRUE;
+                break;
+            }
+            if (pix != ((pix_7_1[i] >> (15 - j)) & 1))
+            {
+                printf("Bad PIX (%d) at pixel %d.\n\n",
+                       pix,
+                       i*16 + j + 1);
+                test_failed = TRUE;
+                break;
+            }
+        }
+    }
+    if (sd->pscd_ptr != sd->pscd_end - 2)
+    {
+        printf("%" PRIdPTR " bytes left after decoder finished.\n\n",
+               sd->pscd_end - sd->pscd_ptr - 2);
+        test_failed = TRUE;
+    }
+    if (test_failed)
+    {
+        printf("Test failed\n");
+        exit(2);
+    }
+    printf("Test passed\n");
+
+    printf("Tests passed\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/t85_tests.c b/libs/spandsp/tests/t85_tests.c
new file mode 100644
index 0000000000..832ee816fc
--- /dev/null
+++ b/libs/spandsp/tests/t85_tests.c
@@ -0,0 +1,369 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t85_tests.c - ITU T.85 FAX image compression and decompression tests
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * These tests are based on code from Markus Kuhn's jbigkit. See
+ * http://www.cl.cam.ac.uk/~mgk25/
+ *
+ * jbigkit is GPL2 licenced. This file is also GPL2 licenced, and our
+ * T.85 code is LGPL2.1 licenced. There are no licence incompatibilities
+ * in this reuse of Markus's work.
+ */
+
+/*! \file */
+
+/*! \page t85_tests_page T.85 image compress and decompression tests
+\section t85_tests_page_sec_1 What does it do
+These tests exercise the image compression and decompression methods defined
+in ITU specifications T.85.
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#define TESTBUF_SIZE        400000
+#define TEST_IMAGE_SIZE     (1951*1960/8)
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+uint8_t testbuf[TESTBUF_SIZE];
+uint8_t test_image[TEST_IMAGE_SIZE];
+
+size_t testbuf_len;
+
+int read_row = 0;
+int write_row = 0;
+
+int clip_to_row = 0;
+
+static int row_read_handler(void *user_data, uint8_t buf[], size_t len)
+{
+    //t85_encode_state_t *s;
+    
+    //s = (t85_encode_state_t *) user_data;
+
+    memcpy(buf, &test_image[len*read_row], len);
+    //printf("Read row %d\n", read_row);
+    if (clip_to_row  &&  read_row == clip_to_row)
+    {
+        clip_to_row = 0;
+        return 0;
+    }
+    read_row++;
+    return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int row_write_handler(void *user_data, const uint8_t buf[], size_t len)
+{
+    uint8_t *bitmap;
+
+    bitmap = (uint8_t *) user_data;
+    memcpy(&bitmap[len*write_row], buf, len);
+    //printf("Write row %d\n", write_row);
+    write_row++;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int comment_handler(void *user_data, const uint8_t buf[], size_t len)
+{
+    if (buf)
+        printf("Comment (%lu): %s\n", (unsigned long int) len, buf);
+    else
+        printf("Comment (%lu): ---\n", (unsigned long int) len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void create_test_image(uint8_t *pic)
+{
+    int i;
+    int j;
+    uint32_t sum;
+    uint32_t prsg;
+    uint32_t repeat[8];
+    uint8_t *p;
+
+    /* Cook up the test image defined in T.82/7.2.1. This image is 1960 x 1951
+       pixels, and occupies a single plane (which it has to for T.85). */
+    memset(pic, 0, TEST_IMAGE_SIZE);
+    p = pic;
+    prsg = 1;
+    for (i = 0;  i < 1951;  i++)
+    {
+        for (j = 0;  j < 1960;  j++)
+        {
+            if (i >= 192)
+            {
+                if (i < 1023  ||  (j & (3 << 3)) == 0)
+                {
+                    sum = (prsg & 1)
+                        + ((prsg >> 2) & 1)
+                        + ((prsg >> 11) & 1)
+                        + ((prsg >> 15) & 1);
+                    prsg = (prsg << 1) + (sum & 1);
+                    if ((prsg & 3) == 0)
+                    {
+                        *p |= (1 << (7 - (j & 7)));
+                        repeat[j & 7] = 1;
+                    }
+                    else
+                    {
+                        repeat[j & 7] = 0;
+                    }
+                }
+                else
+                {
+                    if (repeat[j & 7])
+                        *p |= 1 << (7 - (j & 7));
+                }
+            }
+            if ((j & 7) == 7)
+                ++p;
+        }
+    }
+
+    /* Verify the test image has been generated OK, by checking the number of set pixels */
+    sum = 0;
+    for (i = 0;  i < TEST_IMAGE_SIZE;  i++)
+    {
+        for (j = 0;  j < 8;  j++)
+            sum += ((pic[i] >> j) & 1);
+    }
+    if (sum != 861965)
+    {
+        printf("WARNING: Test image has %" PRIu32 " foreground pixels. There should be 861965.\n",
+               sum);
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Perform a test cycle, as defined in T.82/7, with one set of parameters. */
+static int test_cycle(const char *test_id,
+                      const uint8_t *image,
+                      uint32_t width,
+                      uint32_t height,
+                      uint32_t l0,
+                      int mx,
+                      int options,
+                      int optionsx,
+                      const uint8_t *comment,
+                      size_t correct_length)
+{
+    t85_encode_state_t t85_enc;
+    t85_decode_state_t t85_dec;
+    long int l;
+    size_t image_size;
+    int result;
+    int len;
+    int max_len;
+    size_t bytes_per_row;
+    size_t cnt_a;
+    size_t cnt_b;
+    uint8_t *decoded_image;
+
+    printf("%s: TPBON=%d, LRLTWO=%d, Mx=%d, L0=%" PRIu32 "\n",
+           test_id,
+           (options & T85_TPBON)  ?  1  :  0,
+           (options & T85_LRLTWO)  ?  1  :  0,
+           mx,
+           l0);
+
+    printf("%s.1: Encode\n", test_id);
+    bytes_per_row = (width + 7)/8;
+    image_size = bytes_per_row*height;
+
+    if ((optionsx & T85_VLENGTH))
+    {
+        t85_encode_init(&t85_enc, width, height + 10, row_read_handler, &t85_enc);
+        clip_to_row = height;
+    }
+    else
+    {
+        t85_encode_init(&t85_enc, width, height, row_read_handler, &t85_enc);
+        clip_to_row = 0;
+    }
+    read_row = 0;
+    t85_encode_set_options(&t85_enc, l0, mx, options);
+    /* A comment inserted here should always succeed. The later one, inserted some way
+       down the image, will only succeed if a new chunk is started afterwards. */
+    if (comment)
+        t85_encode_comment(&t85_enc, comment, strlen((const char *) comment) + 1);
+    
+    testbuf_len = 0;
+    max_len = 100;
+    while ((len = t85_encode_get_chunk(&t85_enc, &testbuf[testbuf_len], max_len)) > 0)
+    {
+        testbuf_len += len;
+        max_len = 100;
+        if (testbuf_len + 100 > TESTBUF_SIZE)
+            max_len = TESTBUF_SIZE - testbuf_len;
+        if (comment  &&  testbuf_len == 1000)
+            t85_encode_comment(&t85_enc, comment, strlen((const char *) comment) + 1);
+    }
+    t85_encode_release(&t85_enc);
+    printf("Encoded BIE has %lu bytes\n", (unsigned long int) testbuf_len);
+    if (correct_length > 0)
+    {
+        if (testbuf_len != correct_length)
+        {
+            printf("Incorrect encoded length. Should have been %lu\n", (unsigned long int) correct_length);
+            printf("Test failed\n");
+            exit(2);
+        }
+        printf("Test passed\n");
+    }
+
+    printf("%s.2: Decode in one big chunk\n", test_id);
+    if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
+    {
+        fprintf(stderr, "Out of memory!\n");
+        exit(2);
+    }
+    t85_decode_init(&t85_dec, row_write_handler, decoded_image);
+    if (comment  &&  comment[0] != 'X')
+        t85_decode_set_comment_handler(&t85_dec, 1000, comment_handler, NULL);
+    write_row = 0;
+    result = t85_decode_put_chunk(&t85_dec, testbuf, testbuf_len);
+    if (result == T85_MORE_DATA)
+        result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
+    cnt_a = t85_encode_get_compressed_image_size(&t85_enc);
+    cnt_b = t85_decode_get_compressed_image_size(&t85_dec);
+    if (cnt_a != cnt_b  ||  cnt_a != testbuf_len*8  ||  result != T85_OK)
+    {
+        printf("Decode result %d\n", result);
+        printf("%ld/%ld bits of %ld bits of BIE read. %lu lines decoded.\n",
+               (long int) cnt_a,
+               (long int) cnt_b,
+               (unsigned long int) testbuf_len*8,
+               (unsigned long int) t85_dec.y);
+        printf("Test failed\n");
+        exit(2);
+    }
+    if (memcmp(decoded_image, image, image_size))
+    {
+        printf("Image mismatch\n");
+        printf("Test failed\n");
+        exit(2);
+    }
+    free(decoded_image);
+    t85_decode_release(&t85_dec);
+    printf("Test passed\n");
+
+    printf("%s.3: Decode byte by byte\n", test_id);
+    if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
+    {
+        fprintf(stderr, "Out of memory!\n");
+        exit(2);
+    }
+    t85_decode_init(&t85_dec, row_write_handler, decoded_image);
+    if (comment  &&  comment[0] != 'X')
+        t85_decode_set_comment_handler(&t85_dec, 1000, comment_handler, NULL);
+    write_row = 0;
+    result = T85_MORE_DATA;
+    for (l = 0;  l < testbuf_len;  l++)
+    {
+        result = t85_decode_put_chunk(&t85_dec, &testbuf[l], 1);
+        if (result != T85_MORE_DATA)
+        {
+            l++;
+            break;
+        }
+    }
+    if (result == T85_MORE_DATA)
+        result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
+    if (l != testbuf_len  ||  result != T85_OK)
+    {
+        printf("Decode result %d\n", result);
+        printf("%ld bytes of %ld bytes of BIE read. %lu lines decoded.\n",
+               (long int) l,
+               (unsigned long int) testbuf_len,
+               (unsigned long int) t85_dec.y);
+        printf("Test failed\n");
+        exit(2);
+    }
+    if (memcmp(decoded_image, image, image_size))
+    {
+        printf("Image mismatch\n");
+        printf("Test failed\n");
+        exit(2);
+    }
+    free(decoded_image);
+    t85_decode_release(&t85_dec);
+    printf("Test passed\n");
+    
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char **argv)
+{
+    printf("T.85 JBIG for FAX encoder and decoder tests, from ITU-T T.82\n\n");
+
+    printf("Generating the test image from T.82...\n");
+    create_test_image(test_image);
+
+    /* Run through the tests in T.82/7.2, which are applicable to T.85 */
+    test_cycle("1", test_image, 1960, 1951, 1951, 0, 0,          0, NULL, 317384);
+    test_cycle("2", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, NULL, 317132);
+    test_cycle("3", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, NULL, 253653);
+    /* Again with a comment added and handled */
+    test_cycle("4", test_image, 1960, 1951, 1951, 0, 0,          0, (const uint8_t *) "Comment 4", 317384 + 16);
+    test_cycle("5", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Comment 5", 317132 + 16);
+    test_cycle("6", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, (const uint8_t *) "Comment 6", 253653 + 2*16);
+    /* Again with a comment added, but not handled */
+    test_cycle("7", test_image, 1960, 1951, 1951, 0, 0,          0, (const uint8_t *) "Xomment 7", 317384 + 16);
+    test_cycle("8", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Xomment 8", 317132 + 16);
+    test_cycle("9", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, (const uint8_t *) "Xomment 9", 253653 + 2*16);
+    /* Again with the image variable length and prematurely terminated */
+    test_cycle("10", test_image, 1960, 1951, 1951, 0, T85_VLENGTH,              T85_VLENGTH, NULL, 317384 + 8);
+    test_cycle("11", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, T85_VLENGTH, NULL, 317132 + 8);
+    test_cycle("12", test_image, 1960, 1951,  128, 8, T85_VLENGTH | T85_TPBON,  T85_VLENGTH, NULL, 253653 + 8);
+    /* Again with the image variable length but not prematurely terminated */
+    test_cycle("13", test_image, 1960, 1951, 1951, 0, T85_VLENGTH,              0, NULL, 317384);
+    test_cycle("14", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, 0, NULL, 317132);
+    test_cycle("15", test_image, 1960, 1951,  128, 8, T85_VLENGTH | T85_TPBON,  0, NULL, 253653);
+
+    printf("Tests passed\n");
+
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/