cyberknet
asked on
Using StretchBlt between a form and a printer
I am trying to print TIFF images from visual basic on Windows XP. I have tried many libraries to do this. On windows 2000, I used Imaging for Windows, however it is not available in Windows XP. I have found the FreeImage library from SourceForge, and it loads the image just fine. I can even display it on the form. However, once I have it displaying on the form, I am unable to use StretchBlt to copy the image from the form's hdc to the printer's hdc... According to the MS knowledge base, this is because the devices are different. It suggests using GetDIBits and SetDIBits, but I am unsure how well this will work with the printer object.
100 points for code to transfer an image in the form's hDC (not loaded in the form's .Picture) to the printer and printed out.
100 points for code to transfer an image in the form's hDC (not loaded in the form's .Picture) to the printer and printed out.
ASKER
Perhaps I can be a bit more specific about what I have, and what I want. Sorry for any confusion.
I have a form, and the FreeImage library. I load a page from the tif image, which returns me a hBitmap of the image. I call a FreeImage function which copies the bitmap to a DC. I cannot copy it directly to the printer, it is way too big, I need to scale it first. So I am copying it to the form. So at the moment in the form's hDC we have an image.
If I am reading what you say correctly, you are telling me to add a picturebox at runtime, and blit the image in the form's hdc to the picturebox's hdc, then use PaintPicture to copy the picture from the picturebox to the printer.
Is there no way to copy the bitmap to the printer directly from the form without adding and removing the picturebox? I will be calling this procedure quite frequently, and that takes quite a bit of processing time...
any ideas?
Increased points to 105 (it's all I have)
I have a form, and the FreeImage library. I load a page from the tif image, which returns me a hBitmap of the image. I call a FreeImage function which copies the bitmap to a DC. I cannot copy it directly to the printer, it is way too big, I need to scale it first. So I am copying it to the form. So at the moment in the form's hDC we have an image.
If I am reading what you say correctly, you are telling me to add a picturebox at runtime, and blit the image in the form's hdc to the picturebox's hdc, then use PaintPicture to copy the picture from the picturebox to the printer.
Is there no way to copy the bitmap to the printer directly from the form without adding and removing the picturebox? I will be calling this procedure quite frequently, and that takes quite a bit of processing time...
any ideas?
Increased points to 105 (it's all I have)
ASKER
Maybe posting some code that I tried your method with would help too: (all variables are declared further up in the proc, which I did not copy)
' mlngBitmap() is an array of hBitmaps
' mlngActiveBitmap is the current index
' lng[Height/Width] is the bitmap [height/width]
' lngScaleHeight is the height scaled to fit on the paper
' lngScaleWidth is the width, scaled to fit on the paper
' Please contact me for any more questions pertaining
' to the code.
Call FreeImage_RenderBitmap(mln gBitmap(ml ngActiveBi tmap), hDC) ' renders a hBitmap to a DC
Refresh
Set pctTemp = Controls.Add("VB.PictureBo x", "pctTemp")
SetPicture pctTemp, lngWidth, lngHeight, lngScaleWidth, lngScaleHeight ' blits from form hDC to pctTemp hDC
' The following statement ALWAYS results i nerror 481
' Invalid Picture. I think this is because the
' width and height of pctTemp.Picture are still set to
' 0.
Printer.PaintPicture pctTemp.Picture, 0, 0
Printer.EndDoc
Controls.Remove pctTemp
Public Function SetPicture(pctPicture As PictureBox, lngWidth As Long, lngHeight As Long, lngScaleWidth As Long, lngScaleHeight As Long)
StretchBlt pctPicture.hDC, 1, 1, lngScaleWidth, lngScaleHeight, hDC, 1, 1, lngWidth, lngHeight, vbSrcCopy
End Function
' mlngBitmap() is an array of hBitmaps
' mlngActiveBitmap is the current index
' lng[Height/Width] is the bitmap [height/width]
' lngScaleHeight is the height scaled to fit on the paper
' lngScaleWidth is the width, scaled to fit on the paper
' Please contact me for any more questions pertaining
' to the code.
Call FreeImage_RenderBitmap(mln
Refresh
Set pctTemp = Controls.Add("VB.PictureBo
SetPicture pctTemp, lngWidth, lngHeight, lngScaleWidth, lngScaleHeight ' blits from form hDC to pctTemp hDC
' The following statement ALWAYS results i nerror 481
' Invalid Picture. I think this is because the
' width and height of pctTemp.Picture are still set to
' 0.
Printer.PaintPicture pctTemp.Picture, 0, 0
Printer.EndDoc
Controls.Remove pctTemp
Public Function SetPicture(pctPicture As PictureBox, lngWidth As Long, lngHeight As Long, lngScaleWidth As Long, lngScaleHeight As Long)
StretchBlt pctPicture.hDC, 1, 1, lngScaleWidth, lngScaleHeight, hDC, 1, 1, lngWidth, lngHeight, vbSrcCopy
End Function
ASKER
I earned some more points, so I am increasing the number of points to 185 for a short, clear, correct answer that does not require adding a picturebox to the form.
ASKER
for some reason It is not letting me increase to 185, even though I just earned 80 more points. sorry guys.
I guess, you have some knowledge about handling StdPicture and using it on printer. So, i can explain what i know, without example.
In Form or PictureBox there is a property called Image, which captures what is on it, as StdPicture. Picture property will return only the picture assigned to it.
So, you can use Image property to read whats on hDC to StdPicture.
You can also try BitBlt API to copy image on hDC to directly to other hDC like Printer.hDC.
(I haven't tested on printer, but should work logically)
Tips
For all API Commands define size in pixels.
Autoredraw Property must be false to read from hDC by API
In Form or PictureBox there is a property called Image, which captures what is on it, as StdPicture. Picture property will return only the picture assigned to it.
So, you can use Image property to read whats on hDC to StdPicture.
You can also try BitBlt API to copy image on hDC to directly to other hDC like Printer.hDC.
(I haven't tested on printer, but should work logically)
Tips
For all API Commands define size in pixels.
Autoredraw Property must be false to read from hDC by API
ASKER
You cannot bitblt from form hDC to printer hDC. They are different device types. How do we read from hDC to StdPicture? If I can get what is in the hDC to the StdPicture (bitblt does not do this) then I can use PaintPicture on the printer object, which will remove any need for the solution to employe GetDIBits / SetDIBits, which are very difficult to code with.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
form.Image does exactly what I need. Too bad that I now find out my image library does not work as expected. However, the solution was right. Thank you for your help.
For those who search later:
Blit to a hDC. (picturebox is best, a form's Image property is limited to the width of the form, not the width of the image)
set the Picture property of that object to its Image property. Image reads from the hDC of the object, not it's Picture object.
Use Printer.PaintPicture to copy it from the object to the printer.
this is so blinding simple, I can't believe I didn't know it. Thanks to Rubyn for pointing this out.
For those who search later:
Blit to a hDC. (picturebox is best, a form's Image property is limited to the width of the form, not the width of the image)
set the Picture property of that object to its Image property. Image reads from the hDC of the object, not it's Picture object.
Use Printer.PaintPicture to copy it from the object to the printer.
this is so blinding simple, I can't believe I didn't know it. Thanks to Rubyn for pointing this out.
Thanks, You explained well for others.
But have clear idea to print it on printer. So u dont have to waste papers. :)
'Pixels value of Screen and Printer may be different but Twips are same.
'Pixel is the smallest possible dot on any device
printer.TwipsPerPixelX 'Resolution of Printer '4 for 360dpi
printer.TwipsPerPixelY
Screen.TwipsPerPixelX 'Resolution of Screen. Normally 15
Screen.TwipsPerPixelY
Printer.Width 'Paper Width & Height in Twips
Printer.Height
Printer.ScaleWidth 'Printable Area Scaled by ScaleMode
Printer.ScaleHeight
ScaleX(Value,FromMode,ToMo de) 'For Unit Conversion
ScaleY(Value,FromMode,ToMo de)
Refer MSDN for all properties to get clear idea. But ScaleMode property is not supported in VB.NET, in VB.NET all devices are measured only in pixels.
But have clear idea to print it on printer. So u dont have to waste papers. :)
'Pixels value of Screen and Printer may be different but Twips are same.
'Pixel is the smallest possible dot on any device
printer.TwipsPerPixelX 'Resolution of Printer '4 for 360dpi
printer.TwipsPerPixelY
Screen.TwipsPerPixelX 'Resolution of Screen. Normally 15
Screen.TwipsPerPixelY
Printer.Width 'Paper Width & Height in Twips
Printer.Height
Printer.ScaleWidth 'Printable Area Scaled by ScaleMode
Printer.ScaleHeight
ScaleX(Value,FromMode,ToMo
ScaleY(Value,FromMode,ToMo
Refer MSDN for all properties to get clear idea. But ScaleMode property is not supported in VB.NET, in VB.NET all devices are measured only in pixels.
Private Const SRCCOPY = &HCC0020
Private Function CapturePicture(Obj As PictureBox) As StdPicture
Dim picTemp As PictureBox
Set picTemp = Controls.Add("VB.PictureBo
With picTemp
.Width = Obj.Width
.Height = Obj.Height
.AutoRedraw = True
End With
With Obj
Call BitBlt(picTemp.hDC, 0, 0, _
.ScaleX(.ScaleWidth, .ScaleMode, vbPixels), _
.ScaleY(.ScaleHeight, .ScaleMode, vbPixels), .hDC, 0, 0, SRCCOPY)
End With
Set CapturePicture = picTemp.Image
Call Controls.Remove(picTemp)
End Function
Private Sub Command1_Click()
Let Picture2.Picture = CapturePicture(Picture1)
End Sub
'To run this example
'Create Two picture boxes and one command button
'Place your image control or any control on picture1
'Copy this code to form1
'Run it
'You are having a control which have the image you want.
'You have to place that control in a PictureBox with AutoRedraw property is False
'Call this CapturePicture(PictureBox holding your image control) returns Picture
'You can use the Picture with any command uses StdPicture like PaintPicture
'Using PaintPicture you can draw this Picture to Printer Object
'Ex: Call Printer.PaintPicture(Captu
I Hope this solves your problem. :)