From b5f74435249c983cd705b48bcad5067679187758 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Sat, 21 Jul 2012 04:05:40 +0800 Subject: [PATCH] Started introducing T.42 JPEG support for FAXing, but its not plumbed into the FAX engine yet. --- libs/spandsp/src/Makefile.am | 11 +- libs/spandsp/src/spandsp.h.in | 2 +- libs/spandsp/src/spandsp/expose.h | 2 +- libs/spandsp/src/spandsp/private/t42.h | 86 ++ libs/spandsp/src/spandsp/t42.h | 158 ++++ libs/spandsp/src/t42.c | 1199 ++++++++++++++++++++++++ libs/spandsp/tests/Makefile.am | 4 + libs/spandsp/tests/t42_tests.c | 625 ++++++++++++ 8 files changed, 2081 insertions(+), 6 deletions(-) create mode 100644 libs/spandsp/src/spandsp/private/t42.h create mode 100644 libs/spandsp/src/spandsp/t42.h create mode 100644 libs/spandsp/src/t42.c create mode 100644 libs/spandsp/tests/t42_tests.c diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index 039c8a6c9c..685e3d9328 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -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 \ diff --git a/libs/spandsp/src/spandsp.h.in b/libs/spandsp/src/spandsp.h.in index 0cb5fd3a73..0b1f7aec9b 100644 --- a/libs/spandsp/src/spandsp.h.in +++ b/libs/spandsp/src/spandsp.h.in @@ -109,7 +109,7 @@ #include #include #include -/*#include */ +#include /*#include */ #include #include diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h index f4696d2688..2f90c7f2d4 100644 --- a/libs/spandsp/src/spandsp/expose.h +++ b/libs/spandsp/src/spandsp/expose.h @@ -80,7 +80,7 @@ #include #include #include -/*#include */ +#include /*#include */ #include #include diff --git a/libs/spandsp/src/spandsp/private/t42.h b/libs/spandsp/src/spandsp/private/t42.h new file mode 100644 index 0000000000..7dedfa7ee1 --- /dev/null +++ b/libs/spandsp/src/spandsp/private/t42.h @@ -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 + * + * 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 index 0000000000..d6785cba23 --- /dev/null +++ b/libs/spandsp/src/spandsp/t42.h @@ -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 + * + * 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 index 0000000000..608341fd4c --- /dev/null +++ b/libs/spandsp/src/t42.c @@ -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 + * + * 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 +#include +#include +#include +#if defined(HAVE_TGMATH_H) +#include +#endif +#if defined(HAVE_MATH_H) +#include +#endif +#include +#include "floating_fudge.h" +#include +#include + +#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 ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index 2b5e97afb1..8a0503fe62 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -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 index 0000000000..1ccefed9f0 --- /dev/null +++ b/libs/spandsp/tests/t42_tests.c @@ -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 + * + * 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 +#include +#include +#include +#include + +//#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 ------------------------------------------------------------*/