Getting a bitmap from a DIB handle

MarMan

Member
Joined
Jan 4, 2010
Messages
17
Programming Experience
10+
Hello,

I'm developing a scanning application in VB.NET 2005 using TWAIN. The scanner returns to me a handle to a DIB. I need to save this to a file, but I've been searching everywhere and can't find any information.

Can anyone point me in the right direction? Thanks.
 
Thanks Matt. Unfortunately I need VB help. I found the CodeProject reference you posted before I posted here. It is not VB code, so unless someone can translate or explain what it is doing...

Didn't think so.

Anyway I tried searching for a api that it uses but couldn't find anything. If anyone knows how to use GdipCreateBitmapFromGdiDib or can point me to a VB example then that may solve my problem.

The microsoft site Matt pointed me too has two more leads, but I can't find any information on them either. CreateDIBitmap or SetDIBits may also work if anyone can point me to any explanations on how to use them or VB examples.

Thanks.
 
Using: Convert C# to VB.NET - A free code conversion tool - developer Fusion - ASP.NET, C# Programming, VB.NET, .NET Framework, Java and Visual Basic Tutorials

I get:

VB.NET:
Imports System.Runtime.InteropServices
Imports System.Reflection
Imports System.Drawing

Public Class Form1

    <DllImport("GdiPlus.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True)> _
    Private Shared Function GdipCreateBitmapFromGdiDib(ByVal pBIH As IntPtr, ByVal pPix As IntPtr, ByRef pBitmap As IntPtr) As Integer
    End Function

    Public Shared Function BitmapFromDIB(ByVal pDIB As IntPtr, ByVal pPix As IntPtr) As Bitmap

        Dim mi As MethodInfo = GetType(Bitmap).GetMethod("FromGDIplus", BindingFlags.[Static] Or BindingFlags.NonPublic)

        If mi Is Nothing Then
            Return Nothing
        End If
        ' (permission problem) 

        Dim pBmp As IntPtr = IntPtr.Zero
        Dim status As Integer = GdipCreateBitmapFromGdiDib(pDIB, pPix, pBmp)

        If (status = 0) AndAlso (pBmp <> IntPtr.Zero) Then
            ' success 
            Return DirectCast(mi.Invoke(Nothing, New Object() {pBmp}), Bitmap)
        Else

            Return Nothing
            ' failure 
        End If
    End Function
End Class

This seems to compile fine on my system though I don't have anything to test it out on.

Didn't think so.

It would be advisable to avoid the snarky comments especially when you're coming to a place for help.

Here's the 2nd one converted:

VB.NET:
Imports System
Imports System.IO

Public Class BitmapFromDibStream
    Inherits Stream

    Private dib As Stream = Nothing
    Private header As Byte() = Nothing
    Public Sub New(ByVal dib As Stream)
        Me.dib = dib
        makeHeader()
    End Sub
    Private Sub makeHeader()
        Dim reader As New BinaryReader(dib)

        Dim headerSize As Integer = reader.ReadInt32()
        Dim pixelSize As Integer = CInt(dib.Length) - headerSize
        Dim fileSize As Integer = 14 + headerSize + pixelSize

        Dim bmp As New MemoryStream(14)
        Dim writer As New BinaryWriter(bmp)

        ' Get the palette size
        '                   * The Palette size is stored as an int32 at offset 32
        '                   * Actually stored as number of colours, so multiply by 4
        '                   

        dib.Position = 32
        Dim paletteSize As Integer = 4 * reader.ReadInt32()

        ' Get the palette size from the bbp if none was specified
        If paletteSize = 0 Then
            ' Get the bits per pixel
            '                     * The bits per pixel is store as an int16 at offset 14
            '                     

            dib.Position = 14
            Dim bpp As Integer = reader.ReadInt16()

            ' Only set the palette size if the bpp < 16
            If bpp < 16 Then
                paletteSize = 4 * (2 << (bpp - 1))
            End If
        End If

        ' 1. Write Bitmap File Header:			 
        writer.Write(CByte(AscW("B"c)))
        writer.Write(CByte(AscW("M"c)))
        writer.Write(fileSize)
        writer.Write(CInt(0))
        writer.Write(14 + headerSize + paletteSize)
        header = bmp.GetBuffer()
        writer.Close()
        dib.Position = 0
    End Sub

    Public Overloads Overrides Function Read(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer

        Dim dibCount As Integer = count
        Dim dibOffset As Integer = offset - 14
        Dim result As Integer = 0
        If _position < 14 Then
            Dim headerCount As Integer = Math.Min(count + CInt(_position), 14)
            Array.Copy(header, _position, buffer, offset, headerCount)
            dibCount -= headerCount
            _position += headerCount
            result = headerCount
        End If
        If _position >= 14 Then
            result += dib.Read(buffer, offset + result, dibCount)
            _position = 14 + dib.Position
        End If

        Return CInt(result)
    End Function

    Public Overrides ReadOnly Property CanRead As Boolean
        Get
            Return True
        End Get
    End Property

    Public Overrides ReadOnly Property CanSeek As Boolean
        Get
            Return False
        End Get
    End Property

    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return False
        End Get
    End Property

    Public Overrides Sub Flush()

    End Sub

    Public Overrides ReadOnly Property Length As Long
        Get
            Return 14 + dib.Length
        End Get
    End Property

    Private _position As Long = 0
    Public Overrides Property Position As Long
        Get
            Return _position
        End Get
        Set(ByVal value As Long)
            _position = value
            If (_position > 14) Then
                dib.Position = _position - 14
            End If
        End Set
    End Property

    Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
        Throw New Exception("The method or operation is not implemented.")
    End Function

    Public Overrides Sub SetLength(ByVal value As Long)
        Throw New Exception("The method or operation is not implemented.")
    End Sub

    Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
        Throw New Exception("The method or operation is not implemented.")
    End Sub
End Class
 
I apologize for that comment. You are correct Matt. I was wrong and a bit cranky:mad:, but anyway...

Thanks for that conversion and link. With that I was able to get it to work. That code used a pPix but didn't explain on how to get it. In the comments at the bottom was a link to a spanish site that led me to the line (and function)pPix = GetPixelInfo(pDIB) which works.

I have included the completed code that will convert either a handle or pointer of a DIB to a Bitmap or TIFF file.

VB.NET:
Imports microsoft.VisualBasic
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports System.Reflection

'DIB structure:
'1) BITMAPINFOHEADER
'2) ColorTable or Bitfields array, if any(i.e, predicated on bpp of image)
'3) Bitmap's bits

Module basGraphics

        Private Declare Function GdipCreateBitmapFromGdiDib Lib "GdiPlus.dll" ( _
        ByVal pBIH As IntPtr, _
        ByVal pPix As IntPtr, _
        ByRef pBitmap As IntPtr) As Integer


        <StructLayout(LayoutKind.Sequential, Pack:=2)> Public Class BitmapInfoHeader '40 bytes
        Public biSize As Integer                'Size of the header structure to identify to the API which Bitmap header type we’re passing it.
        Public biWidth As Integer               'Width of the Bitmap we wish to create
        Public biHeight As Integer              'Height of the Bitmap we wish to create
        Public biPlanes As Short                'Colour planes of the Bitmap we wish to create (always 1)
        Public biBitCount As Short              'Colour depth of the Bitmap we wish to create
        Public biCompression As Integer         'Compression method used to store the Bitmap data
        Public biSizeImage As Integer           'Size (in bytes) of the image data
        Public biXPelsPerMeter As Integer       'Number of horizontal pixels per meter on the source device
        Public biYPelsPerMeter As Integer       'Number of vertical pixels per meter on the source device
        Public biClrUsed As Integer             'Number of colours used from palette
        Public biClrImportant As Integer        'Number of colours from palette that are absolutely required for proper display (seldom used any more)
    End Class

    
    Public Function BitmapFromDIB(ByVal hDIB As Integer) As Bitmap

        Dim pDIB As IntPtr


        'Identify the memory pointer to the DIB Handler (hDIB)
        pDIB = New IntPtr(hDIB)

        Return BitmapFromDIB(pDIB)

    End Function

    Public Function BitmapFromDIB(ByVal pDIB As IntPtr) As Bitmap

        Dim intStatus As Integer
        Dim pBmp As IntPtr
        Dim pPix As IntPtr
        Dim mi As MethodInfo

        'Call external GDI method
        mi = GetType(Bitmap).GetMethod("FromGDIplus", BindingFlags.[Static] Or BindingFlags.NonPublic)
        If mi Is Nothing Then
            Return Nothing
        End If

        'Get pointer to bitmap header info
        pPix = GetPixelInfo(pDIB)

        'Initialize memory pointer where Bitmap will be saved
        pBmp = IntPtr.Zero

        'Call external methosd that saves bitmap into pointer
        intStatus = GdipCreateBitmapFromGdiDib(pDIB, pPix, pBmp)

        'If success return bitmap, if failed return null
        If (intStatus = 0) AndAlso (pBmp <> IntPtr.Zero) Then
            Return DirectCast(mi.Invoke(Nothing, New Object() {pBmp}), Bitmap)
        Else
            Return Nothing
        End If

    End Function

    Public Sub SavehDibToTiff(ByVal hDIB As Integer, ByVal strFileName As String, ByVal intXRes As Integer, ByVal intTRes As Integer)

        Dim pDIB As IntPtr


        'Identify the memory pointer to the DIB Handler (hDIB)
        pDIB = New IntPtr(hDIB)

        SavehDibToTiff(pDIB, strFileName, intXRes, intTRes)

    End Sub

    Public Sub SavehDibToTiff(ByVal pDIB As IntPtr, ByVal strFileName As String, ByVal intXRes As Integer, ByVal intTRes As Integer)

        Dim lngEV As Long
        Dim NewBitmap As Bitmap
        Dim ici As ImageCodecInfo


        'Save the contents of DIB pointer into bitmap object
        NewBitmap = BitmapFromDIB(pDIB)

        'Set resolution if needed
        If intXRes > 0 AndAlso intTRes > 0 Then
            NewBitmap.SetResolution(intXRes, intTRes)
        End If

        'Create an instance of the windows TIFF encoder
        ici = GetEncoderInfo("image/tiff")

        'Define encoder parameters
        Dim eps As New EncoderParameters(1)

        'Only one parameter in this case (compression)
        'Create an Encoder Value for TIFF compression Group 4
        lngEV = CLng(EncoderValue.CompressionCCITT4)
        eps.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.Compression, lngEV)

        'Save file
        NewBitmap.Save(strFileName, ici, eps)

    End Sub

    Private Function GetPixelInfo(ByVal pBMP As IntPtr) As IntPtr

        Dim p As Integer
        Dim bmi As BitmapInfoHeader
        Dim bmprect As Rectangle

        bmi = New BitmapInfoHeader()
        Marshal.PtrToStructure(pBMP, bmi)

        bmprect.X = bmprect.Y = 0
        bmprect.Width = bmi.biWidth
        bmprect.Height = bmi.biHeight

        If (bmi.biSizeImage = 0) Then
            On Error Resume Next
            bmi.biSizeImage = Int((((bmi.biWidth * bmi.biBitCount) + 31) & Hex(Not (31))) / 2 ^ 3) * bmi.biHeight
        End If

        p = bmi.biClrUsed
        If ((p = 0) And (bmi.biBitCount <= 8)) Then
            p = Int(1 * 2 ^ bmi.biBitCount)
        End If
        p = (p * 4) + bmi.biSize + CType(pBMP.ToInt32, Integer)

        Return New IntPtr(p)

    End Function

    Private Function GetEncoderInfo(ByVal strMimeType As String) As ImageCodecInfo

        Dim r As Integer
        Dim encoders As ImageCodecInfo()


        encoders = ImageCodecInfo.GetImageEncoders()
        For r = 0 To encoders.Length - 1
            If encoders(r).MimeType = strMimeType Then
                Return encoders(r)
            End If
        Next

        Return Nothing

    End Function

End Module

Thanks again Matt.:)
 

Latest posts

Back
Top