Results 1 to 17 of 17

Thread: Is there a faster way

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Is there a faster way

    Here's the code I'm using:

    Code:
    Private Sub DrawPieces(Picturebox As Picturebox, MaskCokor As Integer, Index As Integer)
     Dim NewX As Integer, NewY As Integer
    
     Picturebox.Picture = Nothing
    
     Picturebox.AutoRedraw = False
     
     For X = 0 To Picture4.ScaleWidth
       For Y = 0 To Picture4.ScaleHeight
         If Picture4.Point(X, Y) = MaskColor Then
           NewX = X + (70 - (CenterPoint(Index, 0)))
           NewY = Y + (69 - (CenterPoint(Index, 1)))
           Picturebox.PaintPicture Picture1.Picture, NewX, NewY, 1, 1, X, Y, 1, 1
         End If
       Next Y
     Next x
     End Sub
    The code scans Picture4 for a particular color (MaskColor) and when it finds this color it calculates the new X and Y of the target picture (Picturebox) and copies a pixel from Picture1 and the same X and Y it found the mask color and then paints that pixel at the new X and Y on Picturebox. This works OK but as you see I'm doing one pixel at a time in the .Paint method
    and was wondering is there another way to do this which would be faster.

  2. #2
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Is there a faster way

    We've been over this a number of times now. Perhaps you were getting stuck trying to persist a MaskBlt operation? I'm not sure why else you'd be falling back on something with the incredible overhead of trying to PaintPicture 1 pixel at a time.

    But since I'm only guessing with so much left to the imagination, here's a shot at one possible solution.

    Code:
    Option Explicit
    
    'Private Const DSTCOPY = &HAA0029
    Private Const QROP_FORESRCCOPY_BACKDSTCOPY As Long = &HAACC0000
    
    Private Declare Function MaskBlt Lib "gdi32" ( _
        ByVal hdcDest As Long, _
        ByVal nXDest As Long, _
        ByVal nYDest As Long, _
        ByVal nWidth As Long, _
        ByVal nHeight As Long, _
        ByVal hdcSrc As Long, _
        ByVal nXSrc As Long, _
        ByVal nYSrc As Long, _
        ByVal hbmMask As Long, _
        ByVal xMask As Long, _
        ByVal yMask As Long, _
        Optional ByVal dwRop As Long = QROP_FORESRCCOPY_BACKDSTCOPY) As Long
    
    Private Sub Command1_Click()
        Dim WidthPx As Long
        Dim HeightPx As Long
        Dim Mask As StdPicture
    
        With picDest
            .AutoRedraw = True
            WidthPx = ScaleX(picSource.Picture.Width, vbHimetric, vbPixels)
            HeightPx = ScaleY(picSource.Picture.Height, vbHimetric, vbPixels)
            Set Mask = MaskMaker.Mask(picSource.Picture, vbGreen)
            MaskBlt .hDC, _
                    Int((ScaleX(.ScaleWidth, ScaleMode, vbPixels) - WidthPx) / 2 + 0.5), _
                    Int((ScaleY(.ScaleHeight, ScaleMode, vbPixels) - HeightPx) / 2 + 0.5), _
                    WidthPx, _
                    HeightPx, _
                    picSource.hDC, _
                    0, _
                    0, _
                    Mask.Handle, _
                    0, _
                    0
            .AutoRedraw = False
            Set .Picture = .Image
        End With
    
        Command1.Enabled = False
    End Sub

    Name:  sshot.png
Views: 393
Size:  13.8 KB

    Bam!

    Sorry about the attachment size, images can eat a lot of space.
    Attached Files Attached Files

  3. #3
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Is there a faster way

    Er, or are you trying to do some kind of "opposite mask" operation copying only pixels that have the MaskColor?

    If so, swap the RasterOp around. Not sure why you'd want to copy only the mask though. Weird. But I'm not a graphics guy so perhaps I'm just confused.

  4. #4
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Is there a faster way

    It would be easier to determine if there was a faster way if we had examples of the pictures involved.

    It looks like Picture4 is strictly a Mask for the shape you want to be transferred, and if that mask was a white on black, or black on white image, then you should be able to use it with Raster Ops to "cut a hole" in the destination, and then fill it with your source.

    I threw together a quick example. The key portion is the following sub.
    Code:
    Private Sub PaintPiece(X As Single, Y As Single)
      'The mask (white on black) is pre-loaded in the .Picture context of picture4
    
      '1. We paint an inverted version of that picture to Picture4 itself
      '      .Picture = original mask, .Image = inverted mask
      '2. We use the inverted image to "cut a hole" in the destination picture
      '3. We restore .Image to the original mask
      '4. We paint our puzzle image onto the mask, giving us a mask shaped copy of the image
      '5. We paint that shaped image onto the destination picture, filling the hole with the image
      
    1  Picture4.PaintPicture Picture4.Picture, 0, 0, , , , , , , vbNotSrcCopy
    2  PictureBox.PaintPicture Picture4.Image, X - 30, Y - 30, , , , , , , vbSrcAnd
    3  Picture4.Cls
    4  Picture4.PaintPicture Picture1.Picture, 0, 0, 120, 120, X - 60, Y - 60, 120, 120, vbSrcAnd
    5  PictureBox.PaintPicture Picture4.Image, X - 30, Y - 30, , , , , , , vbSrcPaint
    
    End Sub
    If you change statement 4 so that X - 60 and Y - 60 is replaced by some constant value, say 200, 200, then when you drag with the right button it should look like what I think you are trying to accomplish, i.e. dragging a shaped portion (a piece) of the source image around the destination image.
    Attached Files Attached Files

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    OK, I include a picture of what I'm doing. The code I posted in my 1st post is what I'm using to do this. For the shapes on the left it goes OK but as the scan gets deeper into Picture4 it starts to slow down so the overall time is quite slow
    Attached Images Attached Images  

  6. #6
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Is there a faster way

    Well that's a minor twist on what I showed above:

    Name:  sshot.png
Views: 348
Size:  22.7 KB


    Of course to get exactly what you are after you'd probably just pull out small chunks based on the bounding box of each not-mask. But I covered that previously in this long series of threads.
    Attached Files Attached Files
    Last edited by dilettante; Jul 19th, 2019 at 04:18 PM.

  7. #7
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Is there a faster way

    {p.s. both times, dile got in and posted while I was composing, so you have good approaches to consider. I may still modify my example in #4 for the exercise}

    Well, hopefully you only have to do it once, after the picture is selected and the size of the puzzle pattern is chosen.
    You would save the pieces once isolated and use them for the duration of the puzzle solving.

    I guess my first optimization would be only scan an area the size of your piece (which would overlap other pieces of course) rather than scan the whole picture.
    I was originally thinking of just scanning through it once, and using a linked list to keep track of the pixels that make up each piece, but that is more complicated and can be pursued later, if needed, but since this should be a one time operation, just scanning a smaller area around the center of each piece, even with a lot of overlap, should be easier to do and much quicker than what you are doing.

    What you do with the pixels is another matter.
    Whether you want to use the approach I did before, where you have the image drawn twice, once on a black ground, and again on a white back.
    Or you want to create a white on black mask as I did in post #4.
    Or you want to create an image on transparent background color, as dilettante did and use maskblt or transparentblt (should be an example around)

    Maybe I'll see if I can use your example jigsaw shape piece image to modify the example in post #4, as a further example.
    I never liked using a multi-color image as a mask like that, as in the early days it was hard to trust that a users machine wouldn't have to modify the colors when displayed because of limited color depth. But, I'll assume if you pick a centerpoint for each piece on a grid, and examine the color value and use it to find the other colors, it will be ok. You definitely can't trust .jpgs for that, and your posted images are converted to jpg I believe, so will have to deal with it.
    Last edited by passel; Jul 19th, 2019 at 04:27 PM.

  8. #8

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    The reason I have a colored mask picture is that I downloaded the template from the net. It was all black lines (the shapes) on white. At that time I had very little knowledge of what to do about anything until I got your first puzzle program and then I started to learn a little bit more but it was kind of over my head at the time so I went about with my original idea. I knew I wanted to use that template I downloaded but figured what good is it if every shape is white so I thought about coloring each shape area a different color then scan the picture of the colored shapes knowing that each color represented a different shape on the original picture. With this logic I couldn't have any two shapes on the template the same color as that would really get things screwed up.

    I played around with dilettante's example (post 6) using my own picture and mask picture and it works very fast however I don't see how to extract the individual pieces one at a time into a picturebox so I can save them. If I knew or knew how to get the X and Y of each colored shape I could use that to pull out the finished shapes and copy them another picturebox so as to save them for later use. For example the 1st shape is at 0,0; the 2nd shape at 69,0; the 3rd at 133,0 but I know that only because I manually got them from MS Paint but I see nowhere in the code how to figure them out.

    I modified your original puzzle program to use the puzzle shapes I made using the code from post 1 and was able to get all the pieces on the surface of picPuzzle (it's a 5 x 7 puzzle) but I has to make each piece one at a time using another program, saving the pieces, and then loading them up in the modified puzzle program but this is not what I really want to do. No one wants to use a puzzle program that takes a long time just making the pieces, saving them, then loading them up.
    Last edited by Code Dummy; Jul 20th, 2019 at 07:44 PM.

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    I finally extracted the individual pieces from picPuzzle to another picturebox and correctly position them and save each one as a bitmap file. However, the problem is that picPuzzle must be visible for the other picturebox to get the extracted image because I'm using TransparentBLT to draw the extracted image. In the game program I can't have picPuzzle visible to the player; only the original picture should be visible but none of the "behind-the-scenes" working pictureboxes. Is this possible?

  10. #10
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Is there a faster way

    Try AutoRedraw = True on your hidden PictureBox.

  11. #11

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    AutoRedraw is True on picPuzzle (you already did that). See below code

    Code:
       '
       '
       Set picPuzzle.Picture = Mask
        
       With picPuzzle
         .AutoRedraw = True
         
         MaskBlt .hDC, _
                 0, _
                 0, _
                 .ScaleWidth, _
                 .ScaleHeight, _
                 picOriginal.hDC, _
                 0, _
                 0, _
                 Mask.Handle, _
                 0, _
                 0
         
         .AutoRedraw = False
            
         Set .Picture = .Image
       End With
       '
       '
    Now, on the other picturebox see below code...

    Code:
       '
       '
     picPiece.AutoRedraw = True
    
     TransparentBlt ByVal picPiece.hDC, picPieceX, picPieceY, 139, 139, picPuzzle.hDC, mX, mY, 139, 139, vbBlack
    
     picPiece.AutoRedraw = False
    
     picPiece.Picture = picPiece.Image
       '
       '
    If picPuzzle is hidden then picPiece does not get the image. I don't know if the problem lies with picPuzzle or picPiece or both but I need both pictureboxes to be hidden from player

  12. #12

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    OK, never mind. Even though I don't see the copied image in picPiece it still gets saved on the disk OK

  13. #13

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    I got everything in order now and I changed the scanning method as passel suggested in his post 7 by just scanning around the piece instead of always starting at 0,0. The scanning time is now super fast. There's just one little problem remaining. When you make a puzzle with 35 pieces (5 x 7) the pieces just don't move too well. I thought it might have been something I did so I went and loaded up the original program and entered 7 x 7 puzzle and it's the same problem. The pieces don't move with the mouse. There's a lag or delay time from the time you move the mouse until the piece actually moves. Don't know what's causing this.

  14. #14
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Is there a faster way

    I'm not sure. I'm on another machine, so don't have a lot of my VB6 work on it, and none of these recent posts.
    So, I had to go back to this post and download the zip file on this machine. I then had to trackdown a picture to use, not a lot of them on this machine.

    I chose a .jpg photo I received from one of my nephews, which I guess would be a collection of grand nephews and nieces that attended some wedding. I figured it might be a "worse case" scenario, as it is a 4690 x 3193 pixel, 400 dpi image, around 9.78 MB in size as a jpg.

    But, I chose 7x7 and generated the puzzle, and as I drag the pieces around, they follow the mouse reasonably fine.

    You didn't perhaps comment out the picPuzzle.Refresh(s) in the picPuzzle_MouseMove sub?

  15. #15

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    I also went back to this post and downloaded your original zip file.

    I ran the program 6 times using the same picture

    1) 2x2 produces big pieces and they move very slow. They do not move with mouse and there's a delay before catching up with the mouse

    2) 3x3 same as 2x2

    3) 4x4 same as 2x2

    4) 5x5 pieces are smaller and move a little but better but still lag behind mouse

    5) 6x6 about the same as 5x5

    6) 7x7 slightly slower than 6x6

    Since you don't have this problem then I would suspect my machine except I have another puzzle program I got off the net and even with 50+ pieces it runs smooth and all pieces move with the mouse. Even the program I was working on which uses user controls moves the pieces with the mouse except the pieces smear which was the main reason I switched over to your program.

    I'm at a lost.

  16. #16
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Is there a faster way

    Well, technically there shouldn't be much of a difference in speed whether you have 4 big pieces (2x2) or 49 smaller pieces (7x7), as the mechanism is the same. The whole puzzle pic area is being redrawn from scratch each time, so the time to update should be fairly consistent, and apparently on your machine is consistently slow.

    To speed it up significantly,would probably involve some manual doublebuffering and limiting how much of the screen is updated by using dirty rectangle type updates, which is more involved than I want to work on right now.

    But, there are a couple of things you can do that may help a little, but whether you would see much improvement or not, I can't say.
    The two simple things I would try, is where the .Cls statement is used to clear picPuzzle before redrawing all the pieces, comment that out and fill the picture with a filled rectangle of the background color instead. That is usually faster.

    In Sub DrawThePieces()
    Code:
      ' picPuzzle.Cls   'clear the background
      picPuzzle.Line (0, 0)-(picPuzzle.ScaleWidth, picPuzzle.ScaleHeight), picPuzzle.BackColor, BF
    The second thing is to use bitblt instead of PaintPicture when drawing the pieces.
    Add the declaration for bitblt at the top of the file (grabbed the declaration that dilettante had in his code).
    Code:
    Private Declare Function BitBlt Lib "gdi32" ( _
        ByVal hdcDest As Long, _
        ByVal XDest As Long, _
        ByVal YDest As Long, _
        ByVal nWidth As Long, _
        ByVal nHeight As Long, _
        ByVal hdcSrc As Long, _
        ByVal XSrc As Long, _
        ByVal YSrc As Long, _
        Optional ByVal dwRop As RasterOpConstants = vbSrcCopy) As Long
    And update Sub DrawPiece to use it.
    Code:
    Private Sub DrawPiece(piece As Integer)
      'Draw the piece (with transparency) to its current location (current location being the center of the piece)
        With PieceInfo(piece)
       '   picPuzzle.PaintPicture picRotBlack(piece).Image, .X - ltOffset, .Y - ltOffset, , , , , , , vbSrcPaint
       '   picPuzzle.PaintPicture picRotWhite(piece).Image, .X - ltOffset, .Y - ltOffset, , , , , , , vbSrcAnd
          BitBlt picPuzzle.hdc, .X - ltOffset, .Y - ltOffset, fullsize, fullsize, picRotBlack(piece).hdc, 0, 0, vbSrcPaint
          BitBlt picPuzzle.hdc, .X - ltOffset, .Y - ltOffset, fullsize, fullsize, picRotWhite(piece).hdc, 0, 0, vbSrcAnd
          
        End With
    
    End Sub
    That should technically speed the drawing up, but probably not enough to be that much different on your machine.
    If I have some spare time in the future, maybe I'll look at changing the paradigm to use an extra backbuffer and limited updates when dragging to speed that up. That should have the most effect on dragging piece speed, but I have real work to content with, and other issues.

  17. #17

    Thread Starter
    Fanatic Member
    Join Date
    Apr 2017
    Posts
    554

    Re: Is there a faster way

    Yesterday I went to the program to make the changes you pointed out above but to my surprise the pieces moved fast and in sync with the mouse movement. I tested all sizes 2x2 to 7x7 and all pieces moved correctly. I didn't make the changes since I figured it wasn't necessary and I just left everything alone. Today, I wanted to do some more work on the program and when I started it up with a 7x7 puzzle it went back to the way it was before with the pieces not moving with the mouse. I have no idea what is causing this. No other program I have behaves this way. I even put a picturebox on the Form and used it's MouseMove event to move it around and it moved with the mouse as normal but the puzzle pieces do not. If this was a constant thing I might conclude that it's the code but I do not believe this to be since it goes back and forth to moving with the mouse to not moving with the mouse. It appears that on certain days it works fine and on other days it does not but I cannot see anything strange going on in the system itself that might cause this. Tomorrow it will probably run fine, who knows

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