# Thread: Is there a faster way

1. ## 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. ## 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, _
Optional ByVal dwRop As Long = QROP_FORESRCCOPY_BACKDSTCOPY) As Long

Private Sub Command1_Click()
Dim WidthPx As Long
Dim HeightPx As Long

With picDest
.AutoRedraw = True
WidthPx = ScaleX(picSource.Picture.Width, vbHimetric, vbPixels)
HeightPx = ScaleY(picSource.Picture.Height, vbHimetric, vbPixels)
Int((ScaleX(.ScaleWidth, ScaleMode, vbPixels) - WidthPx) / 2 + 0.5), _
Int((ScaleY(.ScaleHeight, ScaleMode, vbPixels) - HeightPx) / 2 + 0.5), _
WidthPx, _
HeightPx, _
picSource.hDC, _
0, _
0, _
0, _
0
.AutoRedraw = False
Set .Picture = .Image
End With

Command1.Enabled = False
End Sub```

Bam!

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

3. ## 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. ## 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
'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.

5. ## 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

6. ## Re: Is there a faster way

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

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.

7. ## 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.

8. ## Re: Is there a faster way

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.

9. ## 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. ## Re: Is there a faster way

Try AutoRedraw = True on your hidden PictureBox.

11. ## Re: Is there a faster way

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

Code:
```   '
'

With picPuzzle
.AutoRedraw = True

0, _
0, _
.ScaleWidth, _
.ScaleHeight, _
picOriginal.hDC, _
0, _
0, _
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. ## 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. ## 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. ## 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. ## Re: Is there a faster way

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. ## 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. ## 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
•

Featured