Skip to content

Commit

Permalink
Fixed wrong assumption of rented array size and added example of usin…
Browse files Browse the repository at this point in the history
…g non system.drawing image lib.
  • Loading branch information
soukoku committed Apr 8, 2023
1 parent 29a8031 commit 2474fbf
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 43 deletions.
9 changes: 5 additions & 4 deletions samples/WinForm32/DsmLoader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -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
{
Expand All @@ -24,7 +25,7 @@ public static bool TryUseCustomDSM()
{
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);
}
Expand Down
31 changes: 26 additions & 5 deletions samples/WinForm32/Form1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,23 @@ private void Twain_DataTransferred(TwainAppSession sender, DataTransferredEventA
{
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}");
}
}
Expand Down Expand Up @@ -158,7 +170,7 @@ private void LoadCapInfoList()
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)
{
Expand All @@ -180,6 +192,15 @@ private void LoadCapInfoList()
}
}

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();

Expand Down
6 changes: 5 additions & 1 deletion samples/WinForm32/WinForm32.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down
File renamed without changes.
6 changes: 5 additions & 1 deletion samples/WinForm64/WinForm64.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down
File renamed without changes.
31 changes: 31 additions & 0 deletions src/NTwain/Data/BufferedData.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
11 changes: 6 additions & 5 deletions src/NTwain/DataTransferredEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,32 @@ public DataTransferredEventArgs(TW_AUDIOINFO info, TW_SETUPFILEXFER fileInfo)
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>
/// Whether transferred data is an image or audio.
/// </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.
Expand Down
8 changes: 3 additions & 5 deletions src/NTwain/NTwain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
20 changes: 9 additions & 11 deletions src/NTwain/Native/ImageTools.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NTwain.Data;
using System;
using System.Runtime.InteropServices;

namespace NTwain.Native
Expand All @@ -23,15 +24,13 @@ public static bool IsTiff(IntPtr data)
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"
Expand All @@ -41,7 +40,7 @@ public static bool IsTiff(IntPtr data)
};
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

Expand All @@ -59,13 +58,12 @@ public static bool IsTiff(IntPtr data)

// 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));
Expand All @@ -91,9 +89,9 @@ public static bool IsTiff(IntPtr data)
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)
Expand Down
22 changes: 11 additions & 11 deletions src/NTwain/TwainAppSession.Xfers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Xml.Serialization;
using static NTwain.Native.ImageTools;

namespace NTwain
{
Expand Down Expand Up @@ -66,7 +67,7 @@ void EnterTransferRoutine()
do
{
var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ);
_uiThreadMarshaller.Invoke((ref TW_PENDINGXFERS pending) =>
_uiThreadMarshaller.Invoke(() =>
{
try
{
Expand Down Expand Up @@ -159,7 +160,7 @@ void EnterTransferRoutine()

if (State >= STATE.S5)
{
_uiThreadMarshaller.BeginInvoke((ref TW_PENDINGXFERS pending) =>
_uiThreadMarshaller.BeginInvoke(() =>
{
DisableSource();
});
Expand Down Expand Up @@ -245,11 +246,11 @@ private STS TransferNativeAudio(ref TW_PENDINGXFERS pending)
{
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
{
Expand All @@ -260,7 +261,7 @@ private STS TransferNativeAudio(ref TW_PENDINGXFERS pending)
catch { }
finally
{
XferMemPool.Return(data);
XferMemPool.Return(data.Buffer);
}
}
}
Expand Down Expand Up @@ -420,7 +421,7 @@ private STS TransferMemoryFileImage(ref TW_PENDINGXFERS pending)
{
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 { }
Expand Down Expand Up @@ -448,7 +449,7 @@ private STS TransferFileImage(ref TW_PENDINGXFERS pending)
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 { }
Expand All @@ -473,8 +474,7 @@ private STS TransferNativeImage(ref TW_PENDINGXFERS pending)
{
State = STATE.S7;
lockedPtr = Lock(dataPtr);
byte[]? data = null;

BufferedData data = default;

if (ImageTools.IsDib(lockedPtr))
{
Expand All @@ -490,7 +490,7 @@ private STS TransferNativeImage(ref TW_PENDINGXFERS pending)
// don't support more formats :(
}

if (data != null)
if (data.Buffer != null)
{
try
{
Expand All @@ -501,7 +501,7 @@ private STS TransferNativeImage(ref TW_PENDINGXFERS pending)
catch { }
finally
{
XferMemPool.Return(data);
XferMemPool.Return(data.Buffer);
}
}

Expand Down

0 comments on commit 2474fbf

Please sign in to comment.