*
* Copyright (C) 2020 Thierry HUCHARD <thierry@ordissimo.com>
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
*
* See LICENSE for license terms and conditions
*/
#include "airscan.h"
#include <tiffio.h>
#include <stdint.h>
#include <string.h>
*/
typedef struct {
image_decoder decoder;
TIFF* tif;
uint32_t current_line;
unsigned char *mem_file;
of the tiff file. */
toff_t offset_file;
of the tiff file. */
tsize_t size_file;
image_decoder *jpeg_decoder;
const void *jpeg_data;
size_t jpeg_size;
uint16_t bytes_per_pixel;
uint32_t image_width;
uint32_t image_height;
} image_decoder_tiff;
static void
image_decoder_tiff_reset (image_decoder *decoder);
static error
image_decoder_tiff_setup_old_jpeg (image_decoder_tiff *tiff);
*
* Works like read(2)
*/
static tsize_t
image_decoder_tiff_readproc (thandle_t handle, tdata_t data, tsize_t n)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) handle;
tsize_t n_remaining, n_copy;
void *src_addr;
n_copy = n;
if( (tsize_t) tiff->offset_file >= tiff->size_file) {
n_remaining = 0;
} else {
n_remaining = tiff->size_file - tiff->offset_file;
}
if (n_copy > n_remaining) {
n_copy = n_remaining;
}
if (n_copy <= 0) {
return 0;
}
src_addr = (void*)(&(tiff->mem_file[tiff->offset_file]));
memcpy((void*)data, src_addr, n_copy);
tiff->offset_file += n_copy;
return n_copy;
}
*
* Works like write(2). Not needed for decoder
*/
static tsize_t
image_decoder_tiff_writeproc (thandle_t handle, tdata_t data, tsize_t n)
{
(void) handle;
(void) data;
(void) n;
return -1;
}
*
* Works like lseek(2)
*/
static toff_t
image_decoder_tiff_seekproc (thandle_t handle, toff_t ofs, int whence)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) handle;
toff_t new_offset;
switch (whence) {
case SEEK_SET:
new_offset = ofs;
break;
case SEEK_CUR:
new_offset = tiff->offset_file + ofs;
break;
default:
log_internal_error(NULL);
return -1;
}
tiff->offset_file = new_offset;
return tiff->offset_file;
}
*
* Works like close(2)
*/
static int
image_decoder_tiff_closeproc (thandle_t handle)
{
(void) handle;
return 0;
}
*
* Returns file size, in bytes
*/
static toff_t
image_decoder_tiff_sizeproc (thandle_t handle)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) handle;
return (toff_t) (tiff->size_file);
}
*
* Works like mmap(2). Not required and not implemented
*/
static int
image_decoder_tiff_mapproc (thandle_t fd, tdata_t *pbase, toff_t *psize)
{
(void) fd;
(void) pbase;
(void) psize;
return (0);
}
*
* Works like munmap(2). Not required and not implemented
*/
static void
image_decoder_tiff_unmapproc (thandle_t fd, tdata_t base, toff_t size)
{
(void) fd;
(void) base;
(void) size;
}
*/
static void
image_decoder_tiff_free (image_decoder *decoder)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->tif) {
TIFFClose(tiff->tif);
}
image_decoder_free(tiff->jpeg_decoder);
mem_free(tiff);
}
*/
static error
image_decoder_tiff_begin (image_decoder *decoder, const void *data,
size_t size)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
error err = NULL;
uint16_t compression;
tiff->mem_file = (unsigned char*)data;
tiff->offset_file = 0;
tiff->size_file = size;
tiff->tif = TIFFClientOpen("airscan TIFF Interface",
"r", (image_decoder_tiff*)(tiff),
image_decoder_tiff_readproc, image_decoder_tiff_writeproc,
image_decoder_tiff_seekproc, image_decoder_tiff_closeproc,
image_decoder_tiff_sizeproc,
image_decoder_tiff_mapproc, image_decoder_tiff_unmapproc);
if (tiff->tif == NULL) {
return ERROR("TIFF: invalid open memory");
}
if (tiff->tif == NULL) {
return ERROR("TIFF: broken image");;
}
if (!TIFFGetField(tiff->tif, TIFFTAG_SAMPLESPERPIXEL,
&tiff->bytes_per_pixel)) {
err = ERROR("TIFF: can't get TIFFTAG_SAMPLESPERPIXEL");
goto FAIL;
}
if (!TIFFGetField(tiff->tif, TIFFTAG_IMAGEWIDTH, &tiff->image_width)) {
err = ERROR("TIFF: can't get TIFFTAG_IMAGEWIDTH");
goto FAIL;
}
if (!TIFFGetField(tiff->tif, TIFFTAG_IMAGELENGTH, &tiff->image_height)) {
err = ERROR("TIFF: can't get TIFFTAG_IMAGELENGTH");
goto FAIL;
}
if (!TIFFGetField(tiff->tif, TIFFTAG_COMPRESSION, &compression)) {
err = ERROR("TIFF: can't get TIFFTAG_COMPRESSION");
goto FAIL;
}
switch (compression) {
case COMPRESSION_OJPEG:
err = image_decoder_tiff_setup_old_jpeg(tiff);
break;
}
if (err == NULL && tiff->jpeg_data != NULL) {
err = image_decoder_begin(tiff->jpeg_decoder,
tiff->jpeg_data, tiff->jpeg_size);
}
if (err != NULL) {
goto FAIL;
}
return NULL;
FAIL:
image_decoder_tiff_reset(decoder);
return err;
}
*/
static void
image_decoder_tiff_reset (image_decoder *decoder)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->tif != NULL) {
TIFFClose(tiff->tif);
tiff->tif = NULL;
}
if (tiff->jpeg_data != NULL) {
image_decoder_reset(tiff->jpeg_decoder);
tiff->jpeg_data = NULL;
tiff->jpeg_size = 0;
}
}
*/
static int
image_decoder_tiff_get_bytes_per_pixel (image_decoder *decoder)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->jpeg_data != NULL) {
return image_decoder_get_bytes_per_pixel(tiff->jpeg_decoder);
}
return (int) tiff->bytes_per_pixel;
}
*/
static void
image_decoder_tiff_get_params (image_decoder *decoder, SANE_Parameters *params)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->jpeg_data != NULL) {
image_decoder_get_params(tiff->jpeg_decoder, params);
return;
}
params->last_frame = SANE_TRUE;
params->pixels_per_line = (SANE_Int) tiff->image_width;
params->lines = (SANE_Int) tiff->image_height;
params->depth = 8;
params->bytes_per_line = params->pixels_per_line *
(SANE_Int) tiff->bytes_per_pixel;
if (tiff->bytes_per_pixel == 1) {
params->format = SANE_FRAME_GRAY;
} else {
params->format = SANE_FRAME_RGB;
}
}
*/
static error
image_decoder_tiff_set_window (image_decoder *decoder, image_window *win)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->jpeg_data != NULL) {
return image_decoder_set_window(tiff->jpeg_decoder, win);
}
win->x_off = win->y_off = 0;
win->wid = (int) tiff->image_width;
win->hei = (int) tiff->image_height;
return NULL;
}
*/
static error
image_decoder_tiff_read_line (image_decoder *decoder, void *buffer)
{
image_decoder_tiff *tiff = (image_decoder_tiff*) decoder;
if (tiff->jpeg_data != NULL) {
return image_decoder_read_line(tiff->jpeg_decoder, buffer);
}
if (tiff->current_line >= tiff->image_height) {
return ERROR("TIFF: end of file");
}
if (TIFFReadScanline(tiff->tif, buffer, tiff->current_line, 0) == -1) {
return ERROR("TIFF: read scanline error");
}
tiff->current_line ++;
return NULL;
}
*/
image_decoder*
image_decoder_tiff_new (void)
{
image_decoder_tiff *tiff = mem_new(image_decoder_tiff, 1);
tiff->decoder.content_type = "image/tiff";
tiff->decoder.free = image_decoder_tiff_free;
tiff->decoder.begin = image_decoder_tiff_begin;
tiff->decoder.reset = image_decoder_tiff_reset;
tiff->decoder.get_bytes_per_pixel = image_decoder_tiff_get_bytes_per_pixel;
tiff->decoder.get_params = image_decoder_tiff_get_params;
tiff->decoder.set_window = image_decoder_tiff_set_window;
tiff->decoder.read_line = image_decoder_tiff_read_line;
tiff->jpeg_decoder = image_decoder_jpeg_new();
return &tiff->decoder;
}
*
* Note, libtiff doesn't correctly decode OLD JPEG in line-by-line
* mode (using TIFFReadScanline() interface), so we have to delegate
* this work directly to the JPEG decoder
*/
static error
image_decoder_tiff_setup_old_jpeg (image_decoder_tiff *tiff)
{
uint64_t jpeg_off, jpeg_size;
*
* Note, libtiff validates these tags by itself, and
* if they are not present or invalid, returned value
* for TIFFTAG_JPEGIFOFFSET will be 0
*/
TIFFGetField(tiff->tif, TIFFTAG_JPEGIFOFFSET, &jpeg_off);
TIFFGetField(tiff->tif, TIFFTAG_JPEGIFBYTECOUNT, &jpeg_size);
if (jpeg_off != 0) {
tiff->jpeg_data = tiff->mem_file + (size_t) jpeg_off;
tiff->jpeg_size = (size_t) jpeg_size;
return NULL;
}
* using TIFFTAG_JPEGQTABLES/TIFFTAG_JPEGDCTABLES/TIFFTAG_JPEGIFBYTECOUNT
* tags
*/
return ERROR("TIFF: unsupported old JPEG compression");
}
*/