]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Added some new files for spandsp
authorSteve Underwood <steveu@coppice.org>
Sat, 24 Jul 2010 19:43:53 +0000 (03:43 +0800)
committerSteve Underwood <steveu@coppice.org>
Sat, 24 Jul 2010 19:43:53 +0000 (03:43 +0800)
18 files changed:
libs/spandsp/spandsp/fax-tests.xml [new file with mode: 0644]
libs/spandsp/src/image_translate.c [new file with mode: 0644]
libs/spandsp/src/spandsp/image_translate.h [new file with mode: 0644]
libs/spandsp/src/spandsp/private/image_translate.h [new file with mode: 0644]
libs/spandsp/src/spandsp/private/t4_t6_decode.h [new file with mode: 0644]
libs/spandsp/src/spandsp/private/t4_t6_encode.h [new file with mode: 0644]
libs/spandsp/src/spandsp/private/timezone.h [new file with mode: 0644]
libs/spandsp/src/spandsp/t4_t6_decode.h [new file with mode: 0644]
libs/spandsp/src/spandsp/t4_t6_encode.h [new file with mode: 0644]
libs/spandsp/src/spandsp/timezone.h [new file with mode: 0644]
libs/spandsp/src/timezone.c [new file with mode: 0644]
libs/spandsp/test-data/itu/fax/generate_striped_pages.c [new file with mode: 0644]
libs/spandsp/test-data/local/lenna-colour.tif [new file with mode: 0644]
libs/spandsp/tests/bitstream_tests.c [new file with mode: 0644]
libs/spandsp/tests/image_translate_tests.c [new file with mode: 0644]
libs/spandsp/tests/saturated_tests.c [new file with mode: 0644]
libs/spandsp/tests/timezone_tests.c [new file with mode: 0644]
libs/spandsp/tests/tsb85_extra_tests.sh [new file with mode: 0755]

diff --git a/libs/spandsp/spandsp/fax-tests.xml b/libs/spandsp/spandsp/fax-tests.xml
new file mode 100644 (file)
index 0000000..e770e24
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!DOCTYPE fax-tests SYSTEM "./fax-tests.dtd">
+<fax-tests>
+<config>
+    <path type="IMAGE" value="../test-data/etsi/fax"/>
+</config>
+<messages>
+    <!-- TCF = 2700 bytes at 14400, 2250 at 12000, 1800 at 9600, 1350 at 7200, 900 at 4800 or 450 at 2400 -->
+    <!-- Bad TCF == 10101010.... -->
+    <!-- slow HDLC preamble == 37 flag bytes -->
+    <!-- slow HDLC inter-frame flag sequence == 1 flag byte -->
+    <!-- slow HDLC end flag sequence == 5 flag bytes -->
+    <!-- synchronisation sequence == 250ms of zeros. = 450 bytes at 14400, 375 at 12000, 300 at 9600, 225 at 7200, 150 at 4800 or 75 at 2400 -->
+    <!-- fast HDLC inter-frame flag sequence == 1 flag byte -->
+    <!-- fast HDLC end flag sequence == 10 flag bytes -->
+    <!-- STAIRSTEP image is 1728x1728 pixels. Its is about 15k, so an average of 68.2 bits per row. To
+         cook it as a 31k page requires a min_bits of 141. To cook it as a 63k page requires a min_bits of
+         286. To cook it as a 64k page requires a min_bits of 291 -->
+</messages>
+<test-group name="Supplementary">
+    <test name="PPS-MPS-lost-PPS">
+        <!-- Tester calls DUT and sends one 31k byte STAIRSTEP page and one 15k byte STAIRSTEP page. -->
+        <step type="CALL"/>
+
+        <!--<step dir="T" type="CNG"/>-->
+
+        <step dir="R" type="CED"/>
+        <step dir="R" type="HDLC" modem="V.21" tag="DIS" value="FF C8 01 ..." timeout="60000"/>
+        <step dir="R" type="SILENCE"/>
+
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="DCS" value="FF C8 41 00 50 1F 30"/>
+        <step dir="T" type="POSTAMBLE"/>
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="TCF" modem="V.27ter/4800" value="900"/>
+
+        <step dir="R" type="HDLC" modem="V.21" tag="CFR" value="FF C8 21"/>
+        <step dir="R" type="SILENCE"/>
+
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+        <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif" min_bits="141"/>
+        <step dir="T" type="POSTAMBLE"/>
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="PPS-NULL" value="FF C8 7D 00 00 00 08"/>
+        <step dir="T" type="POSTAMBLE"/>
+
+        <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+        <step dir="R" type="SILENCE"/>
+
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+        <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif"/>
+        <step dir="T" type="POSTAMBLE"/>
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 00 80 08"/>
+        <step dir="T" type="POSTAMBLE"/>
+
+        <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+        <step dir="R" type="SILENCE"/>
+
+        <!-- Repeat the last chunk, as though we missed the MCF -->
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+        <step dir="T" type="PP" value="etsi_300_242_a4_stairstep.tif"/>
+        <step dir="T" type="POSTAMBLE"/>
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 00 80 08"/>
+        <step dir="T" type="POSTAMBLE"/>
+
+        <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+        <step dir="R" type="SILENCE"/>
+
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.27ter/4800"/>
+        <step dir="T" type="PP" value="etsi_300_242_a4_white.tif"/>
+        <step dir="T" type="POSTAMBLE"/>
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="PPS-MPS" value="FF C8 7D 72 80 00 08"/>
+        <step dir="T" type="POSTAMBLE"/>
+
+        <step dir="R" type="HDLC" modem="V.21" tag="MCF" value="FF C8 31"/>
+        <step dir="R" type="SILENCE"/>
+
+        <step type="WAIT" value="75"/>
+        <step dir="T" type="PREAMBLE" modem="V.21"/>
+        <step dir="T" type="HDLC" tag="DCN" value="FF C8 5F"/>
+        <step dir="T" type="POSTAMBLE"/>
+    </test>
+</test-group>
+</fax-tests>
diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c
new file mode 100644 (file)
index 0000000..9221a9a
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <memory.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <tiffio.h>
+#include <assert.h>
+
+#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 (file)
index 0000000..b795c70
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_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 (file)
index 0000000..a4f7daa
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 (file)
index 0000000..1b15fc7
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * 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 (file)
index 0000000..40d750b
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * 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 (file)
index 0000000..b1c770b
--- /dev/null
@@ -0,0 +1,90 @@
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * private/timezone.h - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+#if !defined(_SPANDSP_PRIVATE_TIMEZONE_H_)\r
+#define _SPANDSP_PRIVATE_TIMEZONE_H_\r
+\r
+#define TZ_MAX_CHARS            50      /* Maximum number of abbreviation characters */\r
+\r
+#define TZ_MAX_LEAPS            50      /* Maximum number of leap second corrections */\r
+\r
+#define TZNAME_MAX              255\r
+\r
+/* The TZ_MAX_TIMES value below is enough to handle a bit more than a\r
+ * year's worth of solar time (corrected daily to the nearest second) or\r
+ * 138 years of Pacific Presidential Election time\r
+ * (where there are three time zone transitions every fourth year). */\r
+#define TZ_MAX_TIMES            370\r
+\r
+#if !defined(NOSOLAR)\r
+#define TZ_MAX_TYPES            256     /* Limited by what (unsigned char)'s can hold */\r
+#else\r
+/* Must be at least 14 for Europe/Riga as of Jan 12 1995,\r
+ * as noted by Earl Chew <earl@hpato.aus.hp.com>. */\r
+#define TZ_MAX_TYPES            20      /* Maximum number of local time types */\r
+#endif\r
+\r
+#define TZ_BIGGEST(a, b)        (((a) > (b)) ? (a) : (b))\r
+\r
+/* Time type information */\r
+struct tz_ttinfo_s\r
+{\r
+    int32_t gmtoff;             /* UTC offset in seconds */\r
+    int isdst;                  /* Used to set tm_isdst */\r
+    int abbrind;                /* Abbreviation list index */\r
+    int ttisstd;                /* TRUE if transition is std time */\r
+    int ttisgmt;                /* TRUE if transition is UTC */\r
+};\r
+\r
+/* Leap second information */\r
+struct tz_lsinfo_s\r
+{\r
+    time_t trans;               /* Transition time */\r
+    int32_t corr;               /* Correction to apply */\r
+};\r
+\r
+struct tz_state_s\r
+{\r
+    int leapcnt;\r
+    int timecnt;\r
+    int typecnt;\r
+    int charcnt;\r
+    time_t ats[TZ_MAX_TIMES];\r
+    uint8_t types[TZ_MAX_TIMES];\r
+    struct tz_ttinfo_s ttis[TZ_MAX_TYPES];\r
+    char chars[TZ_BIGGEST(TZ_MAX_CHARS + 1, (2*(TZNAME_MAX + 1)))];\r
+    struct tz_lsinfo_s lsis[TZ_MAX_LEAPS];\r
+};\r
+\r
+struct tz_s\r
+{\r
+    struct tz_state_s state;\r
+    char lcl_tzname[TZNAME_MAX + 1];\r
+    int lcl_is_set;\r
+    const char *tzname[2];\r
+};\r
+\r
+#endif\r
+/*- End of file ------------------------------------------------------------*/\r
diff --git a/libs/spandsp/src/spandsp/t4_t6_decode.h b/libs/spandsp/src/spandsp/t4_t6_decode.h
new file mode 100644 (file)
index 0000000..860d4ec
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * 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 (file)
index 0000000..72a3598
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * 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 (file)
index 0000000..0f9a2ee
--- /dev/null
@@ -0,0 +1,88 @@
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone.h - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \file */\r
+\r
+#if !defined(_SPANDSP_TIMEZONE_H_)\r
+#define _SPANDSP_TIMEZONE_H_\r
+\r
+/*! \page timezone_page Timezone handling\r
+\r
+\section timezone_sec_1 What does it do?\r
+\r
+\section timezone_sec_2 How does it work?\r
+\r
+*/\r
+\r
+typedef struct tz_s tz_t;\r
+\r
+enum\r
+{\r
+    TM_SUNDAY = 0,\r
+    TM_MONDAY,\r
+    TM_TUESDAY,\r
+    TM_WEDNESDAY,\r
+    TM_THURSDAY,\r
+    TM_FRIDAY,\r
+    TM_SATURDAY\r
+};\r
+\r
+enum\r
+{\r
+    TM_JANUARY = 0,\r
+    TM_FEBRUARY,\r
+    TM_MARCH,\r
+    TM_APRIL,\r
+    TM_MAY,\r
+    TM_JUNE,\r
+    TM_JULY,\r
+    TM_AUGUST,\r
+    TM_SEPTEMBER,\r
+    TM_OCTOBER,\r
+    TM_NOVEMBER,\r
+    TM_DECEMBER\r
+};\r
+\r
+#if defined(__cplusplus)\r
+extern "C"\r
+{\r
+#endif\r
+\r
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring);\r
+\r
+SPAN_DECLARE(int) tz_release(tz_t *tz);\r
+\r
+SPAN_DECLARE(int) tz_free(tz_t *tz);\r
+\r
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tm, time_t t);\r
+\r
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst);\r
+\r
+#if defined(__cplusplus)\r
+}\r
+#endif\r
+\r
+#endif\r
+/*- End of file ------------------------------------------------------------*/\r
diff --git a/libs/spandsp/src/timezone.c b/libs/spandsp/src/timezone.c
new file mode 100644 (file)
index 0000000..d0cab09
--- /dev/null
@@ -0,0 +1,822 @@
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone.c - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \file */\r
+\r
+/* Timezone processing might not seem like a DSP activity, but getting the headers\r
+   right on FAXes demands it. We need to handle multiple time zones within a process,\r
+   for FAXes related to different parts of the globe, so the system timezone handling\r
+   is not adequate. */\r
+\r
+/* This timezone handling is derived from public domain software by Arthur David Olson\r
+   <arthur_david_olson@nih.gov> which you may download from ftp://elsie.nci.nih.gov/pub\r
+   at the time of writing. */\r
+\r
+#if defined(HAVE_CONFIG_H)\r
+#include "config.h"\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <inttypes.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <time.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#include "spandsp/telephony.h"\r
+#include "spandsp/timezone.h"\r
+\r
+#include "spandsp/private/timezone.h"\r
+\r
+#if !defined(FALSE)\r
+#define FALSE    0\r
+#endif\r
+\r
+#if !defined(TRUE)\r
+#define TRUE    (!FALSE)\r
+#endif\r
+\r
+#define SECS_PER_MIN            60\r
+#define MINS_PER_HOUR           60\r
+#define HOURS_PER_DAY           24\r
+#define DAYS_PER_WEEK           7\r
+#define DAYS_PER_NON_LEAP_YEAR  365\r
+#define DAYS_PER_LEAP_YEAR      366\r
+#define SECS_PER_HOUR           (SECS_PER_MIN*MINS_PER_HOUR)\r
+#define SECS_PER_DAY            ((long int) SECS_PER_HOUR*HOURS_PER_DAY)\r
+#define MONTHS_PER_YEAR         12\r
+\r
+#define TM_YEAR_BASE            1900\r
+\r
+#define EPOCH_YEAR              1970\r
+#define EPOCH_WDAY              TM_THURSDAY\r
+\r
+#define isleap(y)               (((y)%4) == 0  &&  (((y)%100) != 0  ||  ((y)%400) == 0))\r
+\r
+#define isleap_sum(a, b)        isleap((a)%400 + (b)%400)\r
+\r
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */\r
+#define is_digit(c)             ((unsigned int) (c) - '0' <= 9)\r
+\r
+#define TZ_DEF_RULE_STRING      ",M4.1.0,M10.5.0"\r
+\r
+#define JULIAN_DAY              0       /* Jn - Julian day */\r
+#define DAY_OF_YEAR             1       /* n - day of year */\r
+#define MONTH_NTH_DAY_OF_WEEK   2       /* Mm.n.d - month, week, day of week */\r
+\r
+static const char wildabbr[] = "   ";\r
+\r
+static const char gmt[] = "GMT";\r
+\r
+struct tz_rule_s\r
+{\r
+    int r_type;                         /* Type of rule--see below */\r
+    int r_day;                          /* Day number of rule */\r
+    int r_week;                         /* Week number of rule */\r
+    int r_mon;                          /* Month number of rule */\r
+    long int r_time;                    /* Transition time of rule */\r
+};\r
+\r
+static const int mon_lengths[2][MONTHS_PER_YEAR] =\r
+{\r
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},\r
+    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}\r
+};\r
+\r
+static const int year_lengths[2] =\r
+{\r
+    DAYS_PER_NON_LEAP_YEAR,\r
+    DAYS_PER_LEAP_YEAR\r
+};\r
+\r
+static int increment_overflow(int *number, int delta)\r
+{\r
+    int number0;\r
+\r
+    number0 = *number;\r
+    *number += delta;\r
+    return (*number < number0) != (delta < 0);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+static void set_tzname(tz_t *tz)\r
+{\r
+    struct tz_state_s *sp;\r
+    const struct tz_ttinfo_s *ttisp;\r
+    int i;\r
+\r
+    sp = &tz->state;\r
+    tz->tzname[0] = wildabbr;\r
+    tz->tzname[1] = wildabbr;\r
+    for (i = 0;  i < sp->typecnt;  i++)\r
+    {\r
+        ttisp = &sp->ttis[i];\r
+        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
+    }\r
+    for (i = 0;  i < sp->timecnt;  i++)\r
+    {\r
+        ttisp = &sp->ttis[sp->types[i]];\r
+        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
+    }\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Return the number of leap years through the end of the given year\r
+   where, to make the math easy, the answer for year zero is defined as zero. */\r
+static int leaps_thru_end_of(const int y)\r
+{\r
+    return (y >= 0)  ?  (y/4 - y/100 + y/400)  :  -(leaps_thru_end_of(-(y + 1)) + 1);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+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)\r
+{\r
+    const struct tz_lsinfo_s *lp;\r
+    time_t tdays;\r
+    const int *ip;\r
+    int32_t corr;\r
+    int32_t seconds;\r
+    int32_t rem;\r
+    int idays;\r
+    int y;\r
+    int hit;\r
+    int i;\r
+\r
+    corr = 0;\r
+    hit = 0;\r
+    i = sp->leapcnt;\r
+    while (--i >= 0)\r
+    {\r
+        lp = &sp->lsis[i];\r
+        if (*timep >= lp->trans)\r
+        {\r
+            if (*timep == lp->trans)\r
+            {\r
+                hit = ((i == 0  &&  lp->corr > 0)  ||  lp->corr > sp->lsis[i - 1].corr);\r
+                if (hit)\r
+                {\r
+                    while (i > 0\r
+                           &&\r
+                           sp->lsis[i].trans == sp->lsis[i - 1].trans + 1\r
+                           &&\r
+                           sp->lsis[i].corr == sp->lsis[i - 1].corr + 1)\r
+                    {\r
+                        hit++;\r
+                        --i;\r
+                    }\r
+                }\r
+            }\r
+            corr = lp->corr;\r
+            break;\r
+        }\r
+    }\r
+    y = EPOCH_YEAR;\r
+    tdays = *timep/SECS_PER_DAY;\r
+    rem = *timep - tdays*SECS_PER_DAY;\r
+    while (tdays < 0  ||  tdays >= year_lengths[isleap(y)])\r
+    {\r
+        int newy;\r
+        time_t tdelta;\r
+        int idelta;\r
+        int leapdays;\r
+\r
+        tdelta = tdays / DAYS_PER_LEAP_YEAR;\r
+        idelta = tdelta;\r
+        if (tdelta - idelta >= 1  ||  idelta - tdelta >= 1)\r
+            return NULL;\r
+        if (idelta == 0)\r
+            idelta = (tdays < 0)  ?  -1  :  1;\r
+        newy = y;\r
+        if (increment_overflow(&newy, idelta))\r
+            return NULL;\r
+        leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);\r
+        tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;\r
+        tdays -= leapdays;\r
+        y = newy;\r
+    }\r
+    seconds = tdays*SECS_PER_DAY;\r
+    tdays = seconds/SECS_PER_DAY;\r
+    rem += seconds - tdays*SECS_PER_DAY;\r
+    /* Given the range, we can now fearlessly cast... */\r
+    idays = tdays;\r
+    rem += (offset - corr);\r
+    while (rem < 0)\r
+    {\r
+        rem += SECS_PER_DAY;\r
+        idays--;\r
+    }\r
+    while (rem >= SECS_PER_DAY)\r
+    {\r
+        rem -= SECS_PER_DAY;\r
+        idays++;\r
+    }\r
+    while (idays < 0)\r
+    {\r
+        if (increment_overflow(&y, -1))\r
+            return NULL;\r
+        idays += year_lengths[isleap(y)];\r
+    }\r
+    while (idays >= year_lengths[isleap(y)])\r
+    {\r
+        idays -= year_lengths[isleap(y)];\r
+        if (increment_overflow(&y, 1))\r
+            return NULL;\r
+    }\r
+    tmp->tm_year = y;\r
+    if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))\r
+        return NULL;\r
+    tmp->tm_yday = idays;\r
+    /* The "extra" mods below avoid overflow problems. */\r
+    tmp->tm_wday = EPOCH_WDAY\r
+                 + ((y - EPOCH_YEAR) % DAYS_PER_WEEK)*(DAYS_PER_NON_LEAP_YEAR % DAYS_PER_WEEK)\r
+                 + leaps_thru_end_of(y - 1)\r
+                 - leaps_thru_end_of(EPOCH_YEAR - 1)\r
+                 + idays;\r
+    tmp->tm_wday %= DAYS_PER_WEEK;\r
+    if (tmp->tm_wday < 0)\r
+        tmp->tm_wday += DAYS_PER_WEEK;\r
+    tmp->tm_hour = (int) (rem/SECS_PER_HOUR);\r
+    rem %= SECS_PER_HOUR;\r
+    tmp->tm_min = (int) (rem/SECS_PER_MIN);\r
+    /* A positive leap second requires a special\r
+     * representation. This uses "... ??:59:60" et seq. */\r
+    tmp->tm_sec = (int) (rem%SECS_PER_MIN) + hit;\r
+    ip = mon_lengths[isleap(y)];\r
+    for (tmp->tm_mon = 0;  idays >= ip[tmp->tm_mon];  (tmp->tm_mon)++)\r
+        idays -= ip[tmp->tm_mon];\r
+    tmp->tm_mday = (int) (idays + 1);\r
+    tmp->tm_isdst = 0;\r
+    return tmp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, scan until a character that is not\r
+ * a valid character in a zone name is found. Return a pointer to that\r
+ * character. */\r
+static const char *get_tzname(const char *strp)\r
+{\r
+    char c;\r
+\r
+    while ((c = *strp) != '\0'  &&  !is_digit(c)  &&  c != ','  &&  c != '-'  &&  c != '+')\r
+        strp++;\r
+    return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a number from that string.\r
+ * Check that the number is within a specified range; if it is not, return\r
+ * NULL.\r
+ * Otherwise, return a pointer to the first character not part of the number. */\r
+static const char *get_num(const char *strp, int * const nump, const int min, const int max)\r
+{\r
+    char c;\r
+    int num;\r
+\r
+    if (strp == NULL  ||  !is_digit(c = *strp))\r
+        return NULL;\r
+    num = 0;\r
+    do\r
+    {\r
+        num = num*10 + (c - '0');\r
+        if (num > max)\r
+            return NULL;    /* Illegal value */\r
+        c = *++strp;\r
+    }\r
+    while (is_digit(c));\r
+    if (num < min)\r
+        return NULL;        /* Illegal value */\r
+    *nump = num;\r
+    return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a number of seconds,\r
+ * in hh[:mm[:ss]] form, from the string.\r
+ * If any error occurs, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the number\r
+ * of seconds. */\r
+static const char *get_secs(const char *strp, long int * const secsp)\r
+{\r
+    int num;\r
+\r
+    /* HOURS_PER_DAY*DAYS_PER_WEEK - 1 allows quasi-Posix rules like\r
+     * "M10.4.6/26", which does not conform to Posix,\r
+     * but which specifies the equivalent of\r
+     * "02:00 on the first Sunday on or after 23 Oct". */\r
+    strp = get_num(strp, &num, 0, HOURS_PER_DAY*DAYS_PER_WEEK - 1);\r
+    if (strp == NULL)\r
+        return NULL;\r
+    *secsp = num*(long int) SECS_PER_HOUR;\r
+    if (*strp == ':')\r
+    {\r
+        strp = get_num(strp + 1, &num, 0, MINS_PER_HOUR - 1);\r
+        if (strp == NULL)\r
+            return NULL;\r
+        *secsp += num*SECS_PER_MIN;\r
+        if (*strp == ':')\r
+        {\r
+            /* SECS_PER_MIN allows for leap seconds. */\r
+            strp = get_num(strp + 1, &num, 0, SECS_PER_MIN);\r
+            if (strp == NULL)\r
+                return NULL;\r
+            *secsp += num;\r
+        }\r
+    }\r
+    return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract an offset, in\r
+ * [+-]hh[:mm[:ss]] form, from the string.\r
+ * If any error occurs, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the time. */\r
+static const char *get_offset(const char *strp, long int * const offsetp)\r
+{\r
+    int neg = 0;\r
+\r
+    if (*strp == '-')\r
+    {\r
+        neg = 1;\r
+        strp++;\r
+    }\r
+    else if (*strp == '+')\r
+    {\r
+        strp++;\r
+    }\r
+    strp = get_secs(strp, offsetp);\r
+    if (strp == NULL)\r
+        return NULL;        /* Illegal time */\r
+    if (neg)\r
+        *offsetp = -*offsetp;\r
+    return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a pointer into a time zone string, extract a rule in the form\r
+ * date[/time]. See POSIX section 8 for the format of "date" and "time".\r
+ * If a valid rule is not found, return NULL.\r
+ * Otherwise, return a pointer to the first character not part of the rule. */\r
+static const char *get_rule(const char *strp, struct tz_rule_s * const rulep)\r
+{\r
+    if (*strp == 'J')\r
+    {\r
+        /* Julian day. */\r
+        rulep->r_type = JULIAN_DAY;\r
+        strp = get_num(strp + 1, &rulep->r_day, 1, DAYS_PER_NON_LEAP_YEAR);\r
+    }\r
+    else if (*strp == 'M')\r
+    {\r
+        /* Month, week, day. */\r
+        rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r
+        strp = get_num(strp + 1, &rulep->r_mon, 1, MONTHS_PER_YEAR);\r
+        if (strp == NULL  ||  *strp++ != '.')\r
+            return NULL;\r
+        strp = get_num(strp, &rulep->r_week, 1, 5);\r
+        if (strp == NULL  ||  *strp++ != '.')\r
+            return NULL;\r
+        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_WEEK - 1);\r
+    }\r
+    else if (is_digit(*strp))\r
+    {\r
+        /* Day of the year. */\r
+        rulep->r_type = DAY_OF_YEAR;\r
+        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_LEAP_YEAR - 1);\r
+    }\r
+    else\r
+    {\r
+        /* Invalid format */\r
+        return NULL;\r
+    }\r
+    if (strp == NULL)\r
+        return NULL;\r
+    if (*strp == '/')\r
+    {\r
+        /* Time specified. */\r
+        strp = get_secs(strp + 1, &rulep->r_time);\r
+    }\r
+    else\r
+    {\r
+        /* Default = 2:00:00 */\r
+        rulep->r_time = 2*SECS_PER_HOUR;\r
+    }\r
+    return strp;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r
+ * year, a rule, and the offset from UTC at the time that rule takes effect,\r
+ * calculate the Epoch-relative time that rule takes effect. */\r
+static time_t trans_time(const time_t janfirst, const int year, const struct tz_rule_s * const rulep, const long int offset)\r
+{\r
+    int leapyear;\r
+    time_t value;\r
+    int i;\r
+    int d;\r
+    int m1;\r
+    int yy0;\r
+    int yy1;\r
+    int yy2;\r
+    int dow;\r
+\r
+    value = 0;\r
+    leapyear = isleap(year);\r
+    switch (rulep->r_type)\r
+    {\r
+    case JULIAN_DAY:\r
+        /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r
+         * years.\r
+         * In non-leap years, or if the day number is 59 or less, just\r
+         * add SECS_PER_DAY times the day number-1 to the time of\r
+         * January 1, midnight, to get the day. */\r
+        value = janfirst + (rulep->r_day - 1)*SECS_PER_DAY;\r
+        if (leapyear  &&  rulep->r_day >= 60)\r
+            value += SECS_PER_DAY;\r
+        break;\r
+    case DAY_OF_YEAR:\r
+        /* n - day of year.\r
+         * Just add SECS_PER_DAY times the day number to the time of\r
+         * January 1, midnight, to get the day. */\r
+        value = janfirst + rulep->r_day * SECS_PER_DAY;\r
+        break;\r
+    case MONTH_NTH_DAY_OF_WEEK:\r
+        /* Mm.n.d - nth "dth day" of month m. */\r
+        value = janfirst;\r
+        for (i = 0;  i < rulep->r_mon - 1;  i++)\r
+            value += mon_lengths[leapyear][i]*SECS_PER_DAY;\r
+\r
+        /* Use Zeller's Congruence to get day-of-week of first day of month. */\r
+        m1 = (rulep->r_mon + 9)%12 + 1;\r
+        yy0 = (rulep->r_mon <= 2)  ?  (year - 1)  :  year;\r
+        yy1 = yy0/100;\r
+        yy2 = yy0%100;\r
+        dow = ((26*m1 - 2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1)%7;\r
+        if (dow < 0)\r
+            dow += DAYS_PER_WEEK;\r
+\r
+        /* "dow" is the day-of-week of the first day of the month. Get\r
+         * the day-of-month (zero-origin) of the first "dow" day of the\r
+         * month. */\r
+        d = rulep->r_day - dow;\r
+        if (d < 0)\r
+            d += DAYS_PER_WEEK;\r
+        for (i = 1;  i < rulep->r_week;  i++)\r
+        {\r
+            if (d + DAYS_PER_WEEK >= mon_lengths[leapyear][rulep->r_mon - 1])\r
+                break;\r
+            d += DAYS_PER_WEEK;\r
+        }\r
+\r
+        /* "d" is the day-of-month (zero-origin) of the day we want. */\r
+        value += d*SECS_PER_DAY;\r
+        break;\r
+    }\r
+\r
+    /* "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r
+     * question. To get the Epoch-relative time of the specified local\r
+     * time on that day, add the transition time and the current offset\r
+     * from UTC. */\r
+    return value + rulep->r_time + offset;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+/* Given a POSIX section 8-style TZ string, fill in the rule tables as\r
+   appropriate. */\r
+static int tzparse(const char *name, struct tz_state_s * const sp, const int lastditch)\r
+{\r
+    const char *stdname;\r
+    const char *dstname;\r
+    size_t stdlen;\r
+    size_t dstlen;\r
+    long int stdoffset;\r
+    long int dstoffset;\r
+    long int theirstdoffset;\r
+    long int theirdstoffset;\r
+    long int theiroffset;\r
+    unsigned char *typep;\r
+    char *cp;\r
+    int load_result;\r
+    int isdst;\r
+    int i;\r
+    int j;\r
+    int year;\r
+    struct tz_rule_s start;\r
+    struct tz_rule_s end;\r
+    time_t *atp;\r
+    time_t janfirst;\r
+    time_t starttime;\r
+    time_t endtime;\r
+\r
+    dstname = NULL;\r
+    stdname = name;\r
+    if (lastditch)\r
+    {\r
+        stdlen = strlen(name);      /* Length of standard zone name */\r
+        name += stdlen;\r
+        if (stdlen >= sizeof(sp->chars))\r
+            stdlen = sizeof(sp->chars) - 1;\r
+        stdoffset = 0;\r
+    }\r
+    else\r
+    {\r
+        name = get_tzname(name);\r
+        stdlen = name - stdname;\r
+        if (stdlen < 3)\r
+            return -1;\r
+        if (*name == '\0')\r
+            return -1;\r
+        name = get_offset(name, &stdoffset);\r
+        if (name == NULL)\r
+            return -1;\r
+    }\r
+    load_result = -1;\r
+    if (load_result != 0)\r
+        sp->leapcnt = 0;            /* So, we're off a little */\r
+    if (*name != '\0')\r
+    {\r
+        dstname = name;\r
+        name = get_tzname(name);\r
+        dstlen = name - dstname;    /* Length of DST zone name */\r
+        if (dstlen < 3)\r
+            return -1;\r
+        if (*name != '\0'  &&  *name != ','  &&  *name != ';')\r
+        {\r
+            if ((name = get_offset(name, &dstoffset)) == NULL)\r
+                return -1;\r
+        }\r
+        else\r
+        {\r
+            dstoffset = stdoffset - SECS_PER_HOUR;\r
+        }\r
+        if (*name == '\0'  &&  load_result != 0)\r
+            name = TZ_DEF_RULE_STRING;\r
+        if (*name == ','  ||  *name == ';')\r
+        {\r
+            if ((name = get_rule(name + 1, &start)) == NULL)\r
+                return -1;\r
+            if (*name++ != ',')\r
+                return -1;\r
+            if ((name = get_rule(name, &end)) == NULL)\r
+                return -1;\r
+            if (*name != '\0')\r
+                return -1;\r
+            sp->typecnt = 2;        /* Standard time and DST */\r
+            /* Two transitions per year, from EPOCH_YEAR to 2037. */\r
+            sp->timecnt = 2*(2037 - EPOCH_YEAR + 1);\r
+            if (sp->timecnt > TZ_MAX_TIMES)\r
+                return -1;\r
+            sp->ttis[0].gmtoff = -dstoffset;\r
+            sp->ttis[0].isdst = 1;\r
+            sp->ttis[0].abbrind = stdlen + 1;\r
+            sp->ttis[1].gmtoff = -stdoffset;\r
+            sp->ttis[1].isdst = 0;\r
+            sp->ttis[1].abbrind = 0;\r
+            atp = sp->ats;\r
+            typep = sp->types;\r
+            janfirst = 0;\r
+            for (year = EPOCH_YEAR;  year <= 2037;  year++)\r
+            {\r
+                starttime = trans_time(janfirst, year, &start, stdoffset);\r
+                endtime = trans_time(janfirst, year, &end, dstoffset);\r
+                if (starttime > endtime)\r
+                {\r
+                    *atp++ = endtime;\r
+                    *typep++ = 1;    /* DST ends */\r
+                    *atp++ = starttime;\r
+                    *typep++ = 0;    /* DST begins */\r
+                }\r
+                else\r
+                {\r
+                    *atp++ = starttime;\r
+                    *typep++ = 0;    /* DST begins */\r
+                    *atp++ = endtime;\r
+                    *typep++ = 1;    /* DST ends */\r
+                }\r
+                janfirst += year_lengths[isleap(year)]*SECS_PER_DAY;\r
+            }\r
+        }\r
+        else\r
+        {\r
+            if (*name != '\0')\r
+                return -1;\r
+            /* Initial values of theirstdoffset and theirdstoffset. */\r
+            theirstdoffset = 0;\r
+            for (i = 0;  i < sp->timecnt;  i++)\r
+            {\r
+                j = sp->types[i];\r
+                if (!sp->ttis[j].isdst)\r
+                {\r
+                    theirstdoffset = -sp->ttis[j].gmtoff;\r
+                    break;\r
+                }\r
+            }\r
+            theirdstoffset = 0;\r
+            for (i = 0;  i < sp->timecnt;  i++)\r
+            {\r
+                j = sp->types[i];\r
+                if (sp->ttis[j].isdst)\r
+                {\r
+                    theirdstoffset = -sp->ttis[j].gmtoff;\r
+                    break;\r
+                }\r
+            }\r
+            /* Initially we're assumed to be in standard time. */\r
+            isdst = FALSE;\r
+            theiroffset = theirstdoffset;\r
+            /* Now juggle transition times and types tracking offsets as you do. */\r
+            for (i = 0;  i < sp->timecnt;  i++)\r
+            {\r
+                j = sp->types[i];\r
+                sp->types[i] = sp->ttis[j].isdst;\r
+                if (sp->ttis[j].ttisgmt)\r
+                {\r
+                    /* No adjustment to transition time */\r
+                }\r
+                else\r
+                {\r
+                    /* If summer time is in effect, and the\r
+                     * transition time was not specified as\r
+                     * standard time, add the summer time\r
+                     * offset to the transition time;\r
+                     * otherwise, add the standard time\r
+                     * offset to the transition time. */\r
+                    /* Transitions from DST to DDST\r
+                     * will effectively disappear since\r
+                     * POSIX provides for only one DST\r
+                     * offset. */\r
+                    if (isdst  &&  !sp->ttis[j].ttisstd)\r
+                        sp->ats[i] += (dstoffset - theirdstoffset);\r
+                    else\r
+                        sp->ats[i] += (stdoffset - theirstdoffset);\r
+                }\r
+                theiroffset = -sp->ttis[j].gmtoff;\r
+                if (sp->ttis[j].isdst)\r
+                    theirdstoffset = theiroffset;\r
+                else\r
+                    theirstdoffset = theiroffset;\r
+            }\r
+            /* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. */\r
+            sp->ttis[0].gmtoff = -stdoffset;\r
+            sp->ttis[0].isdst = FALSE;\r
+            sp->ttis[0].abbrind = 0;\r
+            sp->ttis[1].gmtoff = -dstoffset;\r
+            sp->ttis[1].isdst = TRUE;\r
+            sp->ttis[1].abbrind = stdlen + 1;\r
+            sp->typecnt = 2;\r
+        }\r
+    }\r
+    else\r
+    {\r
+        dstlen = 0;\r
+        sp->typecnt = 1;        /* Only standard time */\r
+        sp->timecnt = 0;\r
+        sp->ttis[0].gmtoff = -stdoffset;\r
+        sp->ttis[0].isdst = 0;\r
+        sp->ttis[0].abbrind = 0;\r
+    }\r
+    sp->charcnt = stdlen + 1;\r
+    if (dstlen != 0)\r
+        sp->charcnt += dstlen + 1;\r
+    if ((size_t) sp->charcnt > sizeof(sp->chars))\r
+        return -1;\r
+    cp = sp->chars;\r
+    strncpy(cp, stdname, stdlen);\r
+    cp += stdlen;\r
+    *cp++ = '\0';\r
+    if (dstlen != 0)\r
+    {\r
+        strncpy(cp, dstname, dstlen);\r
+        cp[dstlen] = '\0';\r
+    }\r
+    return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+static void tz_set(tz_t *tz, const char *tzstring)\r
+{\r
+    const char *name = "";\r
+    struct tz_state_s *lclptr = &tz->state;\r
+\r
+    if (tzstring)\r
+        name = tzstring;\r
+\r
+    /* See if we are already set OK */\r
+    if (tz->lcl_is_set > 0  &&  strcmp(tz->lcl_tzname, name) == 0)\r
+        return;\r
+    tz->lcl_is_set = strlen(name) < sizeof(tz->lcl_tzname);\r
+    if (tz->lcl_is_set)\r
+        strcpy(tz->lcl_tzname, name);\r
+\r
+    if (name[0] == '\0')\r
+    {\r
+        /* User wants it fast rather than right, so, we're off a little. */\r
+        lclptr->leapcnt = 0;\r
+        lclptr->timecnt = 0;\r
+        lclptr->typecnt = 0;\r
+        lclptr->ttis[0].isdst = 0;\r
+        lclptr->ttis[0].gmtoff = 0;\r
+        lclptr->ttis[0].abbrind = 0;\r
+        strcpy(lclptr->chars, gmt);\r
+    }\r
+    else if (name[0] == ':'  ||  tzparse(name, lclptr, FALSE) != 0)\r
+    {\r
+        tzparse(gmt, lclptr, TRUE);\r
+    }\r
+    set_tzname(tz);\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tmp, time_t t)\r
+{\r
+    struct tz_state_s *sp;\r
+    const struct tz_ttinfo_s *ttisp;\r
+    int i;\r
+\r
+    sp = &tz->state;\r
+\r
+    if (sp->timecnt == 0  ||  t < sp->ats[0])\r
+    {\r
+        i = 0;\r
+        while (sp->ttis[i].isdst)\r
+        {\r
+            if (++i >= sp->typecnt)\r
+            {\r
+                i = 0;\r
+                break;\r
+            }\r
+        }\r
+    }\r
+    else\r
+    {\r
+        for (i = 1;  i < sp->timecnt;  i++)\r
+        {\r
+            if (t < sp->ats[i])\r
+                break;\r
+        }\r
+        i = (int) sp->types[i - 1];\r
+    }\r
+    ttisp = &sp->ttis[i];\r
+    time_sub(&t, ttisp->gmtoff, sp, tmp);\r
+    tmp->tm_isdst = ttisp->isdst;\r
+    tz->tzname[tmp->tm_isdst] = &sp->chars[ttisp->abbrind];\r
+    return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst)\r
+{\r
+    return tz->tzname[(!isdst)  ?  0  :  1];\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring)\r
+{\r
+    if (tz == NULL)\r
+    {\r
+        if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL)\r
+            return NULL;\r
+    }\r
+    memset(tz, 0, sizeof(*tz));\r
+    tz->tzname[0] =\r
+    tz->tzname[1] = wildabbr;\r
+    tz_set(tz, tzstring);\r
+    return tz;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_release(tz_t *tz)\r
+{\r
+    return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+\r
+SPAN_DECLARE(int) tz_free(tz_t *tz)\r
+{\r
+    if (tz)\r
+        free(tz);\r
+    return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+/*- End of file ------------------------------------------------------------*/\r
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 (file)
index 0000000..0c3eafd
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * generate_striped_pages.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <stdio.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <time.h>
+#include <memory.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include <tiffio.h>
+
+#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 (file)
index 0000000..4a282db
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 (file)
index 0000000..e17b809
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * bitstream_tests.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+//#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 (file)
index 0000000..be0f5ea
--- /dev/null
@@ -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 <steveu@coppice.org>
+ *
+ * Copyright (C) 2009 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/*! \page 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <errno.h>
+
+//#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 (file)
index 0000000..de2a40b
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * saturated_tests.c
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 (file)
index 0000000..fb72f98
--- /dev/null
@@ -0,0 +1,86 @@
+/*\r
+ * SpanDSP - a series of DSP components for telephony\r
+ *\r
+ * timezone_tests.c - Timezone handling for time interpretation\r
+ *\r
+ * Written by Steve Underwood <steveu@coppice.org>\r
+ *\r
+ * Copyright (C) 2010 Steve Underwood\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License version 2.1,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ */\r
+\r
+/*! \page timezone_tests_page Timezone handling tests\r
+\section timezone_tests_page_sec_1 What does it do?\r
+*/\r
+\r
+#if defined(HAVE_CONFIG_H)\r
+#include "config.h"\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <inttypes.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <time.h>\r
+\r
+//#if defined(WITH_SPANDSP_INTERNALS)\r
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES\r
+//#endif\r
+\r
+#include "spandsp.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE    0\r
+#endif\r
+\r
+#ifndef TRUE\r
+#define TRUE    (!FALSE)\r
+#endif\r
+\r
+int main(int argc, char *argv[])\r
+{\r
+    struct tm tms;\r
+    struct tm *tmp = &tms;\r
+    time_t ltime;\r
+    tz_t *tz;\r
+\r
+    /* Get the current time */\r
+    ltime = time(NULL);\r
+\r
+    /* Compute the local current time now for several localities, based on Posix tz strings */\r
+\r
+    tz = tz_init(NULL, "GMT0GMT0,M10.5.0,M3.5.0");\r
+    tz_localtime(tz, tmp, ltime);\r
+    printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+    printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+    tz_init(tz, "CST-8CST-8,M10.5.0,M3.5.0");\r
+    tz_localtime(tz, tmp, ltime);\r
+    printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+    printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+    tz_init(tz, "AEST-10AEDT-11,M10.5.0,M3.5.0");\r
+    tz_localtime(tz, tmp, ltime);\r
+    printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
+    printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
+\r
+    tz_free(tz);\r
+\r
+    return 0;\r
+}\r
+/*- End of function --------------------------------------------------------*/\r
+/*- End of file ------------------------------------------------------------*/\r
diff --git a/libs/spandsp/tests/tsb85_extra_tests.sh b/libs/spandsp/tests/tsb85_extra_tests.sh
new file mode 100755 (executable)
index 0000000..cc43f29
--- /dev/null
@@ -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