CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2
  1. #1
    Join Date
    Apr 2011
    Posts
    20

    Question SetDIBitsToDevice in C#

    Hello,

    I try to use the GDI function "SetDIBitsToDevice", which I call via P/Invoke:
    Code:
     [DllImport("gdi32.dll", EntryPoint = "SetDIBitsToDevice", SetLastError = true)]
     public static extern int SetDIBitsToDevice([In] IntPtr hdc, int xDest, int yDest, uint w, uint h, int xSrc,
                                                           int ySrc, uint startScan, uint cLines, [In] IntPtr lpvBits,
                                                           [In] ref BITMAPINFO lpbmi, DIBColorTable colorUse);
    BITMAPINFO is a struct and DIBColorTable is an enum:

    Code:
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
            public struct BITMAPINFO
            { 
                public uint biSize;
                public int biWidth, biHeight;
                public short biPlanes, biBitCount;
                public uint biCompression, biSizeImage;
                public int biXPelsPerMeter, biYPelsPerMeter;
                public uint biClrUsed, biClrImportant;
                [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=256)]
                public uint[] cols;
    
    public enum DIBColorTable {    DIB_RGB_COLORS = 0,    /* color table in RGBs */
                                           DIB_PAL_COLORS  };    /* color table in palette indices */
            }
    The data to be displayed is a byte array of raw image data. I pass this array to my function, where a BitmapInfo is created and then it is used to display the raw data with SetDIBitsToDevice().

    Code:
    // create BitmapInfo, which is necessary for the SetDIBitsToDevice function
                BITMAPINFO lpbmi;
                lpbmi.biSize            = 40;
                lpbmi.biWidth           = m_ImageWidth;
                lpbmi.biHeight          = - m_ImageHeight;
                lpbmi.biPlanes          = 1;
                lpbmi.biBitCount        = 24;
                lpbmi.biCompression     = (uint)biCompression.BI_RGB;
                lpbmi.biSizeImage       = (uint)(m_ImageHeight * PadLineBytes(m_ImageWidth * lpbmi.biBitCount /8));
                lpbmi.biXPelsPerMeter   = 0;
                lpbmi.biYPelsPerMeter   = 0;
                lpbmi.biClrUsed         = 0;
                lpbmi.biClrImportant    = 0;
                lpbmi.cols = null;
                if (lpbmi.biBitCount == 8)
                {
                    for (int i = 0; i < 256; i++)
                    {
                        lpbmi.cols[i] = (uint)i;
                    }
                }
    
                IntPtr lpvBits = Marshal.UnsafeAddrOfPinnedArrayElement(imageData, 0);
                Graphics graphicsOfPicBox = m_PicView.CreateGraphics();
                IntPtr hdc = graphicsOfPicBox.GetHdc();
    
                int iRet = SetDIBitsToDevice(hdc, 0, 0, (uint)m_ImageWidth, (uint)m_ImageHeight, 0, 0, 0, (uint)m_ImageHeight, lpvBits,
                                    ref lpbmi, DIBColorTable.DIB_RGB_COLORS);
    
                graphicsOfPicBox.ReleaseHdc(hdc);
    This method works almost always. However, sometimes an error ocurres in System.Drawing.dll and the program crashes. See the callstack:

    Code:
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.bFillBitmapInfo(System.IntPtr hdc, System.IntPtr hpal, ref System.Drawing.NativeMethods.BITMAPINFO_FLAT pbmi) + 0x120 bytes	
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.CreateCompatibleDIB(System.IntPtr hdc, System.IntPtr hpal, int ulWidth, int ulHeight, ref System.IntPtr ppvBits) + 0x7a bytes	
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.CreateBuffer(System.IntPtr src, int offsetX, int offsetY, int width, int height) + 0x184 bytes	
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.AllocBuffer(System.Drawing.Graphics targetGraphics, System.IntPtr targetDC, System.Drawing.Rectangle targetRectangle) + 0x86 bytes	
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.AllocBufferInTempManager(System.Drawing.Graphics targetGraphics, System.IntPtr targetDC, System.Drawing.Rectangle targetRectangle) + 0x59 bytes	
     	System.Drawing.dll!System.Drawing.BufferedGraphicsContext.Allocate(System.IntPtr targetDC, System.Drawing.Rectangle targetRectangle) + 0x58 bytes	
     	System.Windows.Forms.dll!System.Windows.Forms.Control.WmPaint(ref System.Windows.Forms.Message m) + 0x235 bytes	
     	System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x2b1 bytes	
     	System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) + 0x13 bytes	
     	System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x31 bytes	
     	System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x64 bytes	
     	user32.dll!770a6238() 	
     	[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]	
     	user32.dll!770a7298() 	
     	user32.dll!770a6899() 	
     	user32.dll!770a7177() 	
     	user32.dll!770a72f1() 	
     	ntdll.dll!775f00e6() 	
     	user32.dll!770b0e8f() 	
     	user32.dll!770b0ee5() 	
     	user32.dll!770a7dfa() 	
    >	mfc100ud.dll!AfxInternalPumpMessage()  Line 183	C++
     	user32.dll!770a7298() 	
     	user32.dll!770a6899() 	
     	user32.dll!770a7177() 	
     	user32.dll!770a72f1() 	
     	ntdll.dll!775f00e6() 	
     	user32.dll!770b0e8f() 	
     	user32.dll!770b0ee5() 	
     	user32.dll!770a7dfa() 	
     	mfc100ud.dll!AfxInternalPumpMessage()  Line 183	C++
     	mfc100ud.dll!CWinThread::PumpMessage()  Line 900	C++
     	ControlDeskNG.exe!DSCXDeskFrameApp::DoRun()  Line 3527 + 0xf bytes	C++
     	ControlDeskNG.exe!DSCXDeskFrameApp::Run()  Line 3454 + 0x8 bytes	C++
     	mfc100ud.dll!AfxWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow)  Line 47 + 0xd bytes	C++
     	ControlDeskNG.exe!wWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow)  Line 26	C++
     	ControlDeskNG.exe!__tmainCRTStartup()  Line 547 + 0x2c bytes	C
     	ControlDeskNG.exe!wWinMainCRTStartup()  Line 371	C
     	kernel32.dll!76d73677() 	
     	ntdll.dll!77619f02() 	
     	ntdll.dll!77619ed5() 	
     	ControlDeskNG.exe!std::_Vector_const_iterator<std::_Vector_val<DsCToolbarConfig *,std::allocator<DsCToolbarConfig *> > >::operator++()  Line 110	C++
    I hope you can tell me what I'm doing wrong. Thanks.

    best regards

  2. #2
    Join Date
    Apr 2011
    Posts
    20

    Question Re: SetDIBitsToDevice in C#

    Hello again,

    I have figured out that the error ocurres, if the stride (width * BytePerPixel) of the image is not a multiple of 4. For example, width = 90, with 24RGB format leads to stride = 90*3 = 270 Bytes. However, 270 % 4 != 0. Therefore I tried to add padding bytes, but it didn't help.

    Meanwhile, I don't use SetDIBitsToDevice function, but the means of .NET :

    Code:
    Graphics graphicsOfPicBox = m_PicView.CreateGraphics();
    
                // copy the image data bytes into Bitmap struct
                Bitmap img = CopyDataToBitmap(m_decompressedImageData, m_ImageHeight, m_ImageWidth);
    
                graphicsOfPicBox.DrawImage((Image)img, 0, 0);
    Here is the code, where the data from a byte array is copied into the Bitmap:

    Code:
    private Bitmap CopyDataToBitmap(byte[] data, int height, int width)
            {
                //IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
    
                //Here create the Bitmap to the know height, width and format
                Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
                //Bitmap bmp = new Bitmap(width, height, PadLineBytes(width * 3), PixelFormat.Format24bppRgb, pointer);
    
                //Create a BitmapData and Lock all pixels to be written 
                BitmapData bmpData = bmp.LockBits(
                                     new Rectangle(0, 0, bmp.Width, bmp.Height),
                                     ImageLockMode.ReadWrite, bmp.PixelFormat);
    
    
                int paddedWidth = PadLineBytes(width * 3);
                bmpData.Stride = paddedWidth;
    
                //Copy the data from the byte array into BitmapData.Scan0
                Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
                //Marshal.Copy(data, 0, bmpData.Scan0, bmpData.Stride*bmpData.Height);
    
                //Unlock the pixels
                bmp.UnlockBits(bmpData);
    
                //Return the bitmap 
                return bmp;
            }
    I guess that either the code with copying byte array into Bitmap or the function DrawImage are not used correctly. If I use the code above, then the images are displayed squezzed and the image lines are not aligned properly. Please help me to get images with padded lines displayed correctly. Thanks.

    best regards

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured