mirror of
https://github.com/soukoku/ntwain.git
synced 2026-01-09 03:04:44 +08:00
Fixed wrong assumption of rented array size and added example of using non system.drawing image lib.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using NTwain.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -11,8 +12,8 @@ namespace WinFormSample
|
||||
{
|
||||
/// <summary>
|
||||
/// For demoing loading dsm from custom path in case
|
||||
/// it's not installed on system and can't be placed
|
||||
/// besides the exe.
|
||||
/// it's not installed on system and don't want to be
|
||||
/// placed besides the exe.
|
||||
/// </summary>
|
||||
static class DsmLoader
|
||||
{
|
||||
@@ -24,7 +25,7 @@ namespace WinFormSample
|
||||
{
|
||||
var dll = Path.Combine(
|
||||
Path.GetDirectoryName(Environment.ProcessPath ?? Assembly.GetExecutingAssembly().Location)!,
|
||||
"platforms\\TWAINDSM.dll");
|
||||
$@"runtimes\win-{(TwainPlatform.Is32bit ? "x86" : "x64")}\native\TWAINDSM.dll");
|
||||
|
||||
__dllPtr = LoadLibraryW(dll);
|
||||
}
|
||||
|
||||
@@ -105,11 +105,23 @@ namespace WinFormSample
|
||||
{
|
||||
Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] data transferred with info {e.ImageInfo}");
|
||||
if (e.Data == null) return;
|
||||
using (var stream = new MemoryStream(e.Data))
|
||||
using (var img = Image.FromStream(stream))
|
||||
|
||||
// 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))
|
||||
{
|
||||
var saveFile = Path.Combine(saveFolder, (DateTime.Now.Ticks / 1000) + ".png");
|
||||
img.Save(saveFile, ImageFormat.Png);
|
||||
if (img.ColorType == ImageMagick.ColorType.Palette)
|
||||
{
|
||||
// bw or gray
|
||||
saveFile += ".png";
|
||||
}
|
||||
else
|
||||
{
|
||||
// color
|
||||
saveFile += ".jpg";
|
||||
img.Quality = 75;
|
||||
}
|
||||
img.Write(saveFile);
|
||||
Debug.WriteLine($"Saved image to {saveFile}");
|
||||
}
|
||||
}
|
||||
@@ -158,7 +170,7 @@ namespace WinFormSample
|
||||
twain.GetCapValues(CAP.CAP_EXTENDEDCAPS, out IList<CAP> extended);
|
||||
foreach (var c in caps)
|
||||
{
|
||||
ListViewItem it = new(c.ToString());
|
||||
ListViewItem it = new(GetFriendlyName(c));
|
||||
|
||||
if (twain.GetCapCurrent(c, out TW_CAPABILITY twcap).RC == TWRC.SUCCESS)
|
||||
{
|
||||
@@ -180,6 +192,15 @@ namespace WinFormSample
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFriendlyName(CAP c)
|
||||
{
|
||||
if (c > CAP.CAP_CUSTOMBASE)
|
||||
{
|
||||
return $"{CAP.CAP_CUSTOMBASE} + {c - CAP.CAP_CUSTOMBASE}";
|
||||
}
|
||||
return c.ToString();
|
||||
}
|
||||
|
||||
// there may be a better way...
|
||||
MethodInfo[] twainMethods = typeof(TwainAppSession).GetMethods();
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
<RootNamespace>WinFormSample</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q8-x86" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\NTwain\NTwain.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -17,7 +21,7 @@
|
||||
<None Update="platforms\qwindows.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="platforms\TWAINDSM.dll">
|
||||
<None Update="runtimes\win-x86\native\TWAINDSM.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
<ProjectReference Include="..\..\src\NTwain\NTwain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q8-x64" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\WinForm32\**\*.cs" Exclude="..\WinForm32\**\obj\**;..\WinForm32\**\bin\**">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
@@ -26,7 +30,7 @@
|
||||
<None Update="platforms\qwindows.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="platforms\TWAINDSM.dll">
|
||||
<None Update="runtimes\win-x64\native\TWAINDSM.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
31
src/NTwain/Data/BufferedData.cs
Normal file
31
src/NTwain/Data/BufferedData.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace NTwain.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple struct with bytes buffer and the valid data length.
|
||||
/// </summary>
|
||||
public struct BufferedData
|
||||
{
|
||||
/// <summary>
|
||||
/// Bytes buffer. This may be bigger than the data size
|
||||
/// and contain invalid data.
|
||||
/// </summary>
|
||||
public byte[]? Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Actual usable data length in the buffer.
|
||||
/// </summary>
|
||||
public int Length;
|
||||
|
||||
/// <summary>
|
||||
/// As a span of usable data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ReadOnlySpan<byte> AsSpan()
|
||||
{
|
||||
if (Buffer != null) return Buffer.AsSpan(0, Length);
|
||||
return Span<byte>.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,18 +13,18 @@ namespace NTwain
|
||||
AudioInfo = info;
|
||||
FileInfo = fileInfo;
|
||||
}
|
||||
public DataTransferredEventArgs(TW_AUDIOINFO info, byte[] data)
|
||||
public DataTransferredEventArgs(TW_AUDIOINFO info, BufferedData data)
|
||||
{
|
||||
AudioInfo = info;
|
||||
Data = data;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public DataTransferredEventArgs(TW_IMAGEINFO info, TW_SETUPFILEXFER? fileInfo, byte[]? data)
|
||||
public DataTransferredEventArgs(TW_IMAGEINFO info, TW_SETUPFILEXFER? fileInfo, BufferedData data)
|
||||
{
|
||||
ImageInfo = info;
|
||||
FileInfo = fileInfo;
|
||||
IsImage = true;
|
||||
Data = data;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,12 +32,13 @@ namespace NTwain
|
||||
/// </summary>
|
||||
public bool IsImage { get; }
|
||||
|
||||
private readonly BufferedData _data;
|
||||
/// <summary>
|
||||
/// The complete file data if memory was involved in the transfer.
|
||||
/// IMPORTANT: Content of this array may not valid once
|
||||
/// the event handler ends.
|
||||
/// </summary>
|
||||
public byte[]? Data { get; }
|
||||
public ReadOnlySpan<byte> Data => _data.AsSpan();
|
||||
|
||||
/// <summary>
|
||||
/// The file info if the transfer involved file information.
|
||||
|
||||
@@ -13,17 +13,15 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net462'">
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
<!--<PackageReference Include="System.Buffers" Version="4.5.1" />-->
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net462'">
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="DSM\DSMGenerator.dummy">
|
||||
<DesignTime>True</DesignTime>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using NTwain.Data;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NTwain.Native
|
||||
@@ -23,15 +24,13 @@ namespace NTwain.Native
|
||||
return test == 0x4949;
|
||||
}
|
||||
|
||||
|
||||
public static unsafe byte[]? GetBitmapData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
||||
public static unsafe BufferedData GetBitmapData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
||||
{
|
||||
var infoHeader = Marshal.PtrToStructure<BITMAPINFOHEADER>(data);
|
||||
if (infoHeader.Validate())
|
||||
{
|
||||
var fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
|
||||
|
||||
|
||||
var fileHeader = new BITMAPFILEHEADER
|
||||
{
|
||||
bfType = 0x4D42, // "BM"
|
||||
@@ -41,7 +40,7 @@ namespace NTwain.Native
|
||||
};
|
||||
fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage;
|
||||
|
||||
var dataCopy = xferMemPool.Rent((int)fileHeader.bfSize);// new byte[fileHeader.bfSize];
|
||||
var dataCopy = xferMemPool.Rent((int)fileHeader.bfSize); // new byte[fileHeader.bfSize];
|
||||
|
||||
// TODO: run benchmark on which one is faster
|
||||
|
||||
@@ -59,13 +58,12 @@ namespace NTwain.Native
|
||||
|
||||
// write image
|
||||
Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize);
|
||||
|
||||
return dataCopy;
|
||||
return new BufferedData { Buffer = dataCopy, Length = (int)fileHeader.bfSize };
|
||||
}
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
|
||||
public static byte[]? GetTiffData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
||||
public static BufferedData GetTiffData(System.Buffers.ArrayPool<byte> xferMemPool, IntPtr data)
|
||||
{
|
||||
// Find the size of the image so we can turn it into a memory stream...
|
||||
var headerSize = Marshal.SizeOf(typeof(TIFFHEADER));
|
||||
@@ -91,9 +89,9 @@ namespace NTwain.Native
|
||||
var dataCopy = xferMemPool.Rent(tiffSize);// new byte[tiffSize];
|
||||
// is this optimal?
|
||||
Marshal.Copy(data, dataCopy, 0, tiffSize);
|
||||
return dataCopy;
|
||||
return new BufferedData { Buffer = dataCopy, Length = tiffSize };
|
||||
}
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
|
||||
//internal static Bitmap ReadBitmapImage(IntPtr data)
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
using static NTwain.Native.ImageTools;
|
||||
|
||||
namespace NTwain
|
||||
{
|
||||
@@ -66,7 +67,7 @@ namespace NTwain
|
||||
do
|
||||
{
|
||||
var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ);
|
||||
_uiThreadMarshaller.Invoke((ref TW_PENDINGXFERS pending) =>
|
||||
_uiThreadMarshaller.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -159,7 +160,7 @@ namespace NTwain
|
||||
|
||||
if (State >= STATE.S5)
|
||||
{
|
||||
_uiThreadMarshaller.BeginInvoke((ref TW_PENDINGXFERS pending) =>
|
||||
_uiThreadMarshaller.BeginInvoke(() =>
|
||||
{
|
||||
DisableSource();
|
||||
});
|
||||
@@ -245,11 +246,11 @@ namespace NTwain
|
||||
{
|
||||
State = STATE.S7;
|
||||
lockedPtr = Lock(dataPtr);
|
||||
byte[]? data = null;
|
||||
BufferedData data = default;
|
||||
|
||||
// TODO: don't know how to read wav/aiff from pointer yet
|
||||
|
||||
if (data != null)
|
||||
if (data.Buffer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -260,7 +261,7 @@ namespace NTwain
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
XferMemPool.Return(data);
|
||||
XferMemPool.Return(data.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,7 +421,7 @@ namespace NTwain
|
||||
{
|
||||
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
|
||||
var args = new DataTransferredEventArgs(info, fileSetup, outStream.ToArray());
|
||||
var args = new DataTransferredEventArgs(info, fileSetup, new BufferedData { Buffer = outStream.ToArray(), Length = (int)outStream.Length });
|
||||
DataTransferred?.Invoke(this, args);
|
||||
}
|
||||
catch { }
|
||||
@@ -448,7 +449,7 @@ namespace NTwain
|
||||
try
|
||||
{
|
||||
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
|
||||
var args = new DataTransferredEventArgs(info, fileSetup, null);
|
||||
var args = new DataTransferredEventArgs(info, fileSetup, default);
|
||||
DataTransferred?.Invoke(this, args);
|
||||
}
|
||||
catch { }
|
||||
@@ -473,8 +474,7 @@ namespace NTwain
|
||||
{
|
||||
State = STATE.S7;
|
||||
lockedPtr = Lock(dataPtr);
|
||||
byte[]? data = null;
|
||||
|
||||
BufferedData data = default;
|
||||
|
||||
if (ImageTools.IsDib(lockedPtr))
|
||||
{
|
||||
@@ -490,7 +490,7 @@ namespace NTwain
|
||||
// don't support more formats :(
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
if (data.Buffer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -501,7 +501,7 @@ namespace NTwain
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
XferMemPool.Return(data);
|
||||
XferMemPool.Return(data.Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user