diff --git a/samples/ScannerTester/ScannerTester.csproj b/samples/ScannerTester/ScannerTester.csproj index d779151..41d3ac3 100644 --- a/samples/ScannerTester/ScannerTester.csproj +++ b/samples/ScannerTester/ScannerTester.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows + net10.0-windows7.0 enable true enable diff --git a/samples/WinConsole32/Program.cs b/samples/WinConsole32/Program.cs index 24a8831..4f76e2c 100644 --- a/samples/WinConsole32/Program.cs +++ b/samples/WinConsole32/Program.cs @@ -127,13 +127,13 @@ namespace WinConsole32 } static int xferCount = 0; - static Stopwatch watch; + static Stopwatch watch = new Stopwatch(); private static void Session_Transferred(TwainAppSession twain, TransferredEventArgs e) { if (e.Data != null) { var saveFile = $"twain_{DateTime.Now:yyyyMMdd_HHmmss}_{xferCount}"; - Console.WriteLine("SUCCESS! Got twain memory data #{0} on thread {1}, saving to {saveFile}.", ++xferCount, Environment.CurrentManagedThreadId, saveFile); + Console.WriteLine($"SUCCESS! Got twain memory data #{++xferCount} on thread {Environment.CurrentManagedThreadId}, saving to {saveFile}."); using (var img = new ImageMagick.MagickImage(e.Data.AsStream())) { @@ -157,11 +157,11 @@ namespace WinConsole32 else if (e.FileInfo != null) { var fi = e.FileInfo.Value; - Console.WriteLine("SUCCESS! Got twain file data #{0} on thread {1} as {saveFile}.", ++xferCount, Environment.CurrentManagedThreadId, fi.FileName); + Console.WriteLine($"SUCCESS! Got twain file data #{++xferCount} on thread {Environment.CurrentManagedThreadId} as {fi.FileName}."); } else { - Console.WriteLine("BUMMER! No twain data #{0} on thread {1}.", ++xferCount, Environment.CurrentManagedThreadId); + Console.WriteLine($"BUMMER! No twain data #{++xferCount} on thread {Environment.CurrentManagedThreadId}."); } e.Dispose(); } @@ -199,10 +199,10 @@ namespace WinConsole32 twain.Caps.ICAP_PIXELTYPE.Set(TWPT.RGB); twain.Caps.ICAP_XRESOLUTION.Set(300f); twain.Caps.ICAP_YRESOLUTION.Set(300f); - twain.Caps.CAP_XFERCOUNT.Set(100); + twain.Caps.CAP_XFERCOUNT.Set(4); xferCount = 0; - watch = Stopwatch.StartNew(); + watch.Restart(); var rc = twain.EnableSource(true, false); } } diff --git a/samples/WinConsole32/WinConsole32.csproj b/samples/WinConsole32/WinConsole32.csproj index c3e86ff..1a9113f 100644 --- a/samples/WinConsole32/WinConsole32.csproj +++ b/samples/WinConsole32/WinConsole32.csproj @@ -11,7 +11,7 @@ - + diff --git a/samples/WinForm32/Form1.cs b/samples/WinForm32/Form1.cs index f383f2e..d2b90d1 100644 --- a/samples/WinForm32/Form1.cs +++ b/samples/WinForm32/Form1.cs @@ -202,7 +202,7 @@ namespace WinFormSample // color saveFile += ".jpg"; format = ImageMagick.MagickFormat.Jpeg; - img.Quality = _jpegQuality; + img.Quality = (uint)_jpegQuality; } if (_saveDisk) img.Write(saveFile); else img.Write(new NoOpStream(), format); diff --git a/samples/WinForm32/WinForm32.csproj b/samples/WinForm32/WinForm32.csproj index 8f2b83a..990196f 100644 --- a/samples/WinForm32/WinForm32.csproj +++ b/samples/WinForm32/WinForm32.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows + net10.0-windows7.0 enable true x86 @@ -10,7 +10,7 @@ - + diff --git a/samples/WinForm64/WinForm64.csproj b/samples/WinForm64/WinForm64.csproj index 0733242..a473097 100644 --- a/samples/WinForm64/WinForm64.csproj +++ b/samples/WinForm64/WinForm64.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows + net10.0-windows7.0 enable true x64 @@ -14,7 +14,7 @@ - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 6b68dc7..4e02bfa 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 4.0.0.0 - alpha.13 + alpha.14 4.0.0.0 diff --git a/src/NTwain/NTwain.csproj b/src/NTwain/NTwain.csproj index 01403ab..1c59160 100644 --- a/src/NTwain/NTwain.csproj +++ b/src/NTwain/NTwain.csproj @@ -1,52 +1,59 @@  - - NTwain - Library containing the TWAIN API for dotnet. - net8.0;net8.0-windows;net9.0;net9.0-windows;net462; - enable - true - + + NTwain + Library containing the TWAIN API for dotnet. + net8.0;net8.0-windows;net9.0;net9.0-windows;net10.0;net10.0-windows;net472; + enable + true + - - true - true - + + true + true + - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - True - True - DSMGenerator.tt - - - TextTemplatingFileGenerator - DSMGenerator.dummy - - + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - + + + True + True + DSMGenerator.tt + + + TextTemplatingFileGenerator + DSMGenerator.dummy + + + + + + diff --git a/src/NTwain/Native/BITMAP.cs b/src/NTwain/Native/BITMAP.cs index 3ab1d2a..d83e016 100644 --- a/src/NTwain/Native/BITMAP.cs +++ b/src/NTwain/Native/BITMAP.cs @@ -2,339 +2,253 @@ using System.Runtime.InteropServices; using System.Text; -namespace NTwain.Native +namespace Windows.Win32.Graphics.Gdi { - // this is a good read - // http://atlc.sourceforge.net/bmp.html + // this is a good read + // http://atlc.sourceforge.net/bmp.html - /// - /// Defines the dimensions and color information for a DIB. - /// - [StructLayout(LayoutKind.Sequential)] - struct BITMAPINFO - { - /// - /// Structure that contains information about the dimensions of color format. - /// - public BITMAPINFOHEADER bmiHeader; - /// - /// 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. - /// - public IntPtr bmiColors; - }; - - /// - /// Structure that contains information about the dimensions and color format of a DIB. - /// - [StructLayout(LayoutKind.Sequential)] - struct BITMAPINFOHEADER - { - #region fields - /// - /// The number of bytes required by the structure. - /// - public uint biSize; - /// - /// The width of the bitmap, in pixels. - /// If Compression is JPEG or PNG, the Width member specifies the width of the decompressed - /// JPEG or PNG image file, respectively. - /// - public int biWidth; - /// - /// The height of the bitmap, in pixels. If Height is positive, - /// the bitmap is a bottom-up DIB and its origin is the lower-left corner. - /// If Height is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. - /// If Height is negative, indicating a top-down DIB, Compression must be either RGB or BITFIELDS. Top-down DIBs cannot be compressed. - /// If Compression is JPEG or PNG, the Height member specifies the height of the decompressed JPEG or PNG image file, respectively. - /// - public int biHeight; - /// - /// The number of planes for the target device. This value must be set to 1. - /// - public ushort biPlanes; - /// - /// The number of bits-per-pixel. The BitCount member - /// determines the number of bits that define each pixel and the maximum number of colors in the bitmap. - /// - public ushort biBitCount; - /// - /// The type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed). - /// - public CompressionType biCompression; - /// - /// The size, in bytes, of the image. This may be set to zero for RGB bitmaps. - /// If Compression is JPEG or PNG, SizeImage indicates the size of the JPEG or PNG image buffer, respectively. - /// - public uint biSizeImage; - /// - /// The horizontal resolution, in pixels-per-meter, of the target device for the bitmap. - /// An application can use this value to select a bitmap from a resource group that - /// best matches the characteristics of the current device. - /// - public int biXPelsPerMeter; - /// - /// The vertical resolution, in pixels-per-meter, of the target device for the bitmap. - /// - public int biYPelsPerMeter; - /// - /// The number of color indexes in the color table that are actually used by the bitmap. - /// If this value is zero, the bitmap uses the maximum number of colors corresponding to - /// the value of the BitCount member for the compression mode specified by Compression. - /// - public uint biClrUsed; - /// - /// The number of color indexes that are required for displaying the bitmap. - /// If this value is zero, all colors are required. - /// - public uint biClrImportant; - #endregion - - #region utilities - - const double METER_INCH_RATIO = 39.3700787; - - /// - /// Gets the horizontal dpi of the bitmap. - /// - /// - public float GetXDpi() + partial struct BITMAPINFOHEADER { - return (float)Math.Round(biXPelsPerMeter / METER_INCH_RATIO, 0); - } - /// - /// Gets the vertical dpi of the bitmap. - /// - /// - public float GetYDpi() - { - return (float)Math.Round(biYPelsPerMeter / METER_INCH_RATIO, 0); - } - /// - /// Gets the size of the structure. - /// - /// - public static uint GetByteSize() - { - return (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); - } - /// - /// Checks to see if this structure contain valid data. - /// It also fills in any missing pieces if possible. - /// - /// - public bool Validate() - { - if (biHeight != 0 && biWidth != 0 && biBitCount != 0) - { - if (biSize == 0) + const double METER_INCH_RATIO = 39.3700787; + + /// + /// Gets the horizontal dpi of the bitmap. + /// + /// + public float GetXDpi() { - biSize = GetByteSize(); + return (float)Math.Round(biXPelsPerMeter / METER_INCH_RATIO, 0); } - if (biClrUsed == 0) + /// + /// Gets the vertical dpi of the bitmap. + /// + /// + public float GetYDpi() { - switch (biBitCount) - { - case 1: - biClrUsed = 2; - break; - case 4: - biClrUsed = 16; - break; - case 8: - biClrUsed = 256; - break; - } + return (float)Math.Round(biYPelsPerMeter / METER_INCH_RATIO, 0); } - if (biSizeImage == 0) + /// + /// Gets the size of the structure. + /// + /// + public static uint GetByteSize() { - biSizeImage = (uint)(((( - biWidth * biBitCount) + 31) & ~31) >> 3) * (uint)Math.Abs(biHeight); + return (uint)Marshal.SizeOf(); + } + /// + /// Checks to see if this structure contain valid data. + /// It also fills in any missing pieces if possible. + /// + /// + public bool Validate() + { + if (biHeight != 0 && biWidth != 0 && biBitCount != 0) + { + if (biSize == 0) + { + biSize = GetByteSize(); + } + if (biClrUsed == 0) + { + switch (biBitCount) + { + case 1: + biClrUsed = 2; + break; + case 4: + biClrUsed = 16; + break; + case 8: + biClrUsed = 256; + break; + } + } + if (biSizeImage == 0) + { + biSizeImage = (uint)(((( + biWidth * biBitCount) + 31) & ~31) >> 3) * (uint)Math.Abs(biHeight); + } + + return true; + } + return false; } - return true; - } - return false; + /// + /// Gets the pointer to scan0 given the header pointer. + /// + /// The header PTR. + /// + public IntPtr GetScan0(IntPtr headerPtr) + { + int p = (int)biClrUsed; + if ((p == 0) && (biBitCount <= 8)) + { + p = 1 << biBitCount; + } + p = (p * 4) + (int)biSize + headerPtr.ToInt32(); + return new IntPtr(p); + } + + /// + /// Gets whether the bitmap is bottom-up or top-down format. + /// + /// + /// true if this instance is bottom up image; otherwise, false. + /// + /// + public bool IsBottomUpImage + { + get + { + return biHeight > 0; + } + } + + + ///// + ///// Gets the System.Drawing pixel format of current structure. + ///// + ///// + //public PixelFormat GetDrawingPixelFormat() + //{ + // switch (biBitCount) + // { + // case 1: + // return PixelFormat.Format1bppIndexed; + // case 4: + // return PixelFormat.Format4bppIndexed; + // case 8: + // return PixelFormat.Format8bppIndexed; + // case 16: + // return PixelFormat.Format16bppRgb565; + // case 24: + // return PixelFormat.Format24bppRgb; + // case 32: + // return PixelFormat.Format32bppRgb; + // case 48: + // return PixelFormat.Format48bppRgb; + // } + // return PixelFormat.DontCare; + //} + + ///// + ///// Gets the color palette that's contained in the header. + ///// Note not all images will have palette, so check if the return value + ///// is null before using it. + ///// + ///// + //public ColorPalette? GetDrawingPalette(IntPtr headerPtr) + //{ + // //if (format == PixelFormat.Format8bppIndexed) + // //{ + // // // update color palette to grayscale version + // // ColorPalette grayPallet = bitmap.Palette; + // // for (int i = 0; i < grayPallet.Entries.Length; i++) + // // { + // // grayPallet.Entries[i] = Color.FromArgb(i, i, i); + // // } + // // bitmap.Palette = grayPallet; // this is what makes the gray pallet take effect + // //} + + // if (biClrUsed > 0) + // { + // byte[] data = new byte[biClrUsed * 4]; + // Marshal.Copy(new IntPtr(headerPtr.ToInt32() + biSize), data, 0, data.Length); + // var dummy = new System.Drawing.Bitmap(1, 1, GetDrawingPixelFormat()); + // ColorPalette pal = dummy.Palette; + // dummy.Dispose(); + // int index = 0; + // int setCount = data.Length / 4; + // for (int i = 0; i < setCount; i++) + // { + // index = i * 4; + // pal.Entries[i] = Color.FromArgb(data[index + 2], data[index + 1], data[index]); + // } + // return pal; + // } + // return null; + //} + + /// + /// Gets the stride size of this bitmap. + /// + /// + public int GetStride() + { + int bitsPerRow = (biBitCount * biWidth); + int strideTest = bitsPerRow / 8 + (bitsPerRow % 8 != 0 ? 1 : 0); + int overage = strideTest % 4; + if (overage > 0) + { + strideTest += (4 - overage); + } + return strideTest; + } + + /// + public override string ToString() + { + return new StringBuilder().Append("BitmapInfoHeader:") + .Append("\r\n\tSize = " + biSize) + .Append("\r\n\tWidth = " + biWidth) + .Append("\r\n\tHeight = " + biHeight) + .Append("\r\n\tPlanes = " + biPlanes) + .Append("\r\n\tBitCount = " + biBitCount) + .Append("\r\n\tCompression = " + Compression) + .Append("\r\n\tSizeImage = " + biSizeImage) + .Append("\r\n\tXPixelsPerMeter = " + biXPelsPerMeter) + .Append("\r\n\tYPixelsPerMeter = " + biYPelsPerMeter) + .Append("\r\n\tColorUsed = " + biClrUsed) + .Append("\r\n\tColorImportant = " + biClrImportant).ToString(); + } + + /// + /// Gets the bitmap compression type. + /// + public CompressionType Compression { get { return (CompressionType)biCompression; } } + + /// + /// Indicates the bitmap compression of . + /// + public enum CompressionType : uint + { + /// + /// An uncompressed format. + /// + BI_RGB = 0, + /// + /// A run-length encoded (RLE) format for bitmaps with 8 bpp. The compression format is a 2-byte format consisting of a count byte followed by a byte containing a color index. For more information, see Bitmap Compression. + /// + BI_RLE8 = 1, + /// + /// An RLE, format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting of a count byte followed by two word-length color indexes. For more information, see Bitmap Compression. + /// + BI_RLE4 = 2, + /// + /// Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components of each pixel. + /// This is valid when used with 16- and 32-bpp bitmaps. + /// + BI_BITFIELDS = 3, + /// + /// Indicates that the image is a JPEG image. + /// + BI_JPEG = 4, + /// + /// Indicates that the image is a PNG image. + /// + BI_PNG = 5 + } } - /// - /// Gets the pointer to scan0 given the header pointer. - /// - /// The header PTR. - /// - public IntPtr GetScan0(IntPtr headerPtr) - { - int p = (int)biClrUsed; - if ((p == 0) && (biBitCount <= 8)) - { - p = 1 << biBitCount; - } - p = (p * 4) + (int)biSize + headerPtr.ToInt32(); - return new IntPtr(p); - } - - /// - /// Gets whether the bitmap is bottom-up or top-down format. - /// - /// - /// true if this instance is bottom up image; otherwise, false. - /// - /// - public bool IsBottomUpImage - { - get - { - return biHeight > 0; - } - } - - - ///// - ///// Gets the System.Drawing pixel format of current structure. - ///// - ///// - //public PixelFormat GetDrawingPixelFormat() + //[StructLayout(LayoutKind.Sequential, Pack = 1)] + //struct BITMAPFILEHEADER //{ - // switch (biBitCount) - // { - // case 1: - // return PixelFormat.Format1bppIndexed; - // case 4: - // return PixelFormat.Format4bppIndexed; - // case 8: - // return PixelFormat.Format8bppIndexed; - // case 16: - // return PixelFormat.Format16bppRgb565; - // case 24: - // return PixelFormat.Format24bppRgb; - // case 32: - // return PixelFormat.Format32bppRgb; - // case 48: - // return PixelFormat.Format48bppRgb; - // } - // return PixelFormat.DontCare; + // public ushort bfType; + // public uint bfSize; + // public ushort bfReserved1; + // public ushort bfReserved2; + // public uint bfOffBits; //} - - ///// - ///// Gets the color palette that's contained in the header. - ///// Note not all images will have palette, so check if the return value - ///// is null before using it. - ///// - ///// - //public ColorPalette? GetDrawingPalette(IntPtr headerPtr) - //{ - // //if (format == PixelFormat.Format8bppIndexed) - // //{ - // // // update color palette to grayscale version - // // ColorPalette grayPallet = bitmap.Palette; - // // for (int i = 0; i < grayPallet.Entries.Length; i++) - // // { - // // grayPallet.Entries[i] = Color.FromArgb(i, i, i); - // // } - // // bitmap.Palette = grayPallet; // this is what makes the gray pallet take effect - // //} - - // if (biClrUsed > 0) - // { - // byte[] data = new byte[biClrUsed * 4]; - // Marshal.Copy(new IntPtr(headerPtr.ToInt32() + biSize), data, 0, data.Length); - // var dummy = new System.Drawing.Bitmap(1, 1, GetDrawingPixelFormat()); - // ColorPalette pal = dummy.Palette; - // dummy.Dispose(); - // int index = 0; - // int setCount = data.Length / 4; - // for (int i = 0; i < setCount; i++) - // { - // index = i * 4; - // pal.Entries[i] = Color.FromArgb(data[index + 2], data[index + 1], data[index]); - // } - // return pal; - // } - // return null; - //} - - /// - /// Gets the stride size of this bitmap. - /// - /// - public int GetStride() - { - int bitsPerRow = (biBitCount * biWidth); - int strideTest = bitsPerRow / 8 + (bitsPerRow % 8 != 0 ? 1 : 0); - int overage = strideTest % 4; - if (overage > 0) - { - strideTest += (4 - overage); - } - return strideTest; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return new StringBuilder().Append("BitmapInfoHeader:") - .Append("\r\n\tSize = " + biSize) - .Append("\r\n\tWidth = " + biWidth) - .Append("\r\n\tHeight = " + biHeight) - .Append("\r\n\tPlanes = " + biPlanes) - .Append("\r\n\tBitCount = " + biBitCount) - .Append("\r\n\tCompression = " + biCompression) - .Append("\r\n\tSizeImage = " + biSizeImage) - .Append("\r\n\tXPixelsPerMeter = " + biXPelsPerMeter) - .Append("\r\n\tYPixelsPerMeter = " + biYPelsPerMeter) - .Append("\r\n\tColorUsed = " + biClrUsed) - .Append("\r\n\tColorImportant = " + biClrImportant).ToString(); - } - #endregion - - /// - /// Indicates the bitmap compression of . - /// - public enum CompressionType : uint - { - /// - /// An uncompressed format. - /// - BI_RGB = 0, - /// - /// A run-length encoded (RLE) format for bitmaps with 8 bpp. The compression format is a 2-byte format consisting of a count byte followed by a byte containing a color index. For more information, see Bitmap Compression. - /// - BI_RLE8 = 1, - /// - /// An RLE, format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting of a count byte followed by two word-length color indexes. For more information, see Bitmap Compression. - /// - BI_RLE4 = 2, - /// - /// Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components of each pixel. - /// This is valid when used with 16- and 32-bpp bitmaps. - /// - BI_BITFIELDS = 3, - /// - /// Indicates that the image is a JPEG image. - /// - BI_JPEG = 4, - /// - /// Indicates that the image is a PNG image. - /// - BI_PNG = 5 - } - }; - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BITMAPFILEHEADER - { - public ushort bfType; - public uint bfSize; - public ushort bfReserved1; - public ushort bfReserved2; - public uint bfOffBits; - } } diff --git a/src/NTwain/Native/ImageTools.cs b/src/NTwain/Native/ImageTools.cs index d987285..86ceec9 100644 --- a/src/NTwain/Native/ImageTools.cs +++ b/src/NTwain/Native/ImageTools.cs @@ -1,145 +1,146 @@ using NTwain.Data; using System; using System.Runtime.InteropServices; +using Windows.Win32.Graphics.Gdi; namespace NTwain.Native { - static class ImageTools - { - // this is modified from twain cs sample - // http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect - - public static bool IsDib(IntPtr data) + static class ImageTools { - // 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; - } + // this is modified from twain cs sample + // http://sourceforge.net/projects/twainforcsharp/?source=typ_redirect - public static unsafe BufferedData? GetBitmapData(IntPtr data) - { - var infoHeader = Marshal.PtrToStructure(data); - if (infoHeader.Validate()) - { - var fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER)); - - var fileHeader = new BITMAPFILEHEADER + public static bool IsDib(IntPtr data) { - bfType = 0x4D42, // "BM" - bfOffBits = (uint)fileHeaderSize + - infoHeader.biSize + - (infoHeader.biClrUsed * 4) - }; - fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage; - - var dataCopy = BufferedData.MemPool.Rent((int)fileHeader.bfSize); // new byte[fileHeader.bfSize]; - - // TODO: run benchmark on which one is faster - - // write file header - //IntPtr tempPtr = Marshal.AllocHGlobal(fileHeaderSize); - //Marshal.StructureToPtr(fileHeader, tempPtr, true); - //Marshal.Copy(tempPtr, dataCopy, 0, fileHeaderSize); - //Marshal.FreeHGlobal(tempPtr); - - // would this be faster? - fixed (byte* p = dataCopy) + // 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) { - Marshal.StructureToPtr(fileHeader, (IntPtr)p, false); + var test = Marshal.ReadInt16(data); + // should be II + return test == 0x4949; } - // write image - Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize); - return new BufferedData(dataCopy, (int)fileHeader.bfSize, true); - } - return default; - } - - public static BufferedData? GetTiffData(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 = Marshal.PtrToStructure((IntPtr)tagPtr); - - switch (tag.u16Tag) + public static unsafe BufferedData? GetBitmapData(IntPtr data) { - case 273: // StripOffsets... - case 279: // StripByteCounts... - tiffSize += (int)tag.u32Value; - break; + var infoHeader = Marshal.PtrToStructure(data); + if (infoHeader.Validate()) + { + var fileHeaderSize = Marshal.SizeOf(); + + var fileHeader = new BITMAPFILEHEADER + { + bfType = 0x4D42, // "BM" + bfOffBits = (uint)fileHeaderSize + + infoHeader.biSize + + (infoHeader.biClrUsed * 4) + }; + fileHeader.bfSize = fileHeader.bfOffBits + infoHeader.biSizeImage; + + var dataCopy = BufferedData.MemPool.Rent((int)fileHeader.bfSize); // new byte[fileHeader.bfSize]; + + // TODO: run benchmark on which one is faster + + // write file header + //IntPtr tempPtr = Marshal.AllocHGlobal(fileHeaderSize); + //Marshal.StructureToPtr(fileHeader, tempPtr, true); + //Marshal.Copy(tempPtr, dataCopy, 0, fileHeaderSize); + //Marshal.FreeHGlobal(tempPtr); + + // would this be faster? + fixed (byte* p = dataCopy) + { + Marshal.StructureToPtr(fileHeader, (IntPtr)p, false); + } + + // write image + Marshal.Copy(data, dataCopy, fileHeaderSize, (int)fileHeader.bfSize - fileHeaderSize); + return new BufferedData(dataCopy, (int)fileHeader.bfSize, true); + } + return default; } - } - if (tiffSize > 0) - { - var dataCopy = BufferedData.MemPool.Rent(tiffSize);// new byte[tiffSize]; - Marshal.Copy(data, dataCopy, 0, tiffSize); - return new BufferedData(dataCopy, tiffSize, true); - } - return default; + public static BufferedData? GetTiffData(IntPtr data) + { + // Find the size of the image so we can turn it into a memory stream... + var headerSize = Marshal.SizeOf(); + var tagSize = Marshal.SizeOf(); + var tiffSize = 0; + var tagPtr = data.ToInt64() + headerSize; + for (int i = 0; i < 999; i++) + { + tagPtr += (tagSize * i); + var tag = Marshal.PtrToStructure((IntPtr)tagPtr); + + switch (tag.u16Tag) + { + case 273: // StripOffsets... + case 279: // StripByteCounts... + tiffSize += (int)tag.u32Value; + break; + } + } + + if (tiffSize > 0) + { + var dataCopy = BufferedData.MemPool.Rent(tiffSize);// new byte[tiffSize]; + Marshal.Copy(data, dataCopy, 0, tiffSize); + return new BufferedData(dataCopy, tiffSize, true); + } + return default; + } + + //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 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; - //} - } } diff --git a/src/NTwain/Native/WIN_MESSAGE.cs b/src/NTwain/Native/WIN_MESSAGE.cs index 132eb3a..b5a4899 100644 --- a/src/NTwain/Native/WIN_MESSAGE.cs +++ b/src/NTwain/Native/WIN_MESSAGE.cs @@ -1,21 +1,21 @@ -using System; -using System.Runtime.InteropServices; +//using System; +//using System.Runtime.InteropServices; -namespace NTwain.Native -{ - /// - /// The MSG structure in Windows for TWAIN use. - /// - [StructLayout(LayoutKind.Sequential)] - struct WIN_MESSAGE - { - public IntPtr hwnd; - public uint message; - public IntPtr wParam; - public IntPtr lParam; - uint _time; - int _x; - int _y; - uint lprivate; - } -} +//namespace NTwain.Native +//{ +// /// +// /// The MSG structure in Windows for TWAIN use. +// /// +// [StructLayout(LayoutKind.Sequential)] +// struct WIN_MESSAGE +// { +// public IntPtr hwnd; +// public uint message; +// public IntPtr wParam; +// public IntPtr lParam; +// uint _time; +// int _x; +// int _y; +// uint lprivate; +// } +//} diff --git a/src/NTwain/Native/WinNativeMethods.cs b/src/NTwain/Native/WinNativeMethods.cs index 8242541..18e80cc 100644 --- a/src/NTwain/Native/WinNativeMethods.cs +++ b/src/NTwain/Native/WinNativeMethods.cs @@ -1,97 +1,97 @@ -using System; -using System.Runtime.InteropServices; +//using System; +//using System.Runtime.InteropServices; -namespace NTwain.Native -{ - /// - /// Native methods for windows. - /// - static partial class WinNativeMethods - { -#if NET7_0_OR_GREATER - [LibraryImport("kernel32", SetLastError = true)] - public static partial IntPtr GlobalAlloc(AllocFlag uFlags, UIntPtr dwBytes); +//namespace NTwain.Native +//{ +// /// +// /// Native methods for windows. +// /// +// static partial class WinNativeMethods +// { +//#if NET7_0_OR_GREATER +// [LibraryImport("kernel32", SetLastError = true)] +// public static partial IntPtr GlobalAlloc(AllocFlag uFlags, UIntPtr dwBytes); - [LibraryImport("kernel32", SetLastError = true)] - public static partial IntPtr GlobalFree(IntPtr hMem); +// [LibraryImport("kernel32", SetLastError = true)] +// public static partial IntPtr GlobalFree(IntPtr hMem); - [LibraryImport("kernel32", SetLastError = true)] - public static partial IntPtr GlobalLock(IntPtr handle); +// [LibraryImport("kernel32", SetLastError = true)] +// public static partial IntPtr GlobalLock(IntPtr handle); - [LibraryImport("kernel32", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool GlobalUnlock(IntPtr handle); -#else - [DllImport("kernel32", SetLastError = true)] - public static extern IntPtr GlobalAlloc(AllocFlag uFlags, UIntPtr dwBytes); +// [LibraryImport("kernel32", SetLastError = true)] +// [return: MarshalAs(UnmanagedType.Bool)] +// public static partial bool GlobalUnlock(IntPtr handle); +//#else +// [DllImport("kernel32", SetLastError = true)] +// public static extern IntPtr GlobalAlloc(AllocFlag uFlags, UIntPtr dwBytes); - [DllImport("kernel32", SetLastError = true)] - public static extern IntPtr GlobalFree(IntPtr hMem); +// [DllImport("kernel32", SetLastError = true)] +// public static extern IntPtr GlobalFree(IntPtr hMem); - [DllImport("kernel32", SetLastError = true)] - public static extern IntPtr GlobalLock(IntPtr handle); +// [DllImport("kernel32", SetLastError = true)] +// public static extern IntPtr GlobalLock(IntPtr handle); - [DllImport("kernel32", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GlobalUnlock(IntPtr handle); -#endif - - [Flags] - public enum AllocFlag : uint - { - /// - /// Allocates fixed memory. The return value is a pointer. - /// - GMEM_FIXED = 0, - /// - /// Allocates movable memory. Memory blocks are never moved in physical memory, but they can be moved within the default heap. - /// The return value is a handle to the memory object. To translate the handle into a pointer, use the GlobalLock function. - /// - GMEM_MOVEABLE = 2, - /// - /// Initializes memory contents to zero. - /// - GMEM_ZEROINIT = 0x40, - GPTR = GMEM_FIXED | GMEM_ZEROINIT, - GHND = GMEM_MOVEABLE | GMEM_ZEROINIT - } +// [DllImport("kernel32", SetLastError = true)] +// [return: MarshalAs(UnmanagedType.Bool)] +// public static extern bool GlobalUnlock(IntPtr handle); +//#endif // [Flags] -// public enum PEEK_MESSAGE_REMOVE_TYPE : uint +// public enum AllocFlag : uint // { -// PM_NOREMOVE = 0x00000000, -// PM_REMOVE = 0x00000001, -// PM_NOYIELD = 0x00000002, -// PM_QS_INPUT = 0x04070000, -// PM_QS_POSTMESSAGE = 0x00980000, -// PM_QS_PAINT = 0x00200000, -// PM_QS_SENDMESSAGE = 0x00400000, +// /// +// /// Allocates fixed memory. The return value is a pointer. +// /// +// GMEM_FIXED = 0, +// /// +// /// Allocates movable memory. Memory blocks are never moved in physical memory, but they can be moved within the default heap. +// /// The return value is a handle to the memory object. To translate the handle into a pointer, use the GlobalLock function. +// /// +// GMEM_MOVEABLE = 2, +// /// +// /// Initializes memory contents to zero. +// /// +// GMEM_ZEROINIT = 0x40, +// GPTR = GMEM_FIXED | GMEM_ZEROINIT, +// GHND = GMEM_MOVEABLE | GMEM_ZEROINIT // } -//#if NET7_0_OR_GREATER -// [LibraryImport("USER32.dll")] -// public static partial int PeekMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PEEK_MESSAGE_REMOVE_TYPE wRemoveMsg); +//// [Flags] +//// public enum PEEK_MESSAGE_REMOVE_TYPE : uint +//// { +//// PM_NOREMOVE = 0x00000000, +//// PM_REMOVE = 0x00000001, +//// PM_NOYIELD = 0x00000002, +//// PM_QS_INPUT = 0x04070000, +//// PM_QS_POSTMESSAGE = 0x00980000, +//// PM_QS_PAINT = 0x00200000, +//// PM_QS_SENDMESSAGE = 0x00400000, +//// } -// [LibraryImport("USER32.dll", SetLastError = true)] -// public static partial int GetMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); +////#if NET7_0_OR_GREATER +//// [LibraryImport("USER32.dll")] +//// public static partial int PeekMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PEEK_MESSAGE_REMOVE_TYPE wRemoveMsg); -// [LibraryImport("USER32.dll")] -// public static partial int TranslateMessage(ref WIN_MESSAGE lpMsg); +//// [LibraryImport("USER32.dll", SetLastError = true)] +//// public static partial int GetMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); -// [LibraryImport("USER32.dll")] -// public static partial nint DispatchMessageW(ref WIN_MESSAGE lpMsg); -//#else -// [DllImport("USER32.dll")] -// public static extern int PeekMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PEEK_MESSAGE_REMOVE_TYPE wRemoveMsg); +//// [LibraryImport("USER32.dll")] +//// public static partial int TranslateMessage(ref WIN_MESSAGE lpMsg); -// [DllImport("USER32.dll", SetLastError = true)] -// public static extern int GetMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); +//// [LibraryImport("USER32.dll")] +//// public static partial nint DispatchMessageW(ref WIN_MESSAGE lpMsg); +////#else +//// [DllImport("USER32.dll")] +//// public static extern int PeekMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PEEK_MESSAGE_REMOVE_TYPE wRemoveMsg); -// [DllImport("USER32.dll")] -// public static extern int TranslateMessage(ref WIN_MESSAGE lpMsg); +//// [DllImport("USER32.dll", SetLastError = true)] +//// public static extern int GetMessageW(ref WIN_MESSAGE lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); -// [DllImport("USER32.dll")] -// public static extern nint DispatchMessageW(ref WIN_MESSAGE lpMsg); -//#endif - } -} +//// [DllImport("USER32.dll")] +//// public static extern int TranslateMessage(ref WIN_MESSAGE lpMsg); + +//// [DllImport("USER32.dll")] +//// public static extern nint DispatchMessageW(ref WIN_MESSAGE lpMsg); +////#endif +// } +//} diff --git a/src/NTwain/NativeMethods.json b/src/NTwain/NativeMethods.json new file mode 100644 index 0000000..dacdfc7 --- /dev/null +++ b/src/NTwain/NativeMethods.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false +} \ No newline at end of file diff --git a/src/NTwain/NativeMethods.txt b/src/NTwain/NativeMethods.txt new file mode 100644 index 0000000..0638a0d --- /dev/null +++ b/src/NTwain/NativeMethods.txt @@ -0,0 +1,8 @@ +MSG +GlobalAlloc +GlobalFree +GlobalLock +GlobalUnlock +BITMAPINFOHEADER +BITMAPINFO +BITMAPFILEHEADER \ No newline at end of file diff --git a/src/NTwain/TwainAppSession.Memory.cs b/src/NTwain/TwainAppSession.Memory.cs index c271d8e..768cd63 100644 --- a/src/NTwain/TwainAppSession.Memory.cs +++ b/src/NTwain/TwainAppSession.Memory.cs @@ -1,114 +1,131 @@ using NTwain.Data; -using NTwain.Native; using System; using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Memory; namespace NTwain { - // this file contains memory methods + // this file contains memory methods - partial class TwainAppSession : IMemoryManager - { - TW_ENTRYPOINT_DELEGATES _entryPoint; - - public IntPtr Alloc(uint size) + partial class TwainAppSession : IMemoryManager { - if (_entryPoint.DSM_MemAllocate != null) - { - return _entryPoint.DSM_MemAllocate(size); - } - else if (TWPlatform.IsWindows) - { - return WinNativeMethods.GlobalAlloc(WinNativeMethods.AllocFlag.GHND, (UIntPtr)size); - } - else if (TWPlatform.IsLinux) - { - return Marshal.AllocHGlobal((int)size); - } - else if (TWPlatform.IsMacOSX) - { - return Marshal.AllocHGlobal((int)size); - } - else - { - throw new PlatformNotSupportedException(); - } + TW_ENTRYPOINT_DELEGATES _entryPoint; + + public IntPtr Alloc(uint size) + { + if (_entryPoint.DSM_MemAllocate != null) + { + return _entryPoint.DSM_MemAllocate(size); + } + else if (TWPlatform.IsWindows) + { +#pragma warning disable CA1416 // Validate platform compatibility + return PInvoke.GlobalAlloc(GLOBAL_ALLOC_FLAGS.GHND, size); +#pragma warning restore CA1416 // Validate platform compatibility + //return WinNativeMethods.GlobalAlloc(WinNativeMethods.AllocFlag.GHND, (UIntPtr)size); + } + else if (TWPlatform.IsLinux) + { + return Marshal.AllocHGlobal((int)size); + } + else if (TWPlatform.IsMacOSX) + { + return Marshal.AllocHGlobal((int)size); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public void Free(IntPtr handle) + { + if (handle == IntPtr.Zero) return; + + if (_entryPoint.DSM_MemFree != null) + { + _entryPoint.DSM_MemFree(handle); + } + else if (TWPlatform.IsWindows) + { +#pragma warning disable CA1416 // Validate platform compatibility + PInvoke.GlobalFree((HGLOBAL)handle); +#pragma warning restore CA1416 // Validate platform compatibility + //WinNativeMethods.GlobalFree(handle); + } + else if (TWPlatform.IsLinux) + { + Marshal.FreeHGlobal(handle); + } + else if (TWPlatform.IsMacOSX) + { + Marshal.FreeHGlobal(handle); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public IntPtr Lock(IntPtr handle) + { + if (handle == IntPtr.Zero) return IntPtr.Zero; + + if (_entryPoint.DSM_MemLock != null) + { + return _entryPoint.DSM_MemLock(handle); + } + else if (TWPlatform.IsWindows) + { + unsafe + { +#pragma warning disable CA1416 // Validate platform compatibility + return (IntPtr)PInvoke.GlobalLock((HGLOBAL)handle); +#pragma warning restore CA1416 // Validate platform compatibility + } + //return WinNativeMethods.GlobalLock(handle); + } + else if (TWPlatform.IsLinux) + { + return handle; + } + else if (TWPlatform.IsMacOSX) + { + return handle; + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public void Unlock(IntPtr handle) + { + if (handle == IntPtr.Zero) return; + + if (_entryPoint.DSM_MemUnlock != null) + { + _entryPoint.DSM_MemUnlock(handle); + } + else if (TWPlatform.IsWindows) + { +#pragma warning disable CA1416 // Validate platform compatibility + PInvoke.GlobalUnlock((HGLOBAL)handle); +#pragma warning restore CA1416 // Validate platform compatibility + //WinNativeMethods.GlobalUnlock(handle); + } + else if (TWPlatform.IsLinux) + { + } + else if (TWPlatform.IsMacOSX) + { + } + else + { + throw new PlatformNotSupportedException(); + } + } } - - public void Free(IntPtr handle) - { - if (handle == IntPtr.Zero) return; - - if (_entryPoint.DSM_MemFree != null) - { - _entryPoint.DSM_MemFree(handle); - } - else if (TWPlatform.IsWindows) - { - WinNativeMethods.GlobalFree(handle); - } - else if (TWPlatform.IsLinux) - { - Marshal.FreeHGlobal(handle); - } - else if (TWPlatform.IsMacOSX) - { - Marshal.FreeHGlobal(handle); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public IntPtr Lock(IntPtr handle) - { - if (handle == IntPtr.Zero) return IntPtr.Zero; - - if (_entryPoint.DSM_MemLock != null) - { - return _entryPoint.DSM_MemLock(handle); - } - else if (TWPlatform.IsWindows) - { - return WinNativeMethods.GlobalLock(handle); - } - else if (TWPlatform.IsLinux) - { - return handle; - } - else if (TWPlatform.IsMacOSX) - { - return handle; - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public void Unlock(IntPtr handle) - { - if (handle == IntPtr.Zero) return; - - if (_entryPoint.DSM_MemUnlock != null) - { - _entryPoint.DSM_MemUnlock(handle); - } - else if (TWPlatform.IsWindows) - { - WinNativeMethods.GlobalUnlock(handle); - } - else if (TWPlatform.IsLinux) - { - } - else if (TWPlatform.IsMacOSX) - { - } - else - { - throw new PlatformNotSupportedException(); - } - } - } } diff --git a/src/NTwain/TwainAppSession.Windows.cs b/src/NTwain/TwainAppSession.Windows.cs index 2380f21..cd6f69d 100644 --- a/src/NTwain/TwainAppSession.Windows.cs +++ b/src/NTwain/TwainAppSession.Windows.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Windows.Interop; +using Windows.Win32.Foundation; using MSG = NTwain.Data.MSG; namespace NTwain @@ -83,11 +84,11 @@ namespace NTwain bool handled = false; if (_state >= STATE.S5) { - WIN_MESSAGE winMsg = new() + Windows.Win32.UI.WindowsAndMessaging.MSG winMsg = new() { - hwnd = hWnd, + hwnd = (HWND)hWnd, message = (uint)msg, - wParam = wParam, + wParam = TWPlatform.Is32bit ? new UIntPtr((uint)wParam.ToInt32()) : new UIntPtr((ulong)wParam.ToInt64()), lParam = lParam }; // no need to do another lock call when using marshal alloc