From 5d7e26141f9e01f31032808248d9279058202f9b Mon Sep 17 00:00:00 2001 From: Steve Underwood 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 #include #include -/*#include */ -/*#include */ +#include +#include /*#include */ /*#include */ #include 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 #include #include -/*#include */ -/*#include */ +#include +#include /*#include */ /*#include */ #include 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#include +#include + +#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 + * + * 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 +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +//#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 + * + * 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 +#include +#include +#include +#include + +//#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 ------------------------------------------------------------*/