mirror of
https://github.com/soukoku/ntwain.git
synced 2026-02-25 13:04:07 +08:00
Merge pull request #39 from soukoku/v4-support-bg-handling
Added possibility to handle transferred data by app in another thread…
This commit is contained in:
37
samples/WinForm32/Form1.Designer.cs
generated
37
samples/WinForm32/Form1.Designer.cs
generated
@@ -41,6 +41,7 @@
|
|||||||
lblState = new System.Windows.Forms.Label();
|
lblState = new System.Windows.Forms.Label();
|
||||||
label3 = new System.Windows.Forms.Label();
|
label3 = new System.Windows.Forms.Label();
|
||||||
btnOpenDef = new System.Windows.Forms.Button();
|
btnOpenDef = new System.Windows.Forms.Button();
|
||||||
|
ckShowUI = new System.Windows.Forms.CheckBox();
|
||||||
capListView = new System.Windows.Forms.ListView();
|
capListView = new System.Windows.Forms.ListView();
|
||||||
colCap = new System.Windows.Forms.ColumnHeader();
|
colCap = new System.Windows.Forms.ColumnHeader();
|
||||||
colType = new System.Windows.Forms.ColumnHeader();
|
colType = new System.Windows.Forms.ColumnHeader();
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
btnStart = new System.Windows.Forms.Button();
|
btnStart = new System.Windows.Forms.Button();
|
||||||
btnShowSettings = new System.Windows.Forms.Button();
|
btnShowSettings = new System.Windows.Forms.Button();
|
||||||
btnClose = new System.Windows.Forms.Button();
|
btnClose = new System.Windows.Forms.Button();
|
||||||
ckShowUI = new System.Windows.Forms.CheckBox();
|
ckBgImageHandling = new System.Windows.Forms.CheckBox();
|
||||||
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
|
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
|
||||||
splitContainer1.Panel1.SuspendLayout();
|
splitContainer1.Panel1.SuspendLayout();
|
||||||
splitContainer1.Panel2.SuspendLayout();
|
splitContainer1.Panel2.SuspendLayout();
|
||||||
@@ -167,6 +168,7 @@
|
|||||||
//
|
//
|
||||||
// splitContainer1.Panel2
|
// splitContainer1.Panel2
|
||||||
//
|
//
|
||||||
|
splitContainer1.Panel2.Controls.Add(ckBgImageHandling);
|
||||||
splitContainer1.Panel2.Controls.Add(ckShowUI);
|
splitContainer1.Panel2.Controls.Add(ckShowUI);
|
||||||
splitContainer1.Panel2.Controls.Add(capListView);
|
splitContainer1.Panel2.Controls.Add(capListView);
|
||||||
splitContainer1.Panel2.Controls.Add(label4);
|
splitContainer1.Panel2.Controls.Add(label4);
|
||||||
@@ -207,6 +209,18 @@
|
|||||||
btnOpenDef.UseVisualStyleBackColor = true;
|
btnOpenDef.UseVisualStyleBackColor = true;
|
||||||
btnOpenDef.Click += btnOpenDef_Click;
|
btnOpenDef.Click += btnOpenDef_Click;
|
||||||
//
|
//
|
||||||
|
// ckShowUI
|
||||||
|
//
|
||||||
|
ckShowUI.AutoSize = true;
|
||||||
|
ckShowUI.Checked = true;
|
||||||
|
ckShowUI.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
ckShowUI.Location = new System.Drawing.Point(437, 35);
|
||||||
|
ckShowUI.Name = "ckShowUI";
|
||||||
|
ckShowUI.Size = new System.Drawing.Size(69, 19);
|
||||||
|
ckShowUI.TabIndex = 9;
|
||||||
|
ckShowUI.Text = "Show UI";
|
||||||
|
ckShowUI.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// capListView
|
// capListView
|
||||||
//
|
//
|
||||||
capListView.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
capListView.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||||
@@ -289,17 +303,17 @@
|
|||||||
btnClose.UseVisualStyleBackColor = true;
|
btnClose.UseVisualStyleBackColor = true;
|
||||||
btnClose.Click += btnClose_Click;
|
btnClose.Click += btnClose_Click;
|
||||||
//
|
//
|
||||||
// ckShowUI
|
// ckBgImageHandling
|
||||||
//
|
//
|
||||||
ckShowUI.AutoSize = true;
|
ckBgImageHandling.AutoSize = true;
|
||||||
ckShowUI.Checked = true;
|
ckBgImageHandling.Checked = true;
|
||||||
ckShowUI.CheckState = System.Windows.Forms.CheckState.Checked;
|
ckBgImageHandling.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
ckShowUI.Location = new System.Drawing.Point(437, 35);
|
ckBgImageHandling.Location = new System.Drawing.Point(512, 35);
|
||||||
ckShowUI.Name = "ckShowUI";
|
ckBgImageHandling.Name = "ckBgImageHandling";
|
||||||
ckShowUI.Size = new System.Drawing.Size(69, 19);
|
ckBgImageHandling.Size = new System.Drawing.Size(220, 19);
|
||||||
ckShowUI.TabIndex = 9;
|
ckBgImageHandling.TabIndex = 10;
|
||||||
ckShowUI.Text = "Show UI";
|
ckBgImageHandling.Text = "Handle image data in another thread";
|
||||||
ckShowUI.UseVisualStyleBackColor = true;
|
ckBgImageHandling.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
@@ -345,5 +359,6 @@
|
|||||||
private System.Windows.Forms.ColumnHeader colSupport;
|
private System.Windows.Forms.ColumnHeader colSupport;
|
||||||
private System.Windows.Forms.ColumnHeader colExtended;
|
private System.Windows.Forms.ColumnHeader colExtended;
|
||||||
private System.Windows.Forms.CheckBox ckShowUI;
|
private System.Windows.Forms.CheckBox ckShowUI;
|
||||||
|
private System.Windows.Forms.CheckBox ckBgImageHandling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,11 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace WinFormSample
|
namespace WinFormSample
|
||||||
@@ -20,6 +19,8 @@ namespace WinFormSample
|
|||||||
{
|
{
|
||||||
private TwainAppSession twain;
|
private TwainAppSession twain;
|
||||||
private readonly string saveFolder;
|
private readonly string saveFolder;
|
||||||
|
readonly Stopwatch watch = new();
|
||||||
|
private bool _useThreadForImag;
|
||||||
|
|
||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
@@ -33,8 +34,9 @@ namespace WinFormSample
|
|||||||
twain.StateChanged += Twain_StateChanged;
|
twain.StateChanged += Twain_StateChanged;
|
||||||
twain.DefaultSourceChanged += Twain_DefaultSourceChanged;
|
twain.DefaultSourceChanged += Twain_DefaultSourceChanged;
|
||||||
twain.CurrentSourceChanged += Twain_CurrentSourceChanged;
|
twain.CurrentSourceChanged += Twain_CurrentSourceChanged;
|
||||||
|
twain.SourceDisabled += Twain_SourceDisabled;
|
||||||
twain.TransferReady += Twain_TransferReady;
|
twain.TransferReady += Twain_TransferReady;
|
||||||
twain.Transferred += Twain_DataTransferred;
|
twain.Transferred += Twain_Transferred;
|
||||||
twain.TransferError += Twain_TransferError;
|
twain.TransferError += Twain_TransferError;
|
||||||
twain.DeviceEvent += Twain_DeviceEvent;
|
twain.DeviceEvent += Twain_DeviceEvent;
|
||||||
|
|
||||||
@@ -47,6 +49,15 @@ namespace WinFormSample
|
|||||||
this.Disposed += Form1_Disposed;
|
this.Disposed += Form1_Disposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Twain_SourceDisabled(TwainAppSession sender, TW_IDENTITY_LEGACY e)
|
||||||
|
{
|
||||||
|
if (watch.IsRunning)
|
||||||
|
{
|
||||||
|
watch.Stop();
|
||||||
|
MessageBox.Show($"Took {watch.Elapsed} to finish that transfer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
|
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
|
||||||
{
|
{
|
||||||
switch (e.Reason)
|
switch (e.Reason)
|
||||||
@@ -103,28 +114,54 @@ namespace WinFormSample
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Twain_DataTransferred(TwainAppSession sender, TransferredEventArgs e)
|
private void Twain_Transferred(TwainAppSession sender, TransferredEventArgs e)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] data transferred with info {e.ImageInfo}");
|
Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] data transferred with info {e.ImageInfo}");
|
||||||
if (e.Data == null) return;
|
// if using a high-speed scanner, imaging handling could be a bottleneck
|
||||||
|
// so it's possible to pass the data to another thread while the scanning
|
||||||
|
// loop happens. Just remember to dispose it after.
|
||||||
|
|
||||||
// example of using some lib to handle image data
|
if (_useThreadForImag)
|
||||||
var saveFile = Path.Combine(saveFolder, (DateTime.Now.Ticks / 1000).ToString());
|
|
||||||
using (var img = new ImageMagick.MagickImage(e.Data))
|
|
||||||
{
|
{
|
||||||
if (img.ColorType == ImageMagick.ColorType.Palette)
|
// bad thread example but whatev. should use a dedicated thread of some sort for real
|
||||||
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
// bw or gray
|
HandleTransferredData(e);
|
||||||
saveFile += ".png";
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
HandleTransferredData(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleTransferredData(TransferredEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// example of using some lib to handle image data
|
||||||
|
var saveFile = Path.Combine(saveFolder, (DateTime.Now.Ticks / 1000).ToString());
|
||||||
|
using (var img = new ImageMagick.MagickImage(e.Data))
|
||||||
{
|
{
|
||||||
// color
|
if (img.ColorType == ImageMagick.ColorType.Palette)
|
||||||
saveFile += ".jpg";
|
{
|
||||||
img.Quality = 75;
|
// bw or gray
|
||||||
|
saveFile += ".png";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// color
|
||||||
|
saveFile += ".jpg";
|
||||||
|
img.Quality = 75;
|
||||||
|
}
|
||||||
|
img.Write(saveFile);
|
||||||
|
Debug.WriteLine($"Saved image to {saveFile}");
|
||||||
}
|
}
|
||||||
img.Write(saveFile);
|
}
|
||||||
Debug.WriteLine($"Saved image to {saveFile}");
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
e.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +399,11 @@ namespace WinFormSample
|
|||||||
|
|
||||||
private void btnStart_Click(object sender, EventArgs e)
|
private void btnStart_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
twain.EnableSource(ckShowUI.Checked, false);
|
if (twain.EnableSource(ckShowUI.Checked, false).IsSuccess)
|
||||||
|
{
|
||||||
|
_useThreadForImag = ckBgImageHandling.Checked;
|
||||||
|
watch.Restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,45 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace NTwain.Data
|
namespace NTwain.Data
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Simple struct with bytes buffer and the valid data length.
|
/// Simple thing with shared bytes buffer and the valid data length.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct BufferedData
|
public class BufferedData : IDisposable
|
||||||
{
|
{
|
||||||
|
// experiment using array pool for things transferred in memory.
|
||||||
|
// this can pool up to a "normal" max of legal size paper in 24 bit at 300 dpi (~31MB)
|
||||||
|
// so the array max is made with 32 MB. Typical usage should be a lot less.
|
||||||
|
internal static readonly ArrayPool<byte> MemPool = ArrayPool<byte>.Create(32 * 1024 * 1024, 8);
|
||||||
|
|
||||||
|
public BufferedData(int size)
|
||||||
|
{
|
||||||
|
_buffer = MemPool.Rent(size);
|
||||||
|
_length = size;
|
||||||
|
_fromPool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal BufferedData(byte[] data, int size, bool fromPool)
|
||||||
|
{
|
||||||
|
_buffer = data;
|
||||||
|
_length = size;
|
||||||
|
_fromPool = fromPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _fromPool;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bytes buffer. This may be bigger than the data size
|
/// Bytes buffer. This may be bigger than the data size
|
||||||
/// and contain invalid data.
|
/// and contain invalid data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[]? Buffer;
|
byte[]? _buffer;
|
||||||
|
public byte[]? Buffer { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Actual usable data length in the buffer.
|
/// Actual usable data length in the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Length;
|
int _length;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// As a span of usable data.
|
/// As a span of usable data.
|
||||||
@@ -24,8 +47,17 @@ namespace NTwain.Data
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public ReadOnlySpan<byte> AsSpan()
|
public ReadOnlySpan<byte> AsSpan()
|
||||||
{
|
{
|
||||||
if (Buffer != null) return Buffer.AsSpan(0, Length);
|
if (_buffer != null) return _buffer.AsSpan(0, _length);
|
||||||
return Span<byte>.Empty;
|
return Span<byte>.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_fromPool && _buffer != null)
|
||||||
|
{
|
||||||
|
MemPool.Return(_buffer);
|
||||||
|
_buffer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace NTwain.Native
|
|||||||
return test == 0x4949;
|
return test == 0x4949;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe BufferedData GetBitmapData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
public static unsafe BufferedData? GetBitmapData(IntPtr data)
|
||||||
{
|
{
|
||||||
var infoHeader = Marshal.PtrToStructure<BITMAPINFOHEADER>(data);
|
var infoHeader = Marshal.PtrToStructure<BITMAPINFOHEADER>(data);
|
||||||
if (infoHeader.Validate())
|
if (infoHeader.Validate())
|
||||||
@@ -40,7 +40,7 @@ namespace NTwain.Native
|
|||||||
};
|
};
|
||||||
fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage;
|
fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage;
|
||||||
|
|
||||||
var dataCopy = xferMemPool.Rent((int)fileHeader.bfSize); // new byte[fileHeader.bfSize];
|
var dataCopy = BufferedData.MemPool.Rent((int)fileHeader.bfSize); // new byte[fileHeader.bfSize];
|
||||||
|
|
||||||
// TODO: run benchmark on which one is faster
|
// TODO: run benchmark on which one is faster
|
||||||
|
|
||||||
@@ -58,12 +58,12 @@ namespace NTwain.Native
|
|||||||
|
|
||||||
// write image
|
// write image
|
||||||
Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize);
|
Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize);
|
||||||
return new BufferedData { Buffer = dataCopy, Length = (int)fileHeader.bfSize };
|
return new BufferedData(dataCopy, (int)fileHeader.bfSize, true);
|
||||||
}
|
}
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedData GetTiffData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
public static BufferedData? GetTiffData(IntPtr data)
|
||||||
{
|
{
|
||||||
// Find the size of the image so we can turn it into a memory stream...
|
// Find the size of the image so we can turn it into a memory stream...
|
||||||
var headerSize = Marshal.SizeOf(typeof(TIFFHEADER));
|
var headerSize = Marshal.SizeOf(typeof(TIFFHEADER));
|
||||||
@@ -86,10 +86,9 @@ namespace NTwain.Native
|
|||||||
|
|
||||||
if (tiffSize > 0)
|
if (tiffSize > 0)
|
||||||
{
|
{
|
||||||
var dataCopy = xferMemPool.Rent(tiffSize);// new byte[tiffSize];
|
var dataCopy = BufferedData.MemPool.Rent(tiffSize);// new byte[tiffSize];
|
||||||
// is this optimal?
|
|
||||||
Marshal.Copy(data, dataCopy, 0, tiffSize);
|
Marshal.Copy(data, dataCopy, 0, tiffSize);
|
||||||
return new BufferedData { Buffer = dataCopy, Length = tiffSize };
|
return new BufferedData(dataCopy, tiffSize, true);
|
||||||
}
|
}
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ namespace NTwain
|
|||||||
EndOfJobFlag = endOfJobFlag;
|
EndOfJobFlag = endOfJobFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether the current transfer should be skipped
|
|
||||||
/// and continue next transfer if there are more data.
|
|
||||||
/// </summary>
|
|
||||||
public bool SkipCurrent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether to cancel the capture phase.
|
/// Gets or sets whether to cancel the capture phase.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ using System;
|
|||||||
|
|
||||||
namespace NTwain
|
namespace NTwain
|
||||||
{
|
{
|
||||||
// TODO: maybe a 2-level "dispose" with end of event being 1
|
|
||||||
// and manual dispose 2 for perf if this is not good enough.
|
|
||||||
|
|
||||||
public class TransferredEventArgs : EventArgs
|
public class TransferredEventArgs : EventArgs, IDisposable
|
||||||
{
|
{
|
||||||
public TransferredEventArgs(TW_AUDIOINFO info, TW_SETUPFILEXFER fileInfo)
|
public TransferredEventArgs(TW_AUDIOINFO info, TW_SETUPFILEXFER fileInfo)
|
||||||
{
|
{
|
||||||
@@ -19,7 +17,7 @@ namespace NTwain
|
|||||||
_data = data;
|
_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransferredEventArgs(TwainAppSession twain, TW_IMAGEINFO info, TW_SETUPFILEXFER? fileInfo, BufferedData data)
|
public TransferredEventArgs(TwainAppSession twain, TW_IMAGEINFO info, TW_SETUPFILEXFER? fileInfo, BufferedData? data)
|
||||||
{
|
{
|
||||||
ImageInfo = info;
|
ImageInfo = info;
|
||||||
FileInfo = fileInfo;
|
FileInfo = fileInfo;
|
||||||
@@ -35,13 +33,13 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsImage { get; }
|
public bool IsImage { get; }
|
||||||
|
|
||||||
private readonly BufferedData _data;
|
private readonly BufferedData? _data;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The complete file data if memory was involved in the transfer.
|
/// The complete file data if memory was involved in the transfer.
|
||||||
/// IMPORTANT: Content of this array may not valid once
|
/// IMPORTANT: Content of this array will not be valid once
|
||||||
/// the event handler ends.
|
/// this event arg has been disposed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlySpan<byte> Data => _data.AsSpan();
|
public ReadOnlySpan<byte> Data => _data == null ? ReadOnlySpan<byte>.Empty : _data.AsSpan();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The file info if the transfer involved file information.
|
/// The file info if the transfer involved file information.
|
||||||
@@ -73,5 +71,9 @@ namespace NTwain
|
|||||||
return _twain.GetExtendedImageInfo(ref container);
|
return _twain.GetExtendedImageInfo(ref container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_data?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,11 +12,6 @@ namespace NTwain
|
|||||||
|
|
||||||
partial class TwainAppSession
|
partial class TwainAppSession
|
||||||
{
|
{
|
||||||
// experiment using array pool for things transferred in memory.
|
|
||||||
// this can pool up to a "normal" max of legal size paper in 24 bit at 300 dpi (~31MB)
|
|
||||||
// so the array max is made with 32 MB. Typical usage should be a lot less.
|
|
||||||
static readonly ArrayPool<byte> XferMemPool = ArrayPool<byte>.Create(32 * 1024 * 1024, 8);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can only be called in state 7, so it's hidden here and
|
/// Can only be called in state 7, so it's hidden here and
|
||||||
/// only exposed in data transferred event.
|
/// only exposed in data transferred event.
|
||||||
@@ -265,11 +260,11 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
State = STATE.S7;
|
State = STATE.S7;
|
||||||
lockedPtr = Lock(dataPtr);
|
lockedPtr = Lock(dataPtr);
|
||||||
BufferedData data = default;
|
BufferedData? data = default;
|
||||||
|
|
||||||
// TODO: don't know how to read wav/aiff from pointer yet
|
// TODO: don't know how to read wav/aiff from pointer yet
|
||||||
|
|
||||||
if (data.Buffer != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -277,10 +272,9 @@ namespace NTwain
|
|||||||
var args = new TransferredEventArgs(info, data);
|
var args = new TransferredEventArgs(info, data);
|
||||||
Transferred?.Invoke(this, args);
|
Transferred?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
catch { }
|
catch
|
||||||
finally
|
|
||||||
{
|
{
|
||||||
XferMemPool.Return(data.Buffer);
|
data.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +315,7 @@ namespace NTwain
|
|||||||
};
|
};
|
||||||
memXferOSX.Memory = memXfer.Memory;
|
memXferOSX.Memory = memXfer.Memory;
|
||||||
|
|
||||||
byte[] dotnetBuff = XferMemPool.Rent((int)buffSize);
|
byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
@@ -354,7 +348,7 @@ namespace NTwain
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (memPtr != IntPtr.Zero) Free(memPtr);
|
if (memPtr != IntPtr.Zero) Free(memPtr);
|
||||||
XferMemPool.Return(dotnetBuff);
|
BufferedData.MemPool.Return(dotnetBuff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -400,7 +394,7 @@ namespace NTwain
|
|||||||
|
|
||||||
// TODO: how to get actual file size before hand? Is it imagelayout?
|
// TODO: how to get actual file size before hand? Is it imagelayout?
|
||||||
// otherwise will just write to stream with lots of copies
|
// otherwise will just write to stream with lots of copies
|
||||||
byte[] dotnetBuff = XferMemPool.Rent((int)buffSize);
|
byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
|
||||||
using var outStream = new MemoryStream();
|
using var outStream = new MemoryStream();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -431,7 +425,7 @@ namespace NTwain
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (memPtr != IntPtr.Zero) Free(memPtr);
|
if (memPtr != IntPtr.Zero) Free(memPtr);
|
||||||
XferMemPool.Return(dotnetBuff);
|
BufferedData.MemPool.Return(dotnetBuff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc == TWRC.XFERDONE)
|
if (rc == TWRC.XFERDONE)
|
||||||
@@ -440,7 +434,7 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
|
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
|
||||||
// ToArray bypasses the XferMemPool but I guess this will have to do for now
|
// ToArray bypasses the XferMemPool but I guess this will have to do for now
|
||||||
var args = new TransferredEventArgs(this, info, fileSetup, new BufferedData { Buffer = outStream.ToArray(), Length = (int)outStream.Length });
|
var args = new TransferredEventArgs(this, info, fileSetup, new BufferedData(outStream.ToArray(), (int)outStream.Length, false));
|
||||||
Transferred?.Invoke(this, args);
|
Transferred?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
@@ -494,15 +488,15 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
State = STATE.S7;
|
State = STATE.S7;
|
||||||
lockedPtr = Lock(dataPtr);
|
lockedPtr = Lock(dataPtr);
|
||||||
BufferedData data = default;
|
BufferedData? data = default;
|
||||||
|
|
||||||
if (ImageTools.IsDib(lockedPtr))
|
if (ImageTools.IsDib(lockedPtr))
|
||||||
{
|
{
|
||||||
data = ImageTools.GetBitmapData(XferMemPool, lockedPtr);
|
data = ImageTools.GetBitmapData(lockedPtr);
|
||||||
}
|
}
|
||||||
else if (ImageTools.IsTiff(lockedPtr))
|
else if (ImageTools.IsTiff(lockedPtr))
|
||||||
{
|
{
|
||||||
data = ImageTools.GetTiffData(XferMemPool, lockedPtr);
|
data = ImageTools.GetTiffData(lockedPtr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -510,7 +504,7 @@ namespace NTwain
|
|||||||
// don't support more formats :(
|
// don't support more formats :(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.Buffer != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -519,10 +513,9 @@ namespace NTwain
|
|||||||
var args = new TransferredEventArgs(this, info, null, data);
|
var args = new TransferredEventArgs(this, info, null, data);
|
||||||
Transferred?.Invoke(this, args);
|
Transferred?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
catch { }
|
catch
|
||||||
finally
|
|
||||||
{
|
{
|
||||||
XferMemPool.Return(data.Buffer);
|
data.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user