Device Independent Bitmaps, C#, and Me...REDUX!

Please spread the word if you like this content!
Digg Button submit to reddit
 

Well, after using the previously posted code in some projects at work, some limitations arose quickly.  The two biggest were support for 8-bit bitmaps, and support for both 5-5-5 and 5-6-5 16-bit bitmaps.

The new code looks like the following:

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Imaging;

using System.Runtime.InteropServices;

using System.IO;

 

 

namespace DIBitmaps

{

    static public class DIB

    {

        // our BITMAPINFOHEADER struct, as per gdi

        // use LayoutKind to make sure data is marshalled as we've laid it out

        [StructLayout(LayoutKind.Sequential)]

        public struct BITMAPINFOHEADER

        {

            public uint biSize;

            public int biWidth;

            public int biHeight;

            public ushort biPlanes;

            public ushort biBitCount;

            public uint biCompression;

            public uint biSizeImage;

            public int biXPelsPerMeter;

            public int biYPelsPerMeter;

            public uint biClrUsed;

            public uint biClrImportant;

            //public void Init()

            //{

            //    biSize = (uint)Marshal.SizeOf(this);

            //}

        }

 

        public static Bitmap BitmapFromDIB(MemoryStream dib)

        {

            // get byte array of device independent bitmap

            byte[] dibBytes = dib.ToArray();

 

            // get the handle for the byte array and "pin" that memory (i.e. prevent

            // garbage collector from gobbling it up right away)...

            GCHandle hdl = GCHandle.Alloc(dibBytes, GCHandleType.Pinned);

 

            // marshal our data into a BITMAPINFOHEADER struct per Win32

            // definition of BITMAPINFOHEADER

            BITMAPINFOHEADER dibHdr = (BITMAPINFOHEADER)Marshal.PtrToStructure

                         (hdl.AddrOfPinnedObject(), typeof(BITMAPINFOHEADER));

            bool is555 = true;

 

            Bitmap bmp = null;

 

            if (dibHdr.biBitCount == 8)

            {

                // set our pointer to end of BITMAPINFOHEADER

                Int64 jumpTo = hdl.AddrOfPinnedObject().ToInt64() + dibHdr.biSize;

                bmp = new Bitmap(dibHdr.biWidth, dibHdr.biHeight, PixelFormat.Format8bppIndexed);

                bmp.SetResolution((100f * (float)dibHdr.biXPelsPerMeter) / 2.54f,

                         (100f * (float)dibHdr.biYPelsPerMeter) / 2.54f);

 

                // set the colors in our palette

                ColorPalette palette = bmp.Palette;

                IntPtr ptr = IntPtr.Zero;

                int colors = (int)(dibBytes.Length - (bmp.Width * bmp.Height) - dibHdr.biSize);

                for (int i = 0; i < 256; i++)

                {

                    ptr = new IntPtr(jumpTo);

                    uint bmiColor = (uint)Marshal.ReadInt32(ptr);

                    int r = (int)((bmiColor & 0xFF0000) >> 16),

                        g = (int)((bmiColor & 0xFF00) >> 8),

                        b = (int)((bmiColor & 0xFF));

                    palette.Entries[i] = Color.FromArgb(r, g, b);

                    jumpTo += 4;

                }

                bmp.Palette = palette;

 

                // now write the remaining bmp data to our bitmap

                BitmapData _8bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),

                        ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

                jumpTo -= hdl.AddrOfPinnedObject().ToInt64();

                Marshal.Copy(dibBytes, (int)jumpTo, _8bd.Scan0, _8bd.Stride * _8bd.Height);

                bmp.UnlockBits(_8bd);

            }

            else if ((dibHdr.biBitCount == 16) && (dibHdr.biCompression == 3))

            {

                Int64 jumpTo = (Int64)(dibHdr.biClrUsed * (uint)4 + dibHdr.biSize);

                IntPtr ptr = new IntPtr(hdl.AddrOfPinnedObject().ToInt64() + jumpTo);

                ushort redMask = (ushort)Marshal.ReadInt16(ptr);

                ptr = new IntPtr(ptr.ToInt64() + (2 * Marshal.SizeOf(typeof(UInt16))));

                ushort greenMask = (ushort)Marshal.ReadInt16(ptr);

                ptr = new IntPtr(ptr.ToInt64() + (2 * Marshal.SizeOf(typeof(UInt16))));

                ushort blueMask = (ushort)Marshal.ReadInt16(ptr);

 

                is555 = ((redMask == 0x7C00) && (greenMask == 0x03E0) && (blueMask == 0x001F));

            }

 

            // go ahead and release the "pin" from our handle on that memory

            hdl.Free();

 

            // If the target device does not have one plane, or we're working with a bitmap other

            // than a non-compressed (BI_RGB) bitmap, we're not gonna work woith it

            if (dibHdr.biPlanes != 1 || (dibHdr.biCompression != 0 && dibHdr.biCompression != 3))

                return null;

 

            if (bmp == null)

            {

                // we need to know beforehand the pixel-depth of our bitmap

                PixelFormat fmt = PixelFormat.Format24bppRgb;

 

                switch (dibHdr.biBitCount)

                {

                    case 32:

                        fmt = PixelFormat.Format32bppRgb;

                        break;

                    case 24:

                        fmt = PixelFormat.Format24bppRgb;

                        break;

                    case 16:

                        fmt = (is555) ? PixelFormat.Format16bppRgb555 :

                                    PixelFormat.Format16bppRgb565;

                        break;

                    default:

                        return null;

                }

 

                // prepare for our output bitmap

                bmp = new Bitmap(dibHdr.biWidth, dibHdr.biHeight, fmt);

 

                // load our "empty" bitmap into memory and lock it for

                // writing in the format we specified

                BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),

                        ImageLockMode.WriteOnly, fmt);

 

                // marshal our device independent bitmap data over to our output bitmap

                Marshal.Copy(dibBytes, Marshal.SizeOf(dibHdr), bd.Scan0, bd.Stride * bd.Height);

 

                // we're done marshalling, so release our bitmapdata lock

                bmp.UnlockBits(bd);

 

            }

 

            if (dibHdr.biHeight > 0)

            {

                // DIB data is upside-down for some reason, so flip it

                bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);

            }

 

            // return our bitmap

            return bmp;

        }

    }

}

 

 

The adjusted code can be downloaded here.

If you find any other issues, please let me know and I'll get it updated ASAP.

~ZagNUT

posted @ Thursday, January 08, 2009 12:24 PM

Print

Comments on this entry:

# re: Device Independent Bitmaps, C#, and Me...REDUX!

Left by ZagNut at 5/11/2009 6:52 PM
Gravatar
Thanks, Yuva.<br /><br />This is just for working with the MS DIB format.<br /><br />Check out mDCM for a C# dicom lib.<br /><br />~ZagNut

# re: Device Independent Bitmaps, C#, and Me...REDUX!

Left by Yuva at 5/11/2009 11:52 PM
Gravatar
Hi,<br />Very good work.<br /><br />Are it also work for DICOM images.

Your comment:



 (will not be displayed)


 
 
 
Please add 2 and 7 and type the answer here:
 

Live Comment Preview:

 
«September»
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789