diff --git a/libs/spandsp/spandsp/fax-tests.xml b/libs/spandsp/spandsp/fax-tests.xml
new file mode 100644
index 0000000000..e770e244b8
--- /dev/null
+++ b/libs/spandsp/spandsp/fax-tests.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c
new file mode 100644
index 0000000000..9221a9afa7
--- /dev/null
+++ b/libs/spandsp/src/image_translate.c
@@ -0,0 +1,466 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate.c - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if defined(HAVE_TGMATH_H)
+#include
+#endif
+#if defined(HAVE_MATH_H)
+#include
+#endif
+#include "floating_fudge.h"
+#include
+#include
+
+#include "spandsp/telephony.h"
+#include "spandsp/fast_convert.h"
+#include "spandsp/logging.h"
+#include "spandsp/saturated.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#if defined(SPANDSP_SUPPORT_T85)
+#include "spandsp/t81_t82_arith_coding.h"
+#include "spandsp/t85.h"
+#endif
+#include "spandsp/t4_t6_decode.h"
+#include "spandsp/t4_t6_encode.h"
+#include "spandsp/image_translate.h"
+
+#include "spandsp/private/logging.h"
+#if defined(SPANDSP_SUPPORT_T85)
+#include "spandsp/private/t81_t82_arith_coding.h"
+#include "spandsp/private/t85.h"
+#endif
+#include "spandsp/private/t4_t6_decode.h"
+#include "spandsp/private/t4_t6_encode.h"
+#include "spandsp/private/t4_rx.h"
+#include "spandsp/private/t4_tx.h"
+#include "spandsp/private/image_translate.h"
+
+static int image_colour16_to_gray8_row(uint8_t mono[], uint16_t colour[], int pixels)
+{
+ int i;
+ uint32_t gray;
+
+ for (i = 0; i < pixels; i++)
+ {
+ gray = colour[3*i]*19595 + colour[3*i + 1]*38469 + colour[3*i + 2]*7472;
+ mono[i] = saturateu8(gray >> 24);
+ }
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_colour8_to_gray8_row(uint8_t mono[], uint8_t colour[], int pixels)
+{
+ int i;
+ uint32_t gray;
+
+ for (i = 0; i < pixels; i++)
+ {
+ gray = colour[3*i]*19595 + colour[3*i + 1]*38469 + colour[3*i + 2]*7472;
+ mono[i] = saturateu8(gray >> 16);
+ }
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_gray16_to_gray8_row(uint8_t mono[], uint16_t gray[], int pixels)
+{
+ int i;
+
+ for (i = 0; i < pixels; i++)
+ mono[i] = gray[i] >> 8;
+ return pixels;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int row_len;
+
+ row_len = (*s->row_read_handler)(s->row_read_user_data, buf, s->input_width*s->bytes_per_pixel);
+ if (row_len != s->input_width*s->bytes_per_pixel)
+ return 0;
+ /* Scrunch colour down to gray, and scrunch 16 bit pixels down to 8 bit pixels */
+ switch (s->input_format)
+ {
+ case IMAGE_TRANSLATE_FROM_GRAY_16:
+ image_gray16_to_gray8_row(buf, (uint16_t *) buf, s->input_width);
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_16:
+ image_colour16_to_gray8_row(buf, (uint16_t *) buf, s->input_width);
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_8:
+ image_colour8_to_gray8_row(buf, buf, s->input_width);
+ break;
+ }
+ return row_len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int image_resize_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int i;
+ int output_width;
+ int output_length;
+ int input_width;
+ int input_length;
+ double c1;
+ double c2;
+ double int_part;
+ int x;
+#if defined(SPANDSP_USE_FIXED_POINT)
+ int frac_row;
+ int frac_col;
+#else
+ double frac_row;
+ double frac_col;
+#endif
+ int row_len;
+ int skip;
+ uint8_t *p;
+
+ if (s->raw_output_row < 0)
+ return 0;
+ output_width = s->output_width - 1;
+ output_length = s->output_length - 1;
+ input_width = s->input_width - 1;
+ input_length = s->input_length - 1;
+
+ skip = s->raw_output_row*input_length/output_length;
+ if (skip >= s->raw_input_row)
+ {
+ skip++;
+ while (skip >= s->raw_input_row)
+ {
+ if (s->raw_input_row >= s->input_length)
+ {
+ s->raw_output_row = -1;
+ break;
+ }
+ row_len = get_and_scrunch_row(s, s->raw_pixel_row[0], s->input_width*s->bytes_per_pixel);
+ if (row_len != s->input_width*s->bytes_per_pixel)
+ {
+ s->raw_output_row = -1;
+ return 0;
+ }
+ s->raw_input_row++;
+ p = s->raw_pixel_row[0];
+ s->raw_pixel_row[0] = s->raw_pixel_row[1];
+ s->raw_pixel_row[1] = p;
+ }
+ }
+
+#if defined(SPANDSP_USE_FIXED_POINT)
+ frac_row = s->raw_output_row*input_length/output_length;
+ frac_row = s->raw_output_row*input_length - frac_row*output_length;
+ for (i = 0; i < output_width; i++)
+ {
+ x = i*input_width/output_width;
+ frac_col = x - x*output_width;
+ c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col;
+ c1 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col;
+ buf[i] = saturateu8(c1 + (c2 - c1)*frac_row);
+ }
+#else
+ frac_row = modf((double) s->raw_output_row*input_length/output_length, &int_part);
+ for (i = 0; i < output_width; i++)
+ {
+ frac_col = modf((double) i*input_width/output_width, &int_part);
+ x = int_part;
+ c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col;
+ c2 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col;
+ buf[i] = saturateu8(c1 + (c2 - c1)*frac_row);
+ }
+#endif
+ if (++s->raw_output_row >= s->output_length)
+ s->raw_output_row = -1;
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ uint8_t find_closest_palette_color(int in)
+{
+ return (in >= 128) ? 255 : 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len)
+{
+ int x;
+ int y;
+ int i;
+ int j;
+ int limit;
+ int old_pixel;
+ int new_pixel;
+ int quant_error;
+ uint8_t *p;
+ uint8_t xx;
+
+ if (s->output_row < 0)
+ return 0;
+ y = s->output_row++;
+ /* This algorithm works over two rows, and outputs the earlier of the two. To
+ make this work:
+ - At row 0 we grab and scrunch two rows.
+ - From row 1 up to the last row we grab one new additional row each time.
+ - At the last row we dither and output, without getting an extra row in. */
+ for (i = (y == 0) ? 0 : 1; i < 2; i++)
+ {
+ p = s->pixel_row[0];
+ s->pixel_row[0] = s->pixel_row[1];
+ s->pixel_row[1] = p;
+
+ /* If this is the end of the image just ignore that there is now rubbish in pixel_row[1].
+ Mark that the end has occurred. This row will be properly output, and the next one
+ will fail, with the end of image condition (i.e. returning zero length) */
+ if (s->resize)
+ {
+ if (image_resize_row(s, s->pixel_row[1], s->output_width*s->bytes_per_pixel) != s->output_width*s->bytes_per_pixel)
+ s->output_row = -1;
+ }
+ else
+ {
+ if (get_and_scrunch_row(s, s->pixel_row[1], s->output_width*s->bytes_per_pixel) != s->output_width*s->bytes_per_pixel)
+ s->output_row = -1;
+ }
+ }
+ /* Apply Floyd-Steinberg dithering to the 8 bit pixels, using a bustrophodontic
+ scan, to reduce the grayscale image to pure black and white */
+ /* The first and last pixels in each row need special treatment, so we do not
+ step outside the row. */
+ if ((y & 1))
+ {
+ x = s->output_width - 1;
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16);
+ for ( ; x > 0; x--)
+ {
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16);
+ }
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ }
+ else
+ {
+ x = 0;
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16);
+ for ( ; x < s->output_width - 1; x++)
+ {
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16);
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16);
+ }
+ old_pixel = s->pixel_row[0][x];
+ new_pixel = find_closest_palette_color(old_pixel);
+ quant_error = old_pixel - new_pixel;
+ s->pixel_row[0][x + 0] = new_pixel;
+ s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16);
+ s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16);
+ }
+ /* Now bit pack the pixel per byte row into a pixel per bit row. */
+ for (i = 0, x = 0; x < s->output_width; i++, x += 8)
+ {
+ xx = 0;
+ /* Allow for the possibility that the width is not a multiple of 8 */
+ limit = (8 <= s->output_width - x) ? 8 : (s->output_width - x);
+ for (j = 0; j < limit; j++)
+ {
+ if (s->pixel_row[0][x + j] <= 128)
+ xx |= (1 << (7 - j));
+ }
+ buf[i] = xx;
+ }
+ return i;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_get_output_width(image_translate_state_t *s)
+{
+ return s->output_width;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_get_output_length(image_translate_state_t *s)
+{
+ return s->output_length;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
+ int input_format,
+ int input_width,
+ int input_length,
+ int output_width,
+ t4_row_read_handler_t row_read_handler,
+ void *row_read_user_data)
+{
+ int i;
+
+ if (s == NULL)
+ {
+ if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+
+ s->input_format = input_format;
+
+ s->input_width = input_width;
+ s->input_length = input_length;
+
+ s->resize = (output_width > 0);
+ s->output_width = (s->resize) ? output_width : s->input_width;
+ s->output_length = (s->resize) ? s->input_length*s->output_width/s->input_width : s->input_length;
+
+ switch (s->input_format)
+ {
+ case IMAGE_TRANSLATE_FROM_GRAY_8:
+ s->bytes_per_pixel = 1;
+ break;
+ case IMAGE_TRANSLATE_FROM_GRAY_16:
+ s->bytes_per_pixel = 2;
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_8:
+ s->bytes_per_pixel = 3;
+ break;
+ case IMAGE_TRANSLATE_FROM_COLOUR_16:
+ s->bytes_per_pixel = 6;
+ break;
+ default:
+ s->bytes_per_pixel = 1;
+ break;
+ }
+
+ /* Allocate the two row buffers we need, using the space requirements we now have */
+ if (s->resize)
+ {
+ for (i = 0; i < 2; i++)
+ {
+ if ((s->raw_pixel_row[i] = (uint8_t *) malloc(s->input_width*s->bytes_per_pixel)) == NULL)
+ return NULL;
+ memset(s->raw_pixel_row[i], 0, s->input_width*s->bytes_per_pixel);
+ if ((s->pixel_row[i] = (uint8_t *) malloc(s->output_width*sizeof(uint8_t))) == NULL)
+ return NULL;
+ memset(s->pixel_row[i], 0, s->output_width*sizeof(uint8_t));
+ }
+ }
+ else
+ {
+ for (i = 0; i < 2; i++)
+ {
+ if ((s->pixel_row[i] = (uint8_t *) malloc(s->output_width*s->bytes_per_pixel)) == NULL)
+ return NULL;
+ memset(s->pixel_row[i], 0, s->output_width*s->bytes_per_pixel);
+ }
+ }
+
+ s->row_read_handler = row_read_handler;
+ s->row_read_user_data = row_read_user_data;
+
+ s->raw_input_row = 0;
+ s->raw_output_row = 0;
+ s->output_row = 0;
+
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (s->raw_pixel_row[i])
+ {
+ free(s->raw_pixel_row[i]);
+ s->raw_pixel_row[i] = NULL;
+ }
+ if (s->pixel_row[i])
+ {
+ free(s->pixel_row[i]);
+ s->pixel_row[i] = NULL;
+ }
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s)
+{
+ int res;
+
+ res = image_translate_release(s);
+ free(s);
+ return res;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/image_translate.h b/libs/spandsp/src/spandsp/image_translate.h
new file mode 100644
index 0000000000..b795c705af
--- /dev/null
+++ b/libs/spandsp/src/spandsp/image_translate.h
@@ -0,0 +1,108 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate.h - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_IMAGE_TRANSLATE_H_)
+#define _SPANDSP_IMAGE_TRANSLATE_H_
+
+/*! \page image_translate_page Image translation
+\section image_translate_page_sec_1 What does it do?
+
+\section image_translate_page_sec_2 How does it work?
+
+\section image_translate_page_sec_3 How do I use it?
+*/
+
+typedef struct image_translate_state_s image_translate_state_t;
+
+enum
+{
+ IMAGE_TRANSLATE_FROM_MONO = 1,
+ IMAGE_TRANSLATE_FROM_GRAY_8 = 2,
+ IMAGE_TRANSLATE_FROM_GRAY_16 = 3,
+ IMAGE_TRANSLATE_FROM_COLOUR_8 = 4,
+ IMAGE_TRANSLATE_FROM_COLOUR_16 = 5
+};
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/*! \brief Get the next row of a translated image.
+ \param s The image translation context.
+ \return the length of the row buffer, in bytes */
+SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len);
+
+/*! \brief Get the width of the image being produced by an image translation context.
+ \param s The image translation context.
+ \return The width of the output image, in pixel. */
+SPAN_DECLARE(int) image_translate_get_output_width(image_translate_state_t *s);
+
+/*! \brief Get the length of the image being produced by an image translation context.
+ \param s The image translation context.
+ \return The length of the output image, in pixel. */
+SPAN_DECLARE(int) image_translate_get_output_length(image_translate_state_t *s);
+
+/*! \brief Initialise an image translation context for rescaling and squashing a gray scale
+ or colour image to a bi-level FAX type image.
+ \param s The image translation context.
+ \param input_format x
+ \param input_width The width of the source image, in pixels.
+ \param input_length The length of the source image, in pixels.
+ \param output_width The width of the output image, in pixels. The length of the output image
+ will be derived automatically from this and the source image dimension, to main the
+ geometry of the original image.
+ \param row_read_handler A callback routine used to pull rows of pixels from the source image
+ into the translation process.
+ \param row_read_user_data An opaque point passed to read_row_handler
+ \return A pointer to the context, or NULL if there was a problem. */
+SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s,
+ int input_format,
+ int input_width,
+ int input_length,
+ int output_width,
+ t4_row_read_handler_t row_read_handler,
+ void *row_read_user_data);
+
+/*! \brief Release the resources associated with an image translation context.
+ \param s The image translation context.
+ \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s);
+
+/*! \brief Free the resources associated with an image translation context.
+ \param s The image translation context.
+ \return 0 for success, otherwise -1. */
+SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/image_translate.h b/libs/spandsp/src/spandsp/private/image_translate.h
new file mode 100644
index 0000000000..a4f7daa81f
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/image_translate.h
@@ -0,0 +1,52 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/image_translate.c - Image translation routines for reworking colour
+ * and gray scale images to be bi-level images of an
+ * appropriate size to be FAX compatible.
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_IMAGE_TRANSLATE_H_)
+#define _SPANDSP_PRIVATE_IMAGE_TRANSLATE_H_
+
+struct image_translate_state_s
+{
+ int input_format;
+ int input_width;
+ int input_length;
+ int output_width;
+ int output_length;
+ int resize;
+ int bytes_per_pixel;
+ int raw_input_row;
+ int raw_output_row;
+ int output_row;
+
+ uint8_t *raw_pixel_row[2];
+ uint8_t *pixel_row[2];
+
+ t4_row_read_handler_t row_read_handler;
+ void *row_read_user_data;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/t4_t6_decode.h b/libs/spandsp/src/spandsp/private/t4_t6_decode.h
new file mode 100644
index 0000000000..1b15fc7f61
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/t4_t6_decode.h
@@ -0,0 +1,101 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t4_t6_decode.h - definitions for T.4/T.6 fax decoding
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2003, 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_T4_T6_DECODE_H_)
+#define _SPANDSP_PRIVATE_T4_T6_DECODE_H_
+
+/*!
+ T.4 1D, T4 2D and T6 decompressor state.
+*/
+struct t4_t6_decode_state_s
+{
+ /*! \brief The type of compression used between the FAX machines. */
+ //int encoding;
+ /*! \brief Width of the current page, in pixels. */
+ //int image_width;
+
+ /*! \brief Callback function to write a row of pixels to the image destination. */
+ t4_row_write_handler_t row_write_handler;
+ /*! \brief Opaque pointer passed to row_write_handler. */
+ void *row_write_user_data;
+
+ /*! \brief A pointer into the image buffer indicating where the last row begins */
+ int last_row_starts_at;
+
+ /*! \brief This variable is used to count the consecutive EOLS we have seen. If it
+ reaches six, this is the end of the image. It is initially set to -1 for
+ 1D and 2D decoding, as an indicator that we must wait for the first EOL,
+ before decoding any image data. */
+ int consecutive_eols;
+
+ /*! \brief The reference or starting changing element on the coding line. At the
+ start of the coding line, a0 is set on an imaginary white changing element
+ situated just before the first element on the line. During the coding of
+ the coding line, the position of a0 is defined by the previous coding mode.
+ (See T.4/4.2.1.3.2.). */
+ int a0;
+ /*! \brief The first changing element on the reference line to the right of a0 and of
+ opposite colour to a0. */
+ int b1;
+ /*! \brief The length of the in-progress run of black or white. */
+ int run_length;
+ /*! \brief 2D horizontal mode control. */
+ int black_white;
+ /*! \brief TRUE if the current run is black */
+ int its_black;
+
+ /*! \brief The current step into the current row run-lengths buffer. */
+ int a_cursor;
+ /*! \brief The current step into the reference row run-lengths buffer. */
+ int b_cursor;
+
+ /*! \brief Incoming bit buffer for decompression. */
+ uint32_t rx_bitstream;
+ /*! \brief The number of bits currently in rx_bitstream. */
+ int rx_bits;
+ /*! \brief The number of bits to be skipped before trying to match the next code word. */
+ int rx_skip_bits;
+
+ /*! \brief Decoded pixel buffer. */
+ //uint32_t pixel_stream;
+ /*! \brief The number of bits currently in pixel_stream. */
+ //int tx_bits;
+
+ /*! \brief Current pixel row number. */
+ //int row;
+
+ /*! \brief The current number of consecutive bad rows. */
+ int curr_bad_row_run;
+ /*! \brief The longest run of consecutive bad rows seen in the current page. */
+ int longest_bad_row_run;
+ /*! \brief The total number of bad rows in the current page. */
+ int bad_rows;
+
+ /*! \brief Error and flow logging control */
+ //logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/t4_t6_encode.h b/libs/spandsp/src/spandsp/private/t4_t6_encode.h
new file mode 100644
index 0000000000..40d750b732
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/t4_t6_encode.h
@@ -0,0 +1,59 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t4_t6_encode.h - definitions for T.4/T.6 fax compression
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2003 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_T4_T6_ENCODE_H_)
+#define _SPANDSP_PRIVATE_T4_T6_ENCODE_H_
+
+/*!
+ T.4 1D, T4 2D and T6 compressor state.
+*/
+struct t4_t6_encode_state_s
+{
+ /*! \brief The minimum number of encoded bits per row. This is a timing thing
+ for hardware FAX machines. */
+ int min_bits_per_row;
+ /*! \brief The current maximum contiguous rows that may be 2D encoded. */
+ int max_rows_to_next_1d_row;
+
+ /*! \brief Number of rows left that can be 2D encoded, before a 1D encoded row
+ must be used. */
+ int rows_to_next_1d_row;
+
+ /*! \brief The number of runs currently in the reference row. */
+ int ref_steps;
+
+ /*! \brief Pointer to the byte containing the next image bit to transmit. */
+ int bit_pos;
+ /*! \brief Pointer to the bit within the byte containing the next image bit to transmit. */
+ int bit_ptr;
+
+ /*! \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;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/timezone.h b/libs/spandsp/src/spandsp/private/timezone.h
new file mode 100644
index 0000000000..b1c770b364
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/timezone.h
@@ -0,0 +1,90 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/timezone.h - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 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.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_TIMEZONE_H_)
+#define _SPANDSP_PRIVATE_TIMEZONE_H_
+
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+
+#define TZNAME_MAX 255
+
+/* The TZ_MAX_TIMES value below is enough to handle a bit more than a
+ * year's worth of solar time (corrected daily to the nearest second) or
+ * 138 years of Pacific Presidential Election time
+ * (where there are three time zone transitions every fourth year). */
+#define TZ_MAX_TIMES 370
+
+#if !defined(NOSOLAR)
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#else
+/* Must be at least 14 for Europe/Riga as of Jan 12 1995,
+ * as noted by Earl Chew . */
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif
+
+#define TZ_BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+/* Time type information */
+struct tz_ttinfo_s
+{
+ int32_t gmtoff; /* UTC offset in seconds */
+ int isdst; /* Used to set tm_isdst */
+ int abbrind; /* Abbreviation list index */
+ int ttisstd; /* TRUE if transition is std time */
+ int ttisgmt; /* TRUE if transition is UTC */
+};
+
+/* Leap second information */
+struct tz_lsinfo_s
+{
+ time_t trans; /* Transition time */
+ int32_t corr; /* Correction to apply */
+};
+
+struct tz_state_s
+{
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ time_t ats[TZ_MAX_TIMES];
+ uint8_t types[TZ_MAX_TIMES];
+ struct tz_ttinfo_s ttis[TZ_MAX_TYPES];
+ char chars[TZ_BIGGEST(TZ_MAX_CHARS + 1, (2*(TZNAME_MAX + 1)))];
+ struct tz_lsinfo_s lsis[TZ_MAX_LEAPS];
+};
+
+struct tz_s
+{
+ struct tz_state_s state;
+ char lcl_tzname[TZNAME_MAX + 1];
+ int lcl_is_set;
+ const char *tzname[2];
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/t4_t6_decode.h b/libs/spandsp/src/spandsp/t4_t6_decode.h
new file mode 100644
index 0000000000..860d4ecea9
--- /dev/null
+++ b/libs/spandsp/src/spandsp/t4_t6_decode.h
@@ -0,0 +1,53 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t4_t6_decode.h - definitions for T.4/T.6 fax decoding
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2003, 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_T4_T6_DECODE_H_)
+#define _SPANDSP_T4_T6_DECODE_H_
+
+/*! \page t4_t6_decode_page T.4 and T.6 FAX image decompression
+
+\section t4_t6_decode_page_sec_1 What does it do?
+The T.4 image compression and decompression routines implement the 1D and 2D
+encoding methods defined in ITU specification T.4. They also implement the pure
+2D encoding method defined in T.6. These are image compression algorithms used
+for FAX transmission.
+
+\section t4_t6_decode_page_sec_1 How does it work?
+*/
+
+typedef struct t4_t6_decode_state_s t4_t6_decode_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/t4_t6_encode.h b/libs/spandsp/src/spandsp/t4_t6_encode.h
new file mode 100644
index 0000000000..72a3598bcb
--- /dev/null
+++ b/libs/spandsp/src/spandsp/t4_t6_encode.h
@@ -0,0 +1,42 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t4_t6_encode.h - definitions for T.4/T.6 fax encoding
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2003, 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_T4_T6_ENCODE_H_)
+#define _SPANDSP_T4_T6_ENCODE_H_
+
+typedef struct t4_t6_encode_state_s t4_t6_encode_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/timezone.h b/libs/spandsp/src/spandsp/timezone.h
new file mode 100644
index 0000000000..0f9a2eecd6
--- /dev/null
+++ b/libs/spandsp/src/spandsp/timezone.h
@@ -0,0 +1,88 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * timezone.h - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 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(_SPANDSP_TIMEZONE_H_)
+#define _SPANDSP_TIMEZONE_H_
+
+/*! \page timezone_page Timezone handling
+
+\section timezone_sec_1 What does it do?
+
+\section timezone_sec_2 How does it work?
+
+*/
+
+typedef struct tz_s tz_t;
+
+enum
+{
+ TM_SUNDAY = 0,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
+
+enum
+{
+ TM_JANUARY = 0,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring);
+
+SPAN_DECLARE(int) tz_release(tz_t *tz);
+
+SPAN_DECLARE(int) tz_free(tz_t *tz);
+
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tm, time_t t);
+
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/timezone.c b/libs/spandsp/src/timezone.c
new file mode 100644
index 0000000000..d0cab0989e
--- /dev/null
+++ b/libs/spandsp/src/timezone.c
@@ -0,0 +1,822 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * timezone.c - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 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 */
+
+/* Timezone processing might not seem like a DSP activity, but getting the headers
+ right on FAXes demands it. We need to handle multiple time zones within a process,
+ for FAXes related to different parts of the globe, so the system timezone handling
+ is not adequate. */
+
+/* This timezone handling is derived from public domain software by Arthur David Olson
+ which you may download from ftp://elsie.nci.nih.gov/pub
+ at the time of writing. */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "spandsp/telephony.h"
+#include "spandsp/timezone.h"
+
+#include "spandsp/private/timezone.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+#define SECS_PER_MIN 60
+#define MINS_PER_HOUR 60
+#define HOURS_PER_DAY 24
+#define DAYS_PER_WEEK 7
+#define DAYS_PER_NON_LEAP_YEAR 365
+#define DAYS_PER_LEAP_YEAR 366
+#define SECS_PER_HOUR (SECS_PER_MIN*MINS_PER_HOUR)
+#define SECS_PER_DAY ((long int) SECS_PER_HOUR*HOURS_PER_DAY)
+#define MONTHS_PER_YEAR 12
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0))
+
+#define isleap_sum(a, b) isleap((a)%400 + (b)%400)
+
+/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned int) (c) - '0' <= 9)
+
+#define TZ_DEF_RULE_STRING ",M4.1.0,M10.5.0"
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+static const char wildabbr[] = " ";
+
+static const char gmt[] = "GMT";
+
+struct tz_rule_s
+{
+ int r_type; /* Type of rule--see below */
+ int r_day; /* Day number of rule */
+ int r_week; /* Week number of rule */
+ int r_mon; /* Month number of rule */
+ long int r_time; /* Transition time of rule */
+};
+
+static const int mon_lengths[2][MONTHS_PER_YEAR] =
+{
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static const int year_lengths[2] =
+{
+ DAYS_PER_NON_LEAP_YEAR,
+ DAYS_PER_LEAP_YEAR
+};
+
+static int increment_overflow(int *number, int delta)
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_tzname(tz_t *tz)
+{
+ struct tz_state_s *sp;
+ const struct tz_ttinfo_s *ttisp;
+ int i;
+
+ sp = &tz->state;
+ tz->tzname[0] = wildabbr;
+ tz->tzname[1] = wildabbr;
+ for (i = 0; i < sp->typecnt; i++)
+ {
+ ttisp = &sp->ttis[i];
+ tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];
+ }
+ for (i = 0; i < sp->timecnt; i++)
+ {
+ ttisp = &sp->ttis[sp->types[i]];
+ tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Return the number of leap years through the end of the given year
+ where, to make the math easy, the answer for year zero is defined as zero. */
+static int leaps_thru_end_of(const int y)
+{
+ return (y >= 0) ? (y/4 - y/100 + y/400) : -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+static struct tm *time_sub(const time_t * const timep, const long int offset, const struct tz_state_s * const sp, struct tm * const tmp)
+{
+ const struct tz_lsinfo_s *lp;
+ time_t tdays;
+ const int *ip;
+ int32_t corr;
+ int32_t seconds;
+ int32_t rem;
+ int idays;
+ int y;
+ int hit;
+ int i;
+
+ corr = 0;
+ hit = 0;
+ i = sp->leapcnt;
+ while (--i >= 0)
+ {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->trans)
+ {
+ if (*timep == lp->trans)
+ {
+ hit = ((i == 0 && lp->corr > 0) || lp->corr > sp->lsis[i - 1].corr);
+ if (hit)
+ {
+ while (i > 0
+ &&
+ sp->lsis[i].trans == sp->lsis[i - 1].trans + 1
+ &&
+ sp->lsis[i].corr == sp->lsis[i - 1].corr + 1)
+ {
+ hit++;
+ --i;
+ }
+ }
+ }
+ corr = lp->corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = *timep/SECS_PER_DAY;
+ rem = *timep - tdays*SECS_PER_DAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)])
+ {
+ int newy;
+ time_t tdelta;
+ int idelta;
+ int leapdays;
+
+ tdelta = tdays / DAYS_PER_LEAP_YEAR;
+ idelta = tdelta;
+ if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+ return NULL;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ return NULL;
+ leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+ seconds = tdays*SECS_PER_DAY;
+ tdays = seconds/SECS_PER_DAY;
+ rem += seconds - tdays*SECS_PER_DAY;
+ /* Given the range, we can now fearlessly cast... */
+ idays = tdays;
+ rem += (offset - corr);
+ while (rem < 0)
+ {
+ rem += SECS_PER_DAY;
+ idays--;
+ }
+ while (rem >= SECS_PER_DAY)
+ {
+ rem -= SECS_PER_DAY;
+ idays++;
+ }
+ while (idays < 0)
+ {
+ if (increment_overflow(&y, -1))
+ return NULL;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)])
+ {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ return NULL;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ return NULL;
+ tmp->tm_yday = idays;
+ /* The "extra" mods below avoid overflow problems. */
+ tmp->tm_wday = EPOCH_WDAY
+ + ((y - EPOCH_YEAR) % DAYS_PER_WEEK)*(DAYS_PER_NON_LEAP_YEAR % DAYS_PER_WEEK)
+ + leaps_thru_end_of(y - 1)
+ - leaps_thru_end_of(EPOCH_YEAR - 1)
+ + idays;
+ tmp->tm_wday %= DAYS_PER_WEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYS_PER_WEEK;
+ tmp->tm_hour = (int) (rem/SECS_PER_HOUR);
+ rem %= SECS_PER_HOUR;
+ tmp->tm_min = (int) (rem/SECS_PER_MIN);
+ /* A positive leap second requires a special
+ * representation. This uses "... ??:59:60" et seq. */
+ tmp->tm_sec = (int) (rem%SECS_PER_MIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; (tmp->tm_mon)++)
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+ return tmp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, scan until a character that is not
+ * a valid character in a zone name is found. Return a pointer to that
+ * character. */
+static const char *get_tzname(const char *strp)
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+')
+ strp++;
+ return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a number from that string.
+ * Check that the number is within a specified range; if it is not, return
+ * NULL.
+ * Otherwise, return a pointer to the first character not part of the number. */
+static const char *get_num(const char *strp, int * const nump, const int min, const int max)
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do
+ {
+ num = num*10 + (c - '0');
+ if (num > max)
+ return NULL; /* Illegal value */
+ c = *++strp;
+ }
+ while (is_digit(c));
+ if (num < min)
+ return NULL; /* Illegal value */
+ *nump = num;
+ return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a number of seconds,
+ * in hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the number
+ * of seconds. */
+static const char *get_secs(const char *strp, long int * const secsp)
+{
+ int num;
+
+ /* HOURS_PER_DAY*DAYS_PER_WEEK - 1 allows quasi-Posix rules like
+ * "M10.4.6/26", which does not conform to Posix,
+ * but which specifies the equivalent of
+ * "02:00 on the first Sunday on or after 23 Oct". */
+ strp = get_num(strp, &num, 0, HOURS_PER_DAY*DAYS_PER_WEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num*(long int) SECS_PER_HOUR;
+ if (*strp == ':')
+ {
+ strp = get_num(strp + 1, &num, 0, MINS_PER_HOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num*SECS_PER_MIN;
+ if (*strp == ':')
+ {
+ /* SECS_PER_MIN allows for leap seconds. */
+ strp = get_num(strp + 1, &num, 0, SECS_PER_MIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract an offset, in
+ * [+-]hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the time. */
+static const char *get_offset(const char *strp, long int * const offsetp)
+{
+ int neg = 0;
+
+ if (*strp == '-')
+ {
+ neg = 1;
+ strp++;
+ }
+ else if (*strp == '+')
+ {
+ strp++;
+ }
+ strp = get_secs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* Illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a rule in the form
+ * date[/time]. See POSIX section 8 for the format of "date" and "time".
+ * If a valid rule is not found, return NULL.
+ * Otherwise, return a pointer to the first character not part of the rule. */
+static const char *get_rule(const char *strp, struct tz_rule_s * const rulep)
+{
+ if (*strp == 'J')
+ {
+ /* Julian day. */
+ rulep->r_type = JULIAN_DAY;
+ strp = get_num(strp + 1, &rulep->r_day, 1, DAYS_PER_NON_LEAP_YEAR);
+ }
+ else if (*strp == 'M')
+ {
+ /* Month, week, day. */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ strp = get_num(strp + 1, &rulep->r_mon, 1, MONTHS_PER_YEAR);
+ if (strp == NULL || *strp++ != '.')
+ return NULL;
+ strp = get_num(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL || *strp++ != '.')
+ return NULL;
+ strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_WEEK - 1);
+ }
+ else if (is_digit(*strp))
+ {
+ /* Day of the year. */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_LEAP_YEAR - 1);
+ }
+ else
+ {
+ /* Invalid format */
+ return NULL;
+ }
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/')
+ {
+ /* Time specified. */
+ strp = get_secs(strp + 1, &rulep->r_time);
+ }
+ else
+ {
+ /* Default = 2:00:00 */
+ rulep->r_time = 2*SECS_PER_HOUR;
+ }
+ return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+ * year, a rule, and the offset from UTC at the time that rule takes effect,
+ * calculate the Epoch-relative time that rule takes effect. */
+static time_t trans_time(const time_t janfirst, const int year, const struct tz_rule_s * const rulep, const long int offset)
+{
+ int leapyear;
+ time_t value;
+ int i;
+ int d;
+ int m1;
+ int yy0;
+ int yy1;
+ int yy2;
+ int dow;
+
+ value = 0;
+ leapyear = isleap(year);
+ switch (rulep->r_type)
+ {
+ case JULIAN_DAY:
+ /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ * years.
+ * In non-leap years, or if the day number is 59 or less, just
+ * add SECS_PER_DAY times the day number-1 to the time of
+ * January 1, midnight, to get the day. */
+ value = janfirst + (rulep->r_day - 1)*SECS_PER_DAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECS_PER_DAY;
+ break;
+ case DAY_OF_YEAR:
+ /* n - day of year.
+ * Just add SECS_PER_DAY times the day number to the time of
+ * January 1, midnight, to get the day. */
+ value = janfirst + rulep->r_day * SECS_PER_DAY;
+ break;
+ case MONTH_NTH_DAY_OF_WEEK:
+ /* Mm.n.d - nth "dth day" of month m. */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; i++)
+ value += mon_lengths[leapyear][i]*SECS_PER_DAY;
+
+ /* Use Zeller's Congruence to get day-of-week of first day of month. */
+ m1 = (rulep->r_mon + 9)%12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0/100;
+ yy2 = yy0%100;
+ dow = ((26*m1 - 2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1)%7;
+ if (dow < 0)
+ dow += DAYS_PER_WEEK;
+
+ /* "dow" is the day-of-week of the first day of the month. Get
+ * the day-of-month (zero-origin) of the first "dow" day of the
+ * month. */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYS_PER_WEEK;
+ for (i = 1; i < rulep->r_week; i++)
+ {
+ if (d + DAYS_PER_WEEK >= mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYS_PER_WEEK;
+ }
+
+ /* "d" is the day-of-month (zero-origin) of the day we want. */
+ value += d*SECS_PER_DAY;
+ break;
+ }
+
+ /* "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ * question. To get the Epoch-relative time of the specified local
+ * time on that day, add the transition time and the current offset
+ * from UTC. */
+ return value + rulep->r_time + offset;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a POSIX section 8-style TZ string, fill in the rule tables as
+ appropriate. */
+static int tzparse(const char *name, struct tz_state_s * const sp, const int lastditch)
+{
+ const char *stdname;
+ const char *dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long int stdoffset;
+ long int dstoffset;
+ long int theirstdoffset;
+ long int theirdstoffset;
+ long int theiroffset;
+ unsigned char *typep;
+ char *cp;
+ int load_result;
+ int isdst;
+ int i;
+ int j;
+ int year;
+ struct tz_rule_s start;
+ struct tz_rule_s end;
+ time_t *atp;
+ time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ dstname = NULL;
+ stdname = name;
+ if (lastditch)
+ {
+ stdlen = strlen(name); /* Length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof(sp->chars))
+ stdlen = sizeof(sp->chars) - 1;
+ stdoffset = 0;
+ }
+ else
+ {
+ name = get_tzname(name);
+ stdlen = name - stdname;
+ if (stdlen < 3)
+ return -1;
+ if (*name == '\0')
+ return -1;
+ name = get_offset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = -1;
+ if (load_result != 0)
+ sp->leapcnt = 0; /* So, we're off a little */
+ if (*name != '\0')
+ {
+ dstname = name;
+ name = get_tzname(name);
+ dstlen = name - dstname; /* Length of DST zone name */
+ if (dstlen < 3)
+ return -1;
+ if (*name != '\0' && *name != ',' && *name != ';')
+ {
+ if ((name = get_offset(name, &dstoffset)) == NULL)
+ return -1;
+ }
+ else
+ {
+ dstoffset = stdoffset - SECS_PER_HOUR;
+ }
+ if (*name == '\0' && load_result != 0)
+ name = TZ_DEF_RULE_STRING;
+ if (*name == ',' || *name == ';')
+ {
+ if ((name = get_rule(name + 1, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = get_rule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* Standard time and DST */
+ /* Two transitions per year, from EPOCH_YEAR to 2037. */
+ sp->timecnt = 2*(2037 - EPOCH_YEAR + 1);
+ if (sp->timecnt > TZ_MAX_TIMES)
+ return -1;
+ sp->ttis[0].gmtoff = -dstoffset;
+ sp->ttis[0].isdst = 1;
+ sp->ttis[0].abbrind = stdlen + 1;
+ sp->ttis[1].gmtoff = -stdoffset;
+ sp->ttis[1].isdst = 0;
+ sp->ttis[1].abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ for (year = EPOCH_YEAR; year <= 2037; year++)
+ {
+ starttime = trans_time(janfirst, year, &start, stdoffset);
+ endtime = trans_time(janfirst, year, &end, dstoffset);
+ if (starttime > endtime)
+ {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ }
+ else
+ {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ janfirst += year_lengths[isleap(year)]*SECS_PER_DAY;
+ }
+ }
+ else
+ {
+ if (*name != '\0')
+ return -1;
+ /* Initial values of theirstdoffset and theirdstoffset. */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; i++)
+ {
+ j = sp->types[i];
+ if (!sp->ttis[j].isdst)
+ {
+ theirstdoffset = -sp->ttis[j].gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; i++)
+ {
+ j = sp->types[i];
+ if (sp->ttis[j].isdst)
+ {
+ theirdstoffset = -sp->ttis[j].gmtoff;
+ break;
+ }
+ }
+ /* Initially we're assumed to be in standard time. */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /* Now juggle transition times and types tracking offsets as you do. */
+ for (i = 0; i < sp->timecnt; i++)
+ {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].isdst;
+ if (sp->ttis[j].ttisgmt)
+ {
+ /* No adjustment to transition time */
+ }
+ else
+ {
+ /* If summer time is in effect, and the
+ * transition time was not specified as
+ * standard time, add the summer time
+ * offset to the transition time;
+ * otherwise, add the standard time
+ * offset to the transition time. */
+ /* Transitions from DST to DDST
+ * will effectively disappear since
+ * POSIX provides for only one DST
+ * offset. */
+ if (isdst && !sp->ttis[j].ttisstd)
+ sp->ats[i] += (dstoffset - theirdstoffset);
+ else
+ sp->ats[i] += (stdoffset - theirstdoffset);
+ }
+ theiroffset = -sp->ttis[j].gmtoff;
+ if (sp->ttis[j].isdst)
+ theirdstoffset = theiroffset;
+ else
+ theirstdoffset = theiroffset;
+ }
+ /* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. */
+ sp->ttis[0].gmtoff = -stdoffset;
+ sp->ttis[0].isdst = FALSE;
+ sp->ttis[0].abbrind = 0;
+ sp->ttis[1].gmtoff = -dstoffset;
+ sp->ttis[1].isdst = TRUE;
+ sp->ttis[1].abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ }
+ }
+ else
+ {
+ dstlen = 0;
+ sp->typecnt = 1; /* Only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].gmtoff = -stdoffset;
+ sp->ttis[0].isdst = 0;
+ sp->ttis[0].abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof(sp->chars))
+ return -1;
+ cp = sp->chars;
+ strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0)
+ {
+ strncpy(cp, dstname, dstlen);
+ cp[dstlen] = '\0';
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tz_set(tz_t *tz, const char *tzstring)
+{
+ const char *name = "";
+ struct tz_state_s *lclptr = &tz->state;
+
+ if (tzstring)
+ name = tzstring;
+
+ /* See if we are already set OK */
+ if (tz->lcl_is_set > 0 && strcmp(tz->lcl_tzname, name) == 0)
+ return;
+ tz->lcl_is_set = strlen(name) < sizeof(tz->lcl_tzname);
+ if (tz->lcl_is_set)
+ strcpy(tz->lcl_tzname, name);
+
+ if (name[0] == '\0')
+ {
+ /* User wants it fast rather than right, so, we're off a little. */
+ lclptr->leapcnt = 0;
+ lclptr->timecnt = 0;
+ lclptr->typecnt = 0;
+ lclptr->ttis[0].isdst = 0;
+ lclptr->ttis[0].gmtoff = 0;
+ lclptr->ttis[0].abbrind = 0;
+ strcpy(lclptr->chars, gmt);
+ }
+ else if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
+ {
+ tzparse(gmt, lclptr, TRUE);
+ }
+ set_tzname(tz);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tmp, time_t t)
+{
+ struct tz_state_s *sp;
+ const struct tz_ttinfo_s *ttisp;
+ int i;
+
+ sp = &tz->state;
+
+ if (sp->timecnt == 0 || t < sp->ats[0])
+ {
+ i = 0;
+ while (sp->ttis[i].isdst)
+ {
+ if (++i >= sp->typecnt)
+ {
+ i = 0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (i = 1; i < sp->timecnt; i++)
+ {
+ if (t < sp->ats[i])
+ break;
+ }
+ i = (int) sp->types[i - 1];
+ }
+ ttisp = &sp->ttis[i];
+ time_sub(&t, ttisp->gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->isdst;
+ tz->tzname[tmp->tm_isdst] = &sp->chars[ttisp->abbrind];
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst)
+{
+ return tz->tzname[(!isdst) ? 0 : 1];
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring)
+{
+ if (tz == NULL)
+ {
+ if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL)
+ return NULL;
+ }
+ memset(tz, 0, sizeof(*tz));
+ tz->tzname[0] =
+ tz->tzname[1] = wildabbr;
+ tz_set(tz, tzstring);
+ return tz;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_release(tz_t *tz)
+{
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_free(tz_t *tz)
+{
+ if (tz)
+ free(tz);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/test-data/itu/fax/generate_striped_pages.c b/libs/spandsp/test-data/itu/fax/generate_striped_pages.c
new file mode 100644
index 0000000000..0c3eafd149
--- /dev/null
+++ b/libs/spandsp/test-data/itu/fax/generate_striped_pages.c
@@ -0,0 +1,123 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * generate_striped_pages.c
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 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 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 */
+
+/*
+ This program generates an TIFF image as a number of small image striped, rather than
+ the usual all in one page FAX images usually consist of in TIFF files.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if defined(HAVE_TGMATH_H)
+#include
+#endif
+#if defined(HAVE_MATH_H)
+#include
+#endif
+#include
+
+#include "spandsp.h"
+
+#define IMAGE_WIDTH 1728
+#define IMAGE_LENGTH 2600
+#define ROWS_PER_STRIPE 37
+
+int main(int argc, char *argv[])
+{
+ TIFF *tiff_file;
+ uint8_t image_buffer[10000];
+ int image_size;
+ time_t now;
+ struct tm *tm;
+ char buf[256 + 1];
+ int i;
+
+ if ((tiff_file = TIFFOpen("striped.tif", "w")) == NULL)
+ return -1;
+
+ TIFFSetField(tiff_file, TIFFTAG_COMPRESSION, COMPRESSION_CCITT_T6);
+ TIFFSetField(tiff_file, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(tiff_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tiff_file, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(tiff_file, TIFFTAG_ROWSPERSTRIP, (int32_t) ROWS_PER_STRIPE);
+ TIFFSetField(tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tiff_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ TIFFSetField(tiff_file, TIFFTAG_XRESOLUTION, 204.0f);
+ TIFFSetField(tiff_file, TIFFTAG_YRESOLUTION, 196.0f);
+ TIFFSetField(tiff_file, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+ TIFFSetField(tiff_file, TIFFTAG_SOFTWARE, "Spandsp");
+ TIFFSetField(tiff_file, TIFFTAG_HOSTCOMPUTER, "host");
+ TIFFSetField(tiff_file, TIFFTAG_FAXSUBADDRESS, "1111");
+ TIFFSetField(tiff_file, TIFFTAG_IMAGEDESCRIPTION, "Image in stripes");
+ TIFFSetField(tiff_file, TIFFTAG_MAKE, "spandsp");
+ TIFFSetField(tiff_file, TIFFTAG_MODEL, "testy");
+
+ time(&now);
+ tm = localtime(&now);
+ sprintf(buf,
+ "%4d/%02d/%02d %02d:%02d:%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ TIFFSetField(tiff_file, TIFFTAG_DATETIME, buf);
+ TIFFSetField(tiff_file, TIFFTAG_FAXRECVTIME, 10);
+ TIFFSetField(tiff_file, TIFFTAG_IMAGEWIDTH, IMAGE_WIDTH);
+ TIFFSetField(tiff_file, TIFFTAG_IMAGELENGTH, IMAGE_LENGTH);
+ TIFFSetField(tiff_file, TIFFTAG_PAGENUMBER, 0, 1);
+
+ image_size = IMAGE_WIDTH*ROWS_PER_STRIPE/8;
+ memset(image_buffer, 0x18, image_size);
+
+ for (i = 0; i < IMAGE_LENGTH/ROWS_PER_STRIPE; i++)
+ {
+ if (IMAGE_LENGTH > (i + 1)*ROWS_PER_STRIPE)
+ image_size = IMAGE_WIDTH*ROWS_PER_STRIPE/8;
+ else
+ image_size = IMAGE_WIDTH*(IMAGE_LENGTH - i*ROWS_PER_STRIPE)/8;
+ if (TIFFWriteEncodedStrip(tiff_file, i, image_buffer, image_size) < 0)
+ return -1;
+ }
+
+ TIFFWriteDirectory(tiff_file);
+ TIFFClose(tiff_file);
+ return 0;
+}
diff --git a/libs/spandsp/test-data/local/lenna-colour.tif b/libs/spandsp/test-data/local/lenna-colour.tif
new file mode 100644
index 0000000000..4a282db3b8
Binary files /dev/null and b/libs/spandsp/test-data/local/lenna-colour.tif differ
diff --git a/libs/spandsp/tests/bitstream_tests.c b/libs/spandsp/tests/bitstream_tests.c
new file mode 100644
index 0000000000..e17b8094a0
--- /dev/null
+++ b/libs/spandsp/tests/bitstream_tests.c
@@ -0,0 +1,189 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * bitstream_tests.c
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2007 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.
+ */
+
+/*! \page bitstream_tests_page Bitstream tests
+\section bitstream_tests_page_sec_1 What does it do?
+
+\section bitstream_tests_page_sec_2 How is it used?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+uint8_t buffer[256];
+
+#define PATTERN 0x11111111
+#define SEQUENCE_LENGTH 17
+
+uint8_t left[] =
+{
+ 0x28, /* 2 of 4, 3, 2, 1 */
+ 0xC8, /* 1 of 6, 5, 2 of 4 */
+ 0xAE, /* 3 of 7, 5 of 6 */
+ 0x67, /* 4 of 8, 4 of 7 */
+ 0x74, /* 4 of 9, 4 of 8 */
+ 0x43, /* 3 of 10, 5 of 9 */
+ 0x32, /* 1 of 11, 7 of 10 */
+ 0xAA, /* 8 of 11 */
+ 0xAE, /* 6 of 12, 2 of 11 */
+ 0xED, /* 2 of 13, 6 of 12 */
+ 0x99, /* 8 of 13 */
+ 0x8E, /* 5 of 14, 3 of 13 */
+ 0xEE, /* 8 of 14 */
+ 0xEE, /* 7 of 15, 1 of 14 */
+ 0xEE, /* 8 of 15 */
+ 0xFF, /* 8 of 16 */
+ 0xFF, /* 8 of 16 */
+ 0x88, /* 8 of 17 */
+ 0x88, /* 8 of 17 */
+ 0x00 /* 1 of 17 */
+};
+uint8_t right[] =
+{
+ 0xD2, /* 1, 2, 3, 2 of 4 */
+ 0x90, /* 2 of 4, 5, 1 of 6 */
+ 0xCA, /* 5 of 6, 3 of 7 */
+ 0x7C, /* 4 of 7, 4 of 8 */
+ 0x87, /* 4 of 8, 4 of 9 */
+ 0x28, /* 5 of 9, 3 of 10 */
+ 0x33, /* 7 of 10, 1 of 11 */
+ 0x55, /* 8 of 11 */
+ 0xED, /* 2 of 11, 6 of 12 */
+ 0x2E, /* 6 of 12, 2 of 13 */
+ 0x33, /* 8 of 13 */
+ 0xEB, /* 3 of 13, 5 of 14 */
+ 0xEE, /* 8 of 14 */
+ 0xDC, /* 1 of 14, 7 of 15 */
+ 0xDD, /* 8 of 15 */
+ 0xFF, /* 8 of 16 */
+ 0xFF, /* 8 of 16 */
+ 0x10, /* 8 of 17 */
+ 0x11, /* 8 of 17 */
+ 0x01 /* 1 of 17 */
+};
+
+int main(int argc, char *argv[])
+{
+ int i;
+ bitstream_state_t state;
+ bitstream_state_t *s;
+ const uint8_t *r;
+ uint8_t *w;
+ uint8_t *cc;
+ unsigned int x;
+ int total_bits;
+
+ s = bitstream_init(&state, TRUE);
+ w = buffer;
+ total_bits = 0;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ bitstream_put(s, &w, PATTERN*i, i + 1);
+ total_bits += (i + 1);
+ }
+ bitstream_flush(s, &w);
+ printf("%d bits written\n", total_bits);
+
+ for (cc = buffer; cc < w; cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+ for (cc = right; cc < right + sizeof(right); cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+
+ if ((w - buffer) != sizeof(right) || memcmp(buffer, right, sizeof(right)))
+ {
+ printf("Test failed\n");
+ exit(2);
+ }
+
+ s = bitstream_init(&state, TRUE);
+ r = buffer;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ x = bitstream_get(s, &r, i + 1);
+ if (x != ((PATTERN*i) & ((1 << (i + 1)) - 1)))
+ {
+ printf("Error 0x%X 0x%X\n", x, ((PATTERN*i) & ((1 << (i + 1)) - 1)));
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+
+ s = bitstream_init(&state, FALSE);
+ w = buffer;
+ total_bits = 0;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ bitstream_put(s, &w, PATTERN*i, i + 1);
+ total_bits += (i + 1);
+ }
+ bitstream_flush(s, &w);
+ printf("%d bits written\n", total_bits);
+
+ for (cc = buffer; cc < w; cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+ for (cc = left; cc < left + sizeof(left); cc++)
+ printf("%02X ", *cc);
+ printf("\n");
+
+ if ((w - buffer) != sizeof(left) || memcmp(buffer, left, sizeof(left)))
+ {
+ printf("Test failed\n");
+ exit(2);
+ }
+
+ s = bitstream_init(&state, FALSE);
+ r = buffer;
+ for (i = 0; i < SEQUENCE_LENGTH; i++)
+ {
+ x = bitstream_get(s, &r, i + 1);
+ if (x != ((PATTERN*i) & ((1 << (i + 1)) - 1)))
+ {
+ printf("Error 0x%X 0x%X\n", x, ((PATTERN*i) & ((1 << (i + 1)) - 1)));
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/image_translate_tests.c b/libs/spandsp/tests/image_translate_tests.c
new file mode 100644
index 0000000000..be0f5ea18c
--- /dev/null
+++ b/libs/spandsp/tests/image_translate_tests.c
@@ -0,0 +1,452 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * image_translate_tests.c - Tests for the image translation routines.
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/*! \page image_translate_tests_page Image translation tests
+\section image_translate_tests_page_sec_1 What does it do?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#define INPUT_TIFF_FILE_NAME "../test-data/local/lenna-colour.tif"
+
+typedef struct
+{
+ const uint8_t *image;
+ int width;
+ int length;
+ int current_row;
+ int bytes_per_pixel;
+} image_descriptor_t;
+
+static void display_row(int row, int width, uint8_t buf[])
+{
+ int i;
+ int test_pixel;
+
+ printf("%3d: ", row);
+ for (i = 0; i < width; i++)
+ {
+ test_pixel = (buf[i >> 3] >> (7 - (i & 7))) & 0x01;
+ printf("%c", (test_pixel) ? ' ' : '@');
+ }
+ printf("\n");
+}
+/*- End of function --------------------------------------------------------*/
+
+static int test_dithered_50_by_50(int row, int width, uint8_t buf[])
+{
+ static const char *image[50] =
+ {
+ " 0: @ @ @ @ @ @ @ @@ @@@@@@@@@@@@@@",
+ " 1: @ @ @ @ @ @ @ @ @ @@ @@ @ @ @ @ @@@@",
+ " 2: @ @ @ @ @ @ @ @@ @ @ @@@ @@@@@@ @@",
+ " 3: @ @ @ @ @ @ @ @ @ @ @@@ @@@@ @@@@@@",
+ " 4: @ @ @ @ @ @ @ @ @ @@@ @ @@ @@@@ @@@@",
+ " 5: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@ @@@@@@",
+ " 6: @ @ @ @ @ @ @@ @@@@ @@@ @@@ @@ @",
+ " 7: @ @ @ @ @ @ @ @ @ @@ @@ @ @@ @@@@@@",
+ " 8: @ @ @ @ @ @ @ @ @@@ @@@@@@@@@ @@@",
+ " 9: @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ @ @ @@@@@",
+ " 10: @ @ @ @ @ @@ @ @@ @@@@@@@@@@@@@",
+ " 11: @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@ @@ @@",
+ " 12: @ @ @ @ @ @ @ @ @ @@ @@@@@@ @@@@@@@",
+ " 13: @ @ @ @ @ @ @ @ @@ @ @ @ @ @@@@@ @@@",
+ " 14: @ @ @ @ @ @ @ @@ @@@ @@@ @ @@@@@@",
+ " 15: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@@ @@",
+ " 16: @ @ @ @ @ @ @ @ @@ @@ @@ @@ @@@@@",
+ " 17: @ @ @ @ @ @ @ @ @ @@ @@ @@ @@ @@@@@@@",
+ " 18: @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@ @ @@@",
+ " 19: @ @ @ @ @ @ @ @ @@@ @@@ @ @@@@@@@",
+ " 20: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@@@@ @@@ @",
+ " 21: @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@",
+ " 22: @ @ @ @ @ @@ @ @ @ @ @@ @@@@@@@ @@ @@",
+ " 23: @ @ @ @ @ @ @ @@ @@ @@ @ @ @@@@@@@",
+ " 24: @ @ @ @ @@ @ @ @@ @ @ @@@@@@ @@@@@",
+ " 25: @ @ @ @ @ @ @ @ @ @ @@@@@ @ @@@@ @@",
+ " 26: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@ @@@@@",
+ " 27: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@ @@@@@@@",
+ " 28: @ @ @ @ @ @ @ @ @@@ @@@ @@@ @@ @@",
+ " 29: @ @ @ @ @ @ @ @ @@ @ @@ @ @@ @@@@@@@",
+ " 30: @ @ @ @ @ @ @ @ @ @ @ @@@@@@@ @@@@@",
+ " 31: @ @ @ @ @ @ @ @ @ @@@@ @ @ @@@@ @@",
+ " 32: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@ @@@@@",
+ " 33: @ @ @ @ @ @ @ @@@ @@ @ @ @@@@@@@",
+ " 34: @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@ @ @@@",
+ " 35: @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@",
+ " 36: @ @ @ @ @ @ @ @ @@ @@@ @@@@@@@ @@@ @",
+ " 37: @ @ @ @ @ @ @ @ @ @ @@ @ @ @@@@@@@",
+ " 38: @ @ @ @ @ @ @ @ @@ @@ @@@@@@@ @@ @@",
+ " 39: @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@",
+ " 40: @ @ @ @ @ @ @ @ @ @@ @@ @@@@ @@@@@",
+ " 41: @ @ @ @ @ @ @ @ @ @@ @ @@ @@@ @@@@ @@",
+ " 42: @ @ @ @ @ @ @@ @@ @@ @@ @@@ @@@@@",
+ " 43: @ @ @ @ @ @ @ @ @ @ @@ @@@ @@@@@@@",
+ " 44: @ @ @ @ @ @ @ @ @ @ @@@ @@@ @@@ @@ @@",
+ " 45: @ @ @ @ @ @ @@ @ @ @ @@ @@@@@@@",
+ " 46: @ @ @ @ @ @ @ @ @ @ @ @@ @@@@@@@@@ @@@@@",
+ " 47: @ @ @ @ @ @ @ @ @ @@ @ @ @ @@@@ @@",
+ " 48: @ @ @ @ @ @ @@ @ @@ @@@@@@ @@@@@",
+ " 49: @ @ @ @ @ @ @ @ @ @ @ @@ @@ @ @ @@@@@@@"
+ };
+ int i;
+ int match;
+ int ref_pixel;
+ int test_pixel;
+
+ match = 0;
+ for (i = 0; i < width; i++)
+ {
+ ref_pixel = (image[row][i + 5] == ' ');
+ test_pixel = (buf[i >> 3] >> (7 - (i & 7))) & 0x01;
+ if (ref_pixel != test_pixel)
+ match = -1;
+ }
+ return match;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int row_read(void *user_data, uint8_t buf[], size_t len)
+{
+ image_descriptor_t *im;
+
+ im = (image_descriptor_t *) user_data;
+ if (im->current_row >= im->length)
+ return 0;
+ memcpy(buf, &im->image[im->current_row*im->width*im->bytes_per_pixel], len);
+ im->current_row++;
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void get_flattened_image(image_translate_state_t *s, int compare)
+{
+ int i;
+ int len;
+ uint8_t row_buf[5000];
+
+ for (i = 0; i < s->output_length; i++)
+ {
+ if ((len = image_translate_row(s, row_buf, (s->output_width + 7)/8)) != (s->output_width + 7)/8)
+ {
+ printf("Image finished early - %d %d\n", len, (s->output_width + 7)/8);
+ exit(2);
+ }
+ display_row(i, s->output_width, row_buf);
+ if (compare)
+ {
+ if (test_dithered_50_by_50(i, s->output_width, row_buf))
+ {
+ printf("Dithered image mismatch at row %d\n", i);
+ printf("Test failed\n");
+ exit(2);
+ }
+ }
+ }
+ if ((len = image_translate_row(s, row_buf, (s->output_width + 7)/8)) != 0)
+ {
+ printf("Image finished late - %d %d\n", len, (s->output_width + 7)/8);
+ exit(2);
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_gray16(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint16_t image[50*50];
+ image_descriptor_t im;
+
+ printf("Dithering from a 16 bit per sample gray scale to bi-level\n");
+ im.image = (const uint8_t *) image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 2;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ image[i*im.width + j] = j*1200;
+ }
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_GRAY_16, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_gray8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint8_t image[50*50];
+ image_descriptor_t im;
+
+ printf("Dithering from a 8 bit per sample gray scale to bi-level\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 1;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ image[i*im.width + j] = j*1200/256;
+ }
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_GRAY_8, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_colour16(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint16_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Dithering from a 3x16 bit per sample colour to bi-level\n");
+ im.image = (const uint8_t *) image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 6;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200;
+ image[i*3*im.width + 3*j + 1] = j*1200;
+ image[i*3*im.width + 3*j + 2] = j*1200;
+ }
+ }
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_16, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dither_tests_colour8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ uint8_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Dithering from a 3x8 bit per sample colour to bi-level\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 3;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200/256;
+ image[i*3*im.width + 3*j + 1] = j*1200/256;
+ image[i*3*im.width + 3*j + 2] = j*1200/256;
+ }
+ }
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_8, im.width, im.length, -1, row_read, &im);
+ get_flattened_image(s, TRUE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void grow_tests_colour8(void)
+{
+ int i;
+ int j;
+ image_translate_state_t resize;
+ image_translate_state_t *s1 = &resize;
+ uint8_t image[50*50*3];
+ image_descriptor_t im;
+
+ printf("Image growth tests\n");
+ im.image = image;
+ im.width = 50;
+ im.length = 50;
+ im.bytes_per_pixel = 3;
+ im.current_row = 0;
+
+ for (i = 0; i < im.length; i++)
+ {
+ for (j = 0; j < im.width; j++)
+ {
+ image[i*3*im.width + 3*j + 0] = j*1200/256;
+ image[i*3*im.width + 3*j + 1] = j*1200/256;
+ image[i*3*im.width + 3*j + 2] = j*1200/256;
+ }
+ }
+
+ s1 = image_translate_init(s1, IMAGE_TRANSLATE_FROM_COLOUR_8, im.width, im.length, 200, row_read, &im);
+
+ get_flattened_image(s1, FALSE);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void lenna_tests(int output_width, const char *file)
+{
+ TIFF *in_file;
+ TIFF *out_file;
+ int image_width;
+ int image_length;
+ int output_length;
+ uint8_t *image;
+ uint8_t *image2;
+ int len;
+ int total;
+ int16_t bits_per_sample;
+ int16_t samples_per_pixel;
+ int i;
+ int n;
+ image_translate_state_t bw;
+ image_translate_state_t *s = &bw;
+ image_descriptor_t im;
+
+ printf("Dithering Lenna from colour to bi-level test\n");
+ if ((in_file = TIFFOpen(INPUT_TIFF_FILE_NAME, "r")) == NULL)
+ return;
+ image_width = 0;
+ TIFFGetField(in_file, TIFFTAG_IMAGEWIDTH, &image_width);
+ if (image_width <= 0)
+ return;
+ image_length = 0;
+ TIFFGetField(in_file, TIFFTAG_IMAGELENGTH, &image_length);
+ if (image_length <= 0)
+ return;
+ bits_per_sample = 0;
+ TIFFGetField(in_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+ samples_per_pixel = 0;
+ TIFFGetField(in_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+ printf("Original image is %d x %d, %d bits per sample, %d samples per pixel\n", image_width, image_length, bits_per_sample, samples_per_pixel);
+ if ((image = malloc(image_width*image_length*samples_per_pixel)) == NULL)
+ return;
+ for (total = 0, i = 0; i < 1000; i++)
+ {
+ len = TIFFReadEncodedStrip(in_file, i, &image[total], image_width*image_length*samples_per_pixel - total);
+ if (len <= 0)
+ break;
+ total += len;
+ if (total == image_width*image_length*samples_per_pixel)
+ {
+ printf("Done\n");
+ break;
+ }
+ }
+ printf("Image size %d %d\n", total, image_width*image_length*samples_per_pixel);
+ TIFFClose(in_file);
+
+ im.image = image;
+ im.width = image_width;
+ im.length = image_length;
+ im.current_row = 0;
+ im.bytes_per_pixel = samples_per_pixel;
+
+ s = image_translate_init(s, IMAGE_TRANSLATE_FROM_COLOUR_8, image_width, image_length, output_width, row_read, &im);
+ output_width = image_translate_get_output_width(s);
+ output_length = image_translate_get_output_length(s);
+
+ if ((out_file = TIFFOpen(file, "w")) == NULL)
+ return;
+ TIFFSetField(out_file, TIFFTAG_IMAGEWIDTH, output_width);
+ TIFFSetField(out_file, TIFFTAG_IMAGELENGTH, output_length);
+ TIFFSetField(out_file, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(out_file, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(out_file, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(out_file, TIFFTAG_ROWSPERSTRIP, -1);
+ TIFFSetField(out_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(out_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(out_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ TIFFSetField(out_file, TIFFTAG_PAGENUMBER, 0, 1);
+ TIFFSetField(out_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(out_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+
+ printf("Input %d x %d, output %d x %d\n", image_width, image_length, output_width, output_length);
+
+ if ((image2 = malloc(output_width*output_length/8)) == NULL)
+ return;
+ memset(image2, 0, output_width*output_length/8);
+ n = 0;
+ for (i = 0; i < output_length; i++)
+ n += image_translate_row(s, &image2[n], output_width/8);
+
+ TIFFWriteEncodedStrip(out_file, 0, image2, output_width*output_length/8);
+ TIFFWriteDirectory(out_file);
+ TIFFClose(out_file);
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char **argv)
+{
+#if 1
+ dither_tests_gray16();
+ dither_tests_gray8();
+ dither_tests_colour16();
+ dither_tests_colour8();
+#endif
+#if 1
+ grow_tests_colour8();
+#endif
+#if 1
+ lenna_tests(0, "lenna-bw.tif");
+ lenna_tests(1728, "lenna-bw-1728.tif");
+ lenna_tests(200, "lenna-bw-200.tif");
+#endif
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/saturated_tests.c b/libs/spandsp/tests/saturated_tests.c
new file mode 100644
index 0000000000..de2a40bcc2
--- /dev/null
+++ b/libs/spandsp/tests/saturated_tests.c
@@ -0,0 +1,311 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * saturated_tests.c
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 2004 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.
+ */
+
+/*! \page saturated_tests_page Saturated arithmetic function tests
+\section saturated_tests_page_sec_1 What does it do?
+???.
+
+\section saturated_tests_page_sec_2 How does it work?
+???.
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+#include "spandsp.h"
+
+int main(int argc, char *argv[])
+{
+ printf("Testing 16 bit saturation\n");
+ if (saturate16(10000) != 10000
+ ||
+ saturate16(-10000) != -10000
+ ||
+ saturate16(32767) != 32767
+ ||
+ saturate16(-32768) != -32768
+ ||
+ saturate16(32768) != 32767
+ ||
+ saturate16(-32769) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 15 bit saturation\n");
+ if (saturate15(10000) != 10000
+ ||
+ saturate15(-10000) != -10000
+ ||
+ saturate15(16383) != 16383
+ ||
+ saturate15(-16384) != -16384
+ ||
+ saturate15(16384) != 16383
+ ||
+ saturate15(-16385) != -16384)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit unsigned saturation\n");
+ if (saturateu16(10000) != 10000
+ ||
+ saturateu16(32767) != 32767
+ ||
+ saturateu16(65535) != 65535
+ ||
+ saturateu16(65536) != 65535)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 8 bit unsigned saturation\n");
+ if (saturateu8(100) != 100
+ ||
+ saturateu8(127) != 127
+ ||
+ saturateu8(255) != 255
+ ||
+ saturateu8(256) != 255)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit saturation from float\n");
+ if (fsaturatef(10000.0f) != 10000
+ ||
+ fsaturatef(-10000.0f) != -10000
+ ||
+ fsaturatef(32767.0f) != 32767
+ ||
+ fsaturatef(-32768.0f) != -32768
+ ||
+ fsaturatef(32768.0f) != 32767
+ ||
+ fsaturatef(-32769.0f) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit saturation from double\n");
+ if (fsaturate(10000.0) != 10000
+ ||
+ fsaturate(-10000.0) != -10000
+ ||
+ fsaturate(32767.0) != 32767
+ ||
+ fsaturate(-32768.0) != -32768
+ ||
+ fsaturate(32768.0) != 32767
+ ||
+ fsaturate(-32769.0) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit fast saturation from float\n");
+ if (ffastsaturatef(10000.0f) != 10000
+ ||
+ ffastsaturatef(-10000.0f) != -10000
+ ||
+ ffastsaturatef(32767.0f) != 32767
+ ||
+ ffastsaturatef(-32768.0f) != -32768
+ ||
+ ffastsaturatef(32768.0f) != 32767
+ ||
+ ffastsaturatef(-32769.0f) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit fast saturation from double\n");
+ if (ffastsaturate(10000.0) != 10000
+ ||
+ ffastsaturate(-10000.0) != -10000
+ ||
+ ffastsaturate(32767.0) != 32767
+ ||
+ ffastsaturate(-32768.0) != -32768
+ ||
+ ffastsaturate(32768.0) != 32767
+ ||
+ ffastsaturate(-32769.0) != -32768)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit float saturation from float\n");
+ if (ffsaturatef(10000.0f) != 10000.0f
+ ||
+ ffsaturatef(-10000.0f) != -10000.0f
+ ||
+ ffsaturatef(32767.0f) != 32767.0f
+ ||
+ ffsaturatef(-32768.0f) != -32768.0f
+ ||
+ ffsaturatef(32768.0f) != 32767.0f
+ ||
+ ffsaturatef(-32769.0f) != -32768.0f)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit double saturation from double\n");
+ if (ffsaturate(10000.0) != 10000.0
+ ||
+ ffsaturate(-10000.0) != -10000.0
+ ||
+ ffsaturate(32767.0) != 32767.0
+ ||
+ ffsaturate(-32768.0) != -32768.0
+ ||
+ ffsaturate(32768.0) != 32767.0
+ ||
+ ffsaturate(-32769.0) != -32768.0)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit add\n");
+ if (saturated_add16(10000, 10000) != 20000
+ ||
+ saturated_add16(10000, -10000) != 0
+ ||
+ saturated_add16(-10000, 10000) != 0
+ ||
+ saturated_add16(-10000, -10000) != -20000
+ ||
+ saturated_add16(-30000, -30000) != INT16_MIN
+ ||
+ saturated_add16(30000, 30000) != INT16_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 32 bit add\n");
+ if (saturated_add32(10000, 10000) != 20000
+ ||
+ saturated_add32(10000, -10000) != 0
+ ||
+ saturated_add32(-10000, 10000) != 0
+ ||
+ saturated_add32(-10000, -10000) != -20000
+ ||
+ saturated_add32(-2000000000, -2000000000) != INT32_MIN
+ ||
+ saturated_add32(2000000000, 2000000000) != INT32_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit subtract\n");
+ if (saturated_sub16(10000, 10000) != 0
+ ||
+ saturated_sub16(10000, -10000) != 20000
+ ||
+ saturated_sub16(-10000, 10000) != -20000
+ ||
+ saturated_sub16(-10000, -10000) != 0
+ ||
+ saturated_sub16(-30000, 30000) != INT16_MIN
+ ||
+ saturated_sub16(30000, -30000) != INT16_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 32 bit subtract\n");
+ if (saturated_sub32(10000, 10000) != 0
+ ||
+ saturated_sub32(10000, -10000) != 20000
+ ||
+ saturated_sub32(-10000, 10000) != -20000
+ ||
+ saturated_sub32(-10000, -10000) != 0
+ ||
+ saturated_sub32(-2000000000, 2000000000) != INT32_MIN
+ ||
+ saturated_sub32(2000000000, -2000000000) != INT32_MAX)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 x 16 => 16 bit multiply\n");
+ if (saturated_mul16(100, 100) != 0
+ ||
+ saturated_mul16(255, 255) != 1
+ ||
+ saturated_mul16(32767, -32768) != -32767
+ ||
+ saturated_mul16(-32768, 32767) != -32767
+ ||
+ saturated_mul16(32767, 32767) != 32766
+ ||
+ saturated_mul16(-32768, -32768) != 32767)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 x 16 => 32 bit multiply\n");
+ if (saturated_mul16_32(100, 100) != 20000
+ ||
+ saturated_mul16_32(-100, 100) != -20000
+ ||
+ saturated_mul16_32(32767, -32768) != -2147418112
+ ||
+ saturated_mul16_32(-32768, 32767) != -2147418112
+ ||
+ saturated_mul16_32(32767, 32767) != 2147352578
+ ||
+ saturated_mul16_32(-32768, -32768) != -2147483648)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Testing 16 bit absolute\n");
+ if (saturated_abs16(10000) != 10000
+ ||
+ saturated_abs16(-10000) != 10000
+ ||
+ saturated_abs16(32767) != 32767
+ ||
+ saturated_abs16(-32768) != 32767)
+ {
+ printf("Test failed.\n");
+ exit(2);
+ }
+ printf("Tests passed.\n");
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/timezone_tests.c b/libs/spandsp/tests/timezone_tests.c
new file mode 100644
index 0000000000..fb72f98437
--- /dev/null
+++ b/libs/spandsp/tests/timezone_tests.c
@@ -0,0 +1,86 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * timezone_tests.c - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood
+ *
+ * Copyright (C) 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.
+ */
+
+/*! \page timezone_tests_page Timezone handling tests
+\section timezone_tests_page_sec_1 What does it do?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+int main(int argc, char *argv[])
+{
+ struct tm tms;
+ struct tm *tmp = &tms;
+ time_t ltime;
+ tz_t *tz;
+
+ /* Get the current time */
+ ltime = time(NULL);
+
+ /* Compute the local current time now for several localities, based on Posix tz strings */
+
+ tz = tz_init(NULL, "GMT0GMT0,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_init(tz, "CST-8CST-8,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_init(tz, "AEST-10AEDT-11,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_free(tz);
+
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/tsb85_extra_tests.sh b/libs/spandsp/tests/tsb85_extra_tests.sh
new file mode 100755
index 0000000000..cc43f293ae
--- /dev/null
+++ b/libs/spandsp/tests/tsb85_extra_tests.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# spandsp fax tests
+#
+# 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 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.
+
+run_tsb85_test()
+{
+ rm -f fax_tests_1.tif
+ echo ./tsb85_tests ${TEST}
+ ./tsb85_tests -x ../spandsp/fax-tests.xml ${TEST} 2>xyzzy2
+ RETVAL=$?
+ if [ $RETVAL != 0 ]
+ then
+ echo tsb85_tests ${TEST} failed!
+ exit $RETVAL
+ fi
+}
+
+for TEST in PPS-MPS-lost-PPS ; do
+ run_tsb85_test
+done