-4

I have in my form, a Transparent Panel which contains a Canvas (I think its implementation is not really important here; someone tell me otherwise). I'm drawing many Bitmaps on this Canvas, bya using:

Panel.Canvas.Draw(Image.Left, Image.Top, Image.Picture.Bitmap, Opacity);
//Image is a TImage instance
//Opacity is an integer 0..255

But when I do some change in the TImage, like redimensioning or moving around the form, I redaw its Picture in the panel. The issue here is that the old positioned image remains on the canvas. Now my question: There is a way to erase a single Bitmap on the Canvas? If so, what it would be? If not, there is a way to erase all the canvas's contents? This way I could redraw the remain pictures.

Edit:

At the end, my problem were on the Transparent Panel I was using. Its Paintmethod was bogous and did not invalidate its canvas to erase the controls on it. Problem solved, anyway. What should I do with the question?

Guill
  • 350
  • 5
  • 17
  • There is no way to "clear out" something from canvas. You can only ask the panel to repaint e.g. by calling `Panel.Invalidate` and the next you won't draw that to be hidden image. Or you can draw something over that area. – TLama Feb 07 '14 at 15:05
  • What do `Invalidate` do exactly? I will update my question to a more specifc sample. – Guill Feb 07 '14 at 15:08
  • In short it asks the system to repaint the control on which the system responds by sending the `WM_PAINT` message which is usually published by controls as the `OnPaint` event. In such event you should draw all your stuff. But this doesn't apply for panel. Panel is not primarily used for custom drawing. – TLama Feb 07 '14 at 15:15
  • I see. But using it in a large loop for example would cause much "flickering". And did not solve my problem because it does redraw the old positioned pictures as well. – Guill Feb 07 '14 at 15:18
  • A canvas does not draw anything by itself (someone might be right). – Sertac Akyuz Feb 07 '14 at 15:21
  • Where did someone said that? – Guill Feb 07 '14 at 15:26
  • Erasing = drawing a rectangle with solid background color brush and clear pen. – Free Consulting Feb 07 '14 at 15:27
  • @Guill - I don't know where someone said that, I only know someone, somewhere said "the implementation is important". What I meant is, it is your code what's drawing on the canvas. – Sertac Akyuz Feb 07 '14 at 15:33
  • I presented it up there. I use that line to add pictures to the Canvas. But i Think i know what you mean. I did not informed it happens 60 times a sec. Like in a side loop. Got? – Guill Feb 07 '14 at 16:33
  • @Guill - Since after clearing the canvas, old positioned pictures are drawn, that loop is what seems to be drawing them. – Sertac Akyuz Feb 07 '14 at 20:11
  • But they are no longer there. I think invalidate do not works the way I'm calling... Nothing in the code could possibly store the old positions of the image. What makes me think they were not erased. Or, the canvas was not cleared with `Invalidate`. – Guill Feb 07 '14 at 21:09
  • @Guill - Yeah you're right, 'invalidate' does not clear anything, it just marks the canvas surface for update for the next paint cycle. You should be filling a color to where you want to erase. – Sertac Akyuz Feb 07 '14 at 21:20

2 Answers2

4

Not really, because the canvas doesn't keep track of distinct images.

However, you can easily "clear" any part of your canvas simply by drawing over it.
This will allow you to redraw all remaining pictures.

This could however be quite time consuming if there are many pictures and a user is busy dragging a single picture around; as this would result in a lot of redrawing for each slight position change of the moving image.

One option is to draw the "active" image with an XOR mask while it's being adjusted. Then it can be erased by simply redrawing in the same position with an XOR mask again. This has the disadvantage that colours become distorted, but it is very efficient.

Another option is to make a copy of part of the canvas where you intend drawing the active picture before you draw. Then you have a simple mechanism to erase the new picture by redrawing the copy in the correct position.


Edit: Detailed explanation of last option in response to comment from Guill:

And how could I draw over it since I have no background? There is some kind of Transparent brush?

Suppose you want to draw and move a picture (perhaps a blue rectangle 20x60):

  • Let's assume you start with a blank Canvas, clWhite background.
  • The initial position is (25,75), so:
  • (A) First copy the 20x60 Rect at (25,75) on your Canvas.
  • The copy will of course be entirely white, but that is exactly what your background looked like.
  • Now draw your rectangle at that position.

Cool, first bit done. now you want to move the rectangle to (40,90):

  • Draw your copied image at (25,75). NB No transparency at all! You want to restore your Canvas to the state at (A), before you drew the blue rectangle.
  • Copy the 20x60 Rect at (40,90). (Again it will be entirely White)
  • Draw your blue rectangle at (40,90).

Ok, so far so good, but our copies are always White. So let's add a second rectangle. (This time red and 80x10.)

  • We'll discard our current copy because we no longer want to move the blue rectangle.
  • We want to place the red rectangle at (45,95), so it overlaps the blue.
  • (B) Copy the 80x10 Rect at (45,95)
  • Note, this time part of the copy is blue, the rest is white.
  • Now draw the red rectangle at (45,95)

As a final step, lets decrease the size of the red rectangle to 5x5:

  • Draw your copied image (45,95). NB Again It's very important that we do not use transparency, because we're trying to restore the portion of the image where we drew the red rectangle back to what it looked like in (B).
  • Copy the 5x5 Rect at (45,95)
  • Draw the smaller red rectangle.

This is a simple rinse-repeat process. As it so happens, this is the same technique used by Windows to draw your mouse cursor as it moves.

Side note: If the image you're drawing is an irregular shape, it doesn't matter. Provided your backup rectangle fully covers the image you're drawing, this technique works.

Community
  • 1
  • 1
Disillusioned
  • 14,635
  • 3
  • 43
  • 77
  • About storing each image I already have noted... And how could I draw over it since I have no background? There is some kind of Transparent brush?(Because if there is, it really should be called a Eraser...) When I said "move round" I mean 'Top' and 'Left' changes at runtime. Not by mouse dragging. – Guill Feb 07 '14 at 16:41
  • @Guill **(1)** Of course you have a background. Even if you didn't draw one yourself. The initial state of your Canvas **is** your background. **(2)** For the purposes of moving one image, you absolutely **do not want** transparency. (I'll try give a more detailed explanation in the answer.) **(3)** It doesn't matter if you manipulate top/left directly, or mouse dragging is used to change top/left; the principle is exactly the same. – Disillusioned Feb 07 '14 at 18:10
  • Well, I did mean I have a total irregular Background. But if `copy` do copy any rectangle, I could Create a new rectangle, then copy that part of the background, whatever it is and draw this part of the background over the old positioned picture. Is that what you mean? (I could do an smiling emoticon now) – Guill Feb 07 '14 at 21:55
  • What is missing here is any commentary on the use of `Panel.Canvas.Draw()`. Which can only mean drawing outside of paint cycles. – David Heffernan Feb 07 '14 at 22:11
3

Your entire approach to painting is incorrect. Windows painting surfaces do not remember their contents. The design of painting in Windows requires each window to be able to re-paint itself when it is asked, that is when it is sent a WM_PAINT message. You must respond to WM_PAINT by painting what you are asked to paint. You are currently breaking that rule. You'll need to re-design your program to fit in with the system.

Do the following:

  1. Add a TPaintBox to your panel.
  2. Add an OnPaint event handler to the paint box that performs all the painting.
  3. When you need to force a re-paint, call Invalidate on the paint box.

That's it. Note that there is no mention of WM_PAINT here. That's because the VCL wraps that all up for you and presents it in a more digestible form.

Required reading on this subject (and many others) is Petzold's classic tome, Programming Windows.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • You must be tired of my face here... But once again, actually on the same project, I'm doing pretty unusual things. But that one specially I think I can follow hardly the rules. About the surfaces not distincting the images, I knew: that is why I should need to Clear. In order to preserve peace in this page and my repitation as begginer untouched. I really should talk directly to you. Do you have some time to me in a chat or something? If not, lets continue. – Guill Feb 07 '14 at 16:44
  • 1
    I don't do chat. I don't know why you think you should be exempt from following the rules. Why can't you manage to paint in response to `WM_PAINT`? You ask Craig if there is a transparent brush. This indicates that you do not understand how Windows painting works. Transparency is achieved by things on the bottom drawing before things on the top. Once that has happened, you cannot un-draw because what was there before is gone. Devices don't maintain layers in the way that paint programs do. On the other hand, if you just followed the rules it would work, trivially. – David Heffernan Feb 07 '14 at 16:46
  • 1
    Really, and please don't take this the wrong way, your next step is to recognise that you don't understand painting, and go back to basics. Before you make any real progress you must understand it from the bottom up. – David Heffernan Feb 07 '14 at 16:54
  • I think that because "the rules" are note rules after all. Delphi compiler is there for you to use how you think its better. We usually follow "the rules" because its a very good pratice. I'm not sure if I understand `WM_PAINT` message. And I thougth someone would be able to help without know All The Road. So I shall open another question with more information about my stuff. Do you agree? – Guill Feb 07 '14 at 16:54
  • I don't think asking more questions will help. What will help will be to acknowledge that you don't understand this at all, and for your to seek to gain understanding. You cannot simply decide to make your own rules up because you are not in a position to do so. Windows will paint your window. Windows will sent you `WM_PAINT` messages. If you choose to ignore them because you prefer to do it your way, your program will simply never work. Pretending that your program exists in isolation to the system is an attitude that is doomed to failure. – David Heffernan Feb 07 '14 at 16:56
  • Just to expose: I think everyone has position to inovate. So I think you should not try to shut them down, but it is personal. Then... on. I will study WM_PAINT. And my project is the total way reverse of "dommed to failure". So thank you and good bye. – Guill Feb 07 '14 at 17:01
  • I'm not trying to stop you from innovating. But Windows is going to want to re-paint your window when another window passes over the top of it. And at that point, you'll get a `WM_PAINT` message. It's entirely up to you what to do with it. – David Heffernan Feb 07 '14 at 17:05
  • 1
    @Guill I concur with David. Innovation is one thing. But you don't buy yourself a caravan and try race it through 1m wide alley-ways. My point is don't try fight against the frame-work Windows and Delphi give you. If you think you can do better, great! But do yourself a favour: first learn **how** and **why** this framework works. Then you'll be in a much better position to develop your improved framework. – Disillusioned Feb 07 '14 at 18:16
  • @Guill On innovation: Your innovation will come from the functionality you offer in your application - not from the changes you make to the framework. I've been using and developing software for many years. I've seen and used a lot of very innovative programs. They were great for the features provided. Some however fought the framework. I remember one in particular tried to control the [input queue](http://stackoverflow.com/questions/21056860/input-trigger-verifying-thread#comment32690279_21056860)... It hogged the CPU..... ***I don't use it anymore***. – Disillusioned Feb 07 '14 at 18:23
  • @Craig, I totally understand what you mean. But you all are assuming I'm fighting the system. I just did not know how to use that specific part of the framework as I said, I'm gonna study it. People really can't blame me by not know nicely how to learn. About the many other "rules" I have been breaking. It was not because I want. But because the framework itself do not support the things I'm doing in a high level. So it is needed some "cheating" over the Windows. Better than using 3rd Party components, I'm doing my own tools. A hard task to a begginer. That's why I ask for you guys. – Guill Feb 07 '14 at 22:03
  • Again, without wishing to seem rude, but you don't understand the framework. Therefore you cannot be in a position to judge whether or not it supports your needs. I'd be astounded if you could not do what you need with routine painting. – David Heffernan Feb 07 '14 at 22:10
  • Well, then hold your pants, because I just tryied it and I was not able. But I recognize I "overcheated" stuff and I am going to do this in other way. It's already done actually. Anyway, thanks for everyone's help. I will try to study more before asking questions here again. (Why seems like I do say "I" so more times than everyone?) – Guill Feb 07 '14 at 22:17