gnome-scan r716 - in trunk: . modules/gsane
- From: bersace svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-scan r716 - in trunk: . modules/gsane
- Date: Sat, 20 Dec 2008 15:39:15 +0000 (UTC)
Author: bersace
Date: Sat Dec 20 15:39:14 2008
New Revision: 716
URL: http://svn.gnome.org/viewvc/gnome-scan?rev=716&view=rev
Log:
Implement acquisition in GSaneScanner.
Added:
trunk/modules/gsane/gsane-processor.c
trunk/modules/gsane/gsane-processor.h
Modified:
trunk/ChangeLog
trunk/modules/gsane/Makefile.am
trunk/modules/gsane/gsane-scanner.c
Modified: trunk/modules/gsane/Makefile.am
==============================================================================
--- trunk/modules/gsane/Makefile.am (original)
+++ trunk/modules/gsane/Makefile.am Sat Dec 20 15:39:14 2008
@@ -20,6 +20,8 @@
gsane-option-handler-generic.c \
gsane-option-manager.h \
gsane-option-manager.c \
+ gsane-processor.h \
+ gsane-processor.c \
gsane-scanner.h \
gsane-scanner.c \
gsane-backend.h \
Added: trunk/modules/gsane/gsane-processor.c
==============================================================================
--- (empty file)
+++ trunk/modules/gsane/gsane-processor.c Sat Dec 20 15:39:14 2008
@@ -0,0 +1,201 @@
+/* GSane - SANE GNOME Scan backend
+ * Copyright  2007-2008 Ãtienne Bersac <bersace gnome org>
+ *
+ * GSane is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GSane 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 GSane. If not, write to:
+ *
+ * the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA
+ */
+
+#include "gsane-processor.h"
+
+typedef void (*GSaneProcessorFunc) (GSaneProcessor *self, guchar *buf, guint buf_len);
+
+struct _GSaneProcessorPrivate {
+ const SANE_Parameters *params;
+ gdouble bytes_per_pixel;
+ /* total bytes read*/
+ volatile guint bytes_read;
+ /* output format of processor */
+ Babl* format;
+ GeglBuffer *buffer;
+ volatile GeglRectangle rect;
+ /* processor function */
+ GSaneProcessorFunc process;
+};
+
+void
+gsane_processor_init(GSaneProcessor *self)
+{
+ self->priv = g_new0(GSaneProcessorPrivate, 1);
+ return;
+}
+
+
+static const gchar*
+gsane_processor_get_babl_color_model(GSaneProcessor *self)
+{
+ const gchar *model = NULL;
+
+ switch(self->priv->params->format) {
+ case SANE_FRAME_GRAY:
+ model = "Y";
+ break;
+ case SANE_FRAME_RGB:
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ model = "RGB";
+ break;
+ default:
+ g_warning("Unsupported SANE frame format.");
+ break;
+ }
+
+ return model;
+}
+
+static inline guint
+gsane_processor_get_color_depth(GSaneProcessor *self)
+{
+ return MAX(8, (((self->priv->params->depth + 7)/8)*8));
+}
+
+/* Compute the pixel format in which data from scanner will be
+ translated to. */
+static Babl*
+gsane_processor_get_babl_format(GSaneProcessor *self)
+{
+ const gchar* model = gsane_processor_get_babl_color_model(self);
+ if (!model)
+ return NULL;
+ guint depth = gsane_processor_get_color_depth(self);
+ gchar* name = g_strdup_printf("%s u%d", model, depth);
+ g_debug("Format is %s", name);
+ return babl_format(name);
+}
+
+static void
+gsane_processor_process_void(GSaneProcessor *self, guchar *buf, guint buf_len)
+{
+}
+
+/* process Y or RGB 8/16 bit into â 8/16 bit. */
+static void
+gsane_processor_process_8bit(GSaneProcessor *self, guchar *buf, guint buf_len)
+{
+ GeglRectangle roi = self->priv->rect;
+ gegl_buffer_set (self->priv->buffer, &roi, self->priv->format, buf, GEGL_AUTO_ROWSTRIDE);
+}
+
+static GSaneProcessorFunc
+gsane_processor_get_func(GSaneProcessor *self)
+{
+ GSaneProcessorFunc func = gsane_processor_process_void;
+ switch(self->priv->params->format) {
+ case SANE_FRAME_RGB:
+ case SANE_FRAME_GRAY:
+ if (self->priv->params->depth/8)
+ func = gsane_processor_process_8bit;
+ else
+ g_warning("Unsupported %dbit frame format.", self->priv->params->depth);
+ break;
+ default:
+ g_warning("Unsupported SANE frame format.");
+ break;
+ }
+ return func;
+}
+
+GeglBuffer*
+gsane_processor_prepare(GSaneProcessor *self, SANE_Parameters* params)
+{
+ GeglRectangle extent = {
+ .x = 0, .y = 0,
+ .width = params->pixels_per_line,
+ .height = params->lines,
+ };
+
+ self->priv->params = params;
+ self->priv->bytes_per_pixel = (gdouble)params->bytes_per_line/(gdouble)params->pixels_per_line;
+ self->priv->process = gsane_processor_get_func(self);
+ g_return_val_if_fail(self->priv->process, NULL);
+ self->priv->format = gsane_processor_get_babl_format(self);
+ g_return_val_if_fail(self->priv->format, NULL);
+
+ self->priv->buffer = gegl_buffer_new(&extent, self->priv->format);
+ return self->priv->buffer;
+}
+
+void
+gsane_processor_process(GSaneProcessor *self, guchar *buf, guint buf_len)
+{
+ g_return_if_fail(self->priv->process);
+ guchar* next_buf = NULL;
+ guint next_buf_len = 0;
+
+ /* define the rect corresponding buf */
+ self->priv->rect.y = self->priv->bytes_read / self->priv->params->bytes_per_line;
+ self->priv->rect.x = self->priv->bytes_read % self->priv->params->bytes_per_line;
+ guint pixel_to_end_of_line = self->priv->params->pixels_per_line - self->priv->rect.x;
+ guint pixels_in_buf = (gdouble)buf_len / self->priv->bytes_per_pixel;
+ self->priv->rect.width = MIN (pixels_in_buf - self->priv->rect.x, pixel_to_end_of_line);
+
+ /* compute the height of the rect and determine whether buf is
+ in several rect */
+ if (self->priv->rect.x > 0) {
+ /* first rect is one line */
+ self->priv->rect.height = 1;
+ if (pixels_in_buf > self->priv->rect.width) {
+ /* recursion for the rest (will start at x=0) */
+ guint processed_bytes = self->priv->rect.width * self->priv->bytes_per_pixel;
+ next_buf = buf + processed_bytes;
+ next_buf_len = buf_len - processed_bytes;
+ }
+ }
+ else {
+ /* multiline */
+ self->priv->rect.height = pixels_in_buf / self->priv->rect.width;
+ guint last_line_length = pixels_in_buf % self->priv->rect.width;
+ if (last_line_length > 0) {
+ /* the last line needs recursion */
+ guint left_bytes = last_line_length * self->priv->bytes_per_pixel;
+ next_buf = buf + buf_len - left_bytes;
+ next_buf_len = left_bytes;
+ }
+ }
+
+ /* extend buffer to fit if needed */
+ GeglRectangle *extent = g_boxed_copy(GEGL_TYPE_RECTANGLE, gegl_buffer_get_extent(self->priv->buffer));
+ extent->height = MAX(extent->height, self->priv->rect.y+self->priv->rect.height);
+ gegl_buffer_set_extent(self->priv->buffer, extent);
+ g_boxed_free(GEGL_TYPE_RECTANGLE, extent);
+
+ /* process */
+ g_debug("Processing %dx%d+%d+%d", self->priv->rect.width, self->priv->rect.height, self->priv->rect.x, self->priv->rect.y);
+ self->priv->process(self, buf, buf_len);
+ self->priv->bytes_read+= buf_len;
+
+ /* recurse for next roi */
+ if (next_buf && next_buf_len)
+ gsane_processor_process(self, next_buf, buf_len);
+}
+
+void
+gsane_processor_destroy(GSaneProcessor *self)
+{
+ g_free(self->priv);
+ return;
+}
Added: trunk/modules/gsane/gsane-processor.h
==============================================================================
--- (empty file)
+++ trunk/modules/gsane/gsane-processor.h Sat Dec 20 15:39:14 2008
@@ -0,0 +1,45 @@
+/* GSane - SANE GNOME Scan backend
+ * Copyright  2007-2008 Ãtienne Bersac <bersace gnome org>
+ *
+ * GSane is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GSane 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 GSane. If not, write to:
+ *
+ * the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA
+ */
+
+#ifndef _GSANE_PROCESSOR_H_
+#define _GSANE_PROCESSOR_H_
+
+#include <gegl.h>
+#include <sane/sane.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GSaneProcessor GSaneProcessor;
+typedef struct _GSaneProcessorPrivate GSaneProcessorPrivate;
+
+struct _GSaneProcessor {
+ GSaneProcessorPrivate *priv;
+};
+
+void gsane_processor_init(GSaneProcessor *self);
+GeglBuffer* gsane_processor_prepare(GSaneProcessor *self, SANE_Parameters *params);
+void gsane_processor_process(GSaneProcessor *self, guchar* buf, guint buf_len);
+void gsane_processor_destroy(GSaneProcessor*self);
+
+
+G_END_DECLS
+
+#endif
Modified: trunk/modules/gsane/gsane-scanner.c
==============================================================================
--- trunk/modules/gsane/gsane-scanner.c (original)
+++ trunk/modules/gsane/gsane-scanner.c Sat Dec 20 15:39:14 2008
@@ -20,13 +20,13 @@
*/
#include <config.h>
-#include <gegl.h>
#include <string.h>
#include <sane/sane.h>
#include "gsane-common.h"
#include "gsane-option-manager.h"
#include "gsane-option-handler.h"
#include "gsane-scanner.h"
+#include "gsane-processor.h"
#define GSANE_SCANNER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSANE_TYPE_SCANNER, GSaneScannerPrivate))
@@ -39,12 +39,31 @@
/* fields */
GHashTable* option_handlers;
- /* SANE */
- SANE_Handle handle;
+ GSaneProcessor processor;
+ /* SANE handle */
+ SANE_Handle handle;
+ /* whether the adf is selected and thus we should scan until "NO_DOCS" */
+ gboolean is_adf_selected;
+ /* the number of pages scanned from the source (should not be > 1 for non ADF source) */
+ guint page_count;
+ /* SANE frame paramters */
+ SANE_Parameters params;
+ /* total number of frame to acquire to get the entire image */
+ guint frame_count;
+ /* number of frame acquired */
+ guint frame_acquired;
+ guint bytes_per_pixel;
+ /* total bytes read*/
+ volatile guint bytes_read;
+ /* total bytes to acquire to get the entire image (all frames) */
+ guint total_bytes_count;
/* status */
- gboolean probe_done;
gboolean guessing_parameters;
+
+ /* Threads */
+ GThread* opt_thread;
+ GThread* params_thread;
};
enum {
@@ -75,7 +94,6 @@
static gboolean
gsane_scanner_check_sane_status(GSaneScanner *self, const gchar* operation, SANE_Status status)
{
- /* TODO: update node status, handle error/warning */
if (status != SANE_STATUS_GOOD) {
const SANE_String_Const message = sane_strstatus(status);
g_warning("SANE operation : %s failed with %s", operation, message);
@@ -91,28 +109,24 @@
SANE_Parameters params;
g_debug("%s: reload params", self->priv->sane_id);
- status = sane_start(self->priv->handle);
- if (!gsane_scanner_check_sane_status(self, "sane_start", status))
- goto end;
-
status = sane_get_parameters(self->priv->handle, ¶ms);
if (gsane_scanner_check_sane_status(self, "sane_get_parameters", status))
gnome_scan_node_update_status(GNOME_SCAN_NODE(self), GNOME_SCAN_STATUS_READY, NULL);
g_debug("%s parameters reloaded", self->priv->sane_id);
- end:
+
sane_cancel(self->priv->handle);
+ self->priv->params_thread = NULL;
self->priv->guessing_parameters = FALSE;
}
void
gsane_scanner_reload_parameters(GSaneScanner* self)
{
- if (self->priv->guessing_parameters)
+ if (self->priv->params_thread)
return;
- self->priv->guessing_parameters = TRUE;
- g_thread_create((GThreadFunc)gsane_scanner_reload_parameters_thread, self, FALSE, NULL);
+ self->priv->params_thread = g_thread_create((GThreadFunc)gsane_scanner_reload_parameters_thread, self, FALSE, NULL);
}
static void
@@ -168,9 +182,10 @@
goto end;
}
- sane_control_option(self->priv->handle, 0, SANE_ACTION_GET_VALUE, &count,NULL);
- g_debug("Device %s : %i options", self->priv->sane_id, count);
+ sane_control_option(self->priv->handle, 0, SANE_ACTION_GET_VALUE, &count, NULL);
+ g_debug("Device %s : %d options", self->priv->sane_id, count);
+ /* loop all SANE options */
for (n = 1; n < count; n++) {
desc = sane_get_option_descriptor(self->priv->handle, n);
switch(desc->type) {
@@ -182,37 +197,113 @@
g_debug("Ignoring button %s.", desc->name);
break;
default:
- if (SANE_OPTION_IS_SETTABLE(desc->cap)) {
+ if (SANE_OPTION_IS_SETTABLE(desc->cap))
gsane_scanner_handle_sane_option(self, n, desc, group);
- }
- else {
+ else
g_debug("Ignoring sensor : %s", desc->name);
- }
break;
}
}
gnome_scan_node_update_status(GNOME_SCAN_NODE(self), GNOME_SCAN_STATUS_UNCONFIGURED, NULL);
gsane_scanner_reload_parameters(self);
+
end:
- self->priv->probe_done = TRUE;
+ self->priv->opt_thread = NULL;
return NULL;
}
+static guint
+gsane_scanner_get_frame_count(GSaneScanner *self)
+{
+ switch(self->priv->params.format) {
+ case SANE_FRAME_RGB:
+ case SANE_FRAME_GRAY:
+ return 1;
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static void
+gsane_scanner_start_scan(GnomeScanNode *node)
+{
+ GSaneScanner *self = GSANE_SCANNER(node);
+ self->priv->page_count = 0;
+}
+
+static gboolean
+gsane_scanner_start_frame(GnomeScanNode *node)
+{
+ GSaneScanner *self = GSANE_SCANNER(node);
+ GeglBuffer *buffer;
+
+ if (self->priv->page_count > 0 && !self->priv->is_adf_selected)
+ return FALSE;
+
+ SANE_Status status = sane_start(self->priv->handle);
+ if (!gsane_scanner_check_sane_status(self, "sane_start", status))
+ return FALSE;
+
+ status = sane_get_parameters(self->priv->handle, &self->priv->params);
+ if (!gsane_scanner_check_sane_status(self, "sane_get_parameters", status))
+ return FALSE;
+
+ self->priv->frame_count = gsane_scanner_get_frame_count(self);
+ self->priv->frame_acquired = 0;
+ self->priv->bytes_read = 0;
+ self->priv->bytes_per_pixel = self->priv->params.bytes_per_line/self->priv->params.pixels_per_line;
+ self->priv->total_bytes_count = self->priv->params.bytes_per_line * self->priv->params.lines * self->priv->frame_count;
+
+ buffer = gsane_processor_prepare(&self->priv->processor, &self->priv->params);
+ gnome_scan_scanner_set_buffer((GnomeScanScanner*)self, buffer);
+ return TRUE;
+}
+
+static gboolean
+gsane_scanner_work(GnomeScanScanner*scanner, gdouble *progress)
+{
+ GSaneScanner *self = GSANE_SCANNER(scanner);
+ SANE_Status status;
+ SANE_Byte *buf;
+ SANE_Int read_len;
+ guint chunk_len = self->priv->params.bytes_per_line;
+
+ buf = g_new0(SANE_Byte, chunk_len);
+ status = sane_read (self->priv->handle, buf, chunk_len, &read_len);
+ if (status != SANE_STATUS_GOOD) {
+ g_debug("%s", sane_strstatus(status));
+ return FALSE;
+ }
+
+ self->priv->bytes_read += read_len;
+ g_debug("Asked %d bytes, got %d", chunk_len, read_len);
+ gsane_processor_process(&self->priv->processor, buf, read_len);
+ g_free(buf);
+
+ *progress = (gdouble)self->priv->bytes_read/(gdouble)self->priv->total_bytes_count;
+ return TRUE;
+}
+
+static void
+gsane_scanner_end_frame(GnomeScanNode*scanner)
+{
+ GSaneScanner *self = GSANE_SCANNER(scanner);
+ sane_cancel(self->priv->handle);
+ self->priv->page_count++;
+}
+
static GObject*
gsane_scanner_constructor(GType type, guint n_construct_properties, GObjectConstructParam* construct_params)
{
GObject* object = G_OBJECT_CLASS(gsane_scanner_parent_class)->constructor(type, n_construct_properties, construct_params);
GSaneScanner *self = GSANE_SCANNER(object);
- GError *error = NULL;
-
- g_thread_create((GThreadFunc) gsane_scanner_probe_options, self, FALSE, &error);
- if (error) {
- g_warning("Unable to init thread : %s",
- dgettext(g_quark_to_string(error->domain), error->message));
- return NULL;
- }
+ self->priv->opt_thread = g_thread_create((GThreadFunc) gsane_scanner_probe_options, self, FALSE, NULL);
return object;
}
@@ -223,17 +314,29 @@
self->priv = GSANE_SCANNER_GET_PRIVATE(self);
self->priv->option_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gsane_option_handler_destroy);
self->priv->handle = NULL;
- self->priv->probe_done = FALSE;
- self->priv->guessing_parameters = FALSE;
+ self->priv->opt_thread = NULL;
+ self->priv->params_thread = NULL;
+ self->priv->is_adf_selected = FALSE;
+ gsane_processor_init(&self->priv->processor);
}
static void
gsane_scanner_finalize(GObject *object)
{
GSaneScanner *self = GSANE_SCANNER(object);
+ g_debug("%s(%s)", __FUNCTION__, self->priv->sane_id);
+
+ gsane_processor_destroy(&self->priv->processor);
+
self->priv->sane_id = (self->priv->sane_id == NULL ? NULL : g_free(self->priv->sane_id), NULL);
self->priv->sane_type = (self->priv->sane_type == NULL ? NULL : g_free(self->priv->sane_type), NULL);
g_hash_table_destroy(self->priv->option_handlers);
+
+ if (self->priv->opt_thread)
+ g_thread_join(self->priv->opt_thread);
+
+ if (self->priv->params_thread)
+ g_thread_join(self->priv->params_thread);
}
static void
@@ -264,7 +367,6 @@
gsane_scanner_class_init(GSaneScannerClass *klass)
{
GObjectClass *o_class = G_OBJECT_CLASS(klass);
- /* GnomeScanNodeClass *n_class = GNOME_SCAN_NODE_CLASS(klass); */
g_type_class_add_private(o_class, sizeof(GSaneScannerPrivate));
@@ -275,6 +377,14 @@
g_object_class_install_property(o_class, GSANE_SCANNER_SANE_ID, g_param_spec_string("sane-id", "SANE ID", "SANE device id", NULL,G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(o_class, GSANE_SCANNER_SANE_TYPE, g_param_spec_string("sane-type", "SANE Type", "SANE device type", NULL, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
g_signal_new("reload-options", GSANE_TYPE_SCANNER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+ GnomeScanNodeClass *n_class = GNOME_SCAN_NODE_CLASS(klass);
+ n_class->start_scan = gsane_scanner_start_scan;
+ n_class->start_frame = gsane_scanner_start_frame;
+ n_class->end_frame = gsane_scanner_end_frame;
+
+ GnomeScanScannerClass *s_class = GNOME_SCAN_SCANNER_CLASS(klass);
+ s_class->work = gsane_scanner_work;
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]