Acquire using strips
This section should be utilized by programmers who are familiar with handling images, Windows bitmap handling, creating image headers, and if you want to capture images sent as tiles instead of image strips. Optionally, if you plan on acquiring a TIFF image using a user-defined buffer, you must be familiar with TIFF headers and tag information.
By default, all image creation and memory allocation issues are handled by DTWAIN when DTWAIN_AcquireBuffered or DTWAIN_AccquireBufferedEx is called. Calling DTWAIN_AcquireBuffered(Ex) is all that is necessary to acquire an image using the buffered transfer mode.
However, there are cases where the programmer who is familiar with image handling will want to control the buffered transfers. It is possible for an application to utilize a user-defined buffer for the returned strip or tile of data and allowing the application to handle the image data being sent by the device. When the application decides to handle the data, DTWAIN no longer attempts to create the entire image. Instead, DTWAIN returns the strip or tile that was transferred by the device back to the application.
The advantage of this is:
The last two points mentioned above are important. Having a user-defined buffer may be the only way to process very large images (100 megabytes or greater) since the memory allocation that would need to be done for DTWAIN to build the entire image in memory would be very prohibitive . Also, high-speed image acquisition is only possible if there is very little processing of the image strips that are being sent from the device to the application. DTWAIN will just return the raw data without further processing directly to the application, thereby eliminating a potential bottleneck that DTWAIN may introduce. The user-defined buffer is currently only available for the DTWAIN_AcquireBuffered and DTWAIN_AcquireBufferedEx functions. It is not available for DTWAIN_AcquireFile, DTWAIN_AcquireFileEx, or DTWAIN_AcquireToClipboard if the flags specify DTWAIN_USEBUFFERED. DTWAIN_AcquireFile(Ex) requires that DTWAIN handle all of the image building, therefore these functions cannot currently utilize the user-defined buffer. The following table summarizes the differences between allowing DTWAIN to handle the image creation (no user-defined buffer) and when a user-defined buffer is specified:
When not using a user-defined buffer, DTWAIN_AcquireBuffered(Ex) will create an entire image if DIB's or JPEG compression is used. For TIFF compressed transfers, only the image data (TIFF without the TIFF tags fields) are generated for both user-defined and non user-defined buffer transfers. For example, if the compression is Group 4 TIFF, only the Group 4 data is acquired.
Notification processing should be used to retrieve data for each strip or tile processed. Without notification processing enabled, the strips will be acquired to the image buffer that was set up in DTWAIN_SetAcquireStripBuffer (see below), but your application will not be able to do anything with the data since there would be no programmatic access to the data while the device is acquiring. Here is an overview of the programming steps required to set up a user-defined buffer: Step 1: The first function that your application should call is DTWAIN_GetAcquireStripSizes. This function returns the minimum, maximum, and preferred size of the buffer (in bytes) that the application should use when acquiring an image in strips. It is up to the programmer to determine the buffer size desired based on these values. The programmer should not specify a buffer size less than the minimum, or more than the maximum. The preferred size should be used most of the time, but any size between the minimum and maximum are valid.
If the value is close to the minimum, the device will transfer more strips, increasing the acquisition time, but the memory usage is low. If the value chosen is close to the maximum, less strips will be transferred, reducing the acquisition time, but memory usage increased. The preferred size is usually a balance between number of strips transferred and memory usage.
Step 2: The next function that your application will need to call is to allocate the memory for the buffer. To do this, your application must call the Windows API function GlobalAlloc using the GHND flag type. If you are not familiar with this function, it is documented in any Windows API function reference. Basically, GlobalAlloc allocates memory and returns a handle to your application. This handle will be used in the next step.
So for the first two steps, your application code (if you code in 'C' or C++) will look similar to this:
DTWAIN_SOURCE Source; /* Assume that the Source is open */
LONG minSize, maxSize, prefSize; HANDLE hTheDibStrip;
/* Get the minimum, maximum, and preferred size */ DTWAIN_GetAcquireStripSizes( Source, &minSize, &maxSize, &prefSize );
/* Use the preferred size to allocate memory */ hTheDibStrip = GlobalAlloc( GHND, prefSize );
Step 3: The next step is to let DTWAIN know your application will use a user-defined buffer. The DTWAIN_SetAcquireStripBuffer does this.
DTWAIN_SOURCE Source; /* Assume that the Source is open */
LONG minSize, maxSize, prefSize; HANDLE hTheDibStrip;
/* Get the minimum, maximum, and preferred size */ DTWAIN_GetAcquireStripSizes( Source, &minSize, &maxSize, &prefSize );
/* Use the preferred size to allocate memory */ hTheDibStrip = GlobalAlloc( GHND, prefSize );
/* Let DTWAIN know about the buffer */ DTWAIN_SetAcquireStripBuffer( Source, hTheDibStrip );
If the handle is not NULL, DTWAIN will automatically use it for acquiring strips of data. To turn off user-defined buffer processing, the handle passed to DTWAIN_SetAcquireStripBuffer must be NULL.
Optional: Call DTWAIN_SetCompressionType at this point if compressed data is desired. If you are acquiring DIB's, there is no need to call DTWAIN_SetCompressionType. Step 4: Call DTWAIN_AcquireBuffered to start the acquisition.
The next steps require that you handle DTWAIN Notifications.
Step 5: Build the image header (see special note below). To do this, you must first trap the DTWAIN_TN_TRANSFERREADY notification and call DTWAIN_GetImageInfo. Below is a small sample of building a DIB header for an RGB (24-bit or higher image) using a callback function (see DTWAIN_SetCallback for more information on setting up a callback function):
Note: DTWAIN does not build any image information such as BITMAPINFOHEADER, TIFF tags, etc. Transferring buffered strip data will only transfer raw image data in the compression format specified in the call to DTWAIN_SetCompressionType. BYTE* pTheDib; BYTE *pDibStart; HANDLE hTheDib; //... LRESULT MyCallback(WPARAM wParam, LPARAM lPara, LONG UserData) { if ( wParam == DTWAIN_TN_TRANSFERREADY ) { double xres, yres; LONG width, height, bpp; LPBITMAPINFO pBitmap; int nSize;
/* Get the image information. You will need this when creating the image header you may want to construct. */ DTWAIN_GetImageInfo(SelectedSource, &xres, &yres, &width, &height, NULL, NULL, &bpp, NULL, NULL, NULL );
/* Please note that you must be familiar with building any image header or other information. The code below is a rough /* outline of how to create a DIB buffer big enough for the raw image data. If acquiring to another type (JPEG, TIFF, etc.) /* you will have to create the requisite helper code for those types, as DTWAIN does not create image headers when /* your application takes responsiblity for each sent strip of image data (see the DTWAIN_TN_TRANSFERSTRIPDONE and /* DTWAIN_TN_TRANSFERDONE notifications below) */ /* Allocate image data */ /* The image will be an RGB image, so palette is not needed */ /* Image is width * bits-per-pixel * height. The calculation makes sure that /* the image is aligned on a DWORD boundary (required for DIB images) */
/* Note that if you were saving to a file, the only thing you would really need to */ /* allocate is the size of a single row of data. This conserves memory if the only */ /* need is to write to a file. */ nSize = (((width * bpp + 31) / 32) * 4) * height;
/* Allocate memory for the entire DIB */ hTheDib = GlobalAlloc( GHND, nSize );
/* Lock the memory and access it as a pointer to an LPBITMAPINFO pTheDib = (BYTE *)GlobalLock ( hTheDib ); pDibStart = pTheDib; pBitmap = (LPBITMAPINFO)pTheDib;
/* fill in the image header */ pBitmap->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBitmap->bmiHeader.biWidth = width; pBitmap->bmiHeader.biHeight = height; pBitmap->bmiHeader.biPlanes = 1; pBitmap->bmiHeader.biBitCount = bpp; pBitmap->bmiHeader.biCompression = BI_RGB; pBitmap->bmiHeader.biSizeImage = nSize; pBitmap->bmiHeader.biClrUsed = 0;
/* Advance past the header and point to where the bitmap data is placed */ pTheDib += sizeof(BITMAPINFOHEADER); return TRUE; } //... } //...
For images less than 24-bits, usually a palette would be defined that would be added at the end of the BITMAPINFO data. Note that the example allocates memory for the entire DIB. If you are transferring a very large DIB and want to save to a file, you would only allocate for a single strip of data, and append the DIB strip to the file.
Step 6: Trap the DTWAIN_TN_TRANSFERSTRIPDONE and DTWAIN_TN_TRANSFERDONE to collect the strips as they are being acquired (this notification is also sent if transferring tiled data instead of stripped data). The DTWAIN_GetAcquireStripData function returns the information concerning the strip (or tile) that was transferred. The following code is an extension of the code in steps 1 - 5:
LRESULT MyCallback(WPARAM wParam, LPARAM lPara, LONG UserData) { LONG bytesWritten; BYTE *pTheDibStrip;
if ( wParam == DTWAIN_TN_TRANSFERSTRIPDONE || wParam == DTWAIN_TN_TRANSFERDONE ) { /* Add the strip to the buffer */ /* First, get the number of bytes received (that's all we care about in the example) */ DTWAIN_GetAcquireStripData(SelectedSource, NULL, NULL, NULL, NULL, NULL, NULL, &bytesWritten);
/* Lock the strip of data to a pointer */ pTheDibStrip = (BYTE *)GlobalLock( hTheDibStrip );
/* Now copy the strip to the DIB, incrementally building the DIB. If saving to a /* file, you may want to append each strip to a file. */ memcpy(pTheDib, pTheDibStrip, bytesWritten);
/* Advance the pointer within the DIB to the next "empty" spot. */ pTheDib += bytesWritten;
/* Unlock data */ GlobalUnlock( pTheDibStrip );
if ( wParam == DTWAIN_TN_TRANSFERDONE ) { /* The entire DIB has been transferred */ /* we can write the raw data we acquired to a file */ FILE *rawFile = fopen("raw.bin", "wb"); if ( rawFile ) { fwrite(pDibStart, GlobalSize(hTheDib), 1, rawFile); fclose(rawFile); } } return TRUE; } //..... }
From step 1), hTheDibStrip was the original strip/tile that was passed to the DTWAIN_SetAcquireStripBuffer function. This buffer will contain the latest strip that was acquired from the Twain device. The example above gets the strip and appends it to the DIB that is being built (the DIB memory was created in step 5). You may also want to call GlobalLock on the strip buffer before you passed it to DTWAIN_SetAcquireStripBuffer so that you don't have to make calls to GlobalLock and GlobalUnlock repeatedly when the strips are being transferred.
Also note that both DTWAIN_TN_TRANSFERDONE and DTWAIN_TN_TRANSFERSTRIPDONE are handled. This is important, since the last strip is transferred when DTWAIN_TN_TRANSFERDONE is sent. Note that if multiple pages are acquired, these steps 5 and 6 will be repeated for each page.
The example above is just an outline of how to handle the user-defined buffer and strips of data. For TIFF transfers, you must be familiar with how to build the TIFF header and the tag information. For JPEG transfers, the process is much simpler in that all that is needed is to append each new strip to the last strip acquired (similar to the example above), but there is no need to create a header.
The functions that handle user-defined buffers are as follows:
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|