-->

How can erase a single Picture from a TCanvas?

2019-09-22 05:00发布

问题:

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?

回答1:

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.



回答2:

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.