From 3ea625c6ae99ea115dbed865c0a7ddb92df0acba Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Wed, 29 May 2013 14:47:48 +0800 Subject: [PATCH] More movement towards colour FAXing --- libs/spandsp/src/Makefile.am | 9 + libs/spandsp/src/at_interpreter.c | 3 + libs/spandsp/src/image_translate.c | 178 +- libs/spandsp/src/make_math_fixed_tables.c | 2 + libs/spandsp/src/make_t43_gray_code_tables.c | 104 ++ libs/spandsp/src/spandsp/expose.h | 2 +- libs/spandsp/src/spandsp/t42.h | 45 +- libs/spandsp/src/spandsp/t43.h | 4 +- libs/spandsp/src/spandsp/t4_rx.h | 2 +- libs/spandsp/src/spandsp/t4_t6_encode.h | 9 +- libs/spandsp/src/t30.c | 78 +- libs/spandsp/src/t42.c | 1577 +++++++++--------- libs/spandsp/src/t42_t43_local.h | 2 +- libs/spandsp/src/t43.c | 217 ++- libs/spandsp/src/t4_rx.c | 30 +- libs/spandsp/src/t4_t6_encode.c | 25 +- libs/spandsp/src/t4_tx.c | 11 +- libs/spandsp/tests/fax_tests.c | 30 + libs/spandsp/tests/t4_t6_tests.c | 4 +- 19 files changed, 1309 insertions(+), 1023 deletions(-) create mode 100644 libs/spandsp/src/make_t43_gray_code_tables.c diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index 368f457311..3d63ae7274 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -52,6 +52,7 @@ EXTRA_DIST = floating_fudge.h \ make_cielab_luts.c \ make_math_fixed_tables.c \ make_modem_filter.c \ + make_t43_gray_code_tables.c \ msvc/config.h \ msvc/Download_TIFF.2005.vcproj \ msvc/Download_TIFF.2008.vcproj \ @@ -356,6 +357,9 @@ make_math_fixed_tables$(EXEEXT): $(top_srcdir)/src/make_math_fixed_tables.c make_modem_filter$(EXEEXT): $(top_srcdir)/src/make_modem_filter.c $(top_srcdir)/src/filter_tools.c $(CC_FOR_BUILD) -o make_modem_filter$(EXEEXT) $(top_srcdir)/src/make_modem_filter.c $(top_srcdir)/src/filter_tools.c -DHAVE_CONFIG_H -I$(top_builddir)/src -lm +make_t43_gray_code_tables$(EXEEXT): $(top_srcdir)/src/make_t43_gray_code_tables.c + $(CC_FOR_BUILD) -o make_t43_gray_code_tables$(EXEEXT) $(top_srcdir)/src/make_t43_gray_code_tables.c -DHAVE_CONFIG_H -I$(top_builddir)/src -lm + # We need to run make_at_dictionary, so it generates the # at_interpreter_dictionary.h file @@ -384,6 +388,11 @@ t42.lo: cielab_luts.h cielab_luts.h: make_cielab_luts$(EXEEXT) ./make_cielab_luts$(EXEEXT) >cielab_luts.h +t43.lo: t43_gray_code_tables.h + +t43_gray_code_tables.h: make_t43_gray_code_tables$(EXEEXT) + ./make_t43_gray_code_tables$(EXEEXT) >t43_gray_code_tables.h + V17_V32BIS_RX_INCL = v17_v32bis_rx_rrc.h v17rx.$(OBJEXT): ${V17_V32BIS_RX_INCL} diff --git a/libs/spandsp/src/at_interpreter.c b/libs/spandsp/src/at_interpreter.c index 1878ab2ed6..2ecdbb866d 100644 --- a/libs/spandsp/src/at_interpreter.c +++ b/libs/spandsp/src/at_interpreter.c @@ -5527,6 +5527,9 @@ SPAN_DECLARE(void) at_interpreter(at_state_t *s, const char *cmd, int len) { if ((entry = command_search(t, &matched)) <= 0) break; + /* The following test shouldn't be needed, but let's keep it here for completeness. */ + if (entry > sizeof(at_commands)/sizeof(at_commands[0])) + break; if ((t = at_commands[entry - 1](s, t)) == NULL) break; if (t == (const char *) -1) diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c index 878323ff2d..b504ca9074 100644 --- a/libs/spandsp/src/image_translate.c +++ b/libs/spandsp/src/image_translate.c @@ -253,6 +253,7 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[]) case T4_IMAGE_TYPE_COLOUR_12BIT: image_gray16_to_colour16_row((uint16_t *) buf, (uint16_t *) buf, s->input_width); break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_gray16_to_colour8_row(buf, (uint16_t *) buf, s->input_width); break; @@ -267,6 +268,7 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[]) case T4_IMAGE_TYPE_COLOUR_12BIT: image_gray8_to_colour16_row((uint16_t *) buf, buf, s->input_width); break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_gray8_to_colour8_row(buf, buf, s->input_width); break; @@ -282,11 +284,13 @@ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[]) case T4_IMAGE_TYPE_GRAY_8BIT: image_colour16_to_gray8_row(buf, (uint16_t *) buf, s->input_width); break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_colour16_to_colour8_row(buf, (uint16_t *) buf, s->input_width); break; } break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: switch (s->output_format) { @@ -374,6 +378,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) switch (s->output_format) { + case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: row8[0] = s->raw_pixel_row[0]; row8[1] = s->raw_pixel_row[1]; @@ -611,12 +616,14 @@ SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], if (s->output_row < 0) return 0; - if (s->output_format == T4_IMAGE_TYPE_BILEVEL) + switch (s->output_format) { + case T4_IMAGE_TYPE_BILEVEL: + case T4_IMAGE_TYPE_COLOUR_BILEVEL: + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: i = floyd_steinberg_dither_row(s, buf); - } - else - { + break; + default: s->output_row++; if (s->resize) { @@ -631,6 +638,7 @@ SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], if (s->output_row < 0) return 0; i = s->output_width*s->output_bytes_per_pixel; + break; } return i; } @@ -656,84 +664,42 @@ SPAN_DECLARE(int) image_translate_set_row_read_handler(image_translate_state_t * } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s, - int output_format, - int output_width, - int output_length, - int input_format, - int input_width, - int input_length, - t4_row_read_handler_t row_read_handler, - void *row_read_user_data) +static int image_format_to_bytes_per_pixel(int image_format) +{ + switch (image_format) + { + default: + case T4_IMAGE_TYPE_BILEVEL: + case T4_IMAGE_TYPE_GRAY_8BIT: + return 1; + case T4_IMAGE_TYPE_GRAY_12BIT: + return 2; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: + case T4_IMAGE_TYPE_COLOUR_8BIT: + return 3; + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: + case T4_IMAGE_TYPE_4COLOUR_8BIT: + return 4; + case T4_IMAGE_TYPE_COLOUR_12BIT: + return 6; + case T4_IMAGE_TYPE_4COLOUR_12BIT: + return 8; + } + return 1; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) image_translate_restart(image_translate_state_t *s, int input_length) { int i; int raw_row_size; int row_size; - if (s == NULL) - { - if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL) - return NULL; - } - memset(s, 0, sizeof(*s)); - - s->input_format = input_format; - s->input_width = input_width; s->input_length = input_length; - - s->output_format = output_format; - - if ((s->resize = (output_width > 0))) - { - s->output_width = output_width; - if (output_length > 0) - s->output_length = output_length; - else - s->output_length = (s->input_length*s->output_width)/s->input_width; - } + if (s->resize) + s->output_length = (s->input_length*s->output_width)/s->input_width; else - { - s->output_width = s->input_width; s->output_length = s->input_length; - } - - switch (s->input_format) - { - case T4_IMAGE_TYPE_GRAY_8BIT: - s->input_bytes_per_pixel = 1; - break; - case T4_IMAGE_TYPE_GRAY_12BIT: - s->input_bytes_per_pixel = 2; - break; - case T4_IMAGE_TYPE_COLOUR_8BIT: - s->input_bytes_per_pixel = 3; - break; - case T4_IMAGE_TYPE_COLOUR_12BIT: - s->input_bytes_per_pixel = 6; - break; - default: - s->input_bytes_per_pixel = 1; - break; - } - - switch (s->output_format) - { - case T4_IMAGE_TYPE_GRAY_8BIT: - s->output_bytes_per_pixel = 1; - break; - case T4_IMAGE_TYPE_GRAY_12BIT: - s->output_bytes_per_pixel = 2; - break; - case T4_IMAGE_TYPE_COLOUR_8BIT: - s->output_bytes_per_pixel = 3; - break; - case T4_IMAGE_TYPE_COLOUR_12BIT: - s->output_bytes_per_pixel = 6; - break; - default: - s->output_bytes_per_pixel = 1; - break; - } /* Allocate the two row buffers we need, using the space requirements we now have */ raw_row_size = s->input_width*s->input_bytes_per_pixel; @@ -744,30 +710,78 @@ SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_sta { for (i = 0; i < 2; i++) { - if ((s->raw_pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) - return NULL; + if (s->raw_pixel_row[i] == NULL) + { + if ((s->raw_pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) + return -1; + } memset(s->raw_pixel_row[i], 0, raw_row_size); } } - if (s->output_format == T4_IMAGE_TYPE_BILEVEL) + switch (s->output_format) { + case T4_IMAGE_TYPE_BILEVEL: + case T4_IMAGE_TYPE_COLOUR_BILEVEL: + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: if (s->resize) raw_row_size = row_size; for (i = 0; i < 2; i++) { - if ((s->pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) - return NULL; + if (s->pixel_row[i] == NULL) + { + if ((s->pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) + return -1; + } memset(s->pixel_row[i], 0, raw_row_size); } + break; } - s->row_read_handler = row_read_handler; - s->row_read_user_data = row_read_user_data; - s->raw_input_row = 0; s->raw_output_row = 0; s->output_row = 0; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s, + int output_format, + int output_width, + int output_length, + int input_format, + int input_width, + int input_length, + t4_row_read_handler_t row_read_handler, + void *row_read_user_data) +{ + if (s == NULL) + { + if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + + s->row_read_handler = row_read_handler; + s->row_read_user_data = row_read_user_data; + + s->input_format = input_format; + s->input_width = input_width; + s->input_length = input_length; + s->input_bytes_per_pixel = image_format_to_bytes_per_pixel(s->input_format); + + s->output_format = output_format; + s->output_bytes_per_pixel = image_format_to_bytes_per_pixel(s->output_format); + + s->resize = (output_width > 0); + if (s->resize) + s->output_width = output_width; + else + s->output_width = s->input_width; + + if (image_translate_restart(s, input_length)) + return NULL; + return s; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/make_math_fixed_tables.c b/libs/spandsp/src/make_math_fixed_tables.c index 2bddd12ced..64b33ca328 100644 --- a/libs/spandsp/src/make_math_fixed_tables.c +++ b/libs/spandsp/src/make_math_fixed_tables.c @@ -114,3 +114,5 @@ int main(int argc, char *argv[]) return 0; } +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/src/make_t43_gray_code_tables.c b/libs/spandsp/src/make_t43_gray_code_tables.c new file mode 100644 index 0000000000..9e45b120ef --- /dev/null +++ b/libs/spandsp/src/make_t43_gray_code_tables.c @@ -0,0 +1,104 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * make_t43_gray_code_tables.c - Generate the Gray code tables for T.43 image + * compression. + * + * Written by Steve Underwood + * + * Copyright (C) 2012 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. + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int i; + int j; + int gray; + int new_gray; + int restore; + + printf("static const int16_t gray_code[4096] =\n{\n"); + for (i = 0; i < 4096; i++) + { + gray = i & 0x800; + for (j = 10; j >= 0; j--) + { + if (((i >> (j + 1)) & 1) ^ ((i >> j) & 1)) + gray |= (1 << j); + } + printf(" 0x%04x, /* 0x%04x */\n", gray, restore); + + /* Now reverse the process and check we get back where we start */ + restore = gray & 0x800; + for (j = 10; j >= 0; j--) + { + if (((restore >> (j + 1)) & 1) ^ ((gray >> j) & 1)) + restore |= (1 << j); + } + + if (i != restore) + { + printf("Ah\n"); + exit(2); + } + } + printf("};\n\n"); + + printf("static const int16_t anti_gray_code[4096] =\n{\n"); + for (i = 0; i < 4096; i++) + { + gray = i; + restore = gray & 0x800; + for (j = 10; j >= 0; j--) + { + if (((restore >> (j + 1)) & 1) ^ ((gray >> j) & 1)) + restore |= (1 << j); + } + printf(" 0x%04x, /* 0x%04x */\n", restore, gray); + + /* Now reverse the process and check we get back where we start */ + new_gray = restore & 0x800; + for (j = 10; j >= 0; j--) + { + if (((restore >> (j + 1)) & 1) ^ ((restore >> j) & 1)) + new_gray |= (1 << j); + } + + if (gray != new_gray) + { + printf("Ah\n"); + exit(2); + } + } + printf("};\n"); + + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h index 49d2a8d52f..5424b7c67e 100644 --- a/libs/spandsp/src/spandsp/expose.h +++ b/libs/spandsp/src/spandsp/expose.h @@ -83,7 +83,7 @@ #include #include #include -/*#include */ +#include #include #include #include diff --git a/libs/spandsp/src/spandsp/t42.h b/libs/spandsp/src/spandsp/t42.h index b864b17536..343780b9fe 100644 --- a/libs/spandsp/src/spandsp/t42.h +++ b/libs/spandsp/src/spandsp/t42.h @@ -48,8 +48,24 @@ extern "C" { #endif +/*! \brief Convert an X0, Y0, Z0 coordinate to a colour tempature */ +SPAN_DECLARE(int) xyz_to_corrected_color_temp(float *temp, float xyz[3]); + +/*! \brief Convert a colour temperature to an X0, Y0, Z0 coordinate */ +SPAN_DECLARE(int) colour_temp_to_xyz(float xyz[3], float temp); + +/*! \brief Convert a row of 8 bit pixels from Lab to sRGB + \param s The Lab parameters context. + \param lab The output pixels + \param srgb The input pixels + \param pixel The number of pixels in the row. */ SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels); +/*! \brief Convert a row of 8 bit pixels from sRGB to Lab + \param s The Lab parameters context. + \param srgb The output pixels + \param lab The input pixels + \param pixel The number of pixels in the row. */ 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); @@ -58,6 +74,8 @@ SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_mi 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) get_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(int) t42_itulab_to_itulab(logging_state_t *logging, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height); SPAN_DECLARE(int) t42_itulab_to_jpeg(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen); @@ -68,15 +86,14 @@ SPAN_DECLARE(int) t42_srgb_to_itulab(logging_state_t *logging, lab_params_t *s, SPAN_DECLARE(int) t42_itulab_to_srgb(logging_state_t *logging, lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height); -SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s, - uint32_t l0, - int mx, - int options); +SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s, uint32_t l0, int quality, 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(int) t42_encode_set_image_type(t42_encode_state_t *s, int image_type); + 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); @@ -92,11 +109,12 @@ 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); +/*! \brief Get the size of the compressed image in bits. + \param s The T.42 context. + \return The size of the image, in bits. */ 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_set_row_read_handler(t42_encode_state_t *s, t4_row_read_handler_t handler, void *user_data); /*! Get the logging context associated with a T.42 encode context. \brief Get the logging context associated with a T.42 encode context. @@ -163,6 +181,14 @@ SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s, t4_row_write_handler_t handler, void *user_data); +/*! A maliciously constructed T.42 image could consume too much memory, and constitute + a denial of service attack on the system. This function allows constraints to be + applied. + \brief Set constraints on the received image size. + \param s The T.42 context. + \param max_xd The maximum permitted width of the full image, in pixels + \param max_yd The maximum permitted height of the full image, in pixels + \return 0 for OK */ SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s, uint32_t max_xd, uint32_t max_yd); @@ -177,6 +203,9 @@ SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s); \return The length of the image, in pixels. */ SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s); +/*! \brief Get the size of the compressed image in bits. + \param s The T.42 context. + \return The size of the image, in bits. */ SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s); /*! Get the logging context associated with a T.42 decode context. @@ -185,6 +214,8 @@ SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s); \return A pointer to the logging context */ SPAN_DECLARE(logging_state_t *) t42_decode_get_logging_state(t42_decode_state_t *s); +/*! \brief Restart a T.42 decode context. + \param s The T.42 context. */ SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s); /*! \brief Prepare to decode an image in T.42 format. diff --git a/libs/spandsp/src/spandsp/t43.h b/libs/spandsp/src/spandsp/t43.h index c6718d18ff..ecc0f2a224 100644 --- a/libs/spandsp/src/spandsp/t43.h +++ b/libs/spandsp/src/spandsp/t43.h @@ -68,10 +68,10 @@ SPAN_DECLARE(int) t43_encode_set_image_width(t43_encode_state_t *s, uint32_t ima SPAN_DECLARE(int) t43_encode_set_image_length(t43_encode_state_t *s, uint32_t length); -SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s); - SPAN_DECLARE(int) t43_encode_set_image_type(t43_encode_state_t *s, int image_type); +SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s); + SPAN_DECLARE(void) t43_encode_comment(t43_encode_state_t *s, const uint8_t comment[], size_t len); /*! \brief Check if we are at the end of the current document page. diff --git a/libs/spandsp/src/spandsp/t4_rx.h b/libs/spandsp/src/spandsp/t4_rx.h index 7ebe5b92ce..ed56cbcf8f 100644 --- a/libs/spandsp/src/spandsp/t4_rx.h +++ b/libs/spandsp/src/spandsp/t4_rx.h @@ -537,7 +537,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t /*! Get the short text name of a compression format. \brief Get the short text name of an encoding format. - \param encoding The encoding type. + \param compression The compression type. \return A pointer to the string. */ SPAN_DECLARE(const char *) t4_compression_to_str(int compression); diff --git a/libs/spandsp/src/spandsp/t4_t6_encode.h b/libs/spandsp/src/spandsp/t4_t6_encode.h index 7431d64f08..a157b444b9 100644 --- a/libs/spandsp/src/spandsp/t4_t6_encode.h +++ b/libs/spandsp/src/spandsp/t4_t6_encode.h @@ -114,20 +114,23 @@ SPAN_DECLARE(logging_state_t *) t4_t6_encode_get_logging_state(t4_t6_encode_stat /*! \brief Restart a T.4 or T.6 encode context. \param s The T.4/T.6 context. - \param image width The image width, in pixels. + \param image_width The image width, in pixels. + \param image_length The image length, in pixels. This can be set to -1, if the length is not known. \return 0 for success, otherwise -1. */ -SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width); +SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width, int image_length); /*! \brief Prepare to encode an image in T.4 or T.6 format. \param s The T.4/T.6 context. \param encoding The encoding mode. - \param image width The image width, in pixels. + \param image_width The image width, in pixels. + \param image_length The image length, in pixels. This can be set to -1, if the length is not known. \param handler A callback routine to handle decoded image rows. \param user_data An opaque pointer passed to handler. \return A pointer to the context, or NULL if there was a problem. */ SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s, int encoding, int image_width, + int image_length, t4_row_read_handler_t handler, void *user_data); diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index a70db393c9..c3e4eadecc 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -751,10 +751,12 @@ static uint8_t check_next_tx_step(t30_state_t *s) more = FALSE; if (more) { + span_log(&s->logging, SPAN_LOG_FLOW, "Another document to send\n"); //if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MULTIPLE_SELECTIVE_POLLING_CAPABLE)) // return T30_EOS; return (s->local_interrupt_pending) ? T30_PRI_EOM : T30_EOM; } + span_log(&s->logging, SPAN_LOG_FLOW, "No more pages to send\n"); return (s->local_interrupt_pending) ? T30_PRI_EOP : T30_EOP; } /*- End of function --------------------------------------------------------*/ @@ -1169,9 +1171,9 @@ int t30_build_dis_or_dtc(t30_state_t *s) /* Ready to receive a fax will be determined separately, and this message edited. */ /* With no modems set we are actually selecting V.27ter fallback at 2400bps */ if ((s->supported_modems & T30_SUPPORT_V27TER)) - set_ctrl_bit(s->local_dis_dtc_frame, 12); + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_MODEM_TYPE_2); if ((s->supported_modems & T30_SUPPORT_V29)) - set_ctrl_bit(s->local_dis_dtc_frame, 11); + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_MODEM_TYPE_1); /* V.17 is only valid when combined with V.29 and V.27ter, so if we enable V.17 we force the others too. */ if ((s->supported_modems & T30_SUPPORT_V17)) s->local_dis_dtc_frame[4] |= (DISBIT6 | DISBIT4 | DISBIT3); @@ -1359,9 +1361,11 @@ int t30_build_dis_or_dtc(t30_state_t *s) /* No double sided printing (alternate mode) */ /* No double sided printing (continuous mode) */ + /* No black and white mixed raster content profile */ /* No shared data memory */ /* No T.44 colour space */ + if ((s->iaf & T30_IAF_MODE_FLOW_CONTROL)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T38_FLOW_CONTROL_CAPABLE); /* No k > 4 */ @@ -1455,27 +1459,23 @@ static int build_dcs(t30_state_t *s) /* Select the compression to use. */ use_bilevel = TRUE; + set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); switch (s->line_compression) { case T4_COMPRESSION_T4_1D: /* There is nothing to set to select this encoding. */ - set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); break; case T4_COMPRESSION_T4_2D: set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_2D_MODE); - set_ctrl_bits(s->dcs_frame, s->min_scan_time_code, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); break; case T4_COMPRESSION_T6: set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T6_MODE); - set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); break; case T4_COMPRESSION_T85: set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_MODE); - set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); break; case T4_COMPRESSION_T85_L0: set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T85_L0_MODE); - set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); break; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -1487,7 +1487,7 @@ static int build_dcs(t30_state_t *s) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); if (image_type == T4_IMAGE_TYPE_GRAY_12BIT || image_type == T4_IMAGE_TYPE_COLOUR_12BIT) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT); - //if (???????? & T4_COMPRESSION_?????)) + //if (???????? & T4_COMPRESSION_NO_SUBSAMPLING)) // set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING); //if (???????? & T4_COMPRESSION_?????)) // set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_PREFERRED_HUFFMAN_TABLES); @@ -1501,7 +1501,7 @@ static int build_dcs(t30_state_t *s) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); if (image_type == T4_IMAGE_TYPE_GRAY_12BIT || image_type == T4_IMAGE_TYPE_COLOUR_12BIT) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_12BIT_COMPONENT); - //if (???????? & T4_COMPRESSION_?????)) + //if (???????? & T4_COMPRESSION_NO_SUBSAMPLING)) // set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING); set_ctrl_bits(s->dcs_frame, T30_MIN_SCAN_0MS, T30_DCS_BIT_MIN_SCAN_LINE_TIME_1); use_bilevel = FALSE; @@ -1684,7 +1684,7 @@ static int build_dcs(t30_state_t *s) || ((s->image_width == T4_WIDTH_1200_A4) && (s->x_resolution == T4_X_RESOLUTION_1200))) { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4\n"); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution); /* No width related bits need to be set. */ } else if (((s->image_width == T4_WIDTH_200_B4) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) @@ -1699,7 +1699,7 @@ static int build_dcs(t30_state_t *s) { if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_255MM)) { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4\n"); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution); set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH); } else @@ -1720,7 +1720,7 @@ static int build_dcs(t30_state_t *s) { if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_303MM)) { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3\n"); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpi x %ddpi\n", s->x_resolution, s->y_resolution); set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH); } else @@ -1747,7 +1747,7 @@ static int build_dcs(t30_state_t *s) return -1; } - /* Deal with the image length */ + /* Set the image length */ /* If the other end supports unlimited length, then use that. Otherwise, if the other end supports B4 use that, as its longer than the default A4 length. */ if ((s->mutual_image_sizes & T4_SUPPORT_LENGTH_UNLIMITED)) @@ -1768,10 +1768,10 @@ static int build_dcs(t30_state_t *s) if ((s->iaf & T30_IAF_MODE_CONTINUOUS_FLOW) && test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_T38_FAX_CAPABLE)) { /* Clear the modem type bits, in accordance with note 77 of Table 2/T.30 */ - clr_ctrl_bit(s->local_dis_dtc_frame, 11); - clr_ctrl_bit(s->local_dis_dtc_frame, 12); - clr_ctrl_bit(s->local_dis_dtc_frame, 13); - clr_ctrl_bit(s->local_dis_dtc_frame, 14); + clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_1); + clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_2); + clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_3); + clr_ctrl_bit(s->local_dis_dtc_frame, T30_DCS_BIT_MODEM_TYPE_4); set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_T38_FAX_MODE); } s->dcs_len = 19; @@ -2013,14 +2013,16 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) static int analyze_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) { + /* The following treats a width field of 11 like 10, which does what note 6 of Table 2/T.30 + says we should do with the invalid value 11. */ static const int widths[6][4] = { - { T4_WIDTH_100_A4, T4_WIDTH_100_B4, T4_WIDTH_100_A3, -1}, /* 100/inch */ - { T4_WIDTH_200_A4, T4_WIDTH_200_B4, T4_WIDTH_200_A3, -1}, /* 200/inch / R8 resolution */ - { T4_WIDTH_300_A4, T4_WIDTH_300_B4, T4_WIDTH_300_A3, -1}, /* 300/inch resolution */ - { T4_WIDTH_400_A4, T4_WIDTH_400_B4, T4_WIDTH_400_A3, -1}, /* 400/inch / R16 resolution */ - { T4_WIDTH_600_A4, T4_WIDTH_600_B4, T4_WIDTH_600_A3, -1}, /* 600/inch resolution */ - {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, -1} /* 1200/inch resolution */ + { T4_WIDTH_100_A4, T4_WIDTH_100_B4, T4_WIDTH_100_A3, T4_WIDTH_100_A3}, /* 100/inch */ + { T4_WIDTH_200_A4, T4_WIDTH_200_B4, T4_WIDTH_200_A3, T4_WIDTH_200_A3}, /* 200/inch / R8 resolution */ + { T4_WIDTH_300_A4, T4_WIDTH_300_B4, T4_WIDTH_300_A3, T4_WIDTH_300_A3}, /* 300/inch resolution */ + { T4_WIDTH_400_A4, T4_WIDTH_400_B4, T4_WIDTH_400_A3, T4_WIDTH_400_A3}, /* 400/inch / R16 resolution */ + { T4_WIDTH_600_A4, T4_WIDTH_600_B4, T4_WIDTH_600_A3, T4_WIDTH_600_A3}, /* 600/inch resolution */ + {T4_WIDTH_1200_A4, T4_WIDTH_1200_B4, T4_WIDTH_1200_A3, T4_WIDTH_1200_A3} /* 1200/inch resolution */ }; uint8_t dcs_frame[T30_MAX_DIS_DTC_DCS_LEN]; int i; @@ -2588,7 +2590,28 @@ static int send_cfr_sequence(t30_state_t *s, int start) /* (CSA) CFR */ /* CFR is usually a simple frame, but can become a sequence with Internet FAXing. */ - send_simple_frame(s, T30_CFR); + if (start) + { + s->step = 0; + } + switch (s->step) + { + case 0: + s->step++; + if (send_csa_frame(s)) + break; + /* Fall through */ + case 1: + s->step++; + send_simple_frame(s, T30_CFR); + break; + case 2: + s->step++; + shut_down_hdlc_tx(s); + break; + default: + return -1; + } return 0; } /*- End of function --------------------------------------------------------*/ @@ -6291,12 +6314,7 @@ SPAN_DECLARE(void) t30_front_end_status(void *user_data, int status) } break; case T30_STATE_F_CFR: - if (s->step == 0) - { - shut_down_hdlc_tx(s); - s->step++; - } - else + if (send_cfr_sequence(s, FALSE)) { if (s->error_correcting_mode) { diff --git a/libs/spandsp/src/t42.c b/libs/spandsp/src/t42.c index 3028c03472..c1f9802fbc 100644 --- a/libs/spandsp/src/t42.c +++ b/libs/spandsp/src/t42.c @@ -38,6 +38,7 @@ #include #include #include +#include #if defined(HAVE_TGMATH_H) #include #endif @@ -48,7 +49,6 @@ #include #include #include -#include #include "spandsp/telephony.h" #include "spandsp/fast_convert.h" @@ -90,12 +90,6 @@ typedef struct 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}, @@ -119,8 +113,86 @@ static const illuminant_t illuminants[] = {"", "", 0.000f, 0.000f, 0.000f} }; -/* This is the error catcher */ -static struct jpeg_error_mgr error_handler; +/* LERP(a,b,c) = linear interpolation macro, is 'a' when c == 0.0 and 'b' when c == 1.0 */ +#define LERP(a,b,c) (((b) - (a))*(c) + (a)) + +typedef struct UVT +{ + double u; + double v; + double t; +} UVT; + +static const double rt[31] = +{ + /* Reciprocal temperature (K) */ + FLT_MIN, + 10.0e-6, + 20.0e-6, + 30.0e-6, + 40.0e-6, + 50.0e-6, + 60.0e-6, + 70.0e-6, + 80.0e-6, + 90.0e-6, + 100.0e-6, + 125.0e-6, + 150.0e-6, + 175.0e-6, + 200.0e-6, + 225.0e-6, + 250.0e-6, + 275.0e-6, + 300.0e-6, + 325.0e-6, + 350.0e-6, + 375.0e-6, + 400.0e-6, + 425.0e-6, + 450.0e-6, + 475.0e-6, + 500.0e-6, + 525.0e-6, + 550.0e-6, + 575.0e-6, + 600.0e-6 +}; + +static const UVT uvt[31] = +{ + {0.18006, 0.26352, -0.24341}, + {0.18066, 0.26589, -0.25479}, + {0.18133, 0.26846, -0.26876}, + {0.18208, 0.27119, -0.28539}, + {0.18293, 0.27407, -0.30470}, + {0.18388, 0.27709, -0.32675}, + {0.18494, 0.28021, -0.35156}, + {0.18611, 0.28342, -0.37915}, + {0.18740, 0.28668, -0.40955}, + {0.18880, 0.28997, -0.44278}, + {0.19032, 0.29326, -0.47888}, + {0.19462, 0.30141, -0.58204}, + {0.19962, 0.30921, -0.70471}, + {0.20525, 0.31647, -0.84901}, + {0.21142, 0.32312, -1.01820}, + {0.21807, 0.32909, -1.21680}, + {0.22511, 0.33439, -1.45120}, + {0.23247, 0.33904, -1.72980}, + {0.24010, 0.34308, -2.06370}, + {0.24792, 0.34655, -2.46810}, /* Note: 0.24792 is a corrected value for the error found in W&S as 0.24702 */ + {0.25591, 0.34951, -2.96410}, + {0.26400, 0.35200, -3.58140}, + {0.27218, 0.35407, -4.36330}, + {0.28039, 0.35577, -5.37620}, + {0.28863, 0.35714, -6.72620}, + {0.29685, 0.35823, -8.59550}, + {0.30505, 0.35907, -11.3240}, + {0.31320, 0.35968, -15.6280}, + {0.32129, 0.36011, -23.3250}, + {0.32931, 0.36038, -40.7700}, + {0.33724, 0.36051, -116.450} +}; static __inline__ uint16_t pack_16(const uint8_t *s) { @@ -148,88 +220,158 @@ static __inline__ int unpack_16(uint8_t *s, uint16_t value) } /*- End of function --------------------------------------------------------*/ -/* Error handler for IJG library */ -static void jpg_error_exit(j_common_ptr cinfo) +SPAN_DECLARE(int) xyz_to_corrected_color_temp(float *temp, float xyz[3]) { - escape_route_t *escape; + float us; + float vs; + float p; + float di; + float dm; + int i; - escape = (escape_route_t *) cinfo->client_data; - (*cinfo->err->format_message)(cinfo, escape->error_message); - longjmp(escape->escape, 1); + /* Protect against possible divide-by-zero failure */ + if ((xyz[0] < 1.0e-20f) && (xyz[1] < 1.0e-20f) && (xyz[2] < 1.0e-20f)) + return -1; + us = (4.0f*xyz[0])/(xyz[0] + 15.0f*xyz[1] + 3.0f*xyz[2]); + vs = (6.0f*xyz[1])/(xyz[0] + 15.0f*xyz[1] + 3.0f*xyz[2]); + dm = 0.0f; + for (i = 0; i < 31; i++) + { + di = (vs - uvt[i].v) - uvt[i].t*(us - uvt[i].u); + if ((i > 0) && (((di < 0.0f) && (dm >= 0.0f)) || ((di >= 0.0f) && (dm < 0.0f)))) + break; /* found lines bounding (us, vs) : i-1 and i */ + dm = di; + } + if (i == 31) + { + /* Bad XYZ input, color temp would be less than minimum of 1666.7 degrees, or too far towards blue */ + return -1; + } + di = di/sqrtf(1.0f + uvt[i ].t*uvt[i ].t); + dm = dm/sqrtf(1.0f + uvt[i - 1].t*uvt[i - 1].t); + p = dm/(dm - di); /* p = interpolation parameter, 0.0 : i-1, 1.0 : i */ + p = 1.0f/(LERP(rt[i - 1], rt[i], p)); + *temp = p; + return 0; } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn) +SPAN_DECLARE(int) colour_temp_to_xyz(float xyz[3], float temp) +{ + float x; + float y; + + /* Should be good for 1667K to 25000K according to Wikipedia */ + if (temp < 1667.0f || temp > 25000.0f) + return -1; + + if (temp < 4000.0f) + x = -0.2661239e9f/(temp*temp*temp) - 0.2343580e6f/(temp*temp) + 0.8776956e3f/temp + 0.179910f; + else + x = -3.0258469e9f/(temp*temp*temp) + 2.1070379e6f/(temp*temp) + 0.2226347e3f/temp + 0.240390f; + + if (temp < 2222.0f) + y = -1.1063814f*x*x*x - 1.34811020f*x*x + 2.18555832f*x - 0.20219683f; + else if (temp < 4000.0f) + y = -0.9549476f*x*x*x - 1.37418593f*x*x + 2.09137015f*x - 0.16748867f; + else + y = 3.0817580f*x*x*x - 5.87338670f*x*x + 3.75112997f*x - 0.37001483f; + + xyz[0] = x/y; + xyz[1] = 1.0f; + xyz[2] = (1.0f - x - y)/y; + + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *lab, 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; + lab->x_n = new_xn/100.0f; + lab->y_n = new_yn/100.0f; + lab->z_n = new_zn/100.0f; } else { - s->x_n = new_xn; - s->y_n = new_yn; - s->z_n = new_zn; + lab->x_n = new_xn; + lab->y_n = new_yn; + lab->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) +SPAN_DECLARE(void) set_lab_gamut(lab_params_t *lab, 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; + lab->range_L = L_max - L_min; + lab->range_a = a_max - a_min; + lab->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; + lab->offset_L = -256.0f*L_min/lab->range_L; + lab->offset_a = -256.0f*a_min/lab->range_a; + lab->offset_b = -256.0f*b_min/lab->range_b; - s->range_L /= (256.0f - 1.0f); - s->range_a /= (256.0f - 1.0f); - s->range_b /= (256.0f - 1.0f); + lab->range_L /= (256.0f - 1.0f); + lab->range_a /= (256.0f - 1.0f); + lab->range_b /= (256.0f - 1.0f); - s->ab_are_signed = ab_are_signed; + lab->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) +SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *lab, 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); + lab->range_L = L_Q/(256.0f - 1.0f); + lab->range_a = a_Q/(256.0f - 1.0f); + lab->range_b = b_Q/(256.0f - 1.0f); - s->offset_L = L_P; - s->offset_a = a_P; - s->offset_b = b_P; + lab->offset_L = L_P; + lab->offset_a = a_P; + lab->offset_b = b_P; - s->ab_are_signed = FALSE; + lab->ab_are_signed = FALSE; } /*- End of function --------------------------------------------------------*/ -void set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4]) +SPAN_DECLARE(void) get_lab_gamut2(lab_params_t *lab, int *L_P, int *L_Q, int *a_P, int *a_Q, int *b_P, int *b_Q) +{ + *L_Q = lab->range_L*(256.0f - 1.0f); + *a_Q = lab->range_a*(256.0f - 1.0f); + *b_Q = lab->range_b*(256.0f - 1.0f); + + *L_P = lab->offset_L; + *a_P = lab->offset_a; + *b_P = lab->offset_b; +} +/*- End of function --------------------------------------------------------*/ + +int set_illuminant_from_code(logging_state_t *logging, lab_params_t *lab, const uint8_t code[4]) { int i; int colour_temp; + float xyz[3]; - if (code[0] == 'C' && code[1] == 'T') + if (memcmp(code, "CT", 2) == 0) { colour_temp = pack_16(&code[2]); span_log(logging, SPAN_LOG_FLOW, "Illuminant colour temp %dK\n", colour_temp); - return; + colour_temp_to_xyz(xyz, (float) colour_temp); + set_lab_illuminant(lab, xyz[0], xyz[1], xyz[2]); + return colour_temp; } for (i = 0; illuminants[i].name[0]; i++) { if (memcmp(code, illuminants[i].tag, 4) == 0) { span_log(logging, SPAN_LOG_FLOW, "Illuminant %s\n", illuminants[i].name); - set_lab_illuminant(s, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn); - break; + set_lab_illuminant(lab, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn); + return 0; } } if (illuminants[i].name[0] == '\0') span_log(logging, SPAN_LOG_FLOW, "Unrecognised illuminant 0x%x 0x%x 0x%x 0x%x\n", code[0], code[1], code[2], code[3]); + return -1; } /*- End of function --------------------------------------------------------*/ @@ -253,102 +395,6 @@ void set_gamut_from_code(logging_state_t *logging, lab_params_t *s, const uint8_ } /*- End of function --------------------------------------------------------*/ -static int is_itu_fax(logging_state_t *logging, 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) - { - /* Markers are: - JPEG_RST0 - JPEG_EOI - JPEG_APP0 - JPEG_COM */ - 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]); - span_log(logging, SPAN_LOG_FLOW, "Version %d, resolution %d dpi\n", val[0], val[1]); - ok = TRUE; - break; - case 1: - span_log(logging, SPAN_LOG_FLOW, "Set gamut\n"); - if (ptr->data_length >= 6 + 12) - { - set_gamut_from_code(logging, s, &data[6]); - ok = TRUE; - } - else - { - span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", ptr->data_length); - } - break; - case 2: - span_log(logging, SPAN_LOG_FLOW, "Set illuminant\n"); - if (ptr->data_length >= 6 + 4) - { - set_illuminant_from_code(logging, s, &data[6]); - ok = TRUE; - } - else - { - span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", ptr->data_length); - } - break; - case 3: - /* Colour palette table */ - span_log(logging, SPAN_LOG_FLOW, "Set colour palette\n"); - if (ptr->data_length >= 6 + 2) - { - val[0] = pack_16(&data[6]); - span_log(logging, SPAN_LOG_FLOW, "Colour palette %d\n", val[0]); - } - else - { - span_log(logging, SPAN_LOG_FLOW, "Got bad G3FAX3 length - %d\n", ptr->data_length); - } - break; - default: - span_log(logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[5], ptr->data_length); - break; - } - } - } - else - { - span_log(logging, SPAN_LOG_FLOW, "Got marker 0x%x, length %d\n", ptr->marker, ptr->data_length); - span_log_buf(logging, SPAN_LOG_FLOW, "Got marker", (const uint8_t *) ptr->data, ptr->data_length); - } - ptr = ptr->next; - } - - return ok; -} -/*- End of function --------------------------------------------------------*/ - -static void set_itu_fax(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; @@ -496,695 +542,149 @@ SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t la } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t42_itulab_to_jpeg(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen) +static int is_itu_fax(t42_decode_state_t *s, jpeg_saved_marker_ptr ptr) { - struct jpeg_decompress_struct decompressor; - struct jpeg_compress_struct compressor; - FILE *in; - FILE *out; - int m; - JSAMPROW scan_line_in; - JSAMPROW scan_line_out; - escape_route_t escape; -#if defined(HAVE_OPEN_MEMSTREAM) - char *outptr; - size_t outsize; -#endif + const uint8_t *data; + int ok; + int val[6]; - escape.error_message[0] = '\0'; - -#if defined(HAVE_OPEN_MEMSTREAM) - if ((in = fmemopen(src, srclen, "r")) == NULL) + ok = FALSE; + for ( ; ptr; ptr = ptr->next) { - span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n"); - return FALSE; - } - outsize = 0; - if ((out = open_memstream(&outptr, &outsize)) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); - fclose(in); - return FALSE; - } - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(in); - fclose(out); - return FALSE; - } -#else - if ((in = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - return FALSE; - } - if (fwrite(src, 1, srclen, in) != srclen) - { - fclose(in); - return FALSE; - } - if (fseek(in, 0, SEEK_SET) != 0) - { - fclose(in); - return FALSE; - } - if ((out = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - fclose(in); - return FALSE; - } -#endif - scan_line_out = NULL; - - if (setjmp(escape.escape)) - { - if (escape.error_message[0]) - span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message); - else - span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); - if (scan_line_out) - free(scan_line_out); - fclose(in); - fclose(out); - return FALSE; + if (ptr->marker != (JPEG_APP0 + 1)) + continue; + if (ptr->data_length < 6) + return FALSE; + /* Markers are: + JPEG_RST0 + JPEG_EOI + JPEG_APP0 + JPEG_COM */ + data = (const uint8_t *) ptr->data; + if (strncmp((const char *) data, "G3FAX", 5)) + return FALSE; + switch (data[5]) + { + case 0: + if (ptr->data_length < 6 + 4) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", ptr->data_length); + return FALSE; + } + val[0] = pack_16(&data[6]); + s->spatial_resolution = pack_16(&data[6 + 2]); + span_log(&s->logging, SPAN_LOG_FLOW, "Version %d, resolution %ddpi\n", val[0], s->spatial_resolution); + ok = TRUE; + break; + case 1: + span_log(&s->logging, SPAN_LOG_FLOW, "Set gamut\n"); + if (ptr->data_length < 6 + 12) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", ptr->data_length); + return FALSE; + } + set_gamut_from_code(&s->logging, &s->lab, &data[6]); + break; + case 2: + span_log(&s->logging, SPAN_LOG_FLOW, "Set illuminant\n"); + if (ptr->data_length < 6 + 4) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", ptr->data_length); + return FALSE; + } + s->illuminant_colour_temperature = set_illuminant_from_code(&s->logging, &s->lab, &data[6]); + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[5], ptr->data_length); + 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 colour space. For ITULab, we use YCbCr as a "don't touch" marker */ - decompressor.out_color_space = JCS_YCbCr; - - /* Sanity check and parameter check */ - if (!is_itu_fax(logging, s, decompressor.marker_list)) - { - span_log(logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n"); - 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 colour space */ - compressor.in_color_space = JCS_RGB; - compressor.input_components = 3; - - jpeg_set_defaults(&compressor); - /* Limit to baseline-JPEG values */ - //jpeg_set_quality(&compressor, quality, TRUE); - - /* 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); - -#if defined(HAVE_OPEN_MEMSTREAM) - fclose(out); - *dst = outptr; - *dstlen = outsize; -#else - *dstlen = ftell(out); - *dst = malloc(*dstlen); - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } - if (fread(*dst, 1, *dstlen, out) != *dstlen) - { - free(*dst); - fclose(out); - return FALSE; - } - fclose(out); -#endif - - return TRUE; + return ok; } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t42_jpeg_to_itulab(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen) +static void set_itu_fax(t42_encode_state_t *s) { - struct jpeg_decompress_struct decompressor; - struct jpeg_compress_struct compressor; - FILE *in; - FILE *out; - int m; - JSAMPROW scan_line_in; - JSAMPROW scan_line_out; - escape_route_t escape; -#if defined(HAVE_OPEN_MEMSTREAM) - char *outptr; - size_t outsize; -#endif + uint8_t data[50]; + int val[6]; - escape.error_message[0] = '\0'; + memcpy(data, "G3FAX\0", 6); + unpack_16(&data[6 + 0], 1994); + unpack_16(&data[6 + 2], s->spatial_resolution); + jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 4); -#if defined(HAVE_OPEN_MEMSTREAM) - if ((in = fmemopen(src, srclen, "r")) == NULL) + if (s->lab.offset_L != 0 + || + s->lab.range_L != 100 + || + s->lab.offset_a != 128 + || + s->lab.range_a != 170 + || + s->lab.offset_b != 96 + || + s->lab.range_b != 200) { - span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n"); - return FALSE; + span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX1\n"); + memcpy(data, "G3FAX\1", 6); + get_lab_gamut2(&s->lab, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]); + unpack_16(&data[6 + 0], val[0]); + unpack_16(&data[6 + 2], val[1]); + unpack_16(&data[6 + 4], val[2]); + unpack_16(&data[6 + 6], val[3]); + unpack_16(&data[6 + 8], val[4]); + unpack_16(&data[6 + 10], val[5]); + jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 12); } - outsize = 0; - if ((out = open_memstream(&outptr, &outsize)) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); - fclose(in); - return FALSE; - } - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(in); - fclose(out); - return FALSE; - } -#else - if ((in = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - return FALSE; - } - if (fwrite(src, 1, srclen, in) != srclen) - { - fclose(in); - return FALSE; - } - if (fseek(in, 0, SEEK_SET) != 0) - { - fclose(in); - return FALSE; - } - if ((out = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - fclose(in); - return FALSE; - } -#endif - scan_line_out = NULL; - if (setjmp(escape.escape)) + if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0 + || + s->illuminant_colour_temperature > 0) { - if (escape.error_message[0]) - span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message); + span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX2\n"); + memcpy(data, "G3FAX\2", 6); + if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0) + { + memcpy(&data[6], s->illuminant_code, 4); + } else - span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); - if (scan_line_out) - free(scan_line_out); - fclose(in); - fclose(out); - return FALSE; + { + memcpy(&data[6 + 0], "CT", 2); + unpack_16(&data[6 + 2], s->illuminant_colour_temperature); + } + jpeg_write_marker(&s->compressor, (JPEG_APP0 + 1), data, 6 + 4); } - /* 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 colour space. For ITULab, we 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 colour space */ - compressor.in_color_space = JCS_YCbCr; - compressor.input_components = 3; - - jpeg_set_defaults(&compressor); - /* Limit to baseline-JPEG values */ - //jpeg_set_quality(&compressor, quality, TRUE); - - 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); - - set_itu_fax(&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); - -#if defined(HAVE_OPEN_MEMSTREAM) - fclose(out); - *dst = outptr; - *dstlen = outsize; -#else - *dstlen = ftell(out); - *dst = malloc(*dstlen); - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } - if (fread(*dst, 1, *dstlen, out) != *dstlen) - { - free(*dst); - fclose(out); - return FALSE; - } - fclose(out); -#endif - - return TRUE; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) t42_srgb_to_itulab(logging_state_t *logging, lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height) -{ - struct jpeg_compress_struct compressor; - FILE *out; - JSAMPROW scan_line_out; - JSAMPROW scan_line_in; - tsize_t pos; - escape_route_t escape; -#if defined(HAVE_OPEN_MEMSTREAM) - char *outptr; - size_t outsize; -#endif - - escape.error_message[0] = '\0'; - -#if defined(HAVE_OPEN_MEMSTREAM) - outsize = 0; - if ((out = open_memstream(&outptr, &outsize)) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); - return FALSE; - } - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } -#else - if ((out = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - return FALSE; - } -#endif - scan_line_out = NULL; - - if (setjmp(escape.escape)) - { - if (escape.error_message[0]) - span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message); - else - span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); - if (scan_line_out) - free(scan_line_out); - fclose(out); - 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 colour space */ - compressor.in_color_space = JCS_YCbCr; - compressor.input_components = 3; - - jpeg_set_defaults(&compressor); - /* Limit to baseline-JPEG values */ - //jpeg_set_quality(&compressor, quality, TRUE); - - /* Size, resolution, etc */ - compressor.image_width = width; - compressor.image_height = height; - - jpeg_start_compress(&compressor, TRUE); - - set_itu_fax(&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 = (JSAMPROW) 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); - -#if defined(HAVE_OPEN_MEMSTREAM) - fclose(out); - *dst = outptr; - *dstlen = outsize; -#else - *dstlen = ftell(out); - *dst = malloc(*dstlen); - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } - if (fread(*dst, 1, *dstlen, out) != *dstlen) - { - free(*dst); - fclose(out); - return FALSE; - } - fclose(out); -#endif - - return TRUE; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) t42_itulab_to_itulab(logging_state_t *logging, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height) -{ - struct jpeg_compress_struct compressor; - FILE *out; - JSAMPROW scan_line_in; - tsize_t pos; - escape_route_t escape; -#if defined(HAVE_OPEN_MEMSTREAM) - char *outptr; - size_t outsize; -#endif - - escape.error_message[0] = '\0'; - -#if defined(HAVE_OPEN_MEMSTREAM) - outsize = 0; - if ((out = open_memstream(&outptr, &outsize)) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); - return FALSE; - } - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } -#else - if ((out = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - return FALSE; - } -#endif - - if (setjmp(escape.escape)) - { - if (escape.error_message[0]) - span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message); - else - span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); - fclose(out); - 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 colour space */ - compressor.in_color_space = JCS_YCbCr; - compressor.input_components = 3; - - jpeg_set_defaults(&compressor); - /* Limit to baseline-JPEG values */ - //jpeg_set_quality(&compressor, quality, TRUE); - - /* Size, resolution, etc */ - compressor.image_width = width; - compressor.image_height = height; - - jpeg_start_compress(&compressor, TRUE); - - set_itu_fax(&compressor); - - for (pos = 0; pos < srclen; pos += compressor.image_width*compressor.num_components) - { - scan_line_in = (JSAMPROW) src + pos; - jpeg_write_scanlines(&compressor, &scan_line_in, 1); - } - - jpeg_finish_compress(&compressor); - jpeg_destroy_compress(&compressor); - -#if defined(HAVE_OPEN_MEMSTREAM) - fclose(out); - *dst = outptr; - *dstlen = outsize; -#else - *dstlen = ftell(out); - *dst = malloc(*dstlen); - if (fseek(out, 0, SEEK_SET) != 0) - { - fclose(out); - return FALSE; - } - if (fread(*dst, 1, *dstlen, out) != *dstlen) - { - free(*dst); - fclose(out); - return FALSE; - } - fclose(out); -#endif - - return TRUE; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) t42_itulab_to_srgb(logging_state_t *logging, lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height) -{ - 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'; - -#if defined(HAVE_OPEN_MEMSTREAM) - if ((in = fmemopen(src, srclen, "r")) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n"); - return FALSE; - } -#else - if ((in = tmpfile()) == NULL) - { - span_log(logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); - return FALSE; - } - if (fwrite(src, 1, srclen, in) != srclen) - { - fclose(in); - return FALSE; - } - if (fseek(in, 0, SEEK_SET) != 0) - { - fclose(in); - return FALSE; - } -#endif - scan_line_out = NULL; - - if (setjmp(escape.escape)) - { - if (escape.error_message[0]) - span_log(logging, SPAN_LOG_FLOW, "%s\n", escape.error_message); - else - span_log(logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); -printf("Error %s.\n", escape.error_message); - if (scan_line_out) - free(scan_line_out); - fclose(in); - 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, FALSE); - /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */ - decompressor.out_color_space = JCS_YCbCr; - /* Sanity check and parameter check */ - if (!is_itu_fax(logging, s, decompressor.marker_list)) - { - span_log(logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n"); -printf("Is not an ITU FAX 1.\n"); - //return FALSE; - } - /* Copy size, resolution, etc */ - *width = decompressor.image_width; - *height = decompressor.image_height; -printf("Is %d x %d\n", decompressor.image_width, decompressor.image_height); - - jpeg_start_decompress(&decompressor); - -printf("Is %d x %d x %d.\n", decompressor.output_width, decompressor.output_height, decompressor.num_components); - 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 = (JSAMPROW) dst + pos; - jpeg_read_scanlines(&decompressor, &scan_line_in, 1); - lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width); - } -printf("Next %d\n", decompressor.output_scanline); - - free(scan_line_in); - jpeg_finish_decompress(&decompressor); - jpeg_destroy_decompress(&decompressor); - fclose(in); - - *dstlen = pos; - -printf("Next2 %d\n", decompressor.output_scanline); - return TRUE; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s, uint32_t l0, - int mx, + int quality, int options) { + s->quality = quality; + s->no_subsampling = (options & 1); } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width) { + s->image_width = image_width; return 0; } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length) +SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t image_length) { + s->image_length = image_length; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) t42_encode_set_image_type(t42_encode_state_t *s, int image_type) +{ + s->image_type = image_type; return 0; } /*- End of function --------------------------------------------------------*/ @@ -1201,25 +701,201 @@ SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comme SPAN_DECLARE(int) t42_encode_image_complete(t42_encode_state_t *s) { + //if (????) + // return SIG_STATUS_END_OF_DATA; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +/* Error handler for IJG library */ +static void jpg_encode_error_exit(j_common_ptr cinfo) +{ + t42_encode_state_t *s; + + s = (t42_encode_state_t *) cinfo->client_data; + (*cinfo->err->format_message)(cinfo, s->error_message); + longjmp(s->escape, 1); +} +/*- End of function --------------------------------------------------------*/ + +/* This is the error catcher */ +static struct jpeg_error_mgr encode_error_handler = +{ + .error_exit = jpg_encode_error_exit, + .output_message = jpg_encode_error_exit +}; + +static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) +{ + int i; + + if (setjmp(s->escape)) + { + if (s->error_message[0]) + span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", s->error_message); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); + if (s->scan_line_out) + { + free(s->scan_line_out); + s->scan_line_out = NULL; + } + if (s->out) + { + fclose(s->out); + s->out = NULL; + } + return -1; + } + + s->compressor.err = jpeg_std_error(&encode_error_handler); + s->compressor.client_data = (void *) s; + + jpeg_create_compress(&s->compressor); + jpeg_stdio_dest(&s->compressor, s->out); + + /* Force the destination colour space */ + if (s->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) + { + s->samples_per_pixel = 3; + s->compressor.in_color_space = JCS_YCbCr; + s->compressor.input_components = s->samples_per_pixel; + } + else + { + s->samples_per_pixel = 1; + s->compressor.in_color_space = JCS_GRAYSCALE; + s->compressor.input_components = s->samples_per_pixel; + } + + jpeg_set_defaults(&s->compressor); + /* Limit to baseline-JPEG values */ + //jpeg_set_quality(&s->compressor, s->quality, TRUE); + + if (s->no_subsampling) + { + /* Set 1:1:1 */ + s->compressor.comp_info[0].h_samp_factor = 1; + s->compressor.comp_info[0].v_samp_factor = 1; + } + else + { + /* Set 4:1:1 */ + s->compressor.comp_info[0].h_samp_factor = 2; + s->compressor.comp_info[0].v_samp_factor = 2; + } + s->compressor.comp_info[1].h_samp_factor = 1; + s->compressor.comp_info[1].v_samp_factor = 1; + s->compressor.comp_info[2].h_samp_factor = 1; + s->compressor.comp_info[2].v_samp_factor = 1; + + /* Size, resolution, etc */ + s->compressor.image_width = s->image_width; + s->compressor.image_height = s->image_length; + + jpeg_start_compress(&s->compressor, TRUE); + + set_itu_fax(s); + + if ((s->scan_line_in = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL) + return -1; + + if (s->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) + { + if ((s->scan_line_out = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL) + return -1; + + for (i = 0; i < s->compressor.image_height; i++) + { + s->row_read_handler(s->row_read_user_data, s->scan_line_in, s->samples_per_pixel*s->image_width); + srgb_to_lab(&s->lab, s->scan_line_out, s->scan_line_in, s->image_width); + jpeg_write_scanlines(&s->compressor, &s->scan_line_out, 1); + } + } + else + { + for (i = 0; i < s->compressor.image_height; i++) + { + s->row_read_handler(s->row_read_user_data, s->scan_line_in, s->image_width); + jpeg_write_scanlines(&s->compressor, &s->scan_line_in, 1); + } + } + + if (s->scan_line_out) + { + free(s->scan_line_out); + s->scan_line_out = NULL; + } + jpeg_finish_compress(&s->compressor); + jpeg_destroy_compress(&s->compressor); + +#if defined(HAVE_OPEN_MEMSTREAM) + fclose(s->out); + s->buf_size = + s->compressed_image_size = s->outsize; +#else + s->buf_size = + s->compressed_image_size = ftell(s->out); + if ((s->compressed_buf = malloc(s->compressed_image_size)) == NULL) + return -1; + if (fseek(s->out, 0, SEEK_SET) != 0) + { + fclose(s->out); + s->out = NULL; + free(s->compressed_buf); + s->compressed_buf = NULL; + return -1; + } + if (fread(s->compressed_buf, 1, s->compressed_image_size, s->out) != s->compressed_image_size) + { + fclose(s->out); + s->out = NULL; + free(s->compressed_buf); + s->compressed_buf = NULL; + return -1; + } + if (s->out) + { + fclose(s->out); + s->out = NULL; + } +#endif + return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) t42_encode_get(t42_encode_state_t *s, uint8_t buf[], size_t max_len) { - return 0; + int len; + + if (s->compressed_image_size == 0) + { + if (t42_srgb_to_itulab_jpeg(s)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert to ITULAB.\n"); + return -1; + } + } + if (s->compressed_image_size >= s->compressed_image_ptr + max_len) + len = max_len; + else + len = s->compressed_image_size - s->compressed_image_ptr; + memcpy(buf, &s->compressed_buf[s->compressed_image_ptr], len); + s->compressed_image_ptr += len; + return len; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s) { - return 0; + return s->image_width; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s) { - return 0; + return s->image_length; } /*- End of function --------------------------------------------------------*/ @@ -1233,6 +909,8 @@ SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s, t4_row_read_handler_t handler, void *user_data) { + s->row_read_handler = handler; + s->row_read_user_data = user_data; return 0; } /*- End of function --------------------------------------------------------*/ @@ -1245,9 +923,52 @@ SPAN_DECLARE(logging_state_t *) t42_encode_get_logging_state(t42_encode_state_t SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length) { - //s->image_width = image_width; - //s->image_length = image_length; + s->image_width = image_width; + s->image_length = image_length; + + if (s->itu_ycc) + { + /* ITU-YCC */ + /* Illuminant D65 */ + set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, FALSE); + } + else + { + /* ITULAB */ + /* Illuminant D50 */ + set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE); + } s->compressed_image_size = 0; + s->compressed_image_ptr = 0; + + s->spatial_resolution = 200; + + s->error_message[0] = '\0'; + +#if defined(HAVE_OPEN_MEMSTREAM) + s->outsize = 0; + if ((s->out = open_memstream((char **) &s->compressed_buf, &s->outsize)) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); + return -1; + } + if (fseek(s->out, 0, SEEK_SET) != 0) + { + fclose(s->out); + s->out = NULL; + return -1; + } +#else + if ((s->out = tmpfile()) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); + return -1; + } +#endif + s->scan_line_out = NULL; + return 0; } /*- End of function --------------------------------------------------------*/ @@ -1267,8 +988,12 @@ SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s, span_log_init(&s->logging, SPAN_LOG_NONE, NULL); span_log_set_protocol(&s->logging, "T.42"); + s->quality = 90; + s->image_type = T4_IMAGE_TYPE_COLOUR_8BIT; + s->row_read_handler = handler; s->row_read_user_data = user_data; + t42_encode_restart(s, image_width, image_length); return s; @@ -1291,8 +1016,202 @@ SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s) } /*- End of function --------------------------------------------------------*/ +/* Error handler for IJG library */ +static void jpg_decode_error_exit(j_common_ptr cinfo) +{ + t42_decode_state_t *s; + + s = (t42_decode_state_t *) cinfo->client_data; + (*cinfo->err->format_message)(cinfo, s->error_message); + longjmp(s->escape, 1); +} +/*- End of function --------------------------------------------------------*/ + +/* This is the error catcher */ +static struct jpeg_error_mgr decode_error_handler = +{ + .error_exit = jpg_decode_error_exit, + .output_message = jpg_decode_error_exit +}; + +static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s) +{ + int i; + + if (s->compressed_buf == NULL) + return -1; + +#if defined(HAVE_OPEN_MEMSTREAM) + if ((s->in = fmemopen(s->compressed_buf, s->compressed_image_size, "r")) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to fmemopen().\n"); + return -1; + } +#else + if ((s->in = tmpfile()) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to tmpfile().\n"); + return -1; + } + if (fwrite(s->compressed_buf, 1, s->compressed_image_size, s->in) != s->compressed_image_size) + { + fclose(s->in); + s->in = NULL; + return -1; + } + if (fseek(s->in, 0, SEEK_SET) != 0) + { + fclose(s->in); + s->in = NULL; + return -1; + } +#endif + s->scan_line_out = NULL; + + if (setjmp(s->escape)) + { + if (s->error_message[0]) + span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", s->error_message); + else + span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); + if (s->scan_line_out) + { + free(s->scan_line_out); + s->scan_line_out = NULL; + } + if (s->in) + { + fclose(s->in); + s->in = NULL; + } + return -1; + } + /* Create input decompressor. */ + s->decompressor.err = jpeg_std_error(&decode_error_handler); + s->decompressor.client_data = (void *) s; + + jpeg_create_decompress(&s->decompressor); + jpeg_stdio_src(&s->decompressor, s->in); + + /* Get the FAX tags */ + for (i = 0; i < 16; i++) + jpeg_save_markers(&s->decompressor, JPEG_APP0 + i, 0xFFFF); + + /* Rewind the file */ + if (fseek(s->in, 0, SEEK_SET) != 0) + { + fclose(s->in); + s->in = NULL; + return -1; + } + + /* Take the header */ + jpeg_read_header(&s->decompressor, FALSE); + /* Sanity check and parameter check */ + if (!is_itu_fax(s, s->decompressor.marker_list)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Is not an ITU FAX.\n"); + return -1; + } + /* Copy size, resolution, etc */ + s->image_width = s->decompressor.image_width; + s->image_length = s->decompressor.image_height; + s->samples_per_pixel = s->decompressor.num_components; + + if (s->samples_per_pixel == 3) + { + /* Now we can force the input colour space. For ITULab, we use YCbCr as a "don't touch" marker */ + s->decompressor.out_color_space = JCS_YCbCr; + span_log(&s->logging, + SPAN_LOG_FLOW, + "Sampling %d %d %d %d %d %d\n", + s->decompressor.comp_info[0].h_samp_factor, + s->decompressor.comp_info[0].v_samp_factor, + s->decompressor.comp_info[1].h_samp_factor, + s->decompressor.comp_info[1].v_samp_factor, + s->decompressor.comp_info[2].h_samp_factor, + s->decompressor.comp_info[2].v_samp_factor); + } + else + { + s->decompressor.out_color_space = JCS_GRAYSCALE; + span_log(&s->logging, + SPAN_LOG_FLOW, + "Sampling %d %d\n", + s->decompressor.comp_info[0].h_samp_factor, + s->decompressor.comp_info[0].v_samp_factor); + } + + jpeg_start_decompress(&s->decompressor); + + if ((s->scan_line_in = malloc(s->samples_per_pixel*s->image_width)) == NULL) + return -1; + + if (s->samples_per_pixel == 3) + { + if ((s->scan_line_out = malloc(s->samples_per_pixel*s->image_width)) == NULL) + return -1; + + while (s->decompressor.output_scanline < s->image_length) + { + jpeg_read_scanlines(&s->decompressor, &s->scan_line_in, 1); + lab_to_srgb(&s->lab, s->scan_line_out, s->scan_line_in, s->image_width); + s->row_write_handler(s->row_write_user_data, s->scan_line_out, s->samples_per_pixel*s->image_width); + } + } + else + { + while (s->decompressor.output_scanline < s->image_length) + { + jpeg_read_scanlines(&s->decompressor, &s->scan_line_in, 1); + s->row_write_handler(s->row_write_user_data, s->scan_line_in, s->image_width); + } + } + + if (s->scan_line_in) + { + free(s->scan_line_in); + s->scan_line_in = NULL; + } + if (s->scan_line_out) + { + free(s->scan_line_out); + s->scan_line_out = NULL; + } + jpeg_finish_decompress(&s->decompressor); + jpeg_destroy_decompress(&s->decompressor); + fclose(s->in); + s->in = NULL; + + return 0; +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status) { + span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status); + switch (status) + { + case SIG_STATUS_TRAINING_IN_PROGRESS: + case SIG_STATUS_TRAINING_FAILED: + case SIG_STATUS_TRAINING_SUCCEEDED: + case SIG_STATUS_CARRIER_UP: + /* Ignore these */ + break; + case SIG_STATUS_CARRIER_DOWN: + case SIG_STATUS_END_OF_DATA: + /* Finalise the image */ + if (!s->end_of_data) + { + if (t42_itulab_jpeg_to_srgb(s)) + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n"); + s->end_of_data = 1; + } + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status); + break; + } } /*- End of function --------------------------------------------------------*/ @@ -1300,11 +1219,22 @@ SPAN_DECLARE(int) t42_decode_put(t42_decode_state_t *s, const uint8_t data[], si { uint8_t *buf; + if (len == 0) + { + if (!s->end_of_data) + { + if (t42_itulab_jpeg_to_srgb(s)) + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n"); + s->end_of_data = 1; + } + return T4_DECODE_OK; + } + if (s->compressed_image_size + len > s->buf_size) { - if ((buf = (uint8_t *) realloc(s->compressed_buf, s->compressed_image_size + 1000)) == NULL) + if ((buf = (uint8_t *) realloc(s->compressed_buf, s->compressed_image_size + len + 10000)) == NULL) return -1; - s->buf_size = s->compressed_image_size + 1000; + s->buf_size = s->compressed_image_size + len + 10000; s->compressed_buf = buf; } memcpy(&s->compressed_buf[s->compressed_image_size], data, len); @@ -1345,13 +1275,13 @@ SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s, SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s) { - return 0; + return s->image_width; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s) { - return 0; + return s->image_length; } /*- End of function --------------------------------------------------------*/ @@ -1369,7 +1299,26 @@ SPAN_DECLARE(logging_state_t *) t42_decode_get_logging_state(t42_decode_state_t SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s) { + if (s->itu_ycc) + { + /* ITU-YCC */ + /* Illuminant D65 */ + set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, FALSE); + } + else + { + /* ITULAB */ + /* Illuminant D50 */ + set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE); + } + + s->end_of_data = 0; s->compressed_image_size = 0; + + s->error_message[0] = '\0'; + return 0; } /*- End of function --------------------------------------------------------*/ @@ -1401,6 +1350,22 @@ SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s, SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s) { + if (s->scan_line_in) + { + free(s->scan_line_in); + s->scan_line_in = NULL; + } + if (s->scan_line_out) + { + free(s->scan_line_out); + s->scan_line_out = NULL; + } + jpeg_destroy_decompress(&s->decompressor); + if (s->in) + { + fclose(s->in); + s->in = NULL; + } if (s->comment) { free(s->comment); diff --git a/libs/spandsp/src/t42_t43_local.h b/libs/spandsp/src/t42_t43_local.h index 6c0fedef9c..45230e57d3 100644 --- a/libs/spandsp/src/t42_t43_local.h +++ b/libs/spandsp/src/t42_t43_local.h @@ -33,7 +33,7 @@ extern "C" { #endif -void set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4]); +int set_illuminant_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[4]); void set_gamut_from_code(logging_state_t *logging, lab_params_t *s, const uint8_t code[12]); diff --git a/libs/spandsp/src/t43.c b/libs/spandsp/src/t43.c index 2b123a8619..d81e8da25c 100644 --- a/libs/spandsp/src/t43.c +++ b/libs/spandsp/src/t43.c @@ -61,6 +61,7 @@ #include "spandsp/private/t42.h" #include "spandsp/private/t43.h" +#include "t43_gray_code_tables.h" #include "t42_t43_local.h" #if !defined(FALSE) @@ -74,19 +75,19 @@ SPAN_DECLARE(const char *) t43_image_type_to_str(int type) { switch (type) { - case 0: + case T43_IMAGE_TYPE_RGB_BILEVEL: return "1 bit/colour image (RGB primaries)"; - case 1: + case T43_IMAGE_TYPE_CMY_BILEVEL: return "1 bit/colour image (CMY primaries)"; - case 2: + case T43_IMAGE_TYPE_CMYK_BILEVEL: return "1 bit/colour image (CMYK primaries)"; - case 16: + case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE: return "Palettized colour image (CIELAB 8 bits/component precision table)"; - case 17: + case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE: return "Palettized colour image (CIELAB 12 bits/component precision table)"; - case 32: + case T43_IMAGE_TYPE_GRAY: return "Gray-scale image (using L*)"; - case 48: + case T43_IMAGE_TYPE_COLOUR: return "Continuous-tone colour image (CIELAB)"; } return "???"; @@ -129,9 +130,11 @@ static __inline__ int unpack_32(uint8_t *s, uint32_t value) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_t len) +static int t43_create_header(t43_encode_state_t *s, uint8_t data[], size_t len) { int pos; + int val[6]; + int bytes_per_entry; pos = 0; unpack_16(data, 0xFFA8); @@ -151,7 +154,7 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_ /* JBIG coding method (0) is the only possible value here */ data[pos] = 0; pos += 1; - data[pos] = 42; //image_type; + data[pos] = s->image_type; pos += 1; data[pos] = s->bit_planes[0]; pos += 1; @@ -181,38 +184,47 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_ pos += 2; memcpy(&data[pos], "G3FAX\1", 6); pos += 6; - unpack_16(&data[pos + 0], s->lab.offset_L); - unpack_16(&data[pos + 2], s->lab.range_L); - unpack_16(&data[pos + 4], s->lab.offset_a); - unpack_16(&data[pos + 6], s->lab.range_a); - unpack_16(&data[pos + 8], s->lab.offset_b); - unpack_16(&data[pos + 10], s->lab.range_b); + get_lab_gamut2(&s->lab, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]); + unpack_16(&data[pos + 0], val[0]); + unpack_16(&data[pos + 2], val[1]); + unpack_16(&data[pos + 4], val[2]); + unpack_16(&data[pos + 6], val[3]); + unpack_16(&data[pos + 8], val[4]); + unpack_16(&data[pos + 10], val[5]); pos += 12; } - if (s->lab.x_n != 0.9638f || s->lab.y_n != 1.0f || s->lab.z_n != 0.8245f) + if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0 + || + s->illuminant_colour_temperature > 0) { span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX2\n"); unpack_16(&data[pos], 0xFFE1); pos += 2; - unpack_16(&data[pos], 2 + 6 + 6); + unpack_16(&data[pos], 2 + 6 + 4); pos += 2; memcpy(&data[pos], "G3FAX\2", 6); pos += 6; - unpack_16(&data[pos], 0); - unpack_16(&data[pos + 2], 0); - unpack_16(&data[pos + 4], 0); - set_illuminant_from_code(&s->logging, &s->lab, &data[pos]); - pos += 6; + if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0) + { + memcpy(&data[pos], s->illuminant_code, 4); + } + else + { + memcpy(&data[pos], "CT", 2); + unpack_16(&data[pos + 2], s->illuminant_colour_temperature); + } + pos += 4; } #if 0 if (s->colour_map) { span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX3\n"); + bytes_per_entry = (table_id == 0) ? 1 : 2; unpack_16(&data[pos], 0xFFE3); pos += 2; - unpack_32(&data[pos], ???); + unpack_32(&data[pos], 2 + 6 + 2 + 4 + 3*s->colour_map_entries*bytes_per_entry); pos += 4; memcpy(&data[pos], "G3FAX\3", 6); pos += 6; @@ -221,7 +233,7 @@ SPAN_DECLARE(int) t43_create_header(t43_decode_state_t *s, uint8_t data[], size_ unpack_32(&data[pos], s->colour_map_entries); pos += 4; srgb_to_lab(&s->lab, &data[pos], s->colour_map, s->colour_map_entries); - pos += 3*s->colour_map_entries; + pos += 3*s->colour_map_entries*bytes_per_entry; } #endif @@ -257,6 +269,12 @@ SPAN_DECLARE(int) t43_encode_set_image_length(t43_encode_state_t *s, uint32_t im } /*- End of function --------------------------------------------------------*/ +SPAN_DECLARE(int) t43_encode_set_image_type(t43_encode_state_t *s, int image_type) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s) { } @@ -343,6 +361,9 @@ SPAN_DECLARE(t43_encode_state_t *) t43_encode_init(t43_encode_state_t *s, image_length, handler, user_data); + + s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE; + return s; } /*- End of function --------------------------------------------------------*/ @@ -390,9 +411,11 @@ SPAN_DECLARE(void) t43_decode_rx_status(t43_decode_state_t *s, int status) static void set_simple_colour_map(t43_decode_state_t *s, int code) { + int i; + switch (code) { - case 0: + case T43_IMAGE_TYPE_RGB_BILEVEL: /* Table 3/T.43 1 bit/colour image (using RGB primaries) */ memset(s->colour_map, 0, sizeof(s->colour_map)); /* Black */ @@ -417,7 +440,7 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code) s->colour_map[3*0xE0 + 2] = 0xF0; s->colour_map_entries = 256; break; - case 1: + case T43_IMAGE_TYPE_CMY_BILEVEL: /* Table 2/T.43 1 bit/colour image (using CMY primaries) */ memset(s->colour_map, 0, sizeof(s->colour_map)); /* White */ @@ -441,7 +464,7 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code) /* Black */ s->colour_map_entries = 256; break; - case 2: + case T43_IMAGE_TYPE_CMYK_BILEVEL: /* Table 1/T.43 1 bit/colour image (using CMYK primaries) */ memset(s->colour_map, 0, sizeof(s->colour_map)); /* White */ @@ -465,16 +488,26 @@ static void set_simple_colour_map(t43_decode_state_t *s, int code) /* Black */ s->colour_map_entries = 256; break; - case 16: + case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE: /* Palettized colour image (using CIELAB 8 bits/component precision table) */ + for (i = 0; i < 3*256; i += 3) + { + s->colour_map[i + 0] = i; + s->colour_map[i + 1] = i; + s->colour_map[i + 2] = i; + } + s->colour_map_entries = 256; break; - case 17: + case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE: /* Palettized colour image (using CIELAB 12 bits/component precision table) */ break; - case 32: + case T43_IMAGE_TYPE_GRAY: /* Gray-scale image (using L*) */ + for (i = 0; i < 256; i++) + s->colour_map[i] = i; + s->colour_map_entries = 256; break; - case 48: + case T43_IMAGE_TYPE_COLOUR: /* Continuous-tone colour image (using CIELAB) */ break; } @@ -490,9 +523,6 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_ uint8_t col[3]; int i; - /* Set defaults */ - set_lab_illuminant(&s->lab, 0.9638f, 1.0f, 0.8245f); - set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE); pos = 0; if (pack_16(&data[pos]) != 0xFFA8) return 0; @@ -520,48 +550,68 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_ { case 0: span_log(&s->logging, SPAN_LOG_FLOW, "Got G3FAX0\n"); - if (seg >= 6 + 10) + if (seg < 6 + 10) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", seg); + } + else { val[0] = pack_16(&data[pos + 6 + 0]); s->spatial_resolution = pack_16(&data[pos + 6 + 2]); val[2] = data[pos + 6 + 4]; - val[3] = data[pos + 6 + 5]; + s->image_type = data[pos + 6 + 5]; s->bit_planes[0] = data[pos + 6 + 6]; s->bit_planes[1] = data[pos + 6 + 7]; s->bit_planes[2] = data[pos + 6 + 8]; s->bit_planes[3] = data[pos + 6 + 9]; + if (s->image_type == T43_IMAGE_TYPE_GRAY) + { + s->samples_per_pixel = 1; + } + else if (s->image_type == T43_IMAGE_TYPE_CMYK_BILEVEL) + { + s->samples_per_pixel = 4; + } + else + { + s->samples_per_pixel = 3; + } span_log(&s->logging, SPAN_LOG_FLOW, - "Version %d, resolution %.2fdpi, coding method %d, type %s (%d), bit planes %d,%d,%d,%d\n", + "Version %d, resolution %ddpi, coding method %d, type %s (%d), bit planes %d,%d,%d,%d\n", val[0], - s->spatial_resolution/100.0f, + s->spatial_resolution, val[2], - t43_image_type_to_str(val[3]), - val[3], + t43_image_type_to_str(s->image_type), + s->image_type, s->bit_planes[0], s->bit_planes[1], s->bit_planes[2], s->bit_planes[3]); - set_simple_colour_map(s, val[3]); - } - else - { - span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", seg); + set_simple_colour_map(s, s->image_type); } break; case 1: span_log(&s->logging, SPAN_LOG_FLOW, "Set gamut\n"); - if (seg >= 6 + 12) - set_gamut_from_code(&s->logging, &s->lab, &data[pos + 6]); - else + if (seg < 6 + 12) + { span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", seg); + } + else + { + set_gamut_from_code(&s->logging, &s->lab, &data[pos + 6]); + } break; case 2: span_log(&s->logging, SPAN_LOG_FLOW, "Set illuminant\n"); - if (seg >= 6 + 4) - set_illuminant_from_code(&s->logging, &s->lab, &data[pos + 6]); - else + if (seg < 6 + 4) + { span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", seg); + } + else + { + s->illuminant_colour_temperature = set_illuminant_from_code(&s->logging, &s->lab, &data[pos + 6]); + } break; default: span_log(&s->logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[pos + 5], seg); @@ -588,7 +638,7 @@ static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_ case 0: /* 8 bit CIELAB */ s->colour_map_entries = pack_32(&data[pos + 8]); - span_log(&s->logging, SPAN_LOG_FLOW, " Entries %6d\n", s->colour_map_entries); + span_log(&s->logging, SPAN_LOG_FLOW, " Entries %6d (len %d)\n", s->colour_map_entries, seg); if (seg >= 12 + s->colour_map_entries*3) { lab_to_srgb(&s->lab, s->colour_map, &data[pos + 12], s->colour_map_entries); @@ -650,7 +700,7 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le if (s->buf == NULL) { - image_size = 3*s->t85.xd*s->t85.yd; + image_size = s->samples_per_pixel*s->t85.xd*s->t85.yd; if ((s->buf = malloc(image_size)) == NULL) return -1; memset(s->buf, 0, image_size); @@ -659,13 +709,25 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le for (i = 0; i < len; i++) { mask = 0x80; - for (j = 0; j < 24; j += 3) + if (s->samples_per_pixel == 1) { - if ((buf[i] & mask)) - s->buf[s->ptr + j] |= s->bit_plane_mask; - mask >>= 1; + for (j = 0; j < 8; j += s->samples_per_pixel) + { + if ((buf[i] & mask)) + s->buf[s->ptr + j] |= s->bit_plane_mask; + mask >>= 1; + } } - s->ptr += 3*8; + else + { + for (j = 0; j < s->samples_per_pixel*8; j += s->samples_per_pixel) + { + if ((buf[i] & mask)) + s->buf[s->ptr + j] |= s->bit_plane_mask; + mask >>= 1; + } + } + s->ptr += s->samples_per_pixel*8; } s->row++; return 0; @@ -724,17 +786,23 @@ SPAN_DECLARE(int) t43_decode_put(t43_decode_state_t *s, const uint8_t data[], si t85_decode_new_plane(&s->t85); } /* Apply the colour map, and produce the RGB data from the collected bit-planes */ - for (j = 0; j < total_len; j += 3) + if (s->samples_per_pixel == 1) { - i = s->buf[j]; - s->buf[j] = s->colour_map[3*i]; - s->buf[j + 1] = s->colour_map[3*i + 1]; - s->buf[j + 2] = s->colour_map[3*i + 2]; + for (j = 0; j < total_len; j += s->samples_per_pixel) + s->buf[j] = s->colour_map[s->buf[j]]; + } + else + { + for (j = 0; j < total_len; j += s->samples_per_pixel) + { + i = s->buf[j]; + s->buf[j] = s->colour_map[3*i]; + s->buf[j + 1] = s->colour_map[3*i + 1]; + s->buf[j + 2] = s->colour_map[3*i + 2]; + } } for (j = 0; j < s->t85.yd; j++) - { - s->row_write_handler(s->row_write_user_data, &s->buf[j*3*s->t85.xd], 3*s->t85.xd); - } + s->row_write_handler(s->row_write_user_data, &s->buf[j*s->samples_per_pixel*s->t85.xd], s->samples_per_pixel*s->t85.xd); return result; } /*- End of function --------------------------------------------------------*/ @@ -794,6 +862,17 @@ SPAN_DECLARE(logging_state_t *) t43_decode_get_logging_state(t43_decode_state_t SPAN_DECLARE(int) t43_decode_restart(t43_decode_state_t *s) { + /* ITULAB */ + /* Illuminant D50 */ + set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE); + + s->t85.min_bit_planes = 1; + s->t85.max_bit_planes = 8; + s->bit_plane_mask = 0x80; + s->current_bit_plane = -1; + s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE; + return t85_decode_restart(&s->t85); } /*- End of function --------------------------------------------------------*/ @@ -816,10 +895,16 @@ SPAN_DECLARE(t43_decode_state_t *) t43_decode_init(t43_decode_state_t *s, t85_decode_init(&s->t85, t85_row_write_handler, s); + /* ITULAB */ + /* Illuminant D50 */ + set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, FALSE); + s->t85.min_bit_planes = 1; s->t85.max_bit_planes = 8; s->bit_plane_mask = 0x80; s->current_bit_plane = -1; + s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE; return s; } diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 765f427cea..7723e14eb2 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -110,9 +110,9 @@ SPAN_DECLARE(const char *) t4_compression_to_str(int compression) case T4_COMPRESSION_T88: return "T.88"; case T4_COMPRESSION_T42_T81: - return "T.81"; + return "T.81+T.42"; case T4_COMPRESSION_SYCC_T81: - return "sYCC T.81"; + return "T.81+sYCC"; case T4_COMPRESSION_T43: return "T.43"; case T4_COMPRESSION_T45: @@ -130,14 +130,20 @@ SPAN_DECLARE(const char *) t4_image_type_to_str(int type) return "bi-level"; case T4_IMAGE_TYPE_COLOUR_BILEVEL: return "bi-level colour"; + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: + return "CMYK bi-level colour"; case T4_IMAGE_TYPE_GRAY_8BIT: return "8-bit gray scale"; case T4_IMAGE_TYPE_GRAY_12BIT: return "12-bit gray scale"; case T4_IMAGE_TYPE_COLOUR_8BIT: return "8-bit colour"; + case T4_IMAGE_TYPE_4COLOUR_8BIT: + return "CMYK 8-bit colour"; case T4_IMAGE_TYPE_COLOUR_12BIT: return "12-bit colour"; + case T4_IMAGE_TYPE_4COLOUR_12BIT: + return "CMYK 12-bit colour"; } return "???"; } @@ -751,9 +757,9 @@ static void select_tiff_compression(t4_rx_state_t *s, int output_image_type) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) +SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) { - switch (encoding) + switch (compression) { case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: @@ -765,12 +771,12 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) case T4_COMPRESSION_T6: break; default: - t4_t6_decode_init(&s->decoder.t4_t6, encoding, s->metadata.image_width, s->row_handler, s->row_handler_user_data); + t4_t6_decode_init(&s->decoder.t4_t6, compression, s->metadata.image_width, s->row_handler, s->row_handler_user_data); break; } - s->metadata.compression = encoding; + s->metadata.compression = compression; select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); - return t4_t6_decode_set_encoding(&s->decoder.t4_t6, encoding); + return t4_t6_decode_set_encoding(&s->decoder.t4_t6, compression); case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: switch (s->metadata.compression) @@ -787,7 +793,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) break; } select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); - s->metadata.compression = encoding; + s->metadata.compression = compression; return 0; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -799,7 +805,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) break; } select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); - s->metadata.compression = encoding; + s->metadata.compression = compression; return 0; #endif case T4_COMPRESSION_T42_T81: @@ -817,7 +823,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) t42_decode_set_image_size_constraints(&s->decoder.t42, T4_WIDTH_1200_A3, 0); break; } - s->metadata.compression = encoding; + s->metadata.compression = compression; select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT); return 0; #if defined(SPANDSP_SUPPORT_T43) @@ -834,7 +840,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) t43_decode_set_image_size_constraints(&s->decoder.t43, T4_WIDTH_1200_A3, 0); break; } - s->metadata.compression = encoding; + s->metadata.compression = compression; select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT); return 0; #endif @@ -847,7 +853,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int encoding) default: break; } - s->metadata.compression = encoding; + s->metadata.compression = compression; select_tiff_compression(s, T4_IMAGE_TYPE_COLOUR_8BIT); return 0; #endif diff --git a/libs/spandsp/src/t4_t6_encode.c b/libs/spandsp/src/t4_t6_encode.c index 00d3ce1d93..ddb8b33a06 100644 --- a/libs/spandsp/src/t4_t6_encode.c +++ b/libs/spandsp/src/t4_t6_encode.c @@ -57,8 +57,8 @@ #include "config.h" #endif -#include #include +#include #include #include #include @@ -983,13 +983,15 @@ SPAN_DECLARE(int) t4_t6_encode_set_encoding(t4_t6_encode_state_t *s, int encodin switch (encoding) { case T4_COMPRESSION_T6: + s->min_bits_per_row = 0; + /* Fall through */ case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T4_1D: s->encoding = encoding; /* Set this to the default value for the lowest resolution in the T.4 spec. */ s->max_rows_to_next_1d_row = 2; s->rows_to_next_1d_row = s->max_rows_to_next_1d_row - 1; - s->row_is_2d = FALSE; + s->row_is_2d = (s->encoding == T4_COMPRESSION_T6); return 0; } return -1; @@ -998,7 +1000,16 @@ SPAN_DECLARE(int) t4_t6_encode_set_encoding(t4_t6_encode_state_t *s, int encodin SPAN_DECLARE(void) t4_t6_encode_set_min_bits_per_row(t4_t6_encode_state_t *s, int bits) { - s->min_bits_per_row = bits; + switch (s->encoding) + { + case T4_COMPRESSION_T6: + s->min_bits_per_row = 0; + break; + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T4_1D: + s->min_bits_per_row = bits; + break; + } } /*- End of function --------------------------------------------------------*/ @@ -1056,9 +1067,12 @@ SPAN_DECLARE(void) t4_t6_encode_set_max_2d_rows_per_1d_row(t4_t6_encode_state_t } y_res_table[] = { {T4_Y_RESOLUTION_STANDARD, 2}, + {T4_Y_RESOLUTION_100, 2}, {T4_Y_RESOLUTION_FINE, 4}, + {T4_Y_RESOLUTION_200, 4}, {T4_Y_RESOLUTION_300, 6}, {T4_Y_RESOLUTION_SUPERFINE, 8}, + {T4_Y_RESOLUTION_400, 8}, {T4_Y_RESOLUTION_600, 12}, {T4_Y_RESOLUTION_800, 16}, {T4_Y_RESOLUTION_1200, 24}, @@ -1094,7 +1108,7 @@ SPAN_DECLARE(logging_state_t *) t4_t6_encode_get_logging_state(t4_t6_encode_stat } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width) +SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width, int image_length) { /* Allow for pages being of different width. */ t4_t6_encode_set_image_width(s, image_width); @@ -1125,6 +1139,7 @@ SPAN_DECLARE(int) t4_t6_encode_restart(t4_t6_encode_state_t *s, int image_width) SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s, int encoding, int image_width, + int image_length, t4_row_read_handler_t handler, void *user_data) { @@ -1142,7 +1157,7 @@ SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s, s->row_read_user_data = user_data; s->max_rows_to_next_1d_row = 2; - t4_t6_encode_restart(s, image_width); + t4_t6_encode_restart(s, image_width, image_length); return s; } diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 02fa125dc6..90b20aeacc 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -29,8 +29,8 @@ #include "config.h" #endif -#include #include +#include #include #include #include @@ -91,6 +91,7 @@ typedef struct uint8_t *buf; int ptr; int row; + int size; int bit_mask; } packer_t; @@ -100,7 +101,7 @@ typedef struct int code; } res_table_t; -static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length); +static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length); static const res_table_t x_res_table[] = { @@ -1102,7 +1103,7 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding) case T4_COMPRESSION_T6: break; default: - t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->metadata.image_width, s->row_handler, s->row_handler_user_data); + t4_t6_encode_init(&s->encoder.t4_t6, encoding, s->metadata.image_width, s->metadata.image_length, s->row_handler, s->row_handler_user_data); t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); break; } @@ -1227,7 +1228,7 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) } /*- End of function --------------------------------------------------------*/ -static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length) +static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length) { s->metadata.image_length = image_length; switch (s->metadata.compression) @@ -1495,7 +1496,7 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s) case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - t4_t6_encode_restart(&s->encoder.t4_t6, s->metadata.image_width); + t4_t6_encode_restart(&s->encoder.t4_t6, s->metadata.image_width, s->metadata.image_length); break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: diff --git a/libs/spandsp/tests/fax_tests.c b/libs/spandsp/tests/fax_tests.c index 5af1675361..49958571a9 100644 --- a/libs/spandsp/tests/fax_tests.c +++ b/libs/spandsp/tests/fax_tests.c @@ -845,6 +845,7 @@ int main(int argc, char *argv[]) | T4_SUPPORT_LENGTH_US_LETTER | T4_SUPPORT_LENGTH_US_LEGAL | T4_SUPPORT_LENGTH_UNLIMITED); +#if 0 t30_set_supported_bilevel_resolutions(t30_state[i], T4_SUPPORT_RESOLUTION_R8_STANDARD | T4_SUPPORT_RESOLUTION_R8_FINE @@ -860,7 +861,36 @@ int main(int argc, char *argv[]) | T4_SUPPORT_RESOLUTION_600_600 | T4_SUPPORT_RESOLUTION_600_1200 | T4_SUPPORT_RESOLUTION_1200_1200); +#elif 0 + t30_set_supported_bilevel_resolutions(t30_state[i], + T4_SUPPORT_RESOLUTION_R8_STANDARD + | T4_SUPPORT_RESOLUTION_R8_FINE + | T4_SUPPORT_RESOLUTION_R8_SUPERFINE + | T4_SUPPORT_RESOLUTION_R16_SUPERFINE); +#else + t30_set_supported_bilevel_resolutions(t30_state[i], + T4_SUPPORT_RESOLUTION_200_100 + | T4_SUPPORT_RESOLUTION_200_200 + | T4_SUPPORT_RESOLUTION_200_400 + | T4_SUPPORT_RESOLUTION_300_300 + | T4_SUPPORT_RESOLUTION_300_600 + | T4_SUPPORT_RESOLUTION_400_400 + | T4_SUPPORT_RESOLUTION_400_800 + | T4_SUPPORT_RESOLUTION_600_600 + | T4_SUPPORT_RESOLUTION_600_1200 + | T4_SUPPORT_RESOLUTION_1200_1200); +#endif +#if 1 t30_set_supported_colour_resolutions(t30_state[i], 0); +#else + t30_set_supported_colour_resolutions(t30_state[i], + T4_SUPPORT_RESOLUTION_100_100 + | T4_SUPPORT_RESOLUTION_200_200 + | T4_SUPPORT_RESOLUTION_300_300 + | T4_SUPPORT_RESOLUTION_400_400 + | T4_SUPPORT_RESOLUTION_600_600 + | T4_SUPPORT_RESOLUTION_1200_1200); +#endif t30_set_supported_output_compressions(t30_state[i], T4_SUPPORT_COMPRESSION_T4_2D); t30_set_ecm_capability(t30_state[i], use_ecm); t30_set_supported_compressions(t30_state[i], diff --git a/libs/spandsp/tests/t4_t6_tests.c b/libs/spandsp/tests/t4_t6_tests.c index bb67643422..c735557fb7 100644 --- a/libs/spandsp/tests/t4_t6_tests.c +++ b/libs/spandsp/tests/t4_t6_tests.c @@ -331,7 +331,7 @@ int main(int argc, char *argv[]) #if 1 printf("Testing image_function->compress->decompress->image_function\n"); /* Send end gets image from a function */ - if ((send_state = t4_t6_encode_init(NULL, compression, 1728, row_read_handler, NULL)) == NULL) + if ((send_state = t4_t6_encode_init(NULL, compression, 1728, -1, row_read_handler, NULL)) == NULL) { printf("Failed to init T.4/T.6 encoder\n"); exit(2); @@ -364,7 +364,7 @@ int main(int argc, char *argv[]) t4_t6_encode_set_encoding(send_state, compression); t4_t6_decode_set_encoding(receive_state, compression); - if (t4_t6_encode_restart(send_state, 1728)) + if (t4_t6_encode_restart(send_state, 1728, -1)) break; if (t4_t6_decode_restart(receive_state, 1728)) break;