]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Started introducing T.42 JPEG support for FAXing, but its not plumbed into the
authorSteve Underwood <steveu@coppice.org>
Fri, 20 Jul 2012 20:05:40 +0000 (04:05 +0800)
committerSteve Underwood <steveu@coppice.org>
Fri, 20 Jul 2012 20:05:40 +0000 (04:05 +0800)
FAX engine yet.

libs/spandsp/src/Makefile.am
libs/spandsp/src/spandsp.h.in
libs/spandsp/src/spandsp/expose.h
libs/spandsp/src/spandsp/private/t42.h [new file with mode: 0644]
libs/spandsp/src/spandsp/t42.h [new file with mode: 0644]
libs/spandsp/src/t42.c [new file with mode: 0644]
libs/spandsp/tests/Makefile.am
libs/spandsp/tests/t42_tests.c [new file with mode: 0644]

index 039c8a6c9ce258ab3fa1010b953c2a7798742a9c..685e3d932801858fb40e18c0b226a2ac0e8465ea 100644 (file)
@@ -143,10 +143,6 @@ libspandsp_la_SOURCES = ademco_contactid.c \
                         super_tone_rx.c \
                         super_tone_tx.c \
                         swept_tone.c \
-                        t4_t6_decode.c \
-                        t4_t6_encode.c \
-                        t4_rx.c \
-                        t4_tx.c \
                         t30.c \
                         t30_api.c \
                         t30_logging.c \
@@ -156,6 +152,11 @@ libspandsp_la_SOURCES = ademco_contactid.c \
                         t38_gateway.c \
                         t38_non_ecm_buffer.c \
                         t38_terminal.c \
+                        t4_t6_decode.c \
+                        t4_t6_encode.c \
+                        t4_rx.c \
+                        t4_tx.c \
+                        t42.c \
                         t81_t82_arith_coding.c \
                         t85_decode.c \
                         t85_encode.c \
@@ -246,6 +247,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
                          spandsp/t4_tx.h \
                          spandsp/t4_t6_decode.h \
                          spandsp/t4_t6_encode.h \
+                         spandsp/t42.h \
                          spandsp/t81_t82_arith_coding.h \
                          spandsp/t85.h \
                          spandsp/telephony.h \
@@ -312,6 +314,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
                          spandsp/private/t4_tx.h \
                          spandsp/private/t4_t6_decode.h \
                          spandsp/private/t4_t6_encode.h \
+                         spandsp/private/t42.h \
                          spandsp/private/t81_t82_arith_coding.h \
                          spandsp/private/t85.h \
                          spandsp/private/time_scale.h \
index 0cb5fd3a73c8e28ef08b69257aff2b599eb7dcae..0b1f7aec9bea0bf7bc0f07ce49d179480d835484 100644 (file)
 #include <spandsp/t4_t6_encode.h>
 #include <spandsp/t81_t82_arith_coding.h>
 #include <spandsp/t85.h>
-/*#include <spandsp/t42.h>*/
+#include <spandsp/t42.h>
 /*#include <spandsp/t43.h>*/
 #include <spandsp/t30.h>
 #include <spandsp/t30_api.h>
index f4696d2688bcc55d2f16231ba9325fa8996f403a..2f90c7f2d4f77b8b58db100b4bc74db89b4dac9f 100644 (file)
@@ -80,7 +80,7 @@
 #include <spandsp/private/t4_t6_encode.h>
 #include <spandsp/private/t81_t82_arith_coding.h>
 #include <spandsp/private/t85.h>
-/*#include <spandsp/private/t42.h>*/
+#include <spandsp/private/t42.h>
 /*#include <spandsp/private/t43.h>*/
 #include <spandsp/private/t4_rx.h>
 #include <spandsp/private/t4_tx.h>
diff --git a/libs/spandsp/src/spandsp/private/t42.h b/libs/spandsp/src/spandsp/private/t42.h
new file mode 100644 (file)
index 0000000..7dedfa7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/t42.h - ITU T.42 JPEG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2011 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_T42_H_)
+#define _SPANDSP_PRIVATE_T42_H_
+
+struct lab_params_s
+{
+    /* Lab gamut */
+    float range_L;
+    float range_a;
+    float range_b;
+    float offset_L;
+    float offset_a;
+    float offset_b;
+    int ab_are_signed;
+
+    /* Illuminant */
+    float x_n;
+    float y_n;
+    float z_n;
+};
+
+/* State of a working instance of the T.42 JPEG FAX encoder */
+struct t42_encode_state_s
+{
+    /*! \brief Callback function to read a row of pixels from the image source. */
+    t4_row_read_handler_t row_read_handler;
+    /*! \brief Opaque pointer passed to row_read_handler. */
+    void *row_read_user_data;
+
+    lab_params_t lab_params;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+/* State of a working instance of the T.42 JPEG FAX decoder */
+struct t42_decode_state_s
+{
+    /*! A callback routine to handle decoded pixel rows */
+    t4_row_write_handler_t row_write_handler;
+    /*! An opaque pointer passed to row_write_handler() */
+    void *row_write_user_data;
+    /*! A callback routine to handle decoded comments */
+    t4_row_write_handler_t comment_handler;
+    /*! An opaque pointer passed to comment_handler() */
+    void *comment_user_data;
+
+    lab_params_t lab_params;
+
+    /*! The contents for a COMMENT marker segment, to be added to the
+        image at the next opportunity. This is set to NULL when nothing is
+        pending. */
+    uint8_t *comment;
+    /*! Length of data pointed to by comment */
+    size_t comment_len;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/t42.h b/libs/spandsp/src/spandsp/t42.h
new file mode 100644 (file)
index 0000000..d6785cb
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t42.h - ITU T.42 JPEG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2011 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_T42_H_)
+#define _SPANDSP_T42_H_
+
+/*! \page t42_page T.42 (JPEG for FAX) image compression and decompression
+
+\section t42_page_sec_1 What does it do?
+
+\section t42_page_sec_1 How does it work?
+*/
+
+/*! State of a working instance of the T.42 encoder */
+typedef struct t42_encode_state_s t42_encode_state_t;
+
+/*! State of a working instance of the T.42 decoder */
+typedef struct t42_decode_state_s t42_decode_state_t;
+
+typedef struct lab_params_s lab_params_t;
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels);
+
+SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t lab[], int pixels);
+    
+SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn);
+
+SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed);
+
+SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q);
+    
+SPAN_DECLARE(void) set_illuminant_from_code(lab_params_t *s, const uint8_t code[4]);
+
+SPAN_DECLARE(void) set_gamut_from_code(lab_params_t *s, const uint8_t code[12]);
+
+SPAN_DECLARE(int) t42_itulab_to_itulab(tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes);
+
+SPAN_DECLARE(int) t42_itulab_to_jpeg(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes);
+
+SPAN_DECLARE(int) t42_jpeg_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes);
+
+SPAN_DECLARE(int) t42_srgb_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes);
+
+SPAN_DECLARE(int) t42_itulab_to_srgb(lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height, char *emsg, size_t max_emsg_bytes);
+
+SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
+                                          uint32_t l0,
+                                          int mx,
+                                          int options);
+
+SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width);
+
+SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length);
+
+SPAN_DECLARE(void) t42_encode_abort(t42_encode_state_t *s);
+
+SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comment[], size_t len);
+
+SPAN_DECLARE(int) t42_encode_get_byte(t42_encode_state_t *s);
+
+SPAN_DECLARE(int) t42_encode_get_chunk(t42_encode_state_t *s, uint8_t buf[], int max_len);
+
+SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s);
+
+SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s);
+
+SPAN_DECLARE(int) t42_encode_get_compressed_image_size(t42_encode_state_t *s);
+
+SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
+                                                  t4_row_read_handler_t handler,
+                                                  void *user_data);
+
+SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length);
+
+SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s,
+                                                   uint32_t image_width,
+                                                   uint32_t image_length,
+                                                   t4_row_read_handler_t handler,
+                                                   void *user_data);
+
+SPAN_DECLARE(int) t42_encode_release(t42_encode_state_t *s);
+
+SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s);
+
+SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status);
+
+SPAN_DECLARE(int) t42_decode_put_byte(t42_decode_state_t *s, int byte);
+
+SPAN_DECLARE(int) t42_decode_put_chunk(t42_decode_state_t *s,
+                                       const uint8_t data[],
+                                       size_t len);
+
+SPAN_DECLARE(int) t42_decode_set_row_write_handler(t42_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data);
+
+SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s,
+                                                 uint32_t max_comment_len,
+                                                 t4_row_write_handler_t handler,
+                                                 void *user_data);
+
+SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
+                                                        uint32_t max_xd,
+                                                        uint32_t max_yd);
+
+SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s);
+
+SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s);
+
+SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s);
+
+SPAN_DECLARE(int) t42_decode_new_plane(t42_decode_state_t *s);
+
+SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s);
+
+SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data);
+
+SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s);
+
+SPAN_DECLARE(int) t42_decode_free(t42_decode_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/t42.c b/libs/spandsp/src/t42.c
new file mode 100644 (file)
index 0000000..608341f
--- /dev/null
@@ -0,0 +1,1199 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t42.c - ITU T.42 JPEG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2011 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tiffio.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include <time.h>
+#include "floating_fudge.h"
+#include <jpeglib.h>
+#include <setjmp.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/async.h"
+#include "spandsp/timezone.h"
+#include "spandsp/t4_rx.h"
+#include "spandsp/t4_tx.h"
+#include "spandsp/t81_t82_arith_coding.h"
+#include "spandsp/t85.h"
+#include "spandsp/t42.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/t81_t82_arith_coding.h"
+#include "spandsp/private/t85.h"
+#include "spandsp/private/t42.h"
+
+#define T42_USE_LUTS
+
+#include "cielab_luts.h"
+
+typedef struct
+{
+    float L;
+    float a;
+    float b;
+} cielab_t;
+
+typedef struct
+{
+    uint8_t tag[5];
+    const char *name;
+    float xn;
+    float yn;
+    float zn;
+} illuminant_t;
+
+typedef struct
+{
+    jmp_buf escape;
+    char error_message[JMSG_LENGTH_MAX];
+} escape_route_t;
+
+static const illuminant_t illuminants[] =
+{
+    {"\0D50",  "CIE D50/2°",   96.422f, 100.000f,  82.521f},
+    {"",       "CIE D50/10°",  96.720f, 100.000f,  81.427f},
+    {"",       "CIE D55/2°",   95.682f, 100.000f,  92.149f},
+    {"",       "CIE D55/10°",  95.799f, 100.000f,  90.926f},
+    {"\0D65",  "CIE D65/2°",   95.047f, 100.000f, 108.883f},
+    {"",       "CIE D65/10°",  94.811f, 100.000f, 107.304f},
+    {"\0D75",  "CIE D75/2°",   94.972f, 100.000f, 122.638f},
+    {"",       "CIE D75/10°",  94.416f, 100.000f, 120.641f},
+    {"\0\0F2", "F02/2°",       99.186f, 100.000f,  67.393f},
+    {"",       "F02/10°",     103.279f, 100.000f,  69.027f},
+    {"\0\0F7", "F07/2°",       95.041f, 100.000f, 108.747f},
+    {"",       "F07/10°",      95.792f, 100.000f, 107.686f},
+    {"\0F11",  "F11/2°",      100.962f, 100.000f,  64.350f},
+    {"",       "F11/10°",     103.863f, 100.000f,  65.607f},
+    {"\0\0SA", "A/2°",        109.850f, 100.000f,  35.585f},
+    {"",       "A/10°",       111.144f, 100.000f,  35.200f},
+    {"\0\0SC", "C/2°",         98.074f, 100.000f, 118.232f},
+    {"",       "C/10°",        97.285f, 100.000f, 116.145f},
+    {"",       "",              0.000f,   0.000f,   0.000f}
+};
+
+/* This is the error catcher */
+static struct jpeg_error_mgr error_handler;
+
+static __inline__ uint16_t pack_16(const uint8_t *s)
+{
+    uint16_t value;
+
+    value = ((uint16_t) s[0] << 8) | (uint16_t) s[1];
+    return value;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ uint32_t pack_32(const uint8_t *s)
+{
+    uint32_t value;
+
+    value = ((uint32_t) s[0] << 24) | ((uint32_t) s[1] << 16) | ((uint32_t) s[2] << 8) | (uint32_t) s[3];
+    return value;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int unpack_16(uint8_t *s, uint16_t value)
+{
+    s[0] = (value >> 8) & 0xFF;
+    s[1] = value & 0xFF;
+    return sizeof(uint16_t);
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Error handler for IJG library */
+static void jpg_error_exit(j_common_ptr cinfo)
+{
+    escape_route_t *escape;
+
+    escape = (escape_route_t *) cinfo->client_data;
+    (*cinfo->err->format_message)(cinfo, escape->error_message);
+    longjmp(escape->escape, 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn)
+{
+    if (new_yn > 10.0f)
+    {
+        s->x_n = new_xn/100.0f;
+        s->y_n = new_yn/100.0f;
+        s->z_n = new_zn/100.0f;
+    }
+    else
+    {
+        s->x_n = new_xn;
+        s->y_n = new_yn;
+        s->z_n = new_zn;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed)
+{
+    s->range_L = L_max - L_min;
+    s->range_a = a_max - a_min;
+    s->range_b = b_max - b_min;
+
+    s->offset_L = -256.0f*L_min/s->range_L;
+    s->offset_a = -256.0f*a_min/s->range_a;
+    s->offset_b = -256.0f*b_min/s->range_b;
+
+    s->range_L /= (256.0f - 1.0f);
+    s->range_a /= (256.0f - 1.0f);
+    s->range_b /= (256.0f - 1.0f);
+
+    s->ab_are_signed = ab_are_signed;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q)
+{
+    s->range_L = L_Q/(256.0f - 1.0f);
+    s->range_a = a_Q/(256.0f - 1.0f);
+    s->range_b = b_Q/(256.0f - 1.0f);
+
+    s->offset_L = L_P;
+    s->offset_a = a_P;
+    s->offset_b = b_P;
+
+    s->ab_are_signed = FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_illuminant_from_code(lab_params_t *s, const uint8_t code[4])
+{
+    int i;
+    int colour_temp;
+
+    if (code[0] == 'C'  &&  code[1] == 'T')
+    {
+        colour_temp = pack_16(&code[2]);
+        printf("Illuminant colour temp %dK\n", colour_temp);
+        return;
+    }
+    for (i = 0;  illuminants[i].name[0];  i++)
+    {
+        if (memcmp(code, illuminants[i].tag, 4) == 0)
+        {
+            printf("Illuminant %s\n", illuminants[i].name);
+            set_lab_illuminant(s, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn);
+            break;
+        }
+    }
+    if (illuminants[i].name[0] == '\0')
+        printf("Unrecognised illuminant 0x%x 0x%x 0x%x 0x%x\n", code[0], code[1], code[2], code[3]);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) set_gamut_from_code(lab_params_t *s, const uint8_t code[12])
+{
+    int i;
+    int val[6];
+
+    for (i = 0;  i < 6;  i++)
+        val[i] = pack_16(&code[2*i]);
+    printf("Gamut L=[%d,%d], a*=[%d,%d], b*=[%d,%d]\n",
+           val[0],
+           val[1],
+           val[2],
+           val[3],
+           val[4],
+           val[5]);
+    set_lab_gamut2(s, val[0], val[1], val[2], val[3], val[4], val[5]);
+}
+/*- End of function --------------------------------------------------------*/
+
+static int isITUfax(lab_params_t *s, jpeg_saved_marker_ptr ptr)
+{
+    const uint8_t *data;
+    int ok;
+    int val[2];
+    int i;
+
+    ok = FALSE;
+    while (ptr)
+    {
+        if (ptr->marker == (JPEG_APP0 + 1)  &&  ptr->data_length >= 6)
+        {
+            data = (const uint8_t *) ptr->data;
+            if (strncmp((const char *) data, "G3FAX", 5) == 0)
+            {
+                switch (data[5])
+                {
+                case 0:
+                    for (i = 0;  i < 2;  i++)
+                        val[i] = pack_16(&data[6 + 2*i]);
+                    printf("Version %d, resolution %d dpi\n", val[0], val[1]);
+                    ok = TRUE;
+                    break;
+                case 1:
+                    printf("Set gamut\n");
+                    set_gamut_from_code(s, &data[6]);
+                    ok = TRUE;
+                    break;
+                case 2:
+                    printf("Set illuminant\n");
+                    set_illuminant_from_code(s, &data[6]);
+                    ok = TRUE;
+                    break;
+                case 3:
+                    /* Colour palette table */
+                    printf("Set colour palette\n");
+                    val[0] = pack_16(&data[6]);
+                    printf("Colour palette %d\n", val[0]);
+                    break;
+                }
+            }
+        }
+
+        ptr = ptr->next;
+    }
+
+    return ok;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void SetITUFax(j_compress_ptr cinfo)
+{
+    uint8_t marker[10] =
+    {
+        'G', '3', 'F', 'A', 'X', '\x00', '\x07', '\xCA', '\x00', '\x00'
+    };
+
+    unpack_16(marker + 8, 200);
+
+    jpeg_write_marker(cinfo, (JPEG_APP0 + 1), marker, 10);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void itu_to_lab(lab_params_t *s, cielab_t *lab, const uint8_t in[3])
+{
+    uint8_t a;
+    uint8_t b;
+
+    /* T.4 E.6.4 */
+    lab->L = s->range_L*(in[0] - s->offset_L);
+    a = in[1];
+    b = in[2];
+    if (s->ab_are_signed)
+    {
+        a += 128;
+        b += 128;
+    }
+    lab->a = s->range_a*(a - s->offset_a);
+    lab->b = s->range_b*(b - s->offset_b);
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void lab_to_itu(lab_params_t *s, uint8_t out[3], const cielab_t *lab)
+{
+    float val;
+
+    /* T.4 E.6.4 */
+    val = floorf(lab->L/s->range_L + s->offset_L);
+    out[0] = (uint8_t) (val < 0.0)  ?  0  :  (val < 256.0)  ?  val  :  255;
+    val = floorf(lab->a/s->range_a + s->offset_a);
+    out[1] = (uint8_t) (val < 0.0)  ?  0  :  (val < 256.0)  ?  val  :  255;
+    val = floorf(lab->b/s->range_b + s->offset_b);
+    out[2] = (uint8_t) (val < 0.0)  ?  0  :  (val < 256.0)  ?  val  :  255;
+    if (s->ab_are_signed)
+    {
+        out[1] -= 128;
+        out[2] -= 128;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels)
+{
+    float x;
+    float y;
+    float z;
+    float r;
+    float g;
+    float b;
+    float xx;
+    float yy;
+    float zz;
+    cielab_t l;
+    int i;
+
+    for (i = 0;  i < pixels;  i++)
+    {
+#if defined(T42_USE_LUTS)
+        r = sRGB_to_linear[srgb[0]];
+        g = sRGB_to_linear[srgb[1]];
+        b = sRGB_to_linear[srgb[2]];
+#else
+        r = srgb[0]/256.0f;
+        g = srgb[1]/256.0f;
+        b = srgb[2]/256.0f;
+
+        /* sRGB to linear RGB */
+        r = (r > 0.04045f)  ?  powf((r + 0.055f)/1.055f, 2.4f)  :  r/12.92f;
+        g = (g > 0.04045f)  ?  powf((g + 0.055f)/1.055f, 2.4f)  :  g/12.92f;
+        b = (b > 0.04045f)  ?  powf((b + 0.055f)/1.055f, 2.4f)  :  b/12.92f;
+#endif
+
+        /* Linear RGB to XYZ */
+        x = 0.4124f*r + 0.3576f*g + 0.1805f*b;
+        y = 0.2126f*r + 0.7152f*g + 0.0722f*b;
+        z = 0.0193f*r + 0.1192f*g + 0.9505f*b;
+
+        /* Normalise for the illuminant */
+        x /= s->x_n;
+        y /= s->y_n;
+        z /= s->z_n;
+    
+        /* XYZ to Lab */
+        xx = (x <= 0.008856f)  ?  (7.787f*x + 0.1379f)  :  cbrtf(x);
+        yy = (y <= 0.008856f)  ?  (7.787f*y + 0.1379f)  :  cbrtf(y);
+        zz = (z <= 0.008856f)  ?  (7.787f*z + 0.1379f)  :  cbrtf(z);
+        l.L = 116.0f*yy - 16.0f;
+        l.a = 500.0f*(xx - yy);
+        l.b = 200.0f*(yy - zz);
+
+        lab_to_itu(s, lab, &l);
+
+        srgb += 3;
+        lab += 3;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t lab[], int pixels)
+{
+    float x;
+    float y;
+    float z;
+    float r;
+    float g;
+    float b;
+    float ll;
+    cielab_t l;
+    int val;
+    int i;
+
+    for (i = 0;  i < pixels;  i++)
+    {
+        itu_to_lab(s, &l, lab);
+
+        /* Lab to XYZ */
+        ll = (1.0f/116.0f)*(l.L + 16.0f); 
+        y = ll;
+        y = (y <= 0.2068f)  ?  (0.1284f*(y - 0.1379f))  :  y*y*y;
+        x = ll + (1.0f/500.0f)*l.a;
+        x = (x <= 0.2068f)  ?  (0.1284f*(x - 0.1379f))  :  x*x*x;
+        z = ll - (1.0f/200.0f)*l.b;
+        z = (z <= 0.2068f)  ?  (0.1284f*(z - 0.1379f))  :  z*z*z;
+
+        /* Normalise for the illuminant */
+        x *= s->x_n;
+        y *= s->y_n;
+        z *= s->z_n;
+
+        /* XYZ to linear RGB */
+        r =  3.2406f*x - 1.5372f*y - 0.4986f*z;
+        g = -0.9689f*x + 1.8758f*y + 0.0415f*z;
+        b =  0.0557f*x - 0.2040f*y + 1.0570f*z;
+
+#if defined(T42_USE_LUTS)
+        val = r*4096.0f;
+        srgb[0] = linear_to_sRGB[(val < 0)  ?  0  :  (val < 4095)  ?  val  :  4095];
+        val = g*4096.0f;
+        srgb[1] = linear_to_sRGB[(val < 0)  ?  0  :  (val < 4095)  ?  val  :  4095];
+        val = b*4096.0f;
+        srgb[2] = linear_to_sRGB[(val < 0)  ?  0  :  (val < 4095)  ?  val  :  4095];
+#else
+        /* Linear RGB to sRGB */
+        r = (r > 0.0031308f)  ?  (1.055f*powf(r, 1.0f/2.4f) - 0.055f)  :  r*12.92f;
+        g = (g > 0.0031308f)  ?  (1.055f*powf(g, 1.0f/2.4f) - 0.055f)  :  g*12.92f;
+        b = (b > 0.0031308f)  ?  (1.055f*powf(b, 1.0f/2.4f) - 0.055f)  :  b*12.92f;
+
+        r = floorf(r*256.0f);
+        g = floorf(g*256.0f);
+        b = floorf(b*256.0f);
+
+        srgb[0] = (r < 0)  ?  0  :  (r <= 255)  ?  r  :  255;
+        srgb[1] = (g < 0)  ?  0  :  (g <= 255)  ?  g  :  255;
+        srgb[2] = (b < 0)  ?  0  :  (b <= 255)  ?  b  :  255;
+#endif
+        srgb += 3;
+        lab += 3;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_itulab_to_jpeg(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes)
+{
+    struct jpeg_decompress_struct decompressor;
+    struct jpeg_compress_struct compressor;
+    char *outptr;
+    size_t outsize;
+    FILE *in;
+    FILE *out;
+    int m;
+    JSAMPROW scan_line_in;
+    JSAMPROW scan_line_out;
+    escape_route_t escape;
+
+    escape.error_message[0] = '\0';
+    emsg[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    in = fmemopen(src, srclen, "r");
+#else
+    in = tmpfile();
+    fwrite(src, 1, srclen, in);
+    rewind(in);
+#endif
+    if (in == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to fmemopen().");
+        return FALSE;
+    }
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    out = open_memstream(&outptr, &outsize);
+#else
+    out = tmpfile();
+#endif
+    if (out == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to open_memstream().");
+        return FALSE;
+    }
+
+    if (setjmp(escape.escape))
+    {
+        strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
+        emsg[max_emsg_bytes - 1] = '\0';
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Unspecified libjpeg error.");
+        return FALSE;
+    }
+
+    /* Create input decompressor. */
+    decompressor.err = jpeg_std_error(&error_handler);
+    decompressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_decompress(&decompressor);
+    jpeg_stdio_src(&decompressor, in);
+
+    /* Needed in the case of ITU Lab input */
+    for (m = 0;  m < 16;  m++)
+        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
+
+    /* Rewind the file */
+    if (fseek(in, 0, SEEK_SET) != 0)
+        return FALSE;
+
+    /* Take the header */
+    jpeg_read_header(&decompressor, TRUE);
+
+    /* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
+    decompressor.out_color_space = JCS_YCbCr;
+
+    /* Sanity check and parameter check */
+    if (!isITUfax(s, decompressor.marker_list))
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Is not ITUFAX.");
+        return FALSE;
+    }
+
+    /* Create compressor */
+    compressor.err = jpeg_std_error(&error_handler);
+    compressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_compress(&compressor);
+    jpeg_stdio_dest(&compressor, out);
+
+    /* Force the destination color space */
+    compressor.in_color_space = JCS_RGB;
+    compressor.input_components = 3;
+
+    jpeg_set_defaults(&compressor);
+    //jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
+
+    /* Copy size, resolution, etc */
+    jpeg_copy_critical_parameters(&decompressor, &compressor);
+
+    /* We need to keep these */
+    compressor.density_unit = decompressor.density_unit;
+    compressor.X_density = decompressor.X_density;
+    compressor.Y_density = decompressor.Y_density;
+
+    jpeg_start_decompress(&decompressor);
+    jpeg_start_compress(&compressor, TRUE);
+
+    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
+        return FALSE;
+
+    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
+    {
+        free(scan_line_in);
+        return FALSE;
+    }
+
+    while (decompressor.output_scanline < decompressor.output_height)
+    {
+        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
+        lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
+        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
+    }
+    free(scan_line_in);
+    free(scan_line_out);
+    jpeg_finish_decompress(&decompressor);
+    jpeg_finish_compress(&compressor);
+    jpeg_destroy_decompress(&decompressor);
+    jpeg_destroy_compress(&compressor);
+    fclose(in);
+    fclose(out);
+
+    *dst = outptr;
+    *dstlen = outsize;
+
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_jpeg_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes)
+{
+    struct jpeg_decompress_struct decompressor;
+    struct jpeg_compress_struct compressor;
+    char *outptr;
+    size_t outsize;
+    FILE *in;
+    FILE *out;
+    int m;
+    JSAMPROW scan_line_in;
+    JSAMPROW scan_line_out;
+    escape_route_t escape;
+
+    escape.error_message[0] = '\0';
+    emsg[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    in = fmemopen(src, srclen, "r");
+#else
+    in = tmpfile();
+    fwrite(src, 1, srclen, in);
+    rewind(in);
+#endif
+    if (in == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to fmemopen().");
+        return FALSE;
+    }
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    out = open_memstream(&outptr, &outsize);
+#else
+    out = tmpfile();
+#endif
+    if (out == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to open_memstream().");
+        return FALSE;
+    }
+
+    if (setjmp(escape.escape))
+    {
+        strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
+        emsg[max_emsg_bytes - 1] = '\0';
+        return FALSE;
+    }
+
+    decompressor.err = jpeg_std_error(&error_handler);
+    decompressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_decompress(&decompressor);
+    jpeg_stdio_src(&decompressor, in);
+
+    /* Needed in the case of ITU Lab input */
+    for (m = 0;  m < 16;  m++)
+        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
+
+    /* Rewind the file */
+    if (fseek(in, 0, SEEK_SET) != 0)
+        return FALSE;
+
+    /* Take the header */
+    jpeg_read_header(&decompressor, TRUE);
+
+    /* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
+    decompressor.out_color_space = JCS_RGB;
+
+    compressor.err = jpeg_std_error(&error_handler);
+    compressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_compress(&compressor);
+    jpeg_stdio_dest(&compressor, out);
+
+    /* Force the destination color space */
+    compressor.in_color_space = JCS_YCbCr;
+    compressor.input_components = 3;
+
+    jpeg_set_defaults(&compressor);
+    //jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
+
+    jpeg_copy_critical_parameters(&decompressor, &compressor);
+
+    /* We need to keep these */
+    compressor.density_unit = decompressor.density_unit;
+    compressor.X_density = decompressor.X_density;
+    compressor.Y_density = decompressor.Y_density;
+
+    jpeg_start_decompress(&decompressor);
+    jpeg_start_compress(&compressor, TRUE);
+
+    SetITUFax(&compressor);
+
+    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
+        return FALSE;
+
+    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
+    {
+        free(scan_line_in);
+        return FALSE;
+    }
+
+    while (decompressor.output_scanline < decompressor.output_height)
+    {
+        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
+        srgb_to_lab(s, scan_line_out, scan_line_in, decompressor.output_width);
+        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
+    }
+
+    free(scan_line_in);
+    free(scan_line_out);
+    jpeg_finish_decompress(&decompressor);
+    jpeg_finish_compress(&compressor);
+    jpeg_destroy_decompress(&decompressor);
+    jpeg_destroy_compress(&compressor);
+    fclose(in);
+    fclose(out);
+
+    *dst = outptr;
+    *dstlen = outsize;
+
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_srgb_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes)
+{
+    struct jpeg_compress_struct compressor;
+    FILE *out;
+    char *outptr;
+    size_t outsize;
+    JSAMPROW scan_line_out;
+    JSAMPROW scan_line_in;
+    tsize_t pos;
+    escape_route_t escape;
+
+    escape.error_message[0] = '\0';
+    emsg[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    out = open_memstream(&outptr, &outsize);
+#else
+    out = tmpfile();
+#endif
+    if (out == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to open_memstream().");
+        return FALSE;
+    }
+
+    if (setjmp(escape.escape))
+    {
+        strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
+        emsg[max_emsg_bytes - 1] = '\0';
+        return FALSE;
+    }
+
+    compressor.err = jpeg_std_error(&error_handler);
+    compressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_compress(&compressor);
+    jpeg_stdio_dest(&compressor, out);
+
+    /* Force the destination color space */
+    compressor.in_color_space = JCS_YCbCr;
+    compressor.input_components = 3;
+
+    jpeg_set_defaults(&compressor);
+    //jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
+
+    /* Size, resolution, etc */
+    compressor.image_width = width;
+    compressor.image_height = height;
+
+    jpeg_start_compress(&compressor, TRUE);
+
+    SetITUFax(&compressor);
+
+    if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
+        return FALSE;
+
+    for (pos = 0;  pos < srclen;  pos += compressor.image_width*compressor.num_components)
+    {
+        scan_line_in = src + pos;
+        srgb_to_lab(s, scan_line_out, scan_line_in, compressor.image_width);
+        jpeg_write_scanlines(&compressor, &scan_line_out, 1);
+    }
+
+    free(scan_line_out);
+    jpeg_finish_compress(&compressor);
+    jpeg_destroy_compress(&compressor);
+    fclose(out);
+
+    *dst = outptr;
+    *dstlen = outsize;
+
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_itulab_to_itulab(tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes)
+{
+    struct jpeg_compress_struct compressor;
+    FILE *out;
+    char *outptr;
+    size_t outsize;
+    JSAMPROW scan_line_in;
+    tsize_t pos;
+    escape_route_t escape;
+
+    escape.error_message[0] = '\0';
+    emsg[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    out = open_memstream(&outptr, &outsize);
+#else
+    out = tmpfile();
+#endif
+    if (out == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to open_memstream().");
+        return FALSE;
+    }
+
+    if (setjmp(escape.escape))
+    {
+        strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
+        emsg[max_emsg_bytes - 1] = '\0';
+        return FALSE;
+    }
+
+    compressor.err = jpeg_std_error(&error_handler);
+    compressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_compress(&compressor);
+    jpeg_stdio_dest(&compressor, out);
+
+    /* Force the destination color space */
+    compressor.in_color_space = JCS_YCbCr;
+    compressor.input_components = 3;
+
+    jpeg_set_defaults(&compressor);
+    //jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
+
+    /* Size, resolution, etc */
+    compressor.image_width = width;
+    compressor.image_height = height;
+
+    jpeg_start_compress(&compressor, TRUE);
+
+    SetITUFax(&compressor);
+
+    for (pos = 0;  pos < srclen;  pos += compressor.image_width*compressor.num_components)
+    {
+        scan_line_in = src + pos;
+        jpeg_write_scanlines(&compressor, &scan_line_in, 1);
+    }
+
+    jpeg_finish_compress(&compressor);
+    jpeg_destroy_compress(&compressor);
+    fclose(out);
+
+    *dst = outptr;
+    *dstlen = outsize;
+
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_itulab_to_srgb(lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height, char *emsg, size_t max_emsg_bytes)
+{
+    struct jpeg_decompress_struct decompressor;
+    JSAMPROW scan_line_out;
+    JSAMPROW scan_line_in;
+    tsize_t pos;
+    FILE *in;
+    int m;
+    escape_route_t escape;
+
+    escape.error_message[0] = '\0';
+    emsg[0] = '\0';
+
+#if defined(HAVE_OPEN_MEMSTREAM)
+    in = fmemopen(src, srclen, "r");
+#else
+    in = tmpfile();
+    fwrite(src, 1, srclen, in);
+    rewind(in);
+#endif
+    if (in == NULL)
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Failed to fmemopen().");
+        return FALSE;
+    }
+
+    if (setjmp(escape.escape))
+    {
+        strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
+        emsg[max_emsg_bytes - 1] = '\0';
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Unspecified libjpeg error.");
+        return FALSE;
+    }
+    /* Create input decompressor. */
+    decompressor.err = jpeg_std_error(&error_handler);
+    decompressor.client_data = (void *) &escape;
+    error_handler.error_exit = jpg_error_exit;
+    error_handler.output_message = jpg_error_exit;
+
+    jpeg_create_decompress(&decompressor);
+    jpeg_stdio_src(&decompressor, in);
+
+    /* Needed in the case of ITU Lab input */
+    for (m = 0;  m < 16;  m++)
+        jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
+
+    /* Rewind the file */
+    if (fseek(in, 0, SEEK_SET) != 0)
+        return FALSE;
+printf("XXXX 10\n");
+    /* Take the header */
+    jpeg_read_header(&decompressor, FALSE);
+printf("XXXX 11\n");
+    /* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
+    decompressor.out_color_space = JCS_YCbCr;
+printf("XXXX 12\n");
+    /* Sanity check and parameter check */
+    if (!isITUfax(s, decompressor.marker_list))
+    {
+        if (emsg[0] == '\0')
+            strcpy(emsg, "Is not ITUFAX.");
+        //return FALSE;
+    }
+printf("XXXX 13\n");
+    /* Copy size, resolution, etc */
+    *width = decompressor.image_width;
+    *height = decompressor.image_height;
+
+    jpeg_start_decompress(&decompressor);
+
+    if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
+        return FALSE;
+
+    for (pos = 0;  decompressor.output_scanline < decompressor.output_height;  pos += decompressor.output_width*decompressor.num_components)
+    {
+        scan_line_out = dst + pos;
+        jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
+        lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
+    }
+
+    free(scan_line_in);
+    jpeg_finish_decompress(&decompressor);
+    jpeg_destroy_decompress(&decompressor);
+    fclose(in);
+
+    *dstlen = pos;
+
+    return TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
+                                          uint32_t l0,
+                                          int mx,
+                                          int options)
+{
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t42_encode_abort(t42_encode_state_t *s)
+{
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comment[], size_t len)
+{
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_get_byte(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_get_chunk(t42_encode_state_t *s, uint8_t buf[], int max_len)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_get_compressed_image_size(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
+                                                  t4_row_read_handler_t handler,
+                                                  void *user_data)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s,
+                                                   uint32_t image_width,
+                                                   uint32_t image_length,
+                                                   t4_row_read_handler_t handler,
+                                                   void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (t42_encode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.42");
+
+    s->row_read_handler = handler;
+    s->row_read_user_data = user_data;
+
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_release(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status)
+{
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_put_byte(t42_decode_state_t *s, int byte)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_put_chunk(t42_decode_state_t *s,
+                                       const uint8_t data[],
+                                       size_t len)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_set_row_write_handler(t42_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s,
+                                                 uint32_t max_comment_len,
+                                                 t4_row_write_handler_t handler,
+                                                 void *user_data)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
+                                                        uint32_t max_xd,
+                                                        uint32_t max_yd)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_new_plane(t42_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s,
+                                                   t4_row_write_handler_t handler,
+                                                   void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (t42_decode_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "T.42");
+
+    s->row_write_handler = handler;
+    s->row_write_user_data = user_data;
+
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s)
+{
+    if (s->comment)
+    {
+        free(s->comment);
+        s->comment = NULL;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) t42_decode_free(t42_decode_state_t *s)
+{
+    int ret;
+
+    ret = t42_decode_release(s);
+    free(s);
+    return ret;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
index 2b5e97afb1f407b99c08b5e262155defbf3ef9b5..8a0503fe6274500b5f7231a228f96fb963707d24 100644 (file)
@@ -110,6 +110,7 @@ noinst_PROGRAMS =   ademco_contactid_tests \
                     t38_non_ecm_buffer_tests \
                     t4_tests \
                     t4_t6_tests \
+                    t42_tests \
                     t81_t82_arith_coding_tests \
                     t85_tests \
                     time_scale_tests \
@@ -317,6 +318,9 @@ t4_tests_LDADD = $(LIBDIR) -lspandsp
 t4_t6_tests_SOURCES = t4_t6_tests.c
 t4_t6_tests_LDADD = $(LIBDIR) -lspandsp
 
+t42_tests_SOURCES = t42_tests.c
+t42_tests_LDADD = $(LIBDIR) -lspandsp
+
 t81_t82_arith_coding_tests_SOURCES = t81_t82_arith_coding_tests.c
 t81_t82_arith_coding_tests_LDADD = $(LIBDIR) -lspandsp
 
diff --git a/libs/spandsp/tests/t42_tests.c b/libs/spandsp/tests/t42_tests.c
new file mode 100644 (file)
index 0000000..1ccefed
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * t42_tests.c - ITU T.42 JPEG for FAX image processing
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2011 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 t42_tests_page T.42 tests
+\section t42_tests_page_sec_1 What does it do
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <memory.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#define IN_FILE_NAME    "../test-data/itu/t24/F21_200.TIF"
+#define OUT_FILE_NAME   "t42_tests_receive.tif"
+
+uint8_t data5[50000000];
+int data5_ptr = 0;
+int plane = 0;
+int bit_mask;
+
+uint8_t xxx[3*256];
+
+lab_params_t lab_param;
+
+int write_row = 0;
+
+typedef struct
+{
+    float L;
+    float a;
+    float b;
+} cielab_t;
+
+#if 0
+static void generate_luts(void)
+{
+    float r;
+    uint8_t srgb;
+    int i;
+
+    printf("static const float srgb_to_linear[256] =\n");
+    printf("{\n");
+    for (i = 0;  i < 256;  i++)
+    {
+        /* Start with "i" as the sRGB value */
+        r = i/256.0f;
+
+        /* sRGB to Linear RGB */
+        r = (r > 0.04045f)  ?  powf((r + 0.055f)/1.055f, 2.4f)  :  r/12.92f;
+        
+        printf((i < 255)  ?  "    %f,\n"  :  "    %f\n", r);
+    }
+    printf("};\n");
+    
+    printf("static const uint8_t linear_to_srgb[4096] =\n");
+    printf("{\n");
+    for (i = 0;  i < 4096;  i++)
+    {
+        /* Start with "i" as the linear RGB value */
+        /* Linear RGB to sRGB */
+        r = i/4096.0f;
+
+        r = (r > 0.0031308f)  ?  (1.055f*powf(r, 1.0f/2.4f) - 0.055f)  :  r*12.92f;
+
+        r = floorf(r*256.0f);
+
+        srgb = (r < 0)  ?  0  :  (r <= 255)  ?  r  :  255;
+
+        printf((i < 4095)  ?  "    %d,\n"  :  "    %d\n", srgb);
+    }
+    printf("};\n");
+}
+/*- End of function --------------------------------------------------------*/
+#endif
+
+static __inline__ uint16_t pack_16(uint8_t *s)
+{
+    uint16_t value;
+
+    value = ((uint16_t) s[0] << 8) | (uint16_t) s[1];
+    return value;
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ uint32_t pack_32(uint8_t *s)
+{
+    uint32_t value;
+
+    value = ((uint32_t) s[0] << 24) | ((uint32_t) s[1] << 16) | ((uint32_t) s[2] << 8) | (uint32_t) s[3];
+    return value;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
+{
+    int i;
+    int j;
+
+    for (i = 0;  i < len;  i++)
+    {
+        for (j = 0;  j < 8;  j++)
+        {
+            if ((buf[i] & (0x80 >> j)))
+                data5[data5_ptr + 3*(8*i + j)] |= bit_mask;
+            else
+                data5[data5_ptr + 3*(8*i + j)] &= ~bit_mask;
+        }
+    }
+    data5_ptr += 3*8*len;
+    write_row++;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int t85_comment_handler(void *user_data, const uint8_t buf[], size_t len)
+{
+    if (buf)
+        printf("Comment (%lu): %s\n", (unsigned long int) len, buf);
+    else
+        printf("Comment (%lu): ---\n", (unsigned long int) len);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+    char kk[256];
+    TIFF *tif;
+    uint32_t w;
+    uint32_t h;
+    tstrip_t nstrips;
+    uint32_t totdata;
+    tsize_t off;
+    uint8_t *data;
+    uint8_t *data2;
+    int row;
+    uint16_t compression;
+    int16_t photometric;
+    int16_t YCbCrSubsampleHoriz;
+    int16_t YCbCrSubsampleVert;
+    int16_t bits_per_pixel;
+    int16_t samples_per_pixel;
+    int16_t planar_config;
+    int bytes_per_row;
+    tsize_t outsize;
+    char *outptr;
+    const char *source_file;
+    int i;
+    int j;
+    int len;
+    tsize_t total_image_len;
+    tsize_t total_len;
+    int process_raw;
+    int result;
+    t85_decode_state_t t85_dec;
+    uint64_t start;
+    uint64_t end;
+    uint16_t *yyyL;
+    uint16_t *yyya;
+    uint16_t *yyyb;
+    uint16_t *yyyz;
+
+    printf("Demo of ITU/Lab library.\n");
+
+    TIFF_FX_init();
+
+    set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
+    set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
+
+#if 0
+    generate_luts();
+#endif
+
+    source_file = (argc > 1)  ?  argv[1]  :  IN_FILE_NAME;
+    /* sRGB to ITU */
+    if ((tif = TIFFOpen(source_file, "r")) == NULL)
+    {
+        printf("Unable to open '%s'!\n", source_file);
+        return 1;
+    }
+    if (TIFFSetDirectory(tif, (tdir_t) 0) < 0)
+    {
+        printf("Unable to set directory '%s'!\n", source_file);
+        return 1;
+    }
+
+    w = 0;
+    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
+    h = 0;
+    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
+    bits_per_pixel = 0;
+    TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_pixel);
+    samples_per_pixel = 0;
+    TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+    compression = 0;
+    TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
+    photometric = 0;
+    TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+    YCbCrSubsampleHoriz = 0;
+    YCbCrSubsampleVert = 0;
+    TIFFGetField(tif, TIFFTAG_YCBCRSUBSAMPLING, &YCbCrSubsampleHoriz, &YCbCrSubsampleVert);
+    planar_config = 0;
+    TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+    off = 0;
+    yyyL = NULL;
+    yyya = NULL;
+    yyyb = NULL;
+    yyyz = NULL;
+    if (TIFFGetField(tif, TIFFTAG_COLORMAP, &yyyL, &yyya, &yyyb, &yyyz))
+    {
+#if 0
+        /* Sweep the colormap in the proper order */
+        for (i = 0;  i < (1 << bits_per_pixel);  i++)
+        {
+            xxx[3*i] = (yyyL[i] >> 8) & 0xFF;
+            xxx[3*i + 1] = (yyya[i] >> 8) & 0xFF;
+            xxx[3*i + 2] = (yyyb[i] >> 8) & 0xFF;
+            printf("Map %3d - %5d %5d %5d\n", i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
+        }
+#else
+        /* Sweep the colormap in the order that seems to work for l04x_02x.tif */
+        for (i = 0;  i < (1 << bits_per_pixel);  i++)
+        {
+            xxx[i] = (yyyL[i] >> 8) & 0xFF;
+            xxx[256 + i] = (yyya[i] >> 8) & 0xFF;
+            xxx[2*256 + i] = (yyyb[i] >> 8) & 0xFF;
+        }
+#endif
+        lab_params_t lab;
+
+        set_lab_illuminant(&lab, 0.9638f, 1.0f, 0.8245f);
+        set_lab_gamut(&lab, 0, 100, -85, 85, -75, 125, FALSE);
+        lab_to_srgb(&lab, xxx, xxx, 256);
+        for (i = 0;  i < (1 << bits_per_pixel);  i++)
+            printf("Map %3d - %5d %5d %5d\n", i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
+    }
+    else
+    {
+        printf("There is no colour map\n");
+    }
+    process_raw = FALSE;
+    printf("Compression is ");
+    switch (compression)
+    {
+    case COMPRESSION_CCITT_T4:
+        printf("T.4\n");
+        return 0;
+    case COMPRESSION_CCITT_T6:
+        printf("T.6\n");
+        return 0;
+    case COMPRESSION_T85:
+        printf("T.85\n");
+        process_raw = TRUE;
+        break;
+    case COMPRESSION_T43:
+        printf("T.43\n");
+        process_raw = TRUE;
+        break;
+    case COMPRESSION_JPEG:
+        printf("JPEG");
+        if (photometric == PHOTOMETRIC_ITULAB)
+        {
+            printf(" ITULAB");
+            process_raw = TRUE;
+        }
+        printf("\n");
+        break;
+    case COMPRESSION_NONE:
+        printf("No compression\n");
+        break;
+    default:
+        printf("Unexpected compression %d\n", compression);
+        break;
+    }
+    if (process_raw)
+    {
+        nstrips = TIFFNumberOfStrips(tif);
+        for (i = 0, total_image_len = 0;  i < nstrips;  i++)
+            total_image_len += TIFFRawStripSize(tif, i);
+        data = malloc(total_image_len);
+        for (i = 0, total_len = 0;  i < nstrips;  i++, total_len += len)
+        {
+            if ((len = TIFFReadRawStrip(tif, i, &data[total_len], total_image_len - total_len)) < 0)
+            {
+                printf("TIFF read error.\n");
+                return -1;
+            }
+        }
+        if (total_len != total_image_len)
+            printf("Size mismatch %d %d\n", total_len, total_image_len);
+        off = total_len;
+        switch (compression)
+        {
+        case COMPRESSION_CCITT_T4:
+            break;
+        case COMPRESSION_CCITT_T6:
+            break;
+        case COMPRESSION_T85:
+            printf("T.85 image %d bytes\n", total_len);
+            for (i = 0;  i < 16;  i++)
+                printf("0x%02x\n", data[i]);
+            t85_decode_init(&t85_dec, t85_row_write_handler, NULL);
+            t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
+            result = t85_decode_put_chunk(&t85_dec, data, total_len);
+            if (result == T85_MORE_DATA)
+                result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
+            len = t85_decode_get_compressed_image_size(&t85_dec);
+            printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
+            t85_decode_release(&t85_dec);
+            return 0;
+        case COMPRESSION_T43:
+            printf("T.43 image %d bytes\n", total_len);
+            if (pack_16(data) == 0xFFA8)
+            {
+                data += 2;
+                total_len -= 2;
+                for (;;)
+                {
+                    if (pack_16(data) == 0xFFE1)
+                    {
+                        data += 2;
+                        total_len -= 2;
+                        len = pack_16(data);
+                        data += len;
+                        total_len -= len;
+                    }
+                    else if (pack_16(data) == 0xFFE3)
+                    {
+                        data += 2;
+                        total_len -= 2;
+                        len = pack_32(data);
+                        data += len;
+                        total_len -= len;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+            }
+
+            bit_mask = 0x80;
+            t85_decode_init(&t85_dec, t85_row_write_handler, NULL);
+            t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
+            t85_dec.min_bit_planes = 1;
+            t85_dec.max_bit_planes = 8;
+            data5_ptr = 0;
+            result = t85_decode_put_chunk(&t85_dec, data, total_len);
+            len = t85_decode_get_compressed_image_size(&t85_dec);
+            printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
+
+            for (j = 1;  j < t85_dec.bit_planes;  j++)
+            {
+                bit_mask >>= 1;
+                data += len/8;
+                total_len -= len/8;
+                t85_decode_new_plane(&t85_dec);
+                data5_ptr = 0;
+                t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
+                result = t85_decode_put_chunk(&t85_dec, data, total_len);
+                len = t85_decode_get_compressed_image_size(&t85_dec);
+                printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
+            }
+            if (result == T85_MORE_DATA)
+            {
+                printf("More\n");
+                result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
+            }
+            len = t85_decode_get_compressed_image_size(&t85_dec);
+            printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
+            t85_decode_release(&t85_dec);
+
+            for (j = 0;  j < data5_ptr;  j += 3)
+            {
+                i = data5[j] & 0xFF;
+//printf("%d %d %d %d %d %d\n", data5_ptr, j, i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
+                data5[j] = xxx[3*i];
+                data5[j + 1] = xxx[3*i + 1];
+                data5[j + 2] = xxx[3*i + 2];
+            }
+
+            if ((tif = TIFFOpen(OUT_FILE_NAME, "w")) == NULL)
+            {
+                printf("Unable to open '%s'!\n", OUT_FILE_NAME);
+                return 1;
+            }
+            TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+            TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
+            // libtiff requires IMAGELENGTH to be set before SAMPLESPERPIXEL,
+            // or StripOffsets and StripByteCounts will have SAMPLESPERPIXEL values
+            TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
+            TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
+            TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+            TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+            TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+            TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+            TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
+            TIFFSetField(tif, TIFFTAG_XRESOLUTION, 200.0f);
+            TIFFSetField(tif, TIFFTAG_YRESOLUTION, 200.0f);
+            TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+            TIFFSetField(tif, TIFFTAG_SOFTWARE, "spandsp");
+            TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "Test");
+            TIFFSetField(tif, TIFFTAG_DATETIME, "2011/02/03 12:30:45");
+            TIFFSetField(tif, TIFFTAG_MAKE, "soft-switch.org");
+            TIFFSetField(tif, TIFFTAG_MODEL, "spandsp");
+            TIFFSetField(tif, TIFFTAG_HOSTCOMPUTER, "i7.coppice.org");
+
+            for (off = 0, i = 0;  i < h;  off += w*3, i++)
+            {
+                TIFFWriteScanline(tif, data5 + off, i, 0);
+            }
+            TIFFWriteDirectory(tif);
+            TIFFClose(tif);
+            return 0;
+        case COMPRESSION_JPEG:
+            break;
+        }
+    }
+    else
+    {
+        printf("Width %d, height %d, bits %d, samples %d\n", w, h, bits_per_pixel, samples_per_pixel);
+
+        bytes_per_row = (bits_per_pixel + 7)/8;
+        bytes_per_row *= w*samples_per_pixel;
+        totdata = h*bytes_per_row;
+        printf("total %d\n", totdata);
+
+        /* Read the image into memory. */
+        data = malloc(totdata);
+        off = 0;
+        for (row = 0;  row < h;  row++)
+        {
+            if (TIFFReadScanline(tif, data + off, row, 0) < 0)
+                return 1;
+            off += bytes_per_row;
+        }
+        printf("total %d, off %d\n", totdata, off);
+
+        /* We now have the image in memory in RGB form */
+
+        if (photometric == PHOTOMETRIC_ITULAB)
+        {
+            printf("YYY ITULAB\n");
+
+            if (!t42_itulab_to_itulab((tdata_t) &outptr, &outsize, data, off, w, h, kk, 256))
+            {
+                printf("Failed to convert to ITULAB - %s\n", kk);
+                return 1;
+            }
+            free(data);
+            data = (uint8_t *) outptr;
+            off = outsize;
+        }
+        else
+        {
+            start = rdtscll();
+            if (photometric == PHOTOMETRIC_CIELAB)
+            {
+                printf("CIELAB\n");
+                /* The default luminant is D50 */
+                set_lab_illuminant(&lab_param, 96.422f, 100.000f,  82.521f);
+                set_lab_gamut(&lab_param, 0, 100, -128, 127, -128, 127, TRUE);
+                lab_to_srgb(&lab_param, data, data, w*h);
+            }
+
+            set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
+            set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
+            if (!t42_srgb_to_itulab(&lab_param, (tdata_t) &outptr, &outsize, data, off, w, h, kk, 256))
+            {
+                printf("Failed to convert to ITULAB - %s\n", kk);
+                return 1;
+            }
+            end = rdtscll();
+            printf("Duration %" PRIu64 "\n", end - start);
+            free(data);
+            data = (uint8_t *) outptr;
+            off = outsize;
+        }
+    }
+    TIFFClose(tif);
+
+    printf("XXX - image is %d by %d, %d bytes\n", w, h, off);
+
+    /* We now have the image in memory in ITULAB form */
+
+    if ((tif = TIFFOpen(OUT_FILE_NAME, "w")) == NULL)
+    {
+        printf("Unable to open '%s'!\n", OUT_FILE_NAME);
+        return 1;
+    }
+    TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
+    // libtiff requires IMAGELENGTH to be set before SAMPLESPERPIXEL,
+    // or StripOffsets and StripByteCounts will have SAMPLESPERPIXEL values
+    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
+    TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
+    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+    TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
+    TIFFSetField(tif, TIFFTAG_XRESOLUTION, 200.0f);
+    TIFFSetField(tif, TIFFTAG_YRESOLUTION, 200.0f);
+    TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+    TIFFSetField(tif, TIFFTAG_SOFTWARE, "spandsp");
+    TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "Test");
+    TIFFSetField(tif, TIFFTAG_DATETIME, "2011/02/03 12:30:45");
+    TIFFSetField(tif, TIFFTAG_MAKE, "soft-switch.org");
+    TIFFSetField(tif, TIFFTAG_MODEL, "spandsp");
+    TIFFSetField(tif, TIFFTAG_HOSTCOMPUTER, "i7.coppice.org");
+
+    if (1)
+    {
+        /* Most image processors won't know what to do with the ITULAB colorspace.
+           So we'll be converting it to RGB for portability. */
+#if 1
+        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+#else
+        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
+#endif
+        if (YCbCrSubsampleHoriz  ||  YCbCrSubsampleVert)
+            TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, YCbCrSubsampleHoriz, YCbCrSubsampleVert);
+        bytes_per_row = (bits_per_pixel + 7)/8;
+        bytes_per_row *= w*samples_per_pixel;
+        totdata = h*bytes_per_row;
+        set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
+        set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
+#if 0
+        start = rdtscll();
+        data2 = NULL;
+        totdata = 0;
+        t42_itulab_to_JPEG(&lab_param, (void **) &data2, &totdata, data, off, kk, 256);
+        end = rdtscll();
+        printf("Duration %" PRIu64 "\n", end - start);
+        printf("Compressed length %d (%p)\n", totdata, data2);
+        if (TIFFWriteRawStrip(tif, 0, data2, totdata) < 0)
+        {
+            printf("Failed to convert from ITULAB - %s\n", kk);
+            return 1;
+        }
+        free(data);
+#else
+        data2 = malloc(totdata);
+        start = rdtscll();
+        if (!t42_itulab_to_srgb(&lab_param, data2, &off, data, off, &w, &h, kk, 256))
+        {
+            printf("Failed to convert from ITULAB - %s\n", kk);
+            return 1;
+        }
+        end = rdtscll();
+        printf("Duration %" PRIu64 "\n", end - start);
+        free(data);
+
+        off = 0;
+        bytes_per_row = (8 + 7)/8;
+        bytes_per_row *= (w*3);
+        for (row = 0;  row < h;  row++)
+        {
+            if (TIFFWriteScanline(tif, data2 + off, row, 0) < 0)
+                return 1;
+            off += bytes_per_row;
+        }
+#endif
+        free(data2);
+    }
+    else
+    {
+#if 1
+        /* If PHOTOMETRIC_ITULAB is not available the admin cannot enable color fax anyway.
+           This is done so that older libtiffs without it can build fine. */
+        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
+#else
+        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
+#endif
+        if (YCbCrSubsampleHoriz  ||  YCbCrSubsampleVert)
+            TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, YCbCrSubsampleHoriz, YCbCrSubsampleVert);
+        if (TIFFWriteRawStrip(tif, 0, (tdata_t) data, off) == -1)
+        {
+            printf("Write error to TIFF file\n");
+            return 1;
+        }
+        free(data);
+    }
+    TIFFWriteDirectory(tif);
+    TIFFClose(tif);
+    printf("Done!\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/