GDI+ Invalidate() artifacts
-
Hi all, Could someone please explain to me why the following code doesnt work?
Rectangle r = new Rectangle(0, 0, 100, 100); Pen p = Pens.Black; private void Form1_Load(object sender, EventArgs e) { this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint,true); //a.AddLine(new Point(10, 10), new Point(100, 10)); } protected override void OnMouseMove(MouseEventArgs e) { //Invalidate old location Invalidate(r); r.Location = e.Location; //Invalidate new location Invalidate(r); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //Clear clipping rect e.Graphics.FillRectangle(Brushes.White, r); //Draw invalidated region e.Graphics.DrawRectangle(p, r); //Method will draw the right and bottom side out of clipping rect }
When i run it, the rectangles bottom and right hand side are drawn outside the clipping rectangle. I thought that if something was drawn outside the clipping rectangle, it would be discarded. However this doesnt seem to be the case here. when i move the mouse, I move the rectangles location also. Before i change the location of the rect, i invalidate it, and then invalidate again once change has been made. In the drawing routine i wipe the clipping rect with background colour, but for some reason artifacts ouside the clipping area remain. The end result of the mouse move is an artifact trail, similar to a mouse trail and exactly like what happens, when you win at ms solitare. It would appear that im not invalidating the right area, but i dont understand why! I have done a fair bit of graphics programming before, and have never experienced any problem like this. If im invalidating the old bounds before changing them for the new bounds, it should be removing the old screen. If i change the drawing code to make sure that the rectangle is drawn inside the clipping rect, the problem doesnt happen. But I just cant understand why the problem occurs anyway. Surely invalidate should cause the overlapping line to be removed? Here is the code that has been modified not to overdraw the clipping region:protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e);
-
Hi all, Could someone please explain to me why the following code doesnt work?
Rectangle r = new Rectangle(0, 0, 100, 100); Pen p = Pens.Black; private void Form1_Load(object sender, EventArgs e) { this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint,true); //a.AddLine(new Point(10, 10), new Point(100, 10)); } protected override void OnMouseMove(MouseEventArgs e) { //Invalidate old location Invalidate(r); r.Location = e.Location; //Invalidate new location Invalidate(r); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //Clear clipping rect e.Graphics.FillRectangle(Brushes.White, r); //Draw invalidated region e.Graphics.DrawRectangle(p, r); //Method will draw the right and bottom side out of clipping rect }
When i run it, the rectangles bottom and right hand side are drawn outside the clipping rectangle. I thought that if something was drawn outside the clipping rectangle, it would be discarded. However this doesnt seem to be the case here. when i move the mouse, I move the rectangles location also. Before i change the location of the rect, i invalidate it, and then invalidate again once change has been made. In the drawing routine i wipe the clipping rect with background colour, but for some reason artifacts ouside the clipping area remain. The end result of the mouse move is an artifact trail, similar to a mouse trail and exactly like what happens, when you win at ms solitare. It would appear that im not invalidating the right area, but i dont understand why! I have done a fair bit of graphics programming before, and have never experienced any problem like this. If im invalidating the old bounds before changing them for the new bounds, it should be removing the old screen. If i change the drawing code to make sure that the rectangle is drawn inside the clipping rect, the problem doesnt happen. But I just cant understand why the problem occurs anyway. Surely invalidate should cause the overlapping line to be removed? Here is the code that has been modified not to overdraw the clipping region:protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e);
Without running your code, I'm thinking the problem is here:
protected override void OnMouseMove(MouseEventArgs e) { //Invalidate old location Invalidate(r); r.Location = e.Location; //Invalidate new location Invalidate(r); }
The first call to
Invalidate(r)
fills the rectangle then draws it. You then move the rectangle and callInvalidate(r)
again which fills the relocated rectangle and then draws it - however, there is no code that is repainting over the old rectangle. You need to keep track of the area that was under the original rectangle and invalidate the union of the old rectangle and the new one (you will only need to call invaldiate once inOnMouseMove
when you do this). Also, the call toe.Graphics.FillRectangle(Brushes.White, r)
in the OnPaint method will need to be adjusted to fill the clip region (can't remember the property name off hand, but should be easy enough to find).----- In the land of the blind, the one eyed man is king.
-
Without running your code, I'm thinking the problem is here:
protected override void OnMouseMove(MouseEventArgs e) { //Invalidate old location Invalidate(r); r.Location = e.Location; //Invalidate new location Invalidate(r); }
The first call to
Invalidate(r)
fills the rectangle then draws it. You then move the rectangle and callInvalidate(r)
again which fills the relocated rectangle and then draws it - however, there is no code that is repainting over the old rectangle. You need to keep track of the area that was under the original rectangle and invalidate the union of the old rectangle and the new one (you will only need to call invaldiate once inOnMouseMove
when you do this). Also, the call toe.Graphics.FillRectangle(Brushes.White, r)
in the OnPaint method will need to be adjusted to fill the clip region (can't remember the property name off hand, but should be easy enough to find).----- In the land of the blind, the one eyed man is king.
Thanks very much for replying. "Also, the call to e.Graphics.FillRectangle(Brushes.White, r) in the OnPaint method will need to be adjusted to fill the clip region (can't remember the property name off hand, but should be easy enough to find)." Yeah i realised i had done that! r instead of e.ClipRectangle property, but it doesnt actually make any difference any way. "You need to keep track of the area that was under the original rectangle and invalidate the union of the old rectangle and the new one.." Thats what the first invalidate does! When you call invalidate, it wont invalidate immediately. If you were to call refresh() then it would go straight to the paint code, but this way it just carries on executing the next instruction. I then set the new location and invalidate this as well. this way both the old and new rects are included in the clipping rect, which is filled in the Paint routine (with the corrected code).
--------------------------- Chris.
-
Thanks very much for replying. "Also, the call to e.Graphics.FillRectangle(Brushes.White, r) in the OnPaint method will need to be adjusted to fill the clip region (can't remember the property name off hand, but should be easy enough to find)." Yeah i realised i had done that! r instead of e.ClipRectangle property, but it doesnt actually make any difference any way. "You need to keep track of the area that was under the original rectangle and invalidate the union of the old rectangle and the new one.." Thats what the first invalidate does! When you call invalidate, it wont invalidate immediately. If you were to call refresh() then it would go straight to the paint code, but this way it just carries on executing the next instruction. I then set the new location and invalidate this as well. this way both the old and new rects are included in the clipping rect, which is filled in the Paint routine (with the corrected code).
--------------------------- Chris.
Herbertmunch wrote:
Thats what the first invalidate does! When you call invalidate, it wont invalidate immediately. If you were to call refresh() then it would go straight to the paint code, but this way it just carries on executing the next instruction.
Yep, you're right - it's been a while since I've done much with GDI apparently, and I forgot that. However, Refresh is a bit more heavy handed - it will invalidate everything then force a redraw - if you only want to force the redraw the invalidated regions, use the Update method. Not that this matters to the issue at hand ... Anyway, I loaded this up in a test project just to see what was going on - I should have done this right away but was too lazy :) Anyway, it looks like what you're seeing is from the draw rectangle actually drawing one pixel past the edge of the rectangle (you can see this easier if you make the rectangle width to be 1, you'll see a two pixel wide line drawn). What you'll want to do is just to invalidate an area slightly larger than your rectangle - so your both of your invalidates should so something like:
Invalidate(Rectangle.Inflate(r, 2, 2))
. Then, assuming your paint has been updated to fill the clip region, all is working correctly. The reason you need to invalidate an area larger than the rectangle on the second invalidate as well as the first is becauase of the same issue - the pen draws one pixel to the right and one down from the actual rectangle, causing the rectangle to be clipped if moving to the right or down.----- In the land of the blind, the one eyed man is king.
-
Herbertmunch wrote:
Thats what the first invalidate does! When you call invalidate, it wont invalidate immediately. If you were to call refresh() then it would go straight to the paint code, but this way it just carries on executing the next instruction.
Yep, you're right - it's been a while since I've done much with GDI apparently, and I forgot that. However, Refresh is a bit more heavy handed - it will invalidate everything then force a redraw - if you only want to force the redraw the invalidated regions, use the Update method. Not that this matters to the issue at hand ... Anyway, I loaded this up in a test project just to see what was going on - I should have done this right away but was too lazy :) Anyway, it looks like what you're seeing is from the draw rectangle actually drawing one pixel past the edge of the rectangle (you can see this easier if you make the rectangle width to be 1, you'll see a two pixel wide line drawn). What you'll want to do is just to invalidate an area slightly larger than your rectangle - so your both of your invalidates should so something like:
Invalidate(Rectangle.Inflate(r, 2, 2))
. Then, assuming your paint has been updated to fill the clip region, all is working correctly. The reason you need to invalidate an area larger than the rectangle on the second invalidate as well as the first is becauase of the same issue - the pen draws one pixel to the right and one down from the actual rectangle, causing the rectangle to be clipped if moving to the right or down.----- In the land of the blind, the one eyed man is king.
thanks " What you'll want to do is just to invalidate an area slightly larger than your rectangle.." I came to the same conclusion, but its just a bit nasty for my liking! I cant remember ever encountering this problem before, which is strange. it makes no sense at all why this occurs. Surely nothing should be displayed in an area that hasnt been invalidated. Why is this 1 pixel wide artifact occuring? Is this a bug with GDI+?
--------------------------- Chris.