diff options
Diffstat (limited to 'plug-ins/twain/twain.c')
-rw-r--r-- | plug-ins/twain/twain.c | 962 |
1 files changed, 962 insertions, 0 deletions
diff --git a/plug-ins/twain/twain.c b/plug-ins/twain/twain.c new file mode 100644 index 0000000..dafb215 --- /dev/null +++ b/plug-ins/twain/twain.c @@ -0,0 +1,962 @@ +/* + * TWAIN Plug-in + * Copyright (C) 1999 Craig Setera + * Craig Setera <setera@home.com> + * 03/31/1999 + * + * Updated for Mac OS X support + * Brion Vibber <brion@pobox.com> + * 07/22/2004 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * + * Based on (at least) the following plug-ins: + * Screenshot + * GIF + * Randomize + * + * Any suggestions, bug-reports or patches are welcome. + * + * This plug-in interfaces to the TWAIN support library in order + * to capture images from TWAIN devices directly into GIMP images. + * The plug-in is capable of acquiring the following type of + * images: + * - B/W (1 bit images translated to grayscale B/W) + * - Grayscale up to 16 bits per pixel + * - RGB up to 16 bits per sample (24, 30, 36, etc.) + * - Paletted images (both Gray and RGB) + * + * Prerequisites: + * Should compile and run on both Win32 and Mac OS X 10.3 (possibly + * also on 10.2). + * + * Known problems: + * - Multiple image transfers will hang the plug-in. The current + * configuration compiles with a maximum of single image transfers. + * - On Mac OS X, canceling doesn't always close things out fully. + * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning. + */ + +/* + * Revision history + * (02/07/99) v0.1 First working version (internal) + * (02/09/99) v0.2 First release to anyone other than myself + * (02/15/99) v0.3 Added image dump and read support for debugging + * (03/31/99) v0.5 Added support for multi-byte samples and paletted + * images. + * (07/23/04) v0.6 Added Mac OS X support. + */ +#include "config.h" + +#include <glib.h> /* Needed when compiling with gcc */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tw_platform.h" +#include "tw_local.h" + +#include "libgimp/gimp.h" +#include "libgimp/stdplugins-intl.h" + +#include "tw_func.h" +#include "tw_util.h" + +#ifdef _DEBUG +#include "tw_dump.h" +#endif /* _DEBUG */ + +/* + * Plug-in Definitions + */ +#define PLUG_IN_NAME "twain-acquire" +#define PLUG_IN_DESCRIPTION N_("Capture an image from a TWAIN datasource") +#define PLUG_IN_HELP "This plug-in will capture an image from a TWAIN datasource" +#define PLUG_IN_AUTHOR "Craig Setera (setera@home.com)" +#define PLUG_IN_COPYRIGHT "Craig Setera" +#define PLUG_IN_VERSION "v0.6 (07/22/2004)" + +#ifdef _DEBUG +#define PLUG_IN_D_NAME "twain-acquire-dump" +#define PLUG_IN_R_NAME "twain-acquire-read" +#endif /* _DEBUG */ + +/* + * Application definitions + */ +#define MAX_IMAGES 1 + +/* + * Definition of the run states + */ +#define RUN_STANDARD 0 +#define RUN_DUMP 1 +#define RUN_READDUMP 2 + +/* Global variables */ +pTW_SESSION twSession = NULL; + +#ifdef _DEBUG +static int twain_run_mode = RUN_STANDARD; +#endif + +/* Forward declarations */ +void preTransferCallback (void *clientData); +int beginTransferCallback (pTW_IMAGEINFO imageInfo, + void *clientData); +int dataTransferCallback (pTW_IMAGEINFO imageInfo, + pTW_IMAGEMEMXFER imageMemXfer, + void *clientData); +int endTransferCallback (int completionState, + int pendingCount, + void *clientData); +void postTransferCallback (int pendingCount, + void *clientData); + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + + +const GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +extern void set_gimp_PLUG_IN_INFO_PTR (GimpPlugInInfo *); + +/* Data structure holding data between runs */ +/* Currently unused... Eventually may be used + * to track dialog data. + */ +typedef struct +{ + gchar sourceName[34]; + gfloat xResolution; + gfloat yResolution; + gint xOffset; + gint yOffset; + gint width; + gint height; + gint imageType; +} TwainValues; + +/* Default Twain values */ +static TwainValues twainvals = +{ + "", + 100.0, 100.0, + 0, 0, + 0, 0, + TWPT_RGB +}; + +/* The standard callback functions */ +TXFR_CB_FUNCS standardCbFuncs = +{ + preTransferCallback, + beginTransferCallback, + dataTransferCallback, + endTransferCallback, + postTransferCallback +}; + +/****************************************************************** + * Dump handling + ******************************************************************/ + +#ifdef _DEBUG +/* The dumper callback functions */ +TXFR_CB_FUNCS dumperCbFuncs = +{ + dumpPreTransferCallback, + dumpBeginTransferCallback, + dumpDataTransferCallback, + dumpEndTransferCallback, + dumpPostTransferCallback +}; + +void +setRunMode (char *argv[]) +{ + char *exeName = strrchr (argv[0], '\\') + 1; + + LogMessage ("Executable name: %s\n", exeName); + + if (!_stricmp (exeName, DUMP_NAME)) + twain_run_mode = RUN_DUMP; + + if (!_stricmp (exeName, RUNDUMP_NAME)) + twain_run_mode = RUN_READDUMP; +} +#endif /* _DEBUG */ + +#ifndef TWAIN_ALTERNATE_MAIN +MAIN () +#endif + +int +scanImage (void) +{ +#ifdef _DEBUG + if (twain_run_mode == RUN_READDUMP) + return readDumpedImage (twSession); + else +#endif /* _DEBUG */ + return getImage (twSession); +} + +/* + * initTwainAppIdentity + * + * Initialize and return our application's identity for + * the TWAIN runtime. + */ +static pTW_IDENTITY +getAppIdentity (void) +{ + pTW_IDENTITY appIdentity = g_new (TW_IDENTITY, 1); + + /* Set up the application identity */ + appIdentity->Id = 0; + appIdentity->Version.MajorNum = 0; + appIdentity->Version.MinorNum = 1; + appIdentity->Version.Language = TWLG_USA; + appIdentity->Version.Country = TWCY_USA; + strcpy(appIdentity->Version.Info, "GIMP TWAIN 0.6"); + appIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR; + appIdentity->ProtocolMinor = TWON_PROTOCOLMINOR; + appIdentity->SupportedGroups = DG_IMAGE; + strcpy(appIdentity->Manufacturer, "Craig Setera"); + strcpy(appIdentity->ProductFamily, "GIMP"); + strcpy(appIdentity->ProductName, "GIMP"); + + return appIdentity; +} + +/* + * initializeTwain + * + * Do the necessary TWAIN initialization. This sets up + * our TWAIN session information. The session stuff is + * something built by me on top of the standard TWAIN + * datasource manager calls. + */ +pTW_SESSION +initializeTwain (void) +{ + pTW_IDENTITY appIdentity; + + /* Get our application's identity */ + appIdentity = getAppIdentity (); + + /* Create a new session object */ + twSession = newSession (appIdentity); + + /* Register our image transfer callback functions */ +#ifdef _DEBUG + if (twain_run_mode == RUN_DUMP) + registerTransferCallbacks (twSession, &dumperCbFuncs, NULL); + else +#endif /* _DEBUG */ + registerTransferCallbacks (twSession, &standardCbFuncs, NULL); + + return twSession; +} + +/****************************************************************** + * GIMP Plug-in entry points + ******************************************************************/ + +/* + * Plug-in Parameter definitions + */ +#define NUMBER_IN_ARGS 1 +#define IN_ARGS { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" } +#define NUMBER_OUT_ARGS 2 +#define OUT_ARGS \ + { GIMP_PDB_INT32, "image-count", "Number of acquired images" }, \ + { GIMP_PDB_INT32ARRAY, "image-ids", "Array of acquired image identifiers" } + + +/* + * query + * + * The plug-in is being queried. Install our procedure for + * acquiring. + */ +static void +query (void) +{ + static const GimpParamDef args[] = { IN_ARGS }; + static const GimpParamDef return_vals[] = { OUT_ARGS }; + +#ifdef _DEBUG + if (twain_run_mode == RUN_DUMP) + { + /* the installation of the plugin */ + gimp_install_procedure (PLUG_IN_D_NAME, + PLUG_IN_DESCRIPTION, + PLUG_IN_HELP, + PLUG_IN_AUTHOR, + PLUG_IN_COPYRIGHT, + PLUG_IN_VERSION, + "TWAIN (Dump)...", + NULL, + GIMP_PLUGIN, + NUMBER_IN_ARGS, + NUMBER_OUT_ARGS, + args, + return_vals); + + gimp_plugin_menu_register (PLUG_IN_D_NAME, "<Image>/File/Create/Acquire"); + } + else if (twain_run_mode == RUN_READDUMP) + { + /* the installation of the plugin */ + gimp_install_procedure (PLUG_IN_R_NAME, + PLUG_IN_DESCRIPTION, + PLUG_IN_HELP, + PLUG_IN_AUTHOR, + PLUG_IN_COPYRIGHT, + PLUG_IN_VERSION, + "TWAIN (Read)...", + NULL, + GIMP_PLUGIN, + NUMBER_IN_ARGS, + NUMBER_OUT_ARGS, + args, + return_vals); + + gimp_plugin_menu_register (PLUG_IN_R_NAME, "<Image>/File/Create/Acquire"); + } + else +#endif /* _DEBUG */ + { + /* the installation of the plugin */ + gimp_install_procedure (PLUG_IN_NAME, + PLUG_IN_DESCRIPTION, + PLUG_IN_HELP, + PLUG_IN_AUTHOR, + PLUG_IN_COPYRIGHT, + PLUG_IN_VERSION, + N_("_Scanner/Camera..."), + NULL, + GIMP_PLUGIN, + NUMBER_IN_ARGS, + NUMBER_OUT_ARGS, + args, + return_vals); + + gimp_plugin_menu_register (PLUG_IN_NAME, "<Image>/File/Create/Acquire"); + } +} + + +/* Return values storage */ +static GimpParam values[3]; + +/* + * run + * + * The plug-in is being requested to run. + * Capture an image from a TWAIN datasource + */ +static void +run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals) +{ + 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; + *return_vals = values; + + INIT_I18N (); + gegl_init (NULL, NULL); + + /* Before we get any further, verify that we have + * TWAIN and that there is actually a datasource + * to be used in doing the acquire. + */ + if (! twainIsAvailable ()) + { + values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; + return; + } + + /* Set up the rest of the return parameters */ + 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); + + /* How are we running today? */ + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + /* Retrieve values from the last run... + * Currently ignored + */ + gimp_get_data (PLUG_IN_NAME, &twainvals); + break; + + case GIMP_RUN_NONINTERACTIVE: + /* Currently, we don't do non-interactive calls. + * Bail if someone tries to call us non-interactively + */ + values[0].data.d_status = GIMP_PDB_CALLING_ERROR; + return; + + case GIMP_RUN_WITH_LAST_VALS: + /* Retrieve values from the last run... + * Currently ignored + */ + gimp_get_data (PLUG_IN_NAME, &twainvals); + break; + + default: + break; + } + + /* Have we succeeded so far? */ + if (values[0].data.d_status == GIMP_PDB_SUCCESS) + twainMain (); + + /* Check to make sure we got at least one valid + * image. + */ + if (values[1].data.d_int32 > 0) + { + /* An image was captured from the TWAIN + * datasource. Do final Interactive + * steps. + */ + if (run_mode == GIMP_RUN_INTERACTIVE) + { + /* Store variable states for next run */ + gimp_set_data (PLUG_IN_NAME, &twainvals, sizeof (TwainValues)); + } + + /* Set return values */ + *nreturn_vals = 3; + } + else + { + values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; + } +} + +/*********************************************************************** + * Image transfer callback functions + ***********************************************************************/ + +/* Data used to carry data between each of + * the callback function calls. + */ +typedef struct +{ + gint32 image_id; + gint32 layer_id; + GeglBuffer *buffer; + const Babl *format; + pTW_PALETTE8 paletteData; + int totalPixels; + int completedPixels; +} ClientDataStruct, *pClientDataStruct; + +/* + * preTransferCallback + * + * This callback function is called before any images + * are transferred. Set up the one time only stuff. + */ +void +preTransferCallback (void *clientData) +{ + /* Initialize our progress dialog */ + gimp_progress_init (_("Transferring data from scanner/camera")); +} + +/* + * beginTransferCallback + * + * The following function is called at the beginning + * of each image transfer. + */ +int +beginTransferCallback (pTW_IMAGEINFO imageInfo, + void *clientData) +{ + pClientDataStruct theClientData = g_new (ClientDataStruct, 1); + + const Babl *format; + GimpImageBaseType imageType; + GimpImageType layerType; + GimpPrecision precision; + + gint bpc = imageInfo->BitsPerPixel / + imageInfo->SamplesPerPixel; + + +#ifdef _DEBUG + logBegin (imageInfo, clientData); +#endif + + /* Decide on the image type */ + switch (imageInfo->PixelType) + { + case TWPT_BW: + /* Set up the image and layer types */ + imageType = GIMP_GRAY; + layerType = GIMP_GRAY_IMAGE; + precision = GIMP_PRECISION_U8_GAMMA; + format = babl_format ("Y' u8"); + break; + + case TWPT_GRAY: + /* Set up the image and layer types */ + imageType = GIMP_GRAY; + layerType = GIMP_GRAY_IMAGE; + + switch (bpc) + { + case 8: + precision = GIMP_PRECISION_U8_GAMMA; + format = babl_format ("Y' u8"); + break; + + case 16: + precision = GIMP_PRECISION_U16_GAMMA; + format = babl_format ("Y' u16"); + break; + + default: + return FALSE; + } + break; + + case TWPT_RGB: + /* Set up the image and layer types */ + imageType = GIMP_RGB; + layerType = GIMP_RGB_IMAGE; + + switch (bpc) + { + case 8: + precision = GIMP_PRECISION_U8_GAMMA; + format = babl_format ("R'G'B' u8"); + break; + + case 16: + precision = GIMP_PRECISION_U16_GAMMA; + format = babl_format ("R'G'B' u16"); + break; + + default: + return FALSE; + } + break; + + case TWPT_PALETTE: + /* Get the palette data */ + theClientData->paletteData = g_new (TW_PALETTE8, 1); + twSession->twRC = callDSM (APP_IDENTITY (twSession), + DS_IDENTITY (twSession), + DG_IMAGE, DAT_PALETTE8, MSG_GET, + (TW_MEMREF) theClientData->paletteData); + if (twSession->twRC != TWRC_SUCCESS) + return FALSE; + + switch (theClientData->paletteData->PaletteType) + { + case TWPA_RGB: + /* Set up the image and layer types */ + imageType = GIMP_RGB; + layerType = GIMP_RGB_IMAGE; + precision = GIMP_PRECISION_U8_GAMMA; + + format = babl_format ("R'G'B' u8"); + break; + + case TWPA_GRAY: + /* Set up the image and layer types */ + imageType = GIMP_GRAY; + layerType = GIMP_GRAY_IMAGE; + precision = GIMP_PRECISION_U8_GAMMA; + + format = babl_format ("Y' u8"); + break; + + default: + return FALSE; + } + break; + + default: + /* We don't know how to deal with anything other than + * the types listed above. Bail for any other image + * type. + */ + return FALSE; + } + + /* Create the GIMP image */ + theClientData->image_id = gimp_image_new_with_precision ( + imageInfo->ImageWidth, + imageInfo->ImageLength, + imageType, + precision); + + /* Set the actual resolution */ + gimp_image_set_resolution (theClientData->image_id, + FIX32ToFloat (imageInfo->XResolution), + FIX32ToFloat (imageInfo->YResolution)); + gimp_image_set_unit (theClientData->image_id, GIMP_UNIT_INCH); + + /* Create a layer */ + theClientData->layer_id = gimp_layer_new (theClientData->image_id, + _("Background"), + imageInfo->ImageWidth, + imageInfo->ImageLength, + layerType, 100, + GIMP_LAYER_MODE_NORMAL); + + /* Add the layer to the image */ + gimp_image_insert_layer (theClientData->image_id, + theClientData->layer_id, -1, 0); + + /* Update the progress dialog */ + theClientData->totalPixels = imageInfo->ImageWidth * imageInfo->ImageLength; + theClientData->completedPixels = 0; + + gimp_progress_update (0.0); + + theClientData->buffer = gimp_drawable_get_buffer (theClientData->layer_id); + theClientData->format = format; + + /* Store our client data for the data transfer callbacks */ + if (clientData) + g_free (clientData); + + setClientData (twSession, (void *) theClientData); + + /* Make sure to return TRUE to continue the image + * transfer + */ + return TRUE; +} + +/* + * bitTransferCallback + * + * The following function is called for each memory + * block that is transferred from the data source if + * the image type is Black/White. + * + * Black and white data is unpacked from bit data + * into byte data and written into a gray scale GIMP + * image. + */ +static char bitMasks[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; +static int +bitTransferCallback (pTW_IMAGEINFO imageInfo, + pTW_IMAGEMEMXFER imageMemXfer, + void *clientData) +{ + int row, col, offset; + char *srcBuf; + char *destBuf; + int rows = imageMemXfer->Rows; + int cols = imageMemXfer->Columns; + pClientDataStruct theClientData = (pClientDataStruct) clientData; + + /* Allocate a buffer as necessary */ + destBuf = gegl_scratch_new (char, rows * cols); + + /* Unpack the image data from bits into bytes */ + srcBuf = (char *) imageMemXfer->Memory.TheMem; + offset = 0; + for (row = 0; row < rows; row++) + { + for (col = 0; col < cols; col++) + { + char byte = srcBuf[(row * imageMemXfer->BytesPerRow) + (col / 8)]; + destBuf[offset++] = ((byte & bitMasks[col % 8]) != 0) ? 255 : 0; + } + } + + /* Update the complete chunk */ + gegl_buffer_set (theClientData->buffer, + GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset, + cols, rows), 0, + theClientData->format, destBuf, + GEGL_AUTO_ROWSTRIDE); + + /* Free the buffer */ + gegl_scratch_free (destBuf); + + /* Update the user on our progress */ + theClientData->completedPixels += (cols * rows); + gimp_progress_update ((double) theClientData->completedPixels / + (double) theClientData->totalPixels); + + return TRUE; +} + +/* + * directTransferCallback + * + * The following function is called for each memory + * block that is transferred from the data source if + * the image type is Grayscale or RGB. + */ +static int +directTransferCallback (pTW_IMAGEINFO imageInfo, + pTW_IMAGEMEMXFER imageMemXfer, + void *clientData) +{ + int rows = imageMemXfer->Rows; + int cols = imageMemXfer->Columns; + pClientDataStruct theClientData = (pClientDataStruct) clientData; + + /* Update the complete chunk */ + gegl_buffer_set (theClientData->buffer, + GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset, + cols, rows), 0, + theClientData->format, imageMemXfer->Memory.TheMem, + imageMemXfer->BytesPerRow); + + /* Update the user on our progress */ + theClientData->completedPixels += (cols * rows); + gimp_progress_update ((double) theClientData->completedPixels / + (double) theClientData->totalPixels); + + return TRUE; +} + +/* + * palettedTransferCallback + * + * The following function is called for each memory + * block that is transferred from the data source if + * the image type is paletted. This does not create + * an indexed image type in GIMP because for some + * reason it does not allow creation of a specific + * palette. This function will create an RGB or Gray + * image and use the palette to set the details of + * the pixels. + */ +static int +palettedTransferCallback (pTW_IMAGEINFO imageInfo, + pTW_IMAGEMEMXFER imageMemXfer, + void *clientData) +{ + int channelsPerEntry; + int row, col; + int rows = imageMemXfer->Rows; + int cols = imageMemXfer->Columns; + char *destBuf; + char *destPtr = NULL; + char *srcPtr = NULL; + + /* Get the client data */ + pClientDataStruct theClientData = (pClientDataStruct) clientData; + + /* Look up the palette entry size */ + channelsPerEntry = + (theClientData->paletteData->PaletteType == TWPA_RGB) ? 3 : 1; + + /* Allocate a buffer as necessary */ + destBuf = gegl_scratch_new (char, rows * cols * channelsPerEntry); + + /* Work through the rows */ + destPtr = destBuf; + for (row = 0; row < rows; row++) + { + srcPtr = (char *) ((char *) imageMemXfer->Memory.TheMem + + (row * imageMemXfer->BytesPerRow)); + + /* Work through the columns */ + for (col = 0; col < cols; col++) + { + /* Get the palette index */ + int index = (unsigned char) *srcPtr; + + srcPtr++; + + switch (theClientData->paletteData->PaletteType) + { + case TWPA_GRAY: + *destPtr = theClientData->paletteData->Colors[index].Channel1; + destPtr++; + break; + + case TWPA_RGB: + *destPtr = theClientData->paletteData->Colors[index].Channel1; + destPtr++; + *destPtr = theClientData->paletteData->Colors[index].Channel2; + destPtr++; + *destPtr = theClientData->paletteData->Colors[index].Channel3; + destPtr++; + } + } + } + + /* Send the complete chunk */ + gegl_buffer_set (theClientData->buffer, + GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset, + cols, rows), 0, + theClientData->format, destBuf, + GEGL_AUTO_ROWSTRIDE); + + /* Free the buffer */ + gegl_scratch_free (destBuf); + + /* Update the user on our progress */ + theClientData->completedPixels += (cols * rows); + gimp_progress_update ((double) theClientData->completedPixels / + (double) theClientData->totalPixels); + + return TRUE; +} + +/* + * dataTransferCallback + * + * The following function is called for each memory + * block that is transferred from the data source. + */ +int +dataTransferCallback (pTW_IMAGEINFO imageInfo, + pTW_IMAGEMEMXFER imageMemXfer, + void *clientData) +{ +#ifdef _DEBUG + logData (imageInfo, imageMemXfer, clientData); +#endif + + /* Choose the appropriate transfer handler */ + switch (imageInfo->PixelType) + { + case TWPT_PALETTE: + return palettedTransferCallback (imageInfo, imageMemXfer, clientData); + + case TWPT_BW: + return bitTransferCallback (imageInfo, imageMemXfer, clientData); + + case TWPT_GRAY: + case TWPT_RGB: + return directTransferCallback (imageInfo, imageMemXfer, clientData); + + default: + return FALSE; + } +} + +/* + * endTransferCallback + * + * The following function is called at the end of the + * image transfer. The caller will be handed + * the image transfer completion state. The + * following values (defined in twain.h) are + * possible: + * + * TWRC_XFERDONE + * The transfer completed successfully + * TWRC_CANCEL + * The transfer was completed by the user + * TWRC_FAILURE + * The transfer failed. + */ +int +endTransferCallback (int completionState, + int pendingCount, + void *clientData) +{ + pClientDataStruct theClientData = (pClientDataStruct) clientData; + + LogMessage ("endTransferCallback: CompState = %d, pending = %d\n", + completionState, pendingCount); + + /* Clean up and detach from the drawable */ + g_object_unref (theClientData->buffer); + + /* Make sure to check our return code */ + if (completionState == TWRC_XFERDONE) + { + /* 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; + + /* Display the image */ + LogMessage ("Displaying image %d\n", theClientData->image_id); + gimp_display_new (theClientData->image_id); + } + else + { + /* The transfer did not complete successfully */ + LogMessage ("Deleting image\n"); + 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); +} + +/* + * postTransferCallback + * + * This callback function will be called + * after all possible images have been + * transferred. + */ +void +postTransferCallback (int pendingCount, + 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 (); +} |