Go Back  Xtreme Visual Basic Talk > Legacy Visual Basic (VB 4/5/6) > Interface and Graphics > Saving Picturebox image as black & white (1-bit) bitmap file


Reply
 
Thread Tools Display Modes
  #1  
Old 07-12-2003, 01:41 AM
A-Dam A-Dam is offline
Junior Contributor
 
Join Date: Nov 2002
Posts: 361
Default Saving Picturebox image as black & white (1-bit) bitmap file


The GetBitmapBits function is giving me trouble. I have a program that generates reports and sends the output to a Picturebox for viewing and/or the Printer for printing (duh). So the customer can save a copy of the report, I use SavePicture on the Picturebox's Image property. A report of about 8 pages results in a large (up to 600 x 7000 pixel) bitmap. This file would be 10 megs when created with the SavePicture function. I hate wasting that much disk space (even if it is the customer's disk)! A 1-bit bitmap results in a file about 400KB, almost as compact as a gif image. Everything works, except the byte array returned by GetBitmapBits is not always Long-aligned. I have to adjust the bitmap width to get the scan lines padded on the 4-byte boundary. I think that it is Integer-aligning, but I can't seem to make a valid bitmap file unless the scan lines are multiples of 4 bytes. The code is below. I hope this thread posts.

Code:
'' General declarations section (omitted) has '' BITMAP type and API functions declared Private Sub SavePictureBW() Dim hdcMono As Long, hbmpMono As Long, hbmpOld As Long, dxBlt As Long, dyBlt As Long, success As Long Dim num As Integer Dim bmpsrc As BITMAP Dim bitmaparray() As Byte ' Picturebox's ScaleMode is set to pixels ' Get the dimensions dxBlt = pbSrc.ScaleWidth dyBlt = pbSrc.ScaleHeight ' Create memory device context hdcMono = CreateCompatibleDC(0) ' Create monochrome bitmap hbmpMono = CreateCompatibleBitmap(hdcMono, dxBlt, dyBlt) ' Use GetObject to fill bmpsrc with bitmap info success = GetBitmapObject(hbmpMono, LenB(bmpsrc), bmpsrc) '''''''''''''''''''<<<< ' This code makes sure scan lines get padded correctly ' I would like to avoid changing the bitmap's width, but ' I can't figure out a better way Do While (bmpsrc.bmWidthBytes Mod 4) ' Delete bitmap before creating another Call DeleteObject(hbmpMono) ' Reduce bitmap width until it is Dword aligned dxBlt = dxBlt - 1 ' Create new mono bitmap hbmpMono = CreateCompatibleBitmap(hdcMono, dxBlt, dyBlt) ' Use GetObject to fill bmpsrc with bitmap info success = GetBitmapObject(hbmpMono, LenB(bmpsrc), bmpsrc) Loop '''''''''''''''''''<<<< hbmpOld = SelectObject(hdcMono, hbmpMono) ' Copy picturebox's bitmap to DC to create mono mask ' Bitmap must be flipped vertically with StretchBlt or byte array will be "upside-down" success = StretchBlt(hdcMono, 0, 0, dxBlt, dyBlt, pbSrc.hdc, 0, dyBlt - 1, dxBlt, -dyBlt, SRCCOPY) ReDim bitmaparray(1 To (bmpsrc.bmWidthBytes * bmpsrc.bmHeight)) ' Fill the byte array with the bit data success = GetBitmapBits(hbmpMono, UBound(bitmaparray), bitmaparray(1)) ' Here you would create the bitmap's file header data ' and save it all to disk as the finished black & white bitmap ' Clean up ' Select old bitmap back into dc first Call SelectObject(hdcMono, hbmpOld) Call DeleteDC(hdcMono) Call DeleteObject(hbmpMono) MsgBox "done" End Sub


Thanks for any help.
__________________
It looks like ketchup; it tastes like ketchup; but brother, it ain't ketchup!

Last edited by A-Dam; 07-12-2003 at 09:38 AM.
Reply With Quote
  #2  
Old 07-12-2003, 09:50 AM
A-Dam A-Dam is offline
Junior Contributor
 
Join Date: Nov 2002
Posts: 361
Default

No reply yet? I know some of you are bitmap gurus. You have "Blit" for a middle name. Any ideas? Will GetDiBits always Dword align?
Please Help.
__________________
It looks like ketchup; it tastes like ketchup; but brother, it ain't ketchup!
Reply With Quote
  #3  
Old 07-12-2003, 10:34 AM
beachcomber beachcomber is offline
Junior Contributor
 
Join Date: Jul 2003
Posts: 274
Default

Save the file in some compressed format such as jpg.

You can find the dll and bas modules for the declarations for the intell library at planet source code. The early versions which are posted are royality free and require no specific licensing requirements for use.

But I don't understand why you don't send your report to a control which is a bit more gdi friendly such as the richtextbox. You already are having to have to format the text - then you must play with the picturepox.image and hdc properties to refresh and paint etc..

Printing to the richtextbox, for instance, is just a manner of formatting you text into strings and sending it to the control. No gdi interfacing is required. Then just send the report to the printer, save the file with the control's built in features. That way the report can be viewed in a number of text applications and via a graphic application.

Using the flexgrid for instance has a picture property that can be set to monochrome (black & white) - that is an other option.

Last edited by beachcomber; 07-12-2003 at 10:44 AM.
Reply With Quote
  #4  
Old 07-12-2003, 11:27 PM
A-Dam A-Dam is offline
Junior Contributor
 
Join Date: Nov 2002
Posts: 361
Default

I knew someone would say to convert to jpeg or gif. I read up on the Intel Jpeg library quite a while ago. Back then I heard that it is no longer free, but that you can still find it and download it from certain places. I sold my program to a customer that does regular audits to make sure that there is no pirated software on the company computers. I wanted to avoid third-party controls.
I was considering using a richtextbox when I designed the program. You have good control over text formatting, and the ability to save as RTF files is definitely sweet. But I decided against the richtextbox for a few reasons. My reports are only in black and white. They don't need multiple font colors or faces or sizes (which you could get just as easily with a picturebox). One of the reports is typically between 5 and 10 pages. I output that to a scrollable Picturebox. I don't know if you will have troubles by making a richtextbox 100,000 twips in height. Probably not, but the Picturebox control gives the same output to the screen as you will get when you send that output to the printer. I've attached an example of my output:

You said:
Quote:
Printing to the richtextbox, for instance, is just a manner of formatting you text into strings and sending it to the control. No gdi interfacing is required. Then just send the report to the printer...
How would you handle page headers or keeping detail groups from being split to separate pages?

Quote:
You already are having to have to format the text - then you must play with the picturepox.image and hdc properties to refresh and paint etc..
My reports basically consist of Print and CurrentX statements. I don't have to refresh and paint. I only use the image and hdc properties when I am creating the bitmap file that this thread was addressing. If that Intel library will create a jpeg file from a picturebox's image, and if there are no licensing problems, then I might look into it. Thanks for the suggestions.
BUT, does anyone know about getting a bitmap's bits into a properly aligned array?
Attached Images
File Type: gif GPhoursreport5_23_03 10'26'32 AM.gif (7.3 KB, 16 views)
__________________
It looks like ketchup; it tastes like ketchup; but brother, it ain't ketchup!

Last edited by A-Dam; 07-12-2003 at 11:33 PM.
Reply With Quote
  #5  
Old 07-13-2003, 09:17 AM
beachcomber beachcomber is offline
Junior Contributor
 
Join Date: Jul 2003
Posts: 274
Default

Ok - the older versions of the library are still free. If you go the the Intell website and do a bit of checking you will see that the earlier version is still free. They have resigned their control and changed its name for the newer version that requires licensing.

The older version usually is packed with the license agreement for the dll and that should address any issues your customer may have.

However ...

If there are issues then you can use a control that is shipped with all versions of windows (I don't know about xp etc) or can be downloaded from microsoft - imaging for windows.

It contains four controls basically and you can use them to save and change images. In fact a simple matter of some property settings to get a monochrome image conversion.

You can find them in the project component manager

Kodak Image Admin Control
Kodak Image Edit Control
Kodak Image Scan Control 'use to interface with a scanning device
Kodak Image Thumbnail Control

In your version of msdn you will find a complete manual (I mean manual) on using these controls.

You don't have to include these controls in your distribution package as they should be on all platforms after win95 automatically.

I don't like these controls myself as there are easier methods - but they can do what you want by setting a few properties, methods, and procedures built into them.
Reply With Quote
  #6  
Old 08-19-2003, 03:13 AM
A-Dam A-Dam is offline
Junior Contributor
 
Join Date: Nov 2002
Posts: 361
Default

Here's a procedure for saving a picturebox's image as a black & white bmp file. I finally got back to that old project and figured it out. GetBitmapBits wasn't getting the bits like it should, at least I couldn't get it to work. GetDIBits did the trick.
This works well for saving text. All white pixels are saved as white. Anything other than white is saved as black. It doesn't do greyscale or dithering, so photos will come out pretty much all black. The suggestion to use the Intel jpeg library didn't suit my purpose. Saving a black & white image as a jpg still resulted in a huge file size. This procedure creates a file about 24 times smaller than the SavePicture statement.
Maybe someone can find use for it, so here it is.

Code:
Private Sub SavePictureBW(ByVal ctrl As PictureBox, ByVal destfile As String) Dim hdcMono As Long, hbmpMono As Long, hbmpOld As Long, dxBlt As Long, dyBlt As Long, success As Long Dim numscans As Long, byteswide As Long, totalbytes As Long, lfilesize As Long Dim bmpsrc As BITMAP, bmpdst As BITMAP Dim bInfo As BITMAPINFO Dim bitmaparray() As Byte, fileheader() As Byte Dim ff As Integer 'Object's scalemode must be Pixel. dxBlt = ctrl.ScaleWidth dyBlt = ctrl.ScaleHeight 'Create monochrome bitmap from control. hdcMono = CreateCompatibleDC(0) hbmpMono = CreateCompatibleBitmap(hdcMono, dxBlt, dyBlt) success = GetBitmapObject(hbmpMono, Len(bmpsrc), bmpsrc) hbmpOld = SelectObject(hdcMono, hbmpMono) success = BitBlt(hdcMono, 0, 0, dxBlt, dyBlt, ctrl.hdc, 0, 0, SRCCOPY) 'Calculate array size needed for bitmap bits (dword aligned) numscans = dyBlt by8 = dxBlt / 8 If (dxBlt Mod 8) = 0 And (by8 Mod 4) = 0 Then byteswide = by8 Else byteswide = (Int(by8) + 4) - (Int(by8) Mod 4) End If totalbytes = numscans * byteswide ReDim bitmaparray(1 To totalbytes) 'Set BITMAPINFO values to pass to GetDIBits function. With bInfo .bmiHeader.biSize = Len(.bmiHeader) .bmiHeader.biWidth = bmpsrc.bmWidth .bmiHeader.biHeight = bmpsrc.bmHeight .bmiHeader.biPlanes = bmpsrc.bmPlanes .bmiHeader.biBitCount = bmpsrc.bmBitsPixel .bmiHeader.biCompression = BI_RGB End With success = GetDIBits(hdcMono, hbmpMono, 0, numscans, bitmaparray(1), bInfo, DIB_RGB_COLORS) 'bitmaparray should now contain bitmap bit data. Now create bitmap file header. ReDim fileheader(1 To &H3E) fileheader(1) = &H42 'B fileheader(2) = &H4D 'M lfilesize = UBound(fileheader) + UBound(bitmaparray) fileheader(3) = lfilesize And 255 fileheader(4) = (lfilesize \ 256) And 255 fileheader(5) = (lfilesize \ 65536) And 255 fileheader(6) = (lfilesize \ 16777216) And 255 fileheader(11) = &H3E 'offset fileheader(15) = &H28 'size of bitmapinfoheader fileheader(19) = dxBlt And 255 fileheader(20) = (dxBlt \ 256) And 255 fileheader(21) = (dxBlt \ 65536) And 255 fileheader(22) = (dxBlt \ 16777216) And 255 fileheader(23) = dyBlt And 255 fileheader(24) = (dyBlt \ 256) And 255 fileheader(25) = (dyBlt \ 65536) And 255 fileheader(26) = (dyBlt \ 16777216) And 255 fileheader(27) = 1 fileheader(29) = 1 fileheader(35) = UBound(bitmaparray) And 255 fileheader(36) = (UBound(bitmaparray) \ 256) And 255 fileheader(37) = (UBound(bitmaparray) \ 65536) And 255 fileheader(38) = (UBound(bitmaparray) \ 16777216) And 255 fileheader(47) = 2 fileheader(51) = 2 fileheader(59) = &HFF fileheader(60) = &HFF fileheader(61) = &HFF ff = FreeFile Open destfile For Binary Access Write As #ff Put #ff, , fileheader Put #ff, , bitmaparray Close #ff ' Clean up Call SelectObject(hdcMono, hbmpOld) Call DeleteDC(hdcMono) Call DeleteObject(hbmpMono) End Sub
__________________
It looks like ketchup; it tastes like ketchup; but brother, it ain't ketchup!
Reply With Quote
  #7  
Old 08-19-2003, 08:59 AM
OnErr0r's Avatar
OnErr0r OnErr0r is offline
Obsessive OPtimizer

Administrator
* Guru *
 
Join Date: Jun 2002
Location: Debug Window
Posts: 13,718
Default

For your byte alignment you could use:

by8 = (dxBlt + 7) \ 8 ' byte align bits
byteswide = (by8 + 3) And Not 3 ' dword align bytes
Reply With Quote
  #8  
Old 08-19-2003, 11:30 PM
A-Dam A-Dam is offline
Junior Contributor
 
Join Date: Nov 2002
Posts: 361
Default

Thank you. I was looking for a cleaner way to do that. In the documentation, I found this a lot:

Code:
biSizeImage = ((((biWidth * biBitCount) + 31) & ~31) >> 3) * biHeight

Since it was written for C, I didn't know how to translate the shifts into VB code. Well, I guess I did figure it out in my own way. That's what a noob is supposed to do, right?
__________________
It looks like ketchup; it tastes like ketchup; but brother, it ain't ketchup!
Reply With Quote
  #9  
Old 08-19-2003, 11:54 PM
OnErr0r's Avatar
OnErr0r OnErr0r is offline
Obsessive OPtimizer

Administrator
* Guru *
 
Join Date: Jun 2002
Location: Debug Window
Posts: 13,718
Default

That would work well too. Bitcount is 1, so I guess this would be correct:

Code:
byteswidth = ((dxBlt + 31) And Not 31) \ 8

Note that / is floating point division, and \ is integer division, suitable for shifting.

>> is right shift and each number of bits shifted is equivalent to integer division by 2 ^ number of bits.

2 ^ 1 = 2 (>> 1)
2 ^ 2 = 4 (>> 2)
2 ^ 3 = 8 (>> 3)
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Installation Problem - PLs help urgenlty dpdsouza Installation / Documentation 4 12-02-2004 07:09 PM
Doesn't want to register! MikeyM Installation / Documentation 5 03-02-2003 08:22 PM
PUblic array With images NeverMore5 Game Programming 3 02-07-2003 01:59 PM
saving bitmap info to file w/o picturebox wtlebo13 General 7 12-14-2002 11:43 AM

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
 
 
-->