PDA

Click to See Complete Forum and Search --> : [VB] stdPicture Render (How To)


LaVolpe
Apr 4th, 2010, 01:48 PM
What is the .Render (http://msdn.microsoft.com/en-us/library/aa244163(v=VS.60).aspx) method of the stdPicture object and how does one use it.

FYI: a stdPicture object is either an object declared as stdPicture or any VB .Picture/.Image property, including those of forms, image controls, pictureboxes, etc. Some other properties may be stdPicture objects too, like a command button's .DisabledPicture property or a form's .Icon property.

:rolleyes: The .Render method has all the benefits of VB's PaintPicture method, except one, and has a few additional benefits that VB's PaintPicture does not. In short, you have some more flexibility with .Render, but its usage is not widely known or understood.

:confused: What does PaintPicture do that .Render cannot? It can render a stdPicture object with different raster operations, i.e., vbSrcPaint, vbSrcErase, etc. The .Render method cannot by itself. SetROP2 (http://msdn.microsoft.com/en-us/library/aa453651.aspx) API can do the job though.

:thumb: What does .Render do that PaintPicture cannot? A few things
1. It can render to any DC, memory DC, DC returned by API, or VB .hDC property (major advantage)
2. It can render transparent GIFs, maintaining transparency (minor advantage)
3. It is faster because less VB overhead is involved

;) What APIs do PaintPicture & .Render replicate? BitBlt, StretchBlt, DrawIcon, DrawIconEx and metafile rendering. So you see, you have some powerful API replication in some common VB methods.

:( Why is .Render not understood well? Because it is harder to use and PaintPicture can suffice a vast majority of the time and is easier to use. To understand .Render, you need to understand a bit about bitmaps and .Render's parameter requirements
a. Destination parameters for .Render are in pixels
b. Source parameters for .Render are in himetrics
c. stdPicture objects are DIBs, stored upside down. So source parameters must be flipped vertically when rendering.

:cool: Knowing the above, one can build a custom Render function using a stdPicture object. The following includes options for: stretching, scaling, offsetting, centering, partial rendering, and user-defined scalemodes.

Simply copy & paste to your project. Commented well in the code.
Tip: To make copying easier, hit the "Quote" button below & copy from that window.
Private Function RenderStdPicture(theTarget As Variant, thePic As StdPicture, _
Optional ByVal destX As Long, Optional ByVal destY As Long, _
Optional ByVal destWidth As Long, Optional ByVal destHeight As Long, _
Optional ByVal srcX As Long, Optional ByVal srcY As Long, _
Optional ByVal srcWidth As Long, Optional ByVal srcHeight As Long, _
Optional ByVal ParamScaleMode As ScaleModeConstants = vbUser, _
Optional ByVal Centered As Boolean = False, Optional ByVal ZoomFactor As Single = 1&) As Boolean

' Return Value [out]
' If no errors occur, return value is True. If error or invalid parameters passed, value is False
' Parameters [in]
' theTarget: a VB form, picturebox, usercontrol or a valid hDC (no error checking for valid DC)
' ... If Object, then it must expose a ScaleMode and hDC property
' ... and if centering and an object, must also expose ScaleWidth & ScaleHeight properties
' thePic: a VB ImageControl, stdPicture object, or VB .Picture property
' destX: horizontal offset on theTarget where drawing begins, default is zero
' destY: vertical offset on theTarget where drawing begins, default is zero
' destWidth: rendered image width & will be multiplied against ZoomFactor; default is thePic.Width
' destHeight: rendered image height & will be multiplied against ZoomFactor; default is thePic.Height
' srcX: horizontal offset of thePic to begin rendering from; default is zero
' srcY: vertical offset of thePic to begin rendering from; default is zero
' srcWidth: thePic width that will be rendered; default is thePic.Width
' srcHeight: thePic height that will be rendered; default is thePic.Height
' ParamScaleMode: Scalemode for passed parameters.
' If vbUser, then theTarget scalemode is used if theTarget is an Object else vbPixels if theTarget is an hDC
' Centered: If True, rendered image is centered in theTarget, offset by destX and/or destY
' If theTarget is a DC, then Centered is ignored. You must pass the correct destX,destY values
' ZoomFactor: Scaling option. Values>1 zoom out and Values<1||>0 zoom in

' Tip: To stretch image to a picturebox dimensions, pass destWidth & destHeight
' as the picturebox's scalewidth & scaleheight respectively and ZoomFactor of 1

If thePic Is Nothing Then Exit Function ' sanity checks first
If thePic.Handle = 0& Then Exit Function
If ZoomFactor <= 0! Then Exit Function

Dim Width As Long, Height As Long, destDC As Long

' the stdPicture.Render method requires vbPixels for destination and vbHimetrics for source
Width = ScaleX(thePic.Width, vbHimetric, vbPixels) ' image size in pixels
Height = ScaleY(thePic.Height, vbHimetric, vbPixels)

On Error Resume Next
If IsObject(theTarget) Then ' passed object? If so, set scalemode if needed
If theTarget Is Nothing Then Exit Function
If ParamScaleMode = vbUser Then ParamScaleMode = theTarget.ScaleMode
destDC = theTarget.hDC
ElseIf IsNumeric(theTarget) Then ' passed hDC? If so, set scalemode if needed
If ParamScaleMode = vbUser Then ParamScaleMode = vbPixels
destDC = Val(theTarget)
Centered = False ' only applicable if theTarget is a VB object
Else
Exit Function ' unhandled; abort
End If
If Err Then ' checks above generated an error; probably passing object without scalemode property?
Err.Clear
Exit Function
End If

If destWidth Then ' calculate destination width in pixels from ParamScaleMode
destWidth = ScaleX(destWidth, ParamScaleMode, vbPixels) * ZoomFactor
Else
destWidth = Width * ZoomFactor
End If
If destHeight Then 'calculate destination height in pixels from ParamScaleMode
destHeight = ScaleY(destHeight, ParamScaleMode, vbPixels) * ZoomFactor
Else
destHeight = Height * ZoomFactor
End If
' get destX,destY in pixels from ParamScaleMode
If destX Then destX = ScaleX(destX, ParamScaleMode, vbPixels)
If destY Then destY = ScaleY(destY, ParamScaleMode, vbPixels)
If Centered Then ' Offset destX,destY if centering
destX = (ScaleX(theTarget.ScaleWidth, theTarget.ScaleMode, vbPixels) - destWidth) / 2 + destX
destY = (ScaleY(theTarget.ScaleHeight, theTarget.ScaleMode, vbPixels) - destHeight) / 2 + destY
End If
' setup source coords/bounds and convert to vbHimetrics
If srcX Then srcX = ScaleX(srcX, ParamScaleMode, vbHimetric)
If srcY Then srcY = ScaleY(srcY, ParamScaleMode, vbHimetric)
If srcWidth Then srcWidth = ScaleX(srcWidth, ParamScaleMode, vbHimetric) Else srcWidth = thePic.Width
If srcHeight Then srcHeight = ScaleY(srcHeight, ParamScaleMode, vbHimetric) Else srcHeight = thePic.Height

If Err Then ' passed bad parameters or
Err.Clear ' passed object that has no ScaleMode property (i.e., VB Frame)
Else
With thePic ' render, the "Or 0&" below are required else mismatch errors occur
.Render destDC Or 0&, destX Or 0&, destY Or 0&, destWidth Or 0&, destHeight Or 0&, _
srcX Or 0&, .Height - srcY, srcWidth Or 0&, -srcHeight, ByVal 0&
End With ' return success/failure
If Err Then Err.Clear Else RenderStdPicture = True
End If
On Error GoTo 0

End Function

Edited: Short version, no resizing, scaling, positioning, or any other options. Just draw picture at 0,0 coordinates on any DC
' destDC can be any DC
Dim thePic As stdPicture
Set thePic = LoadPicture("C:\Images\Screenshots\image1002.jpg")
With thePic
.Render destDC Or 0&, 0&, 0&, ScaleX(.Width, vbHimetric, vbPixels), ScaleY(.Height, vbHimetric, vbPixels), _
0&, .Height, .Width, -.Height, ByVal 0&
End With

Merri
Aug 21st, 2010, 02:48 PM
Here is a more compact solution than the Or 0&:

Option Explicit

Dim m_Pic As StdPicture

Private Sub Form_Load()
Set m_Pic = LoadPicture(App.Path & "\Test.gif")
End Sub

Private Sub Form_Paint()
Dim Width As Long, Height As Long
' convert himetric to pixels
Width = Me.ScaleX(m_Pic.Width, vbHimetric, vbPixels)
Height = Me.ScaleY(m_Pic.Height, vbHimetric, vbPixels)
' render normal
m_Pic.Render Me.hDC, 0, 0, --Width, --Height, 0, m_Pic.Height, m_Pic.Width, -m_Pic.Height, ByVal 0&
' render mirrored
m_Pic.Render Me.hDC, --Width, 100, -Width, --Height, 0, m_Pic.Height, m_Pic.Width, -m_Pic.Height, ByVal 0&
End Sub

It seems passing variables is somehow troublesome, I don't really understand why it would give the Type Mismatch error, but using -- is good enough to get rid of it.


Sample attachment project here (http://www.vbforums.com/showthread.php?p=3869078#post3869078).

Merri
Aug 22nd, 2010, 05:33 AM
Here is a short ready-to-go function and a sample attachment project. This project is especially a sample on how to draw a transparent GIF.

Private Sub DrawSprite(ByVal TargetDC As Long, Sprite As StdPicture, ByVal X As Long, ByVal Y As Long, Optional ByVal Center As Boolean, Optional ByVal Mirror As Boolean, Optional ByVal Flip As Boolean)
Dim Width As Long, Height As Long
' convert himetric to pixels, manual edition
Width = ((m_Sprite.Width / 2540) * 1440) \ Screen.TwipsPerPixelX
Height = ((m_Sprite.Height / 2540) * 1440) \ Screen.TwipsPerPixelY
' center sprite on X & Y?
If Center Then X = X - Width \ 2: Y = Y - Height \ 2
' need flip or mirror?
If Flip Then Y = Y + Height: Height = -Height
If Mirror Then X = X + Width: Width = -Width
' draw it (the -- trick fixes the Type Mismatch error)
Sprite.Render --TargetDC, --X, --Y, --Width, --Height, _
0, Sprite.Height, Sprite.Width, -Sprite.Height, ByVal 0&
End Sub

I noticed ScaleX & ScaleY throw off the pixel values a bit too much for my taste, it seems doing calculation using \ operator for the final math works faster and gives a bit nicer looking code than forcing rounding up.

For comparison, here is the old Width & Height calculation: ' convert himetric to pixels (always round up using negative for Int)
Width = -Int(-Me.ScaleX(m_Sprite.Width, vbHimetric, vbPixels))
Height = -Int(-Me.ScaleY(m_Sprite.Height, vbHimetric, vbPixels))

LaVolpe
Aug 23rd, 2010, 08:41 AM
It seems passing variables is somehow troublesome, I don't really understand why it would give the Type Mismatch error, but using -- is good enough to get rid of it.
Any method that forces VB to re-evaluate the variables will work
--Width
Width Or 0&
Width \ 1&
etc, etc

More compact solution? I prefer the Or 0& method. Easier to read, less confusion in my opinion & possibly less cpu cycles (though not interested to prove/disprove it).

This is even more compact and far easier on the eyes:

Sprite.Render (TargetDC), (X), (Y), (Width), (Height), _
0, Sprite.Height, Sprite.Width, -Sprite.Height, ByVal 0&

Merri
Aug 23rd, 2010, 08:58 AM
It isn't any shorter (same amount of characters), but it probably confuses less most people :) For me the Or 0& is harder to read and it probably takes longer to understand for a random guy than --. But () wins being best for the eyes. -- wins for using least pixels representing a character on the screen! :D

Too bad it looks like Render is one of those hugely underused functions only because it is a little hard to deal with. Maybe I'll try do a small game without a single graphical API declaration just for the fun of it, some day :)

ThePiper
Nov 30th, 2010, 03:31 PM
What is the .Render (http://msdn.microsoft.com/en-us/library/aa244163(v=VS.60).aspx) method of the stdPicture object and how does one use it.

Thank you, LaVolpe and Merri, for this excellent lesson! :)