tmyroadctfig / twaindotnet

MIT License
179 stars 117 forks source link

Incremental scanning progress in UI #38

Open jeske opened 7 years ago

jeske commented 7 years ago

TLDR - This patch allows scanned image data to arrive incrementally, instead of only once the scan is done. It also may have lower memory consumption during the scan, because twain hands off incremental buffers to TwainDotNet. (though it could be lower still, see below)

Detailed Changes

This patch adds a new DataSourceManager.UseIncrementalMemoryXfer boolean flag (default false), which when set to true causes TwainDotNet to use new code for incremental TWAIN ImageMemXfer, instead of the existing blocking ImageNativeXfer. This allows the image to appear incrementally, and lower memory consumption, because the marshalling handoffs between Twain, TwainDotNet, and managed Bitmaps occur in smaller incremental buffers.

This patch also fixes a memory leak in DataSourcemanager.TransferPictures(), where hbitmap is not deallocated if a TWAIN failure code causes an early-exit before the IDisposable BitmapRenderer was created to manage it's lifetime. The hbitmap to Bitmap conversion is now a static method, and TransferPictures() closes lifetime on hbitmap in a finally clause.

Unfortunately, the diff display for TransferPictures and TransferPicturesIncremental are a bit intermingled, because these two functions are adjacent and have some similar pre and post code around the transfers.

It also adds a TwainBool enum, because this added tests for twain boolean values, and it felt a bit error prone to just use "1" and "0" numbers.

Areas for improvement:

The new MemoryXfer Incremental mode does not support 40bpp CMYK, or Planar RRR-GGG-BBB formats, because these formats are not supported by Gdi32Native.SetDIBitsToDevice. These formats could either be supported via other conversion means (using unsafe code), or the code could be changed to fallback to ImageNativeXfer (assuming it supports them). One wrinkle is that I can't find specific details of the TWAIN 40bit CMYK format in the spec.

One of the benefits of incremental transfers is consuming less memory for very large scan images. This code does reduce memory consumption, but it could reduce it more. Currently TransferPicturesIncremental allocates a large Bitmap to hold the entire finished image, to remain compatible with the existing TransferImageEventArgs info. However, if a separate TransferImageEventIncrementalArgs was made, that large allocation would be unnecessary.

jeske commented 7 years ago

updated to support 1-bit and 8-bit greyscale via MemoryXfer