summaryrefslogtreecommitdiff
path: root/backend/pixma.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/pixma.c')
-rw-r--r--backend/pixma.c356
1 files changed, 340 insertions, 16 deletions
diff --git a/backend/pixma.c b/backend/pixma.c
index d50e4ca..d33a74e 100644
--- a/backend/pixma.c
+++ b/backend/pixma.c
@@ -1,6 +1,6 @@
/* SANE - Scanner Access Now Easy.
- Copyright (C) 2011-2016 Rolf Bensch <rolf at bensch hyphen online dot de>
+ Copyright (C) 2011-2019 Rolf Bensch <rolf at bensch hyphen online dot de>
Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
@@ -66,6 +66,7 @@
# include "../include/sane/sanei_thread.h"
# include "../include/sane/sanei_backend.h"
# include "../include/sane/sanei_config.h"
+# include "../include/sane/sanei_jpeg.h"
#ifdef NDEBUG
# define PDBG(x)
@@ -122,8 +123,26 @@ typedef struct pixma_sane_t
SANE_Pid reader_taskid;
int wpipe, rpipe;
SANE_Bool reader_stop;
+
+ /* Valid for JPEG source */
+ djpeg_dest_ptr jdst;
+ struct jpeg_decompress_struct jpeg_cinfo;
+ struct jpeg_error_mgr jpeg_err;
+ SANE_Bool jpeg_header_seen;
} pixma_sane_t;
+typedef struct
+{
+ struct jpeg_source_mgr jpeg;
+
+ pixma_sane_t *s;
+ JOCTET *buffer;
+
+ SANE_Byte *linebuffer;
+ SANE_Int linebuffer_size;
+ SANE_Int linebuffer_index;
+} pixma_jpeg_src_mgr;
+
static const char vendor_str[] = "CANON";
static const char type_str[] = "multi-function peripheral";
@@ -291,7 +310,7 @@ update_button_state (pixma_sane_t * ss, SANE_Int * info)
}
if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w)
- {
+ {
*info |= SANE_INFO_RELOAD_OPTIONS;
OVAL (opt_button_1).w = b1;
OVAL (opt_button_2).w = b2;
@@ -642,9 +661,9 @@ control_option (pixma_sane_t * ss, SANE_Int n,
SANE_Int dummy;
/* info may be null, better to set a dummy here then test everywhere */
- if (info == NULL)
+ if (info == NULL)
info = &dummy;
-
+
cfg = pixma_get_config (ss->s);
/* PDBG (pixma_dbg (4, "*control_option***** n = %u, a = %u\n", n, a)); */
@@ -697,7 +716,7 @@ control_option (pixma_sane_t * ss, SANE_Int n,
ss->button_option_is_cached[ BUTTON_GROUP_INDEX(n) ] = 0;
}
- /* now deal with getting and setting of options */
+ /* now deal with getting and setting of options */
switch (SOD (n).type)
{
case SANE_TYPE_BOOL:
@@ -1094,7 +1113,7 @@ terminate_reader_task (pixma_sane_t * ss, int *exit_code)
pid = ss->reader_taskid;
if (!sanei_thread_is_valid (pid))
- return -1;
+ return pid;
if (sanei_thread_is_forked ())
{
sanei_thread_kill (pid);
@@ -1105,7 +1124,7 @@ terminate_reader_task (pixma_sane_t * ss, int *exit_code)
/* pixma_cancel (ss->s); What is this for ? Makes end-of-scan buggy => removing */
}
result = sanei_thread_waitpid (pid, &status);
- ss->reader_taskid = -1;
+ sanei_thread_invalidate (ss->reader_taskid);
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
ss->idle = SANE_TRUE;
@@ -1119,7 +1138,8 @@ terminate_reader_task (pixma_sane_t * ss, int *exit_code)
else
{
PDBG (pixma_dbg (1, "WARNING:waitpid() failed %s\n", strerror (errno)));
- return -1;
+ sanei_thread_invalidate (pid);
+ return pid;
}
}
@@ -1159,7 +1179,7 @@ start_reader_task (pixma_sane_t * ss)
if (is_forked)
{
pid = sanei_thread_begin (reader_process, ss);
- if (pid > 0)
+ if (sanei_thread_is_valid (pid))
{
close (ss->wpipe);
ss->wpipe = -1;
@@ -1184,6 +1204,245 @@ start_reader_task (pixma_sane_t * ss)
return 0;
}
+/* libJPEG API callbacks */
+static void
+jpeg_init_source(j_decompress_ptr __sane_unused__ cinfo)
+{
+ /* No-op */
+}
+
+static void
+jpeg_term_source(j_decompress_ptr __sane_unused__ cinfo)
+{
+ /* No-op */
+}
+
+static boolean
+jpeg_fill_input_buffer(j_decompress_ptr cinfo)
+{
+ pixma_jpeg_src_mgr *mgr = (pixma_jpeg_src_mgr *)cinfo->src;
+ int size;
+ int retry;
+
+ for (retry = 0; retry < 30; retry ++ )
+ {
+ size = read (mgr->s->rpipe, mgr->buffer, 1024);
+ if (size == 0)
+ {
+ return FALSE;
+ }
+ else if (size < 0)
+ {
+ sleep (1);
+ }
+ else
+ {
+ mgr->jpeg.next_input_byte = mgr->buffer;
+ mgr->jpeg.bytes_in_buffer = size;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ pixma_jpeg_src_mgr *mgr = (pixma_jpeg_src_mgr *)cinfo->src;
+
+ if (num_bytes > 0)
+ {
+ /* Read and throw away extra */
+ while (num_bytes > (long)mgr->jpeg.bytes_in_buffer)
+ {
+ num_bytes -= (long)mgr->jpeg.bytes_in_buffer;
+ jpeg_fill_input_buffer(cinfo);
+ }
+
+ /* Update jpeg info structure with leftover */
+ mgr->jpeg.next_input_byte += (size_t) num_bytes;
+ mgr->jpeg.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+/* Pixma JPEG reader helpers */
+static SANE_Status
+pixma_jpeg_start(pixma_sane_t *s)
+{
+ pixma_jpeg_src_mgr *mgr;
+
+ s->jpeg_cinfo.err = jpeg_std_error(&s->jpeg_err);
+
+ jpeg_create_decompress(&s->jpeg_cinfo);
+
+ s->jpeg_cinfo.src = (struct jpeg_source_mgr *)(*s->jpeg_cinfo.mem->alloc_small)((j_common_ptr)&s->jpeg_cinfo,
+ JPOOL_PERMANENT, sizeof(pixma_jpeg_src_mgr));
+
+ memset(s->jpeg_cinfo.src, 0, sizeof(pixma_jpeg_src_mgr));
+
+ mgr = (pixma_jpeg_src_mgr *)s->jpeg_cinfo.src;
+ mgr->s = s;
+
+ mgr->buffer = (JOCTET *)(*s->jpeg_cinfo.mem->alloc_small)((j_common_ptr)&s->jpeg_cinfo,
+ JPOOL_PERMANENT,
+ 1024 * sizeof(JOCTET));
+
+ mgr->jpeg.init_source = jpeg_init_source;
+ mgr->jpeg.fill_input_buffer = jpeg_fill_input_buffer;
+ mgr->jpeg.skip_input_data = jpeg_skip_input_data;
+ mgr->jpeg.resync_to_restart = jpeg_resync_to_restart;
+ mgr->jpeg.term_source = jpeg_term_source;
+ mgr->jpeg.bytes_in_buffer = 0;
+ mgr->jpeg.next_input_byte = NULL;
+
+ s->jpeg_header_seen = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+pixma_jpeg_read_header(pixma_sane_t *s)
+{
+ pixma_jpeg_src_mgr *src = (pixma_jpeg_src_mgr *)s->jpeg_cinfo.src;
+
+ if (jpeg_read_header(&s->jpeg_cinfo, TRUE))
+ {
+ s->jdst = sanei_jpeg_jinit_write_ppm(&s->jpeg_cinfo);
+
+ if (jpeg_start_decompress(&s->jpeg_cinfo))
+ {
+ int size;
+
+ DBG(3, "%s: w: %d, h: %d, components: %d\n",
+ __func__,
+ s->jpeg_cinfo.output_width, s->jpeg_cinfo.output_height,
+ s->jpeg_cinfo.output_components);
+
+ size = s->jpeg_cinfo.output_width * s->jpeg_cinfo.output_components * 1;
+
+ src->linebuffer = (*s->jpeg_cinfo.mem->alloc_large)((j_common_ptr)&s->jpeg_cinfo,
+ JPOOL_PERMANENT, size);
+
+ src->linebuffer_size = 0;
+ src->linebuffer_index = 0;
+
+ s->jpeg_header_seen = 1;
+
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(0, "%s: decompression failed\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ DBG(0, "%s: cannot read JPEG header\n", __func__);
+ return SANE_STATUS_IO_ERROR;
+ }
+}
+
+static void
+pixma_jpeg_finish(pixma_sane_t *ss)
+{
+ jpeg_destroy_decompress(&ss->jpeg_cinfo);
+}
+
+static void
+pixma_jpeg_read(pixma_sane_t *ss, SANE_Byte *data,
+ SANE_Int max_length, SANE_Int *length)
+{
+ struct jpeg_decompress_struct cinfo = ss->jpeg_cinfo;
+ pixma_jpeg_src_mgr *src = (pixma_jpeg_src_mgr *)ss->jpeg_cinfo.src;
+
+ int l;
+
+ *length = 0;
+
+ /* copy from line buffer if available */
+ if (src->linebuffer_size && src->linebuffer_index < src->linebuffer_size)
+ {
+ *length = src->linebuffer_size - src->linebuffer_index;
+
+ if (*length > max_length)
+ *length = max_length;
+
+ memcpy(data, src->linebuffer + src->linebuffer_index, *length);
+ src->linebuffer_index += *length;
+
+ return;
+ }
+
+ if (cinfo.output_scanline >= cinfo.output_height)
+ {
+ *length = 0;
+ return;
+ }
+
+ /* scanlines of decompressed data will be in ss->jdst->buffer
+ * only one line at time is supported
+ */
+
+ l = jpeg_read_scanlines(&cinfo, ss->jdst->buffer, 1);
+ if (l == 0)
+ return;
+
+ /* from ss->jdst->buffer to linebuffer
+ * linebuffer holds width * bytesperpixel
+ */
+
+ (*ss->jdst->put_pixel_rows)(&cinfo, ss->jdst, 1, (char *)src->linebuffer);
+
+ *length = ss->sp.w * ss->sp.channels;
+ /* Convert RGB into grayscale */
+ if (ss->sp.channels == 1)
+ {
+ unsigned int i;
+ unsigned char *d = (unsigned char *)src->linebuffer;
+ unsigned char *s = (unsigned char *)src->linebuffer;
+ for (i = 0; i < ss->sp.w; i++)
+ {
+ /* Using BT.709 luma formula, fixed-point */
+ int sum = ( s[0]*2126 + s[1]*7152 + s[2]*722 );
+ *d = sum / 10000;
+ d ++;
+ s += 3;
+ }
+ }
+
+ /* Maybe pack into lineary binary image */
+ if (ss->sp.depth == 1)
+ {
+ *length /= 8;
+ unsigned int i;
+ unsigned char *d = (unsigned char *)src->linebuffer;
+ unsigned char *s = (unsigned char *)src->linebuffer;
+ unsigned char b = 0;
+ for (i = 1; i < ss->sp.w + 1; i++)
+ {
+ if (*(s++) > 127)
+ b = (b << 1) | 0;
+ else
+ b = (b << 1) | 1;
+ }
+ if ((i % 8) == 0)
+ *(d++) = b;
+ }
+
+ src->linebuffer_size = *length;
+ src->linebuffer_index = 0;
+
+ if (*length > max_length)
+ *length = max_length;
+
+ memcpy(data, src->linebuffer + src->linebuffer_index, *length);
+ src->linebuffer_index += *length;
+}
+
+
+
static SANE_Status
read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
{
@@ -1199,7 +1458,35 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
if (ss->cancel)
/* ss->rpipe has already been closed by sane_cancel(). */
return SANE_STATUS_CANCELLED;
- count = read (ss->rpipe, buf, size);
+ if (ss->sp.mode_jpeg && !ss->jpeg_header_seen)
+ {
+ status = pixma_jpeg_read_header(ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ close (ss->rpipe);
+ pixma_jpeg_finish(ss);
+ ss->rpipe = -1;
+ if (sanei_thread_is_valid (terminate_reader_task (ss, &status))
+ && status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ else
+ {
+ /* either terminate_reader_task failed or
+ rpipe was closed but we expect more data */
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+
+ if (ss->sp.mode_jpeg)
+ {
+ count = -1;
+ pixma_jpeg_read(ss, buf, size, &count);
+ }
+ else
+ count = read (ss->rpipe, buf, size);
}
while (count == -1 && errno == EINTR);
@@ -1215,6 +1502,8 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
close (ss->rpipe);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
+ if (ss->sp.mode_jpeg)
+ pixma_jpeg_finish(ss);
return SANE_STATUS_IO_ERROR;
}
@@ -1229,6 +1518,8 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
close (ss->rpipe);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
+ if (ss->sp.mode_jpeg)
+ pixma_jpeg_finish(ss);
}
else if (count == 0)
{
@@ -1236,6 +1527,8 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
PRIu64" bytes received, %"PRIu64" bytes expected\n",
ss->image_bytes_read, ss->sp.image_size));
close (ss->rpipe);
+ if (ss->sp.mode_jpeg)
+ pixma_jpeg_finish(ss);
ss->rpipe = -1;
if (sanei_thread_is_valid (terminate_reader_task (ss, &status))
&& status != SANE_STATUS_GOOD)
@@ -1376,7 +1669,7 @@ sane_open (SANE_String_Const name, SANE_Handle * h)
return SANE_STATUS_NO_MEM;
ss->next = first_scanner;
first_scanner = ss;
- ss->reader_taskid = -1;
+ sanei_thread_initialize (ss->reader_taskid);
ss->wpipe = -1;
ss->rpipe = -1;
ss->idle = SANE_TRUE;
@@ -1441,7 +1734,7 @@ sane_control_option (SANE_Handle h, SANE_Int n,
if (!ss->idle && a != SANE_ACTION_GET_VALUE)
{
PDBG (pixma_dbg (3, "Warning: !idle && !SANE_ACTION_GET_VALUE\n"));
- if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
+ if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
return SANE_STATUS_INVAL;
}
@@ -1454,16 +1747,16 @@ sane_control_option (SANE_Handle h, SANE_Int n,
if ((opt->sod.type != SANE_TYPE_BUTTON && !v) ||
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
- break;
+ break;
case SANE_ACTION_SET_AUTO:
if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) ||
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
- break;
+ break;
case SANE_ACTION_GET_VALUE:
if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
- break;
+ break;
default:
return SANE_STATUS_UNSUPPORTED;
}
@@ -1527,6 +1820,19 @@ sane_start (SANE_Handle h)
ss->page_count++;
if (calc_scan_param (ss, &ss->sp) < 0)
return SANE_STATUS_INVAL;
+
+ /* Prepare the JPEG decompressor, if needed */
+ if (ss->sp.mode_jpeg)
+ {
+ SANE_Status status;
+ status = pixma_jpeg_start(ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ PDBG (pixma_dbg(1, "%s: pixma_jpeg_start: %s\n", __func__, sane_strstatus(status)) );
+ return status;
+ }
+ }
+
ss->image_bytes_read = 0;
/* TODO: Check paper here in sane_start(). A function like
pixma_get_status() is needed. */
@@ -1538,6 +1844,22 @@ sane_start (SANE_Handle h)
ss->last_read_status = SANE_STATUS_GOOD;
ss->scanning = SANE_TRUE;
ss->idle = SANE_FALSE;
+ if (ss->sp.mode_jpeg && !ss->jpeg_header_seen)
+ {
+ SANE_Status status;
+ status = pixma_jpeg_read_header(ss);
+ if (status != SANE_STATUS_GOOD)
+ {
+ close (ss->rpipe);
+ pixma_jpeg_finish(ss);
+ ss->rpipe = -1;
+ if (sanei_thread_is_valid (terminate_reader_task (ss, &error))
+ && error != SANE_STATUS_GOOD)
+ {
+ return error;
+ }
+ }
+ }
}
return map_error (error);
}
@@ -1635,6 +1957,8 @@ sane_cancel (SANE_Handle h)
if (ss->idle)
return;
close (ss->rpipe);
+ if (ss->sp.mode_jpeg)
+ pixma_jpeg_finish(ss);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
ss->idle = SANE_TRUE;
@@ -1799,7 +2123,7 @@ type int original
type int target
default 0
title Target operation type
- cap soft_detect advanced
+ cap soft_detect advanced
type int scan-resolution
default 0