gnome-scan r716 - in trunk: . modules/gsane



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, &params);
 	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]