Link to home
Start Free TrialLog in
Avatar of cyberknet
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.
Avatar of Rubyn
Rubyn

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 Const SRCCOPY = &HCC0020

Private Function CapturePicture(Obj As PictureBox) As StdPicture
Dim picTemp As PictureBox
    Set picTemp = Controls.Add("VB.PictureBox", "PicTemp")
    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(CapturePicture(Picture1), 0, 0, 100, 100)

I Hope this solves your problem. :)
Avatar of cyberknet

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)

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(mlngBitmap(mlngActiveBitmap), hDC) ' renders a hBitmap to a DC
    Refresh
       
    Set pctTemp = Controls.Add("VB.PictureBox", "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
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.
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
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
Avatar of Rubyn
Rubyn

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
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,ToMode) 'For Unit Conversion
ScaleY(Value,FromMode,ToMode)

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.