Introducing T.85 encode and decodei to spandsp. This is not connected into the FAX engine in this update.

This commit is contained in:
Steve Underwood 2012-07-14 23:59:01 +08:00
parent 287678bc56
commit 5d7e26141f
13 changed files with 3407 additions and 4 deletions

View File

@ -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 \

View File

@ -108,8 +108,8 @@
#include <spandsp/image_translate.h>
#include <spandsp/t4_t6_decode.h>
#include <spandsp/t4_t6_encode.h>
/*#include <spandsp/t81_t82_arith_coding.h>*/
/*#include <spandsp/t85.h>*/
#include <spandsp/t81_t82_arith_coding.h>
#include <spandsp/t85.h>
/*#include <spandsp/t42.h>*/
/*#include <spandsp/t43.h>*/
#include <spandsp/t30.h>

View File

@ -78,8 +78,8 @@
#include <spandsp/private/image_translate.h>
#include <spandsp/private/t4_t6_decode.h>
#include <spandsp/private/t4_t6_encode.h>
/*#include <spandsp/private/t81_t82_arith_coding.h>*/
/*#include <spandsp/private/t85.h>*/
#include <spandsp/private/t81_t82_arith_coding.h>
#include <spandsp/private/t85.h>
/*#include <spandsp/private/t42.h>*/
/*#include <spandsp/private/t43.h>*/
#include <spandsp/private/t4_rx.h>

View File

@ -0,0 +1,79 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t81_t82_arith_coding.h - ITU T.81 and T.82 QM-coder arithmetic encoding
* and decoding
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if !defined(_SPANDSP_PRIVATE_T81_T82_ARITH_CODING_H_)
#define _SPANDSP_PRIVATE_T81_T82_ARITH_CODING_H_
/* State of a working instance of the QM-coder arithmetic encoder */
struct t81_t82_arith_encode_state_s
{
/*! A register - see T.82 Table 23 */
uint32_t a;
/*! C register - see T.82 Table 23 */
uint32_t c;
/*! Probability status for contexts. MSB = MPS */
uint8_t st[4096];
/*! Number of buffered 0xFF values that might still overflow */
int32_t sc;
/*! Bit shift counter. This determines when the next byte will be written */
int ct;
/*! Buffer for the most recent output byte which is not 0xFF */
int buffer;
/*! Callback function to deliver the encoded data, byte by byte */
void (*output_byte_handler)(void *, int);
/*! Opaque pointer passed to byte_out */
void *user_data;
};
/* State of a working instance of the QM-coder arithmetic decoder */
struct t81_t82_arith_decode_state_s
{
/*! A register - see T.82 Table 25 */
uint32_t a;
/*! C register - see T.82 Table 25 */
uint32_t c;
/*! Probability status for contexts. MSB = MPS */
uint8_t st[4096];
/*! Bit-shift counter. Determines when next byte will be read.
Special value -1 signals that zero-padding has started */
int ct;
/*! Pointer to next PSCD data byte */
const uint8_t *pscd_ptr;
/*! Pointer to byte after PSCD */
const uint8_t *pscd_end;
/*! Boolean flag that controls initial fill of s->c */
int startup;
/*! Boolean flag that triggers return -2 between reaching PSCD end
and decoding the first symbol that might never have been encoded
in the first place */
int nopadding;
};
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,212 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* private/t85.h - ITU T.85 JBIG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2008, 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(_SPANDSP_PRIVATE_T85_H_)
#define _SPANDSP_PRIVATE_T85_H_
/* Maximum number of ATMOVEs per stripe that the decoder can handle */
#define T85_ATMOVES_MAX 1
/* TP special pixels */
#define TPB2CX 0x195
#define TPB3CX 0x0E5
/* T.82 table 2 - symbolic constants */
enum
{
T82_STUFF = 0x00,
T82_RESERVE = 0x01,
T82_SDNORM = 0x02,
T82_SDRST = 0x03,
T82_ABORT = 0x04,
T82_NEWLEN = 0x05,
T82_ATMOVE = 0x06,
T82_COMMENT = 0x07,
T82_ESC = 0xFF
};
/* State of a working instance of the T.85 JBIG FAX encoder */
struct t85_encode_state_s
{
/*! \brief Callback function to read a row of pixels from the image source. */
t4_row_read_handler_t row_read_handler;
/*! \brief Opaque pointer passed to row_read_handler. */
void *row_read_user_data;
/*! The number of bit planes. Always 1 for true T.85 */
uint8_t bit_planes;
uint8_t current_bit_plane;
/*! The width of the full image, in pixels */
uint32_t xd;
/*! The height of the full image, in pixels */
uint32_t yd;
/*! Then number of rows per stripe */
uint32_t l0;
/*! Maximum ATMOVE window size (0 - 127) */
int mx;
/*! Encoding parameters */
int options;
/*! The contents for a COMMENT marker segment, to be added to the
image at the next opportunity. This is set to NULL when nothing is
pending. */
const uint8_t *comment;
/*! Length of data pointed to by comment */
size_t comment_len;
/*! Next row number to be encoded */
uint32_t y;
/*! Next row within current stripe */
uint32_t i;
/*! Flag for handling NEWLEN processing. */
int newlen;
/*! X-offset of adaptive template pixel */
int32_t tx;
/*! Adaptive template algorithm variables */
uint32_t c_all;
/*! Adaptive template algorithm variables */
uint32_t c[128];
/*! New TX value, or <0 for analysis in progress */
int32_t new_tx;
/*! TRUE if previous row was typical */
int prev_ltp;
/*! Pointers to the 3 row buffers */
uint8_t *prev_row[3];
/*! Pointer to a block of allocated memory 3 rows long, which
we divide up for the 3 row buffers. */
uint8_t *row_buf;
uint8_t *bitstream;
int bitstream_len;
int bitstream_iptr;
int bitstream_optr;
int fill_with_white;
/*! \brief The size of the compressed image, in bytes. */
int compressed_image_size;
/*! Arithmetic encoder state */
t81_t82_arith_encode_state_t s;
/*! \brief Error and flow logging control */
logging_state_t logging;
};
/* State of a working instance of the T.85 JBIG FAX decoder */
struct t85_decode_state_s
{
/*! A callback routine to handle decoded pixel rows */
t4_row_write_handler_t row_write_handler;
/*! An opaque pointer passed to row_write_handler() */
void *row_write_user_data;
/*! A callback routine to handle decoded comments */
t4_row_write_handler_t comment_handler;
/*! An opaque pointer passed to comment_handler() */
void *comment_user_data;
uint8_t min_bit_planes;
uint8_t max_bit_planes;
/*! The maximum length of comment to be passed to the comment handler */
uint32_t max_comment_len;
/*! The maximum permitted width of the full image, in pixels */
uint32_t max_xd;
/*! The maximum permitted height of the full image, in pixels */
uint32_t max_yd;
/*! The number of bit planes expected, according to the header. Always 1 for true T.85 */
uint8_t bit_planes;
uint8_t current_bit_plane;
/*! The width of the full image, in pixels */
uint32_t xd;
/*! The height of the full image, in pixels */
uint32_t yd;
/*! The number of rows per stripe */
uint32_t l0;
/*! Maximum ATMOVE window size */
int mx;
/*! Encoding parameters */
int options;
/*! The current row and the previous 2 rows of image data */
int p[3];
/*! Pointers to the 3 row buffers */
uint8_t *prev_row[3];
/*! Pointer to a block of allocated memory 3 rows long, which
we divide up for the 3 row buffers. */
uint8_t *row_buf;
/*! The length of the row buffer */
int row_buf_len;
/*! Bytes per pixel row */
size_t bytes_per_row;
/*! X-offset of AT pixel */
int32_t tx;
/*! Number of bytes read so far */
uint32_t bie_len;
/*! Buffer space for the BIH or marker segments fragments */
uint8_t buffer[20];
/*! Number of bytes in buffer. */
int buf_len;
/*! Required number of bytes in buffer to proceed with processing
its contents. */
int buf_needed;
/*! The content of a decoded COMMENT marker segment. */
uint8_t *comment;
/*! The expected length of a decoded COMMENT segment */
uint32_t comment_len;
/*! The length of COMMENT decoded to date */
uint32_t comment_progress;
/*! Current column */
uint32_t x;
/*! Current row */
uint32_t y;
/*! Current row within the current stripe */
uint32_t i;
/*! Number of AT moves in the current stripe */
int at_moves;
/*! Rows at which an AT move will happen */
uint32_t at_row[T85_ATMOVES_MAX];
/*! ATMOVE x-offsets in current stripe */
int at_tx[T85_ATMOVES_MAX];
/*! Working data for decode_pscd() */
uint32_t row_h[3];
/*! Flag for TPBON/TPDON: next pixel is a pseudo pixel */
int pseudo;
/*! Line is not typical flag. */
int lntp;
/*! Flag that row_write_handler() requested an interrupt. */
int interrupt;
/*! Flag that the data to be decoded has run out. */
int end_of_data;
/*! Arithmetic decoder state */
t81_t82_arith_decode_state_t s;
/*! \brief The size of the compressed image, in bytes. */
int compressed_image_size;
/*! \brief Error and flow logging control */
logging_state_t logging;
};
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,82 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t81_t82_arith_coding.h - ITU T.81 and T.82 QM-coder arithmetic encoding
* and decoding
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if !defined(_SPANDSP_T81_T82_ARITH_CODING_H_)
#define _SPANDSP_T81_T82_ARITH_CODING_H_
/*! \page t81_t82_arith_coding_page T.81 and T.82 QM-coder arithmetic encoding and decoding
\section t81_t82_arith_coding_page_sec_1 What does it do?
A similar arithmetic coder, called the QM-coder, is used by several image compression
schemes. These routines implement this coder in a (hopefully) reusable way.
\section t81_t82_arith_coding_page_sec_1 How does it work?
*/
/* State of a working instance of the arithmetic encoder */
typedef struct t81_t82_arith_encode_state_s t81_t82_arith_encode_state_t;
/* State of a working instance of the arithmetic decoder */
typedef struct t81_t82_arith_decode_state_s t81_t82_arith_decode_state_t;
#if defined(__cplusplus)
extern "C"
{
#endif
SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_arith_encode_state_t *s,
void (*output_byte_handler)(void *, int),
void *user_data);
SPAN_DECLARE(int) t81_t82_arith_encode_restart(t81_t82_arith_encode_state_t *s, int reuse_st);
SPAN_DECLARE(int) t81_t82_arith_encode_release(t81_t82_arith_encode_state_t *s);
SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s);
SPAN_DECLARE(void) t81_t82_arith_encode(t81_t82_arith_encode_state_t *s, int cx, int pix);
SPAN_DECLARE(void) t81_t82_arith_encode_flush(t81_t82_arith_encode_state_t *s);
SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_arith_decode_state_t *s);
SPAN_DECLARE(int) t81_t82_arith_decode_restart(t81_t82_arith_decode_state_t *s, int reuse_st);
SPAN_DECLARE(int) t81_t82_arith_decode_release(t81_t82_arith_decode_state_t *s);
SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s);
SPAN_DECLARE(int) t81_t82_arith_decode(t81_t82_arith_decode_state_t *s, int cx);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,288 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t85.h - ITU T.85 JBIG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2008, 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if !defined(_SPANDSP_T85_H_)
#define _SPANDSP_T85_H_
/*! \page t85_page T.85 (JBIG for FAX) image compression and decompression
\section t85_page_sec_1 What does it do?
The T.85 image compression and decompression routines implement the variant of the
JBIG encoding method defined in ITU specification T.85. This is an image compression
algorithm used for black and white FAX transmission. T.85 defines a subset of the
full JBIG spec (T.82), which only handled a single progressively scanned bit plane.
This results in a great deal of simplification, and results in the ability to
compress or decompress progressively, while only buffering the latest 3 pixel rows
of the image.
\section t85_page_sec_1 How does it work?
*/
/*! Bits in the option byte of the T.82 BIH which are valid for T.85 */
enum
{
/*! Enable typical prediction (bottom) */
T85_TPBON = 0x08,
/*! Variable length image */
T85_VLENGTH = 0x20,
/*! Lowest-resolution-layer is a two-line template */
T85_LRLTWO = 0x40
};
/*! Return values from the T.85 decoder */
enum
{
/*! More image data is needed */
T85_MORE_DATA = 0,
/*! Image completed successfully */
T85_OK = -1,
/*! The decoder has interrupted */
T85_INTERRUPT = -2,
/*! An abort was found in the image data */
T85_ABORTED = -3,
/*! A memory allocation error occurred */
T85_NOMEM = -4,
/*! The image data is invalid. This includes finding values
in the BIH which lie outside the T.85 domain */
T85_INVALID_DATA = -5
};
/*! State of a working instance of the T.85 encoder */
typedef struct t85_encode_state_s t85_encode_state_t;
/*! State of a working instance of the T.85 decoder */
typedef struct t85_decode_state_s t85_decode_state_t;
#if defined(__cplusplus)
extern "C"
{
#endif
/*! \brief Get the next byte of the current document page. The document will
be padded for the current minimum scan line time.
\param s The T.85 context.
\return The next byte. For the last byte of data, bit 8 is
set. In this case, one or more bits of the byte may be padded with
zeros, to complete the byte. */
SPAN_DECLARE(int) t85_encode_get_byte(t85_encode_state_t *s);
/*! \brief Get the next chunk of the current document page. The document will
be padded for the current minimum scan line time.
\param s The T.85 context.
\param buf The buffer into which the chunk is to written.
\param max_len The maximum length of the chunk.
\return The actual length of the chunk. If this is less than max_len it
indicates that the end of the document has been reached. */
SPAN_DECLARE(int) t85_encode_get_chunk(t85_encode_state_t *s, uint8_t buf[], int max_len);
/*! \brief Set the row read handler for a T.85 encode context.
\param s The T.85 context.
\param handler A pointer to the handler routine.
\param user_data An opaque pointer passed to the handler routine.
\return 0 for success, otherwise -1. */
SPAN_DECLARE(int) t85_encode_set_row_read_handler(t85_encode_state_t *s,
t4_row_read_handler_t handler,
void *user_data);
/*! \brief Prepare to encode an image in T.85 format.
\param s The T.85 context.
\param image_width Image width, in pixels.
\param image_length Image length, in pixels.
\param handler A callback routine to handle encoded image rows.
\param user_data An opaque pointer passed to handler.
\return A pointer to the context, or NULL if there was a problem. */
SPAN_DECLARE(t85_encode_state_t *) t85_encode_init(t85_encode_state_t *s,
uint32_t image_width,
uint32_t image_length,
t4_row_read_handler_t handler,
void *user_data);
/*! \brief Restart a T.85 encode context.
\param s The T.85 context.
\param image width The image width, in pixels.
\return 0 for success, otherwise -1. */
SPAN_DECLARE(int) t85_encode_restart(t85_encode_state_t *s,
uint32_t image_width,
uint32_t image_length);
SPAN_DECLARE(int) t85_encode_release(t85_encode_state_t *s);
SPAN_DECLARE(int) t85_encode_free(t85_encode_state_t *s);
/*! \brief Set the T.85 options
\param s The T.85 context.
\brief l0 ???
\brief mx ???
\brief options ???. */
SPAN_DECLARE(void) t85_encode_set_options(t85_encode_state_t *s,
uint32_t l0,
int mx,
int options);
/*! \brief Insert a comment in the encoded file.
\param s The T.85 context.
\param comment The comment. Note that this is not a C string, and may contain any bytes.
\param len The length of the comment. */
SPAN_DECLARE(void) t85_encode_comment(t85_encode_state_t *s,
const uint8_t comment[],
size_t len);
/*! \brief Set the image width.
\param s The T.85 context.
\param width The width of the image.
\return 0 for success, otherwise -1. */
SPAN_DECLARE(int) t85_encode_set_image_width(t85_encode_state_t *s, uint32_t image_width);
/*! \brief Alter the length of a T.85 encoded image. The new length cannot be greater than the
originally specified length. If the new length is less than the current length it
will be silently adjusted to the current length. Therefore, adjust the length to 1
will make the currently encoded length the final length.
\param s The T.85 context.
\param length The new image length, in pixels.
\return 0 if OK, or -1 if the request was not valid. */
SPAN_DECLARE(int) t85_encode_set_image_length(t85_encode_state_t *s, uint32_t length);
/*! \brief Get the width of the image.
\param s The T.85 context.
\return The width of the image, in pixels. */
SPAN_DECLARE(uint32_t) t85_encode_get_image_width(t85_encode_state_t *s);
/*! \brief Get the length of the image.
\param s The T.85 context.
\return The length of the image, in pixels. */
SPAN_DECLARE(uint32_t) t85_encode_get_image_length(t85_encode_state_t *s);
/*! \brief Get the size of the compressed image, in bits.
\param s The T.85 context.
\return The size of the compressed image, in bits. */
SPAN_DECLARE(int) t85_encode_get_compressed_image_size(t85_encode_state_t *s);
/*! \brief Stop image encoding prematurely.
\param s The T.85 context. */
SPAN_DECLARE(void) t85_encode_abort(t85_encode_state_t *s);
/*! \brief Prepare to decode an image in T.85 format.
\param s The T.85 context.
\param handler A callback routine to handle decoded image rows.
\param user_data An opaque pointer passed to handler.
\return A pointer to the context, or NULL if there was a problem. */
SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s);
SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s);
SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s);
SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s);
/*! \brief Get the width of the image.
\param s The T.85 context.
\return The width of the image, in pixels. */
SPAN_DECLARE(uint32_t) t85_decode_get_image_width(t85_decode_state_t *s);
/*! \brief Get the length of the image.
\param s The T.85 context.
\return The length of the image, in pixels. */
SPAN_DECLARE(uint32_t) t85_decode_get_image_length(t85_decode_state_t *s);
/*! \brief Get the size of the compressed image, in bits.
\param s The T.85 context.
\return The size of the compressed image, in bits. */
SPAN_DECLARE(int) t85_decode_get_compressed_image_size(t85_decode_state_t *s);
/*! \brief Set the row handler routine.
\param s The T.85 context.
\param handler A callback routine to handle decoded image rows.
\param user_data An opaque pointer passed to handler.
\return 0 for OK. */
SPAN_DECLARE(int) t85_decode_set_row_write_handler(t85_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data);
/*! \brief Set the comment handler routine.
\param s The T.85 context.
\param max_comment_len The maximum length of comment to be passed to the handler.
\param handler A callback routine to handle decoded comment.
\param user_data An opaque pointer passed to handler.
\return 0 for OK. */
SPAN_DECLARE(int) t85_decode_set_comment_handler(t85_decode_state_t *s,
uint32_t max_comment_len,
t4_row_write_handler_t handler,
void *user_data);
/*! A maliciously constructed T.85 image could consume too much memory, and constitute
a denial of service attack on the system. This function allows constraints to be
applied.
\brief Set constraints on the received image size.
\param s The T.85 context.
\param max_xd The maximum permitted width of the full image, in pixels
\param max_yd The maximum permitted height of the full image, in pixels
\return 0 for OK */
SPAN_DECLARE(int) t85_decode_set_image_size_constraints(t85_decode_state_t *s,
uint32_t max_xd,
uint32_t max_yd);
/*! After the final BIE byte has been delivered to t85_decode_put_xx(), it may still
return T85_MORE_DATA when the T85_VLENGTH option was used, and no NEWLEN
marker section has appeared yet. This is because such a BIE is not
self-terminating (i.e. there could still be a NEWLEN followed by an SDNORM
or SDRST at the very end of the final stripe, which needs to be processed
before the final row is output. See ITU-T Recommendation T.85, Appendix I).
Therefore, after the last byte has been delivered, call this routine to
signal the end of the BIE. This is necessary to allow the routine to finish
processing BIEs with option T85_VLENGTH that do not actually contain any
NEWLEN marker section.
\brief Inform the T.85 decode engine of a status change in the signal source (end
of tx, rx signal change, etc.).
\param s The T.85 context.
\param status The type of status change which occured. */
SPAN_DECLARE(void) t85_decode_rx_status(t85_decode_state_t *s, int status);
/*! \brief Decode a byte of T.85 data.
\param s The T.85 context.
\param byte The data to be decoded.
\return 0 for OK. */
SPAN_DECLARE(int) t85_decode_put_byte(t85_decode_state_t *s, int byte);
/*! \brief Decode a chunk of T.85 data.
\param s The T.85 context.
\param data The data to be decoded.
\param len The length of the data to be decoded.
\return 0 for OK. */
SPAN_DECLARE(int) t85_decode_put_chunk(t85_decode_state_t *s,
const uint8_t data[],
size_t len);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,509 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t81_t82_arith_coding.c - ITU T.81 and T.82 QM-coder arithmetic encoding
* and decoding
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "spandsp/telephony.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/private/t81_t82_arith_coding.h"
/* T.82 defines the QM-coder at a level very close to actual code. Therefore
this file closely mirrors the routine names, variable names, and flow
described in T.82. QM-Coder is supposed to be the same in some other image
compression schemes, such as T.81. However, this code has not been checked
to see if it follows the letter of any spec other than T.82. */
#define FALSE 0
#define TRUE (!FALSE)
/* Code bytes which must trigger stuffing */
enum
{
T81_T82_STUFF = 0x00,
T81_T82_ESC = 0xFF
};
/* This table is from T.82 table 24 - Probability estimation table */
static const struct probability_estimation_s
{
uint16_t lsz;
uint8_t nlps; /* The SWITCH bit is packed into the top of this byte */
uint8_t nmps;
} prob[113] =
{
{0x5A1D, 1 + 128, 1},
{0x2586, 14, 2},
{0x1114, 16, 3},
{0x080B, 18, 4},
{0x03D8, 20, 5},
{0x01DA, 23, 6},
{0x00E5, 25, 7},
{0x006F, 28, 8},
{0x0036, 30, 9},
{0x001A, 33, 10},
{0x000D, 35, 11},
{0x0006, 9, 12},
{0x0003, 10, 13},
{0x0001, 12, 13},
{0x5A7F, 15 + 128, 15},
{0x3F25, 36, 16},
{0x2CF2, 38, 17},
{0x207C, 39, 18},
{0x17B9, 40, 19},
{0x1182, 42, 20},
{0x0CEF, 43, 21},
{0x09A1, 45, 22},
{0x072F, 46, 23},
{0x055C, 48, 24},
{0x0406, 49, 25},
{0x0303, 51, 26},
{0x0240, 52, 27},
{0x01B1, 54, 28},
{0x0144, 56, 29},
{0x00F5, 57, 30},
{0x00B7, 59, 31},
{0x008A, 60, 32},
{0x0068, 62, 33},
{0x004E, 63, 34},
{0x003B, 32, 35},
{0x002C, 33, 9},
{0x5AE1, 37 + 128, 37},
{0x484C, 64, 38},
{0x3A0D, 65, 39},
{0x2EF1, 67, 40},
{0x261F, 68, 41},
{0x1F33, 69, 42},
{0x19A8, 70, 43},
{0x1518, 72, 44},
{0x1177, 73, 45},
{0x0E74, 74, 46},
{0x0BFB, 75, 47},
{0x09F8, 77, 48},
{0x0861, 78, 49},
{0x0706, 79, 50},
{0x05CD, 48, 51},
{0x04DE, 50, 52},
{0x040F, 50, 53},
{0x0363, 51, 54},
{0x02D4, 52, 55},
{0x025C, 53, 56},
{0x01F8, 54, 57},
{0x01A4, 55, 58},
{0x0160, 56, 59},
{0x0125, 57, 60},
{0x00F6, 58, 61},
{0x00CB, 59, 62},
{0x00AB, 61, 63},
{0x008F, 61, 32},
{0x5B12, 65 + 128, 65},
{0x4D04, 80, 66},
{0x412C, 81, 67},
{0x37D8, 82, 68},
{0x2FE8, 83, 69},
{0x293C, 84, 70},
{0x2379, 86, 71},
{0x1EDF, 87, 72},
{0x1AA9, 87, 73},
{0x174E, 72, 74},
{0x1424, 72, 75},
{0x119C, 74, 76},
{0x0F6B, 74, 77},
{0x0D51, 75, 78},
{0x0BB6, 77, 79},
{0x0A40, 77, 48},
{0x5832, 80 + 128, 81},
{0x4D1C, 88, 82},
{0x438E, 89, 83},
{0x3BDD, 90, 84},
{0x34EE, 91, 85},
{0x2EAE, 92, 86},
{0x299A, 93, 87},
{0x2516, 86, 71},
{0x5570, 88 + 128, 89},
{0x4CA9, 95, 90},
{0x44D9, 96, 91},
{0x3E22, 97, 92},
{0x3824, 99, 93},
{0x32B4, 99, 94},
{0x2E17, 93, 86},
{0x56A8, 95 + 128, 96},
{0x4F46, 101, 97},
{0x47E5, 102, 98},
{0x41CF, 103, 99},
{0x3C3D, 104, 100},
{0x375E, 99, 93},
{0x5231, 105, 102},
{0x4C0F, 106, 103},
{0x4639, 107, 104},
{0x415E, 103, 99},
{0x5627, 105 + 128, 106},
{0x50E7, 108, 107},
{0x4B85, 109, 103},
{0x5597, 110, 109},
{0x504F, 111, 107},
{0x5A10, 110 + 128, 111},
{0x5522, 112, 109},
{0x59EB, 112 + 128, 111}
};
static __inline__ void output_stuffed_byte(t81_t82_arith_encode_state_t *s, int byte)
{
s->output_byte_handler(s->user_data, byte);
if (byte == T81_T82_ESC)
s->output_byte_handler(s->user_data, T81_T82_STUFF);
}
/*- End of function --------------------------------------------------------*/
static __inline__ void byteout(t81_t82_arith_encode_state_t *s)
{
uint32_t temp;
/* T.30 figure 26 - BYTEOUT */
temp = s->c >> 19;
if (temp > 0xFF)
{
if (s->buffer >= 0)
output_stuffed_byte(s, s->buffer + 1);
while (s->sc)
{
s->output_byte_handler(s->user_data, 0x00);
s->sc--;
}
s->buffer = temp & 0xFF;
}
else if (temp == 0xFF)
{
s->sc++;
}
else
{
if (s->buffer >= 0)
output_stuffed_byte(s, s->buffer);
while (s->sc)
{
output_stuffed_byte(s, T81_T82_ESC);
s->sc--;
}
s->buffer = temp;
}
s->c &= 0x7FFFF;
s->ct = 8;
}
/*- End of function --------------------------------------------------------*/
static __inline__ void renorme(t81_t82_arith_encode_state_t *s)
{
/* T.82 figure 25 - RENORME */
do
{
s->a <<= 1;
s->c <<= 1;
s->ct--;
if (s->ct == 0)
byteout(s);
}
while (s->a < 0x8000);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t81_t82_arith_encode(t81_t82_arith_encode_state_t *s, int cx, int pix)
{
uint32_t ss;
/* T.82 figure 22 - ENCODE */
ss = s->st[cx] & 0x7F;
if (((pix << 7) ^ s->st[cx]) & 0x80)
{
/* T.82 figure 23 - CODELPS */
s->a -= prob[ss].lsz;
if (s->a >= prob[ss].lsz)
{
s->c += s->a;
s->a = prob[ss].lsz;
}
s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
renorme(s);
}
else
{
/* T.82 figure 24 - CODEMPS */
s->a -= prob[ss].lsz;
if (s->a < 0x8000)
{
if (s->a < prob[ss].lsz)
{
s->c += s->a;
s->a = prob[ss].lsz;
}
s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
renorme(s);
}
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t81_t82_arith_encode_flush(t81_t82_arith_encode_state_t *s)
{
uint32_t temp;
/* T.82 figure 28 - FLUSH */
/* T.82 figure 29 - CLEARBITS */
temp = (s->c + s->a - 1) & 0xFFFF0000;
s->c = (temp < s->c) ? (temp + 0x8000) : temp;
/* T.82 figure 30 - FINALWRITES */
s->c <<= s->ct;
if ((s->c > 0x7FFFFFF))
{
if (s->buffer >= 0)
output_stuffed_byte(s, s->buffer + 1);
/* Only output 0x00 bytes if something non-0x00 will follow */
if ((s->c & 0x7FFF800))
{
while (s->sc)
{
output_stuffed_byte(s, 0x00);
s->sc--;
}
}
}
else
{
/* The next bit says s->buffer + 1 in T.82, but that makes no sense. It doesn't
agree with how we code things away from the flush condition, and it gives
answers which don't seem to match other JBIG coders. */
if (s->buffer >= 0)
output_stuffed_byte(s, s->buffer);
while (s->sc)
{
output_stuffed_byte(s, 0xFF);
s->sc--;
}
}
/* Only output final bytes if they are not 0x00 */
if ((s->c & 0x7FFF800))
{
output_stuffed_byte(s, (s->c >> 19) & 0xFF);
if ((s->c & 0x7F800))
output_stuffed_byte(s, (s->c >> 11) & 0xFF);
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_encode_restart(t81_t82_arith_encode_state_t *s, int reuse_st)
{
/* T.82 figure 27 - INITENC */
if (!reuse_st)
memset(s->st, 0, sizeof(s->st));
s->c = 0;
s->a = 0x10000;
s->sc = 0;
s->ct = 11;
s->buffer = -1;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_arith_encode_state_t *s,
void (*output_byte_handler)(void *, int),
void *user_data)
{
if (s == NULL)
{
if ((s = (t81_t82_arith_encode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
s->output_byte_handler = output_byte_handler;
s->user_data = user_data;
t81_t82_arith_encode_restart(s, FALSE);
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_encode_release(t81_t82_arith_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s)
{
int ret;
ret = t81_t82_arith_encode_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_decode(t81_t82_arith_decode_state_t *s, int cx)
{
uint32_t ss;
int pix;
/* T.82 figure 35 - RENORMD */
while (s->a < 0x8000 || s->startup)
{
while (s->ct <= 8 && s->ct >= 0)
{
/* First we can move a new byte into s->c */
if (s->pscd_ptr >= s->pscd_end)
return -1;
if (s->pscd_ptr[0] == T81_T82_ESC)
{
if (s->pscd_ptr + 1 >= s->pscd_end)
return -1;
if (s->pscd_ptr[1] == T81_T82_STUFF)
{
s->c |= (0xFF << (8 - s->ct));
s->ct += 8;
s->pscd_ptr += 2;
}
else
{
/* Start padding with zero bytes */
s->ct = -1;
if (s->nopadding)
{
/* Subsequent symbols might depend on zero padding */
s->nopadding = FALSE;
return -2;
}
}
}
else
{
s->c |= (int32_t) *(s->pscd_ptr++) << (8 - s->ct);
s->ct += 8;
}
}
s->a <<= 1;
s->c <<= 1;
if (s->ct >= 0)
s->ct--;
if (s->a == 0x10000)
s->startup = FALSE;
}
/* T.82 figure 32 - DECODE */
ss = s->st[cx] & 0x7F;
if ((s->c >> 16) >= (s->a -= prob[ss].lsz))
{
/* T.82 figure 33 - LPS_EXCHANGE */
if (s->a < prob[ss].lsz)
{
s->c -= (s->a << 16);
s->a = prob[ss].lsz;
pix = s->st[cx] >> 7;
s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
}
else
{
s->c -= (s->a << 16);
s->a = prob[ss].lsz;
pix = 1 - (s->st[cx] >> 7);
s->st[cx] = (s->st[cx]& 0x80) ^ prob[ss].nlps;
}
}
else
{
if (s->a < 0x8000)
{
/* T.82 figure 34 - MPS_EXCHANGE */
if (s->a < prob[ss].lsz)
{
pix = 1 - (s->st[cx] >> 7);
s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
}
else
{
pix = s->st[cx] >> 7;
s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
}
}
else
{
pix = s->st[cx] >> 7;
}
}
return pix;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_decode_restart(t81_t82_arith_decode_state_t *s, int reuse_st)
{
if (!reuse_st)
memset(s->st, 0, sizeof(s->st));
s->c = 0;
s->a = 1;
s->ct = 0;
s->startup = TRUE;
s->nopadding = FALSE;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_arith_decode_state_t *s)
{
if (s == NULL)
{
if ((s = (t81_t82_arith_decode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
t81_t82_arith_decode_restart(s, FALSE);
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_decode_release(t81_t82_arith_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s)
{
int ret;
ret = t81_t82_arith_decode_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,864 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t85_decode.c - ITU T.85 JBIG for FAX image decompression
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009, 2010 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/async.h"
#include "spandsp/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/t85.h"
#include "spandsp/private/logging.h"
#include "spandsp/private/t81_t82_arith_coding.h"
#include "spandsp/private/t85.h"
#define FALSE 0
#define TRUE (!FALSE)
static __inline__ int32_t pack_32(uint8_t *s)
{
int32_t value;
value = (((int32_t) s[0] << 24) | ((int32_t) s[1] << 16) | ((int32_t) s[2] << 8) | (int32_t) s[3]);
return value;
}
/*- End of function --------------------------------------------------------*/
/* Decode some PSCD bytes, output the decoded rows as they are completed. Return
the number of bytes which have actually been read. This will be less than len
if a marker segment was part of the data or if the final byte was 0xFF, meaning
that this code can not determine whether we have a marker segment. */
static size_t decode_pscd(t85_decode_state_t *s, const uint8_t data[], size_t len)
{
uint8_t *hp[3];
int32_t o;
int cx;
int i;
int pix;
int slntp;
int buffered_rows;
buffered_rows = (s->options & T85_LRLTWO) ? 2 : 3;
/* Forward data to the arithmetic decoder */
s->s.pscd_ptr = data;
s->s.pscd_end = data + len;
for (s->interrupt = FALSE; s->i < s->l0 && s->y < s->yd && !s->interrupt; s->i++, s->y++)
{
/* Point to the current image bytes */
for (i = 0; i < 3; i++)
hp[i] = s->row_buf + s->p[i]*s->bytes_per_row + (s->x >> 3);
/* Adaptive template changes */
if (s->x == 0 && s->pseudo)
{
for (i = 0; i < s->at_moves; i++)
{
if (s->at_row[i] == s->i)
s->tx = s->at_tx[i];
}
}
/* Typical prediction */
if ((s->options & T85_TPBON) && s->pseudo)
{
slntp = t81_t82_arith_decode(&s->s, (s->options & T85_LRLTWO) ? TPB2CX : TPB3CX);
if (slntp < 0)
return s->s.pscd_ptr - data;
s->lntp = !(slntp ^ s->lntp);
if (!s->lntp)
{
/* This row is 'typical' (i.e. identical to the previous one) */
if (s->p[1] < 0)
{
/* First row of page or (following SDRST) of stripe */
for (i = 0; i < s->bytes_per_row; i++)
hp[0][i] = 0;
s->interrupt = s->row_write_handler(s->row_write_user_data, hp[0], s->bytes_per_row);
/* Rotate the ring buffer that holds the last few rows */
s->p[2] = s->p[1];
s->p[1] = s->p[0];
if (++(s->p[0]) >= buffered_rows)
s->p[0] = 0;
}
else
{
s->interrupt = s->row_write_handler(s->row_write_user_data, hp[1], s->bytes_per_row);
/* Duplicate the last row in the ring buffer */
s->p[2] = s->p[1];
}
continue;
}
/* This row is 'not typical' and has to be coded completely */
}
s->pseudo = FALSE;
if (s->x == 0)
{
s->row_h[0] = 0;
s->row_h[1] = (s->p[1] >= 0) ? ((int32_t) hp[1][0] << 8) : 0;
s->row_h[2] = (s->p[2] >= 0) ? ((int32_t) hp[2][0] << 8) : 0;
}
/* Decode row */
while (s->x < s->xd)
{
if ((s->x & 7) == 0)
{
if (s->x < (s->bytes_per_row - 1)*8 && s->p[1] >= 0)
{
s->row_h[1] |= hp[1][1];
if (s->p[2] >= 0)
s->row_h[2] |= hp[2][1];
}
}
if ((s->options & T85_LRLTWO))
{
/* Two row template */
do
{
cx = (s->row_h[0] & 0x00F);
if (s->tx)
{
cx |= ((s->row_h[1] >> 9) & 0x3E0);
if (s->x >= (uint32_t) s->tx)
{
if (s->tx < 8)
{
cx |= ((s->row_h[0] >> (s->tx - 5)) & 0x010);
}
else
{
o = (s->x - s->tx) - (s->x & ~7);
cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 4);
}
}
}
else
{
cx |= ((s->row_h[1] >> 9) & 0x3F0);
}
pix = t81_t82_arith_decode(&s->s, cx);
if (pix < 0)
return s->s.pscd_ptr - data;
s->row_h[0] = (s->row_h[0] << 1) | pix;
s->row_h[1] <<= 1;
}
while ((++s->x & 7) && s->x < s->xd);
}
else
{
/* Three row template */
do
{
cx = ((s->row_h[2] >> 7) & 0x380) | (s->row_h[0] & 0x003);
if (s->tx)
{
cx |= ((s->row_h[1] >> 11) & 0x078);
if (s->x >= (uint32_t) s->tx)
{
if (s->tx < 8)
{
cx |= ((s->row_h[0] >> (s->tx - 3)) & 0x004);
}
else
{
o = (s->x - s->tx) - (s->x & ~7);
cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 2);
}
}
}
else
{
cx |= ((s->row_h[1] >> 11) & 0x07C);
}
pix = t81_t82_arith_decode(&s->s, cx);
if (pix < 0)
return s->s.pscd_ptr - data;
s->row_h[0] = (s->row_h[0] << 1) | pix;
s->row_h[1] <<= 1;
s->row_h[2] <<= 1;
}
while ((++s->x & 7) && s->x < s->xd);
}
*hp[0]++ = s->row_h[0];
hp[1]++;
hp[2]++;
}
*(hp[0] - 1) <<= (s->bytes_per_row*8 - s->xd);
s->interrupt = s->row_write_handler(s->row_write_user_data, &s->row_buf[s->p[0]*s->bytes_per_row], s->bytes_per_row);
s->x = 0;
s->pseudo = TRUE;
/* Shuffle the row buffers */
s->p[2] = s->p[1];
s->p[1] = s->p[0];
if (++(s->p[0]) >= buffered_rows)
s->p[0] = 0;
}
return s->s.pscd_ptr - data;
}
/*- End of function --------------------------------------------------------*/
static int finish_sde(t85_decode_state_t *s)
{
/* Decode final pixels based on trailing zero bytes */
s->s.nopadding = FALSE;
if (decode_pscd(s, s->buffer, 2) != 2 && s->interrupt)
return 1;
/* Prepare decoder for next SDE */
t81_t82_arith_decode_restart(&s->s, s->buffer[1] == T82_SDNORM);
s->s.nopadding = s->options & T85_VLENGTH;
s->x = 0;
s->i = 0;
s->pseudo = TRUE;
s->at_moves = 0;
if (s->buffer[1] == T82_SDRST)
{
s->tx = 0;
s->lntp = TRUE;
s->p[0] = 0;
s->p[1] = -1;
s->p[2] = -1;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static int check_bih(t85_decode_state_t *s)
{
/* Check that the fixed parameters have the values they are expected to be
fixed at - see T.85/Table 1 */
/* DL - Initial layer to be transmitted */
/* D - Number of differential layers */
/* Unspecified byte */
/* MY - Maximum vertical offset allowed for AT pixel */
/* Order byte */
if (s->buffer[0] != 0
||
s->buffer[1] != 0
||
s->buffer[3] != 0
||
s->buffer[17] != 0
||
#if T85_STRICT_ORDER_BITS
s->buffer[18] != 0)
#else
(s->buffer[18] & 0xF0) != 0)
#endif
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Fixed bytes do not contain expected values.\n");
return T85_INVALID_DATA;
}
/* P - Number of bit planes */
if (s->buffer[2] < s->min_bit_planes || s->buffer[2] > s->max_bit_planes)
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. %d bit planes. Should be %d to %d.\n", s->buffer[2], s->min_bit_planes, s->max_bit_planes);
return T85_INVALID_DATA;
}
s->bit_planes = s->buffer[2];
s->current_bit_plane = 0;
/* Now look at the stuff which actually counts in a T.85 header. */
/* XD - Horizontal image size at layer D */
s->xd = pack_32(&s->buffer[4]);
if (s->xd == 0 || (s->max_xd && s->xd > s->max_xd))
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Width is %" PRIu32 "\n", s->xd);
return T85_INVALID_DATA;
}
/* YD - Vertical image size at layer D */
s->yd = pack_32(&s->buffer[8]);
if (s->yd == 0 || (s->max_yd && s->yd > s->max_yd))
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Length is %" PRIu32 "\n", s->yd);
return T85_INVALID_DATA;
}
/* L0 - Rows per stripe, at the lowest resolution */
s->l0 = pack_32(&s->buffer[12]);
if (s->l0 == 0)
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. L0 is %" PRIu32 "\n", s->l0);
return T85_INVALID_DATA;
}
/* MX - Maximum horizontal offset allowed for AT pixel */
s->mx = s->buffer[16];
if (s->mx > 127)
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. MX is %d\n", s->mx);
return T85_INVALID_DATA;
}
/* Options byte */
s->options = s->buffer[19];
if ((s->options & 0x97))
{
span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Options are 0x%X\n", s->options);
return T85_INVALID_DATA;
}
span_log(&s->logging, SPAN_LOG_FLOW, "BIH is OK. Image is %" PRIu32 "x%" PRIu32 " pixels\n", s->xd, s->yd);
return T85_OK;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t85_decode_rx_status(t85_decode_state_t *s, int status)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status);
switch (status)
{
case SIG_STATUS_TRAINING_IN_PROGRESS:
case SIG_STATUS_TRAINING_FAILED:
case SIG_STATUS_TRAINING_SUCCEEDED:
case SIG_STATUS_CARRIER_UP:
/* Ignore these */
break;
case SIG_STATUS_CARRIER_DOWN:
case SIG_STATUS_END_OF_DATA:
/* Finalise the image */
s->end_of_data = 1;
t85_decode_put_chunk(s, NULL, 0);
break;
default:
span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status);
break;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_put_byte(t85_decode_state_t *s, int byte)
{
uint8_t data[1];
if (byte < 0)
{
t85_decode_rx_status(s, byte);
return (s->y >= s->yd) ? T85_OK : T85_MORE_DATA;
}
data[0] = byte;
return t85_decode_put_chunk(s, data, 1);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_put_chunk(t85_decode_state_t *s,
const uint8_t data[],
size_t len)
{
int ret;
uint32_t y;
uint8_t *buf;
size_t bytes_per_row;
size_t min_len;
size_t chunk;
size_t cnt;
int i;
s->compressed_image_size += len;
cnt = 0;
if (s->bie_len < 20)
{
/* Read in the 20-byte BIH */
i = (s->bie_len + len > 20) ? (20 - s->bie_len) : len;
memcpy(&s->buffer[s->bie_len], data, i);
s->bie_len += i;
cnt = i;
if (s->bie_len < 20)
return T85_MORE_DATA;
if ((ret = check_bih(s)) != T85_OK)
return ret;
/* Set up the two/three row buffer */
bytes_per_row = (s->xd + 7) >> 3;
min_len = ((s->options & T85_LRLTWO) ? 2 : 3)*bytes_per_row;
if (min_len > s->row_buf_len)
{
/* We need to expand the 3 row buffer */
if ((buf = (uint8_t *) realloc(s->row_buf, min_len)) == NULL)
return T85_NOMEM;
s->row_buf = buf;
s->row_buf_len = min_len;
}
t81_t82_arith_decode_init(&s->s);
s->s.nopadding = s->options & T85_VLENGTH;
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
s->comment_len = 0;
s->comment_progress = 0;
s->buf_len = 0;
s->buf_needed = 2;
s->x = 0;
s->y = 0;
s->i = 0;
s->pseudo = TRUE;
s->at_moves = 0;
s->tx = 0;
s->lntp = TRUE;
s->bytes_per_row = bytes_per_row;
s->p[0] = 0;
s->p[1] = -1;
s->p[2] = -1;
}
/* BID processing loop */
while (cnt < len || s->end_of_data == 1)
{
if (s->end_of_data == 1)
{
s->buf_needed = 2;
s->options &= ~T85_VLENGTH;
s->end_of_data = 2;
}
if (s->comment_len)
{
/* We are in a COMMENT. Absorb its contents */
chunk = len - cnt;
if ((s->comment_progress + chunk) >= s->comment_len)
{
/* Finished */
chunk = s->comment_len - s->comment_progress;
/* If the comment was too long to be passed to the handler, we still
call the handler with the buffer set to NULL, so it knows a large
comment has occurred. */
if (s->comment)
memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
if (s->comment_handler)
s->interrupt = s->comment_handler(s->comment_user_data, s->comment, s->comment_len);
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
s->comment_len = 0;
s->comment_progress = 0;
}
else
{
if (s->comment)
memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
s->comment_progress += chunk;
}
cnt += chunk;
continue;
}
/* Load marker segments into s->buffer for processing */
if (s->buf_len > 0)
{
/* We are in a marker of some kind. Load the first 2 bytes of
the marker, so we can determine its type, and hence its full
length. */
while (s->buf_len < s->buf_needed && cnt < len)
s->buffer[s->buf_len++] = data[cnt++];
/* Check we have enough bytes to see the message type */
if (s->buf_len < s->buf_needed)
continue;
switch (s->buffer[1])
{
case T82_STUFF:
/* Forward stuffed 0xFF to arithmetic decoder. This is likely to be
the commonest thing for us to hit here. */
decode_pscd(s, s->buffer, 2);
s->buf_len = 0;
if (s->interrupt)
return T85_INTERRUPT;
break;
case T82_ABORT:
s->buf_len = 0;
return T85_ABORTED;
case T82_COMMENT:
s->buf_needed = 6;
if (s->buf_len < 6)
continue;
s->buf_needed = 2;
s->buf_len = 0;
s->comment_len = pack_32(&s->buffer[2]);
/* Only try to buffer and process the comment's contents if we have
a defined callback routine to do something with it. */
/* If this malloc fails we carry on working just fine, and don't try to
process the contents of the comment. That is fairly benign, as
the comments are not generally of critical importance, so let's
not worry. */
if (s->comment_handler && s->comment_len > 0 && s->comment_len <= s->max_comment_len)
s->comment = malloc(s->comment_len);
s->comment_progress = 0;
continue;
case T82_ATMOVE:
s->buf_needed = 8;
if (s->buf_len < 8)
continue;
s->buf_needed = 2;
s->buf_len = 0;
if (s->at_moves >= T85_ATMOVES_MAX)
return T85_INVALID_DATA;
s->at_row[s->at_moves] = pack_32(&s->buffer[2]);
s->at_tx[s->at_moves] = s->buffer[6];
if (s->at_tx[s->at_moves] > s->mx
||
(s->at_tx[s->at_moves] > 0 && s->at_tx[s->at_moves] < ((s->options & T85_LRLTWO) ? 5 : 3))
||
s->buffer[7] != 0)
{
return T85_INVALID_DATA;
}
s->at_moves++;
break;
case T82_NEWLEN:
s->buf_needed = 6;
if (s->buf_len < 6)
continue;
s->buf_needed = 2;
s->buf_len = 0;
if (!(s->options & T85_VLENGTH))
return T85_INVALID_DATA;
s->options &= ~T85_VLENGTH;
y = pack_32(&s->buffer[2]);
/* An update to the image length is not allowed to stretch it. */
if (y > s->yd)
return T85_INVALID_DATA;
s->yd = y;
break;
case T82_SDNORM:
case T82_SDRST:
if (!(s->options & T85_VLENGTH))
{
/* A plain SDNORM or SDRST with no peek ahead required */
s->buf_len = 0;
if (finish_sde(s))
return T85_INTERRUPT;
/* Check whether this was the last SDE */
if (s->y >= s->yd)
{
s->compressed_image_size -= (len - cnt);
return T85_OK;
}
break;
}
/* This is the messy case. We need to peek ahead, as this element
might be immediately followed by a T82_NEWLEN which affects the
limit of what we decode here. */
if (s->buf_needed < 3)
s->buf_needed = 3;
if (s->buf_len < 3)
continue;
/* Peek ahead to see whether a NEWLEN marker segment follows */
if (s->buffer[2] != T82_ESC)
{
/* This is not an escape sequence, so push the single peek-ahead
byte back into the buffer. We should always have just grabbed
at least one byte, so this should be safe. */
s->buf_needed = 2;
s->buf_len = 0;
cnt--;
/* Process the T82_SDNORM or T82_SDRST */
if (finish_sde(s))
return T85_INTERRUPT;
/* Check whether this was the last SDE */
if (s->y >= s->yd)
{
s->compressed_image_size -= (len - cnt);
return T85_OK;
}
break;
}
if (s->buf_needed < 4)
s->buf_needed = 4;
if (s->buf_len < 4)
continue;
if (s->buffer[3] != T82_NEWLEN)
{
s->buf_needed = 2;
/* Process the T82_SDNORM or T82_SDRST */
if (finish_sde(s))
return T85_INTERRUPT;
/* Check whether this was the last SDE */
if (s->y >= s->yd)
{
s->compressed_image_size -= (len - cnt);
return T85_OK;
}
/* Recycle the two peek-ahead marker sequence bytes to
be processed later. */
s->buffer[0] = s->buffer[2];
s->buffer[1] = s->buffer[3];
s->buf_len = 2;
break;
}
if (s->buf_needed < 8)
s->buf_needed = 8;
if (s->buf_len < 8)
continue;
s->buf_needed = 2;
s->buf_len = 0;
/* We must have a complete T82_NEWLEN to be here, which we need
to process immediately. */
s->options &= ~T85_VLENGTH;
y = pack_32(&s->buffer[4]);
/* An update to the image length is not allowed to stretch it. */
if (y > s->yd)
return T85_INVALID_DATA;
/* Things look OK, so accept this new length, and proceed. */
s->yd = y;
/* Now process the T82_SDNORM or T82_SDRST */
if (finish_sde(s))
return T85_INTERRUPT;
/* We might be at the end of the image now, but even if we are
there should still be a final training T82_SDNORM or T82_SDRST
that we should pick up. When we do, we won't wait for further
T82_NEWLEN entries, so we should stop crisply on the last byte
of the image. */
break;
default:
s->buf_len = 0;
return T85_INVALID_DATA;
}
}
else if (cnt < len && data[cnt] == T82_ESC)
{
s->buffer[s->buf_len++] = data[cnt++];
}
else
{
/* We have found PSCD bytes */
cnt += decode_pscd(s, data + cnt, len - cnt);
if (s->interrupt)
return T85_INTERRUPT;
/* We should only have stopped processing PSCD if
we ran out of data, or hit a T82_ESC */
if (cnt < len && data[cnt] != T82_ESC)
return T85_INVALID_DATA;
}
}
return T85_MORE_DATA;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_set_row_write_handler(t85_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data)
{
s->row_write_handler = handler;
s->row_write_user_data = user_data;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_set_comment_handler(t85_decode_state_t *s,
uint32_t max_comment_len,
t4_row_write_handler_t handler,
void *user_data)
{
s->max_comment_len = max_comment_len;
s->comment_handler = handler;
s->comment_user_data = user_data;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_set_image_size_constraints(t85_decode_state_t *s,
uint32_t max_xd,
uint32_t max_yd)
{
s->max_xd = max_xd;
s->max_yd = max_yd;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t85_decode_get_image_width(t85_decode_state_t *s)
{
return s->xd;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t85_decode_get_image_length(t85_decode_state_t *s)
{
return s->yd;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_get_compressed_image_size(t85_decode_state_t *s)
{
return s->compressed_image_size*8;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s)
{
if (s->current_bit_plane >= s->bit_planes - 1)
return -1;
s->current_bit_plane++;
s->tx = 0;
memset(s->buffer, 0, sizeof(s->buffer));
s->buf_len = 0;
s->buf_needed = 0;
s->at_moves = 0;
memset(s->at_row, 0, sizeof(s->at_row));
memset(s->at_tx, 0, sizeof(s->at_tx));
memset(s->row_h, 0, sizeof(s->row_h));
s->pseudo = FALSE;
s->lntp = FALSE;
s->interrupt = FALSE;
s->end_of_data = 0;
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
s->comment_len = 0;
s->comment_progress = 0;
s->compressed_image_size = 0;
t81_t82_arith_decode_restart(&s->s, FALSE);
s->s.nopadding = s->options & T85_VLENGTH;
s->buf_len = 0;
s->buf_needed = 2;
s->x = 0;
s->y = 0;
s->i = 0;
s->pseudo = TRUE;
s->at_moves = 0;
s->tx = 0;
s->lntp = TRUE;
s->p[0] = 0;
s->p[1] = -1;
s->p[2] = -1;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s)
{
s->xd = 0;
s->yd = 0;
s->l0 = 0;
s->mx = 0;
s->bytes_per_row = 0;
s->tx = 0;
s->bie_len = 0;
memset(s->buffer, 0, sizeof(s->buffer));
s->buf_len = 0;
s->buf_needed = 0;
s->at_moves = 0;
memset(s->at_row, 0, sizeof(s->at_row));
memset(s->at_tx, 0, sizeof(s->at_tx));
memset(s->row_h, 0, sizeof(s->row_h));
s->pseudo = FALSE;
s->lntp = FALSE;
s->interrupt = FALSE;
s->end_of_data = 0;
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
s->comment_len = 0;
s->comment_progress = 0;
s->compressed_image_size = 0;
t81_t82_arith_decode_restart(&s->s, FALSE);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data)
{
if (s == NULL)
{
if ((s = (t85_decode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "T.85");
s->row_write_handler = handler;
s->row_write_user_data = user_data;
s->min_bit_planes = 1;
s->max_bit_planes = 1;
s->max_xd = 0;
s->max_yd = 0;
t81_t82_arith_decode_init(&s->s);
t85_decode_restart(s);
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s)
{
if (s->row_buf)
{
free(s->row_buf);
s->row_buf = NULL;
}
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s)
{
int ret;
ret = t85_decode_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,747 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t85_encode.c - ITU T.85 JBIG for FAX image compression
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009, 2010 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/t85.h"
#include "spandsp/private/logging.h"
#include "spandsp/private/t81_t82_arith_coding.h"
#include "spandsp/private/t85.h"
/* Image length update status */
enum
{
NEWLEN_NONE = 0,
NEWLEN_PENDING = 1,
NEWLEN_HANDLED = 2
};
static __inline__ void unpack_32(uint8_t *s, int32_t value)
{
s[3] = value & 0xFF;
value >>= 8;
s[2] = value & 0xFF;
value >>= 8;
s[1] = value & 0xFF;
value >>= 8;
s[0] = value & 0xFF;
}
/*- End of function --------------------------------------------------------*/
static void put_stuff(t85_encode_state_t *s, const uint8_t buf[], int len)
{
uint8_t *new_buf;
uint32_t bytes_per_row;
if (s->bitstream_iptr + len >= s->bitstream_len)
{
/* TODO: Handle memory allocation errors properly */
/* The number of uncompressed bytes per row seems like a reasonable measure
of what to expect as a poor case for a compressed row. */
bytes_per_row = (s->xd + 7) >> 3;
if ((new_buf = realloc(s->bitstream, s->bitstream_len + len + bytes_per_row)) == NULL)
return;
s->bitstream = new_buf;
s->bitstream_len += (len + bytes_per_row);
}
memcpy(&s->bitstream[s->bitstream_iptr], buf, len);
s->bitstream_iptr += len;
s->compressed_image_size += len;
}
/*- End of function --------------------------------------------------------*/
/* Callback function for the arithmetic encoder */
static void output_byte(void *user_data, int byte)
{
t85_encode_state_t *s;
uint8_t c = byte;
s = (t85_encode_state_t *) user_data;
c = (uint8_t) byte;
put_stuff(s, &c, 1);
}
/*- End of function --------------------------------------------------------*/
static __inline__ void output_esc_code(t85_encode_state_t *s, int code)
{
uint8_t buf[2];
buf[0] = T82_ESC;
buf[1] = code;
put_stuff(s, buf, 2);
}
/*- End of function --------------------------------------------------------*/
static __inline__ void output_newlen(t85_encode_state_t *s)
{
uint8_t buf[6];
if (s->newlen == NEWLEN_PENDING)
{
buf[0] = T82_ESC;
buf[1] = T82_NEWLEN;
unpack_32(&buf[2], s->yd);
put_stuff(s, buf, 6);
if (s->y == s->yd)
{
/* See T.82/6.2.6.2 */
output_esc_code(s, T82_SDNORM);
}
s->newlen = NEWLEN_HANDLED;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void output_comment(t85_encode_state_t *s)
{
uint8_t buf[6];
if (s->comment)
{
buf[0] = T82_ESC;
buf[1] = T82_COMMENT;
unpack_32(&buf[2], s->comment_len);
put_stuff(s, buf, 6);
put_stuff(s, s->comment, s->comment_len);
s->comment = NULL;
s->comment_len = 0;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void output_atmove(t85_encode_state_t *s)
{
uint8_t buf[8];
if (s->new_tx >= 0 && s->new_tx != s->tx)
{
s->tx = s->new_tx;
buf[0] = T82_ESC;
buf[1] = T82_ATMOVE;
unpack_32(&buf[2], 0);
buf[6] = s->tx;
buf[7] = 0;
put_stuff(s, buf, 8);
}
}
/*- End of function --------------------------------------------------------*/
static void generate_bih(t85_encode_state_t *s, uint8_t *buf)
{
/* DL - Initial layer to be transmitted */
buf[0] = 0;
/* D - Number of differential layers */
buf[1] = 0;
/* P - Number of bit planes */
buf[2] = s->bit_planes;
/* Unspecified */
buf[3] = 0;
/* XD - Horizontal image size at layer D */
unpack_32(&buf[4], s->xd);
/* YD - Vertical image size at layer D */
unpack_32(&buf[8], s->yd);
/* L0 - Rows per stripe, at the lowest resolution */
unpack_32(&buf[12], s->l0);
/* MX - Maximum horizontal offset allowed for AT pixel */
buf[16] = s->mx;
/* MY - Maximum vertical offset allowed for AT pixel */
buf[17] = 0;
/* Order byte: */
/* 4 unused bits */
/* HITOLO - transmission order of differential layers */
/* SEQ - indication of progressive-compatible sequential coding */
/* ILEAVE - interleaved transmission order of multiple bit plane */
/* SMID - transmission order of stripes */
/* Note that none of these are relevant to T.85 */
buf[18] = 0;
/* Options byte: */
/* 1 unused bit */
/* LRLTWO - number of reference rows */
/* VLENGTH - indication of possible use of NEWLEN marker segment */
/* TPDON - use of TP for Typical Prediction for differential layers */
/* TPBON - use of TP for base layer */
/* DPON - use of Deterministic Prediction */
/* DPPRIV - use of private DP table */
/* DPLAST - use of last DP table */
/* Note that only T85_TPBON, T85_VLENGTH, and T85_LRLTWO are relevant to T.85 */
buf[19] = s->options;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t85_encode_set_options(t85_encode_state_t *s,
uint32_t l0,
int mx,
int options)
{
if (s->y > 0)
return;
/* Its still OK to change things */
if (l0 >= 1 && l0 <= s->yd)
s->l0 = l0;
if (mx >= 0 && mx <= 127)
s->mx = mx;
if (options >= 0)
s->options = options & (T85_TPBON | T85_VLENGTH | T85_LRLTWO);
}
/*- End of function --------------------------------------------------------*/
static int get_next_row(t85_encode_state_t *s)
{
uint8_t buf[20];
uint32_t bytes_per_row;
const uint8_t *hp[3];
uint8_t *z;
uint32_t row_h[3];
uint32_t j;
int32_t o;
uint32_t a;
uint32_t p;
uint32_t t;
uint32_t c_min;
uint32_t c_max;
uint32_t cl_min;
uint32_t cl_max;
int ltp;
int cx;
int t_max;
int i;
if (s->y >= s->yd)
{
/* We have already finished pumping out the image */
return -1;
}
s->bitstream_iptr = 0;
s->bitstream_optr = 0;
bytes_per_row = (s->xd + 7) >> 3;
/* Rotate the three rows which we buffer */
z = s->prev_row[2];
s->prev_row[2] = s->prev_row[1];
s->prev_row[1] = s->prev_row[0];
s->prev_row[0] = z;
/* Copy the new row to our buffer, and ensure the last byte of the row is
zero padded. The need to tweak this is actually the only reason for
storing a third row. We do not want to tamper with the source buffer. */
if (s->fill_with_white)
{
memset(s->prev_row[0], 0, bytes_per_row);
}
else
{
if (s->row_read_handler(s->row_read_user_data, s->prev_row[0], bytes_per_row) <= 0)
{
/* The source has stopped feeding us rows early. Try to clip the image
to the current size. */
if (t85_encode_set_image_length(s, 1) == 0)
return 0;
/* We can't clip the image to the current length. We will have to
continue up to the original length with blank (all white) rows. */
s->fill_with_white = TRUE;
memset(s->prev_row[0], 0, bytes_per_row);
}
}
if ((s->xd & 7))
s->prev_row[0][bytes_per_row - 1] &= ~((1 << (8 - (s->xd & 7))) - 1);
if (s->current_bit_plane == 0 && s->y == 0)
{
/* Things that need to be done before the first row is encoded */
generate_bih(s, buf);
put_stuff(s, buf, 20);
}
if (s->i == 0)
{
/* Things that need to be done before the next SDE is encoded */
output_newlen(s);
output_comment(s);
output_atmove(s);
if (s->mx == 0)
{
/* Disable ATMOVE analysis */
s->new_tx = 0;
}
else
{
/* Enable ATMOVE analysis */
s->new_tx = -1;
s->c_all = 0;
for (i = 0; i <= s->mx; i++)
s->c[i] = 0;
}
t81_t82_arith_encode_restart(&s->s, TRUE);
}
/* Typical prediction */
ltp = FALSE;
if ((s->options & T85_TPBON))
{
/* Look for a match between the rows */
ltp = (memcmp(s->prev_row[0], s->prev_row[1], bytes_per_row) == 0);
t81_t82_arith_encode(&s->s,
(s->options & T85_LRLTWO) ? TPB2CX : TPB3CX,
(ltp == s->prev_ltp));
s->prev_ltp = ltp;
}
if (!ltp)
{
/* Pointer to the first image byte in each the three rows of interest */
hp[0] = s->prev_row[0];
hp[1] = s->prev_row[1];
hp[2] = s->prev_row[2];
row_h[0] = 0;
row_h[1] = (uint32_t) hp[1][0] << 8;
row_h[2] = (uint32_t) hp[2][0] << 8;
/* Encode row */
for (j = 0; j < s->xd; )
{
row_h[0] |= hp[0][0];
if (j < (bytes_per_row - 1)*8)
{
row_h[1] |= hp[1][1];
row_h[2] |= hp[2][1];
}
if ((s->options & T85_LRLTWO))
{
/* Two row template */
do
{
row_h[0] <<= 1;
row_h[1] <<= 1;
row_h[2] <<= 1;
cx = (row_h[0] >> 9) & 0x00F;
if (s->tx)
{
cx |= ((row_h[1] >> 10) & 0x3E0);
if (j >= (uint32_t) s->tx)
{
o = (j - s->tx) - (j & ~7);
cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 4);
}
}
else
{
cx |= ((row_h[1] >> 10) & 0x3F0);
}
p = (row_h[0] >> 8) & 1;
t81_t82_arith_encode(&s->s, cx, p);
/* Update the statistics for adaptive template changes,
if this analysis is in progress. */
if (s->new_tx < 0 && j >= s->mx && j < s->xd - 2)
{
if (p == ((row_h[1] >> 14) & 1))
s->c[0]++;
for (t = 5; t <= s->mx && t <= j; t++)
{
o = (j - t) - (j & ~7);
a = (hp[0][o >> 3] >> (7 - (o & 7))) & 1;
if (a == p)
s->c[t]++;
}
if (p == 0)
{
for ( ; t <= s->mx; t++)
s->c[t]++;
}
++s->c_all;
}
}
while ((++j & 7) && j < s->xd);
}
else
{
/* Three row template */
do
{
row_h[0] <<= 1;
row_h[1] <<= 1;
row_h[2] <<= 1;
cx = ((row_h[2] >> 8) & 0x380) | ((row_h[0] >> 9) & 0x003);
if (s->tx)
{
cx |= ((row_h[1] >> 12) & 0x078);
if (j >= (uint32_t) s->tx)
{
o = (j - s->tx) - (j & ~7);
cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 2);
}
}
else
{
cx |= ((row_h[1] >> 12) & 0x07C);
}
p = (row_h[0] >> 8) & 1;
t81_t82_arith_encode(&s->s, cx, p);
/* Update the statistics for adaptive template changes,
if this analysis is in progress. */
if (s->new_tx < 0 && j >= s->mx && j < s->xd - 2)
{
if (p == ((row_h[1] >> 14) & 1))
s->c[0]++;
for (t = 3; t <= s->mx && t <= j; t++)
{
o = (j - t) - (j & ~7);
a = (hp[0][o >> 3] >> (7 - (o & 7))) & 1;
if (a == p)
s->c[t]++;
}
if (p == 0)
{
for ( ; t <= s->mx; t++)
s->c[t]++;
}
++s->c_all;
}
}
while ((++j & 7) && j < s->xd);
}
hp[0]++;
hp[1]++;
hp[2]++;
}
}
s->i++;
s->y++;
if (s->i == s->l0 || s->y == s->yd)
{
/* We are at the end of the stripe */
t81_t82_arith_encode_flush(&s->s);
output_esc_code(s, T82_SDNORM);
s->i = 0;
output_newlen(s);
}
/* T.82/Annex C - is it time for an adaptive template change? */
if (s->new_tx < 0 && s->c_all > 2048)
{
c_min =
cl_min = UINT32_MAX;
c_max =
cl_max = 0;
t_max = 0;
for (i = (s->options & T85_LRLTWO) ? 5 : 3; i <= s->mx; i++)
{
if (s->c[i] > c_max)
c_max = s->c[i];
if (s->c[i] < c_min)
c_min = s->c[i];
if (s->c[i] > s->c[t_max])
t_max = i;
}
cl_min = (s->c[0] < c_min) ? s->c[0] : c_min;
cl_max = (s->c[0] > c_max) ? s->c[0] : c_max;
/* This test is straight from T.82/Figure C.2 */
if ((s->c_all - c_max) < (s->c_all >> 3)
&&
(c_max - s->c[s->tx]) > (s->c_all - c_max)
&&
(c_max - s->c[s->tx]) > (s->c_all >> 4)
&&
(c_max - (s->c_all - s->c[s->tx])) > (s->c_all - c_max)
&&
(c_max - (s->c_all - s->c[s->tx])) > (s->c_all >> 4)
&&
(c_max - c_min) > (s->c_all >> 2)
&&
(s->tx || (cl_max - cl_min) > (s->c_all >> 3)))
{
/* It is time to perform an ATMOVE */
s->new_tx = t_max;
}
else
{
/* Disable further analysis */
s->new_tx = s->tx;
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_set_image_width(t85_encode_state_t *s, uint32_t image_width)
{
int bytes_per_row;
uint8_t *t;
if (s->xd == image_width)
return 0;
/* Are we too late to change the width for this page? */
if (s->y > 0)
return -1;
s->xd = image_width;
bytes_per_row = (s->xd + 7) >> 3;
if ((t = (uint8_t *) realloc(s->row_buf, 3*bytes_per_row)) == NULL)
return -1;
s->row_buf = t;
memset(s->row_buf, 0, 3*bytes_per_row);
s->prev_row[0] = s->row_buf;
s->prev_row[1] = s->row_buf + bytes_per_row;
s->prev_row[2] = s->row_buf + 2*bytes_per_row;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_set_image_length(t85_encode_state_t *s, uint32_t length)
{
/* We must have variable length enabled.
We do not allow the length to be changed multiple times.
We only allow an image to be shrunk, and not stretched.
We do not allow the length to become zero. */
if (!(s->options & T85_VLENGTH) || s->newlen == NEWLEN_HANDLED || length >= s->yd || length < 1)
{
/* Invalid parameter */
return -1;
}
if (s->y > 0)
{
/* TODO: If we are already beyond the new length, we scale back the new length silently.
Is there any downside to this? */
if (length < s->y)
length = s->y;
if (s->yd != length)
s->newlen = NEWLEN_PENDING;
}
s->yd = length;
if (s->y == s->yd)
{
/* We are already at the end of the image, so finish it off. */
if (s->i > 0)
{
t81_t82_arith_encode_flush(&s->s);
output_esc_code(s, T82_SDNORM);
s->i = 0;
}
output_newlen(s);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t85_encode_abort(t85_encode_state_t *s)
{
output_esc_code(s, T82_ABORT);
/* Make the image appear to be complete, so the encoder stops outputting.
Take care, as this means s->y is now telling lies. */
s->y = s->yd;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t85_encode_comment(t85_encode_state_t *s, const uint8_t comment[], size_t len)
{
s->comment = comment;
s->comment_len = len;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_get_byte(t85_encode_state_t *s)
{
if (s->bitstream_optr >= s->bitstream_iptr)
{
do
{
if (get_next_row(s) < 0)
return 0x100;
}
while (s->bitstream_iptr == 0);
}
return s->bitstream[s->bitstream_optr++];
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_get_chunk(t85_encode_state_t *s, uint8_t buf[], int max_len)
{
int len;
int n;
for (len = 0; len < max_len; len += n)
{
if (s->bitstream_optr >= s->bitstream_iptr)
{
if (get_next_row(s) < 0)
return len;
}
n = s->bitstream_iptr - s->bitstream_optr;
if (n > max_len - len)
n = max_len - len;
memcpy(&buf[len], &s->bitstream[s->bitstream_optr], n);
s->bitstream_optr += n;
}
return len;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t85_encode_get_image_width(t85_encode_state_t *s)
{
return s->xd;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t85_encode_get_image_length(t85_encode_state_t *s)
{
return s->yd;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_get_compressed_image_size(t85_encode_state_t *s)
{
return s->compressed_image_size*8;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_set_row_read_handler(t85_encode_state_t *s,
t4_row_read_handler_t handler,
void *user_data)
{
s->row_read_handler = handler;
s->row_read_user_data = user_data;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_restart(t85_encode_state_t *s, uint32_t image_width, uint32_t image_length)
{
int bytes_per_row;
/* Allow the image width to be anything, although only a few values are actually
permitted by the T.85 and T.4 specs. Higher levels in the stack need to impose
these restrictions. */
t85_encode_set_image_width(s, image_width);
bytes_per_row = (s->xd + 7) >> 3;
memset(s->row_buf, 0, 3*bytes_per_row);
s->yd = image_length;
s->comment = NULL;
s->comment_len = 0;
s->y = 0;
s->i = 0;
s->newlen = NEWLEN_NONE;
s->new_tx = -1;
s->tx = 0;
s->prev_ltp = FALSE;
s->bitstream_iptr = 0;
s->bitstream_optr = 0;
if (s->bitstream)
{
free(s->bitstream);
s->bitstream = NULL;
}
s->bitstream_len = 0;
s->fill_with_white = FALSE;
s->compressed_image_size = 0;
t81_t82_arith_encode_init(&s->s, output_byte, s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t85_encode_state_t *) t85_encode_init(t85_encode_state_t *s,
uint32_t image_width,
uint32_t image_length,
t4_row_read_handler_t handler,
void *user_data)
{
if (s == NULL)
{
if ((s = (t85_encode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "T.85");
s->row_read_handler = handler;
s->row_read_user_data = user_data;
/* T.85 BASIC setting for L0. In T.85 this may only be changed if T.30 negotiation permits. */
s->l0 = 128;
/* No ATMOVE pending */
s->mx = 127;
/* Default options */
s->options = T85_TPBON | T85_VLENGTH;
s->bitstream = NULL;
s->bitstream_len = 0;
s->bit_planes = 1;
s->current_bit_plane = 0;
t85_encode_restart(s, image_width, image_length);
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_release(t85_encode_state_t *s)
{
if (s->row_buf)
{
free(s->row_buf);
s->row_buf = NULL;
}
if (s->bitstream)
{
free(s->bitstream);
s->bitstream = NULL;
s->bitstream_len = 0;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t85_encode_free(t85_encode_state_t *s)
{
int ret;
ret = t85_encode_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -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

View File

@ -0,0 +1,238 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t81_t82_arith_coding_tests.c - Tests for the ITU T.81 and T.82 arithmetic
* encoder/decoder, based on the test description
* in T.82
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
/*! \page t81_t82_arith_coding_tests_page T.81 and T.82 Arithmetic encoder/decoder tests
\section t81_t82_arith_coding_tests_pagesec_1 What does it do
These tests exercise the arithmetic encoder and decoder for T.81 and T.82. As T.85 is based
on T.82, this is also the arithmetic coder for T.85.
These tests are based on T.82 section 7. Nothing beyond the prescibed tests is performed at
the present time.
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
//#if defined(WITH_SPANDSP_INTERNALS)
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
//#endif
#include "spandsp.h"
#define MSG_SIZE 10000
#define FALSE 0
#define TRUE (!FALSE)
uint8_t msg[MSG_SIZE];
int32_t msg_len;
static void write_byte(void *user_data, int byte)
{
if (msg_len < MSG_SIZE)
msg[msg_len++] = byte;
}
/*- End of function --------------------------------------------------------*/
int main(int argc, char *argv[])
{
t81_t82_arith_encode_state_t *se;
t81_t82_arith_decode_state_t *sd;
int i;
int j;
int test_failed;
int pix;
const uint8_t *pp;
/* Test data from T.82 7.1 */
static const uint16_t pix_7_1[16] =
{
0x05E0, 0x0000, 0x8B00, 0x01C4, 0x1700, 0x0034, 0x7FFF, 0x1A3F,
0x951B, 0x05D8, 0x1D17, 0xE770, 0x0000, 0x0000, 0x0656, 0x0E6A
};
/* Test data from T.82 7.1 */
static const uint16_t cx_7_1[16] =
{
0x0FE0, 0x0000, 0x0F00, 0x00F0, 0xFF00, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
/* Test data from T.82 7.1 - scd with stuffing and SDNORM termination */
static const uint8_t sde_7_1[32] =
{
0x69, 0x89, 0x99, 0x5C, 0x32, 0xEA, 0xFA, 0xA0,
0xD5, 0xFF, 0x00, 0x52, 0x7F, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x3F,
0xFF, 0x00, 0x2D, 0x20, 0x82, 0x91, 0xFF, 0x02
};
#define SDE_7_1_LEN 30 /* Don't include the termination SDNORM */
#define SDE_7_1_FULL_LEN 32 /* Include the termination SDNORM */
printf("T.81/T.82 arithmetic encoder tests, from ITU-T T.82\n\n");
printf("Arithmetic encoder tests from ITU-T T.82/7.1\n");
if ((se = t81_t82_arith_encode_init(NULL, write_byte, NULL)) == NULL)
{
fprintf(stderr, "Failed to allocate arithmetic encoder!\n");
exit(2);
}
msg_len = 0;
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
{
t81_t82_arith_encode(se,
(cx_7_1[i] >> (15 - j)) & 1,
(pix_7_1[i] >> (15 - j)) & 1);
}
}
t81_t82_arith_encode_flush(se);
if (msg_len != SDE_7_1_LEN || memcmp(msg, sde_7_1, SDE_7_1_LEN))
{
printf("Encoded data: ");
for (i = 0; i < msg_len; i++)
printf("%02X", msg[i]);
printf("\n");
printf("Expected data: ");
for (i = 0; i < SDE_7_1_LEN; i++)
printf("%02X", sde_7_1[i]);
printf("\n");
printf("Test failed\n");
exit(2);
}
printf("Test passed\n");
printf("Arithmetic decoder tests from ITU-T T.82/7.1\n");
printf("Decoding byte by byte...\n");
test_failed = FALSE;
if ((sd = t81_t82_arith_decode_init(NULL)) == NULL)
{
fprintf(stderr, "Failed to allocate arithmetic decoder!\n");
exit(2);
}
pp = sde_7_1;
sd->pscd_ptr = pp;
sd->pscd_end = pp + 1;
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
{
for (;;)
{
pix = t81_t82_arith_decode(sd, (cx_7_1[i] >> (15 - j)) & 1);
if ((pix >= 0 || sd->pscd_end >= sde_7_1 + SDE_7_1_FULL_LEN))
break;
pp++;
if (sd->pscd_ptr != pp - 1)
sd->pscd_ptr = pp;
sd->pscd_end = pp + 1;
}
if (pix < 0)
{
printf("Bad pixel %d, byte %" PRIdPTR ".\n\n",
i*16 + j + 1,
sd->pscd_ptr - sd->pscd_end);
test_failed = TRUE;
break;
}
if (pix != ((pix_7_1[i] >> (15 - j)) & 1))
{
printf("Bad PIX (%d) at pixel %d.\n\n",
pix,
i*16 + j + 1);
test_failed = TRUE;
break;
}
}
}
if (sd->pscd_ptr != sd->pscd_end - 2)
{
printf("%" PRIdPTR " bytes left after decoder finished.\n\n",
sd->pscd_end - sd->pscd_ptr - 2);
test_failed = TRUE;
}
if (test_failed)
{
printf("Test failed\n");
exit(2);
}
printf("Test passed\n");
printf("Decoding chunk by chunk...\n");
test_failed = FALSE;
t81_t82_arith_decode_init(sd);
sd->pscd_ptr = sde_7_1;
sd->pscd_end = sde_7_1 + SDE_7_1_FULL_LEN;
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
{
pix = t81_t82_arith_decode(sd, (cx_7_1[i] >> (15 - j)) & 1);
if (pix < 0)
{
printf("Bad pixel %d, byte %" PRIdPTR ".\n\n",
i*16 + j + 1,
sd->pscd_ptr - sd->pscd_end);
test_failed = TRUE;
break;
}
if (pix != ((pix_7_1[i] >> (15 - j)) & 1))
{
printf("Bad PIX (%d) at pixel %d.\n\n",
pix,
i*16 + j + 1);
test_failed = TRUE;
break;
}
}
}
if (sd->pscd_ptr != sd->pscd_end - 2)
{
printf("%" PRIdPTR " bytes left after decoder finished.\n\n",
sd->pscd_end - sd->pscd_ptr - 2);
test_failed = TRUE;
}
if (test_failed)
{
printf("Test failed\n");
exit(2);
}
printf("Test passed\n");
printf("Tests passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,369 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* t85_tests.c - ITU T.85 FAX image compression and decompression tests
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2009 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* These tests are based on code from Markus Kuhn's jbigkit. See
* http://www.cl.cam.ac.uk/~mgk25/
*
* jbigkit is GPL2 licenced. This file is also GPL2 licenced, and our
* T.85 code is LGPL2.1 licenced. There are no licence incompatibilities
* in this reuse of Markus's work.
*/
/*! \file */
/*! \page t85_tests_page T.85 image compress and decompression tests
\section t85_tests_page_sec_1 What does it do
These tests exercise the image compression and decompression methods defined
in ITU specifications T.85.
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
//#if defined(WITH_SPANDSP_INTERNALS)
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
//#endif
#include "spandsp.h"
#define TESTBUF_SIZE 400000
#define TEST_IMAGE_SIZE (1951*1960/8)
#define FALSE 0
#define TRUE (!FALSE)
uint8_t testbuf[TESTBUF_SIZE];
uint8_t test_image[TEST_IMAGE_SIZE];
size_t testbuf_len;
int read_row = 0;
int write_row = 0;
int clip_to_row = 0;
static int row_read_handler(void *user_data, uint8_t buf[], size_t len)
{
//t85_encode_state_t *s;
//s = (t85_encode_state_t *) user_data;
memcpy(buf, &test_image[len*read_row], len);
//printf("Read row %d\n", read_row);
if (clip_to_row && read_row == clip_to_row)
{
clip_to_row = 0;
return 0;
}
read_row++;
return len;
}
/*- End of function --------------------------------------------------------*/
static int row_write_handler(void *user_data, const uint8_t buf[], size_t len)
{
uint8_t *bitmap;
bitmap = (uint8_t *) user_data;
memcpy(&bitmap[len*write_row], buf, len);
//printf("Write row %d\n", write_row);
write_row++;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int comment_handler(void *user_data, const uint8_t buf[], size_t len)
{
if (buf)
printf("Comment (%lu): %s\n", (unsigned long int) len, buf);
else
printf("Comment (%lu): ---\n", (unsigned long int) len);
return 0;
}
/*- End of function --------------------------------------------------------*/
static void create_test_image(uint8_t *pic)
{
int i;
int j;
uint32_t sum;
uint32_t prsg;
uint32_t repeat[8];
uint8_t *p;
/* Cook up the test image defined in T.82/7.2.1. This image is 1960 x 1951
pixels, and occupies a single plane (which it has to for T.85). */
memset(pic, 0, TEST_IMAGE_SIZE);
p = pic;
prsg = 1;
for (i = 0; i < 1951; i++)
{
for (j = 0; j < 1960; j++)
{
if (i >= 192)
{
if (i < 1023 || (j & (3 << 3)) == 0)
{
sum = (prsg & 1)
+ ((prsg >> 2) & 1)
+ ((prsg >> 11) & 1)
+ ((prsg >> 15) & 1);
prsg = (prsg << 1) + (sum & 1);
if ((prsg & 3) == 0)
{
*p |= (1 << (7 - (j & 7)));
repeat[j & 7] = 1;
}
else
{
repeat[j & 7] = 0;
}
}
else
{
if (repeat[j & 7])
*p |= 1 << (7 - (j & 7));
}
}
if ((j & 7) == 7)
++p;
}
}
/* Verify the test image has been generated OK, by checking the number of set pixels */
sum = 0;
for (i = 0; i < TEST_IMAGE_SIZE; i++)
{
for (j = 0; j < 8; j++)
sum += ((pic[i] >> j) & 1);
}
if (sum != 861965)
{
printf("WARNING: Test image has %" PRIu32 " foreground pixels. There should be 861965.\n",
sum);
}
}
/*- End of function --------------------------------------------------------*/
/* Perform a test cycle, as defined in T.82/7, with one set of parameters. */
static int test_cycle(const char *test_id,
const uint8_t *image,
uint32_t width,
uint32_t height,
uint32_t l0,
int mx,
int options,
int optionsx,
const uint8_t *comment,
size_t correct_length)
{
t85_encode_state_t t85_enc;
t85_decode_state_t t85_dec;
long int l;
size_t image_size;
int result;
int len;
int max_len;
size_t bytes_per_row;
size_t cnt_a;
size_t cnt_b;
uint8_t *decoded_image;
printf("%s: TPBON=%d, LRLTWO=%d, Mx=%d, L0=%" PRIu32 "\n",
test_id,
(options & T85_TPBON) ? 1 : 0,
(options & T85_LRLTWO) ? 1 : 0,
mx,
l0);
printf("%s.1: Encode\n", test_id);
bytes_per_row = (width + 7)/8;
image_size = bytes_per_row*height;
if ((optionsx & T85_VLENGTH))
{
t85_encode_init(&t85_enc, width, height + 10, row_read_handler, &t85_enc);
clip_to_row = height;
}
else
{
t85_encode_init(&t85_enc, width, height, row_read_handler, &t85_enc);
clip_to_row = 0;
}
read_row = 0;
t85_encode_set_options(&t85_enc, l0, mx, options);
/* A comment inserted here should always succeed. The later one, inserted some way
down the image, will only succeed if a new chunk is started afterwards. */
if (comment)
t85_encode_comment(&t85_enc, comment, strlen((const char *) comment) + 1);
testbuf_len = 0;
max_len = 100;
while ((len = t85_encode_get_chunk(&t85_enc, &testbuf[testbuf_len], max_len)) > 0)
{
testbuf_len += len;
max_len = 100;
if (testbuf_len + 100 > TESTBUF_SIZE)
max_len = TESTBUF_SIZE - testbuf_len;
if (comment && testbuf_len == 1000)
t85_encode_comment(&t85_enc, comment, strlen((const char *) comment) + 1);
}
t85_encode_release(&t85_enc);
printf("Encoded BIE has %lu bytes\n", (unsigned long int) testbuf_len);
if (correct_length > 0)
{
if (testbuf_len != correct_length)
{
printf("Incorrect encoded length. Should have been %lu\n", (unsigned long int) correct_length);
printf("Test failed\n");
exit(2);
}
printf("Test passed\n");
}
printf("%s.2: Decode in one big chunk\n", test_id);
if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
{
fprintf(stderr, "Out of memory!\n");
exit(2);
}
t85_decode_init(&t85_dec, row_write_handler, decoded_image);
if (comment && comment[0] != 'X')
t85_decode_set_comment_handler(&t85_dec, 1000, comment_handler, NULL);
write_row = 0;
result = t85_decode_put_chunk(&t85_dec, testbuf, testbuf_len);
if (result == T85_MORE_DATA)
result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
cnt_a = t85_encode_get_compressed_image_size(&t85_enc);
cnt_b = t85_decode_get_compressed_image_size(&t85_dec);
if (cnt_a != cnt_b || cnt_a != testbuf_len*8 || result != T85_OK)
{
printf("Decode result %d\n", result);
printf("%ld/%ld bits of %ld bits of BIE read. %lu lines decoded.\n",
(long int) cnt_a,
(long int) cnt_b,
(unsigned long int) testbuf_len*8,
(unsigned long int) t85_dec.y);
printf("Test failed\n");
exit(2);
}
if (memcmp(decoded_image, image, image_size))
{
printf("Image mismatch\n");
printf("Test failed\n");
exit(2);
}
free(decoded_image);
t85_decode_release(&t85_dec);
printf("Test passed\n");
printf("%s.3: Decode byte by byte\n", test_id);
if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
{
fprintf(stderr, "Out of memory!\n");
exit(2);
}
t85_decode_init(&t85_dec, row_write_handler, decoded_image);
if (comment && comment[0] != 'X')
t85_decode_set_comment_handler(&t85_dec, 1000, comment_handler, NULL);
write_row = 0;
result = T85_MORE_DATA;
for (l = 0; l < testbuf_len; l++)
{
result = t85_decode_put_chunk(&t85_dec, &testbuf[l], 1);
if (result != T85_MORE_DATA)
{
l++;
break;
}
}
if (result == T85_MORE_DATA)
result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
if (l != testbuf_len || result != T85_OK)
{
printf("Decode result %d\n", result);
printf("%ld bytes of %ld bytes of BIE read. %lu lines decoded.\n",
(long int) l,
(unsigned long int) testbuf_len,
(unsigned long int) t85_dec.y);
printf("Test failed\n");
exit(2);
}
if (memcmp(decoded_image, image, image_size))
{
printf("Image mismatch\n");
printf("Test failed\n");
exit(2);
}
free(decoded_image);
t85_decode_release(&t85_dec);
printf("Test passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
int main(int argc, char **argv)
{
printf("T.85 JBIG for FAX encoder and decoder tests, from ITU-T T.82\n\n");
printf("Generating the test image from T.82...\n");
create_test_image(test_image);
/* Run through the tests in T.82/7.2, which are applicable to T.85 */
test_cycle("1", test_image, 1960, 1951, 1951, 0, 0, 0, NULL, 317384);
test_cycle("2", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, NULL, 317132);
test_cycle("3", test_image, 1960, 1951, 128, 8, T85_TPBON, 0, NULL, 253653);
/* Again with a comment added and handled */
test_cycle("4", test_image, 1960, 1951, 1951, 0, 0, 0, (const uint8_t *) "Comment 4", 317384 + 16);
test_cycle("5", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Comment 5", 317132 + 16);
test_cycle("6", test_image, 1960, 1951, 128, 8, T85_TPBON, 0, (const uint8_t *) "Comment 6", 253653 + 2*16);
/* Again with a comment added, but not handled */
test_cycle("7", test_image, 1960, 1951, 1951, 0, 0, 0, (const uint8_t *) "Xomment 7", 317384 + 16);
test_cycle("8", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Xomment 8", 317132 + 16);
test_cycle("9", test_image, 1960, 1951, 128, 8, T85_TPBON, 0, (const uint8_t *) "Xomment 9", 253653 + 2*16);
/* Again with the image variable length and prematurely terminated */
test_cycle("10", test_image, 1960, 1951, 1951, 0, T85_VLENGTH, T85_VLENGTH, NULL, 317384 + 8);
test_cycle("11", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, T85_VLENGTH, NULL, 317132 + 8);
test_cycle("12", test_image, 1960, 1951, 128, 8, T85_VLENGTH | T85_TPBON, T85_VLENGTH, NULL, 253653 + 8);
/* Again with the image variable length but not prematurely terminated */
test_cycle("13", test_image, 1960, 1951, 1951, 0, T85_VLENGTH, 0, NULL, 317384);
test_cycle("14", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, 0, NULL, 317132);
test_cycle("15", test_image, 1960, 1951, 128, 8, T85_VLENGTH | T85_TPBON, 0, NULL, 253653);
printf("Tests passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/