提交 b5f74435 authored 作者: Steve Underwood's avatar Steve Underwood

Started introducing T.42 JPEG support for FAXing, but its not plumbed into the

FAX engine yet.
上级 9fe08675
...@@ -143,10 +143,6 @@ libspandsp_la_SOURCES = ademco_contactid.c \ ...@@ -143,10 +143,6 @@ libspandsp_la_SOURCES = ademco_contactid.c \
super_tone_rx.c \ super_tone_rx.c \
super_tone_tx.c \ super_tone_tx.c \
swept_tone.c \ swept_tone.c \
t4_t6_decode.c \
t4_t6_encode.c \
t4_rx.c \
t4_tx.c \
t30.c \ t30.c \
t30_api.c \ t30_api.c \
t30_logging.c \ t30_logging.c \
...@@ -156,6 +152,11 @@ libspandsp_la_SOURCES = ademco_contactid.c \ ...@@ -156,6 +152,11 @@ libspandsp_la_SOURCES = ademco_contactid.c \
t38_gateway.c \ t38_gateway.c \
t38_non_ecm_buffer.c \ t38_non_ecm_buffer.c \
t38_terminal.c \ t38_terminal.c \
t4_t6_decode.c \
t4_t6_encode.c \
t4_rx.c \
t4_tx.c \
t42.c \
t81_t82_arith_coding.c \ t81_t82_arith_coding.c \
t85_decode.c \ t85_decode.c \
t85_encode.c \ t85_encode.c \
...@@ -246,6 +247,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ ...@@ -246,6 +247,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
spandsp/t4_tx.h \ spandsp/t4_tx.h \
spandsp/t4_t6_decode.h \ spandsp/t4_t6_decode.h \
spandsp/t4_t6_encode.h \ spandsp/t4_t6_encode.h \
spandsp/t42.h \
spandsp/t81_t82_arith_coding.h \ spandsp/t81_t82_arith_coding.h \
spandsp/t85.h \ spandsp/t85.h \
spandsp/telephony.h \ spandsp/telephony.h \
...@@ -312,6 +314,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ ...@@ -312,6 +314,7 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \
spandsp/private/t4_tx.h \ spandsp/private/t4_tx.h \
spandsp/private/t4_t6_decode.h \ spandsp/private/t4_t6_decode.h \
spandsp/private/t4_t6_encode.h \ spandsp/private/t4_t6_encode.h \
spandsp/private/t42.h \
spandsp/private/t81_t82_arith_coding.h \ spandsp/private/t81_t82_arith_coding.h \
spandsp/private/t85.h \ spandsp/private/t85.h \
spandsp/private/time_scale.h \ spandsp/private/time_scale.h \
......
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
#include <spandsp/t4_t6_encode.h> #include <spandsp/t4_t6_encode.h>
#include <spandsp/t81_t82_arith_coding.h> #include <spandsp/t81_t82_arith_coding.h>
#include <spandsp/t85.h> #include <spandsp/t85.h>
/*#include <spandsp/t42.h>*/ #include <spandsp/t42.h>
/*#include <spandsp/t43.h>*/ /*#include <spandsp/t43.h>*/
#include <spandsp/t30.h> #include <spandsp/t30.h>
#include <spandsp/t30_api.h> #include <spandsp/t30_api.h>
......
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
#include <spandsp/private/t4_t6_encode.h> #include <spandsp/private/t4_t6_encode.h>
#include <spandsp/private/t81_t82_arith_coding.h> #include <spandsp/private/t81_t82_arith_coding.h>
#include <spandsp/private/t85.h> #include <spandsp/private/t85.h>
/*#include <spandsp/private/t42.h>*/ #include <spandsp/private/t42.h>
/*#include <spandsp/private/t43.h>*/ /*#include <spandsp/private/t43.h>*/
#include <spandsp/private/t4_rx.h> #include <spandsp/private/t4_rx.h>
#include <spandsp/private/t4_tx.h> #include <spandsp/private/t4_tx.h>
......
/*
* SpanDSP - a series of DSP components for telephony
*
* private/t42.h - ITU T.42 JPEG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(_SPANDSP_PRIVATE_T42_H_)
#define _SPANDSP_PRIVATE_T42_H_
struct lab_params_s
{
/* Lab gamut */
float range_L;
float range_a;
float range_b;
float offset_L;
float offset_a;
float offset_b;
int ab_are_signed;
/* Illuminant */
float x_n;
float y_n;
float z_n;
};
/* State of a working instance of the T.42 JPEG FAX encoder */
struct t42_encode_state_s
{
/*! \brief Callback function to read a row of pixels from the image source. */
t4_row_read_handler_t row_read_handler;
/*! \brief Opaque pointer passed to row_read_handler. */
void *row_read_user_data;
lab_params_t lab_params;
/*! \brief Error and flow logging control */
logging_state_t logging;
};
/* State of a working instance of the T.42 JPEG FAX decoder */
struct t42_decode_state_s
{
/*! A callback routine to handle decoded pixel rows */
t4_row_write_handler_t row_write_handler;
/*! An opaque pointer passed to row_write_handler() */
void *row_write_user_data;
/*! A callback routine to handle decoded comments */
t4_row_write_handler_t comment_handler;
/*! An opaque pointer passed to comment_handler() */
void *comment_user_data;
lab_params_t lab_params;
/*! The contents for a COMMENT marker segment, to be added to the
image at the next opportunity. This is set to NULL when nothing is
pending. */
uint8_t *comment;
/*! Length of data pointed to by comment */
size_t comment_len;
/*! \brief Error and flow logging control */
logging_state_t logging;
};
#endif
/*- End of file ------------------------------------------------------------*/
/*
* SpanDSP - a series of DSP components for telephony
*
* t42.h - ITU T.42 JPEG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if !defined(_SPANDSP_T42_H_)
#define _SPANDSP_T42_H_
/*! \page t42_page T.42 (JPEG for FAX) image compression and decompression
\section t42_page_sec_1 What does it do?
\section t42_page_sec_1 How does it work?
*/
/*! State of a working instance of the T.42 encoder */
typedef struct t42_encode_state_s t42_encode_state_t;
/*! State of a working instance of the T.42 decoder */
typedef struct t42_decode_state_s t42_decode_state_t;
typedef struct lab_params_s lab_params_t;
#if defined(__cplusplus)
extern "C"
{
#endif
SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels);
SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t lab[], int pixels);
SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn);
SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed);
SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q);
SPAN_DECLARE(void) set_illuminant_from_code(lab_params_t *s, const uint8_t code[4]);
SPAN_DECLARE(void) set_gamut_from_code(lab_params_t *s, const uint8_t code[12]);
SPAN_DECLARE(int) t42_itulab_to_itulab(tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes);
SPAN_DECLARE(int) t42_itulab_to_jpeg(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes);
SPAN_DECLARE(int) t42_jpeg_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes);
SPAN_DECLARE(int) t42_srgb_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes);
SPAN_DECLARE(int) t42_itulab_to_srgb(lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height, char *emsg, size_t max_emsg_bytes);
SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
uint32_t l0,
int mx,
int options);
SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width);
SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length);
SPAN_DECLARE(void) t42_encode_abort(t42_encode_state_t *s);
SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comment[], size_t len);
SPAN_DECLARE(int) t42_encode_get_byte(t42_encode_state_t *s);
SPAN_DECLARE(int) t42_encode_get_chunk(t42_encode_state_t *s, uint8_t buf[], int max_len);
SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s);
SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s);
SPAN_DECLARE(int) t42_encode_get_compressed_image_size(t42_encode_state_t *s);
SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
t4_row_read_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length);
SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s,
uint32_t image_width,
uint32_t image_length,
t4_row_read_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t42_encode_release(t42_encode_state_t *s);
SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s);
SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status);
SPAN_DECLARE(int) t42_decode_put_byte(t42_decode_state_t *s, int byte);
SPAN_DECLARE(int) t42_decode_put_chunk(t42_decode_state_t *s,
const uint8_t data[],
size_t len);
SPAN_DECLARE(int) t42_decode_set_row_write_handler(t42_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s,
uint32_t max_comment_len,
t4_row_write_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
uint32_t max_xd,
uint32_t max_yd);
SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s);
SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s);
SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s);
SPAN_DECLARE(int) t42_decode_new_plane(t42_decode_state_t *s);
SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s);
SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data);
SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s);
SPAN_DECLARE(int) t42_decode_free(t42_decode_state_t *s);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/
/*
* SpanDSP - a series of DSP components for telephony
*
* t42.c - ITU T.42 JPEG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <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
#include <time.h>
#include "floating_fudge.h"
#include <jpeglib.h>
#include <setjmp.h>
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/async.h"
#include "spandsp/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/t85.h"
#include "spandsp/t42.h"
#include "spandsp/private/logging.h"
#include "spandsp/private/t81_t82_arith_coding.h"
#include "spandsp/private/t85.h"
#include "spandsp/private/t42.h"
#define T42_USE_LUTS
#include "cielab_luts.h"
typedef struct
{
float L;
float a;
float b;
} cielab_t;
typedef struct
{
uint8_t tag[5];
const char *name;
float xn;
float yn;
float zn;
} illuminant_t;
typedef struct
{
jmp_buf escape;
char error_message[JMSG_LENGTH_MAX];
} escape_route_t;
static const illuminant_t illuminants[] =
{
{"\0D50", "CIE D50/2°", 96.422f, 100.000f, 82.521f},
{"", "CIE D50/10°", 96.720f, 100.000f, 81.427f},
{"", "CIE D55/2°", 95.682f, 100.000f, 92.149f},
{"", "CIE D55/10°", 95.799f, 100.000f, 90.926f},
{"\0D65", "CIE D65/2°", 95.047f, 100.000f, 108.883f},
{"", "CIE D65/10°", 94.811f, 100.000f, 107.304f},
{"\0D75", "CIE D75/2°", 94.972f, 100.000f, 122.638f},
{"", "CIE D75/10°", 94.416f, 100.000f, 120.641f},
{"\0\0F2", "F02/2°", 99.186f, 100.000f, 67.393f},
{"", "F02/10°", 103.279f, 100.000f, 69.027f},
{"\0\0F7", "F07/2°", 95.041f, 100.000f, 108.747f},
{"", "F07/10°", 95.792f, 100.000f, 107.686f},
{"\0F11", "F11/2°", 100.962f, 100.000f, 64.350f},
{"", "F11/10°", 103.863f, 100.000f, 65.607f},
{"\0\0SA", "A/2°", 109.850f, 100.000f, 35.585f},
{"", "A/10°", 111.144f, 100.000f, 35.200f},
{"\0\0SC", "C/2°", 98.074f, 100.000f, 118.232f},
{"", "C/10°", 97.285f, 100.000f, 116.145f},
{"", "", 0.000f, 0.000f, 0.000f}
};
/* This is the error catcher */
static struct jpeg_error_mgr error_handler;
static __inline__ uint16_t pack_16(const uint8_t *s)
{
uint16_t value;
value = ((uint16_t) s[0] << 8) | (uint16_t) s[1];
return value;
}
/*- End of function --------------------------------------------------------*/
static __inline__ uint32_t pack_32(const uint8_t *s)
{
uint32_t value;
value = ((uint32_t) s[0] << 24) | ((uint32_t) s[1] << 16) | ((uint32_t) s[2] << 8) | (uint32_t) s[3];
return value;
}
/*- End of function --------------------------------------------------------*/
static __inline__ int unpack_16(uint8_t *s, uint16_t value)
{
s[0] = (value >> 8) & 0xFF;
s[1] = value & 0xFF;
return sizeof(uint16_t);
}
/*- End of function --------------------------------------------------------*/
/* Error handler for IJG library */
static void jpg_error_exit(j_common_ptr cinfo)
{
escape_route_t *escape;
escape = (escape_route_t *) cinfo->client_data;
(*cinfo->err->format_message)(cinfo, escape->error_message);
longjmp(escape->escape, 1);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *s, float new_xn, float new_yn, float new_zn)
{
if (new_yn > 10.0f)
{
s->x_n = new_xn/100.0f;
s->y_n = new_yn/100.0f;
s->z_n = new_zn/100.0f;
}
else
{
s->x_n = new_xn;
s->y_n = new_yn;
s->z_n = new_zn;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) set_lab_gamut(lab_params_t *s, int L_min, int L_max, int a_min, int a_max, int b_min, int b_max, int ab_are_signed)
{
s->range_L = L_max - L_min;
s->range_a = a_max - a_min;
s->range_b = b_max - b_min;
s->offset_L = -256.0f*L_min/s->range_L;
s->offset_a = -256.0f*a_min/s->range_a;
s->offset_b = -256.0f*b_min/s->range_b;
s->range_L /= (256.0f - 1.0f);
s->range_a /= (256.0f - 1.0f);
s->range_b /= (256.0f - 1.0f);
s->ab_are_signed = ab_are_signed;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) set_lab_gamut2(lab_params_t *s, int L_P, int L_Q, int a_P, int a_Q, int b_P, int b_Q)
{
s->range_L = L_Q/(256.0f - 1.0f);
s->range_a = a_Q/(256.0f - 1.0f);
s->range_b = b_Q/(256.0f - 1.0f);
s->offset_L = L_P;
s->offset_a = a_P;
s->offset_b = b_P;
s->ab_are_signed = FALSE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) set_illuminant_from_code(lab_params_t *s, const uint8_t code[4])
{
int i;
int colour_temp;
if (code[0] == 'C' && code[1] == 'T')
{
colour_temp = pack_16(&code[2]);
printf("Illuminant colour temp %dK\n", colour_temp);
return;
}
for (i = 0; illuminants[i].name[0]; i++)
{
if (memcmp(code, illuminants[i].tag, 4) == 0)
{
printf("Illuminant %s\n", illuminants[i].name);
set_lab_illuminant(s, illuminants[i].xn, illuminants[i].yn, illuminants[i].zn);
break;
}
}
if (illuminants[i].name[0] == '\0')
printf("Unrecognised illuminant 0x%x 0x%x 0x%x 0x%x\n", code[0], code[1], code[2], code[3]);
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) set_gamut_from_code(lab_params_t *s, const uint8_t code[12])
{
int i;
int val[6];
for (i = 0; i < 6; i++)
val[i] = pack_16(&code[2*i]);
printf("Gamut L=[%d,%d], a*=[%d,%d], b*=[%d,%d]\n",
val[0],
val[1],
val[2],
val[3],
val[4],
val[5]);
set_lab_gamut2(s, val[0], val[1], val[2], val[3], val[4], val[5]);
}
/*- End of function --------------------------------------------------------*/
static int isITUfax(lab_params_t *s, jpeg_saved_marker_ptr ptr)
{
const uint8_t *data;
int ok;
int val[2];
int i;
ok = FALSE;
while (ptr)
{
if (ptr->marker == (JPEG_APP0 + 1) && ptr->data_length >= 6)
{
data = (const uint8_t *) ptr->data;
if (strncmp((const char *) data, "G3FAX", 5) == 0)
{
switch (data[5])
{
case 0:
for (i = 0; i < 2; i++)
val[i] = pack_16(&data[6 + 2*i]);
printf("Version %d, resolution %d dpi\n", val[0], val[1]);
ok = TRUE;
break;
case 1:
printf("Set gamut\n");
set_gamut_from_code(s, &data[6]);
ok = TRUE;
break;
case 2:
printf("Set illuminant\n");
set_illuminant_from_code(s, &data[6]);
ok = TRUE;
break;
case 3:
/* Colour palette table */
printf("Set colour palette\n");
val[0] = pack_16(&data[6]);
printf("Colour palette %d\n", val[0]);
break;
}
}
}
ptr = ptr->next;
}
return ok;
}
/*- End of function --------------------------------------------------------*/
static void SetITUFax(j_compress_ptr cinfo)
{
uint8_t marker[10] =
{
'G', '3', 'F', 'A', 'X', '\x00', '\x07', '\xCA', '\x00', '\x00'
};
unpack_16(marker + 8, 200);
jpeg_write_marker(cinfo, (JPEG_APP0 + 1), marker, 10);
}
/*- End of function --------------------------------------------------------*/
static __inline__ void itu_to_lab(lab_params_t *s, cielab_t *lab, const uint8_t in[3])
{
uint8_t a;
uint8_t b;
/* T.4 E.6.4 */
lab->L = s->range_L*(in[0] - s->offset_L);
a = in[1];
b = in[2];
if (s->ab_are_signed)
{
a += 128;
b += 128;
}
lab->a = s->range_a*(a - s->offset_a);
lab->b = s->range_b*(b - s->offset_b);
}
/*- End of function --------------------------------------------------------*/
static __inline__ void lab_to_itu(lab_params_t *s, uint8_t out[3], const cielab_t *lab)
{
float val;
/* T.4 E.6.4 */
val = floorf(lab->L/s->range_L + s->offset_L);
out[0] = (uint8_t) (val < 0.0) ? 0 : (val < 256.0) ? val : 255;
val = floorf(lab->a/s->range_a + s->offset_a);
out[1] = (uint8_t) (val < 0.0) ? 0 : (val < 256.0) ? val : 255;
val = floorf(lab->b/s->range_b + s->offset_b);
out[2] = (uint8_t) (val < 0.0) ? 0 : (val < 256.0) ? val : 255;
if (s->ab_are_signed)
{
out[1] -= 128;
out[2] -= 128;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srgb[], int pixels)
{
float x;
float y;
float z;
float r;
float g;
float b;
float xx;
float yy;
float zz;
cielab_t l;
int i;
for (i = 0; i < pixels; i++)
{
#if defined(T42_USE_LUTS)
r = sRGB_to_linear[srgb[0]];
g = sRGB_to_linear[srgb[1]];
b = sRGB_to_linear[srgb[2]];
#else
r = srgb[0]/256.0f;
g = srgb[1]/256.0f;
b = srgb[2]/256.0f;
/* sRGB to linear RGB */
r = (r > 0.04045f) ? powf((r + 0.055f)/1.055f, 2.4f) : r/12.92f;
g = (g > 0.04045f) ? powf((g + 0.055f)/1.055f, 2.4f) : g/12.92f;
b = (b > 0.04045f) ? powf((b + 0.055f)/1.055f, 2.4f) : b/12.92f;
#endif
/* Linear RGB to XYZ */
x = 0.4124f*r + 0.3576f*g + 0.1805f*b;
y = 0.2126f*r + 0.7152f*g + 0.0722f*b;
z = 0.0193f*r + 0.1192f*g + 0.9505f*b;
/* Normalise for the illuminant */
x /= s->x_n;
y /= s->y_n;
z /= s->z_n;
/* XYZ to Lab */
xx = (x <= 0.008856f) ? (7.787f*x + 0.1379f) : cbrtf(x);
yy = (y <= 0.008856f) ? (7.787f*y + 0.1379f) : cbrtf(y);
zz = (z <= 0.008856f) ? (7.787f*z + 0.1379f) : cbrtf(z);
l.L = 116.0f*yy - 16.0f;
l.a = 500.0f*(xx - yy);
l.b = 200.0f*(yy - zz);
lab_to_itu(s, lab, &l);
srgb += 3;
lab += 3;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) lab_to_srgb(lab_params_t *s, uint8_t srgb[], const uint8_t lab[], int pixels)
{
float x;
float y;
float z;
float r;
float g;
float b;
float ll;
cielab_t l;
int val;
int i;
for (i = 0; i < pixels; i++)
{
itu_to_lab(s, &l, lab);
/* Lab to XYZ */
ll = (1.0f/116.0f)*(l.L + 16.0f);
y = ll;
y = (y <= 0.2068f) ? (0.1284f*(y - 0.1379f)) : y*y*y;
x = ll + (1.0f/500.0f)*l.a;
x = (x <= 0.2068f) ? (0.1284f*(x - 0.1379f)) : x*x*x;
z = ll - (1.0f/200.0f)*l.b;
z = (z <= 0.2068f) ? (0.1284f*(z - 0.1379f)) : z*z*z;
/* Normalise for the illuminant */
x *= s->x_n;
y *= s->y_n;
z *= s->z_n;
/* XYZ to linear RGB */
r = 3.2406f*x - 1.5372f*y - 0.4986f*z;
g = -0.9689f*x + 1.8758f*y + 0.0415f*z;
b = 0.0557f*x - 0.2040f*y + 1.0570f*z;
#if defined(T42_USE_LUTS)
val = r*4096.0f;
srgb[0] = linear_to_sRGB[(val < 0) ? 0 : (val < 4095) ? val : 4095];
val = g*4096.0f;
srgb[1] = linear_to_sRGB[(val < 0) ? 0 : (val < 4095) ? val : 4095];
val = b*4096.0f;
srgb[2] = linear_to_sRGB[(val < 0) ? 0 : (val < 4095) ? val : 4095];
#else
/* Linear RGB to sRGB */
r = (r > 0.0031308f) ? (1.055f*powf(r, 1.0f/2.4f) - 0.055f) : r*12.92f;
g = (g > 0.0031308f) ? (1.055f*powf(g, 1.0f/2.4f) - 0.055f) : g*12.92f;
b = (b > 0.0031308f) ? (1.055f*powf(b, 1.0f/2.4f) - 0.055f) : b*12.92f;
r = floorf(r*256.0f);
g = floorf(g*256.0f);
b = floorf(b*256.0f);
srgb[0] = (r < 0) ? 0 : (r <= 255) ? r : 255;
srgb[1] = (g < 0) ? 0 : (g <= 255) ? g : 255;
srgb[2] = (b < 0) ? 0 : (b <= 255) ? b : 255;
#endif
srgb += 3;
lab += 3;
}
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_itulab_to_jpeg(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes)
{
struct jpeg_decompress_struct decompressor;
struct jpeg_compress_struct compressor;
char *outptr;
size_t outsize;
FILE *in;
FILE *out;
int m;
JSAMPROW scan_line_in;
JSAMPROW scan_line_out;
escape_route_t escape;
escape.error_message[0] = '\0';
emsg[0] = '\0';
#if defined(HAVE_OPEN_MEMSTREAM)
in = fmemopen(src, srclen, "r");
#else
in = tmpfile();
fwrite(src, 1, srclen, in);
rewind(in);
#endif
if (in == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to fmemopen().");
return FALSE;
}
#if defined(HAVE_OPEN_MEMSTREAM)
out = open_memstream(&outptr, &outsize);
#else
out = tmpfile();
#endif
if (out == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to open_memstream().");
return FALSE;
}
if (setjmp(escape.escape))
{
strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
emsg[max_emsg_bytes - 1] = '\0';
if (emsg[0] == '\0')
strcpy(emsg, "Unspecified libjpeg error.");
return FALSE;
}
/* Create input decompressor. */
decompressor.err = jpeg_std_error(&error_handler);
decompressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_decompress(&decompressor);
jpeg_stdio_src(&decompressor, in);
/* Needed in the case of ITU Lab input */
for (m = 0; m < 16; m++)
jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
/* Rewind the file */
if (fseek(in, 0, SEEK_SET) != 0)
return FALSE;
/* Take the header */
jpeg_read_header(&decompressor, TRUE);
/* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
decompressor.out_color_space = JCS_YCbCr;
/* Sanity check and parameter check */
if (!isITUfax(s, decompressor.marker_list))
{
if (emsg[0] == '\0')
strcpy(emsg, "Is not ITUFAX.");
return FALSE;
}
/* Create compressor */
compressor.err = jpeg_std_error(&error_handler);
compressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_compress(&compressor);
jpeg_stdio_dest(&compressor, out);
/* Force the destination color space */
compressor.in_color_space = JCS_RGB;
compressor.input_components = 3;
jpeg_set_defaults(&compressor);
//jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
/* Copy size, resolution, etc */
jpeg_copy_critical_parameters(&decompressor, &compressor);
/* We need to keep these */
compressor.density_unit = decompressor.density_unit;
compressor.X_density = decompressor.X_density;
compressor.Y_density = decompressor.Y_density;
jpeg_start_decompress(&decompressor);
jpeg_start_compress(&compressor, TRUE);
if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
return FALSE;
if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
{
free(scan_line_in);
return FALSE;
}
while (decompressor.output_scanline < decompressor.output_height)
{
jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
jpeg_write_scanlines(&compressor, &scan_line_out, 1);
}
free(scan_line_in);
free(scan_line_out);
jpeg_finish_decompress(&decompressor);
jpeg_finish_compress(&compressor);
jpeg_destroy_decompress(&decompressor);
jpeg_destroy_compress(&compressor);
fclose(in);
fclose(out);
*dst = outptr;
*dstlen = outsize;
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_jpeg_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, char *emsg, size_t max_emsg_bytes)
{
struct jpeg_decompress_struct decompressor;
struct jpeg_compress_struct compressor;
char *outptr;
size_t outsize;
FILE *in;
FILE *out;
int m;
JSAMPROW scan_line_in;
JSAMPROW scan_line_out;
escape_route_t escape;
escape.error_message[0] = '\0';
emsg[0] = '\0';
#if defined(HAVE_OPEN_MEMSTREAM)
in = fmemopen(src, srclen, "r");
#else
in = tmpfile();
fwrite(src, 1, srclen, in);
rewind(in);
#endif
if (in == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to fmemopen().");
return FALSE;
}
#if defined(HAVE_OPEN_MEMSTREAM)
out = open_memstream(&outptr, &outsize);
#else
out = tmpfile();
#endif
if (out == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to open_memstream().");
return FALSE;
}
if (setjmp(escape.escape))
{
strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
emsg[max_emsg_bytes - 1] = '\0';
return FALSE;
}
decompressor.err = jpeg_std_error(&error_handler);
decompressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_decompress(&decompressor);
jpeg_stdio_src(&decompressor, in);
/* Needed in the case of ITU Lab input */
for (m = 0; m < 16; m++)
jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
/* Rewind the file */
if (fseek(in, 0, SEEK_SET) != 0)
return FALSE;
/* Take the header */
jpeg_read_header(&decompressor, TRUE);
/* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
decompressor.out_color_space = JCS_RGB;
compressor.err = jpeg_std_error(&error_handler);
compressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_compress(&compressor);
jpeg_stdio_dest(&compressor, out);
/* Force the destination color space */
compressor.in_color_space = JCS_YCbCr;
compressor.input_components = 3;
jpeg_set_defaults(&compressor);
//jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
jpeg_copy_critical_parameters(&decompressor, &compressor);
/* We need to keep these */
compressor.density_unit = decompressor.density_unit;
compressor.X_density = decompressor.X_density;
compressor.Y_density = decompressor.Y_density;
jpeg_start_decompress(&decompressor);
jpeg_start_compress(&compressor, TRUE);
SetITUFax(&compressor);
if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
return FALSE;
if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
{
free(scan_line_in);
return FALSE;
}
while (decompressor.output_scanline < decompressor.output_height)
{
jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
srgb_to_lab(s, scan_line_out, scan_line_in, decompressor.output_width);
jpeg_write_scanlines(&compressor, &scan_line_out, 1);
}
free(scan_line_in);
free(scan_line_out);
jpeg_finish_decompress(&decompressor);
jpeg_finish_compress(&compressor);
jpeg_destroy_decompress(&decompressor);
jpeg_destroy_compress(&compressor);
fclose(in);
fclose(out);
*dst = outptr;
*dstlen = outsize;
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_srgb_to_itulab(lab_params_t *s, tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes)
{
struct jpeg_compress_struct compressor;
FILE *out;
char *outptr;
size_t outsize;
JSAMPROW scan_line_out;
JSAMPROW scan_line_in;
tsize_t pos;
escape_route_t escape;
escape.error_message[0] = '\0';
emsg[0] = '\0';
#if defined(HAVE_OPEN_MEMSTREAM)
out = open_memstream(&outptr, &outsize);
#else
out = tmpfile();
#endif
if (out == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to open_memstream().");
return FALSE;
}
if (setjmp(escape.escape))
{
strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
emsg[max_emsg_bytes - 1] = '\0';
return FALSE;
}
compressor.err = jpeg_std_error(&error_handler);
compressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_compress(&compressor);
jpeg_stdio_dest(&compressor, out);
/* Force the destination color space */
compressor.in_color_space = JCS_YCbCr;
compressor.input_components = 3;
jpeg_set_defaults(&compressor);
//jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
/* Size, resolution, etc */
compressor.image_width = width;
compressor.image_height = height;
jpeg_start_compress(&compressor, TRUE);
SetITUFax(&compressor);
if ((scan_line_out = (JSAMPROW) malloc(compressor.image_width*compressor.num_components)) == NULL)
return FALSE;
for (pos = 0; pos < srclen; pos += compressor.image_width*compressor.num_components)
{
scan_line_in = src + pos;
srgb_to_lab(s, scan_line_out, scan_line_in, compressor.image_width);
jpeg_write_scanlines(&compressor, &scan_line_out, 1);
}
free(scan_line_out);
jpeg_finish_compress(&compressor);
jpeg_destroy_compress(&compressor);
fclose(out);
*dst = outptr;
*dstlen = outsize;
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_itulab_to_itulab(tdata_t *dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t width, uint32_t height, char *emsg, size_t max_emsg_bytes)
{
struct jpeg_compress_struct compressor;
FILE *out;
char *outptr;
size_t outsize;
JSAMPROW scan_line_in;
tsize_t pos;
escape_route_t escape;
escape.error_message[0] = '\0';
emsg[0] = '\0';
#if defined(HAVE_OPEN_MEMSTREAM)
out = open_memstream(&outptr, &outsize);
#else
out = tmpfile();
#endif
if (out == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to open_memstream().");
return FALSE;
}
if (setjmp(escape.escape))
{
strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
emsg[max_emsg_bytes - 1] = '\0';
return FALSE;
}
compressor.err = jpeg_std_error(&error_handler);
compressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_compress(&compressor);
jpeg_stdio_dest(&compressor, out);
/* Force the destination color space */
compressor.in_color_space = JCS_YCbCr;
compressor.input_components = 3;
jpeg_set_defaults(&compressor);
//jpeg_set_quality(&compressor, quality, TRUE /* limit to baseline-JPEG values */);
/* Size, resolution, etc */
compressor.image_width = width;
compressor.image_height = height;
jpeg_start_compress(&compressor, TRUE);
SetITUFax(&compressor);
for (pos = 0; pos < srclen; pos += compressor.image_width*compressor.num_components)
{
scan_line_in = src + pos;
jpeg_write_scanlines(&compressor, &scan_line_in, 1);
}
jpeg_finish_compress(&compressor);
jpeg_destroy_compress(&compressor);
fclose(out);
*dst = outptr;
*dstlen = outsize;
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_itulab_to_srgb(lab_params_t *s, tdata_t dst, tsize_t *dstlen, tdata_t src, tsize_t srclen, uint32_t *width, uint32_t *height, char *emsg, size_t max_emsg_bytes)
{
struct jpeg_decompress_struct decompressor;
JSAMPROW scan_line_out;
JSAMPROW scan_line_in;
tsize_t pos;
FILE *in;
int m;
escape_route_t escape;
escape.error_message[0] = '\0';
emsg[0] = '\0';
#if defined(HAVE_OPEN_MEMSTREAM)
in = fmemopen(src, srclen, "r");
#else
in = tmpfile();
fwrite(src, 1, srclen, in);
rewind(in);
#endif
if (in == NULL)
{
if (emsg[0] == '\0')
strcpy(emsg, "Failed to fmemopen().");
return FALSE;
}
if (setjmp(escape.escape))
{
strncpy(emsg, escape.error_message, max_emsg_bytes - 1);
emsg[max_emsg_bytes - 1] = '\0';
if (emsg[0] == '\0')
strcpy(emsg, "Unspecified libjpeg error.");
return FALSE;
}
/* Create input decompressor. */
decompressor.err = jpeg_std_error(&error_handler);
decompressor.client_data = (void *) &escape;
error_handler.error_exit = jpg_error_exit;
error_handler.output_message = jpg_error_exit;
jpeg_create_decompress(&decompressor);
jpeg_stdio_src(&decompressor, in);
/* Needed in the case of ITU Lab input */
for (m = 0; m < 16; m++)
jpeg_save_markers(&decompressor, JPEG_APP0 + m, 0xFFFF);
/* Rewind the file */
if (fseek(in, 0, SEEK_SET) != 0)
return FALSE;
printf("XXXX 10\n");
/* Take the header */
jpeg_read_header(&decompressor, FALSE);
printf("XXXX 11\n");
/* Now we can force the input colorspace. For ITULab, we will use YCbCr as a "don't touch" marker */
decompressor.out_color_space = JCS_YCbCr;
printf("XXXX 12\n");
/* Sanity check and parameter check */
if (!isITUfax(s, decompressor.marker_list))
{
if (emsg[0] == '\0')
strcpy(emsg, "Is not ITUFAX.");
//return FALSE;
}
printf("XXXX 13\n");
/* Copy size, resolution, etc */
*width = decompressor.image_width;
*height = decompressor.image_height;
jpeg_start_decompress(&decompressor);
if ((scan_line_in = (JSAMPROW) malloc(decompressor.output_width*decompressor.num_components)) == NULL)
return FALSE;
for (pos = 0; decompressor.output_scanline < decompressor.output_height; pos += decompressor.output_width*decompressor.num_components)
{
scan_line_out = dst + pos;
jpeg_read_scanlines(&decompressor, &scan_line_in, 1);
lab_to_srgb(s, scan_line_out, scan_line_in, decompressor.output_width);
}
free(scan_line_in);
jpeg_finish_decompress(&decompressor);
jpeg_destroy_decompress(&decompressor);
fclose(in);
*dstlen = pos;
return TRUE;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t42_encode_set_options(t42_encode_state_t *s,
uint32_t l0,
int mx,
int options)
{
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_set_image_width(t42_encode_state_t *s, uint32_t image_width)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_set_image_length(t42_encode_state_t *s, uint32_t length)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t42_encode_abort(t42_encode_state_t *s)
{
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t42_encode_comment(t42_encode_state_t *s, const uint8_t comment[], size_t len)
{
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_get_byte(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_get_chunk(t42_encode_state_t *s, uint8_t buf[], int max_len)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t42_encode_get_image_width(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t42_encode_get_image_length(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_get_compressed_image_size(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_set_row_read_handler(t42_encode_state_t *s,
t4_row_read_handler_t handler,
void *user_data)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width, uint32_t image_length)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s,
uint32_t image_width,
uint32_t image_length,
t4_row_read_handler_t handler,
void *user_data)
{
if (s == NULL)
{
if ((s = (t42_encode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "T.42");
s->row_read_handler = handler;
s->row_read_user_data = user_data;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_release(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status)
{
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_put_byte(t42_decode_state_t *s, int byte)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_put_chunk(t42_decode_state_t *s,
const uint8_t data[],
size_t len)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_set_row_write_handler(t42_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_set_comment_handler(t42_decode_state_t *s,
uint32_t max_comment_len,
t4_row_write_handler_t handler,
void *user_data)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_set_image_size_constraints(t42_decode_state_t *s,
uint32_t max_xd,
uint32_t max_yd)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t42_decode_get_image_width(t42_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(uint32_t) t42_decode_get_image_length(t42_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_get_compressed_image_size(t42_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_new_plane(t42_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s,
t4_row_write_handler_t handler,
void *user_data)
{
if (s == NULL)
{
if ((s = (t42_decode_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "T.42");
s->row_write_handler = handler;
s->row_write_user_data = user_data;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s)
{
if (s->comment)
{
free(s->comment);
s->comment = NULL;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) t42_decode_free(t42_decode_state_t *s)
{
int ret;
ret = t42_decode_release(s);
free(s);
return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
...@@ -110,6 +110,7 @@ noinst_PROGRAMS = ademco_contactid_tests \ ...@@ -110,6 +110,7 @@ noinst_PROGRAMS = ademco_contactid_tests \
t38_non_ecm_buffer_tests \ t38_non_ecm_buffer_tests \
t4_tests \ t4_tests \
t4_t6_tests \ t4_t6_tests \
t42_tests \
t81_t82_arith_coding_tests \ t81_t82_arith_coding_tests \
t85_tests \ t85_tests \
time_scale_tests \ time_scale_tests \
...@@ -317,6 +318,9 @@ t4_tests_LDADD = $(LIBDIR) -lspandsp ...@@ -317,6 +318,9 @@ t4_tests_LDADD = $(LIBDIR) -lspandsp
t4_t6_tests_SOURCES = t4_t6_tests.c t4_t6_tests_SOURCES = t4_t6_tests.c
t4_t6_tests_LDADD = $(LIBDIR) -lspandsp t4_t6_tests_LDADD = $(LIBDIR) -lspandsp
t42_tests_SOURCES = t42_tests.c
t42_tests_LDADD = $(LIBDIR) -lspandsp
t81_t82_arith_coding_tests_SOURCES = t81_t82_arith_coding_tests.c t81_t82_arith_coding_tests_SOURCES = t81_t82_arith_coding_tests.c
t81_t82_arith_coding_tests_LDADD = $(LIBDIR) -lspandsp t81_t82_arith_coding_tests_LDADD = $(LIBDIR) -lspandsp
......
/*
* SpanDSP - a series of DSP components for telephony
*
* t42_tests.c - ITU T.42 JPEG for FAX image processing
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \file */
/*! \page t42_tests_page T.42 tests
\section t42_tests_page_sec_1 What does it do
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
//#if defined(WITH_SPANDSP_INTERNALS)
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
//#endif
#include "spandsp.h"
#define IN_FILE_NAME "../test-data/itu/t24/F21_200.TIF"
#define OUT_FILE_NAME "t42_tests_receive.tif"
uint8_t data5[50000000];
int data5_ptr = 0;
int plane = 0;
int bit_mask;
uint8_t xxx[3*256];
lab_params_t lab_param;
int write_row = 0;
typedef struct
{
float L;
float a;
float b;
} cielab_t;
#if 0
static void generate_luts(void)
{
float r;
uint8_t srgb;
int i;
printf("static const float srgb_to_linear[256] =\n");
printf("{\n");
for (i = 0; i < 256; i++)
{
/* Start with "i" as the sRGB value */
r = i/256.0f;
/* sRGB to Linear RGB */
r = (r > 0.04045f) ? powf((r + 0.055f)/1.055f, 2.4f) : r/12.92f;
printf((i < 255) ? " %f,\n" : " %f\n", r);
}
printf("};\n");
printf("static const uint8_t linear_to_srgb[4096] =\n");
printf("{\n");
for (i = 0; i < 4096; i++)
{
/* Start with "i" as the linear RGB value */
/* Linear RGB to sRGB */
r = i/4096.0f;
r = (r > 0.0031308f) ? (1.055f*powf(r, 1.0f/2.4f) - 0.055f) : r*12.92f;
r = floorf(r*256.0f);
srgb = (r < 0) ? 0 : (r <= 255) ? r : 255;
printf((i < 4095) ? " %d,\n" : " %d\n", srgb);
}
printf("};\n");
}
/*- End of function --------------------------------------------------------*/
#endif
static __inline__ uint16_t pack_16(uint8_t *s)
{
uint16_t value;
value = ((uint16_t) s[0] << 8) | (uint16_t) s[1];
return value;
}
/*- End of function --------------------------------------------------------*/
static __inline__ uint32_t pack_32(uint8_t *s)
{
uint32_t value;
value = ((uint32_t) s[0] << 24) | ((uint32_t) s[1] << 16) | ((uint32_t) s[2] << 8) | (uint32_t) s[3];
return value;
}
/*- End of function --------------------------------------------------------*/
static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t len)
{
int i;
int j;
for (i = 0; i < len; i++)
{
for (j = 0; j < 8; j++)
{
if ((buf[i] & (0x80 >> j)))
data5[data5_ptr + 3*(8*i + j)] |= bit_mask;
else
data5[data5_ptr + 3*(8*i + j)] &= ~bit_mask;
}
}
data5_ptr += 3*8*len;
write_row++;
return 0;
}
/*- End of function --------------------------------------------------------*/
static int t85_comment_handler(void *user_data, const uint8_t buf[], size_t len)
{
if (buf)
printf("Comment (%lu): %s\n", (unsigned long int) len, buf);
else
printf("Comment (%lu): ---\n", (unsigned long int) len);
return 0;
}
/*- End of function --------------------------------------------------------*/
int main(int argc, char *argv[])
{
char kk[256];
TIFF *tif;
uint32_t w;
uint32_t h;
tstrip_t nstrips;
uint32_t totdata;
tsize_t off;
uint8_t *data;
uint8_t *data2;
int row;
uint16_t compression;
int16_t photometric;
int16_t YCbCrSubsampleHoriz;
int16_t YCbCrSubsampleVert;
int16_t bits_per_pixel;
int16_t samples_per_pixel;
int16_t planar_config;
int bytes_per_row;
tsize_t outsize;
char *outptr;
const char *source_file;
int i;
int j;
int len;
tsize_t total_image_len;
tsize_t total_len;
int process_raw;
int result;
t85_decode_state_t t85_dec;
uint64_t start;
uint64_t end;
uint16_t *yyyL;
uint16_t *yyya;
uint16_t *yyyb;
uint16_t *yyyz;
printf("Demo of ITU/Lab library.\n");
TIFF_FX_init();
set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
#if 0
generate_luts();
#endif
source_file = (argc > 1) ? argv[1] : IN_FILE_NAME;
/* sRGB to ITU */
if ((tif = TIFFOpen(source_file, "r")) == NULL)
{
printf("Unable to open '%s'!\n", source_file);
return 1;
}
if (TIFFSetDirectory(tif, (tdir_t) 0) < 0)
{
printf("Unable to set directory '%s'!\n", source_file);
return 1;
}
w = 0;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
h = 0;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
bits_per_pixel = 0;
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_pixel);
samples_per_pixel = 0;
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
compression = 0;
TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
photometric = 0;
TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
YCbCrSubsampleHoriz = 0;
YCbCrSubsampleVert = 0;
TIFFGetField(tif, TIFFTAG_YCBCRSUBSAMPLING, &YCbCrSubsampleHoriz, &YCbCrSubsampleVert);
planar_config = 0;
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar_config);
off = 0;
yyyL = NULL;
yyya = NULL;
yyyb = NULL;
yyyz = NULL;
if (TIFFGetField(tif, TIFFTAG_COLORMAP, &yyyL, &yyya, &yyyb, &yyyz))
{
#if 0
/* Sweep the colormap in the proper order */
for (i = 0; i < (1 << bits_per_pixel); i++)
{
xxx[3*i] = (yyyL[i] >> 8) & 0xFF;
xxx[3*i + 1] = (yyya[i] >> 8) & 0xFF;
xxx[3*i + 2] = (yyyb[i] >> 8) & 0xFF;
printf("Map %3d - %5d %5d %5d\n", i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
}
#else
/* Sweep the colormap in the order that seems to work for l04x_02x.tif */
for (i = 0; i < (1 << bits_per_pixel); i++)
{
xxx[i] = (yyyL[i] >> 8) & 0xFF;
xxx[256 + i] = (yyya[i] >> 8) & 0xFF;
xxx[2*256 + i] = (yyyb[i] >> 8) & 0xFF;
}
#endif
lab_params_t lab;
set_lab_illuminant(&lab, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&lab, 0, 100, -85, 85, -75, 125, FALSE);
lab_to_srgb(&lab, xxx, xxx, 256);
for (i = 0; i < (1 << bits_per_pixel); i++)
printf("Map %3d - %5d %5d %5d\n", i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
}
else
{
printf("There is no colour map\n");
}
process_raw = FALSE;
printf("Compression is ");
switch (compression)
{
case COMPRESSION_CCITT_T4:
printf("T.4\n");
return 0;
case COMPRESSION_CCITT_T6:
printf("T.6\n");
return 0;
case COMPRESSION_T85:
printf("T.85\n");
process_raw = TRUE;
break;
case COMPRESSION_T43:
printf("T.43\n");
process_raw = TRUE;
break;
case COMPRESSION_JPEG:
printf("JPEG");
if (photometric == PHOTOMETRIC_ITULAB)
{
printf(" ITULAB");
process_raw = TRUE;
}
printf("\n");
break;
case COMPRESSION_NONE:
printf("No compression\n");
break;
default:
printf("Unexpected compression %d\n", compression);
break;
}
if (process_raw)
{
nstrips = TIFFNumberOfStrips(tif);
for (i = 0, total_image_len = 0; i < nstrips; i++)
total_image_len += TIFFRawStripSize(tif, i);
data = malloc(total_image_len);
for (i = 0, total_len = 0; i < nstrips; i++, total_len += len)
{
if ((len = TIFFReadRawStrip(tif, i, &data[total_len], total_image_len - total_len)) < 0)
{
printf("TIFF read error.\n");
return -1;
}
}
if (total_len != total_image_len)
printf("Size mismatch %d %d\n", total_len, total_image_len);
off = total_len;
switch (compression)
{
case COMPRESSION_CCITT_T4:
break;
case COMPRESSION_CCITT_T6:
break;
case COMPRESSION_T85:
printf("T.85 image %d bytes\n", total_len);
for (i = 0; i < 16; i++)
printf("0x%02x\n", data[i]);
t85_decode_init(&t85_dec, t85_row_write_handler, NULL);
t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
result = t85_decode_put_chunk(&t85_dec, data, total_len);
if (result == T85_MORE_DATA)
result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
len = t85_decode_get_compressed_image_size(&t85_dec);
printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
t85_decode_release(&t85_dec);
return 0;
case COMPRESSION_T43:
printf("T.43 image %d bytes\n", total_len);
if (pack_16(data) == 0xFFA8)
{
data += 2;
total_len -= 2;
for (;;)
{
if (pack_16(data) == 0xFFE1)
{
data += 2;
total_len -= 2;
len = pack_16(data);
data += len;
total_len -= len;
}
else if (pack_16(data) == 0xFFE3)
{
data += 2;
total_len -= 2;
len = pack_32(data);
data += len;
total_len -= len;
}
else
{
break;
}
}
}
bit_mask = 0x80;
t85_decode_init(&t85_dec, t85_row_write_handler, NULL);
t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
t85_dec.min_bit_planes = 1;
t85_dec.max_bit_planes = 8;
data5_ptr = 0;
result = t85_decode_put_chunk(&t85_dec, data, total_len);
len = t85_decode_get_compressed_image_size(&t85_dec);
printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
for (j = 1; j < t85_dec.bit_planes; j++)
{
bit_mask >>= 1;
data += len/8;
total_len -= len/8;
t85_decode_new_plane(&t85_dec);
data5_ptr = 0;
t85_decode_set_comment_handler(&t85_dec, 1000, t85_comment_handler, NULL);
result = t85_decode_put_chunk(&t85_dec, data, total_len);
len = t85_decode_get_compressed_image_size(&t85_dec);
printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
}
if (result == T85_MORE_DATA)
{
printf("More\n");
result = t85_decode_put_byte(&t85_dec, SIG_STATUS_END_OF_DATA);
}
len = t85_decode_get_compressed_image_size(&t85_dec);
printf("Compressed image is %d bytes, %d rows\n", len/8, write_row);
t85_decode_release(&t85_dec);
for (j = 0; j < data5_ptr; j += 3)
{
i = data5[j] & 0xFF;
//printf("%d %d %d %d %d %d\n", data5_ptr, j, i, xxx[3*i], xxx[3*i + 1], xxx[3*i + 2]);
data5[j] = xxx[3*i];
data5[j + 1] = xxx[3*i + 1];
data5[j + 2] = xxx[3*i + 2];
}
if ((tif = TIFFOpen(OUT_FILE_NAME, "w")) == NULL)
{
printf("Unable to open '%s'!\n", OUT_FILE_NAME);
return 1;
}
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
// libtiff requires IMAGELENGTH to be set before SAMPLESPERPIXEL,
// or StripOffsets and StripByteCounts will have SAMPLESPERPIXEL values
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
TIFFSetField(tif, TIFFTAG_XRESOLUTION, 200.0f);
TIFFSetField(tif, TIFFTAG_YRESOLUTION, 200.0f);
TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(tif, TIFFTAG_SOFTWARE, "spandsp");
TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "Test");
TIFFSetField(tif, TIFFTAG_DATETIME, "2011/02/03 12:30:45");
TIFFSetField(tif, TIFFTAG_MAKE, "soft-switch.org");
TIFFSetField(tif, TIFFTAG_MODEL, "spandsp");
TIFFSetField(tif, TIFFTAG_HOSTCOMPUTER, "i7.coppice.org");
for (off = 0, i = 0; i < h; off += w*3, i++)
{
TIFFWriteScanline(tif, data5 + off, i, 0);
}
TIFFWriteDirectory(tif);
TIFFClose(tif);
return 0;
case COMPRESSION_JPEG:
break;
}
}
else
{
printf("Width %d, height %d, bits %d, samples %d\n", w, h, bits_per_pixel, samples_per_pixel);
bytes_per_row = (bits_per_pixel + 7)/8;
bytes_per_row *= w*samples_per_pixel;
totdata = h*bytes_per_row;
printf("total %d\n", totdata);
/* Read the image into memory. */
data = malloc(totdata);
off = 0;
for (row = 0; row < h; row++)
{
if (TIFFReadScanline(tif, data + off, row, 0) < 0)
return 1;
off += bytes_per_row;
}
printf("total %d, off %d\n", totdata, off);
/* We now have the image in memory in RGB form */
if (photometric == PHOTOMETRIC_ITULAB)
{
printf("YYY ITULAB\n");
if (!t42_itulab_to_itulab((tdata_t) &outptr, &outsize, data, off, w, h, kk, 256))
{
printf("Failed to convert to ITULAB - %s\n", kk);
return 1;
}
free(data);
data = (uint8_t *) outptr;
off = outsize;
}
else
{
start = rdtscll();
if (photometric == PHOTOMETRIC_CIELAB)
{
printf("CIELAB\n");
/* The default luminant is D50 */
set_lab_illuminant(&lab_param, 96.422f, 100.000f, 82.521f);
set_lab_gamut(&lab_param, 0, 100, -128, 127, -128, 127, TRUE);
lab_to_srgb(&lab_param, data, data, w*h);
}
set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
if (!t42_srgb_to_itulab(&lab_param, (tdata_t) &outptr, &outsize, data, off, w, h, kk, 256))
{
printf("Failed to convert to ITULAB - %s\n", kk);
return 1;
}
end = rdtscll();
printf("Duration %" PRIu64 "\n", end - start);
free(data);
data = (uint8_t *) outptr;
off = outsize;
}
}
TIFFClose(tif);
printf("XXX - image is %d by %d, %d bytes\n", w, h, off);
/* We now have the image in memory in ITULAB form */
if ((tif = TIFFOpen(OUT_FILE_NAME, "w")) == NULL)
{
printf("Unable to open '%s'!\n", OUT_FILE_NAME);
return 1;
}
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
// libtiff requires IMAGELENGTH to be set before SAMPLESPERPIXEL,
// or StripOffsets and StripByteCounts will have SAMPLESPERPIXEL values
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1);
TIFFSetField(tif, TIFFTAG_XRESOLUTION, 200.0f);
TIFFSetField(tif, TIFFTAG_YRESOLUTION, 200.0f);
TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(tif, TIFFTAG_SOFTWARE, "spandsp");
TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "Test");
TIFFSetField(tif, TIFFTAG_DATETIME, "2011/02/03 12:30:45");
TIFFSetField(tif, TIFFTAG_MAKE, "soft-switch.org");
TIFFSetField(tif, TIFFTAG_MODEL, "spandsp");
TIFFSetField(tif, TIFFTAG_HOSTCOMPUTER, "i7.coppice.org");
if (1)
{
/* Most image processors won't know what to do with the ITULAB colorspace.
So we'll be converting it to RGB for portability. */
#if 1
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
#else
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
#endif
if (YCbCrSubsampleHoriz || YCbCrSubsampleVert)
TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, YCbCrSubsampleHoriz, YCbCrSubsampleVert);
bytes_per_row = (bits_per_pixel + 7)/8;
bytes_per_row *= w*samples_per_pixel;
totdata = h*bytes_per_row;
set_lab_illuminant(&lab_param, 0.9638f, 1.0f, 0.8245f);
set_lab_gamut(&lab_param, 0, 100, -85, 85, -75, 125, FALSE);
#if 0
start = rdtscll();
data2 = NULL;
totdata = 0;
t42_itulab_to_JPEG(&lab_param, (void **) &data2, &totdata, data, off, kk, 256);
end = rdtscll();
printf("Duration %" PRIu64 "\n", end - start);
printf("Compressed length %d (%p)\n", totdata, data2);
if (TIFFWriteRawStrip(tif, 0, data2, totdata) < 0)
{
printf("Failed to convert from ITULAB - %s\n", kk);
return 1;
}
free(data);
#else
data2 = malloc(totdata);
start = rdtscll();
if (!t42_itulab_to_srgb(&lab_param, data2, &off, data, off, &w, &h, kk, 256))
{
printf("Failed to convert from ITULAB - %s\n", kk);
return 1;
}
end = rdtscll();
printf("Duration %" PRIu64 "\n", end - start);
free(data);
off = 0;
bytes_per_row = (8 + 7)/8;
bytes_per_row *= (w*3);
for (row = 0; row < h; row++)
{
if (TIFFWriteScanline(tif, data2 + off, row, 0) < 0)
return 1;
off += bytes_per_row;
}
#endif
free(data2);
}
else
{
#if 1
/* If PHOTOMETRIC_ITULAB is not available the admin cannot enable color fax anyway.
This is done so that older libtiffs without it can build fine. */
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB);
#else
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
#endif
if (YCbCrSubsampleHoriz || YCbCrSubsampleVert)
TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, YCbCrSubsampleHoriz, YCbCrSubsampleVert);
if (TIFFWriteRawStrip(tif, 0, (tdata_t) data, off) == -1)
{
printf("Write error to TIFF file\n");
return 1;
}
free(data);
}
TIFFWriteDirectory(tif);
TIFFClose(tif);
printf("Done!\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论