Solved

Erasing certain portion of picture box from Transparent Blit function

Posted on 1999-01-04
24
311 Views
Last Modified: 2010-05-03
I have two picture boxes Pic1 and pic2 and pic2 is inside pic1.
I call a transparent blit function to make pic2 appear in pic1 as transparent:
TransparentBlt pic1.hdc,pic1.hdc,pic2.hdc,R,40,0,vbWhite

I can let the user changes pic2  through a command button and everytime it is changed, i can just pic1.cls and call the transparent blt function again with new coordinates.
But the PROBLEM is i also let the user modify pic1 (as in Paint) whereby the user can draw lines or dots on pic1.
So how can I keep track of what is drawn just now so that when i cls the pic1, i still get the existing picture with whatever which was drawn and only pic2 changes?
Or how do I just erase that certain portion of pic2 which is blit on pic1?

pls help!
0
Comment
Question by:tezi
  • 10
  • 10
  • 2
  • +2
24 Comments
 
LVL 13

Expert Comment

by:Mirkwood
Comment Utility
Not very clear question.
Why not keep the original of pic1 stored somewhere else.
So pic1 is the original
So pic2 is the modified pic1
and pic3 is the transparent version
0
 
LVL 1

Expert Comment

by:wdavidsmith
Comment Utility
You might also disable the mouse while it's over the picture box... set the "Locked" property to True, or otherwise prohibit mouse entry into the picture box.

Just an idea...
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
You might try setting Pic1's AutoRedraw property to True.  However,  this could interfer with your users drawing on it in the first place (but it is easy enough to try).
0
 

Author Comment

by:tezi
Comment Utility
What I want is:
pic1 now contains pic2 which has been blit transparently on pic1. So now I would like the user to be able to change the location of pic2 (for example 3 pixels to the right)

But if I call back the transparent blit function, the previous blit picture will still be there and my current pic2 will just overlay on it. I wanted to erase the previous blit picture location.
The cls method does not apply as I can let the user draw on pic1. So if I just cls the pic1, I would lost track of the location of what the user draws.

Any ideas on how to clear that particular portion of pic2? Or is there a method to keep track of what is drawn?
would appreciate if example of codes is given...
0
 
LVL 13

Expert Comment

by:Mirkwood
Comment Utility
Try the following:
-copy of the current contents of pic1 to another picture (paintpicture/bitblt?)
-cls picture1
-copy it backed but now shifted to the right position (paintpicture/bitblt?)
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
Mirkwood's suggestion is the easiest, but depending on the size of the picture in Pic1 it could be slow, you could amend his suggestion to only bitblt the rectangle that is going to be covered by Pic2 to another hidden picture, do this before you bitblt Pic2 to it's location on Pic1.  Then when you are about to move Pic2 again, bitblt the hidden saved Pic1 rectangle back to it's position in Pic1, bitblt the new rectangle that is going to be covered by Pic2 to the hidden picture, then bitblt pic2 to the new location.

You'll have to keep track of the coordinates of the old location and the new location, but it should be faster bitblt-ing just a small rectangle of Pic1 than the whole contents of Pic1 and this should keep the amount of "flash" down.  

MD
0
 
LVL 2

Expert Comment

by:polygon
Comment Utility
Ok, I couldn't understand the question completely, but it seems to me that the following lines may help you (sorry if you already know what I will say):

When Pic.Autoredraw is set to false, anything you draw on the picture remains visible (as opposed to that when Autoredraw is true, the contents of the picture is reset to the original picture). So, the original contents of the picture is kept in Pic.Picture property and the actual contents in Pic.Image property.
I suppose your Pic.Autoredraw is False as you want to draw on the picture.

Ok, sorry if I've wasted some of your time, but who knows, I may have helped.
0
 

Author Comment

by:tezi
Comment Utility
i haven't try the suggestions yet... but have you all guys taken into account that I need to have the previous blit picture on pic1 removed first, then only i changed the contents of pic2....

just to remind, no overlaying of the blit pictures...

0
 

Author Comment

by:tezi
Comment Utility
mdougan, are you suggesting to me that I should use the bitblt method from the hidden picture to blit exactly on pic1 so that pic1 will be disappear from view?
can you give me example of code to perform this?

0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
The technique I'm talking about is a well known technique for doing animation.  Basically you have a background (in your case this is Pic1) that you want to save a rectangle from.  Then you paint (BitBlt) something (Pic2) on top of that background.  Now, when you want to move Pic2 to another location you need to restore the background to it's previous state, and you do this by copying the rectangle that you saved back to where you had first painted Pic2.  Then you can save background at the next location for Pic2 and so on.  I don't have a code sample, but I'll do a pseudo code example below:

public Sub SaveBackground(X as Long, Y as Long, W as Long, H as Long)

    BitBlt(from Pic1, X, Y, for W, and H, to PicSave, 0, 0, W, H)

end sub

public Sub RestoreBackground(X as Long, Y as Long, W as Long, H as Long)

    BitBlt(from PicSave,0, 0, for W, and H, to Pic1, X, Y, W, H)

end sub    

Then, in your code somewhere say you have a loop that is going to move Pic2 5 times to the right:

Dim i as Integer
Dim X as Long
Dim Y as Long
Dim W as Long
Dim H as Long

W = Pic2.Width
H = Pic2.Height

for i = 1 to 5
     SaveBackground((i * 50), 50, W, H)
     BitBlt(from Pic2, 0, 0, W, Ht, to Pic1, (i*50), 50, W, H)
     Sleep(100)
     RestoreBackground((i*50), 50, W, H)
next i

Now, the things I have as parms for the BitBlt are not correct, they're just there to show you what you are trying to do, you'll have to look BitBlt up to get the correct parms, but this pseudo code should give you the right idea.  Also, since you want to let the user move Pic2 around, you probably wont be doing this in a loop, so you'll need to save the old position of Pic2, and the new position of Pic2 and then do this Save and Restore just before you BitBlt Pic2 to the new location (then move the new position variables into the old position variables for the next time they move.

Hope this helps?

MD
0
 

Author Comment

by:tezi
Comment Utility
well mdougan
i'm almost close to a solution. Just one thing, actually i also allow the user to draw on pic1.
So i thought of saving the pic1.picture which includes the drawn lines  with the SavePicture method but it can't. I guess it only saves the pic1.picture property and not what is drawn on it.
How bout the adjustment of the autoredraw property, should I set it to True or False to save what is drawn?
I tried both ways but still fails to save what is drawn on pic1 to pic1.picture...

does the bitblit method copies what is drawn on the background as well? haven't tried it yet...

just hoping that the above suggestion would work so that I can use the Savepicture method to save the drawn things... and i just change the background just as you suggested.

any comments?
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
I believe that BitBlt will copy anything that is drawn on Pic1 to the SavePic hidden picture, and then BitBlt back from SavePic to Pic1 will restore what the user has drawn.  But I haven't tried this to be sure.  There is also the chance that if you wanted to save what the user has drawn, that you could BitBlt the whole Pic1 to SavePic, and then (because I think that BitBlt puts the image in the "picture" property, that you could then do a Savepicture on SavePic to save the image to a file.  I'll see if I can test this out sometime today, and I'll be able to confirm or deny it!

I also believe that you want to keep the autoredraw property to false.  Because when you have autoredraw set to true, then VB actually keeps a copy of the original contents of Pic1 in memory, and whenever the screen needs repainting, it repaints Pic1 (so, if you haven't saved what the user has drawn, you would lose it).  I think this would get in the way of what you are trying to do.  However, whenever I'm having trouble with pictures etc. I always try changing that first, just to see if it makes a difference.

I'll get back to you later.

MD
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 18

Accepted Solution

by:
mdougan earned 200 total points
Comment Utility
OK, here's the scoop.  The above technique is good for saving and restoring the background of Pic1 - complete with user drawings, to a SavePic before drawing Pic2 on top of it.  Then, when you wish to move Pic2 to another location, you restore the saved rectangle of Pic1, save the background at the new location, then move Pic2 to the new location.  The following code will demonstrate this.  To use this create a standard project.  Add a Module.  In the code for the module place the Win32 API declares:

Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Declare Function LineTo Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long) As Long

Public Const SRCCOPY = &HCC0020

Then in the form, create 3 picture Boxes.  Name them Pic1, Pic2, and PicSave.  Set each of their ScaleModes to Pixel, set each of their AutoRedraws to False.  Put a large bitmap in Pic1, put a small bitmap in Pic2.  Adjust the size of Pic2 to just fit the size of the bitmap.  Set the ScaleMode  of the form to Pixel.  Create a command button called Command1.  Then, paste the following code into it.

Use the arrow keys to move pic2 around inside of pic1 and watch what is being saved in PicSave.  Click on the command button from time to time to draw a line on Pic1 (you will then have to click inside of Pic1 again to give it the focus, so that your arrow buttons continue to work).  You will see that the routines save the lines that you have drawn and redraws them when pic2 moves off of them.  Now, the only thing that does not work is that SavePicture does not save the changes to Pic1 in a file.  Perhaps someone can suggest how to get the information drawn on Pic1 using it's hDC can be transfered to it's picture property or image property so that SavePicture could work.

Also, you could probably use the Transparent Blit to move Pic2 around Pic1 transparently.  Or you can use BitBlt and a couple of other flags instead of SRCCOPY.  If you want to see a copy of this type of transparent blitting, check out the VB\SAMPLES\COMPTOOLS\CALLDLLS project.


Dim OldX As Long
Dim OldY As Long
Dim NewX As Long
Dim NewY As Long
Dim FirstTime As Boolean

Private Sub Command1_Click()
Dim RC As Long

RC = LineTo(Pic1.hDC, NewX, NewY)

End Sub

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
   
    Select Case KeyCode
        Case vbKeyLeft
         NewX = NewX - 5
        Case vbKeyRight
         NewX = NewX + 5
        Case vbKeyUp
         NewY = NewY - 5
        Case vbKeyDown
         NewY = NewY + 5
    End Select
    PaintPic2
   
End Sub

Private Sub Form_Load()
    FirstTime = True
End Sub

Public Sub PaintPic2()
Dim RC As Long
If FirstTime = True Then
   FirstTime = False
   RC = BitBlt(PicSave.hDC, 0, 0, Pic2.Width, Pic2.Height, Pic1.hDC, 0, 0, SRCCOPY)
   Exit Sub
End If
   
' Restore saved rectangle to Pic1
   RC = BitBlt(Pic1.hDC, OldX, OldY, Pic2.Width, Pic2.Height, PicSave.hDC, 0, 0, SRCCOPY)
' Save a copy of the rectangle that is about to be overdrawn
   RC = BitBlt(PicSave.hDC, 0, 0, Pic2.Width, Pic2.Height, Pic1.hDC, NewX, NewY, SRCCOPY)
' Draw pic2 onto pic1
   RC = BitBlt(Pic1.hDC, NewX, NewY, Pic2.Width, Pic2.Height, Pic2.hDC, 0, 0, SRCCOPY)
   OldX = NewX
   OldY = NewY
End Sub
0
 

Author Comment

by:tezi
Comment Utility
mdougan,
i still can't get your codes to run, because the NewX,NewY ,OldX and OldY has not been assigned values...
and I am busy at the moment.
so can you give me the whole workable codes with the values of the X and Y?

actually I'm still doubtful whether it will work because what I wanted to use is the Transparent Blit function and whatever is drawn previously can still be seen...

I'm also running out of time to give a working copy to the user...
worst come to worst, I think I'll disable this function until I can get it done.

another thing, where are you actually referring  to for this directory? VB\SAMPLES\COMPTOOLS\CALLDLLS

Thank you for your time.
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
Hi,

The code I gave you above did work for me in VB 5.0.  As far as not initializing OldX, OldY, NewX, NewY, I was counting on VBs initialization of numeric variables to zero.  So, each of these values starts out at zero.  So, nothing happens until the user presses the left or right or up or down arrows.  When that happens, depending on which arrow was pressed, the value of NewX or NewY is incremented or decremented by 5 (if this wasn't a quick and dirty example, I'd check to make sure that NewX and NewY were not less than zero, and if so, make them zero).

I'm not familiar with the transparent blit function that you are talking about, but I assume that you would use it at the place where I'm blitting Pic2 onto Pic1, and I think everything else should work exactly the same (because you still need to save the background before blitting Pic2 on top of it).  

' Draw pic2 onto pic1
   RC = BitBlt(Pic1.hDC, NewX, NewY, Pic2.Width, Pic2.Height, Pic2.hDC, 0, 0, SRCCOPY)

(replace this call with your transparent blit)

The path I gave you for the sample was slightly wrong.  It should be:
VB\SAMPLES\COMPTOOL\CALLDLLS

where comptool does not have an "s" at the end.  This should be under your VB directory.  By default, VB is installed under Program Files\DevStudio, so, the whole path should be:

C:\Program Files\DevStudio\VB\SAMPLES\COMPTOOL\CALLDLLS

That is assuming that you told VB to install the sample programs when you installed it.  

Make sure you read through my instructions above carefully.  You need to make sure that Pic2 is smaller than Pic1 (and I made SavePic bigger than both) to get this sample to work.

Let me know if you still can't get it to work for you.

MD
0
 

Author Comment

by:tezi
Comment Utility
Hi mdougan,
I still can't get the codes running, I don't know why?! Even the form_keydown event is not working...
Can you just mail me your copy of the testing program?
My email account is
tanyujin@usa.net

Thank you in advance!
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
I'll look for it, but I think it was on a different development machine.  Check to make sure that your form has the Keypreview property set to true.  

Also, I'm using VB 5.0, so, the declare statements are the 32-Bit versions.  If you are using VB3.O or VB4.0 16-bit, then the declare statements have to change.

MD
0
 

Author Comment

by:tezi
Comment Utility
Hi mdougan

your codes work very perfectly, however there is one big major problem. I also have a few other picture boxes blit on pic1. So when I save the background, I also manage to save them as well. This is a good thing.

The bad thing is when I paste back the background on pic1, the partially "cut" pic3 which is blit on pic1 and is copied on picsAve will appear with the border. so pic3 looks kinda cut off into half.

One more thing, the user can draw inside the blit pictures. And when the background is pasted on it, the drawn lines is gone. :-(

ANy ideas?


0
 

Author Comment

by:tezi
Comment Utility
Just to ask for your opinion Mdougan...
Alright, what is your opinion if I try like this...

I create a pictemp same size as pic1. I will put it on top of pic1 but I set the pictemp Visible = False and Enabled = False.

So everything will still be blit on pic1. However, when the user starts to draw, I will bitblt everything to pictemp. Then I set the Visible = True but the Enabled = False.
Now I cls pic1. So now pictemp can be seen but the user is actually drawing on pic1. Therefore, pic1 will contain whatever which is drawn.

Now when the user moves some other picture boxes, for eg pic3 or pic4 (inside pic1), I just cls the whole pictemp, blit everything all over again for the new locations and blit whatever which is drawn in pic1 to pictemp.

What do u think of this? coz I really in time pressure now. Thanks in advance.
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
Yikes!?!  This thing is getting pretty complicated!  Perhaps you can tell me in more detail what your application does, and maybe we can think about other possible ways you can do this.  If you want to keep the thread here, that's fine, or you can e-mail me at mdougan@earthlink.net

MD

P.S. I was getting a problem with the border thing when I had a 3D border on Pic2.  Turn off the 3D and any Border Style for the little pics.
0
 

Author Comment

by:tezi
Comment Utility
mdougan

it's not exactly borders.... i 've already turn off all borders...the pic1 just appear with a white line( as a result of the pasting) on pic3... so pic3 (which is also a blit one on pic1 looks "cut into half"...

well probably there are other solutions...
what do u think of the one I suggested?
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
I think you are on the right track, but I don't think I'd put pictemp on top of pic1, I think I'd put it on the bottom.  I would have the background picture there, and anytime the user "draws" on anything - Pic1 or Pic2 or Pic3, I would actually draw in two places, Pic1, 2 or 3 AND on PicTemp.  Then, whenever you are restoring the background, just blit it from the PicTemp (instead of PicSave as we had in the code above).  Do you get what I'm suggesting?

MD
0
 

Author Comment

by:tezi
Comment Utility
mdougan

I am confused about the sequence of events which should come first already...

if you copy what was drawn on pic1 and  pictemp, how do you cls everything, blit everything to its new locations and yet blit back whatever which is drawn on pic1?

shouldn't either one of the picture boxes, eg pictemp be a buffer to just store the drawn lines or dots and nothing else?

can you just brief me on the sequence on which should happen first?
0
 
LVL 18

Expert Comment

by:mdougan
Comment Utility
I guess it depends on if you are giving the user the chance to clear the background of all of the drawing.  If so, then you do need to store a clean background somewhere.  But what I would do is this:

Put a background picture in Pic1 and PicTemp
(put PicTemp exactly under Pic1)

Everytime the user draws on Pic1, or 2 or 3, draw on those pics, but also draw on PicTemp - so that PicTemp has all of the user's drawings on it (plus the background).  When you want to clear the user's drawings, restore the background picture for Pic1 and PicTemp - either from another Picture or load the bmp again.

Everytime the user moves Pic2 or 3, then you can skip what we did before about blitting to PicSave (because we have already saved the drawing by drawing to PicTemp), however, in the step where you restore the background by blitting from PicSave to Pic1, then you would blit from PicTemp to Pic1 instead.  And you would blit the exact rectangle from PicTemp that you are trying to restore in Pic1.  So, change the code above to:

' you can get rid of the first time save code

' Restore saved rectangle to Pic1
'(note PicSave changed to PicTemp, and now using OldX
' and OldY instead of 0, 0)
   RC = BitBlt(Pic1.hDC, OldX, OldY, Pic2.Width, Pic2.Height, PicTemp.hDC, OldX, OldY, SRCCOPY)

' get rid of the save step, no longer necessary

' Draw pic2 onto pic1
   RC = BitBlt(Pic1.hDC, NewX, NewY, Pic2.Width, Pic2.Height, Pic2.hDC, 0, 0, SRCCOPY)

The only thing that is tricky, is that when the user draws on Pic2 or Pic3, you have to translate the coordinates to the proper coordinates on Pic1 to do the drawing (on Pic1 and PicTemp) - you do this by adding the X coordinate in the MouseDown for Pic2 to the Left property for Pic2 to give you the X coordinate on Pic1, same for the Y and the Top property.


0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Introduction While answering a recent question about filtering a custom class collection, I realized that this could be accomplished with very little code by using the ScriptControl (SC) library.  This article will introduce you to the SC library a…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now