From df669f700ae685f19cd8a569882066139898513e Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 6 Aug 2013 02:38:06 +0800 Subject: [PATCH] A cleanup of FAX image size/resolution matching accuracy. Squashing of bi-level images should now be correct. This is now close to supporting grey scale and colour. --- libs/spandsp/INSTALL | 397 ++++--- libs/spandsp/spandsp-sim/Makefile.am | 2 +- libs/spandsp/src/Makefile.am | 2 +- libs/spandsp/src/spandsp/t4_tx.h | 17 - libs/spandsp/src/t30.c | 414 +++---- libs/spandsp/src/t4_rx.c | 254 +++-- libs/spandsp/src/t4_tx.c | 1457 ++++++++++++++++++++---- libs/spandsp/tests/Makefile.am | 2 +- libs/spandsp/tests/regression_tests.sh | 9 + libs/spandsp/tests/t31_tests.c | 4 +- libs/spandsp/tests/t4_tests.c | 3 - libs/spandsp/tests/tsb85_tests.c | 65 +- 12 files changed, 1860 insertions(+), 766 deletions(-) diff --git a/libs/spandsp/INSTALL b/libs/spandsp/INSTALL index 2abc70492a..007e9396d0 100644 --- a/libs/spandsp/INSTALL +++ b/libs/spandsp/INSTALL @@ -1,121 +1,80 @@ -Building and installing spandsp -=============================== - -A number of distributions include spandsp, but they usually use older versions -of the library, which lack a lot of the features of the current version. Before -installing spandsp, make sure there are no older versions already on your -machine. Make sure libtiff is installed on your machine. Versions 3.5.7, -3.6.0, 3.7.1 and 3.8.2 seem to work OK. There have been several bugs related -to FAX document handling in some versions of libtiff. Also, some people have -had trouble using spandsp because they had more than one version of libtiff -on their machine. Take care with this. If you are using an RPM based system, -such as RedHat or Fedora, you will need the libtiff and libtiff-devel RPMs -installed to be able to build spandsp. - -You can use the usual: - - ./configure - make - make install - -process to build the spandsp library. Note that if you use configure in this -way, the software will be installed in /usr/local. In this case make sure your -/etc/ld.so.conf file has an entry for /usr/local/lib. If you wish the software -to be installed in /usr, you should build it with the commands. - - ./configure --prefix=/usr - make - make install - - -Building the programming documentation -====================================== - -If you wish to build the programming documentation for spandsp, configure -spandsp with: - - ./configure --enable-doc - -You need doxygen installed on your machine. - - -Building the test suite -======================= - -Most sections of the spandsp library have an accompanying test program in the -test directory. If you wish to build these test programs, configure spandsp -with: - - ./configure --enable-tests - -To build these tests you will need libaudiofile installed on your machine. To -build the modem tests, with the GUI monitoring feature you will need Fltk 1.1.4 -or later, an audio meter module and a cartesian plotting module. Fltk may be -obtained from http://www.fltk.org. The audio meter module may be obtained from -http://www.soft-switch.org/downloads/Fl_Audio_Meter.tgz . The cartesian plotting -module may be obtained from http://134.226.68.29/fltk. However, there is no -suitable makefile supplied with that. You can find a version at -http://www.soft-switch.org/downloads/Fl_Cartesian.tgz which will build as a -Linux library. The actual code in both these versions is identical. -You need to have Fltk 1.1.4 or later installed before building the plotting -library. - - -Applications -============ - -Applications support for spandsp is built into packages such as Callweaver, -FreeSwitch and iaxmodem. Code to add spandsp based FAX support to Asterisk may -be found at http://sourceforge.net/projects/agx-ast-addons. - +Installation Instructions +************************* +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. Basic Installation ================== - These are generic installation instructions. + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, a file -`config.cache' that saves the results of its tests to speed up -reconfiguring, and a file `config.log' containing compiler output -(useful mainly for debugging `configure'). +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can -be considered for the next release. If at some point `config.cache' -contains results you don't want to keep, you may remove or edit it. +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. - The file `configure.ac' is used to create `configure' by a program -called `autoconf'. You only need `configure.ac' if you want to change -it or regenerate `configure' using a newer version of `autoconf'. + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. -The simplest way to compile this package is: + The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. + `./configure' to configure the package for your system. - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with - the package. + the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and - documentation. + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. - 5. You can remove the program binaries and object files from the + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is @@ -124,62 +83,119 @@ The simplest way to compile this package is: all sorts of other programs in order to regenerate files that came with the distribution. + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + Compilers and Options ===================== Some systems require unusual options for compilation or linking that -the `configure' script does not know about. You can give `configure' -initial values for variables by setting them in the environment. Using -a Bourne-compatible shell, you can do that on the command line like -this: - CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. -Or on systems that have the `env' program, you can do it like this: - env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the +own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. - If you have to use a `make' that does not supports the `VPATH' -variable, you have to compile the package for one architecture at a time -in the source code directory. After you have installed the package for -one architecture, use `make distclean' before reconfiguring for another -architecture. + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. Installation Names ================== - By default, `make install' will install the package's files in -`/usr/local/bin', `/usr/local/man', etc. You can specify an -installation prefix other than `/usr/local' by giving `configure' the -option `--prefix=PATH'. + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you -give `configure' the option `--exec-prefix=PATH', the package will use -PATH as the prefix for installing programs and libraries. -Documentation and other data files will still use the regular prefix. +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give -options like `--bindir=PATH' to specify different values for particular +options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. -Optional Features -================= - Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE @@ -192,25 +208,80 @@ find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + Specifying the System Type ========================== - There may be some features `configure' can not figure out -automatically, but needs to determine by the type of host the package -will run on. Usually `configure' can figure that out, but if it prints -a message saying it can not guess the host type, give it the -`--host=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name with three fields: + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + CPU-COMPANY-SYSTEM -See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the host type. +where SYSTEM can have one of these forms: - If you are building compiler tools for cross-compiling, you can also -use the `--target=TYPE' option to select the type of system they will -produce code for and the `--build=TYPE' option to select the type of -system on which you are compiling the package. + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. Sharing Defaults ================ @@ -223,19 +294,56 @@ default values for variables like `CC', `cache_file', and `prefix'. `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. -Operation Controls +Defining Variables ================== + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + `configure' recognizes the following options to control how it operates. -`--cache-file=FILE' - Use and save the results of the tests in FILE instead of - `./config.cache'. Set FILE to `/dev/null' to disable caching, for - debugging `configure'. - `--help' - Print a summary of the options to `configure', and exit. +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. `--quiet' `--silent' @@ -248,8 +356,15 @@ operates. Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. -`--version' - Print the version of Autoconf used to generate the `configure' - script, and exit. +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. -`configure' also accepts some other, not widely useful, options. +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/libs/spandsp/spandsp-sim/Makefile.am b/libs/spandsp/spandsp-sim/Makefile.am index d33575a55c..4eb86c4b49 100644 --- a/libs/spandsp/spandsp-sim/Makefile.am +++ b/libs/spandsp/spandsp-sim/Makefile.am @@ -34,7 +34,7 @@ EXTRA_DIST = libspandsp_sim.dsp \ msvc/vc9proj.head \ msvc/vc9proj.foot -INCLUDES = -I$(top_builddir) -I$(top_builddir)/src -DDATADIR="\"$(pkgdatadir)\"" +AM_CPPFLAGS = -I$(top_builddir) -I$(top_builddir)/src -DDATADIR="\"$(pkgdatadir)\"" noinst_PROGRAMS = make_line_models diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index a794e37fb5..7a28c27aad 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -78,7 +78,7 @@ EXTRA_DIST = floating_fudge.h \ spandsp/private/README \ spandsp/version.h.in -INCLUDES = -I$(top_builddir) +AM_CPPFLAGS = -I$(top_builddir) lib_LTLIBRARIES = libspandsp.la diff --git a/libs/spandsp/src/spandsp/t4_tx.h b/libs/spandsp/src/spandsp/t4_tx.h index b411892c78..41974c9fcf 100644 --- a/libs/spandsp/src/spandsp/t4_tx.h +++ b/libs/spandsp/src/spandsp/t4_tx.h @@ -323,12 +323,6 @@ SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, int supported_bilevel_resolutions, int supported_colour_resolutions); -/*! \brief Set the compression for the encoded data. - \param s The T.4 context. - \param encoding The encoding. - \return 0 for success, otherwise -1. */ -SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding); - /*! \brief Set the minimum number of encoded bits per row. This allows the makes the encoding process to be set to comply with the minimum row time specified by a remote receiving machine. @@ -336,11 +330,6 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding); \param bits The minimum number of bits per row. */ SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits); -/*! \brief Set the width of the image. - \param s The T.4 context. - \param image_width The image width, in pixels. */ -SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width); - /*! \brief Set the maximum number of 2D encoded rows between 1D encoded rows. This is only valid for T.4 2D encoding. \param s The T.4 context. @@ -381,12 +370,6 @@ SPAN_DECLARE(void) t4_tx_set_header_overlays_image(t4_tx_state_t *s, int header_ \return 0 for success, otherwise -1. */ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, void *user_data); -/*! \brief Set the row squashing ratio, for adjusting row-to-row (y) resolution of bi-level - images for a T.4 transmit context. - \param s The T.4 transmit context. - \param row_squashing_ratio Vertical squashing ratio. */ -SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio); - /*! \brief Get the number of pages in the file. \param s The T.4 context. \return The number of pages, or -1 if there is an error. */ diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index 484b627a2b..3a4a12acf5 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -1224,7 +1224,7 @@ int t30_build_dis_or_dtc(t30_state_t *s) // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T88_CAPABILITY_3); //} - //if ((s->supported_compressions & (T4_COMPRESSION_COLOUR | T4_COMPRESSION_GRAYSCALE))) + if ((s->supported_compressions & (T4_COMPRESSION_COLOUR | T4_COMPRESSION_GRAYSCALE))) { if ((s->supported_compressions & T4_COMPRESSION_COLOUR)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FULL_COLOUR_CAPABLE); @@ -1249,8 +1249,8 @@ int t30_build_dis_or_dtc(t30_state_t *s) if ((s->supported_compressions & T4_COMPRESSION_12BIT)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE); - //if ((s->supported_compressions & T4_COMPRESSION_NO_SUBSAMPLING)) - // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING); + if ((s->supported_compressions & T4_COMPRESSION_NO_SUBSAMPLING)) + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING); /* No custom illuminant */ /* No custom gamut range */ @@ -1423,8 +1423,6 @@ static int prune_dis_dtc(t30_state_t *s) static int build_dcs(t30_state_t *s) { int i; - int bad; - int row_squashing_ratio; int use_bilevel; int image_type; @@ -1523,229 +1521,21 @@ static int build_dcs(t30_state_t *s) break; } - /* Set the Y resolution bits */ - row_squashing_ratio = 1; - bad = T30_ERR_NORESSUPPORT; - if ((use_bilevel && (s->current_page_resolution & s->mutual_bilevel_resolutions)) - || - (!use_bilevel && (s->current_page_resolution & s->mutual_colour_resolutions))) - { - /* The resolution is supported by both parties */ - switch (s->current_page_resolution) - { - case T4_RESOLUTION_1200_1200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_1200_1200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200); - break; - case T4_RESOLUTION_600_1200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_1200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_600_600: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_600); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600); - break; - case T4_RESOLUTION_400_800: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_800); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_400_400: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); - break; - case T4_RESOLUTION_300_600: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_600); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_300_300: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); - break; - case T4_RESOLUTION_200_400: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_200_200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); - break; - case T4_RESOLUTION_200_100: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_100_100: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100); - break; - case T4_RESOLUTION_R16_SUPERFINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); - break; - case T4_RESOLUTION_R8_SUPERFINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); - break; - case T4_RESOLUTION_R8_FINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - break; - case T4_RESOLUTION_R8_STANDARD: - /* Nothing special to set */ - break; - } - bad = T30_ERR_OK; - } - else - { -#if 0 - /* Deal with resolution fudging */ - if ((s->current_page_resolution & (T4_RESOLUTION_R16_SUPERFINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_STANDARD))) - { - if ((s->mutual_bilevel_resolutions & (T4_RESOLUTION_400_400 | T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_100))) - { - /* Fudge between imperial and metric */ - } - } - else if ((s->current_page_resolution & (T4_RESOLUTION_400_400 | T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_100)) - { - if ((s->mutual_bilevel_resolutions & (T4_RESOLUTION_R16_SUPERFINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_STANDARD))) - { - /* Fudge between imperial and metric */ - } - } -#endif - /* Deal with squashing options */ - if ((s->current_page_resolution & T4_RESOLUTION_R8_SUPERFINE)) - { - if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_FINE)) - { - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_STANDARD)) - { - row_squashing_ratio = 4; - bad = T30_ERR_OK; - } - } - else if ((s->current_page_resolution & T4_RESOLUTION_R8_FINE) && (s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_STANDARD)) - { - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->current_page_resolution & T4_RESOLUTION_200_400)) - { - if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_200_200)) - { - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_200_100)) - { - row_squashing_ratio = 4; - bad = T30_ERR_OK; - } - } - else if ((s->current_page_resolution & T4_RESOLUTION_200_200) && (s->mutual_bilevel_resolutions & T4_RESOLUTION_200_100)) - { - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - } - - t4_tx_set_row_squashing_ratio(&s->t4.tx, row_squashing_ratio); - if (bad != T30_ERR_OK) - { - t30_set_status(s, bad); - span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution (%d x %d) not acceptable\n", s->x_resolution, s->y_resolution); - return -1; - } - - /* Deal with the image width. */ - /* Low (R4) res widths are not supported in recent versions of T.30 */ - bad = T30_ERR_OK; - /* 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. */ - if (((s->image_width == T4_WIDTH_200_A4) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) - || - ((s->image_width == T4_WIDTH_300_A4) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_A4) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_A4) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_A4) && (s->x_resolution == T4_X_RESOLUTION_1200))) + /* Set the image width */ + switch (s->line_width_code) { + case T4_SUPPORT_WIDTH_215MM: span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4 at %ddpm x %ddpm\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)) - || - ((s->image_width == T4_WIDTH_300_B4) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_B4) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_B4) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_B4) && (s->x_resolution == T4_X_RESOLUTION_1200))) - { - if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_255MM)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH); - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - } - else if (((s->image_width == T4_WIDTH_200_A3) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) - || - ((s->image_width == T4_WIDTH_300_A3) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_A3) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_A3) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_A3) && (s->x_resolution == T4_X_RESOLUTION_1200))) - { - if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_303MM)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH); - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - - if (bad != T30_ERR_OK) - { - t30_set_status(s, bad); - span_log(&s->logging, - SPAN_LOG_FLOW, - "Image width (%d pixels) and resolution (%d x %d) is not an acceptable\n", - s->image_width, - s->x_resolution, - s->y_resolution); - return -1; + break; + case T4_SUPPORT_WIDTH_255MM: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); + break; + case T4_SUPPORT_WIDTH_303MM: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); + break; } /* Set the image length */ @@ -1760,6 +1550,77 @@ static int build_dcs(t30_state_t *s) else if ((s->mutual_image_sizes & T4_SUPPORT_LENGTH_US_LEGAL)) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NORTH_AMERICAN_LEGAL); + /* Set the Y resolution bits */ + switch (s->current_page_resolution) + { + case T4_RESOLUTION_1200_1200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_1200_1200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200); + break; + case T4_RESOLUTION_600_1200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_1200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_600_600: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_600); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600); + break; + case T4_RESOLUTION_400_800: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_800); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_400_400: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); + break; + case T4_RESOLUTION_300_600: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_600); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_300_300: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); + break; + case T4_RESOLUTION_200_400: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_200_200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); + break; + case T4_RESOLUTION_200_100: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_100_100: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100); + break; + case T4_RESOLUTION_R16_SUPERFINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); + break; + case T4_RESOLUTION_R8_SUPERFINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); + break; + case T4_RESOLUTION_R8_FINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); + break; + case T4_RESOLUTION_R8_STANDARD: + /* Nothing special to set */ + break; + } + if (s->error_correcting_mode) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_ECM_MODE); @@ -1832,7 +1693,7 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) if (!s->error_correcting_mode) { /* Remove any compression schemes which need error correction to work. */ - s->mutual_compressions &= (0xF0000000 | T4_COMPRESSION_NONE | T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D); + s->mutual_compressions &= (0xFF800000 | T4_COMPRESSION_NONE | T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D); if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE)) s->mutual_compressions &= ~T4_COMPRESSION_T4_2D; } @@ -1866,8 +1727,8 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE)) s->mutual_compressions &= ~T4_COMPRESSION_12BIT; - //if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING)) - // ???? = T4_COMPRESSION_T42_T81_SUBSAMPLING; + if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING)) + s->mutual_compressions &= ~T4_COMPRESSION_NO_SUBSAMPLING; /* bit74 custom illuminant */ /* bit75 custom gamut range */ @@ -2087,7 +1948,7 @@ static int analyze_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING)) { - //???? = T4_COMPRESSION_T42_T81_SUBSAMPLING; + //???? = T4_COMPRESSION_NO_SUBSAMPLING; } if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_PREFERRED_HUFFMAN_TABLES)) @@ -2529,7 +2390,6 @@ static int send_dcs_sequence(t30_state_t *s, int start) /* Schedule training after the messages */ if (start) { - prune_dcs(s); set_state(s, T30_STATE_D); s->step = 0; } @@ -2654,22 +2514,12 @@ static void set_min_scan_time(t30_state_t *s) { case T4_Y_RESOLUTION_SUPERFINE: case T4_Y_RESOLUTION_400: - if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE)) - { - s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; - break; - } - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n"); - /* Fall through */ + s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; + break; case T4_Y_RESOLUTION_FINE: case T4_Y_RESOLUTION_200: - if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE)) - { - s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; - break; - } - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n"); - /* Fall through */ + s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; + break; case T4_Y_RESOLUTION_STANDARD: case T4_Y_RESOLUTION_100: s->min_scan_time_code = translate_min_scan_time[0][min_bits_field]; @@ -2689,6 +2539,8 @@ static void set_min_scan_time(t30_state_t *s) static int start_sending_document(t30_state_t *s) { + int res; + if (s->tx_file[0] == '\0') { /* There is nothing to send */ @@ -2703,27 +2555,68 @@ static int start_sending_document(t30_state_t *s) return -1; } s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX; - t4_tx_get_pages_in_file(&s->t4.tx); - t4_tx_set_tx_encoding(&s->t4.tx, s->line_compression); + t4_tx_set_local_ident(&s->t4.tx, s->tx_info.ident); t4_tx_set_header_info(&s->t4.tx, s->header_info); if (s->use_own_tz) t4_tx_set_header_tz(&s->t4.tx, &s->tz); - if (tx_start_page(s)) + t4_tx_get_pages_in_file(&s->t4.tx); + + if ((res = t4_tx_set_tx_image_format(&s->t4.tx, + s->mutual_compressions, + s->mutual_image_sizes, + s->mutual_bilevel_resolutions, + s->mutual_colour_resolutions)) < 0) { - span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n"); - t30_set_status(s, T30_ERR_FILEERROR); + switch (res) + { + case T4_IMAGE_FORMAT_INCOMPATIBLE: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image format\n"); + t30_set_status(s, T30_ERR_BADTIFFHDR); + break; + case T4_IMAGE_FORMAT_NOSIZESUPPORT: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image size\n"); + t30_set_status(s, T30_ERR_NOSIZESUPPORT); + break; + case T4_IMAGE_FORMAT_NORESSUPPORT: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image resolution\n"); + t30_set_status(s, T30_ERR_NORESSUPPORT); + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image format\n"); + t30_set_status(s, T30_ERR_BADTIFF); + break; + } return -1; } + s->line_image_type = t4_tx_get_tx_image_type(&s->t4.tx); + s->line_compression = t4_tx_get_tx_compression(&s->t4.tx); + s->image_width = t4_tx_get_tx_image_width(&s->t4.tx); + s->line_width_code = t4_tx_get_tx_image_width_code(&s->t4.tx); s->x_resolution = t4_tx_get_tx_x_resolution(&s->t4.tx); s->y_resolution = t4_tx_get_tx_y_resolution(&s->t4.tx); - s->image_width = t4_tx_get_tx_image_width(&s->t4.tx); - /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and - must be evaluated before the minimum scan row bits can be evaluated. */ + s->current_page_resolution = t4_tx_get_tx_resolution(&s->t4.tx); + + span_log(&s->logging, + SPAN_LOG_FLOW, + "Choose image type %s (%d), compression %s (%d)\n", + t4_image_type_to_str(s->line_image_type), + s->line_image_type, + t4_compression_to_str(s->line_compression), + s->line_compression); + + /* The minimum scan time to be used can't be evaluated until we know the Y resolution. */ set_min_scan_time(s); + if (tx_start_page(s)) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n"); + t30_set_status(s, T30_ERR_BADTIFFHDR); + return -1; + } + if (s->error_correcting_mode) { if (get_partial_ecm_page(s) == 0) @@ -2795,21 +2688,6 @@ static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) send_dcn(s); return -1; } - - /* Choose a compression scheme from amongst those mutually available */ - if ((s->mutual_compressions & T4_COMPRESSION_T85_L0)) - s->line_compression = T4_COMPRESSION_T85_L0; - else if ((s->mutual_compressions & T4_COMPRESSION_T85)) - s->line_compression = T4_COMPRESSION_T85; - else if ((s->mutual_compressions & T4_COMPRESSION_T6)) - s->line_compression = T4_COMPRESSION_T6; - else if ((s->mutual_compressions & T4_COMPRESSION_T4_2D)) - s->line_compression = T4_COMPRESSION_T4_2D; - else - s->line_compression = T4_COMPRESSION_T4_1D; - - span_log(&s->logging, SPAN_LOG_FLOW, "Choose compression %s (%d)\n", t4_compression_to_str(s->line_compression), s->line_compression); - if (s->phase_b_handler) { new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]); @@ -6825,7 +6703,7 @@ SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s, | T4_SUPPORT_LENGTH_UNLIMITED; /* Set the output encoding to something safe. Most things get 1D and 2D encoding right. Quite a lot get other things wrong. */ - s->supported_output_compressions = T4_COMPRESSION_T4_2D; + s->supported_output_compressions = T4_COMPRESSION_T4_2D | T4_COMPRESSION_T42_T81; s->local_min_scan_time_code = T30_MIN_SCAN_0MS; span_log_init(&s->logging, SPAN_LOG_NONE, NULL); span_log_set_protocol(&s->logging, "T.30"); diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 5a7bb6bc45..b7bed5dd18 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -234,11 +234,24 @@ static int set_tiff_directory_info(t4_rx_state_t *s) #endif #if defined(SPANDSP_SUPPORT_T42) case T4_COMPRESSION_T42_T81: + output_compression = COMPRESSION_JPEG; + bits_per_sample = 8; + if (t->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) + { + samples_per_pixel = 3; + photometric = PHOTOMETRIC_YCBCR; //PHOTOMETRIC_ITULAB; + } + else + { + samples_per_pixel = 1; + photometric = PHOTOMETRIC_MINISBLACK; + } + break; case T4_COMPRESSION_SYCC_T81: output_compression = COMPRESSION_JPEG; bits_per_sample = 8; samples_per_pixel = 3; - photometric = PHOTOMETRIC_ITULAB; + photometric = PHOTOMETRIC_YCBCR; break; #endif #if defined(SPANDSP_SUPPORT_T43) @@ -288,6 +301,13 @@ static int set_tiff_directory_info(t4_rx_state_t *s) TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB); + if (t->compression == T4_COMPRESSION_T42_T81) + { + TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 2, 2); + //TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 1, 1); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGQUALITY, 75); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } /* TIFFTAG_STRIPBYTECOUNTS and TIFFTAG_STRIPOFFSETS are added automatically */ x_resolution = s->metadata.x_resolution/100.0f; @@ -344,30 +364,32 @@ static int set_tiff_directory_info(t4_rx_state_t *s) is always one greater than the highest page number in the file. */ s->tiff.pages_in_file = s->current_page + 1; s->metadata.image_length = 0; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - /* We only get bad row info from pages received in non-ECM mode. */ - if (output_compression == COMPRESSION_CCITT_T4) + case 0: + s->metadata.image_length = 1024; + break; + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: + if ((s->metadata.compression & (T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D))) { - if (s->decoder.t4_t6.bad_rows) + /* We only get bad row info from pages received in non-ECM mode. */ + if (output_compression == COMPRESSION_CCITT_T4) { - TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->decoder.t4_t6.bad_rows); - TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->decoder.t4_t6.longest_bad_row_run); - TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED); - } - else - { - TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN); + if (s->decoder.t4_t6.bad_rows) + { + TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->decoder.t4_t6.bad_rows); + TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->decoder.t4_t6.longest_bad_row_run); + TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED); + } + else + { + TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN); + } } } - /* Fall through */ - case T4_COMPRESSION_T6: s->metadata.image_length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: s->metadata.image_length = t85_decode_get_image_length(&s->decoder.t85); break; #if defined(SPANDSP_SUPPORT_T88) @@ -401,6 +423,34 @@ static int set_tiff_directory_info(t4_rx_state_t *s) /* Create a placeholder for the global parameters IFD, to be filled in later */ TIFFSetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, 0); } + +#if 0 + /* Paletised image? */ + TIFFSetField(t->tiff_file, TIFFTAG_INDEXED, 1); + /* T.44 mode */ + TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 0); + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 2\n"); + { + float xxx[] = {20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0}; + TIFFSetField(t->tiff_file, TIFFTAG_DECODE, (uint16) 2*samples_per_pixel, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 3\n"); + { + uint16_t xxx[] = {12, 34, 45, 67}; + TIFFSetField(t->tiff_file, TIFFTAG_IMAGEBASECOLOR, (uint16_t) samples_per_pixel, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 4\n"); + TIFFSetField(t->tiff_file, TIFFTAG_T82OPTIONS, 0); + { + uint32_t xxx[] = {34, 56, 78, 90}; + TIFFSetField(t->tiff_file, TIFFTAG_STRIPROWCOUNTS, (uint16_t) 5, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 5\n"); + { + uint32_t xxx[] = {2, 3}; + TIFFSetField(t->tiff_file, TIFFTAG_IMAGELAYER, xxx); + } +#endif #endif return 0; } @@ -450,10 +500,10 @@ static int write_tiff_t85_image(t4_rx_state_t *s) if (buf_len < image_len + 65536) { buf_len += 65536; - if ((buf2 = realloc(buf, buf_len)) == NULL) + if ((buf2 = span_realloc(buf, buf_len)) == NULL) { if (buf) - free(buf); + span_free(buf); return -1; } buf = buf2; @@ -468,7 +518,7 @@ static int write_tiff_t85_image(t4_rx_state_t *s) return -1; } t85_encode_release(&t85); - free(buf); + span_free(buf); return 0; } /*- End of function --------------------------------------------------------*/ @@ -496,10 +546,10 @@ static int write_tiff_t43_image(t4_rx_state_t *s) if (buf_len < image_len + 65536) { buf_len += 65536; - if ((buf2 = realloc(buf, buf_len)) == NULL) + if ((buf2 = span_realloc(buf, buf_len)) == NULL) { if (buf) - free(buf); + span_free(buf); return -1; } buf = buf2; @@ -511,7 +561,7 @@ static int write_tiff_t43_image(t4_rx_state_t *s) if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, buf, image_len) < 0) span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file); t43_encode_release(&t43); - free(buf); + span_free(buf); return 0; } /*- End of function --------------------------------------------------------*/ @@ -525,49 +575,57 @@ static int write_tiff_image(t4_rx_state_t *s) #endif t = &s->tiff; - if (t->image_buffer == NULL || t->image_size <= 0) + if (s->pre_encoded_ptr <= 0 && (t->image_buffer == NULL || t->image_size <= 0)) return -1; /* Set up the TIFF directory info... */ set_tiff_directory_info(s); /* ...Put the directory in the file before the image data, to get them in the order specified for TIFF/F files... */ - if (!TIFFCheckpointDirectory(t->tiff_file)) - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page); + //if (!TIFFCheckpointDirectory(t->tiff_file)) + // span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page); /* ...and write out the image... */ - switch (t->compression) + if (s->pre_encoded_ptr > 0) { - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t85_image(s) < 0) - return -1; - break; + if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, s->pre_encoded_buf, s->pre_encoded_ptr) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file); + } + else + { + switch (t->compression) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t85_image(s) < 0) + return -1; + break; #if defined(SPANDSP_SUPPORT_T88) - case T4_COMPRESSION_T88: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t88_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T88: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t88_image(s) < 0) + return -1; + break; #endif #if defined(SPANDSP_SUPPORT_T43) - case T4_COMPRESSION_T43: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t43_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T43: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t43_image(s) < 0) + return -1; + break; #endif #if defined(SPANDSP_SUPPORT_T45) - case T4_COMPRESSION_T45: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t45_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T45: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t45_image(s) < 0) + return -1; + break; #endif - default: - /* Let libtiff do the compression */ - if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); - break; + default: + /* Let libtiff do the compression */ + if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + break; + } } /* ...then finalise the directory entry, and libtiff is happy. */ if (!TIFFWriteDirectory(t->tiff_file)) @@ -633,7 +691,7 @@ static int close_tiff_output_file(t4_rx_state_t *s) put in it. */ if (s->current_page == 0) remove(s->tiff.file); - free((char *) s->tiff.file); + span_free((char *) s->tiff.file); } s->tiff.file = NULL; return 0; @@ -646,7 +704,7 @@ static void tiff_rx_release(t4_rx_state_t *s) close_tiff_output_file(s); if (s->tiff.image_buffer) { - free(s->tiff.image_buffer); + span_free(s->tiff.image_buffer); s->tiff.image_buffer = NULL; s->tiff.image_size = 0; s->tiff.image_buffer_size = 0; @@ -673,10 +731,10 @@ SPAN_DECLARE(int) t4_rx_put(t4_rx_state_t *s, const uint8_t buf[], size_t len) if (s->pre_encoded_len < s->pre_encoded_ptr + 65536) { s->pre_encoded_len += 65536; - if ((buf2 = realloc(s->pre_encoded_buf, s->pre_encoded_len)) == NULL) + if ((buf2 = span_realloc(s->pre_encoded_buf, s->pre_encoded_len)) == NULL) { if (s->pre_encoded_buf) - free(s->pre_encoded_buf); + span_free(s->pre_encoded_buf); return -1; } s->pre_encoded_buf = buf2; @@ -757,11 +815,19 @@ static void select_tiff_compression(t4_rx_state_t *s, int output_image_type) else if ((s->supported_tiff_compressions & T4_COMPRESSION_T43)) s->tiff.compression = T4_COMPRESSION_T43; } + s->tiff.image_type = output_image_type; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) { +#if 0 + output_image_type = T4_IMAGE_TYPE_BILEVEL; + s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); + s->current_decoder = 0; + return 0; +#else switch (compression) { case T4_COMPRESSION_T4_1D: @@ -775,6 +841,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t4_t6_decode_init(&s->decoder.t4_t6, compression, s->metadata.image_width, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6; break; } s->metadata.compression = compression; @@ -789,14 +856,15 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t85_decode_init(&s->decoder.t85, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ t85_decode_set_image_size_constraints(&s->decoder.t85, T4_WIDTH_1200_A3, 0); break; } - select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); return 0; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -805,10 +873,12 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) case T4_COMPRESSION_T88: break; default: + t88_decode_init(&s->decoder.t88, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T88; break; } - select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); return 0; #endif case T4_COMPRESSION_T42_T81: @@ -820,6 +890,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t42_decode_init(&s->decoder.t42, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T42_T81; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ @@ -837,6 +908,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t43_decode_init(&s->decoder.t43, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T43; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ @@ -854,6 +926,8 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) case T4_COMPRESSION_T45: break; default: + t45_decode_init(&s->decoder.t45, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T45; break; } s->metadata.compression = compression; @@ -863,6 +937,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) } return -1; +#endif } /*- End of function --------------------------------------------------------*/ @@ -876,14 +951,11 @@ SPAN_DECLARE(int) t4_rx_set_row_write_handler(t4_rx_state_t *s, t4_row_write_han { s->row_handler = handler; s->row_handler_user_data = user_data; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: return t4_t6_decode_set_row_write_handler(&s->decoder.t4_t6, handler, user_data); - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: return t85_decode_set_row_write_handler(&s->decoder.t85, handler, user_data); #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -914,12 +986,11 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t t->image_y_resolution = s->metadata.y_resolution; t->x_resolution = s->metadata.x_resolution; t->y_resolution = s->metadata.y_resolution; + t->compression = s->metadata.compression; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t4_t6_decode_get_image_width(&s->decoder.t4_t6); t->length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); @@ -930,8 +1001,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t t->bad_rows = s->decoder.t4_t6.bad_rows; t->longest_bad_row_run = s->decoder.t4_t6.longest_bad_row_run; break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t85_decode_get_image_width(&s->decoder.t85); t->length = t85_decode_get_image_length(&s->decoder.t85); @@ -945,7 +1015,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t break; #endif case T4_COMPRESSION_T42_T81: - t->type = T4_IMAGE_TYPE_COLOUR_8BIT; //T4_IMAGE_TYPE_GRAY_8BIT; + t->type = T4_IMAGE_TYPE_GRAY_8BIT; //T4_IMAGE_TYPE_COLOUR_8BIT; t->width = t42_decode_get_image_width(&s->decoder.t42); t->length = t42_decode_get_image_length(&s->decoder.t42); t->image_type = t->type; @@ -976,21 +1046,18 @@ SPAN_DECLARE(int) t4_rx_start_page(t4_rx_state_t *s) { span_log(&s->logging, SPAN_LOG_FLOW, "Start rx page %d - compression %s\n", s->current_page, t4_compression_to_str(s->metadata.compression)); - switch (s->metadata.compression) + switch (s->current_decoder) { case 0: s->pre_encoded_ptr = 0; s->pre_encoded_len = 0; s->image_put_handler = NULL; break; - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: t4_t6_decode_restart(&s->decoder.t4_t6, s->metadata.image_width); s->image_put_handler = (t4_image_put_handler_t) t4_t6_decode_put; break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: t85_decode_restart(&s->decoder.t85); s->image_put_handler = (t4_image_put_handler_t) t85_decode_put; break; @@ -1036,7 +1103,7 @@ static int tiff_row_write_handler(void *user_data, const uint8_t buf[], size_t l { if (s->tiff.image_size + len >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_buffer_size + 100*len)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_buffer_size + 100*len)) == NULL) return -1; s->tiff.image_buffer_size += 100*len; s->tiff.image_buffer = t; @@ -1057,15 +1124,15 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s) if (s->image_put_handler) s->image_put_handler((void *) &s->decoder, NULL, 0); - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case 0: + length = s->pre_encoded_ptr; + break; + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: length = t85_decode_get_image_length(&s->decoder.t85); break; #if defined(SPANDSP_SUPPORT_T88) @@ -1100,6 +1167,7 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s) if (write_tiff_image(s) == 0) s->current_page++; s->tiff.image_size = 0; + s->pre_encoded_ptr = 0; } else { @@ -1142,6 +1210,7 @@ SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int s->metadata.y_resolution = T4_Y_RESOLUTION_FINE; s->current_page = 0; + s->current_decoder = 0; /* Default handler */ s->row_handler = tiff_row_write_handler; @@ -1153,7 +1222,7 @@ SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int if (open_tiff_output_file(s, file) < 0) { if (allocated) - free(s); + span_free(s); return NULL; } /* Save the file name for logging reports. */ @@ -1167,14 +1236,11 @@ SPAN_DECLARE(int) t4_rx_release(t4_rx_state_t *s) { if (s->tiff.file) tiff_rx_release(s); - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: return t4_t6_decode_release(&s->decoder.t4_t6); - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: return t85_decode_release(&s->decoder.t85); #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -1200,7 +1266,7 @@ SPAN_DECLARE(int) t4_rx_free(t4_rx_state_t *s) int ret; ret = t4_rx_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 9e8f3e678c..35d3632f60 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -96,41 +96,37 @@ typedef struct int bit_mask; } packer_t; -typedef struct -{ - float resolution; - int code; -} res_table_t; +static void t4_tx_set_image_type(t4_tx_state_t *s, int image_type); +static void set_image_width(t4_tx_state_t *s, uint32_t image_width); +static void set_image_length(t4_tx_state_t *s, uint32_t 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[] = +static const float x_res_table[] = { - { 100.0f/CM_PER_INCH, T4_X_RESOLUTION_100}, - { 102.0f/CM_PER_INCH, T4_X_RESOLUTION_R4}, - { 200.0f/CM_PER_INCH, T4_X_RESOLUTION_200}, - { 204.0f/CM_PER_INCH, T4_X_RESOLUTION_R8}, - { 300.0f/CM_PER_INCH, T4_X_RESOLUTION_300}, - { 400.0f/CM_PER_INCH, T4_X_RESOLUTION_400}, - { 408.0f/CM_PER_INCH, T4_X_RESOLUTION_R16}, - { 600.0f/CM_PER_INCH, T4_X_RESOLUTION_600}, - {1200.0f/CM_PER_INCH, T4_X_RESOLUTION_1200}, - { -1.00f, -1} + 100.0f*100.0f/CM_PER_INCH, + 102.0f*100.0f/CM_PER_INCH, + 200.0f*100.0f/CM_PER_INCH, + 204.0f*100.0f/CM_PER_INCH, + 300.0f*100.0f/CM_PER_INCH, + 400.0f*100.0f/CM_PER_INCH, + 408.0f*100.0f/CM_PER_INCH, + 600.0f*100.0f/CM_PER_INCH, + 1200.0f*100.0f/CM_PER_INCH, + -1.00f }; -static const res_table_t y_res_table[] = +static const float y_res_table[] = { - { 38.50f, T4_Y_RESOLUTION_STANDARD}, - { 100.0f/CM_PER_INCH, T4_Y_RESOLUTION_100}, - { 77.00f, T4_Y_RESOLUTION_FINE}, - { 200.0f/CM_PER_INCH, T4_Y_RESOLUTION_200}, - { 300.0f/CM_PER_INCH, T4_Y_RESOLUTION_300}, - { 154.00f, T4_Y_RESOLUTION_SUPERFINE}, - { 400.0f/CM_PER_INCH, T4_Y_RESOLUTION_400}, - { 600.0f/CM_PER_INCH, T4_Y_RESOLUTION_600}, - { 800.0f/CM_PER_INCH, T4_Y_RESOLUTION_800}, - {1200.0f/CM_PER_INCH, T4_Y_RESOLUTION_1200}, - { -1.00f, -1} + 38.50f*100.0f, + 100.0f*100.0f/CM_PER_INCH, + 77.00f*100.0f, + 200.0f*100.0f/CM_PER_INCH, + 300.0f*100.0f/CM_PER_INCH, + 154.00f*100.0f, + 400.0f*100.0f/CM_PER_INCH, + 600.0f*100.0f/CM_PER_INCH, + 800.0f*100.0f/CM_PER_INCH, + 1200.0f*100.0f/CM_PER_INCH, + -1.00f }; static const int resolution_map[10][9] = @@ -215,7 +211,65 @@ SPAN_DECLARE(void) TIFF_FX_init(void) /*- End of function --------------------------------------------------------*/ #endif -static int match_resolution(int res_unit, float actual, const res_table_t table[]) +static int code_to_x_resolution(int code) +{ + static const int xxx[] = + { + T4_X_RESOLUTION_R8, /* R8 x standard */ + T4_X_RESOLUTION_R8, /* R8 x fine */ + T4_X_RESOLUTION_R8, /* R8 x superfine */ + T4_X_RESOLUTION_R16, /* R16 x superfine */ + T4_X_RESOLUTION_100, /* 100x100 */ + T4_X_RESOLUTION_200, /* 200x100 */ + T4_X_RESOLUTION_200, /* 200x200 */ + T4_X_RESOLUTION_200, /* 200x400 */ + T4_X_RESOLUTION_300, /* 300x300 */ + T4_X_RESOLUTION_300, /* 300x600 */ + T4_X_RESOLUTION_400, /* 400x400 */ + T4_X_RESOLUTION_400, /* 400x800 */ + T4_X_RESOLUTION_600, /* 600x600 */ + T4_X_RESOLUTION_600, /* 600x1200 */ + T4_X_RESOLUTION_1200 /* 1200x1200 */ + }; + int entry; + + entry = top_bit(code); + if (entry < 0 || entry > 14) + return 0; + return xxx[entry]; +} +/*- End of function --------------------------------------------------------*/ + +static int code_to_y_resolution(int code) +{ + static const int yyy[] = + { + T4_Y_RESOLUTION_STANDARD, /* R8 x standard */ + T4_Y_RESOLUTION_FINE, /* R8 x fine */ + T4_Y_RESOLUTION_SUPERFINE, /* R8 x superfine */ + T4_Y_RESOLUTION_SUPERFINE, /* R16 x superfine */ + T4_Y_RESOLUTION_100, /* 100x100 */ + T4_Y_RESOLUTION_100, /* 200x100 */ + T4_Y_RESOLUTION_200, /* 200x200 */ + T4_Y_RESOLUTION_400, /* 200x400 */ + T4_Y_RESOLUTION_300, /* 300x300 */ + T4_Y_RESOLUTION_600, /* 300x600 */ + T4_Y_RESOLUTION_400, /* 400x400 */ + T4_Y_RESOLUTION_800, /* 400x800 */ + T4_Y_RESOLUTION_600, /* 600x600 */ + T4_Y_RESOLUTION_1200, /* 600x1200 */ + T4_Y_RESOLUTION_1200 /* 1200x1200 */ + }; + int entry; + + entry = top_bit(code); + if (entry < 0 || entry > 14) + return 0; + return yyy[entry]; +} +/*- End of function --------------------------------------------------------*/ + +static int match_resolution(float actual, const float table[]) { int i; int best_entry; @@ -225,16 +279,14 @@ static int match_resolution(int res_unit, float actual, const res_table_t table[ if (actual == 0.0f) return -1; - if (res_unit == RESUNIT_INCH) - actual /= CM_PER_INCH; best_ratio = 0.0f; best_entry = -1; - for (i = 0; table[i].code > 0; i++) + for (i = 0; table[i] > 0.0f; i++) { - if (actual > table[i].resolution) - ratio = table[i].resolution/actual; + if (actual > table[i]) + ratio = table[i]/actual; else - ratio = actual/table[i].resolution; + ratio = actual/table[i]; if (ratio > best_ratio) { best_entry = i; @@ -247,7 +299,51 @@ static int match_resolution(int res_unit, float actual, const res_table_t table[ } /*- End of function --------------------------------------------------------*/ -#if 0 //defined(SPANDSP_SUPPORT_TIFF_FX) +static int best_colour_resolution(float actual, int allowed_resolutions) +{ + static const struct + { + float resolution; + int resolution_code; + } x_res_table[] = + { + { 100.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_100_100}, + { 200.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_200_200}, + { 300.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_300_300}, + { 400.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_400_400}, + { 600.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_600_600}, + {1200.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_1200_1200}, + { -1.00f, -1} + }; + int i; + int best_entry; + float best_ratio; + float ratio; + + if (actual == 0.0f) + return -1; + + best_ratio = 0.0f; + best_entry = -1; + for (i = 0; x_res_table[i].resolution > 0.0f; i++) + { + if (!(allowed_resolutions & x_res_table[i].resolution_code)) + continue; + if (actual > x_res_table[i].resolution) + ratio = x_res_table[i].resolution/actual; + else + ratio = actual/x_res_table[i].resolution; + if (ratio > best_ratio) + { + best_entry = i; + best_ratio = ratio; + } + } + return x_res_table[best_entry].resolution_code; +} +/*- End of function --------------------------------------------------------*/ + +#if defined(SPANDSP_SUPPORT_TIFF_FX) static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) { int i; @@ -266,7 +362,7 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) /* TODO: This only allows for 8 bit deep maps */ span_log(&s->logging, SPAN_LOG_FLOW, "Got a colour map\n"); s->colour_map_entries = 1 << bits_per_sample; - if ((s->colour_map = realloc(s->colour_map, 3*s->colour_map_entries)) == NULL) + if ((s->colour_map = span_realloc(s->colour_map, 3*s->colour_map_entries)) == NULL) return -1; #if 0 /* Sweep the colormap in the proper order */ @@ -286,7 +382,7 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) s->colour_map[2*s->colour_map_entries + i] = (map_b[i] >> 8) & 0xFF; } #endif - lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, 256); + lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, s->colour_map_entries); for (i = 0; i < s->colour_map_entries; i++) span_log(&s->logging, SPAN_LOG_FLOW, "Map %3d - %5d %5d %5d\n", i, s->colour_map[3*i], s->colour_map[3*i + 1], s->colour_map[3*i + 2]); return 0; @@ -309,8 +405,18 @@ static int get_tiff_directory_info(t4_tx_state_t *s) }; char *u; char uu[10]; + float *fl_parms; uint64_t diroff; + float lmin; + float lmax; + float amin; + float amax; + float bmin; + float bmax; uint8_t parm8; +#endif +#if defined(TIFFTAG_INDEXED) + uint16_t parm16; #endif uint32_t parm32; int best_x_entry; @@ -321,6 +427,8 @@ static int get_tiff_directory_info(t4_tx_state_t *s) uint16_t bits_per_sample; uint16_t samples_per_pixel; uint16_t res_unit; + uint16_t YCbCrSubsample_horiz; + uint16_t YCbCrSubsample_vert; t = &s->tiff; bits_per_sample = 1; @@ -331,6 +439,8 @@ static int get_tiff_directory_info(t4_tx_state_t *s) t->image_type = T4_IMAGE_TYPE_BILEVEL; else if (samples_per_pixel == 3 && bits_per_sample == 1) t->image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; + else if (samples_per_pixel == 4 && bits_per_sample == 1) + t->image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; else if (samples_per_pixel == 1 && bits_per_sample == 8) t->image_type = T4_IMAGE_TYPE_GRAY_8BIT; else if (samples_per_pixel == 1 && bits_per_sample > 8) @@ -341,62 +451,111 @@ static int get_tiff_directory_info(t4_tx_state_t *s) t->image_type = T4_IMAGE_TYPE_COLOUR_12BIT; else return -1; -#if 0 - /* Limit ourselves to plain black and white pages */ - if (t->image_type != T4_IMAGE_TYPE_BILEVEL) - return -1; + +#if defined(TIFFTAG_INDEXED) + parm16 = 0; + if (TIFFGetField(t->tiff_file, TIFFTAG_INDEXED, &parm16)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Indexed %s (%u)\n", (parm16) ? "palette image" : "non-palette image", parm16); + if (parm16 == 1) + { + /* Its an indexed image, so its really a colour image, even though it may have only one sample per pixel */ + if (samples_per_pixel == 1 && bits_per_sample == 8) + t->image_type = T4_IMAGE_TYPE_COLOUR_8BIT; + else if (samples_per_pixel == 1 && bits_per_sample > 8) + t->image_type = T4_IMAGE_TYPE_COLOUR_12BIT; + } + } #endif + parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32); - t->image_width = - s->metadata.image_width = parm32; + t->image_width = parm32; parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGELENGTH, &parm32); - t->image_length = - s->metadata.image_length = parm32; + t->image_length = parm32; + x_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution); y_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution); res_unit = RESUNIT_INCH; TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit); + + t->x_resolution = x_resolution*100.0f; + t->y_resolution = y_resolution*100.0f; + if (res_unit == RESUNIT_INCH) + { + t->x_resolution /= CM_PER_INCH; + t->y_resolution /= CM_PER_INCH; + } + + if (((best_x_entry = match_resolution(t->x_resolution, x_res_table)) >= 0) + && + ((best_y_entry = match_resolution(t->y_resolution, y_res_table)) >= 0)) + { + t->resolution_code = resolution_map[best_y_entry][best_x_entry]; + } + else + { + t->resolution_code = 0; + } + t->photo_metric = PHOTOMETRIC_MINISWHITE; TIFFGetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, &t->photo_metric); - set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f); + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); t->compression = -1; TIFFGetField(t->tiff_file, TIFFTAG_COMPRESSION, &t->compression); + switch (t->compression) + { + case COMPRESSION_CCITT_T4: + span_log(&s->logging, SPAN_LOG_FLOW, "T.4\n"); + break; + case COMPRESSION_CCITT_T6: + span_log(&s->logging, SPAN_LOG_FLOW, "T.6\n"); + break; + case COMPRESSION_T85: + span_log(&s->logging, SPAN_LOG_FLOW, "T.85\n"); + break; +#if defined(SPANDSP_SUPPORT_T43) + case COMPRESSION_T43: + span_log(&s->logging, SPAN_LOG_FLOW, "T.43\n"); + break; +#endif + case COMPRESSION_JPEG: + span_log(&s->logging, SPAN_LOG_FLOW, "JPEG\n"); + if (t->photo_metric == PHOTOMETRIC_ITULAB) + span_log(&s->logging, SPAN_LOG_FLOW, "ITULAB\n"); + break; + case COMPRESSION_NONE: + span_log(&s->logging, SPAN_LOG_FLOW, "No compression\n"); + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected compression %d\n", t->compression); + break; + } + +#if defined(SPANDSP_SUPPORT_TIFF_FX) + read_colour_map(s, bits_per_sample); +#endif + + YCbCrSubsample_horiz = 0; + YCbCrSubsample_vert = 0; + if (TIFFGetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, &YCbCrSubsample_horiz, &YCbCrSubsample_vert)) + span_log(&s->logging, SPAN_LOG_FLOW, "Subsampling %d %d\n", YCbCrSubsample_horiz, YCbCrSubsample_vert); + t->fill_order = FILLORDER_LSB2MSB; - if (res_unit == RESUNIT_INCH) - t->x_resolution = x_resolution*100.0f/CM_PER_INCH; - else - t->x_resolution = x_resolution*100.0f; - /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */ - if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0) - best_x_entry = 3; - s->metadata.x_resolution = x_res_table[best_x_entry].code; - - if (res_unit == RESUNIT_INCH) - t->y_resolution = y_resolution*100.0f/CM_PER_INCH; - else - t->y_resolution = y_resolution*100.0f; - if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0) - best_y_entry = 0; - s->metadata.y_resolution = y_res_table[best_y_entry].code; - - s->metadata.resolution_code = resolution_map[best_y_entry][best_x_entry]; - - t4_tx_set_image_width(s, s->metadata.image_width); - t4_tx_set_image_length(s, s->metadata.image_length); - t4_tx_set_max_2d_rows_per_1d_row(s, -s->metadata.y_resolution); #if defined(SPANDSP_SUPPORT_TIFF_FX) if (TIFFGetField(t->tiff_file, TIFFTAG_PROFILETYPE, &parm32)) span_log(&s->logging, SPAN_LOG_FLOW, "Profile type %u\n", parm32); if (TIFFGetField(t->tiff_file, TIFFTAG_FAXPROFILE, &parm8)) span_log(&s->logging, SPAN_LOG_FLOW, "FAX profile %s (%u)\n", tiff_fx_fax_profiles[parm8], parm8); + if (TIFFGetField(t->tiff_file, TIFFTAG_CODINGMETHODS, &parm32)) span_log(&s->logging, SPAN_LOG_FLOW, "Coding methods 0x%x\n", parm32); if (TIFFGetField(t->tiff_file, TIFFTAG_VERSIONYEAR, &u)) @@ -408,11 +567,60 @@ static int get_tiff_directory_info(t4_tx_state_t *s) if (TIFFGetField(t->tiff_file, TIFFTAG_MODENUMBER, &parm8)) span_log(&s->logging, SPAN_LOG_FLOW, "Mode number %u\n", parm8); + switch (t->photo_metric) + { + case PHOTOMETRIC_ITULAB: +#if 1 + /* 8 bit version */ + lmin = 0.0f; + lmax = 100.0f; + amin = -21760.0f/255.0f; + amax = 21590.0f/255.0f; + bmin = -19200.0f/255.0f; + bmax = 31800.0f/255.0f; +#else + /* 12 bit version */ + lmin = 0.0f; + lmax = 100.0f; + amin = -348160.0f/4095.0f + amax = 347990.0f/4095.0f + bmin = -307200.0f/4095.0f + bmax = 511800.0f/4095.0f +#endif + break; + default: + lmin = 0.0f; + lmax = 0.0f; + amin = 0.0f; + amax = 0.0f; + bmin = 0.0f; + bmax = 0.0f; + break; + } + + if (TIFFGetField(t->tiff_file, TIFFTAG_DECODE, &parm16, &fl_parms)) + { + lmin = fl_parms[0]; + lmax = fl_parms[1]; + amin = fl_parms[2]; + amax = fl_parms[3]; + bmin = fl_parms[4]; + bmax = fl_parms[5]; + span_log(&s->logging, SPAN_LOG_FLOW, "Got decode tag %f %f %f %f %f %f\n", lmin, lmax, amin, amax, bmin, bmax); + } + + /* TIFFTAG_IMAGEBASECOLOR */ + + if (TIFFGetField(t->tiff_file, TIFFTAG_T82OPTIONS, &parm32)) + span_log(&s->logging, SPAN_LOG_FLOW, "T.82 options 0x%x\n", parm32); + + /* TIFFTAG_STRIPROWCOUNTS */ + /* TIFFTAG_IMAGELAYER */ + /* If global parameters are present they should only be on the first page of the file. However, as we scan the file we might as well look for them on any page. */ if (TIFFGetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, &diroff)) { - span_log(&s->logging, SPAN_LOG_FLOW, "Global parameters IFD at %" PRIu64 "\n", diroff); if (!TIFFReadCustomDirectory(t->tiff_file, diroff, &tiff_fx_field_array)) { span_log(&s->logging, SPAN_LOG_FLOW, "Global parameter read failed\n"); @@ -451,8 +659,6 @@ static int test_tiff_directory_info(t4_tx_state_t *s) uint16_t bits_per_sample; uint16_t samples_per_pixel; int image_type; - int best_x_entry; - int best_y_entry; float x_resolution; float y_resolution; t4_tx_tiff_state_t *t; @@ -466,6 +672,8 @@ static int test_tiff_directory_info(t4_tx_state_t *s) image_type = T4_IMAGE_TYPE_BILEVEL; else if (samples_per_pixel == 3 && bits_per_sample == 1) image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; + else if (samples_per_pixel == 4 && bits_per_sample == 1) + image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; else if (samples_per_pixel == 1 && bits_per_sample == 8) image_type = T4_IMAGE_TYPE_GRAY_8BIT; else if (samples_per_pixel == 1 && bits_per_sample > 8) @@ -476,18 +684,14 @@ static int test_tiff_directory_info(t4_tx_state_t *s) image_type = T4_IMAGE_TYPE_COLOUR_12BIT; else image_type = -1; -#if 0 - /* Limit ourselves to plain black and white pages */ - if (t->image_type != T4_IMAGE_TYPE_BILEVEL) - return -1; -#endif if (t->image_type != image_type) return 1; parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32); if (s->tiff.image_width != (int) parm32) - return 1; + return 2; + x_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution); y_resolution = 0.0f; @@ -495,16 +699,17 @@ static int test_tiff_directory_info(t4_tx_state_t *s) res_unit = RESUNIT_INCH; TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit); - /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */ - if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0) - return 1; - if (s->metadata.x_resolution != x_res_table[best_x_entry].code) - return 1; - - if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0) - return 1; - if (s->metadata.y_resolution != y_res_table[best_y_entry].code) - return 1; + x_resolution *= 100.0f; + y_resolution *= 100.0f; + if (res_unit == RESUNIT_INCH) + { + x_resolution /= CM_PER_INCH; + y_resolution /= CM_PER_INCH; + } + if (s->tiff.x_resolution != (int) x_resolution) + return 3; + if (s->tiff.y_resolution != (int) y_resolution) + return 4; return 0; } @@ -535,6 +740,19 @@ static int open_tiff_input_file(t4_tx_state_t *s, const char *file) } /*- End of function --------------------------------------------------------*/ +static int metadata_row_read_handler(void *user_data, uint8_t buf[], size_t len) +{ + t4_tx_state_t *s; + + s = (t4_tx_state_t *) user_data; + if (s->tiff.row >= s->metadata.image_length) + return 0; + memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); + s->tiff.row++; + return len; +} +/*- End of function --------------------------------------------------------*/ + static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; @@ -544,6 +762,11 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) s = (t4_tx_state_t *) user_data; if (s->tiff.row >= s->tiff.image_length) return 0; + if (s->tiff.image_buffer == NULL) + { + exit(2); + return 0; + } memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); s->tiff.row++; @@ -551,7 +774,7 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) far end will accept, we need to squash it down to size. */ for (i = 1; i < s->row_squashing_ratio && s->tiff.row < s->tiff.image_length; i++) { - for (j = 0; j < s->tiff.image_width/8; j++) + for (j = 0; j < len; j++) buf[j] |= s->tiff.image_buffer[s->tiff.row*len + j]; s->tiff.row++; } @@ -559,19 +782,52 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) } /*- End of function --------------------------------------------------------*/ -static int row_read(void *user_data, uint8_t buf[], size_t len) +static int translate_row_read2(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; + s = (t4_tx_state_t *) user_data; + memcpy(buf, &s->pack_buf[s->pack_ptr], len); + s->pack_ptr += len; + s->pack_row++; + return len; +} +/*- End of function --------------------------------------------------------*/ + +static int translate_row_read(void *user_data, uint8_t buf[], size_t len) +{ + t4_tx_state_t *s; + int i; + int j; + s = (t4_tx_state_t *) user_data; if (s->tiff.raw_row >= s->tiff.image_length) return 0; + if (TIFFReadScanline(s->tiff.tiff_file, buf, s->tiff.raw_row, 0) < 0) return 0; + s->tiff.raw_row++; + + /* If this is a bi-level image which is stretched more vertically than we are able + to send we need to squash it down to size. */ + for (i = 1; i < s->row_squashing_ratio; i++) + { +#if defined(_MSC_VER) + uint8_t *extra_buf = (uint8_t *) _alloca(len); +#else + uint8_t extra_buf[len]; +#endif + + if (TIFFReadScanline(s->tiff.tiff_file, extra_buf, s->tiff.raw_row, 0) < 0) + return 0; + s->tiff.raw_row++; + /* We know this is a bi-level image if we are squashing */ + for (j = 0; j < s->tiff.image_width/8; j++) + buf[j] |= extra_buf[s->tiff.image_width/8 + j]; + } if (s->apply_lab) lab_to_srgb(&s->lab_params, buf, buf, len/3); - s->tiff.raw_row++; return len; } /*- End of function --------------------------------------------------------*/ @@ -601,6 +857,35 @@ static int embedded_comment_handler(void *user_data, const uint8_t buf[], size_t } /*- End of function --------------------------------------------------------*/ +static int read_tiff_raw_image(t4_tx_state_t *s) +{ + int num_strips; + int total_len; + int len; + int i; + + num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); + total_len = 0; + for (i = 0; i < num_strips; i++) + total_len += TIFFRawStripSize(s->tiff.tiff_file, i); + if ((s->pre_encoded_buf = span_realloc(s->pre_encoded_buf, total_len)) == NULL) + return -1; + total_len = 0; + for (i = 0; i < num_strips; i++, total_len += len) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &s->pre_encoded_buf[total_len], len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); + return -1; + } + } + s->pre_encoded_len = total_len; + s->pre_encoded_ptr = 0; + return 0; +} +/*- End of function --------------------------------------------------------*/ + static int read_tiff_t85_image(t4_tx_state_t *s) { int biggest; @@ -628,9 +913,9 @@ static int read_tiff_t85_image(t4_tx_state_t *s) s->tiff.image_size = s->tiff.image_length*((s->tiff.image_width + 7)/8); if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - free(raw_data); + span_free(raw_data); return -1; } s->tiff.image_buffer_size = s->tiff.image_size; @@ -639,6 +924,7 @@ static int read_tiff_t85_image(t4_tx_state_t *s) pack.buf = s->tiff.image_buffer; pack.ptr = 0; + pack.size = s->tiff.image_size; pack.row = 0; t85_decode_init(&t85, packing_row_write_handler, &pack); t85_decode_set_comment_handler(&t85, 1000, embedded_comment_handler, s); @@ -650,7 +936,7 @@ static int read_tiff_t85_image(t4_tx_state_t *s) if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0) { span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); - free(raw_data); + span_free(raw_data); return -1; } result = t85_decode_put(&t85, raw_data, len); @@ -663,40 +949,55 @@ static int read_tiff_t85_image(t4_tx_state_t *s) len = t85_decode_get_compressed_image_size(&t85); span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->tiff.image_length); t85_decode_release(&t85); - free(raw_data); + span_free(raw_data); return 0; } /*- End of function --------------------------------------------------------*/ #if defined(SPANDSP_SUPPORT_T43) -static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) +static int read_tiff_t43_image(t4_tx_state_t *s) { + int biggest; int num_strips; - int total_len; int len; int i; - int total_image_len; - int image_size; - logging_state_t *logging; + int result; + uint8_t *t; uint8_t *raw_data; + logging_state_t *logging; t43_decode_state_t t43; packer_t pack; + uint16_t bits_per_sample; + uint16_t samples_per_pixel; + + bits_per_sample = 1; + TIFFGetField(s->tiff.tiff_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + samples_per_pixel = 3; + TIFFGetField(s->tiff.tiff_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); + + samples_per_pixel = 3; num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); - total_image_len = 0; + biggest = 0; for (i = 0; i < num_strips; i++) - total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i); - if ((raw_data = span_alloc(total_image_len)) == NULL) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if (len > biggest) + biggest = len; + } + if ((raw_data = span_alloc(biggest)) == NULL) return -1; - total_len = 0; - for (i = 0; i < num_strips; i++, total_len += len) + s->tiff.image_size = samples_per_pixel*s->tiff.image_width*s->tiff.image_length; + if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n"); + span_free(raw_data); return -1; } + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; } t43_decode_init(&t43, packing_row_write_handler, &pack); @@ -704,22 +1005,35 @@ static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) logging = t43_decode_get_logging_state(&t43); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); - image_size = 3*s->metadata.image_length*s->metadata.image_width; - if ((*buf = span_alloc(image_size)) == NULL) - return -1; - - pack.buf = *buf; + pack.buf = s->tiff.image_buffer; pack.ptr = 0; + pack.size = s->tiff.image_size; pack.row = 0; - t43_decode_put(&t43, raw_data, total_len); + + result = -1; + for (i = 0; i < num_strips; i++) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); + span_free(raw_data); + return -1; + } + result = t43_decode_put(&t43, raw_data, len); + if (result != T4_DECODE_MORE_DATA) + break; + } + if (result == T4_DECODE_MORE_DATA) + result = t43_decode_put(&t43, NULL, 0); + t43_decode_release(&t43); - free(raw_data); - return image_size; + span_free(raw_data); + return s->tiff.image_size; } /*- End of function --------------------------------------------------------*/ #endif -#if 0 static int read_tiff_t42_t81_image(t4_tx_state_t *s) { int total_len; @@ -763,7 +1077,7 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0) { span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); - free(raw_data); + span_free(raw_data); return -1; } } @@ -776,9 +1090,9 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) s->tiff.image_size = samples_per_pixel*s->tiff.image_width*s->tiff.image_length; if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - free(raw_data); + span_free(raw_data); return -1; } s->tiff.image_buffer_size = s->tiff.image_size; @@ -795,11 +1109,10 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) t42_decode_put(&t42, NULL, 0); t42_decode_release(&t42); - free(raw_data); + span_free(raw_data); return s->tiff.image_size; } /*- End of function --------------------------------------------------------*/ -#endif static int read_tiff_decompressed_image(t4_tx_state_t *s) { @@ -814,7 +1127,7 @@ static int read_tiff_decompressed_image(t4_tx_state_t *s) s->tiff.image_size = s->tiff.image_length*TIFFScanlineSize(s->tiff.tiff_file); if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) return -1; s->tiff.image_buffer_size = s->tiff.image_size; s->tiff.image_buffer = t; @@ -831,7 +1144,7 @@ static int read_tiff_decompressed_image(t4_tx_state_t *s) } } /* We might need to flip all the bits, so 1 = black and 0 = white. */ - if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL && s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) { span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file); for (i = 0; i < s->tiff.image_size; i++) @@ -849,50 +1162,160 @@ static int read_tiff_image(t4_tx_state_t *s) { int total_len; int i; + int len; uint8_t *t; - image_translate_state_t *translator; + + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + image_translate_restart(&s->translator, s->tiff.image_length); + s->metadata.image_length = image_translate_get_output_length(&s->translator); + } + else + { + s->metadata.image_length = s->tiff.image_length; + } + s->pack_buf = NULL; + s->pack_ptr = 0; + s->pack_row = 0; if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL) { - /* We need to dither this image down to pure black and white, possibly resizing it - along the way. */ - if ((translator = image_translate_init(NULL, T4_IMAGE_TYPE_BILEVEL, 1728, -1, s->tiff.image_type, s->metadata.image_width, s->metadata.image_length, row_read, s)) == NULL) - return -1; - s->metadata.image_width = image_translate_get_output_width(translator); - s->metadata.image_length = image_translate_get_output_length(translator); - s->metadata.x_resolution = T4_X_RESOLUTION_R8; - s->metadata.y_resolution = T4_Y_RESOLUTION_FINE; - s->metadata.resolution_code = T4_RESOLUTION_R8_FINE; - s->tiff.image_size = (s->metadata.image_width*s->metadata.image_length + 7)/8; - if (s->tiff.image_size >= s->tiff.image_buffer_size) + /* If colour/gray scale is supported we may be able to send the image as it is, perhaps after + a resizing. Otherwise we need to resize it, and squash it to a bilevel image. */ + if (s->tiff.compression == COMPRESSION_JPEG && s->tiff.photo_metric == PHOTOMETRIC_ITULAB) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) - return -1; - s->tiff.image_buffer_size = s->tiff.image_size; - s->tiff.image_buffer = t; + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (read_tiff_t42_t81_image(s) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } } - s->tiff.raw_row = 0; - switch (s->tiff.photo_metric) +#if defined(SPANDSP_SUPPORT_T43) + else if (s->tiff.compression == COMPRESSION_T43) { - case PHOTOMETRIC_CIELAB: - /* The default luminant is D50 */ - set_lab_illuminant(&s->lab_params, 0.96422f, 1.0f, 0.82521f); - set_lab_gamut(&s->lab_params, 0, 100, -128, 127, -128, 127, TRUE); - s->apply_lab = TRUE; - break; - case PHOTOMETRIC_ITULAB: - set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f); - set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); - s->apply_lab = TRUE; - break; - default: - s->apply_lab = FALSE; - break; + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if ((len = read_tiff_t43_image(s)) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } + } +#endif +#if defined(SPANDSP_SUPPORT_T45) + else if (s->tiff.compression == COMPRESSION_T45) + { + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (read_tiff_t45_image(s) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } + } +#endif + else + { + /* Let libtiff handle the decompression */ + TIFFSetField(s->tiff.tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + image_translate_set_row_read_handler(&s->translator, translate_row_read, s); + } + else + { + if (read_tiff_decompressed_image(s) < 0) + return -1; + } + } + set_image_width(s, s->metadata.image_width); + set_image_length(s, s->metadata.image_length); + t4_tx_set_image_type(s, s->metadata.image_type); + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + /* We need to dither this image down to pure black and white, possibly resizing it + along the way. */ + s->tiff.image_size = (s->metadata.image_width*s->metadata.image_length + 7)/8; + if (s->tiff.image_size >= s->tiff.image_buffer_size) + { + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + return -1; + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; + } + s->tiff.raw_row = 0; + switch (s->tiff.photo_metric) + { + case PHOTOMETRIC_CIELAB: + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab_params, 0, 100, -128, 127, -128, 127, TRUE); + s->apply_lab = TRUE; + break; + case PHOTOMETRIC_ITULAB: + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); + s->apply_lab = TRUE; + break; + default: + s->apply_lab = FALSE; + break; + } + total_len = 0; + for (i = 0; i < s->metadata.image_length; i++) + total_len += image_translate_row(&s->translator, &s->tiff.image_buffer[total_len], s->metadata.image_width/8); + image_translate_release(&s->translator); + s->row_handler = metadata_row_read_handler; + s->row_handler_user_data = (void *) s; + } + else + { + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + total_len = 0; + s->tiff.image_buffer = span_realloc(s->tiff.image_buffer, s->metadata.image_width*s->metadata.image_length*3); + for (i = 0; i < s->metadata.image_length; i++) + total_len += image_translate_row(&s->translator, &s->tiff.image_buffer[total_len], s->metadata.image_width); + image_translate_release(&s->translator); + s->row_handler = metadata_row_read_handler; + s->row_handler_user_data = (void *) s; + } + else + { + s->row_handler = tiff_row_read_handler; + s->row_handler_user_data = (void *) s; + } } - total_len = 0; - for (i = 0; i < s->metadata.image_length; i++) - total_len += image_translate_row(translator, &s->tiff.image_buffer[total_len], s->metadata.image_width/8); - image_translate_free(translator); } else { @@ -903,13 +1326,59 @@ static int read_tiff_image(t4_tx_state_t *s) slightly long one, but lets not bother. */ switch (s->tiff.compression) { - case COMPRESSION_T85: - /* Decode the whole image into a buffer */ - /* libtiff probably cannot decompress T.85, so we must handle it ourselves */ - /* Decode the whole image into a buffer */ - if (read_tiff_t85_image(s) < 0) - return -1; +#if defined(SPANDSP_SUPPORT_T88) + case COMPRESSION_T88: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T88: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* libtiff probably cannot decompress T.88, so we must handle it ourselves */ + /* Decode the whole image into a buffer */ + if (read_tiff_t88_image(s) < 0) + return -1; + break; + } break; +#endif + case COMPRESSION_T85: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* libtiff probably cannot decompress T.85, so we must handle it ourselves */ + /* Decode the whole image into a buffer */ + if (read_tiff_t85_image(s) < 0) + return -1; + break; + } + break; +#if 0 + case COMPRESSION_CCITT_T6: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T6: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* Decode the whole image into a buffer */ + /* Let libtiff handle the decompression */ + if (read_tiff_decompressed_image(s) < 0) + return -1; + break; + } + break; +#endif default: /* Decode the whole image into a buffer */ /* Let libtiff handle the decompression */ @@ -930,12 +1399,12 @@ static void tiff_tx_release(t4_tx_state_t *s) TIFFClose(s->tiff.tiff_file); s->tiff.tiff_file = NULL; if (s->tiff.file) - free((char *) s->tiff.file); + span_free((char *) s->tiff.file); s->tiff.file = NULL; } if (s->tiff.image_buffer) { - free(s->tiff.image_buffer); + span_free(s->tiff.image_buffer); s->tiff.image_buffer = NULL; s->tiff.image_size = 0; s->tiff.image_buffer_size = 0; @@ -1023,37 +1492,74 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) int pattern; int pos; int row; + int i; char *t; t4_tx_state_t *s; s = (t4_tx_state_t *) user_data; - switch (s->metadata.y_resolution) + switch (s->metadata.resolution_code) { - case T4_Y_RESOLUTION_1200: - y_repeats = 12; - break; - case T4_Y_RESOLUTION_800: - y_repeats = 8; - break; - case T4_Y_RESOLUTION_600: - y_repeats = 6; - break; - case T4_Y_RESOLUTION_SUPERFINE: - case T4_Y_RESOLUTION_400: - y_repeats = 4; - break; - case T4_Y_RESOLUTION_300: - y_repeats = 3; - break; - case T4_Y_RESOLUTION_FINE: - case T4_Y_RESOLUTION_200: - y_repeats = 2; - break; default: + case T4_RESOLUTION_100_100: + x_repeats = 1; y_repeats = 1; break; + case T4_RESOLUTION_R8_STANDARD: + case T4_RESOLUTION_200_100: + x_repeats = 2; + y_repeats = 1; + break; + case T4_RESOLUTION_R8_FINE: + case T4_RESOLUTION_200_200: + x_repeats = 2; + y_repeats = 2; + break; + case T4_RESOLUTION_300_300: + x_repeats = 3; + y_repeats = 3; + break; + case T4_RESOLUTION_R8_SUPERFINE: + case T4_RESOLUTION_200_400: + x_repeats = 2; + y_repeats = 4; + break; + case T4_RESOLUTION_R16_SUPERFINE: + case T4_RESOLUTION_400_400: + x_repeats = 4; + y_repeats = 4; + break; + case T4_RESOLUTION_400_800: + x_repeats = 4; + y_repeats = 8; + break; + case T4_RESOLUTION_300_600: + x_repeats = 3; + y_repeats = 6; + break; + case T4_RESOLUTION_600_600: + x_repeats = 6; + y_repeats = 6; + break; + case T4_RESOLUTION_600_1200: + x_repeats = 6; + y_repeats = 12; + break; + case T4_RESOLUTION_1200_1200: + x_repeats = 12; + y_repeats = 12; + break; + } + switch (s->metadata.width_code) + { + case T4_SUPPORT_WIDTH_215MM: + break; + case T4_SUPPORT_WIDTH_255MM: + x_repeats *= 2; + break; + case T4_SUPPORT_WIDTH_303MM: + x_repeats *= 3; + break; } - y_repeats /= s->row_squashing_ratio; if (s->header_overlays_image) { /* Read and dump a row of the real image, allowing for the possibility @@ -1064,16 +1570,60 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) return len; } } + t = s->header_text; row = s->header_row/y_repeats; pos = 0; - for (t = s->header_text; *t && pos <= len - 2; t++) + switch (s->metadata.image_type) { - pattern = header_font[(uint8_t) *t][row]; - buf[pos++] = (uint8_t) (pattern >> 8); - buf[pos++] = (uint8_t) (pattern & 0xFF); + case T4_IMAGE_TYPE_BILEVEL: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + buf[pos++] = (uint8_t) (pattern >> 8); + buf[pos++] = (uint8_t) (pattern & 0xFF); + } + if (pos < len) + memset(&buf[pos], 0, len - pos); + break; + case T4_IMAGE_TYPE_GRAY_8BIT: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + for (i = 0; i < 16; i++) + { + buf[pos + i] = (pattern & 0x8000) ? 0 : 0xFF; + pattern <<= 1; + } + pos += 16; + } + if (pos < len) + memset(&buf[pos], 0xFF, len - pos); + break; + case T4_IMAGE_TYPE_COLOUR_8BIT: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + for (i = 0; i < 16; i++) + { + buf[pos + 3*i + 0] = + buf[pos + 3*i + 1] = + buf[pos + 3*i + 2] = (pattern & 0x8000) ? 0 : 0xFF; + pattern <<= 1; + } + pos += 3*16; + } + if (pos < len) + memset(&buf[pos], 0xFF, len - pos); + break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: + case T4_IMAGE_TYPE_GRAY_12BIT: + case T4_IMAGE_TYPE_4COLOUR_8BIT: + case T4_IMAGE_TYPE_COLOUR_12BIT: + case T4_IMAGE_TYPE_4COLOUR_12BIT: + default: + memset(buf, 0xFF, len); } - while (pos < len) - buf[pos++] = 0; s->header_row++; if (s->header_row >= 16*y_repeats) { @@ -1084,12 +1634,6 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio) -{ - s->row_squashing_ratio = row_squashing_ratio; -} -/*- End of function --------------------------------------------------------*/ - SPAN_DECLARE(int) t4_tx_next_page_has_different_format(t4_tx_state_t *s) { span_log(&s->logging, SPAN_LOG_FLOW, "Checking for the existence of page %d\n", s->current_page + 1); @@ -1113,8 +1657,372 @@ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handl } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) +SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, + int supported_compressions, + int supported_image_sizes, + int supported_bilevel_resolutions, + int supported_colour_resolutions) { + static const struct + { + int width; + int width_code; + int res_code; /* Correct resolution code */ + int alt_res_code; /* Fallback resolution code, where a metric/inch swap is possible */ + } width_info[] = + { + { T4_WIDTH_100_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_100_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_100_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_300_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_300_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_300_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_600_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_600_1200, 0}, + { T4_WIDTH_600_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_600_1200, 0}, + { T4_WIDTH_600_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_600_1200, 0}, + {T4_WIDTH_1200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_1200_1200, 0}, + {T4_WIDTH_1200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_1200_1200, 0}, + {T4_WIDTH_1200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_1200_1200, 0}, + {0x7FFFFFFF, -1, -1, -1} + }; + + static const struct + { + int resolution; + struct + { + int resolution; + int squashing_factor; + } fallback[4]; + } squashable[4] = + { + { + T4_RESOLUTION_200_400, + { + {T4_RESOLUTION_200_200, 2}, + {T4_RESOLUTION_R8_FINE, 2}, + {T4_RESOLUTION_200_100, 4}, + {T4_RESOLUTION_R8_STANDARD, 4} + } + }, + { + T4_RESOLUTION_200_200, + { + {T4_RESOLUTION_200_100, 2}, + {T4_RESOLUTION_R8_STANDARD, 2}, + {0, 0}, + {0, 0} + } + }, + { + T4_RESOLUTION_R8_SUPERFINE, + { + {T4_RESOLUTION_R8_FINE, 2}, + {T4_RESOLUTION_200_200, 2}, + {T4_RESOLUTION_R8_STANDARD, 4}, + {T4_RESOLUTION_200_100, 4} + } + }, + { + T4_RESOLUTION_R8_FINE, + { + {T4_RESOLUTION_R8_STANDARD, 2}, + {T4_RESOLUTION_200_100, 2}, + {0, 0}, + {0, 0} + } + } + }; + + int i; + int j; + int entry; + int compression; + int res; + + compression = -1; + s->metadata.image_type = s->tiff.image_type; + if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Non-bi-level image\n"); + /* Can we send this page as it is? */ + if (supported_colour_resolutions + && + (supported_compressions & (T4_COMPRESSION_T42_T81 | T4_COMPRESSION_T43 | T4_COMPRESSION_T45 | T4_COMPRESSION_SYCC_T81)) + && + (((s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL || s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT || s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + && + (supported_compressions & T4_COMPRESSION_COLOUR)) + || + ((s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT || s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + && + (supported_compressions & T4_COMPRESSION_GRAYSCALE)))) + { + /* Gray-scale/colour is possible */ + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale/colour is allowed\n"); + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_COLOUR)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Colour is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + } + else if (s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_GRAYSCALE)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + } + /* Choose the best gray-scale/colour encoding available to us */ + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL && (supported_compressions & T4_COMPRESSION_T43)) + compression = T4_COMPRESSION_T43; + else if ((supported_compressions & T4_COMPRESSION_T42_T81)) + compression = T4_COMPRESSION_T42_T81; + else if ((supported_compressions & T4_COMPRESSION_T43)) + compression = T4_COMPRESSION_T43; + else if ((supported_compressions & T4_COMPRESSION_T45)) + compression = T4_COMPRESSION_T45; + else if ((supported_compressions & T4_COMPRESSION_SYCC_T81)) + compression = T4_COMPRESSION_SYCC_T81; + + //best_colour_resolution(s->tiff.x_resolution, supported_colour_resolutions); + } + else + { + /* Gray-scale/colour is not possible. Can we flatten the image to send it? */ + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale/colour is not allowed\n"); + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_COLOUR_TO_BILEVEL)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Flattening is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + s->metadata.image_type = T4_IMAGE_TYPE_BILEVEL; + } + else if (s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_GRAY_TO_BILEVEL)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Flattening is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + s->metadata.image_type = T4_IMAGE_TYPE_BILEVEL; + } + /* Squashing to a bi-level image is possible */ + span_log(&s->logging, SPAN_LOG_FLOW, "The image may be flattened to %d\n", s->metadata.image_type); + } + } + + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + /* Choose the best bi-level encoding available to us */ + if ((supported_compressions & T4_COMPRESSION_T85_L0)) + compression = T4_COMPRESSION_T85_L0; + else if ((supported_compressions & T4_COMPRESSION_T85)) + compression = T4_COMPRESSION_T85; + else if ((supported_compressions & T4_COMPRESSION_T6)) + compression = T4_COMPRESSION_T6; + else if ((supported_compressions & T4_COMPRESSION_T4_2D)) + compression = T4_COMPRESSION_T4_2D; + else + compression = T4_COMPRESSION_T4_1D; + } + + /* Deal with the image width/resolution combination. */ + /* Look for a pattern that matches the image */ + res = T4_IMAGE_FORMAT_NOSIZESUPPORT; + for (entry = 0; s->tiff.image_width >= width_info[entry].width; entry++) + { + if (s->tiff.image_width == width_info[entry].width && s->tiff.resolution_code == width_info[entry].res_code) + { + res = T4_IMAGE_FORMAT_OK; + break; + } + } + + s->metadata.width_code = width_info[entry].width_code; + + s->row_squashing_ratio = 1; + if (res == T4_IMAGE_FORMAT_OK) + { + /* We have a valid width/resolution combination */ + + /* Check if this width/resolution combination is supported */ + if (!(supported_image_sizes & width_info[entry].width_code)) + return T4_IMAGE_FORMAT_NOSIZESUPPORT; + + /* No resize necessary */ + s->metadata.image_width = s->tiff.image_width; + s->metadata.image_length = s->tiff.image_length; + + res = T4_IMAGE_FORMAT_NORESSUPPORT; + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + if ((width_info[entry].res_code & supported_bilevel_resolutions)) + { + /* We can use the resolution of the original image */ + s->metadata.resolution_code = s->tiff.resolution_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + else + { + /* The resolution is not supported, but there might be an approximation, or a length + squashing that might work. */ + if ((width_info[entry].alt_res_code & supported_bilevel_resolutions)) + { + /* We can do a metric/imperial swap, and have a usable resolution */ + span_log(&s->logging, + SPAN_LOG_FLOW, + "Image resolution %s falls back to %s\n", + t4_image_resolution_to_str(s->tiff.resolution_code), + t4_image_resolution_to_str(width_info[entry].alt_res_code)); + s->metadata.resolution_code = width_info[entry].alt_res_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + else + { + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + { + if ((s->tiff.resolution_code & (T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE))) + { + /* This might be a resolution we can squash down to something which is supported */ + for (i = 0; i < 4; i++) + { + if ((s->tiff.resolution_code & squashable[i].resolution)) + break; + } + if (i < 4) + { + /* This is a squashable resolution, so let's see if there is a valid + fallback we can squash the image to, scanning through the entries + in their order of preference. */ + for (j = 0; j < 4; j++) + { + if ((supported_bilevel_resolutions & squashable[i].fallback[j].resolution)) + { + span_log(&s->logging, + SPAN_LOG_FLOW, + "Image resolution %s falls back to %s\n", + t4_image_resolution_to_str(s->tiff.resolution_code), + t4_image_resolution_to_str(squashable[i].fallback[j].resolution)); + s->row_squashing_ratio = squashable[i].fallback[j].squashing_factor; + s->metadata.resolution_code = squashable[i].fallback[j].resolution; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + break; + } + } + } + } + } + } + } + /* If we have not succeeded in matching up the size and resolution, the next step will + depend on whether the original was a bi-level image. If it was we are stuck, as you can't + really resize those. If it was not, a resize might be possible */ + if (res != T4_IMAGE_FORMAT_OK) + { + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + return T4_IMAGE_FORMAT_NORESSUPPORT; + if (!(supported_compressions & T4_COMPRESSION_RESCALING)) + return T4_IMAGE_FORMAT_NORESSUPPORT; + } + /* TODO */ + } + else + { + if ((width_info[entry].res_code & supported_bilevel_resolutions)) + { + if ((s->tiff.resolution_code & supported_colour_resolutions)) + { + /* We can use the resolution of the original image */ + s->metadata.resolution_code = width_info[entry].res_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + } + } + } + else + { + /* Can we rework the image to fit? */ + /* We can't rework a bilevel image that fits none of the patterns */ + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + return T4_IMAGE_FORMAT_NORESSUPPORT; + res = T4_IMAGE_FORMAT_OK; + /* Any other kind of image might be resizable */ + s->metadata.image_width = T4_WIDTH_200_A4; + s->metadata.resolution_code = T4_RESOLUTION_200_200; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + } + + if (res != T4_IMAGE_FORMAT_OK) + return res; + + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (image_translate_init(&s->translator, s->metadata.image_type, s->metadata.image_width, -1, s->tiff.image_type, s->tiff.image_width, s->tiff.image_length, translate_row_read2, s) == NULL) + return T4_IMAGE_FORMAT_INCOMPATIBLE; + s->metadata.image_length = image_translate_get_output_length(&s->translator); + } + + if (compression != s->metadata.compression) { switch (compression) { @@ -1129,13 +2037,13 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; default: t4_t6_encode_init(&s->encoder.t4_t6, compression, 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; } s->metadata.compression = compression; + res = T4_IMAGE_FORMAT_OK; if (t4_t6_encode_set_encoding(&s->encoder.t4_t6, compression)) - return -1; - return s->metadata.compression; + res = -1; + break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: switch (s->metadata.compression) @@ -1148,7 +2056,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: switch (s->metadata.compression) @@ -1160,7 +2069,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif case T4_COMPRESSION_T42_T81: case T4_COMPRESSION_SYCC_T81: @@ -1174,7 +2084,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: switch (s->metadata.compression) @@ -1186,7 +2097,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif #if defined(SPANDSP_SUPPORT_T45) case T4_COMPRESSION_T45: @@ -1199,24 +2111,25 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif } } - return -1; -} -/*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits) -{ switch (s->metadata.compression) { case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - t4_t6_encode_set_min_bits_per_row(&s->encoder.t4_t6, bits); + t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); break; } + + set_image_width(s, s->metadata.image_width); + set_image_length(s, s->metadata.image_length); + t4_tx_set_image_type(s, s->metadata.image_type); + return res; } /*- End of function --------------------------------------------------------*/ @@ -1256,7 +2169,13 @@ SPAN_DECLARE(int) t4_tx_get_tx_image_width(t4_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) +SPAN_DECLARE(int) t4_tx_get_tx_image_width_code(t4_tx_state_t *s) +{ + return s->metadata.width_code; +} +/*- End of function --------------------------------------------------------*/ + +static void set_image_width(t4_tx_state_t *s, uint32_t image_width) { s->metadata.image_width = image_width; switch (s->metadata.compression) @@ -1293,11 +2212,16 @@ 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, uint32_t image_length) +static void set_image_length(t4_tx_state_t *s, uint32_t image_length) { s->metadata.image_length = image_length; switch (s->metadata.compression) { + case T4_COMPRESSION_T4_1D: + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T6: + t4_t6_encode_set_image_length(&s->encoder.t4_t6, image_length); + break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: t85_encode_set_image_length(&s->encoder.t85, image_length); @@ -1325,6 +2249,47 @@ static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length) } /*- End of function --------------------------------------------------------*/ +static void t4_tx_set_image_type(t4_tx_state_t *s, int image_type) +{ + s->metadata.image_type = image_type; + switch (s->metadata.compression) + { +#if defined(SPANDSP_SUPPORT_T88) + case T4_COMPRESSION_T88: + t88_encode_set_image_type(&s->encoder.t88, image_type); + break; +#endif + case T4_COMPRESSION_T42_T81: + case T4_COMPRESSION_SYCC_T81: + t42_encode_set_image_type(&s->encoder.t42, image_type); + break; +#if defined(SPANDSP_SUPPORT_T43) + case T4_COMPRESSION_T43: + t43_encode_set_image_type(&s->encoder.t43, image_type); + break; +#endif +#if defined(SPANDSP_SUPPORT_T45) + case T4_COMPRESSION_T45: + t45_encode_set_image_type(&s->encoder.t45, image_type); + break; +#endif + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits) +{ + switch (s->metadata.compression) + { + case T4_COMPRESSION_T4_1D: + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T6: + t4_t6_encode_set_min_bits_per_row(&s->encoder.t4_t6, bits); + break; + } +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(void) t4_tx_set_max_2d_rows_per_1d_row(t4_tx_state_t *s, int max) { switch (s->metadata.compression) @@ -1395,8 +2360,9 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t t->image_x_resolution = s->tiff.x_resolution; t->image_y_resolution = s->tiff.y_resolution; t->x_resolution = s->metadata.x_resolution; - t->y_resolution = s->metadata.y_resolution/s->row_squashing_ratio; + t->y_resolution = s->metadata.y_resolution; + t->type = s->metadata.image_type; t->compression = s->metadata.compression; switch (s->metadata.compression) @@ -1404,16 +2370,14 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t4_t6_encode_get_image_width(&s->encoder.t4_t6); - t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6)/s->row_squashing_ratio; + t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6); t->line_image_size = t4_t6_encode_get_compressed_image_size(&s->encoder.t4_t6)/8; break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: - t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t85_encode_get_image_width(&s->encoder.t85); - t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio; + t->length = t85_encode_get_image_length(&s->encoder.t85); t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8; break; #if defined(SPANDSP_SUPPORT_T88) @@ -1425,16 +2389,14 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t #endif case T4_COMPRESSION_T42_T81: case T4_COMPRESSION_SYCC_T81: - t->type = 0; t->width = t42_encode_get_image_width(&s->encoder.t42); - t->length = t42_encode_get_image_length(&s->encoder.t42)/s->row_squashing_ratio; + t->length = t42_encode_get_image_length(&s->encoder.t42); t->line_image_size = t42_encode_get_compressed_image_size(&s->encoder.t42)/8; break; #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: - t->type = 0; t->width = t43_encode_get_image_width(&s->encoder.t43); - t->length = t43_encode_get_image_length(&s->encoder.t43)/s->row_squashing_ratio; + t->length = t43_encode_get_image_length(&s->encoder.t43); t->line_image_size = t43_encode_get_compressed_image_size(&s->encoder.t43)/8; break; #endif @@ -1451,6 +2413,13 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s) { + if (s->pre_encoded_len > 0) + { + if (s->pre_encoded_ptr >= s->pre_encoded_len) + return SIG_STATUS_END_OF_DATA; + return 0; + } + switch (s->metadata.compression) { case T4_COMPRESSION_T4_1D: @@ -1482,7 +2451,21 @@ SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s) SPAN_DECLARE(int) t4_tx_get_bit(t4_tx_state_t *s) { + int bit; + /* We only get bit by bit for T.4 1D and T.4 2-D. */ + if (s->pre_encoded_len > 0) + { + if (s->pre_encoded_ptr >= s->pre_encoded_len) + return SIG_STATUS_END_OF_DATA; + bit = (s->pre_encoded_buf[s->pre_encoded_ptr] >> s->pre_encoded_bit) & 1; + if (++s->pre_encoded_bit >= 8) + { + s->pre_encoded_bit = 0; + s->pre_encoded_ptr++; + } + return bit; + } return t4_t6_encode_get_bit(&s->encoder.t4_t6); } /*- End of function --------------------------------------------------------*/ @@ -1500,6 +2483,7 @@ SPAN_DECLARE(int) t4_tx_get(t4_tx_state_t *s, uint8_t buf[], size_t max_len) if (s->image_get_handler) return s->image_get_handler((void *) &s->encoder, buf, max_len); + return 0; } /*- End of function --------------------------------------------------------*/ @@ -1562,9 +2546,10 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s) s->image_get_handler = NULL; break; } + /* If there is a page header, create that first */ - if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0) - //if (s->header_info && s->header_info[0] && make_header(s) == 0) + //if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0) + if (s->header_info && s->header_info[0] && make_header(s) == 0) { s->header_row = 0; set_row_read_handler(s, header_row_read_handler, (void *) s); @@ -1633,7 +2618,7 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int if (open_tiff_input_file(s, file) < 0) { if (allocated) - free(s); + span_free(s); return NULL; } s->tiff.file = strdup(file); @@ -1644,7 +2629,7 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int { tiff_tx_release(s); if (allocated) - free(s); + span_free(s); return NULL; } } @@ -1658,12 +2643,12 @@ SPAN_DECLARE(int) t4_tx_release(t4_tx_state_t *s) tiff_tx_release(s); if (s->header_text) { - free(s->header_text); + span_free(s->header_text); s->header_text = NULL; } if (s->colour_map) { - free(s->colour_map); + span_free(s->colour_map); s->colour_map = NULL; } switch (s->metadata.compression) @@ -1700,7 +2685,7 @@ SPAN_DECLARE(int) t4_tx_free(t4_tx_state_t *s) int ret; ret = t4_tx_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index 8eda2bb201..881a4839b5 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -46,7 +46,7 @@ EXTRA_DIST = fax_tests.sh \ MAINTAINERCLEANFILES = Makefile.in -INCLUDES = -I$(top_builddir)/src -I$(top_builddir)/spandsp-sim -DDATADIR="\"$(pkgdatadir)\"" +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_builddir)/spandsp-sim -DDATADIR="\"$(pkgdatadir)\"" LIBDIR = -L$(top_builddir)/src diff --git a/libs/spandsp/tests/regression_tests.sh b/libs/spandsp/tests/regression_tests.sh index 31aeb5a952..949eca3fcf 100755 --- a/libs/spandsp/tests/regression_tests.sh +++ b/libs/spandsp/tests/regression_tests.sh @@ -43,6 +43,15 @@ then fi echo adsi_tests completed OK +./alloc_tests >$STDOUT_DEST 2>$STDERR_DEST +RETVAL=$? +if [ $RETVAL != 0 ] +then + echo alloc_tests failed! + exit $RETVAL +fi +echo alloc_tests completed OK + ./async_tests >$STDOUT_DEST 2>$STDERR_DEST RETVAL=$? if [ $RETVAL != 0 ] diff --git a/libs/spandsp/tests/t31_tests.c b/libs/spandsp/tests/t31_tests.c index a002aa73bc..c7d941384f 100644 --- a/libs/spandsp/tests/t31_tests.c +++ b/libs/spandsp/tests/t31_tests.c @@ -106,9 +106,9 @@ static const struct command_response_s fax_send_test_seq[] = EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), // #if 1 - RESPONSE("\xFF\x13\x80\x00\xEE\xF8\x80\x80\x89\x80\x80\x80\x18\x18\xB9\x10\x03"), // For audio FAXing + RESPONSE("\xFF\x13\x80\x00\xEE\xF8\x80\x80\x99\x80\x80\x80\x18\x58\x0D\x10\x03"), // For audio FAXing #else - RESPONSE("\xFF\x13\x80\x04\xEE\xF8\x80\x80\x89\x80\x80\x80\x18\x84\x09\x10\x03"), // For T.38 FAXing + RESPONSE("\xFF\x13\x80\x04\xEE\xF8\x80\x80\x99\x80\x80\x80\x18\xC4\xBD\x10\x03"), // For T.38 FAXing #endif RESPONSE("\r\nOK\r\n"), //EXCHANGE("AT+FRH=3\r", "\r\nNO CARRIER\r\n"), diff --git a/libs/spandsp/tests/t4_tests.c b/libs/spandsp/tests/t4_tests.c index ac682bd620..f01b1d3db7 100644 --- a/libs/spandsp/tests/t4_tests.c +++ b/libs/spandsp/tests/t4_tests.c @@ -518,7 +518,6 @@ int main(int argc, char *argv[]) compression = compression_sequence[compression_step++]; } } -#if 0 if (t4_tx_set_tx_image_format(send_state, compression, T4_SUPPORT_WIDTH_215MM @@ -548,8 +547,6 @@ int main(int argc, char *argv[]) { break; } -#endif - t4_tx_set_tx_encoding(send_state, compression); t4_rx_set_rx_encoding(receive_state, compression); if (t4_tx_start_page(send_state)) diff --git a/libs/spandsp/tests/tsb85_tests.c b/libs/spandsp/tests/tsb85_tests.c index d8f626520c..e8064b3d45 100644 --- a/libs/spandsp/tests/tsb85_tests.c +++ b/libs/spandsp/tests/tsb85_tests.c @@ -397,6 +397,7 @@ static void fax_prepare(void) | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_100_100 | T4_RESOLUTION_200_100 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_400 @@ -980,7 +981,37 @@ static int next_step(faxtester_state_t *s) else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_T6; } - t4_tx_set_tx_encoding(&t4_tx_state, compression_type); + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); if (t4_tx_start_page(&t4_tx_state)) { @@ -1020,7 +1051,37 @@ static int next_step(faxtester_state_t *s) else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_T6; } - t4_tx_set_tx_encoding(&t4_tx_state, compression_type); + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); if (t4_tx_start_page(&t4_tx_state)) {