Added tiff support.

This commit is contained in:
soukoku
2015-02-18 21:06:07 -05:00
parent b27a9ba4cc
commit daaeb840af
10 changed files with 166 additions and 104 deletions

View File

@@ -124,11 +124,8 @@
<Compile Include="..\NTwain\Internals\WrappedManualResetEvent.cs"> <Compile Include="..\NTwain\Internals\WrappedManualResetEvent.cs">
<Link>Internals\WrappedManualResetEvent.cs</Link> <Link>Internals\WrappedManualResetEvent.cs</Link>
</Compile> </Compile>
<Compile Include="..\NTwain\Interop\BITMAPINFO.cs"> <Compile Include="..\NTwain\Interop\BITMAP.cs">
<Link>Interop\BITMAPINFO.cs</Link> <Link>Interop\BITMAP.cs</Link>
</Compile>
<Compile Include="..\NTwain\Interop\BITMAPINFOHEADER.cs">
<Link>Interop\BITMAPINFOHEADER.cs</Link>
</Compile> </Compile>
<Compile Include="..\NTwain\Interop\MESSAGE.cs"> <Compile Include="..\NTwain\Interop\MESSAGE.cs">
<Link>Interop\MESSAGE.cs</Link> <Link>Interop\MESSAGE.cs</Link>
@@ -136,6 +133,9 @@
<Compile Include="..\NTwain\Interop\NativeMethods.cs"> <Compile Include="..\NTwain\Interop\NativeMethods.cs">
<Link>Interop\NativeMethods.cs</Link> <Link>Interop\NativeMethods.cs</Link>
</Compile> </Compile>
<Compile Include="..\NTwain\Interop\TIFF.cs">
<Link>Interop\TIFF.cs</Link>
</Compile>
<Compile Include="..\NTwain\Interop\UnsafeNativeMethods.cs"> <Compile Include="..\NTwain\Interop\UnsafeNativeMethods.cs">
<Link>Interop\UnsafeNativeMethods.cs</Link> <Link>Interop\UnsafeNativeMethods.cs</Link>
</Compile> </Compile>

View File

@@ -143,16 +143,16 @@ namespace NTwain
/// Gets the bitmap from the <see cref="NativeData"/> if it's an image. /// Gets the bitmap from the <see cref="NativeData"/> if it's an image.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public Bitmap GetNativeBitmap() public Image GetNativeImage()
{ {
Bitmap image = null; Image image = null;
if (NativeData != IntPtr.Zero) if (NativeData != IntPtr.Zero)
{ {
if (PlatformInfo.Current.IsWindows) if (ImageTools.IsDib(NativeData))
{ {
image = ImageTools.ReadBitmapImage(NativeData); image = ImageTools.ReadBitmapImage(NativeData);
} }
else if (PlatformInfo.Current.IsLinux) else if (ImageTools.IsTiff(NativeData))
{ {
image = ImageTools.ReadTiffImage(NativeData); image = ImageTools.ReadTiffImage(NativeData);
} }

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; using System.Security;
@@ -14,77 +15,117 @@ namespace NTwain
{ {
public static class ImageTools public static class ImageTools
{ {
internal static Bitmap ReadBitmapImage(IntPtr dibBitmap) internal static bool IsDib(IntPtr data)
{
// a quick check not guaranteed correct,
// compare first 2 bytes to size of struct (which is also the first field)
var test = Marshal.ReadInt16(data);
// should be 40
return test == BITMAPINFOHEADER.GetByteSize();
}
internal static bool IsTiff(IntPtr data)
{
var test = Marshal.ReadInt16(data);
// should be II
return test == 0x4949;
}
internal static Bitmap ReadBitmapImage(IntPtr data)
{ {
Bitmap finalImg = null; Bitmap finalImg = null;
Bitmap tempImg = null; Bitmap tempImg = null;
if (IsDib(dibBitmap)) try
{ {
try var header = (BITMAPINFOHEADER)Marshal.PtrToStructure(data, typeof(BITMAPINFOHEADER));
{
var header = (BITMAPINFOHEADER)Marshal.PtrToStructure(dibBitmap, typeof(BITMAPINFOHEADER));
if (header.Validate()) if (header.Validate())
{
PixelFormat format = header.GetDrawingPixelFormat();
tempImg = new Bitmap(header.biWidth, Math.Abs(header.biHeight), header.GetStride(), format, header.GetScan0(dibBitmap));
ColorPalette pal = header.GetDrawingPalette(dibBitmap);
if (pal != null)
{
tempImg.Palette = pal;
}
float xdpi = header.GetXDpi();
float ydpi = header.GetYDpi();
if (xdpi != 0 && ydpi == 0)
{
ydpi = xdpi;
}
else if (ydpi != 0 && xdpi == 0)
{
xdpi = ydpi;
}
if (xdpi != 0)
{
tempImg.SetResolution(xdpi, ydpi);
}
if (header.IsBottomUpImage)
{
tempImg.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
finalImg = tempImg;
tempImg = null;
}
}
finally
{ {
if (tempImg != null) PixelFormat format = header.GetDrawingPixelFormat();
tempImg = new Bitmap(header.biWidth, Math.Abs(header.biHeight), header.GetStride(), format, header.GetScan0(data));
ColorPalette pal = header.GetDrawingPalette(data);
if (pal != null)
{ {
tempImg.Dispose(); tempImg.Palette = pal;
} }
float xdpi = header.GetXDpi();
float ydpi = header.GetYDpi();
if (xdpi != 0 && ydpi == 0)
{
ydpi = xdpi;
}
else if (ydpi != 0 && xdpi == 0)
{
xdpi = ydpi;
}
if (xdpi != 0)
{
tempImg.SetResolution(xdpi, ydpi);
}
if (header.IsBottomUpImage)
{
tempImg.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
finalImg = tempImg;
tempImg = null;
}
}
finally
{
if (tempImg != null)
{
tempImg.Dispose();
} }
} }
return finalImg; return finalImg;
} }
static bool IsDib(IntPtr dibBitmap) internal static Image ReadTiffImage(IntPtr data)
{ {
// a quick check not guaranteed correct, // this is modified from twain cs sample
// compare first byte to size of struct (which is also the first field) // http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect
var test = Marshal.ReadInt32(dibBitmap);
// should be 40
return test == BITMAPINFOHEADER.GetByteSize(); // Find the size of the image so we can turn it into a memory stream...
var headerSize = Marshal.SizeOf(typeof(TIFFHEADER));
var tagSize = Marshal.SizeOf(typeof(TIFFTAG));
var tiffSize = 0;
var header = (TIFFHEADER)Marshal.PtrToStructure(data, typeof(TIFFHEADER));
var tagPtr = data.ToInt64() + headerSize;
for (int i = 0; i < 999; i++)
{
tagPtr += (tagSize * i);
var tag = (TIFFTAG)Marshal.PtrToStructure((IntPtr)tagPtr, typeof(TIFFTAG));
switch (tag.u16Tag)
{
case 273: // StripOffsets...
case 279: // StripByteCounts...
tiffSize += (int)tag.u32Value;
break;
}
}
if (tiffSize > 0)
{
var dataCopy = new byte[tiffSize];
Marshal.Copy(data, dataCopy, 0, tiffSize);
return Image.FromStream(new MemoryStream(dataCopy));
}
return null;
} }
/// <summary> /// <summary>
/// Converts a <see cref="Bitmap"/> to WPF <see cref="BitmapSource"/>. /// Converts an <see cref="Image"/> to WPF <see cref="BitmapSource"/> if the image
/// is a <see cref="Bitmap"/>.
/// </summary> /// </summary>
/// <param name="image">The image to convert.</param> /// <param name="image">The image to convert.</param>
/// <returns></returns> /// <returns></returns>
public static BitmapSource ConvertToWpfBitmap(this Bitmap image) public static BitmapSource ConvertToWpfBitmap(this Image image)
{ {
if (image != null) var bmp = image as Bitmap;
if (bmp != null)
{ {
using (var hbm = new SafeHBitmapHandle(image.GetHbitmap(), true)) using (var hbm = new SafeHBitmapHandle(bmp.GetHbitmap(), true))
{ {
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hbm.DangerousGetHandle(), hbm.DangerousGetHandle(),
@@ -110,10 +151,5 @@ namespace NTwain
return NativeMethods.DeleteObject(handle); return NativeMethods.DeleteObject(handle);
} }
} }
internal static Bitmap ReadTiffImage(IntPtr data)
{
return null;
}
} }
} }

View File

@@ -7,11 +7,34 @@ using System.Text;
namespace NTwain.Interop namespace NTwain.Interop
{ {
// this is a good read
// http://atlc.sourceforge.net/bmp.html
/// <summary>
/// Defines the dimensions and color information for a DIB.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct BITMAPINFO
{
/// <summary>
/// Structure that contains information about the dimensions of color format.
/// </summary>
public BITMAPINFOHEADER bmiHeader;
/// <summary>
/// This contains one of the following:
/// 1. An array of RGBQUAD. The elements of the array that make up the color table.
/// 2. An array of 16-bit unsigned integers that specifies indexes into the currently realized logical palette. This use of bmiColors is allowed for functions that use DIBs.
/// The number of entries in the array depends on the values of the biBitCount and biClrUsed members of the BITMAPINFOHEADER structure.
/// </summary>
public IntPtr bmiColors;
};
/// <summary> /// <summary>
/// Structure that contains information about the dimensions and color format of a DIB. /// Structure that contains information about the dimensions and color format of a DIB.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER struct BITMAPINFOHEADER
{ {
#region fields #region fields
/// <summary> /// <summary>

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing;
namespace NTwain.Interop
{
// this is a good read
// http://atlc.sourceforge.net/bmp.html
/// <summary>
/// Defines the dimensions and color information for a DIB.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
/// <summary>
/// Structure that contains information about the dimensions of color format.
/// </summary>
public BITMAPINFOHEADER bmiHeader;
/// <summary>
/// This contains one of the following:
/// 1. An array of RGBQUAD. The elements of the array that make up the color table.
/// 2. An array of 16-bit unsigned integers that specifies indexes into the currently realized logical palette. This use of bmiColors is allowed for functions that use DIBs.
/// The number of entries in the array depends on the values of the biBitCount and biClrUsed members of the BITMAPINFOHEADER structure.
/// </summary>
public IntPtr bmiColors;
};
}

35
NTwain/Interop/TIFF.cs Normal file
View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace NTwain.Interop
{
// this is from twain cs sample
// http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect
/// <summary>
/// The TIFF file header.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TIFFHEADER
{
public ushort u8ByteOrder;
public ushort u16Version;
public uint u32OffsetFirstIFD;
public ushort u16u16IFD;
}
/// <summary>
/// An individual TIFF Tag.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TIFFTAG
{
public ushort u16Tag;
public ushort u16Type;
public uint u32Count;
public uint u32Value;
}
}

View File

@@ -70,13 +70,13 @@
<Compile Include="ImageTools.cs" /> <Compile Include="ImageTools.cs" />
<Compile Include="Internals\InternalMessageLoopHook.cs" /> <Compile Include="Internals\InternalMessageLoopHook.cs" />
<Compile Include="Internals\ITwainSessionInternal.cs" /> <Compile Include="Internals\ITwainSessionInternal.cs" />
<Compile Include="Interop\BITMAPINFO.cs" /> <Compile Include="Interop\BITMAP.cs" />
<Compile Include="Interop\BITMAPINFOHEADER.cs" />
<Compile Include="Interop\MESSAGE.cs" /> <Compile Include="Interop\MESSAGE.cs" />
<Compile Include="Internals\TransferLogic.cs" /> <Compile Include="Internals\TransferLogic.cs" />
<Compile Include="Internals\WindowsHook.cs" /> <Compile Include="Internals\WindowsHook.cs" />
<Compile Include="Internals\WrappedManualResetEvent.cs" /> <Compile Include="Internals\WrappedManualResetEvent.cs" />
<Compile Include="Interop\NativeMethods.cs" /> <Compile Include="Interop\NativeMethods.cs" />
<Compile Include="Interop\TIFF.cs" />
<Compile Include="IPlatformInfo.cs" /> <Compile Include="IPlatformInfo.cs" />
<Compile Include="ITripletControl.cs" /> <Compile Include="ITripletControl.cs" />
<Compile Include="ITwainSession.cs" /> <Compile Include="ITwainSession.cs" />

View File

@@ -51,8 +51,8 @@ TwainSession class provides many events, but these 2 are the most important
or all subsequent transfers using the event object. or all subsequent transfers using the event object.
* DataTransferred - fired after the transfer has occurred. The data available depends on * DataTransferred - fired after the transfer has occurred. The data available depends on
what you've specified using the TWAIN API before starting the transfer. If using image what you've specified using the TWAIN API before starting the transfer. If using image
native transfer, the event arg provides a quick GetNativeBitmap() method to convert the native transfer, the event arg provides a quick GetNativeImage() method to convert the
data to a System.Drawing.Bitmap. data to a System.Drawing.Image.
NOTE: do not try to close the source/session in the handler of these 2 events or something NOTE: do not try to close the source/session in the handler of these 2 events or something
unpredictable will happen. Either let the scan run its course or cancel the scan using the flag unpredictable will happen. Either let the scan run its course or cancel the scan using the flag
@@ -140,7 +140,7 @@ session.Close();
Caveats Caveats
-------------------------------------- --------------------------------------
At the moment the DataTransferredEventArgs only provides conversion routine to At the moment the DataTransferredEventArgs only provides conversion routine to
a Bitmap image when using native transfer. an image when using native transfer.
If other transfer methods are used you'll have to deal with them yourself. If other transfer methods are used you'll have to deal with them yourself.
If you just call session.Open() without passing a message loop hook argument, an If you just call session.Open() without passing a message loop hook argument, an

View File

@@ -289,7 +289,7 @@ namespace Tester.WPF
BitmapSource img = null; BitmapSource img = null;
if (e.NativeData != IntPtr.Zero) if (e.NativeData != IntPtr.Zero)
{ {
img = e.GetNativeBitmap().ConvertToWpfBitmap(); img = e.GetNativeImage().ConvertToWpfBitmap();
} }
else if (!string.IsNullOrEmpty(e.FileDataPath)) else if (!string.IsNullOrEmpty(e.FileDataPath))
{ {

View File

@@ -87,10 +87,10 @@ namespace Tester.Winform
} }
// handle image data // handle image data
Bitmap img = null; Image img = null;
if (e.NativeData != IntPtr.Zero) if (e.NativeData != IntPtr.Zero)
{ {
img = e.GetNativeBitmap(); img = e.GetNativeImage();
} }
else if (!string.IsNullOrEmpty(e.FileDataPath)) else if (!string.IsNullOrEmpty(e.FileDataPath))
{ {