Results 1 to 21 of 21

Thread: [VB] stdPicture Render (How To)

  1. #1

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    [VB] stdPicture Render (How To)

    What is the .Render 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.

    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.

    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 API can do the job though.

    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.

    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.
    vb Code:
    1. Private Function RenderStdPicture(theTarget As Variant, thePic As StdPicture, _
    2.                              Optional ByVal destX As Single, Optional ByVal destY As Single, _
    3.                              Optional ByVal destWidth As Single, Optional ByVal destHeight As Single, _
    4.                              Optional ByVal srcX As Single, Optional ByVal srcY As Single, _
    5.                              Optional ByVal srcWidth As Single, Optional ByVal srcHeight As Single, _
    6.                              Optional ByVal ParamScaleMode As ScaleModeConstants = vbUser, _
    7.                              Optional ByVal Centered As Boolean = False, Optional ByVal ZoomFactor As Single = 1&) As Boolean
    8.                                    
    9.     ' Return Value [out]
    10.     '   If no errors occur, return value is True. If error or invalid parameters passed, value is False
    11.     ' Parameters [in]
    12.     '   theTarget: a VB form, picturebox, usercontrol or a valid hDC (no error checking for valid DC)
    13.     '       ... If Object, then it must expose a ScaleMode and hDC property
    14.     '       ... and if centering and an object, must also expose ScaleWidth & ScaleHeight properties
    15.     '   thePic: a VB ImageControl, stdPicture object, or VB .Picture property
    16.     '   destX: horizontal offset on theTarget where drawing begins, default is zero
    17.     '   destY: vertical offset on theTarget where drawing begins, default is zero
    18.     '   destWidth: rendered image width & will be multiplied against ZoomFactor; default is thePic.Width
    19.     '   destHeight: rendered image height & will be multiplied against ZoomFactor; default is thePic.Height
    20.     '   srcX: horizontal offset of thePic to begin rendering from; default is zero
    21.     '   srcY: vertical offset of thePic to begin rendering from; default is zero
    22.     '   srcWidth: thePic width that will be rendered; default is thePic.Width
    23.     '   srcHeight: thePic height that will be rendered; default is thePic.Height
    24.     '   ParamScaleMode: Scalemode for passed parameters.
    25.     '       If vbUser, then theTarget scalemode is used if theTarget is an Object else vbPixels if theTarget is an hDC
    26.     '   Centered: If True, rendered image is centered in theTarget, offset by destX and/or destY
    27.     '       If theTarget is a DC, then Centered is ignored. You must pass the correct destX,destY values
    28.     '   ZoomFactor: Scaling option. Values>1 zoom out and Values<1||>0 zoom in
    29.    
    30.     ' Tip: To stretch image to a picturebox dimensions, pass destWidth & destHeight
    31.     '   as the picturebox's scalewidth & scaleheight respectively and ZoomFactor of 1
    32.                                    
    33.     If thePic Is Nothing Then Exit Function                 ' sanity checks first
    34.     If thePic.Handle = 0& Then Exit Function
    35.     If ZoomFactor <= 0! Then Exit Function
    36.    
    37.     Dim Width As Single, Height As Single, destDC As Long
    38.    
    39.     ' the stdPicture.Render method requires vbPixels for destination and vbHimetrics for source
    40.     Width = ScaleX(thePic.Width, vbHimetric, vbPixels)      ' image size in pixels
    41.     Height = ScaleY(thePic.Height, vbHimetric, vbPixels)
    42.    
    43.     On Error Resume Next
    44.     If IsObject(theTarget) Then         ' passed object? If so, set scalemode if needed
    45.         If theTarget Is Nothing Then Exit Function
    46.         If ParamScaleMode = vbUser Then ParamScaleMode = theTarget.ScaleMode
    47.         destDC = theTarget.hDC
    48.     ElseIf IsNumeric(theTarget) Then    ' passed hDC? If so, set scalemode if needed
    49.         If ParamScaleMode = vbUser Then ParamScaleMode = vbPixels
    50.         destDC = Val(theTarget)
    51.         Centered = False                ' only applicable if theTarget is a VB object
    52.     Else
    53.         Exit Function                   ' unhandled; abort
    54.     End If
    55.     If Err Then                         ' checks above generated an error; probably passing object without scalemode property?
    56.         Err.Clear
    57.         Exit Function
    58.     End If        
    59.  
    60.     If destWidth Then                   ' calculate destination width in pixels from ParamScaleMode
    61.         destWidth = ScaleX(destWidth, ParamScaleMode, vbPixels) * ZoomFactor
    62.     Else
    63.         destWidth = Width * ZoomFactor
    64.     End If
    65.     If destHeight Then                  'calculate destination height in pixels from ParamScaleMode
    66.         destHeight = ScaleY(destHeight, ParamScaleMode, vbPixels) * ZoomFactor
    67.     Else
    68.         destHeight = Height * ZoomFactor
    69.     End If
    70.                                         ' get destX,destY in pixels from ParamScaleMode
    71.     If destX Then destX = ScaleX(destX, ParamScaleMode, vbPixels)
    72.     If destY Then destY = ScaleY(destY, ParamScaleMode, vbPixels)
    73.     If Centered Then                    ' Offset destX,destY if centering
    74.         destX = (ScaleX(theTarget.ScaleWidth, theTarget.ScaleMode, vbPixels) - destWidth) / 2 + destX
    75.         destY = (ScaleY(theTarget.ScaleHeight, theTarget.ScaleMode, vbPixels) - destHeight) / 2 + destY
    76.     End If
    77.                                         ' setup source coords/bounds and convert to vbHimetrics
    78.     If srcX Then srcX = ScaleX(srcX, ParamScaleMode, vbHimetric)
    79.     If srcY Then srcY = ScaleY(srcY, ParamScaleMode, vbHimetric)
    80.     If srcWidth Then srcWidth = ScaleX(srcWidth, ParamScaleMode, vbHimetric) Else srcWidth = thePic.Width
    81.     If srcHeight Then srcHeight = ScaleY(srcHeight, ParamScaleMode, vbHimetric) Else srcHeight = thePic.Height
    82.    
    83.     If Err Then                         ' passed bad parameters or
    84.         Err.Clear                       ' passed object that has no ScaleMode property (i.e., VB Frame)
    85.     Else
    86.         With thePic                     ' render, the (destDC) below and variables declared as Single needed, else type mismatch errors occur
    87.             .Render (destDC), destX, destY, destWidth, destHeight, _
    88.                 srcX, .Height - srcY, srcWidth, -srcHeight, ByVal 0&
    89.         End With                        ' return success/failure
    90.         If Err Then Err.Clear Else RenderStdPicture = True
    91.     End If
    92.     On Error GoTo 0
    93.    
    94. End Function

    Edited: Short version, no resizing, scaling, positioning, or any other options. Just draw picture at 0,0 coordinates on any DC
    Code:
       ' destDC can be any DC
        Dim thePic As stdPicture
        Set thePic = LoadPicture("C:\Images\Screenshots\image1002.jpg")
        With thePic
            .Render (destDC), 0&, 0&, ScaleX(.Width, vbHimetric, vbPixels), ScaleY(.Height, vbHimetric, vbPixels), _
                0&, .Height, .Width, -.Height, ByVal 0&
        End With
    Edited: Pointed out in post #8 below, we should use Single vartypes for our variables we plan on passing to .Render
    Last edited by LaVolpe; Dec 28th, 2014 at 11:56 PM. Reason: updated comments/sample code
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  2. #2
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: [VB] stdPicture Render (How To)

    Here is a more compact solution than the Or 0&:

    Code:
    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.
    Last edited by Merri; Aug 21st, 2010 at 02:59 PM.

  3. #3
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: [VB] stdPicture Render (How To)

    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.

    Code:
    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:
    Code:
        ' 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))
    Attached Images Attached Images  
    Attached Files Attached Files
    Last edited by Merri; Aug 22nd, 2010 at 06:01 AM.

  4. #4

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by Merri View Post
    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:
    Code:
        Sprite.Render (TargetDC), (X), (Y), (Width), (Height), _
            0, Sprite.Height, Sprite.Width, -Sprite.Height, ByVal 0&
    Last edited by LaVolpe; Aug 23rd, 2010 at 08:46 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  5. #5
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: [VB] stdPicture Render (How To)

    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!

    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

  6. #6
    New Member
    Join Date
    Nov 2010
    Posts
    2

    Thumbs up Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by LaVolpe View Post
    What is the .Render method of the stdPicture object and how does one use it.
    Thank you, LaVolpe and Merri, for this excellent lesson!

  7. #7
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,181

    Re: [VB] stdPicture Render (How To)

    Can someone explain WHY it can't take LONG datatype variables directly as input, without any of this funny business? Clearly a number of the parameters are "As Long" (when you type out the function and hit space or comma it shows what it is expecting as input INCLUDING the data types). So why does passing them Long type variables cause this error. This is the ONLY function that has the issue of not accepting the very data type it claims to be expecting. Please explain what is going on here.

  8. #8
    Fanatic Member
    Join Date
    Jan 2006
    Posts
    557

    Re: [VB] stdPicture Render (How To)

    Thanks for bringing my attention to this little known method. It really does wonders. It has helped me improve the speed of my double buffering system by a tremendous amount.

    Now to clear the bit of confusion about .render, here is the answer to one mitigated point in the above discussion.

    1- The MSDN page reference in post #1 is not very complete since it does not document the types for calling the routine and it does not mention either the upside down nature of the stdPicture bit arrangement

    2- VB's object browser says this about the .render procedure :

    Sub Render(hdc As Long, x As Long, y As Long, cx As Long, cy As Long, xSrc As OLE_XPOS_HIMETRIC, ySrc As OLE_YPOS_HIMETRIC, cxSrc As OLE_XSIZE_HIMETRIC, cySrc As OLE_YSIZE_HIMETRIC, prcWBounds As Any)


    This is NOT accurate. The arguments for destination are NOT type Long. Also, OLE_XPOS_HIMETRIC is quite undescriptive for programmers not familiar with Windows Foundation Classes. Furthermore, VB6-API programmers might think the prominent type LONG might do the job, but that would be surprising since the base unit of hiMetric is .01 millimeters.

    As it turns out, ALL arguments except for hdc and wbounds are of type SINGLE.

    This line of code will call .render with the appropriate variable types and without the infamous type mismatch error :

    Code:
    mainpic.Image.Render bufferpic.hdc, 0!, 0!, sWidth!, sHeight!, hmSrcLeft!, hmSrcTop!, hmSrcWidth!, hmSrcHeight!, 0&
    and that is only logical since .render is designed to work with any units supported by the stdPicture scale mode, and not only typical pixel unit used normally with gdi or gdi+ routines.

  9. #9

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by Navion
    As it turns out, ALL arguments except for hdc and wbounds are of type SINGLE.
    Code:
    mainpic.Image.Render bufferpic.hdc, 0!, 0!, sWidth!, sHeight!, hmSrcLeft!, hmSrcTop!, hmSrcWidth!, hmSrcHeight!, 0&
    Think you solved it. In the form's click event add this line of code:
    Code:
    Me.Icon.Render Me.hDC, 0&, 0&, 32&, 32&, 0&, Me.Icon.Height, Me.Icon.Width, -Me.Icon.Height, ByVal 0&
    No errors & it renders just fine. Notice that the destination position/size are all LONG vartypes

    But when passing variables, then the issue arises: This line of code generates the error.
    Code:
    Dim Xy As Long, Cxy As Long
    Cxy = 32
    Me.Icon.Render Me.hDC, Xy, Xy, Cxy, Cxy, 0&, Me.Icon.Height, Me.Icon.Width, -Me.Icon.Height, ByVal 0&
    But using Singles instead of Longs... no errors
    Code:
    Dim Xy As Single, Cxy As Single, lDC As Long
    Cxy = 32: lDC = Me.hDC
    Me.Icon.Render (lDC), Xy, Xy, Cxy, Cxy, Xy, Me.Icon.Height, Me.Icon.Width, -Me.Icon.Height, ByVal 0&
    Last edited by LaVolpe; Dec 28th, 2014 at 11:57 PM. Reason: added a few examples
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  10. #10
    Fanatic Member
    Join Date
    Nov 2013
    Posts
    658

    Re: [VB] stdPicture Render (How To)

    Hi - good job.

    Don't know if anyone has noticed but one "major" drawback of using the this .Render Method though is that the picture doesn't persist after a refresh.

    For example: Upon activation of the Form, render the picture onto the form's DC ( or any control's Dc) as shown on this thread then carry out a refresh of the form or simply change the form's zoom and you will see that the form's picture disappears.

    This drawback is more relevant and limiting in VBA because office userforms don't have a Paint event where this .Render code can conviniently be re-executed to restore the picture after a each refresh.

    Regards.
    Last edited by JAAFAR; Jun 7th, 2019 at 07:27 AM.

  11. #11

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [VB] stdPicture Render (How To)

    That's expected -- use AutoRedraw on the object being drawn to
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  12. #12
    Fanatic Member
    Join Date
    Nov 2013
    Posts
    658

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by LaVolpe View Post
    That's expected -- use AutoRedraw on the object being drawn to
    True for VB6 not for VBA where the AutoRedraw Property doesn't exist.

    The only vba fix I can think of is to subclass the vba form and handle the WM_PAINT msg.

    Regards.

  13. #13
    Fanatic Member
    Join Date
    Jan 2013
    Posts
    894

    Re: [VB] stdPicture Render (How To)

    The ppl don't understand the .RENDER because the help tag (the popup), describes it as long , long, long, long, long , Ole_xpos_himetric, ole_ypos_himetric, ole_xsize_himetric, ole_ysize_himetric, any


    and when you try other than 0,0,pic.width,pic.height.... it will return Type mismatch error 13

    doesn't matter if you do proper

    dim picw as OLE_XSIZE_HIMETRIC .... and so on.

    it will return type mismatch any attempt


    Because what works, is to use all "Single" variables.

    include if you do it (air coding)

    .render hdc, , , , , usercontrol.scalex(value, pixels, himetrics) it will fail with error 13.

    the only way is to use Single variable, and asign the value singleX = usercontrol.scalex(value, pixels, himetrics) and then use the singleX variable in the .render method.


    I ran like 3 hours of headaches about this..., Microsoft ruined it with the help popup.

    (when writing code to render partial picture related to the XY scrolling values for the usercontrol)
    Last edited by flyguille; Jul 12th, 2019 at 07:15 AM.

  14. #14

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [VB] stdPicture Render (How To)

    I believe the real problem is with the VB stdPicture object somehow, someway.

    Look at the two .Render calls below. They are identical with the exception of which interface they are called on. When using IPicture, no issues passing Longs. When using stdPicture (or IPictureDisp or Picture), then we have mismatch errors.
    Code:
        Dim xy As Long, cxy As Long, tDC As Long
        cxy = 64
        tDC = Me.hDC
        
        Dim oIPict As IPicture  ' << IPicture object
        Set oIPict = Me.Icon
        With oIPict
            .Render tDC, xy, xy, cxy, cxy, 0, .Height, .Width, -.Height, ByVal 0&
        End With
        
        With Me.Icon  '  << stdPicture, IPictureDisp, Picture object
            .Render tDC, xy, xy, cxy, cxy, 0, .Height, .Width, -.Height, ByVal 0&
        End With
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  15. #15
    Fanatic Member
    Join Date
    Jan 2013
    Posts
    894

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by LaVolpe View Post
    I believe the real problem is with the VB stdPicture object somehow, someway.

    Look at the two .Render calls below. They are identical with the exception of which interface they are called on. When using IPicture, no issues passing Longs. When using stdPicture (or IPictureDisp or Picture), then we have mismatch errors.
    Code:
        Dim xy As Long, cxy As Long, tDC As Long
        cxy = 64
        tDC = Me.hDC
        
        Dim oIPict As IPicture  ' << IPicture object
        Set oIPict = Me.Icon
        With oIPict
            .Render tDC, xy, xy, cxy, cxy, 0, .Height, .Width, -.Height, ByVal 0&
        End With
        
        With Me.Icon  '  << stdPicture, IPictureDisp, Picture object
            .Render tDC, xy, xy, cxy, cxy, 0, .Height, .Width, -.Height, ByVal 0&
        End With
    yup all StdPictures here.... but it runs me nut! Crazy man!.

    Include I copied the .render from other Usercontrol that is well known that is working... but in this one I was unable to get it to work. Include I tested if was something with the original JPG image, so get the MSPAINT resaved it (the original was from the web), and nothing.... 3 hours of hazle. !!! The others usercontrols gets images from .PNG files, but IIRC, when loading it in stdpictures, all are converted to bitmaps so the source don't matter, right?
    Last edited by flyguille; Jul 12th, 2019 at 04:44 PM.

  16. #16
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,092

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by flyguille View Post
    but it runs me nut! Crazy man!.
    Been there, done that. Here is what I come up with: Just use a helper pvRenderPicture function implemented like this:

    thinBasic Code:
    1. Private Sub pvRenderPicture(pPicture As IPicture, ByVal hDC As Long, _
    2.         ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, _
    3.         ByVal xSrc As OLE_XPOS_HIMETRIC, ByVal ySrc As OLE_YPOS_HIMETRIC, _
    4.         ByVal cxSrc As OLE_XSIZE_HIMETRIC, ByVal cySrc As OLE_YSIZE_HIMETRIC)
    5.     If Not pPicture Is Nothing Then
    6.         If pPicture.Handle <> 0 Then
    7.             pPicture.Render hDC, X, Y, cx, cy, xSrc, ySrc, cxSrc, cySrc, ByVal 0
    8.         End If
    9.     End If
    10. End Sub
    This sub *always* works w/ whatever arguments you pass in (constants, strings, retvals from ScaleX/Y), because the parameters of pvRenderPicture are effectively local variables (data on stack) for the compiler and so .Render method gets all its actual arguments from local variables of correct data-type e.g. Longs and OLE_X/YPOS_HIMETRICs.

    cheers,
    </wqw>

  17. #17
    Fanatic Member
    Join Date
    Jan 2013
    Posts
    894

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by wqweto View Post
    Been there, done that. Here is what I come up with: Just use a helper pvRenderPicture function implemented like this:

    thinBasic Code:
    1. Private Sub pvRenderPicture(pPicture As IPicture, ByVal hDC As Long, _
    2.         ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, _
    3.         ByVal xSrc As OLE_XPOS_HIMETRIC, ByVal ySrc As OLE_YPOS_HIMETRIC, _
    4.         ByVal cxSrc As OLE_XSIZE_HIMETRIC, ByVal cySrc As OLE_YSIZE_HIMETRIC)
    5.     If Not pPicture Is Nothing Then
    6.         If pPicture.Handle <> 0 Then
    7.             pPicture.Render hDC, X, Y, cx, cy, xSrc, ySrc, cxSrc, cySrc, ByVal 0
    8.         End If
    9.     End If
    10. End Sub
    This sub *always* works w/ whatever arguments you pass in (constants, strings, retvals from ScaleX/Y), because the parameters of pvRenderPicture are effectively local variables (data on stack) for the compiler and so .Render method gets all its actual arguments from local variables of correct data-type e.g. Longs and OLE_X/YPOS_HIMETRICs.

    cheers,
    </wqw>
    your code works because you declare it as IPicture, not because you use HIMETRIC data types,

    I got the pictures in StdPicture variables, and using the .render with that image, you can't use HIMETRIC data type, it will get error 13.

    That is what runs me nut.

    Now, your code can be called with an StdPicture, when is declaring to accept IPicture?

    if the variables are local, vs globals has nothing to do. It is just that the compiler understand that StdPicture ->,,,, HIMETRIC Type Mismatch..... so StdPicture ,,,,,, Single = ok. and Ipicture,,,,,, Himetric -> ok, Ipicture,,,,,, single -> ????


    VB allows to do SET IPicture = StdPicture? without performance loss?
    Last edited by flyguille; Jul 13th, 2019 at 12:45 PM.

  18. #18

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [VB] stdPicture Render (How To)

    If you declare varIPic as IPicture then call: Set varIPic = someStdPic, any performance loss would be negligible. Here is what VB is doing, in simple terms:
    Code:
    Dim varIPic As IPicture  >> VB creates new variable
    Set varIPic = someStdPicture >> VB performs these steps
        Call someStdPicture's IUnknown:QueryInterface to see if it supports IPicture & it will
        QueryInterface returns existing IPicture interface and assigns it to varIPic
    -- when varIPic is set to Nothing or goes out of scope, VB calls IUnknown:Release on it
    So, by using another interface, there are a few extra calls behind the scenes, but those are going to be fast. There might actually be a slight performance boost if stdPicture's Render call uses IDispatch:Invoke or the DispInvoke API and IPicture's Render call does not. Reason is usage of variants.

    Edited and just FYI:
    OLE_XPOS_HIMETRIC, OLE_YPOS_HIMETRIC, OLE_XSIZE_HIMETRIC, OLE_YSIZE_HIMETRIC can all be substituted with LONGs. This is because, the typelib defines those as aliases/typedefs of the vartype Long. Of course, if passing longs, use IPicture.Render

    from the typelib
    Code:
    typedef [uuid(66504306-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_XPOS_HIMETRIC;
    
    typedef [uuid(66504307-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_YPOS_HIMETRIC;
    
    typedef [uuid(66504308-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_XSIZE_HIMETRIC;
    
    typedef [uuid(66504309-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_YSIZE_HIMETRIC;
    Last edited by LaVolpe; Jul 13th, 2019 at 02:04 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  19. #19
    Fanatic Member
    Join Date
    Jan 2013
    Posts
    894

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by LaVolpe View Post
    If you declare varIPic as IPicture then call: Set varIPic = someStdPic, any performance loss would be negligible. Here is what VB is doing, in simple terms:
    Code:
    Dim varIPic As IPicture  >> VB creates new variable
    Set varIPic = someStdPicture >> VB performs these steps
        Call someStdPicture's IUnknown:QueryInterface to see if it supports IPicture & it will
        QueryInterface returns existing IPicture interface and assigns it to varIPic
    -- when varIPic is set to Nothing or goes out of scope, VB calls IUnknown:Release on it
    So, by using another interface, there are a few extra calls behind the scenes, but those are going to be fast. There might actually be a slight performance boost if stdPicture's Render call uses IDispatch:Invoke or the DispInvoke API and IPicture's Render call does not. Reason is usage of variants.

    Edited and just FYI:
    OLE_XPOS_HIMETRIC, OLE_YPOS_HIMETRIC, OLE_XSIZE_HIMETRIC, OLE_YSIZE_HIMETRIC can all be substituted with LONGs. This is because, the typelib defines those as aliases/typedefs of the vartype Long. Of course, if passing longs, use IPicture.Render

    from the typelib
    Code:
    typedef [uuid(66504306-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_XPOS_HIMETRIC;
    
    typedef [uuid(66504307-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_YPOS_HIMETRIC;
    
    typedef [uuid(66504308-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_XSIZE_HIMETRIC;
    
    typedef [uuid(66504309-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_YSIZE_HIMETRIC;
    Yes, using long works when all stdPictures are loaded from PNGs using the GDI, so, as my skins are all done with png, and I used longs in the .render, never found this problem, but then in another usercontrol object, I use a StdPicture which loads a JPG from web (the jpg is created in PHP api at webserver side), so, download and using the normal LoadPciture() from VB, and first attempt used all longs as always, failes, so, I observed the help tips, used the HIMETRIC types, failes, failes failed....., set up like 0,0,usercontrol.scalex, usercontrol.scaley FAILED.

    imagine a student encounter this problem, he will think, the .render is crap, better stay away from that. And so, that must be the "it is not well known", or unpopular.

  20. #20
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,092

    Re: [VB] stdPicture Render (How To)

    Quote Originally Posted by flyguille View Post
    your code works because you declare it as IPicture, not because you use HIMETRIC data types
    You are right about IPicture being the reason everything is working with my code. I never said it's working because it's using "HIMETRIC data types" but the root-cause hypothesis of using local variable is still wrong. My bad here!

    The StdPicture class is declared as this is stdole2.tlb

    Code:
        [
          uuid(0BE35204-8F91-11CE-9DE3-00AA004BB851)
        ]
        coclass StdPicture {
            [default] dispinterface Picture;
            interface IPicture;
        };
    while the dispinterface Picture is further aliased as this

    Code:
    typedef [public] Picture IPictureDisp;
    So StdPicture supports both IPicture and Picture (a.k.a IPictureDisp) interfaces and the second one is its default interface that VB6 uses when not explicitly cast to IPicture.

    Both IPicture and IPictureDisp have Render methods

    Code:
        [
          odl,
          uuid(7BF80980-BF32-101A-8BBB-00AA00300CAB),
          helpstring("Picture Object"),
          hidden
        ]
        interface IPicture : IUnknown {
            [propget]       HRESULT _stdcall Handle([out, retval] OLE_HANDLE* phandle);
            [propget]       HRESULT _stdcall hPal([out, retval] OLE_HANDLE* phpal);
            [propget]       HRESULT _stdcall Type([out, retval] short* ptype);
            [propget]       HRESULT _stdcall Width([out, retval] OLE_XSIZE_HIMETRIC* pwidth);
            [propget]       HRESULT _stdcall Height([out, retval] OLE_YSIZE_HIMETRIC* pheight);
                            HRESULT _stdcall Render([in] int hdc, [in] long x, [in] long y, [in] long cx, [in] long cy, [in] OLE_XPOS_HIMETRIC xSrc, [in] OLE_YPOS_HIMETRIC ySrc, [in] OLE_XSIZE_HIMETRIC cxSrc, [in] OLE_YSIZE_HIMETRIC cySrc, [in] void* prcWBounds);
            [propput]       HRESULT _stdcall hPal([in] OLE_HANDLE phpal);
            [propget]       HRESULT _stdcall CurDC([out, retval] int* phdcOut);
                            HRESULT _stdcall SelectPicture([in] int hdcIn, [out] int* phdcOut, [out] OLE_HANDLE* phbmpOut);
            [propget]       HRESULT _stdcall KeepOriginalFormat([out, retval] VARIANT_BOOL* pfkeep);
            [propput]       HRESULT _stdcall KeepOriginalFormat([in] VARIANT_BOOL pfkeep);
                            HRESULT _stdcall PictureChanged();
                            HRESULT _stdcall SaveAsFile([in] void* pstm, [in] VARIANT_BOOL fSaveMemCopy, [out] long* pcbSize);
            [propget]       HRESULT _stdcall Attributes([out, retval] long* pdwAttr);
                            HRESULT _stdcall SetHdc([in] OLE_HANDLE hdc);
        };
    
        [
          uuid(7BF80981-BF32-101A-8BBB-00AA00300CAB)
        ]
        dispinterface Picture {
            properties:
                [id(00000000), readonly]            OLE_HANDLE Handle;
                [id(0x00000002)]                    OLE_HANDLE hPal;
                [id(0x00000003), readonly]          short Type;
                [id(0x00000004), readonly]          OLE_XSIZE_HIMETRIC Width;
                [id(0x00000005), readonly]          OLE_YSIZE_HIMETRIC Height;
            methods:
                [id(0x00000006)]                    void Render(int hdc, long x, long y, long cx, long cy, OLE_XPOS_HIMETRIC xSrc, OLE_YPOS_HIMETRIC ySrc, OLE_XSIZE_HIMETRIC cxSrc, OLE_YSIZE_HIMETRIC cySrc, void* prcWBounds);
        };
    . . . but these two Render methods are implented differently -- one is a vtable entry while the other on the dispinterface is purely late-bound (IDispatch::Invoke) only.

    This late-bound one is failing and is 100% buggy because it cannot accept VT_BYREF flagged variants -- it just compares paramCx.vt == VT_I4 and all other integral vartypes and bails out w/ E_PARAMETER if type is too much for it.

    When you pass hDC + 0 or -(-X) or (Y) or ScaleX(Width, ...) or UserControl.hDC (prop access) the expression is not lvalue (in compiler lingo) so it cannot be converted to a Variant w/ VT_BYREF flag and that is the reason why Render on the dispinterface works in this case. It works with Single/Double/String variables too (any type other that Long) becase these are implicitly cast to Long i.e. CLng(dblWidth) by the compiler so again are not lvalues and Variants are not flagged w/ VT_BYREF.

    Render on IPicture is the honey-badger version of the two and works whatever you throw at it and that is what I'm using and frankly everyone should use too.

    Summary: Render on IPictureDisp fails when passing local variables/parameters of the correct (Long) data-type because the VB6 compiler makes an extra effort to pass VT_BYREF flagged Variants. The reason this extra effort is allowed is because in typelibs the paramters for dispinterface are *not* described with ByRef/ByVal mode of passing per se (as for dual interfaces). The actual bug is in the IPictureDisp::Invoke implementation in oleaut32.dll

    cheers,
    </wqw>

  21. #21
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,092

    Re: [VB] stdPicture Render (How To)

    Here is a snippet from a recent submission to this form

    thinBasic Code:
    1. Private Sub UserControl_Paint()
    2.     If mGIF0 Is Nothing Then
    3.         Line (0, 0)- _
    4.              (ScaleWidth - ScaleX(1, vbPixels, ScaleMode), _
    5.               ScaleHeight - ScaleY(1, vbPixels, ScaleMode)), _
    6.              , _
    7.              B
    8.     Else
    9.         With mGIF0
    10.             .Render hDC, _
    11.                     0, _
    12.                     0, _
    13.                     ScaleX(.Width, vbHimetric, vbPixels), _
    14.                     ScaleY(.Height, vbHimetric, vbPixels), _
    15.                     0, _
    16.                     .Height, _
    17.                     .Width, _
    18.                     -.Height, _
    19.                     ByVal 0&
    20.     . . .
    Notice that this is directly calling Render on StdPicture (i.e. on IPictureDisp) and it *does* work fine because all params are either a retval from a property/method or constant literals. First param hDC is a property of the current UserControl, then some numeric literals, then two method calls of ScaleX/Y, more consts and finally .Width/.Height are properties of the very StdPicture.

    The moment for instance hDC is declared a local Long variable the Render call will fail with type mismatch which is a very misleading error message in this case.

    cheers,
    </wqw>

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width