borderAndreaVB free resources for Visual Basic developersborder

AndreaVB Visual Basic and VB.NET source code resources - Copyright © 1999-2009 Andrea Tincani

AndreaVB Home | News Home | Forum Home | Downloads | Register | Search | PM | Profile

Previous Topic (Ways of doing Image Processing)Next Topic (String Handling in Java) New Topic Post Reply
AndreaVB OnLine : Articles and tutorials : DIB SECTION
Poster Resource
Asim-GDI GURU
Level: Sage

Registered: 29-07-2005
Posts: 54
icon DIB SECTION

Windows offers a lot of ways to handle a bitmap. Every way with its own advantages and painful disadvantages. According to Windows Bitmap is classified into two types namely  : Device Dependent Bitmpas and Device Independent Bitmaps. Primarilly both the types store  themself in memory,if any other place is not defined as the destination for the data to be  kept.Device Dependent Bitmaps (DDB) are those which are all look after by Windows itself  from the creation to their demise but only for one particular device as the name suggests.  It means that if a DDB is made according to one device then it won't work on any other  device. Like if once you create a DDB on a device with certain internal settings, the DDB  will only work on this device or on the devices which are of same capablities. But the  biggest drawback of using DDB is that they can not  be saved on to disk. This is where a  practical programmer says goodbye to DDB seeking for some independance. Device Independant  Bitmaps (DIB) are what practical GDI programmers mostly deal with. DIBs are nothing more  than a bunch of structures containing certain settings that Windows needs the programmer to  set before making the DIB. But when we talk about DIBs we mean a lot of restrictions like  you can't directly draw on it using the standard GDI API (LineTo,Ellipse). Thus it make  DIBs another painful way to handle the bitmap. So here the idea should come to your mind of  something that has the advantages of both but no disadvantages. Win32 addressed this issue  so ellegantly that it made a third classification of Bitmap which is really matches with  your idea. DIB Section is a type of Bitmap which is organized on the same structures DIBs  are managed on but with the facilities of DDBs. It can be created using a single API, it  can be selected into any device regardless of the device type, its pixel data can be  accessed using just one API, it is friendly to all the GDI drawing API, it can be saved to  disk, and what not.
Without going into any further details let me show how to make a DIB Section.


Option Explicit
' DECLARATIONS...

' Types Declarations
Private Type RGBQUAD
    rgbBlue As Byte
    rgbGreen As Byte
    rgbRed As Byte
    rgbReserved As Byte
End Type

Private Type BITMAPINFOHEADER
  biSize          As Long
  biWidth         As Long
  biHeight        As Long
  biPlanes        As Integer
  biBitCount      As Integer
  biCompression   As Long
  biSizeImage     As Long
  biXPelsPerMeter As Long
  biYPelsPerMeter As Long
  biClrUsed       As Long
  biClrImportant  As Long
End Type

Private Type BITMAPINFO
    bmiHeader As BITMAPINFOHEADER
    bmiColors As RGBQUAD
End Type

' API Declarations
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
Private Declare Function SetDIBits Lib "gdi32" (ByVal hdc As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hdc As Long, ByRef pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function SetPixelV Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Long
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

' Constants Declarations
Private Const BI_RGB = 0&
Private Const DIB_RGB_COLORS = 0
Private Const SRCCOPY = &HCC0020

' Personal Declaration
Private bDIB() As RGBQUAD
Private BI As BITMAPINFO
Private DC As Long
Private DIBSection As Long
Private oldDIB As Long


Private Sub Form_Load()
' Creating the HDC to contain the DIB Section
DC = CreateCompatibleDC(Picture1.hdc)
If (DC <> 0) Then   ' Checking whether the DC is made or not
' Finally making the 6 settings required by the programmer to set
With BI.bmiHeader
    .biSize = 40
    .biWidth = Picture1.ScaleWidth     ' Picture Width
    .biHeight = -Picture1.ScaleHeight  ' Picture Height(dont confuse with the (-) sign.Just make it compulsory)
    .biPlanes = 1
    .biBitCount = 32
    .biSizeImage = (((Picture1.ScaleWidth * 3) + 3) And &HFFFFFFFC) * Picture1.ScaleHeight ' This is how we can calculate the Image Size in Bytes
End With
Dim addr As Long ' This will contain the memory address to the Bitmap data in the memory
' And now making the EMPTY DIB Section with above settings
DIBSection = CreateDIBSection(DC, BI, DIB_RGB_COLORS, addr, 0, 0)
If (DIBSection <> 0) Then ' Checking whether the DIB Section is made or not
    ' Selecting the DIB Section into the DC to do drawing on it
    oldDIB = SelectObject(DC, DIBSection)
Else
    Call DeleteDC(DC) ' If the DIB Section could not be made then delete the DC we made earlier
End If
End If
End Sub

' Now the DIB Section is made and ready to be used. You can do usuall GDI drawing on it or can
' do special stuff like passing the Pixel data through a Image Filter(like GreyScale)
' I'll show you two ways to handle the allready made DIB Section

Private Sub Grey_Click()
Dim X As Long, Y As Long, ti As Long, Pixel As Long, GrayScale As Long
ti = GetTickCount
' Now copying the Picturebox Image in the EMPTY DIB Section
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)
' We have to go through each and every pixel in the Bitmap
For X = 0 To Picture1.ScaleWidth
  For Y = 0 To Picture1.ScaleHeight
        Pixel = GetPixel(DC, X, Y) ' Get one pixel from the DIB Section
        ' Now we process the acquired pixel
        GrayScale = ((77& * (Pixel And &HFF&) + 152& * (Pixel And &HFF00&) \ &H100& + 28& * (Pixel \ &H10000)) \ 256&) * &H10101
        ' Then setting back the processed pixel in the DIB Section
        Call SetPixelV(DC, X, Y, GrayScale)
  Next Y
Next X
' Copying the processed picture into the Picturebox
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, DC, 0, 0, SRCCOPY)
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

Private Sub Grey1_Click()
Dim X As Long, Y As Long, ti As Long
ti = GetTickCount
' Now copying the Picturebox Image in the EMPTY DIB Section
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)
' Getting the Pixel Array of the DIB Section
ReDim bDIB(1 To Picture1.ScaleWidth, 1 To Picture1.ScaleHeight) As RGBQUAD
' The following API is the ONE which gets the Pixel Array of the DIB Section
Call GetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
' We have to go through each and every pixel in the Bitmap
For X = 1 To Picture1.ScaleWidth
  For Y = 1 To Picture1.ScaleHeight
        bDIB(X, Y).rgbBlue = 0.3 * bDIB(X, Y).rgbRed + 0.587 * bDIB(X, Y).rgbGreen + 0.114 * bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbGreen = bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbRed = bDIB(X, Y).rgbBlue
  Next Y
Next X
' Setting back the Pixel Array of the DIB Section
Call SetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
' Copying the processed picture into the Picturebox
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, DC, 0, 0, SRCCOPY)
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

Private Sub Negative_Click()
Dim X As Long, Y As Long, ti As Long
ti = GetTickCount
' Now copying the Picturebox Image in the EMPTY DIB Section
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)
' Getting the Pixel Array of the DIB Section
ReDim bDIB(1 To Picture1.ScaleWidth, 1 To Picture1.ScaleHeight) As RGBQUAD
' The following API is the ONE which gets the Pixel Array of the DIB Section
Call GetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
' We have to go through each and every pixel in the Bitmap
For X = 1 To Picture1.ScaleWidth
  For Y = 1 To Picture1.ScaleHeight
        ' The following is the way we can calculate the negative shade of every pixel in the DIB Section
        ' Just Red = 255 - Red , Green = 255 - Green , Blue = 255 - Blue
        bDIB(X, Y).rgbBlue = 255 - bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbGreen = 255 - bDIB(X, Y).rgbGreen
        bDIB(X, Y).rgbRed = 255 - bDIB(X, Y).rgbRed
  Next Y
Next X
' Setting back the Pixel Array of the DIB Section
Call SetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
' Copying the processed picture into the Picturebox
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, DC, 0, 0, SRCCOPY)
MsgBox "Operation completed in " & GetTickCount - ti & " ms"
End Sub

' The following part is the most important one in the whole script
' because if its omited then a Windows Crash(especially in NT)
' is highly expected
' Actually here we free all the memory we ever used in the script
Private Sub Form_Unload(Cancel As Integer)
Erase bDIB()
Call SelectObject(DC, oldDIB) ' Good Programming Practice
Call DeleteObject(DIBSection) ' Deleting the DIB Section
Call DeleteDC(DC)             ' Deleting the container DC
End Sub

  The script is commented alot just to make it easier for the reader to understand how we created the DIB Section and how we are using it in different ways. The normal time the Negative and Fast Grey Scale function takes on my system(AMD Sempron, 1.20 GHz, 192 MB RAM) is something like 16 ms when processing a 420x270 bitmap. Now lets discuss the creation of the DIB Section.

The Header Size is always 40 bytes but the programmer can obviously use len(BI.bmiHeader).
BI.bmiHeader.biSize = 40
The following, as the comment says, is the width of the Bitmap(in pixels). Simply the programmer is defining the dimensions of the bitmap he want in the next two lines.
BI.bmiHeader.biWidth = Picture1.ScaleWidth     ' Picture Width
Device Independent Bitmaps are always upside-down, so to get it in the correct allignment we specify a (-) minus sign with the height. If you dont understand what I just said then just make is compulsory to put the minus sign with the height.
BI.bmiHeader.biHeight = -Picture1.ScaleHeight
biPlanes specifies the number of planes for the target device. This value must be set to 1.
BI.bmiHeader.biPlanes = 1
The following line makes a lot of meaning in this whole procedure. Actually this is the main thing that Windows needs to create the DIB. This specifies the number of bits-per-pixel. It determines the number of bits that define each pixel and the maximum number of colors in the bitmap. This member must be 32 in our case. I dont want to confuse the reader thats why not dealing with the 24Bit Image structure.
BI.bmiHeader.biBitCount = 32
Calculating the size of the Bitmap is one of the most painful part of the code, I feel. Just one miss you do and your code wont work. Actually in the following line we are not only calculating the size, rather we are also making it compatible with 32 bit boundary. This is neccessary so do it in the same way in your projects.
BI.bmiHeader.biSizeImage = (((Picture1.ScaleWidth * 3) + 3) And &HFFFFFFFC) * Picture1.ScaleHeight
For practical programmer the following variable's value is of more importance than the real DIB Section. By good programing  techniques we can get the DEFAULT array of the Bitmap, and then need of refreshing the array and the DIB becomes meaningless. But at this level this is not usefull to you. Just dont confuse with this variable and believe that this is another setting.
Dim addr As Long
In the comments I said we are now finally making an EMPTY DIB Section. Empty really means empty, meaning that its Pixel Array contains nothing. Its just like a newly constructed house which is not painted yet.
DIBSection = CreateDIBSection(DC, BI, DIB_RGB_COLORS, addr, 0, 0)

   By now we have seen how we can create an EMPTY DIB Section. Now we'll see how to paint our empty house. This is done by simple BitBlt API (Bit-Block Transfer) which is nothing but the VB PaintPicture command but very faster than the VB command. Actually we are just copying the image present in the Picturebox to the EMPTY DIB Section. Its like our Picturebox is the Paint Box and BitBlt API is the brush, by using which we paint the empty house.
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)

Now how to access the pixels in the DIB Section. A very common way is always to go through a fool's way, i.e. GetPixel & SetPixel technique. I'll not waste time in describing that part of the script. I'll directly jump to the part where we get the pixel array and process it directly like good professional programmers.

First we are using the BitBlt to copy the image in the Picturebox to the EMPTY DIB Section.
Call BitBlt(DC, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, Picture1.hdc, 0, 0, SRCCOPY)
Now we are defining our local array to the dimensions of the bitmap. This is neccessary.
ReDim bDIB(1 To Picture1.ScaleWidth, 1 To Picture1.ScaleHeight) As RGBQUAD
Finally Getting the Pixel Array from the PAINTED DIB Section to our local bDIB() array.
Call GetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
Now going through each and every pixel to process it.
For X = 1 To Picture1.ScaleWidth
  For Y = 1 To Picture1.ScaleHeight
The following is the standard filter for making a picture look its negative.

        bDIB(X, Y).rgbBlue = 255 - bDIB(X, Y).rgbBlue
        bDIB(X, Y).rgbGreen = 255 - bDIB(X, Y).rgbGreen
        bDIB(X, Y).rgbRed = 255 - bDIB(X, Y).rgbRed

And now setting back a copy of our local processed pixel array (bDIB()) to the original DIB Section Pixel Array. This is the way we refresh the DIB Section original array. This is the most annoying part of doing Bits Editing in the way I did it here. I obviously have another very very easy and 10 times faster way to do it using memory manipulation but that would not be a wise idea to introduce it at this moment to the reader of all level.
Call SetDIBits(DC, DIBSection, 0&, Picture1.ScaleHeight, bDIB(1, 1), BI.bmiHeader, 0&)
Everything done. The DIB Section origianl array is refreshed according to our local processed array. So now we'll copy the entire image of the DIB Section to the Picturebox.
Call BitBlt(Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, DC, 0, 0, SRCCOPY)

   The comments that the most important job of a good programmer is to remove all the memory space he ever acquired in his script at the end of the program. So we are gonna do it in the Form_Unlod subroutine.

Somebody once said to me that erasing the VB made arrays is not that important as VB will do the job itself, but I beleive if I must create it then I must destroy it in the end.
Erase bDIB()
In the following line we select out the DIB Section of the DC so that we can delete the DC( Deleting the empty DC is a good programing practice).
Call SelectObject(DC, oldDIB)
Now in the following two lines, we are deleting the DIB Secion using DeleteObject and then deleting the DC using DeleteDC. This actually tells you that yes DIB Section is a Windows object and you are required to delete it in the end.
Call DeleteObject(DIBSection) ' Deleting the DIB Section
Call DeleteDC(DC)             ' Deleting the container DC


  So by now I suppose I described the whole lot of code good. And was able to make you understand. This thing is very easy to do and Windows provide a lot of help to the developers ( novice to expert ) in many means. Like there are articles, example (in your favourite languages), MSDN Library, and very comprehensive forums, all available at the Microsoft site. Just kick start your mind to go in this direction who knows Windows will introduce something better than DIB Section in Windows Vista.

[Edited by Asim-GDI GURU on 17-07-2006 at 04:13 AM GMT]

16-07-2006 at 11:12 PM
View Profile Send Email to User Show All Posts | Add Comment
AndreaVB OnLine : Articles and tutorials : DIB SECTION
Previous Topic (Ways of doing Image Processing)Next Topic (String Handling in Java)New Topic Post Reply
Surf To:


Not Logged In? Username: Password: Lost your password?
Partners: Download Actual Software | Free Software Download
borderAndreaVB free resources for Visual Basic developersborder

borderAndreaVB Visual Basic and VB.NET source code resources - Copyright © 1999-2009 Andrea Tincaniborder