935 lines
31 KiB
C
935 lines
31 KiB
C
/*
|
|
* SpanDSP - a series of DSP components for telephony
|
|
*
|
|
* t43.c - ITU T.43 JBIG for grey and colour FAX image processing
|
|
*
|
|
* Written by Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* Copyright (C) 2011, 2013 Steve Underwood
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 2.1,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <tiffio.h>
|
|
#if defined(HAVE_TGMATH_H)
|
|
#include <tgmath.h>
|
|
#endif
|
|
#if defined(HAVE_MATH_H)
|
|
#include <math.h>
|
|
#endif
|
|
#if defined(HAVE_STDBOOL_H)
|
|
#include <stdbool.h>
|
|
#else
|
|
#include "spandsp/stdbool.h"
|
|
#endif
|
|
#include <time.h>
|
|
#include "floating_fudge.h"
|
|
#include <setjmp.h>
|
|
|
|
#include "spandsp/telephony.h"
|
|
#include "spandsp/alloc.h"
|
|
#include "spandsp/logging.h"
|
|
#include "spandsp/async.h"
|
|
#include "spandsp/timezone.h"
|
|
#include "spandsp/t4_rx.h"
|
|
#include "spandsp/t4_tx.h"
|
|
#include "spandsp/t81_t82_arith_coding.h"
|
|
#include "spandsp/t85.h"
|
|
#include "spandsp/t42.h"
|
|
#include "spandsp/t43.h"
|
|
|
|
#include "spandsp/private/logging.h"
|
|
#include "spandsp/private/t81_t82_arith_coding.h"
|
|
#include "spandsp/private/t85.h"
|
|
#include "spandsp/private/t42.h"
|
|
#include "spandsp/private/t43.h"
|
|
|
|
#include "t43_gray_code_tables.h"
|
|
#include "t42_t43_local.h"
|
|
|
|
SPAN_DECLARE(const char *) t43_image_type_to_str(int type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case T43_IMAGE_TYPE_RGB_BILEVEL:
|
|
return "1 bit/colour image (RGB primaries)";
|
|
case T43_IMAGE_TYPE_CMY_BILEVEL:
|
|
return "1 bit/colour image (CMY primaries)";
|
|
case T43_IMAGE_TYPE_CMYK_BILEVEL:
|
|
return "1 bit/colour image (CMYK primaries)";
|
|
case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE:
|
|
return "Palettized colour image (CIELAB 8 bits/component precision table)";
|
|
case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE:
|
|
return "Palettized colour image (CIELAB 12 bits/component precision table)";
|
|
case T43_IMAGE_TYPE_GRAY:
|
|
return "Gray-scale image (using L*)";
|
|
case T43_IMAGE_TYPE_COLOUR:
|
|
return "Continuous-tone colour image (CIELAB)";
|
|
}
|
|
return "???";
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ uint16_t pack_16(const uint8_t *s)
|
|
{
|
|
uint16_t value;
|
|
|
|
value = ((uint16_t) s[0] << 8) | (uint16_t) s[1];
|
|
return value;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ uint32_t pack_32(const uint8_t *s)
|
|
{
|
|
uint32_t value;
|
|
|
|
value = ((uint32_t) s[0] << 24) | ((uint32_t) s[1] << 16) | ((uint32_t) s[2] << 8) | (uint32_t) s[3];
|
|
return value;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
#if 0
|
|
static __inline__ int unpack_16(uint8_t *s, uint16_t value)
|
|
{
|
|
s[0] = (value >> 8) & 0xFF;
|
|
s[1] = value & 0xFF;
|
|
return sizeof(uint16_t);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ int unpack_32(uint8_t *s, uint32_t value)
|
|
{
|
|
s[0] = (value >> 24) & 0xFF;
|
|
s[1] = (value >> 16) & 0xFF;
|
|
s[2] = (value >> 8) & 0xFF;
|
|
s[3] = value & 0xFF;
|
|
return sizeof(uint16_t);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int t43_create_header(t43_encode_state_t *s, uint8_t data[], size_t len)
|
|
{
|
|
int pos;
|
|
int val[6];
|
|
#if 0
|
|
int bytes_per_entry;
|
|
#endif
|
|
|
|
pos = 0;
|
|
unpack_16(data, 0xFFA8);
|
|
pos += 2;
|
|
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX0\n");
|
|
unpack_16(&data[pos], 0xFFE1);
|
|
pos += 2;
|
|
unpack_16(&data[pos], 2 + 6 + 10);
|
|
pos += 2;
|
|
memcpy(&data[pos], "G3FAX\0", 6);
|
|
pos += 6;
|
|
unpack_16(&data[pos], 1997);
|
|
pos += 2;
|
|
unpack_16(&data[pos], s->spatial_resolution);
|
|
pos += 2;
|
|
/* JBIG coding method (0) is the only possible value here */
|
|
data[pos] = 0;
|
|
pos += 1;
|
|
data[pos] = s->image_type;
|
|
pos += 1;
|
|
data[pos] = s->bit_planes[0];
|
|
pos += 1;
|
|
data[pos] = s->bit_planes[1];
|
|
pos += 1;
|
|
data[pos] = s->bit_planes[2];
|
|
pos += 1;
|
|
data[pos] = s->bit_planes[3];
|
|
pos += 1;
|
|
|
|
if (s->lab.offset_L != 0
|
|
||
|
|
s->lab.range_L != 100
|
|
||
|
|
s->lab.offset_a != 128
|
|
||
|
|
s->lab.range_a != 170
|
|
||
|
|
s->lab.offset_b != 96
|
|
||
|
|
s->lab.range_b != 200)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX1\n");
|
|
unpack_16(&data[pos], 0xFFE1);
|
|
pos += 2;
|
|
unpack_16(&data[pos], 2 + 6 + 12);
|
|
pos += 2;
|
|
memcpy(&data[pos], "G3FAX\1", 6);
|
|
pos += 6;
|
|
get_lab_gamut2(&s->lab, &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]);
|
|
unpack_16(&data[pos + 0], val[0]);
|
|
unpack_16(&data[pos + 2], val[1]);
|
|
unpack_16(&data[pos + 4], val[2]);
|
|
unpack_16(&data[pos + 6], val[3]);
|
|
unpack_16(&data[pos + 8], val[4]);
|
|
unpack_16(&data[pos + 10], val[5]);
|
|
pos += 12;
|
|
}
|
|
|
|
if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0
|
|
||
|
|
s->illuminant_colour_temperature > 0)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX2\n");
|
|
unpack_16(&data[pos], 0xFFE1);
|
|
pos += 2;
|
|
unpack_16(&data[pos], 2 + 6 + 4);
|
|
pos += 2;
|
|
memcpy(&data[pos], "G3FAX\2", 6);
|
|
pos += 6;
|
|
if (memcmp(s->illuminant_code, "\0\0\0\0", 4) != 0)
|
|
{
|
|
memcpy(&data[pos], s->illuminant_code, 4);
|
|
}
|
|
else
|
|
{
|
|
memcpy(&data[pos], "CT", 2);
|
|
unpack_16(&data[pos + 2], s->illuminant_colour_temperature);
|
|
}
|
|
pos += 4;
|
|
}
|
|
|
|
#if 0
|
|
if (s->colour_map)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX3\n");
|
|
bytes_per_entry = (table_id == 0) ? 1 : 2;
|
|
unpack_16(&data[pos], 0xFFE3);
|
|
pos += 2;
|
|
unpack_32(&data[pos], 2 + 6 + 2 + 4 + 3*s->colour_map_entries*bytes_per_entry);
|
|
pos += 4;
|
|
memcpy(&data[pos], "G3FAX\3", 6);
|
|
pos += 6;
|
|
unpack_16(&data[pos], table_id);
|
|
pos += 2;
|
|
unpack_32(&data[pos], s->colour_map_entries);
|
|
pos += 4;
|
|
srgb_to_lab(&s->lab, &data[pos], s->colour_map, s->colour_map_entries);
|
|
pos += 3*s->colour_map_entries*bytes_per_entry;
|
|
}
|
|
#endif
|
|
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Putting G3FAX-FF\n");
|
|
unpack_16(&data[pos], 0xFFE1);
|
|
pos += 2;
|
|
unpack_16(&data[pos], 2 + 6);
|
|
pos += 2;
|
|
memcpy(&data[pos], "G3FAX\xFF", 6);
|
|
pos += 6;
|
|
return pos;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
#endif
|
|
|
|
SPAN_DECLARE(void) t43_encode_set_options(t43_encode_state_t *s,
|
|
uint32_t l0,
|
|
int mx,
|
|
int options)
|
|
{
|
|
t85_encode_set_options(&s->t85, l0, mx, options);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_set_image_width(t43_encode_state_t *s, uint32_t image_width)
|
|
{
|
|
return t85_encode_set_image_width(&s->t85, image_width);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_set_image_length(t43_encode_state_t *s, uint32_t image_length)
|
|
{
|
|
return t85_encode_set_image_length(&s->t85, image_length);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_set_image_type(t43_encode_state_t *s, int image_type)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) t43_encode_abort(t43_encode_state_t *s)
|
|
{
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) t43_encode_comment(t43_encode_state_t *s, const uint8_t comment[], size_t len)
|
|
{
|
|
t85_encode_comment(&s->t85, comment, len);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_image_complete(t43_encode_state_t *s)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_get(t43_encode_state_t *s, uint8_t buf[], size_t max_len)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(uint32_t) t43_encode_get_image_width(t43_encode_state_t *s)
|
|
{
|
|
return t85_encode_get_image_width(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(uint32_t) t43_encode_get_image_length(t43_encode_state_t *s)
|
|
{
|
|
return t85_encode_get_image_length(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_get_compressed_image_size(t43_encode_state_t *s)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_set_row_read_handler(t43_encode_state_t *s,
|
|
t4_row_read_handler_t handler,
|
|
void *user_data)
|
|
{
|
|
s->row_read_handler = handler;
|
|
s->row_read_user_data = user_data;
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(logging_state_t *) t43_encode_get_logging_state(t43_encode_state_t *s)
|
|
{
|
|
return &s->logging;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_restart(t43_encode_state_t *s, uint32_t image_width, uint32_t image_length)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(t43_encode_state_t *) t43_encode_init(t43_encode_state_t *s,
|
|
uint32_t image_width,
|
|
uint32_t image_length,
|
|
t4_row_read_handler_t handler,
|
|
void *user_data)
|
|
{
|
|
if (s == NULL)
|
|
{
|
|
if ((s = (t43_encode_state_t *) span_alloc(sizeof(*s))) == NULL)
|
|
return NULL;
|
|
}
|
|
memset(s, 0, sizeof(*s));
|
|
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
|
|
span_log_set_protocol(&s->logging, "T.43");
|
|
|
|
s->row_read_handler = handler;
|
|
s->row_read_user_data = user_data;
|
|
|
|
t85_encode_init(&s->t85,
|
|
image_width,
|
|
image_length,
|
|
handler,
|
|
user_data);
|
|
|
|
s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
|
|
|
|
return s;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_release(t43_encode_state_t *s)
|
|
{
|
|
t85_encode_release(&s->t85);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_encode_free(t43_encode_state_t *s)
|
|
{
|
|
int ret;
|
|
|
|
t85_encode_release(&s->t85);
|
|
ret = t43_encode_release(s);
|
|
span_free(s);
|
|
return ret;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) t43_decode_rx_status(t43_decode_state_t *s, int status)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status);
|
|
switch (status)
|
|
{
|
|
case SIG_STATUS_TRAINING_IN_PROGRESS:
|
|
case SIG_STATUS_TRAINING_FAILED:
|
|
case SIG_STATUS_TRAINING_SUCCEEDED:
|
|
case SIG_STATUS_CARRIER_UP:
|
|
/* Ignore these */
|
|
break;
|
|
case SIG_STATUS_CARRIER_DOWN:
|
|
case SIG_STATUS_END_OF_DATA:
|
|
/* Finalise the image */
|
|
t85_decode_put(&s->t85, NULL, 0);
|
|
break;
|
|
default:
|
|
span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status);
|
|
break;
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static void set_simple_colour_map(t43_decode_state_t *s, int code)
|
|
{
|
|
int i;
|
|
|
|
switch (code)
|
|
{
|
|
case T43_IMAGE_TYPE_RGB_BILEVEL:
|
|
/* Table 3/T.43 1 bit/colour image (using RGB primaries) */
|
|
memset(s->colour_map, 0, sizeof(s->colour_map));
|
|
/* Black */
|
|
/* Blue */
|
|
s->colour_map[3*0x20 + 2] = 0xF0;
|
|
/* Green */
|
|
s->colour_map[3*0x40 + 1] = 0xF0;
|
|
/* Green + Blue */
|
|
s->colour_map[3*0x60 + 1] = 0xF0;
|
|
s->colour_map[3*0x60 + 2] = 0xF0;
|
|
/* Red */
|
|
s->colour_map[3*0x80 + 0] = 0xF0;
|
|
/* Red + Blue */
|
|
s->colour_map[3*0xA0 + 0] = 0xF0;
|
|
s->colour_map[3*0xA0 + 2] = 0xF0;
|
|
/* Red + Green */
|
|
s->colour_map[3*0xC0 + 0] = 0xF0;
|
|
s->colour_map[3*0xC0 + 1] = 0xF0;
|
|
/* White */
|
|
s->colour_map[3*0xE0 + 0] = 0xF0;
|
|
s->colour_map[3*0xE0 + 1] = 0xF0;
|
|
s->colour_map[3*0xE0 + 2] = 0xF0;
|
|
s->colour_map_entries = 256;
|
|
break;
|
|
case T43_IMAGE_TYPE_CMY_BILEVEL:
|
|
/* Table 2/T.43 1 bit/colour image (using CMY primaries) */
|
|
memset(s->colour_map, 0, sizeof(s->colour_map));
|
|
/* White */
|
|
s->colour_map[3*0x00 + 0] = 0xF0;
|
|
s->colour_map[3*0x00 + 1] = 0xF0;
|
|
s->colour_map[3*0x00 + 2] = 0xF0;
|
|
/* Yellow */
|
|
s->colour_map[3*0x20 + 0] = 0xF0;
|
|
s->colour_map[3*0x20 + 1] = 0xF0;
|
|
/* Magenta */
|
|
s->colour_map[3*0x40 + 0] = 0xF0;
|
|
s->colour_map[3*0x40 + 2] = 0xF0;
|
|
/* Magenta + Yellow */
|
|
s->colour_map[3*0x60 + 0] = 0xF0;
|
|
/* Cyan */
|
|
s->colour_map[3*0x80 + 1] = 0xF0;
|
|
/* Cyan + Yellow */
|
|
s->colour_map[3*0xA0 + 1] = 0xF0;
|
|
/* Cyan + Magenta */
|
|
s->colour_map[3*0xC0 + 2] = 0xF0;
|
|
/* Black */
|
|
s->colour_map_entries = 256;
|
|
break;
|
|
case T43_IMAGE_TYPE_CMYK_BILEVEL:
|
|
/* Table 1/T.43 1 bit/colour image (using CMYK primaries) */
|
|
memset(s->colour_map, 0, sizeof(s->colour_map));
|
|
/* White */
|
|
s->colour_map[3*0x00 + 0] = 0xF0;
|
|
s->colour_map[3*0x00 + 1] = 0xF0;
|
|
s->colour_map[3*0x00 + 2] = 0xF0;
|
|
/* Yellow */
|
|
s->colour_map[3*0x20 + 0] = 0xF0;
|
|
s->colour_map[3*0x20 + 1] = 0xF0;
|
|
/* Magenta */
|
|
s->colour_map[3*0x40 + 0] = 0xF0;
|
|
s->colour_map[3*0x40 + 2] = 0xF0;
|
|
/* Magenta + Yellow */
|
|
s->colour_map[3*0x60 + 0] = 0xF0;
|
|
/* Cyan */
|
|
s->colour_map[3*0x80 + 1] = 0xF0;
|
|
/* Cyan + Yellow */
|
|
s->colour_map[3*0xA0 + 1] = 0xF0;
|
|
/* Cyan + Magenta */
|
|
s->colour_map[3*0xC0 + 2] = 0xF0;
|
|
/* Black */
|
|
s->colour_map_entries = 256;
|
|
break;
|
|
case T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE:
|
|
/* Palettized colour image (using CIELAB 8 bits/component precision table) */
|
|
for (i = 0; i < 3*256; i += 3)
|
|
{
|
|
s->colour_map[i + 0] = i;
|
|
s->colour_map[i + 1] = i;
|
|
s->colour_map[i + 2] = i;
|
|
}
|
|
s->colour_map_entries = 256;
|
|
break;
|
|
case T43_IMAGE_TYPE_12BIT_COLOUR_PALETTE:
|
|
/* Palettized colour image (using CIELAB 12 bits/component precision table) */
|
|
break;
|
|
case T43_IMAGE_TYPE_GRAY:
|
|
/* Gray-scale image (using L*) */
|
|
for (i = 0; i < 256; i++)
|
|
s->colour_map[i] = i;
|
|
s->colour_map_entries = 256;
|
|
break;
|
|
case T43_IMAGE_TYPE_COLOUR:
|
|
/* Continuous-tone colour image (using CIELAB) */
|
|
break;
|
|
}
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int t43_analyse_header(t43_decode_state_t *s, const uint8_t data[], size_t len)
|
|
{
|
|
int seg;
|
|
int pos;
|
|
int table_id;
|
|
int val[6];
|
|
uint8_t col[3];
|
|
int i;
|
|
|
|
pos = 0;
|
|
if (pack_16(&data[pos]) != 0xFFA8)
|
|
return 0;
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got BCIH (bit-plane colour image header)\n");
|
|
pos += 2;
|
|
for (;;)
|
|
{
|
|
if (pack_16(&data[pos]) == 0xFFE1)
|
|
{
|
|
pos += 2;
|
|
seg = pack_16(&data[pos]);
|
|
pos += 2;
|
|
seg -= 2;
|
|
if (seg >= 6 && strncmp((char *) &data[pos], "G3FAX", 5) == 0)
|
|
{
|
|
if (data[pos + 5] == 0xFF)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got ECIH (end of colour image header)\n");
|
|
if (seg != 6)
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad ECIH length - %d\n", seg);
|
|
pos += seg;
|
|
break;
|
|
}
|
|
switch (data[pos + 5])
|
|
{
|
|
case 0:
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got G3FAX0\n");
|
|
if (seg < 6 + 10)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX0 length - %d\n", seg);
|
|
}
|
|
else
|
|
{
|
|
val[0] = pack_16(&data[pos + 6 + 0]);
|
|
s->spatial_resolution = pack_16(&data[pos + 6 + 2]);
|
|
val[2] = data[pos + 6 + 4];
|
|
s->image_type = data[pos + 6 + 5];
|
|
s->bit_planes[0] = data[pos + 6 + 6];
|
|
s->bit_planes[1] = data[pos + 6 + 7];
|
|
s->bit_planes[2] = data[pos + 6 + 8];
|
|
s->bit_planes[3] = data[pos + 6 + 9];
|
|
if (s->image_type == T43_IMAGE_TYPE_GRAY)
|
|
{
|
|
s->samples_per_pixel = 1;
|
|
}
|
|
else if (s->image_type == T43_IMAGE_TYPE_CMYK_BILEVEL)
|
|
{
|
|
s->samples_per_pixel = 4;
|
|
}
|
|
else
|
|
{
|
|
s->samples_per_pixel = 3;
|
|
}
|
|
span_log(&s->logging,
|
|
SPAN_LOG_FLOW,
|
|
"Version %d, resolution %ddpi, coding method %d, type %s (%d), bit planes %d,%d,%d,%d\n",
|
|
val[0],
|
|
s->spatial_resolution,
|
|
val[2],
|
|
t43_image_type_to_str(s->image_type),
|
|
s->image_type,
|
|
s->bit_planes[0],
|
|
s->bit_planes[1],
|
|
s->bit_planes[2],
|
|
s->bit_planes[3]);
|
|
set_simple_colour_map(s, s->image_type);
|
|
}
|
|
break;
|
|
case 1:
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Set gamut\n");
|
|
if (seg < 6 + 12)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX1 length - %d\n", seg);
|
|
}
|
|
else
|
|
{
|
|
set_gamut_from_code(&s->logging, &s->lab, &data[pos + 6]);
|
|
}
|
|
break;
|
|
case 2:
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Set illuminant\n");
|
|
if (seg < 6 + 4)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX2 length - %d\n", seg);
|
|
}
|
|
else
|
|
{
|
|
s->illuminant_colour_temperature = set_illuminant_from_code(&s->logging, &s->lab, &data[pos + 6]);
|
|
}
|
|
break;
|
|
default:
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got unexpected G3FAX%d length - %d\n", data[pos + 5], seg);
|
|
break;
|
|
}
|
|
}
|
|
pos += seg;
|
|
}
|
|
else if (pack_16(&data[pos]) == 0xFFE3)
|
|
{
|
|
pos += 2;
|
|
seg = pack_32(&data[pos]);
|
|
pos += 4;
|
|
seg -= 4;
|
|
if (seg >= 6)
|
|
{
|
|
if (strncmp((char *) &data[pos], "G3FAX\3", 6) == 0)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got G3FAX3\n");
|
|
table_id = pack_16(&data[pos + 6]);
|
|
span_log(&s->logging, SPAN_LOG_FLOW, " Table ID %3d\n", table_id);
|
|
switch (table_id)
|
|
{
|
|
case 0:
|
|
/* 8 bit CIELAB */
|
|
s->colour_map_entries = pack_32(&data[pos + 8]);
|
|
span_log(&s->logging, SPAN_LOG_FLOW, " Entries %6d (len %d)\n", s->colour_map_entries, seg);
|
|
if (seg >= 12 + s->colour_map_entries*3)
|
|
{
|
|
lab_to_srgb(&s->lab, s->colour_map, &data[pos + 12], s->colour_map_entries);
|
|
}
|
|
else
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX3 length - %d\n", seg);
|
|
}
|
|
break;
|
|
case 4:
|
|
/* 12 bit CIELAB */
|
|
s->colour_map_entries = pack_32(&data[pos + 8]);
|
|
span_log(&s->logging, SPAN_LOG_FLOW, " Entries %6d\n", s->colour_map_entries);
|
|
/* TODO: implement 12bit stuff */
|
|
if (seg >= 12 + s->colour_map_entries*3*2)
|
|
{
|
|
for (i = 0; i < s->colour_map_entries; i++)
|
|
{
|
|
col[0] = pack_16(&data[pos + 12 + 6*i]) >> 4;
|
|
col[1] = pack_16(&data[pos + 12 + 6*i + 2]) >> 4;
|
|
col[2] = pack_16(&data[pos + 12 + 6*i + 4]) >> 4;
|
|
lab_to_srgb(&s->lab, &s->colour_map[3*i], col, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX3 length - %d\n", seg);
|
|
}
|
|
break;
|
|
default:
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "Got bad G3FAX3 table ID - %d\n", table_id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pos += seg;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
|
|
{
|
|
t43_decode_state_t *s;
|
|
int i;
|
|
int j;
|
|
int image_size;
|
|
uint8_t mask;
|
|
|
|
/* Repack the bit packed T.85 image plane to a 3 x 8 bits per pixel colour image.
|
|
We use the red entry now. This will be remapped to RGB later, as we apply the
|
|
colour map. */
|
|
s = (t43_decode_state_t *) user_data;
|
|
|
|
if (s->buf == NULL)
|
|
{
|
|
image_size = s->samples_per_pixel*s->t85.xd*s->t85.yd;
|
|
if ((s->buf = span_alloc(image_size)) == NULL)
|
|
return -1;
|
|
memset(s->buf, 0, image_size);
|
|
}
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
mask = 0x80;
|
|
if (s->samples_per_pixel == 1)
|
|
{
|
|
for (j = 0; j < 8; j += s->samples_per_pixel)
|
|
{
|
|
if ((buf[i] & mask))
|
|
s->buf[s->ptr + j] |= s->bit_plane_mask;
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < s->samples_per_pixel*8; j += s->samples_per_pixel)
|
|
{
|
|
if ((buf[i] & mask))
|
|
s->buf[s->ptr + j] |= s->bit_plane_mask;
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
s->ptr += s->samples_per_pixel*8;
|
|
}
|
|
s->row++;
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_put(t43_decode_state_t *s, const uint8_t data[], size_t len)
|
|
{
|
|
int i;
|
|
int j;
|
|
int plane_len;
|
|
int total_len;
|
|
int result;
|
|
|
|
/* TODO: this isn't allowing for the header arriving in chunks */
|
|
if (s->current_bit_plane < 0)
|
|
{
|
|
i = t43_analyse_header(s, data, len);
|
|
data += i;
|
|
len -= i;
|
|
s->bit_plane_mask = 0x80;
|
|
s->current_bit_plane++;
|
|
|
|
/* There must be at least one bit plane. The real value for this will
|
|
be filled in as the first plane is processed */
|
|
s->t85.bit_planes = 1;
|
|
s->ptr = 0;
|
|
s->row = 0;
|
|
s->buf = NULL;
|
|
s->plane_ptr = 0;
|
|
t85_decode_new_plane(&s->t85);
|
|
}
|
|
|
|
/* Now deal the bit-planes, one after another. */
|
|
total_len = 0;
|
|
result = 0;
|
|
while (s->current_bit_plane < s->t85.bit_planes)
|
|
{
|
|
result = t85_decode_put(&s->t85, data, len);
|
|
if (result != T4_DECODE_OK)
|
|
{
|
|
s->plane_ptr += len;
|
|
return result;
|
|
}
|
|
plane_len = t85_decode_get_compressed_image_size(&s->t85);
|
|
data += (plane_len/8 - s->plane_ptr);
|
|
len -= (plane_len/8 - s->plane_ptr);
|
|
total_len = s->ptr;
|
|
|
|
/* Start the next plane */
|
|
s->bit_plane_mask >>= 1;
|
|
s->ptr = 0;
|
|
s->row = 0;
|
|
s->plane_ptr = 0;
|
|
s->current_bit_plane++;
|
|
t85_decode_new_plane(&s->t85);
|
|
}
|
|
/* Apply the colour map, and produce the RGB data from the collected bit-planes */
|
|
if (s->samples_per_pixel == 1)
|
|
{
|
|
for (j = 0; j < total_len; j += s->samples_per_pixel)
|
|
s->buf[j] = s->colour_map[s->buf[j]];
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < total_len; j += s->samples_per_pixel)
|
|
{
|
|
i = s->buf[j];
|
|
s->buf[j] = s->colour_map[3*i];
|
|
s->buf[j + 1] = s->colour_map[3*i + 1];
|
|
s->buf[j + 2] = s->colour_map[3*i + 2];
|
|
}
|
|
}
|
|
for (j = 0; j < s->t85.yd; j++)
|
|
s->row_write_handler(s->row_write_user_data, &s->buf[j*s->samples_per_pixel*s->t85.xd], s->samples_per_pixel*s->t85.xd);
|
|
return result;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_set_row_write_handler(t43_decode_state_t *s,
|
|
t4_row_write_handler_t handler,
|
|
void *user_data)
|
|
{
|
|
s->row_write_handler = handler;
|
|
s->row_write_user_data = user_data;
|
|
s->t85.row_write_handler = handler;
|
|
s->t85.row_write_user_data = user_data;
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_set_comment_handler(t43_decode_state_t *s,
|
|
uint32_t max_comment_len,
|
|
t4_row_write_handler_t handler,
|
|
void *user_data)
|
|
{
|
|
return t85_decode_set_comment_handler(&s->t85, max_comment_len, handler, user_data);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_set_image_size_constraints(t43_decode_state_t *s,
|
|
uint32_t max_xd,
|
|
uint32_t max_yd)
|
|
{
|
|
return t85_decode_set_image_size_constraints(&s->t85, max_xd, max_yd);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(uint32_t) t43_decode_get_image_width(t43_decode_state_t *s)
|
|
{
|
|
return t85_decode_get_image_width(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(uint32_t) t43_decode_get_image_length(t43_decode_state_t *s)
|
|
{
|
|
return t85_decode_get_image_length(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_get_compressed_image_size(t43_decode_state_t *s)
|
|
{
|
|
return t85_decode_get_compressed_image_size(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(logging_state_t *) t43_decode_get_logging_state(t43_decode_state_t *s)
|
|
{
|
|
return &s->logging;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_restart(t43_decode_state_t *s)
|
|
{
|
|
/* ITULAB */
|
|
/* Illuminant D50 */
|
|
//set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f);
|
|
set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f);
|
|
set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, false);
|
|
|
|
s->t85.min_bit_planes = 1;
|
|
s->t85.max_bit_planes = 8;
|
|
s->bit_plane_mask = 0x80;
|
|
s->current_bit_plane = -1;
|
|
s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
|
|
|
|
return t85_decode_restart(&s->t85);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(t43_decode_state_t *) t43_decode_init(t43_decode_state_t *s,
|
|
t4_row_write_handler_t handler,
|
|
void *user_data)
|
|
{
|
|
if (s == NULL)
|
|
{
|
|
if ((s = (t43_decode_state_t *) span_alloc(sizeof(*s))) == NULL)
|
|
return NULL;
|
|
}
|
|
memset(s, 0, sizeof(*s));
|
|
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
|
|
span_log_set_protocol(&s->logging, "T.43");
|
|
|
|
s->row_write_handler = handler;
|
|
s->row_write_user_data = user_data;
|
|
|
|
t85_decode_init(&s->t85, t85_row_write_handler, s);
|
|
|
|
/* ITULAB */
|
|
/* Illuminant D50 */
|
|
//set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f);
|
|
set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f);
|
|
set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, false);
|
|
|
|
s->t85.min_bit_planes = 1;
|
|
s->t85.max_bit_planes = 8;
|
|
s->bit_plane_mask = 0x80;
|
|
s->current_bit_plane = -1;
|
|
s->image_type = T43_IMAGE_TYPE_8BIT_COLOUR_PALETTE;
|
|
|
|
return s;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_release(t43_decode_state_t *s)
|
|
{
|
|
t85_decode_release(&s->t85);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) t43_decode_free(t43_decode_state_t *s)
|
|
{
|
|
int ret;
|
|
|
|
ret = t43_decode_release(s);
|
|
t85_decode_free(&s->t85);
|
|
span_free(s);
|
|
return ret;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|