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