Draw on top of Image

Hello. I have a custom control that loads a large image and displays it on screen (using scrollbars). The problem is that I want to draw on the visible part of the image (the part that is currently on screen) and I want to be able to modify the things that I'm drawing (i.e. if I draw a line on top of the image, I want to be able to remove that line later).

I did something like this, with the help of this forum: (where Image image = Image.FromFile("picture.jpg"); )

protected override void OnPaint(PaintEventArgs e)
{
   e.Graphics.SmoothingMode =
SmoothingMode.AntiAlias;
   Rectangle src = new Rectangle(-AutoScrollPosition.X, -AutoScrollPosition.Y, this.Width, this.Height);
   Rectangle dest = new Rectangle(0, 0, this.Width, this.Height);
   if (image == null) e.Graphics.FillRectangle(new SolidBrush(this.BackColor), dest);
   else
   {

        Graphics.FromImage(image).FillEllipse(new SolidBrush(Color.Red), x, y, rad, rad);
        e.Graphics.DrawImage(image, dest, src, GraphicsUnit.Pixel);
   }
}

If I do that, I draw on top on the picture, but I cannot erase the drawing later. Any ideea on how I can overcome that Thanks.



Answer this question

Draw on top of Image

  • Fantonis

    Hi Again Epsilon_Ro sorry I got distracted and hit send early...

    If you want more than a reversible line/frame, you'll have to start double buffering and keeping track of versions of the image. With this approach your paint simply outputs the portion of the image required to fill the viewport, and you keep track of changes in some kind of undo buffer.

    Another approach is to represent the user drawing (lines, rectangles, etc.) in an object model, and draw the objects after you've rendered the image. In essence you build a representation of what they can draw, and everytime you paint you draw "on top" of the image.

    Again HTH



  • Tzal

    Looks familiar. You're drawing the circle into the bitmap, you probably want to draw it on top of the bitmap and redraw it when the (x, y) coordinates change. Try this:

    protected override void OnPaint(PaintEventArgs e) {
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    Rectangle src = new Rectangle(-AutoScrollPosition.X, -AutoScrollPosition.Y, this.Width, this.Height);
    Rectangle dest = new Rectangle(0, 0, this.Width, this.Height);
    if (image == null) e.Graphics.FillRectangle(new SolidBrush(this.BackColor), dest);
    else {
    e.Graphics.DrawImage(image, dest, src, GraphicsUnit.Pixel);
    int sx = x + AutoScrollPosition.X;
    int sy = y + AutoScrollPosition.Y;
    e.Graphics.FillEllipse(new SolidBrush(Color.Red), sx, sy, rad, rad);
    }
    }
    protected override void OnScroll(ScrollEventArgs se) {
    // Force form to be repainted
    Invalidate();
    base.OnScroll(se);
    }

    If the x or y coordinate changes, call the Invalidate() method to force the form to repaint itself and display the circle in a different position. Like I did in the OnScroll() method, AutoScrollPosition changes. If the circle is large, you'll notice flicker. Search the forums for "NoBlinkPanel".


  • MSP.Saami

    Thank you so much, it works. This is what I wanted. Thanks again.
  • giddy

    Hello and thanks for your reply. I've looked over the ControlPaint.DrawReversibleFrame and it's not what I need. In my application, the user loads a big picture and then clicks on it, and everywhere he clicks, I want to draw a circle with FillElipse. I keep all these objects in an ArrayList and display them everytime the user clicks on the picture.

    Now the problem is that I have to allow the user to remove the circles he selects. I was thinking about keeping track of the changes (something like the Memento Design Pattern), but I don't know what circle the user will want to remove and I only need to remove that one and leave the other ones intact.

    When I draw the circles the way I wrote in my first post, they become a part of the image, and even though I remove the circle from the list, it's still beeing displayed on top of the image. This happens because I use the Graphics from the Image object when I first draw the circle. I need a way to display the picture and add circles on top of the picture, but without modifying it. At the mean time, when I draw the circles I need to use the image's coordinates, not the control's coordinates, because the image is much larger then the control (it has scrollbars). Thanks.


  • grellsworth

    Hi Epsilon_Ro

    There are a copuple of answers. If all you need is a straight line and/or a rectangle. use ControlPaint.DrawReversibleLine and ControlPaint.DrawReversibleFrame.

    If you draw a reversible frame @ 10,10 that is 50 x 50, and draw it again, it will remember what was underneath it.

    The trick is that a reversible line, also known as an ROP line (Raster Ops Paint or something like that) is in screen coordinates and will have to be adjusted for ScaleTransformations and the like. ROP's work well for simple selections, but have their limitations.

    HTH



  • Draw on top of Image