Strange behaviour of Redraw

i'm using c# example to build my own .net application. what i have noticed is a bug with selection rectangle (e.g. when you zoom window the view), that rectangle disappears when mouse stops. after few hours of digging i came to the following:
in my code on mouse move
1. case ActionState::ViewZoomWindow:
shell->RedrawView();
DrawRect(e->X, e->Y);
break;
rectangle disappears
2. case ActionState::ViewZoomWindow:
shell->RedrawView();
for (int i = 0; i DrawRect(e->X, e->Y);
break;
rectangle shows! notice new extra line with dummy cycle. i can only suggest that call to Redraw is somehow paralleled and it cunsumes all changes made to the vieport in a short period after actual function call... though it looks a bit weird.. (another way to make rectangle to show is to duplicate DrawRect call several times). whatever the resason i ran out of ideas how to fix it..
any insights?

p.s. another strange thing is that this flickering seems only appears in c# example, other samples have no such bug.

Pawel's picture

Hi devast,

it would be good if you could provide more details about your functions. Actually, in "ViewZoomWindow" you should draw the rectangle twice: first time to erase the old trace, second time to draw the new rectangle. Is this what your "DrawRect" function is doing?

The flickering is not a bug. I guess you abuse the call to RedrawView(). In fact, you don't need it at all as the selection rectangle is not drawn in the OCC viewer.

Pawel

devast's picture

Hi Pawel.
Actually flickering occurs in original occ c# sample...

And about drawing rectangle twice.. i just need to clear viewer surface, that's why i call Redraw. Occ and gdi share same surface (viewer window) and you can't actually 'erase' rectangle, you can only draw another on top, thus overriding pixels behind. What is done in occ example is drawing rectangle with background color (i think it's wrong to call this 'erase'). Also 'erasing' is unnecessary and only adds more flickering!.. Subsequent call to UpdateView will redraw surface complitely. Comment out 'erase' line and see for yourself. Don't know why but sample codes are very poorly designed and written... (at least c# one) i thought such a big library would have a higher level :(

void DrawRectangle(bool draw)
{
...
if (this.IsRectVisible ||(!draw))//erase the rect
{
int r=myView.GetBGColR();
int g=myView.GetBGColG();
int b=myView.GetBGColB();
p = new Pen(System.Drawing.Color.FromArgb( r,g, b)); // 'erase'
this.IsRectVisible=false;
this.myView.UpdateView();
}
else if (draw)
{
p = new Pen(System.Drawing.Color.White);
this.IsRectVisible=true;
}
...
}

so basically i'm doing the same thing, just calling redraw instead of update (well, i tried both..) and getting same flickering..
My DrawRect just draws rectangle on gdi surface.
i've stooped on this solution:

case ActionState::ViewZoomWindow:
shell->RedrawView();
for (int i = 0; i < 10; i++)
DrawRect(e->X, e->Y);
break;

looks not very ugly, works and i don't need to spend weeks on fixing..

Pawel's picture

If you import all necessary functions from gdi32.dll you can try something like this:

private void DrawRectangle(bool draw)
{
Graphics gr = Graphics.FromHwnd(this.Handle);

if( this.IsRectVisible == true )//erase the rect if it's there
{
IntPtr hdc = gr.GetHdc();
SetROP2(hdc, R2_MERGEPENNOT);
MoveToEx(hdc, theRectDownX, theRectDownY, IntPtr.Zero );
LineTo(hdc, theRectDownX, theRectUpY);
LineTo(hdc, theRectUpX, theRectUpY);
LineTo(hdc, theRectUpX, theRectDownY);
LineTo(hdc, theRectDownX, theRectDownY);
gr.ReleaseHdc();

this.IsRectVisible = false;
}

if (draw) //draw selection rectangle
{
this.theRectDownX = Math.Min(this.myXmin, this.myXmax);
this.theRectDownX = Math.Min(this.theButtonDownX, this.theRectDownX);

this.theRectDownY = Math.Min(this.myYmin, this.myYmax);
this.theRectDownY = Math.Min(this.theButtonDownY, this.theRectDownY);

this.theRectUpX = Math.Max(this.myXmin, this.myXmax);
this.theRectUpX = Math.Max(this.theButtonDownX, this.theRectUpX);

this.theRectUpY = Math.Max(this.myYmin, this.myYmax);
this.theRectUpY = Math.Max(this.theButtonDownY, this.theRectUpY);

IntPtr hdc = gr.GetHdc();
SetROP2(hdc, R2_MERGEPENNOT);
MoveToEx(hdc, theRectDownX, theRectDownY, IntPtr.Zero);
LineTo(hdc, theRectDownX, theRectUpY);
LineTo(hdc, theRectUpX, theRectUpY);
LineTo(hdc, theRectUpX, theRectDownY);
LineTo(hdc, theRectDownX, theRectDownY);
gr.ReleaseHdc();

this.IsRectVisible=true;
}
}

There should be no need for the extra loop.

Pawel

devast's picture

could you explain the point of using pure native calls instead of .net calls? 'cos i bellieve .net gdi+ invokes winapi anyway..

i use extra loop only for purpose of spending some time..
for example i replaced for loop with
Thread::Sleep(120);
and though the result is worser but rectangle NOT disappears.

Pawel's picture

Native API lets you call 'SetROP2' function in order to combine pens and interiors of filled objects with the colors already on the screen. This allows accomplishing what I called 'erasing the old trace' before.

I guess .NET does not offer this possibility (at least in 2.0 version).

Don't forget to deactivate Aero if you're under Vista...

Pawel

devast's picture

ok, shortly, i got your point and tried your solution and it works BUT i still concerned about two things.

1. Presented approach seems like a cheat to me. Rectangle is drawn with screen inverted color, thus 'erasing' it is just another draw with inverted color. Resulting rect has non constant color and its color depends on background color. If for example i'll need to draw something with predefined constant color this approach simply will not work. While this anyway could do the trick, it's not real drawing-erasing.

2. Still i'm confused why my approach isn't work. Logic is clear and simple:
a. erase surface
b. draw new graphics on cleared surface
Role of erasing surface belongs to Redraw call, which automatically not only clears window surface but also renders viewer contents onto it. Then i just draw rectangle on top of that. I have feeling that the problem lies in the nature of 3d graphics rendering technique (more exactly double buffering technique). Call to redraw in turn calls openGL functions which are adressed by videocard parallel to cpu exectuion (which are gdi calls). Simply, i just think that call to gdi drawrectangle is finished before opengl presents contents of the buffer onto same [as gdi] surface thus discarding previosly drawn rectangle... Well this at least explains why i should call dummy cycles and hang around for a while after redraw call...

Pawel's picture

hi devast,

I'm not sure why the original approach doesn't work. I guess I was having the same problem some time ago but I did not dig deeper and switched to the 'SetROP2 approach' because it offers a substantial advantage over the other one: If your visualized scene is complex enough and you try to redraw it completely each time the view catches the MouseMove event you might end up making coffee each time you move the mouse... (maybe a slightly exaggerated example)

devast's picture

well, maybe you're right. thx for the answers Pawel!

devast's picture

and one more thing, as you've said seems .net doesn't have SetROP2 function analogue, what a shame...