Update image parse code.

This commit is contained in:
soukoku
2015-02-19 22:30:13 -05:00
parent eef0b9db92
commit 9ba2b0d0e0
10 changed files with 284 additions and 182 deletions

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
@@ -140,26 +141,28 @@ namespace NTwain
/// <summary>
/// Gets the bitmap from the <see cref="NativeData"/> if it's an image.
/// Gets the image stream from the <see cref="NativeData"/> if it's an image.
/// </summary>
/// <returns></returns>
public Image GetNativeImage()
public Stream GetNativeImageStream()
{
Image image = null;
Stream retVal = null;
if (NativeData != IntPtr.Zero)
{
if (ImageTools.IsDib(NativeData))
{
image = ImageTools.ReadBitmapImage(NativeData);
retVal = ImageTools.GetBitmapStream(NativeData);
}
else if (ImageTools.IsTiff(NativeData))
{
image = ImageTools.ReadTiffImage(NativeData);
retVal = ImageTools.GetTiffStream(NativeData);
}
}
return image;
return retVal; ;
}
}
}

View File

@@ -1,154 +0,0 @@
using Microsoft.Win32.SafeHandles;
using NTwain.Interop;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Windows.Media.Imaging;
namespace NTwain
{
public static class ImageTools
{
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 tempImg = null;
try
{
var header = (BITMAPINFOHEADER)Marshal.PtrToStructure(data, typeof(BITMAPINFOHEADER));
if (header.Validate())
{
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.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;
}
internal static Image ReadTiffImage(IntPtr data)
{
// this is modified from twain cs sample
// http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect
// 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 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>
/// Converts an <see cref="Image"/> to WPF <see cref="BitmapSource"/> if the image
/// is a <see cref="Bitmap"/>.
/// </summary>
/// <param name="image">The image to convert.</param>
/// <returns></returns>
public static BitmapSource ConvertToWpfBitmap(this Image image)
{
var bmp = image as Bitmap;
if (bmp != null)
{
using (var hbm = new SafeHBitmapHandle(bmp.GetHbitmap(), true))
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hbm.DangerousGetHandle(),
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
return null;
}
class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.DeleteObject(handle);
}
}
}
}

View File

@@ -0,0 +1,138 @@
using NTwain.Interop;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace NTwain.Internals
{
static class ImageTools
{
// this is modified from twain cs sample
// http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect
public 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();
}
public static bool IsTiff(IntPtr data)
{
var test = Marshal.ReadInt16(data);
// should be II
return test == 0x4949;
}
public static Stream GetBitmapStream(IntPtr data)
{
var infoHeader = (BITMAPINFOHEADER)Marshal.PtrToStructure(data, typeof(BITMAPINFOHEADER));
if (infoHeader.Validate())
{
var fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
var fileHeader = new BITMAPFILEHEADER();
fileHeader.bfType = 0x4D42; // "BM"
fileHeader.bfOffBits = (uint)fileHeaderSize +
infoHeader.biSize +
(infoHeader.biClrUsed * 4);
fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage;
var dataCopy = new byte[fileHeader.bfSize];
// write file header
IntPtr tempPtr = Marshal.AllocHGlobal(fileHeaderSize);
Marshal.StructureToPtr(fileHeader, tempPtr, true);
Marshal.Copy(tempPtr, dataCopy, 0, fileHeaderSize);
Marshal.FreeHGlobal(tempPtr);
// write image
Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize);
return new MemoryStream(dataCopy);
}
return null;
}
public static Stream GetTiffStream(IntPtr data)
{
// 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 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];
// is this optimal?
Marshal.Copy(data, dataCopy, 0, tiffSize);
return new MemoryStream(dataCopy);
}
return null;
}
//internal static Bitmap ReadBitmapImage(IntPtr data)
//{
// Bitmap finalImg = null;
// Bitmap tempImg = null;
// try
// {
// var header = (BITMAPINFOHEADER)Marshal.PtrToStructure(data, typeof(BITMAPINFOHEADER));
// if (header.Validate())
// {
// 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.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;
//}
}
}

View File

@@ -330,4 +330,13 @@ namespace NTwain.Interop
}
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BITMAPFILEHEADER
{
public ushort bfType;
public uint bfSize;
public ushort bfReserved1;
public ushort bfReserved2;
public uint bfOffBits;
}
}

View File

@@ -68,7 +68,7 @@
<Compile Include="DataTransferredEventArgs.cs" />
<Compile Include="IMemoryManager.cs" />
<Compile Include="Internals\ICommittable.cs" />
<Compile Include="ImageTools.cs" />
<Compile Include="Internals\ImageTools.cs" />
<Compile Include="Internals\InternalMessageLoopHook.cs" />
<Compile Include="Internals\ITwainSessionInternal.cs" />
<Compile Include="Internals\LinuxMemoryManager.cs" />
@@ -155,6 +155,7 @@
<Compile Include="Data\TwainValues.cs" />
<Compile Include="SourceEnableMode.cs" />
<Compile Include="Data\ValueExtensions.cs" />
<Compile Include="WpfImageTools.cs" />
</ItemGroup>
<ItemGroup>
<None Include="NTwain.nuspec" />

91
NTwain/WpfImageTools.cs Normal file
View File

@@ -0,0 +1,91 @@
using System.IO;
using System.Windows.Media.Imaging;
namespace NTwain
{
// this is in its own class to not depend on PresentationCore.dll on mono if it's not used.
/// <summary>
/// Contains extension methods for wpf images.
/// </summary>
public static class WpfImageTools
{
// <summary>
/// Loads a <see cref="Stream" /> into WPF <see cref="BitmapSource" />. The image created
/// will be a copy so the stream can be disposed once this call returns.
/// </summary>
/// <param name="stream">The image stream.</param>
/// <returns></returns>
public static BitmapSource ConvertToWpfBitmap(this Stream stream)
{
return ConvertToWpfBitmap(stream, 0, 0);
}
/// <summary>
/// Loads a <see cref="Stream" /> into WPF <see cref="BitmapSource" />. The image created
/// will be a copy so the stream can be disposed once this call returns.
/// </summary>
/// <param name="stream">The image stream.</param>
/// <param name="decodeWidth">Max width of the decoded image. Pass 0 to use default.</param>
/// <param name="decodeHeight">Max height of the decoded image. Pass 0 to use default.</param>
/// <returns></returns>
public static BitmapSource ConvertToWpfBitmap(this Stream stream, int decodeWidth, int decodeHeight)
{
if (stream != null)
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.DecodePixelHeight = decodeHeight;
image.DecodePixelWidth = decodeWidth;
image.StreamSource = stream;
image.EndInit();
if (image.CanFreeze)
{
image.Freeze();
}
return image;
}
return null;
}
///// <summary>
///// Converts an <see cref="Image"/> to WPF <see cref="BitmapSource"/> if the image
///// is a <see cref="Bitmap"/>.
///// </summary>
///// <param name="image">The image to convert.</param>
///// <returns></returns>
//public static BitmapSource ConvertToWpfBitmap(this Image image)
//{
// var bmp = image as Bitmap;
// if (bmp != null)
// {
// using (var hbm = new SafeHBitmapHandle(bmp.GetHbitmap(), true))
// {
// return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
// hbm.DangerousGetHandle(),
// IntPtr.Zero,
// System.Windows.Int32Rect.Empty,
// BitmapSizeOptions.FromEmptyOptions());
// }
// }
// return null;
//}
//class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
//{
// [SecurityCritical]
// public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
// : base(ownsHandle)
// {
// SetHandle(preexistingHandle);
// }
// protected override bool ReleaseHandle()
// {
// return NativeMethods.DeleteObject(handle);
// }
//}
}
}