[gimp/bug-601821] Bug 601821 - Retrieve multiple images from TWAIN data source



commit 7266c173c8be94c3cc716f1b9dbe3c8bee540a67
Author: Jens M. Plonka <jens plonka gmx de>
Date:   Tue Jun 14 21:06:52 2016 +0200

    Bug 601821 - Retrieve multiple images from TWAIN data source
    
    (primarily multiple pages from a scanner document feeder)

 plug-ins/twain/tw_func.c  |  217 ++++++++++++++++++++++-----------------------
 plug-ins/twain/tw_func.h  |   19 ++--
 plug-ins/twain/tw_local.h |    9 +--
 plug-ins/twain/twain.c    |   81 ++++++++---------
 4 files changed, 158 insertions(+), 168 deletions(-)
---
diff --git a/plug-ins/twain/tw_func.c b/plug-ins/twain/tw_func.c
index f677f40..b31c3cd 100644
--- a/plug-ins/twain/tw_func.c
+++ b/plug-ins/twain/tw_func.c
@@ -142,13 +142,20 @@ twainError (int errorCode)
 char *
 currentTwainError (pTW_SESSION twSession)
 {
-  TW_STATUS twStatus;
+  pTW_STATUS twStatus;
+  char *     error;
+
+  twStatus = g_new (TW_STATUS, 1);
 
   /* Get the current status code from the DSM */
   twSession->twRC = DSM_GET_STATUS(twSession, twStatus);
 
   /* Return the mapped error code */
-  return twainError (twStatus.ConditionCode);
+  error = twainError (twStatus->ConditionCode);
+
+  g_free (twStatus);
+
+  return error;
 }
 
 /*
@@ -187,6 +194,8 @@ getImage (pTW_SESSION twSession)
     return FALSE;
   }
 
+  set_ds_capabilities (twSession);
+
   requestImageAcquire (twSession, TRUE);
 
   return TRUE;
@@ -342,10 +351,26 @@ openDS (pTW_SESSION twSession)
 }
 
 /*
- * setBufferedXfer
+ * set_ds_capabilities
+ *
+ * Sets the required capabilities of the data source.
  */
-static int
-setBufferedXfer (pTW_SESSION twSession)
+void
+set_ds_capabilities (pTW_SESSION twSession)
+{
+  if (!DS_IS_OPEN(twSession))
+  {
+    log_message ("Open data source before setting capabilities!\n", currentTwainError(twSession));
+  }
+  else
+  {
+    set_ds_capability (twSession, ICAP_XFERMECH, TWTY_UINT16, TWSX_MEMORY);
+    set_ds_capability (twSession, CAP_XFERCOUNT, TWTY_INT16, TWBP_AUTO);
+  }
+}
+
+void
+set_ds_capability (pTW_SESSION twSession, TW_UINT16 cap, TW_UINT16 type, TW_UINT32 value)
 {
   TW_CAPABILITY bufXfer;
   pTW_ONEVALUE pvalOneValue;
@@ -353,25 +378,25 @@ setBufferedXfer (pTW_SESSION twSession)
   /* Make sure the data source is open first */
   if (DS_IS_CLOSED(twSession))
   {
-    return FALSE;
+    return;
   }
 
   /* Create the capability information */
-  bufXfer.Cap = ICAP_XFERMECH;
+  bufXfer.Cap = cap;
   bufXfer.ConType = TWON_ONEVALUE;
   bufXfer.hContainer = twainAllocHandle (sizeof(TW_ONEVALUE));
   if (bufXfer.hContainer == NULL)
   {
     g_message ("Error allocating memory for XFer mechanism.\n");
-    return FALSE;
+    return;
   }
   pvalOneValue = (pTW_ONEVALUE) twainLockHandle (bufXfer.hContainer);
-  pvalOneValue->ItemType = TWTY_UINT16;
-  pvalOneValue->Item = TWSX_MEMORY;
+  pvalOneValue->ItemType = type;
+  pvalOneValue->Item = value;
   twainUnlockHandle (bufXfer.hContainer);
 
   /* Make the call to the source manager */
-  twSession->twRC = DSM_XFER_SET(twSession, bufXfer);
+  twSession->twRC = DSM_CAPABILITY_SET(twSession, bufXfer);
   if (twSession->twRC == TWRC_FAILURE)
   {
     g_message ("Could not set capability: %s\n", currentTwainError(twSession));
@@ -380,9 +405,6 @@ setBufferedXfer (pTW_SESSION twSession)
   /* Free the container */
   twainUnlockHandle (bufXfer.hContainer);
   twainFreeHandle (bufXfer.hContainer);
-
-  /* Let the caller know what happened */
-  return (twSession->twRC==TWRC_SUCCESS);
 }
 
 /*
@@ -402,29 +424,25 @@ requestImageAcquire (pTW_SESSION twSession, gboolean showUI)
     return FALSE;
   }
 
-  /* Set the transfer mode */
-  if (setBufferedXfer (twSession))
-  {
-    /* Set the UI information */
-    ui.ShowUI = TRUE;
-    ui.ModalUI = TRUE;
-    /* In Windows, the callbacks are sent to the window message handler */
-    ui.hParent = twSession->hwnd;
+  /* Set the UI information */
+  ui.ShowUI = TRUE;
+  ui.ModalUI = TRUE;
+  /* In Windows, the callbacks are sent to the window message handler */
+  ui.hParent = twSession->hwnd;
 
-    /* Make the call to the source manager */
-    twSession->twRC = DSM_ENABLE_DS(twSession, ui);
+  /* Make the call to the source manager */
+  twSession->twRC = DSM_ENABLE_DS(twSession, ui);
 
-    switch (twSession->twRC)
-    {
-      case TWRC_SUCCESS:
-        /* We are now at a new twain state */
-        twSession->twainState = 5;
-        return TRUE;
+  switch (twSession->twRC)
+  {
+    case TWRC_SUCCESS:
+      /* We are now at a new twain state */
+      twSession->twainState = 5;
+      return TRUE;
 
-         case TWRC_FAILURE:
-       g_message ("Error enabeling data source: %s\n", currentTwainError(twSession));
-          break;
-    }
+    case TWRC_FAILURE:
+      g_message ("Error enabeling data source: %s\n", currentTwainError(twSession));
+      break;
   }
   return FALSE;
 }
@@ -531,7 +549,7 @@ closeDSM (pTW_SESSION twSession)
  * Begin an image transfer.
  */
 static int
-beginImageTransfer (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
+beginImageTransfer (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo, gboolean first)
 {
   /* Clear our structures */
   memset (imageInfo, 0, sizeof (TW_IMAGEINFO));
@@ -569,23 +587,16 @@ beginImageTransfer (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
  * from State 6 to 7.  Return the reason for exiting the transfer.
  */
 static void
-transferImage (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
+transferImage (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo, TW_UINT32 bufferSize)
 {
-  TW_SETUPMEMXFER setupMemXfer;
   TW_IMAGEMEMXFER imageMemXfer;
   char *buffer;
 
-  /* Clear our structures */
-  memset (&setupMemXfer, 0, sizeof (TW_SETUPMEMXFER));
-  memset (&imageMemXfer, 0, sizeof (TW_IMAGEMEMXFER));
-
-  /* Find out how the source would like to transfer... */
-  twSession->twRC = DSM_XFER_START(twSession, setupMemXfer);
-
   /* Allocate the buffer for the transfer */
-  buffer = g_new (char, setupMemXfer.Preferred);
+  memset (&imageMemXfer, 0, sizeof (TW_IMAGEMEMXFER));
+  buffer = g_new (char, bufferSize);
   imageMemXfer.Memory.Flags = TWMF_APPOWNS | TWMF_POINTER;
-  imageMemXfer.Memory.Length = setupMemXfer.Preferred;
+  imageMemXfer.Memory.Length = bufferSize;
   imageMemXfer.Memory.TheMem = (TW_MEMREF) buffer;
 
   /* Get the data */
@@ -621,13 +632,13 @@ transferImage (pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
 }
 
 /*
- * endPendingTransfer
+ * endImageTransfer
  *
- * Cancel the currently pending transfer.
- * Return the count of pending transfers.
+ * Finish transferring an image.  Return the count
+ * of pending images.
  */
-static int
-endPendingTransfer (pTW_SESSION twSession)
+static void
+endImageTransfer (pTW_SESSION twSession)
 {
   TW_PENDINGXFERS pendingXfers;
 
@@ -638,54 +649,8 @@ endPendingTransfer (pTW_SESSION twSession)
     twSession->twainState = 5;
   }
 
-  return pendingXfers.Count;
-}
-
-/*
- * cancelPendingTransfers
- *
- * Cancel all pending image transfers.
- */
-void
-cancelPendingTransfers (pTW_SESSION twSession)
-{
-  TW_PENDINGXFERS pendingXfers;
-
-  twSession->twRC = DSM_XFER_RESET(twSession, pendingXfers);
-}
-
-/*
- * endImageTransfer
- *
- * Finish transferring an image.  Return the count
- * of pending images.
- */
-static int
-endImageTransfer (pTW_SESSION twSession, int *pendingCount)
-{
-  gboolean continueTransfers;
-  int exitCode = twSession->twRC;
-
-  /* Have now exited the transfer for some reason... Figure out
-   * why and what to do about it
-   */
-  switch (twSession->twRC)
-  {
-    case TWRC_XFERDONE:
-    case TWRC_CANCEL:
-      *pendingCount = endPendingTransfer (twSession);
-      break;
-
-    case TWRC_FAILURE:
-      g_message ("Failure received: %s\n", currentTwainError(twSession));
-      *pendingCount = endPendingTransfer (twSession);
-      break;
-  }
-
   /* Call the end transfer callback */
-  CB_XFER_END(twSession, continueTransfers, exitCode, pendingCount);
-
-  return (*pendingCount && continueTransfers);
+  CB_XFER_END(twSession);
 }
 
 /*
@@ -697,8 +662,11 @@ endImageTransfer (pTW_SESSION twSession, int *pendingCount)
 static void
 transferImages (pTW_SESSION twSession)
 {
-  TW_IMAGEINFO imageInfo;
-  int pendingCount;
+  TW_IMAGEINFO    imageInfo;
+  TW_PENDINGXFERS pendingXfers;
+  gboolean        first;
+  TW_UINT32       bufferSize;
+  TW_SETUPMEMXFER setupMemXfer;
 
   /* Check the image transfer callback function
    * before even attempting to do the transfer
@@ -718,28 +686,57 @@ transferImages (pTW_SESSION twSession)
     CB_XFER_PRE(twSession);
   }
 
+  first = TRUE;
+
+  /* Clear our structures */
+  memset (&setupMemXfer, 0, sizeof (TW_SETUPMEMXFER));
+
+  /* Find out how the source would like to transfer... */
+  twSession->twRC = DSM_XFER_START(twSession, setupMemXfer);
+  switch (twSession->twRC)
+  {
+    case TWRC_SUCCESS:
+      bufferSize = setupMemXfer.Preferred;
+      break;
+
+    case TWRC_FAILURE:
+      log_message ("Failure setting transfer buffer: %s\n", currentTwainError(twSession));
+      break;
+
+    default:
+      break;
+  }
+
   /* Loop through the available images */
-  do
+  while (beginImageTransfer (twSession, &imageInfo, first))
   {
+    first = FALSE;
     /* Move to the new state */
     twSession->twainState = 6;
 
-    /* Begin the image transfer */
-    if (!beginImageTransfer (twSession, &imageInfo))
-    {
-      continue;
-    }
-
     /* Call the image transfer function */
-    transferImage (twSession, &imageInfo);
+    transferImage (twSession, &imageInfo, bufferSize);
+
+    endImageTransfer (twSession);
+  }
+
+  /* Reset any open transfers */
+  DSM_XFER_RESET(twSession, pendingXfers);
 
-  } while (endImageTransfer (twSession, &pendingCount));
+  /* This will close the datasource and datasource
+   * manager.  Then the message queue will be shut
+   * down and the run() procedure will finally be
+   * able to finish.
+   */
+  disableDS (twSession);
+  closeDS (twSession);
+  closeDSM (twSession);
 
   /*
    * Inform our application that we are done
    * transferring images.
    */
-  CB_XFER_POST(twSession, pendingCount);
+  CB_XFER_POST(twSession);
 }
 
 void
diff --git a/plug-ins/twain/tw_func.h b/plug-ins/twain/tw_func.h
index 54ad04f..091b58a 100644
--- a/plug-ins/twain/tw_func.h
+++ b/plug-ins/twain/tw_func.h
@@ -110,7 +110,7 @@ typedef int (* TW_TXFR_DATA_CB)(pTW_IMAGEINFO, pTW_IMAGEMEMXFER, void *);
  * TWRC_FAILURE
  *  The transfer failed.
  */
-typedef int (* TW_TXFR_END_CB)(int, int, void *);
+typedef void (* TW_TXFR_END_CB)(int, void *);
 
 /*
  * Post-image transfer callback
@@ -119,7 +119,7 @@ typedef int (* TW_TXFR_END_CB)(int, int, void *);
  * of the possible images have been transferred
  * from the datasource.
  */
-typedef void (* TW_POST_TXFR_CB)(int, void *);
+typedef void (* TW_POST_TXFR_CB)(void *);
 
 /*
  * The following structure defines the
@@ -211,11 +211,11 @@ typedef struct _TWAIN_SESSION {
 #define CB_XFER_DATA(s, i, m) (*s->transferFunctions->txfrDataCb) \
     (i, &m, s->clientData)
 
-#define CB_XFER_END(s, c, e, p)   if (s->transferFunctions->txfrEndCb) \
-    c = (*s->transferFunctions->txfrEndCb) (e, *p, s->clientData)
+#define CB_XFER_END(s)   if (s->transferFunctions->txfrEndCb) \
+    (*s->transferFunctions->txfrEndCb) (s->twRC, s->clientData)
 
-#define CB_XFER_POST(s, p) if (s->transferFunctions->postTxfrCb) \
-    (*s->transferFunctions->postTxfrCb) (p, s->clientData)
+#define CB_XFER_POST(s) if (s->transferFunctions->postTxfrCb) \
+    (*s->transferFunctions->postTxfrCb) (s->clientData)
 
 /* Session structure access
  * macros
@@ -236,7 +236,7 @@ typedef struct _TWAIN_SESSION {
 #define DS_IS_DISABLED(s) ((s == NULL) ? TRUE  : s->twainState <  5)
 
 #define DSM_GET_STATUS(ses, sta)  callDSM(ses->appIdentity, ses->dsIdentity, \
-          DG_CONTROL, DAT_STATUS, MSG_GET, (TW_MEMREF) &sta)
+          DG_CONTROL, DAT_STATUS, MSG_GET, (TW_MEMREF) sta)
 
 #define DSM_OPEN(ses) callDSM(ses->appIdentity, NULL,\
           DG_CONTROL, DAT_PARENT, MSG_OPENDSM, (TW_MEMREF) &(ses->hwnd))
@@ -274,7 +274,7 @@ typedef struct _TWAIN_SESSION {
 #define DSM_GET_IMAGE(ses, i) callDSM(ses->appIdentity, ses->dsIdentity,\
           DG_IMAGE, DAT_IMAGEINFO, MSG_GET, (TW_MEMREF) i)
 
-#define DSM_XFER_SET(ses, x) callDSM(ses->appIdentity, ses->dsIdentity,\
+#define DSM_CAPABILITY_SET(ses, x) callDSM(ses->appIdentity, ses->dsIdentity,\
           DG_CONTROL, DAT_CAPABILITY, MSG_SET, (TW_MEMREF) &x)
 
 #define DSM_XFER_START(ses, x) callDSM(ses->appIdentity, ses->dsIdentity,\
@@ -313,5 +313,6 @@ void            processTwainMessage (TW_UINT16 message, pTW_SESSION twSession);
 pTW_SESSION     newSession (pTW_IDENTITY twSession);
 pTW_DATA_SOURCE get_available_ds (pTW_SESSION twSession);
 int             adjust_selected_data_source (pTW_SESSION twSession);
-
+void            set_ds_capabilities (pTW_SESSION twSession);
+void            set_ds_capability (pTW_SESSION twSession, TW_UINT16 cap, TW_UINT16 type, TW_UINT32 value);
 #endif /* _TW_FUNC_H */
diff --git a/plug-ins/twain/tw_local.h b/plug-ins/twain/tw_local.h
index 3289a9e..0815a90 100644
--- a/plug-ins/twain/tw_local.h
+++ b/plug-ins/twain/tw_local.h
@@ -51,11 +51,6 @@
     { GIMP_PDB_INT32, "image-count", "Number of acquired images" }, \
     { GIMP_PDB_INT32ARRAY, "image-ids", "Array of acquired image identifiers" }
 
-/*
- * Application definitions
- */
-#define MAX_IMAGES 1
-
 /* Functions which the platform-independent code will call */
 TW_UINT16 callDSM (
   pTW_IDENTITY pOrigin,
@@ -81,8 +76,8 @@ pTW_SESSION initializeTwain (void);
 void      preTransferCallback (void *);
 int       beginTransferCallback (pTW_IMAGEINFO, void *);
 int       dataTransferCallback (pTW_IMAGEINFO, pTW_IMAGEMEMXFER, void *);
-int       endTransferCallback (int, int, void *);
-void      postTransferCallback (int, void *);
+void      endTransferCallback (int, void *);
+void      postTransferCallback (void *);
 void      register_menu (pTW_IDENTITY dsIdentity);
 void      register_scanner_menus (void);
 
diff --git a/plug-ins/twain/twain.c b/plug-ins/twain/twain.c
index bd98c4c..3c24e70 100644
--- a/plug-ins/twain/twain.c
+++ b/plug-ins/twain/twain.c
@@ -8,7 +8,8 @@
  * Brion Vibber <brion pobox com>
  * 07/22/2004
  *
- * Added for Win x64 support, changed data source selection.
+ * Added for Win x64 support, changed data source selection, fixed
+ * scanning more than one image.
  * Jens M. Plonka <jens plonka gmx de>
  * 11/25/2011
  *
@@ -61,7 +62,8 @@
  *  (03/31/99)  v0.5   Added support for multi-byte samples and paletted
  *                     images.
  *  (07/23/04)  v0.6   Added Mac OS X support.
- *  (11/25/11)  v0.7   Added Win x64 support, changed data source selection.
+ *  (11/25/11)  v0.7   Added Win x64 support, changed data source selection,
+ *                     fixed scanning more than one image.
  */
 #include "config.h"
 
@@ -94,6 +96,10 @@ static const GimpParamDef args[] = { IN_ARGS };
 static const GimpParamDef return_vals[] = { OUT_ARGS };
 
 static char  *destBuf = NULL;
+
+/* The list of images to be transferred */
+GList        *images = NULL;
+
 static char bitMasks[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
 
 /* Return values storage */
@@ -309,14 +315,17 @@ run (const gchar      *name,
      gint             *nreturn_vals,
      GimpParam       **return_vals)
 {
-  GimpRunMode run_mode = param[0].data.d_int32;
+  GList       *list;
+  gint32       count = 0;
+  gint32       i;
+  GimpRunMode  run_mode = param[0].data.d_int32;
 
   /* Initialize the return values
    * Always return at least the status to the caller.
    */
   values[0].type = GIMP_PDB_STATUS;
   values[0].data.d_status = GIMP_PDB_SUCCESS;
-  *nreturn_vals = 1;
+  *nreturn_vals = 3;
   *return_vals = values;
 
   INIT_I18N ();
@@ -335,7 +344,7 @@ run (const gchar      *name,
   values[1].type = GIMP_PDB_INT32;
   values[1].data.d_int32 = 0;
   values[2].type = GIMP_PDB_INT32ARRAY;
-  values[2].data.d_int32array = g_new (gint32, MAX_IMAGES);
+  values[2].data.d_int32array = NULL; /* Will be assigned after acquiring images */
 
   /* How are we running today? */
   switch (run_mode)
@@ -353,15 +362,31 @@ run (const gchar      *name,
   /* Have we succeeded so far? */
   if (values[0].data.d_status == GIMP_PDB_SUCCESS)
   {
-    twainMain (name);
+    if (twainMain (name))
+    {
+      count = g_list_length (images);
+    }
   }
 
+  /* Retrun the number of images */
+  values[1].data.d_int32      = count;
+
   /* Check to make sure we got at least one valid
    * image.
    */
-  if (values[1].data.d_int32 > 0) {
-    /* Set return values */
-    *nreturn_vals = 3;
+  if (count == 0)
+  {
+    values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+  }
+  else
+  {
+    /* Return the list of image IDs */
+    values[2].data.d_int32array = g_new (gint32, count);
+    for (list = images, i = 0; list; list = g_list_next (list), i++)
+    {
+      values[2].data.d_int32array[i] = GPOINTER_TO_INT (list->data);
+    }
+    g_list_free (images);
   }
 }
 
@@ -478,12 +503,6 @@ beginTransferCallback (pTW_IMAGEINFO imageInfo, void *clientData)
   gimp_pixel_rgn_init (&(theClientData->pixel_rgn), theClientData->drawable,
       0, 0, width, length, TRUE, FALSE);
 
-  /* Store our client data for the data transfer callbacks */
-  if (clientData)
-  {
-    g_free (clientData);
-  }
-
   twSession->clientData = (void *) theClientData;
 
   /* Make sure to return TRUE to continue the image
@@ -826,8 +845,8 @@ dataTransferCallback (
  * TWRC_FAILURE
  *  The transfer failed.
  */
-int
-endTransferCallback (int completionState, int pendingCount, void *clientData)
+void
+endTransferCallback (int twRC, void *clientData)
 {
   pClientDataStruct theClientData = (pClientDataStruct) clientData;
 
@@ -841,13 +860,9 @@ endTransferCallback (int completionState, int pendingCount, void *clientData)
   gimp_drawable_detach (theClientData->drawable);
 
   /* Make sure to check our return code */
-  if (completionState == TWRC_XFERDONE)
+  if ((twRC == TWRC_XFERDONE) || (twRC == TWRC_SUCCESS))
   {
-    /* We have a completed image transfer */
-    values[2].type = GIMP_PDB_INT32ARRAY;
-    values[2].data.d_int32array[values[1].data.d_int32++] =
-      theClientData->image_id;
-
+    images = g_list_prepend (images, GINT_TO_POINTER (theClientData->image_id));
     /* Display the image */
     gimp_display_new (theClientData->image_id);
   }
@@ -856,9 +871,6 @@ endTransferCallback (int completionState, int pendingCount, void *clientData)
     /* The transfer did not complete successfully */
     gimp_image_delete (theClientData->image_id);
   }
-
-  /* Shut down if we have received all of the possible images */
-  return (values[1].data.d_int32 < MAX_IMAGES);
 }
 
 /*
@@ -869,23 +881,8 @@ endTransferCallback (int completionState, int pendingCount, void *clientData)
  * transferred.
  */
 void
-postTransferCallback (int pendingCount, void *clientData)
+postTransferCallback (void *clientData)
 {
-  /* Shut things down. */
-  if (pendingCount != 0)
-  {
-    cancelPendingTransfers(twSession);
-  }
-
-  /* This will close the datasource and datasource
-   * manager.  Then the message queue will be shut
-   * down and the run() procedure will finally be
-   * able to finish.
-   */
-  disableDS (twSession);
-  closeDS (twSession);
-  closeDSM (twSession);
-
   /* Post a message to close up the application */
   twainQuitApplication ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]