Results 1 to 16 of 16

Thread: [RESOLVED] Is BitBlt the best way?

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Resolved [RESOLVED] Is BitBlt the best way?

    Hey,

    I'm making a program now where you have lots of "Pets". Each looks different according to it's "PetID" but I only have one "Pet" object for blitting. Therefore my code looks like this:

    Code:
    Private Function PetDraw(i As Integer)
    
    'Call this function to draw Pets
    'Assumes that the mask is right of the sprite in the image file
    'Frames are square and side-by-side
    
    Pet.Picture = LoadPicture(App.Path & "\Images\" & PetID(i) & ".gif")
    
    If (PetLeft(i) = False) Then
        BitBlt Me.hdc, PetX(i), PetY(i), (Pet.Width / 2), Pet.Height, Pet.hdc, (Pet.Width / 2), 0, vbSrcAnd
        BitBlt Me.hdc, PetX(i), PetY(i), (Pet.Width / 2), Pet.Height, Pet.hdc, 0, 0, vbSrcPaint
    Else 'Flip Pet (facing LEFT)
        StretchBlt Me.hdc, PetX(i) + (Pet.Width / 2), PetY(i), -(Pet.Width / 2), Pet.Height, Pet.hdc, (Pet.Width / 2), 0, (Pet.Width / 2), Pet.Height, vbSrcAnd
        StretchBlt Me.hdc, PetX(i) + (Pet.Width / 2), PetY(i), -(Pet.Width / 2), Pet.Height, Pet.hdc, (Pet.Width / 2), 0, Pet.Width, Pet.Height, vbSrcPaint
    End If
    
    End Function
    I have two questions:

    1) Is this at all efficient, as the line in red changes the Pet object's picture very frequently to draw all the different Pets?

    2) Is there a better way of drawing transparent pictures without each image having to have a mask alongside it? I've drawn a lot of Pets and I don't particularly want to go through them adding masks.

    Thanks,

    - Danjb

  2. #2
    Fanatic Member FireXtol's Avatar
    Join Date
    Apr 2010
    Posts
    874

    Re: Is BitBlt the best way?

    TransparentBlt and AlphaBlend APIs can do it in a single call using an image that has transparency. LoadPictureGDI+ in my signature is well designed for loading sprites, such as GIFs or PNGs. The accompanying AlphaBlt can draw them.

    You could make Pet an array, and load a picture into each array element instead of loading them every time.

  3. #3
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Is BitBlt the best way?

    I definitely agree with storing the pictures to an array... and if the same picture could be used for more than one pet at the same time, it might even be worth having one array for the pictures, and then for each pet store the array index of the picture array.


    In addition to that, I notice that there are already several arrays for pets (PetX/PetY/PetLeft), which is a bit "untidy". I would recommend creating a user defined Type instead, and then creating an array of that. eg:
    Code:
    Type udt_Pet
      X as Long
      Y as Long
      FacingLeft as Boolean
      'more items here...
    End Type
    Private Pets() as udt_Pet
    The usage of this array wouldn't be too different when using just a single item (eg: PetX(i) becomes Pets(i).X ), but by using a With block it makes a big difference to the size of code that uses multiple items for the same pet (and makes it slightly faster too).

    For the code you showed, it would be changed like this:
    Code:
        With Pets(i)
          If (.FacingLeft = False) Then
            BitBlt Me.hdc, .X, .Y, (Pet.Width / 2), Pet.Height, Pet.hdc, (Pet.Width / 2), 0, vbSrcAnd
            BitBlt Me.hdc, .X, .Y, (Pet.Width / 2), Pet.Height, Pet.hdc, 0, 0, vbSrcPaint
          Else
    ...
          End If
        End With

    Another issue regarding efficiency, doing division is rather slow, so your repeated use of (Pet.Width / 2) is not helping (but is not on the same scale as loading the pictures). It would be best to calculate it just once and store it to a variable.

  4. #4

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Re: Is BitBlt the best way?

    Thanks for the tips!

    Is there a way of using AlphaBlend or TransparentBlt along with a mirror transformation?

  5. #5
    Fanatic Member FireXtol's Avatar
    Join Date
    Apr 2010
    Posts
    874

    Re: Is BitBlt the best way?

    From: http://msdn.microsoft.com/en-us/libr...51(VS.85).aspx
    AlphaBlend does not support mirroring. If either the width or height of the source or destination is negative, this call will fail.

    From: http://msdn.microsoft.com/en-us/libr...41(VS.85).aspx
    TransparentBlt does not mirror a bitmap if either the width or height, of either the source or destination, is negative.

    Seems like a no.

  6. #6

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Re: Is BitBlt the best way?

    Yeah, that's what I found, too. So is it possible to use a StretchBlt afterwards or something, or is there a different method?

  7. #7
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Is BitBlt the best way?

    I don't think you can get away with StretchBlt without using a mask, which would bring you back to where you were before.

    What you could do is find a mask maker program (I'm pretty sure there is one in the CodeBank or UtilityBank forums), as that will make it much easier to create the masks.

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

    Re: Is BitBlt the best way?

    How about not using any API call and not having a mask? Just set transparent color in your GIF palette and draw.

    Project with a simple test picture attached, code only below:
    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
    The -- trick fixes the mysterious Type Mismatch problem. It seems that you can't pass a variable as-is: constants & values received from objects seem to work OK.

    Also posted in LaVolpe's StdPicture Render thread.
    Attached Files Attached Files

  9. #9

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Re: Is BitBlt the best way?

    ^ That sounds ideal.

    It's not just that making the masks is so much hassle, I just don't like the idea of all my images becoming mask-ified in case I want to use them for something else. I'll look for the mask maker program nonethless though.

    Thanks once again for the help guys, I'll let you know if I have any problems

    EDIT: Problems :/

    So I tried the Render method, and after a while figured it out (taking rather a long time to realise that AutoRedraw had to be set to False). However, I'm having a bit of trouble tweaking it to work with an array of Pets.

    Am I correct in thinking that the Form_Paint event is what happens every time the LoadPicture command is used?

    Here's my code.

    Code:
    For i = 1 to NumPets
    
    '[Change X and Y co-ordinates a bit...]
    
    Set PetPic(i) = LoadPicture(App.Path & "\Images\" & Pet(i).ID & ".gif")
    
    Next i
    (I'm using si's advice about a user-defined Type as well)

    Code:
    Private Sub Form_Paint()
    
        Dim Width As Long, Height As Long
        
        ' Convert himetric to pixels
        Width = Me.ScaleX(PetPic(CurrentPet).Width, vbHimetric, vbPixels)
        Height = Me.ScaleY(PetPic(CurrentPet).Height, vbHimetric, vbPixels)
        
        If (Pet(CurrentPet).FacingLeft = False) Then
            ' Render normal
            PetPic(CurrentPet).Render Me.hdc, --Pet(CurrentPet).X, --Pet(CurrentPet).Y, --Width, --Height, 0, PetPic(CurrentPet).Height, PetPic(CurrentPet).Width, -PetPic(CurrentPet).Height, ByVal 0&
        Else
            ' Render mirrored
            PetPic(CurrentPet).Render Me.hdc, --Pet(CurrentPet).X, --Pet(CurrentPet).Y, -Width, --Height, 0, PetPic(CurrentPet).Height, PetPic(CurrentPet).Width, -PetPic(CurrentPet).Height, ByVal 0&
        End If
    
    End Sub
    I've set "CurrentPet" to a variable, equal to "i" in the previous code, as it was the only way I knew of transferring the variable to Form_Paint.

    The code works until I try and add a second Pet - at which point the first Pet disappears, leaving me with only the second one. If I add a third, the second disappears, and so on.

    I've probably either missed something really obvious or totally misunderstood the Form_Paint event...
    Last edited by Danjb; Aug 22nd, 2010 at 05:46 PM.

  10. #10
    Fanatic Member FireXtol's Avatar
    Join Date
    Apr 2010
    Posts
    874

    Re: Is BitBlt the best way?

    Form_Paint happens when the form is required to redraw itself.

    To draw all your pets you'd want to loop through them all, like you do when loading....

    vb Code:
    1. For i = 1 to CurrentPet '(?)
    2. .Render ....
    3. Next i

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

    Re: Is BitBlt the best way?

    In the LaVolpe thread I linked in my last post I added a sample project that uses AutoRedraw.

  12. #12

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Re: Is BitBlt the best way?

    Ah, I see! So it was the Me.Refresh that was causing the Form_Paint event, meaning that it was being called outside of my loop, and therefore only being called once, for the last Pet that had been moved.

    I've now put the loop inside the Form_Paint event and it works like a charm
    (in fact better than I thought, since I thought I'd have to call LoadPicture every frame but really I only have to call it once per Pet, and it's stored to my "PetPic" StdPicture array)

    Thanks a lot!

    EDIT: It would appear that the .Render method has the same sort of effect as using transparent images - they occasionally "flicker" black for a split second... am I doing something wrong or is that something I'll just have to put up with?
    Last edited by Danjb; Aug 23rd, 2010 at 04:38 AM.

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

    Re: [RESOLVED] Is BitBlt the best way?

    The only downside with Form_Paint event & not using AutoRedraw is that if you don't create a background buffer using other means (such as API) then you are likely to get flickering. However, if you don't draw a lot and if the flickering isn't that bad then it isn't a problem

  14. #14

    Thread Starter
    Lively Member
    Join Date
    Jul 2009
    Posts
    97

    Re: [RESOLVED] Is BitBlt the best way?

    I've now gone with AutoRedraw, so no more flickering.

    Thanks again!
    I think I ended up using every piece of advice I was given in this thread; you guys are great

  15. #15
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: [RESOLVED] Is BitBlt the best way?

    We're happy to help.


    By the way, you could extend the Type so that it also includes the picture, rather than having a separate array for that.

    If you do that and use a With block, the Form_Paint code above could be reduced to this:
    Code:
    Private Sub Form_Paint()
    
        Dim Width As Long, Height As Long
        
        With Pet(CurrentPet)
          ' Convert himetric to pixels
          Width = Me.ScaleX(.Pic.Width, vbHimetric, vbPixels)
          Height = Me.ScaleY(.Pic.Height, vbHimetric, vbPixels)
        
          If (.FacingLeft = False) Then
              ' Render normal
              .Pic.Render Me.hdc, --.X, --.Y, --Width, --Height, 0, .Pic.Height, .Pic.Width, -.Pic.Height, ByVal 0&
          Else
              ' Render mirrored
              .Pic.Render Me.hdc, --.X, --.Y, -Width, --Height, 0, .Pic.Height, .Pic.Width, -.Pic.Height, ByVal 0&
          End If
        End With
    
    End Sub
    It has 2 extra lines, but makes the existing ones shorter and easier to read, plus a bit faster too.

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

    Re: [RESOLVED] Is BitBlt the best way?

    As always when it comes to With block, you should never, ever, use Exit Do, Exit Sub, or similar method that forces code execution outside the With block. This will cause internal problems and is a possible cause for very-hard-to-figure-out memory leaks.

    Just a generic FYI, not specific to this case, just something to remember when using With blocks

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