T30_SUPPORT_COMPRESSION_SYCC_T81 = 0x200,
/*! T.88 monochrome JBIG2 compression */
T30_SUPPORT_COMPRESSION_T88 = 0x400,
+ /*! Gray-scale support by multi-level codecs */
+ T30_SUPPORT_COMPRESSION_GRAYSCALE = 0x1000000,
+ /*! Colour support by multi-level codecs */
+ T30_SUPPORT_COMPRESSION_COLOUR = 0x2000000,
+ /*! 12 bit mode for gray scale and colour */
+ T30_SUPPORT_COMPRESSION_12BIT = 0x4000000,
+ /*! Convert a colour image to a gray-scale one */
+ T30_SUPPORT_COMPRESSION_COLOUR_TO_GRAY = 0x8000000,
/*! Dither a gray scale image down a simple bilevel image, with rescaling to fit a FAX page */
T30_SUPPORT_GRAY_TO_BILEVEL = 0x10000000,
/*! Dither a colour image down a simple bilevel image, with rescaling to fit a FAX page */
- T30_SUPPORT_COLOUR_TO_BILEVEL = 0x20000000
+ T30_SUPPORT_COLOUR_TO_BILEVEL = 0x20000000,
+ /*! Rescale an image (except a bi-level image) to fit a permitted FAX width when necessary */
+ T30_SUPPORT_COMPRESSION_RESCALING = 0x40000000
};
enum
{
/* ECM allowed */
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_ECM_CAPABLE);
+
/* Only offer the option of fancy compression schemes, if we are
also offering the ECM option needed to support them. */
if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T6))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T85_L0_CAPABLE);
}
+ if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_COLOUR))
+ set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FULL_COLOUR_CAPABLE);
+
if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T42_T81))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T81_CAPABLE);
if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T43))
}
//if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_T89))
// set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T89_CAPABLE);
+
+ if ((s->supported_compressions & T30_SUPPORT_COMPRESSION_12BIT))
+ set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE);
+
+ //if ((s->supported_compressions & 30_SUPPORT_COMPRESSION_NO_SUBSAMPLING))
+ // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING);
+
+ /* No custom illuminant */
+ /* No custom gamut range */
}
if ((s->supported_t30_features & T30_SUPPORT_FIELD_NOT_VALID))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FNV_CAPABLE);
/* No mode 26 (T.505) */
/* No digital network capability */
/* No duplex operation */
- /* No JPEG */
- /* No full colour */
- /* No 12bits/pel */
- /* No sub-sampling (1:1:1) */
- /* No custom illuminant */
- /* No custom gamut range */
if ((s->supported_image_sizes & T30_SUPPORT_US_LETTER_LENGTH))
set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NORTH_AMERICAN_LETTER_CAPABLE);
if ((s->supported_image_sizes & T30_SUPPORT_US_LEGAL_LENGTH))
return -1;
}
}
-
queue_phase(s, T30_PHASE_B_TX);
/* Try to send something */
if (s->tx_file[0])
s->y_resolution = -1;
//s->current_page_resolution = 0;
x = -1;
- if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE))
+ if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE) || test_ctrl_bit(dcs_frame, T30_DCS_BIT_T43_MODE))
{
/* Gray scale or colour image */
+
+ /* Note 35 of Table 2/T.30 */
+ if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE))
+ {
+ /* We are going to work in full colour mode */
+ }
+
+ if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_12BIT_COMPONENT))
+ {
+ /* We are going to work in 12 bit mode */
+ }
+
+ if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING))
+ {
+ //???? = T30_SUPPORT_COMPRESSION_T42_T81_SUBSAMPLING;
+ }
+
if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200))
{
if ((s->supported_colour_resolutions & T30_SUPPORT_RESOLUTION_1200_1200))
/* Check which compression the far end has decided to use. */
#if defined(SPANDSP_SUPPORT_T42)
- if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE))
+ if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_T81_MODE))
{
s->line_encoding = T4_COMPRESSION_T42_T81;
}
process_rx_dcs(s, msg, len);
break;
case T30_DCN:
+ /* Received a DCN while waiting for a DIS or DCN */
t30_set_status(s, T30_ERR_RX_DCNWHY);
disconnect(s);
break;
supported_compressions &= T30_SUPPORT_COMPRESSION_T4_1D
| T30_SUPPORT_COMPRESSION_T4_2D
| T30_SUPPORT_COMPRESSION_T6
+ | T30_SUPPORT_COMPRESSION_T85
+ | T30_SUPPORT_COMPRESSION_T85_L0
//| T30_SUPPORT_COMPRESSION_T81
#if defined(SPANDSP_SUPPORT_T43)
| T30_SUPPORT_COMPRESSION_T43
#endif
- | T30_SUPPORT_COMPRESSION_T85
- | T30_SUPPORT_COMPRESSION_T85_L0
| 0;
s->supported_compressions = supported_compressions;
t30_build_dis_or_dtc(s);
SPAN_DECLARE(int) t30_set_supported_bilevel_resolutions(t30_state_t *s, int supported_resolutions)
{
+ supported_resolutions &= 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;
+ /* Make sure anything needed for colour is enabled as a bi-level image, as that is a
+ rule from T.30. 100x100 is an exception, as it doesn't exist as a bi-level resolution. */
+ supported_resolutions |= (s->supported_colour_resolutions & ~T4_RESOLUTION_100_100);
s->supported_bilevel_resolutions = supported_resolutions;
t30_build_dis_or_dtc(s);
return 0;
SPAN_DECLARE(int) t30_set_supported_colour_resolutions(t30_state_t *s, int supported_resolutions)
{
+ supported_resolutions &= T4_RESOLUTION_100_100
+ | T4_RESOLUTION_200_200
+ | T4_RESOLUTION_300_300
+ | T4_RESOLUTION_400_400
+ | T4_RESOLUTION_600_600
+ | T4_RESOLUTION_1200_1200;
s->supported_colour_resolutions = supported_resolutions;
+ /* Make sure anything needed for colour is enabled as a bi-level image, as that is a
+ rule from T.30. 100x100 is an exception, as it doesn't exist as a bi-level resolution. */
+ s->supported_bilevel_resolutions |= (s->supported_colour_resolutions & ~T4_RESOLUTION_100_100);
t30_build_dis_or_dtc(s);
return 0;
}
/*! The number of centimetres in one inch */
#define CM_PER_INCH 2.54f
+typedef struct
+{
+ uint8_t *buf;
+ int ptr;
+} packer_t;
+
#if defined(SPANDSP_SUPPORT_TIFF_FX)
extern TIFFFieldArray tiff_fx_field_array;
#endif
}
/*- End of function --------------------------------------------------------*/
+static int row_read_handler(void *user_data, uint8_t row[], size_t len)
+{
+ packer_t *s;
+
+ s = (packer_t *) user_data;
+ memcpy(row, &s->buf[s->ptr], len);
+ s->ptr += len;
+ return len;
+}
+/*- End of function --------------------------------------------------------*/
+
static int write_tiff_image(t4_rx_state_t *s)
{
t4_rx_tiff_state_t *t;
+ uint8_t *buf;
+ uint8_t *buf2;
+ int buf_len;
+ int len;
+ int len2;
+ t85_encode_state_t t85;
+#if defined(SPANDSP_SUPPORT_T43)
+ t43_encode_state_t t43;
+#endif
+ packer_t packer;
#if defined(SPANDSP_SUPPORT_TIFF_FX)
uint64_t offset;
#endif
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... */
- 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);
+ switch (t->output_encoding)
+ {
+ case T4_COMPRESSION_T85:
+ case T4_COMPRESSION_T85_L0:
+ span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.85 compression.\n", t->file);
+ buf_len = 0;
+ buf = NULL;
+ packer.buf = t->image_buffer;
+ packer.ptr = 0;
+ t85_encode_init(&t85, s->image_width, s->image_length, row_read_handler, &packer);
+ //if (t->output_encoding == T4_COMPRESSION_T85_L0)
+ // t85_encode_set_options(&t85, 256, -1, -1);
+ len2 = 0;
+ do
+ {
+ if (buf_len < len2 + 50000)
+ {
+ buf_len += 50000;
+ if ((buf2 = realloc(buf, buf_len)) == NULL)
+ {
+ if (buf)
+ free(buf);
+ return -1;
+ }
+ buf = buf2;
+ }
+ len = t85_encode_get(&t85, &buf[len2], 50000);
+ len2 += len;
+ }
+ while (len > 0);
+ if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
+ span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
+ t85_encode_release(&t85);
+ free(buf);
+ break;
+#if defined(SPANDSP_SUPPORT_T43)
+ case T4_COMPRESSION_T43:
+ span_log(&s->logging, SPAN_LOG_WARNING, "%s: TODO need T.43 compression.\n", t->file);
+ buf_len = 0;
+ buf = NULL;
+ packer.buf = t->image_buffer;
+ packer.ptr = 0;
+ t43_encode_init(&t43, s->image_width, s->image_length, row_read_handler, &packer);
+ len2 = 0;
+ do
+ {
+ if (buf_len < len2 + 50000)
+ {
+ buf_len += 50000;
+ if ((buf2 = realloc(buf, buf_len)) == NULL)
+ {
+ if (buf)
+ free(buf);
+ return -1;
+ }
+ buf = buf2;
+ }
+ len = t43_encode_get(&t43, &buf[len2], 50000);
+ len2 += len;
+ }
+ while (len > 0);
+ if (TIFFWriteRawStrip(t->tiff_file, 0, buf, len2) < 0)
+ span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file);
+ t43_encode_release(&t43);
+ free(buf);
+ 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;
+ }
/* ...then finalise the directory entry, and libtiff is happy. */
if (!TIFFWriteDirectory(t->tiff_file))
span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to write directory for page %d.\n", t->file, s->current_page);
{
TIFFSetField(t->tiff_file, TIFFTAG_FAXPROFILE, PROFILETYPE_G3_FAX);
TIFFSetField(t->tiff_file, TIFFTAG_PROFILETYPE, FAXPROFILE_F);
+ TIFFSetField(t->tiff_file, TIFFTAG_CODINGMETHODS, CODINGMETHODS_T4_1D | CODINGMETHODS_T4_2D | CODINGMETHODS_T6);
TIFFSetField(t->tiff_file, TIFFTAG_VERSIONYEAR, "1998");
+ TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 3);
offset = 0;
if (!TIFFWriteCustomDirectory(t->tiff_file, &offset))
/*! The number of centimetres in one inch */
#define CM_PER_INCH 2.54f
+typedef struct
+{
+ uint8_t *buf;
+ int ptr;
+ int row;
+ int bit_mask;
+} t85_packer_t;
+
static void t4_tx_set_image_length(t4_tx_state_t *s, int image_length);
#if defined(SPANDSP_SUPPORT_TIFF_FX)
/* TIFF-FX related extensions to the tag set supported by libtiff */
+
static const TIFFFieldInfo tiff_fx_tiff_field_info[] =
{
{TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, FIELD_CUSTOM, FALSE, FALSE, (char *) "Indexed"},
{TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, FIELD_CUSTOM, FALSE, FALSE, (char *) "ImageLayer"},
};
+#if 1
static TIFFField tiff_fx_tiff_fields[] =
{
{ TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, (char *) "Indexed" },
};
TIFFFieldArray tiff_fx_field_array = { tfiatOther, 0, 12, tiff_fx_tiff_fields };
+#endif
static TIFFExtendProc _ParentExtender = NULL;
return -1;
/* TODO: This only allows for 8 bit deep maps */
- if ((s->colour_map = realloc(s->colour_map, 3*256)) == NULL)
- return -1;
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)
+ return -1;
#if 0
/* Sweep the colormap in the proper order */
- for (i = 0; i < (1 << bits_per_sample); i++)
+ for (i = 0; i < s->colour_map_entries; i++)
{
s->colour_map[3*i + 0] = (map_L[i] >> 8) & 0xFF;
s->colour_map[3*i + 1] = (map_a[i] >> 8) & 0xFF;
}
#else
/* Sweep the colormap in the order that seems to work for l04x_02x.tif */
- for (i = 0; i < (1 << bits_per_sample); i++)
+ for (i = 0; i < s->colour_map_entries; i++)
{
- s->colour_map[0*256 + i] = (map_L[i] >> 8) & 0xFF;
- s->colour_map[1*256 + i] = (map_a[i] >> 8) & 0xFF;
- s->colour_map[2*256 + i] = (map_b[i] >> 8) & 0xFF;
+ s->colour_map[0*s->colour_map_entries + i] = (map_L[i] >> 8) & 0xFF;
+ s->colour_map[1*s->colour_map_entries + i] = (map_a[i] >> 8) & 0xFF;
+ 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);
- for (i = 0; i < (1 << bits_per_sample); i++)
+ 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;
}