twain / twain-cs

A C# interface for TWAIN
168 stars 67 forks source link

NativeToBitmap assumptions. [sf#15] #19

Closed kolomiets closed 4 years ago

kolomiets commented 7 years ago

Reported by neticous on 2015-10-13 20:27 UTC Assuming // Windows uses a DIB, the first usigned short is 40... if (u16Magic == 40) Is not exactly true. For instance the Canoscan Lide 90, 100, 200 sends a DIB back but yoour code does not recognize it and sends back a null bitmap. I added an else to the NativeTobitmap method inorder for this to work. I also cleaned up a leak in the code that assumes if (u16Magic == 40). I have pasted the code below. You can diff to see the changes.

I hope it is OK to paste code here.

///

/// Get .NET 'Bitmap' object from memory DIB via stream constructor. /// This should work for most DIBs. /// /// Our operating system /// The pointer to something (presumably a BITMAP or a TIFF image) /// C# Bitmap of image private static Bitmap NativeToBitmap(Platform a_platform, IntPtr a_intptrNative) { // We need the first two bytes to decide if we have a DIB or a TIFF... ushort u16Magic; u16Magic = (ushort)Marshal.PtrToStructure(a_intptrNative, typeof(ushort));

        // Windows uses a DIB, the first usigned short is 40...
        if (u16Magic == 40)
        {
            byte[] bBitmap;
            BITMAPFILEHEADER bitmapfileheader;
            BITMAPINFOHEADER bitmapinfoheader;

            // Our incoming DIB is a bitmap info header...
            bitmapinfoheader = (BITMAPINFOHEADER)Marshal.PtrToStructure(a_intptrNative, typeof(BITMAPINFOHEADER));

            // Build our file header...
            bitmapfileheader = new BITMAPFILEHEADER();
            bitmapfileheader.bfType = 0x4D42; // "BM"
            bitmapfileheader.bfSize
                = (uint)Marshal.SizeOf(typeof(BITMAPFILEHEADER)) +
                   bitmapinfoheader.biSize +
                   (bitmapinfoheader.biClrUsed * 4) +
                   bitmapinfoheader.biSizeImage;
            bitmapfileheader.bfOffBits
                = (uint)Marshal.SizeOf(typeof(BITMAPFILEHEADER)) +
                   bitmapinfoheader.biSize +
                   (bitmapinfoheader.biClrUsed * 4);

            // Copy the file header into our byte array...
            IntPtr intptr = Marshal.AllocHGlobal(Marshal.SizeOf(bitmapfileheader));
            Marshal.StructureToPtr(bitmapfileheader, intptr, true);
            bBitmap = new byte[bitmapfileheader.bfSize];
            Marshal.Copy(intptr, bBitmap, 0, Marshal.SizeOf(bitmapfileheader));
            Marshal.FreeHGlobal(intptr);
            intptr = IntPtr.Zero;

            // Copy the rest of the DIB into our byte array......
            Marshal.Copy(a_intptrNative, bBitmap, Marshal.SizeOf(typeof(BITMAPFILEHEADER)), (int)bitmapfileheader.bfSize - Marshal.SizeOf(typeof(BITMAPFILEHEADER)));
            Bitmap bitmap = null;
            // Now we can turn the in-memory bitmap file into a Bitmap object...
            using (MemoryStream memorystream = new MemoryStream(bBitmap))
            {

                // Unfortunately the stream has to be kept with the bitmap...
                using (Bitmap bitmapStream = new Bitmap(memorystream))
                {

                    // So we make a copy (ick)...
                    bitmap = new Bitmap(bitmapStream);
                    bitmapStream.Dispose();
                }

                // Cleanup...
                //bitmapStream.Dispose();
                memorystream.Close();
                //bitmapStream = null;
                //memorystream = null;
            }
            bBitmap = null;

            // Return our bitmap...
            return (bitmap);
        }

        // Linux and Mac OS X use TIFF.  We'll handle a simple Intel TIFF ("II")...
        else if (u16Magic == 0x4949)
        {
            int iTiffSize;
            ulong u64;
            ulong u64Pointer;
            ulong u64TiffHeaderSize;
            ulong u64TiffTagSize;
            byte[] abTiff;
            TIFFHEADER tiffheader;
            TIFFTAG tifftag;

            // Init stuff...
            tiffheader = new TIFFHEADER();
            tifftag = new TIFFTAG();
            u64TiffHeaderSize = (ulong)Marshal.SizeOf(tiffheader);
            u64TiffTagSize = (ulong)Marshal.SizeOf(tifftag);

            // Find the size of the image so we can turn it into a memory stream...
            iTiffSize = 0;
            tiffheader = (TIFFHEADER)Marshal.PtrToStructure(a_intptrNative, typeof(TIFFHEADER));
            for (u64 = 0; u64 < 999; u64++)
            {
                u64Pointer = (ulong)a_intptrNative + u64TiffHeaderSize + (u64TiffTagSize * u64);
                tifftag = (TIFFTAG)Marshal.PtrToStructure((IntPtr)u64Pointer, typeof(TIFFTAG));

                // StripOffsets...
                if (tifftag.u16Tag == 273)
                {
                    iTiffSize += (int)tifftag.u32Value;
                }

                // StripByteCounts...
                if (tifftag.u16Tag == 279)
                {
                    iTiffSize += (int)tifftag.u32Value;
                }
            }

            // No joy...
            if (iTiffSize == 0)
            {
                return (null);
            }

            // Copy the data to our byte array...
            abTiff = new byte[iTiffSize];
            Marshal.Copy(a_intptrNative, abTiff, 0, iTiffSize);

            // Move the image into a memory stream...
            MemoryStream memorystream = new MemoryStream(abTiff);

            // Turn the memory stream into an in-memory TIFF image...
            Image imageTiff = Image.FromStream(memorystream);

            // Convert the in-memory tiff to a Bitmap object...
            Bitmap bitmap = new Bitmap(imageTiff);

            // Cleanup...
            abTiff = null;
            memorystream = null;
            imageTiff = null;

            // Return our bitmap...
            return (bitmap);
        }
        else
        {
            IntPtr dibhand;
            IntPtr bmpptr;
            IntPtr pixptr;

            dibhand = a_intptrNative;

            if (dibhand != IntPtr.Zero)
            {
                bmpptr = GlobalLock(dibhand);
                pixptr = GetPixelInfo(bmpptr);
                IntPtr img = IntPtr.Zero;

                int st = GdipCreateBitmapFromGdiDib(bmpptr, pixptr, ref img);
                if ((st != 0) || (img == IntPtr.Zero))
                    return (null);

                MethodInfo mi = typeof(Bitmap).GetMethod("FromGDIplus", BindingFlags.Static | BindingFlags.NonPublic);
                Bitmap bmp = null;

                if ((st == 0) && (img != IntPtr.Zero)) // success 
                    bmp = (Bitmap)mi.Invoke(null, new object[] { img });

                //GlobalFree(dibhand);
                GlobalUnlock(dibhand);
                dibhand = IntPtr.Zero;
                Bitmap b = new Bitmap(bmp);
                bmp.Dispose();
                bmp = null;
                return b;
            }

        }

        // Uh-oh...
        return (null);
    }

    Thanks

    neticous

    PS: you all have done some very handle work here. Thank you.
kolomiets commented 7 years ago

Commented by neticous on 2015-10-13 20:32 UTC I added some using statements so the above would compile.

using System; using System.Runtime.InteropServices; using System.Diagnostics; using System.Drawing; using System.IO; using System.Threading; using System.Reflection;

kolomiets commented 7 years ago

Commented by chenar-kostya on 2015-10-16 17:47 UTC Thank you, it really helped.

kolomiets commented 7 years ago

Updated by mlmcl on 2015-10-18 00:42 UTC

kolomiets commented 7 years ago

Commented by mlmcl on 2015-10-18 00:42 UTC Thanks for the help. I found the root cause of the problem. The GlobalLock is the key, with a GPTR it returns the same value, with a GHND it returns the pointer. I managed to get a scanner that exhibits the problem as described in this ticket, and confirmed the fix, you'll see the way I did it in the code update that should come out tomorrow. I'll leave the ticket open for confirmation that it's working...

kolomiets commented 7 years ago

Updated by mlmcl on 2015-10-18 09:28 UTC

kolomiets commented 7 years ago

Commented by mlmcl on 2015-10-18 09:28 UTC The new code is up, please let me know if it makes a difference...