Introducing T.85 encode and decodei to spandsp. This is not connected into the FAX engine in this update.
This commit is contained in:
parent
287678bc56
commit
5d7e26141f
|
@ -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 \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
Loading…
Reference in New Issue