Invalidate event (Repaint) can be starved with long executing actions

Bug #857123 reported by ciplogic
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Pinta
New
Undecided
Unassigned

Bug Description

In PintaCanvas (Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs) is the processing of messages.

In the worst casse scenario, like a took that is occurring many times and in itself that it take a lot of time, i.e. gradient on a big picture, the painting of the control occurs very late.

Steps to reproduce:
- Create a new picture of 3000x3000 or any size that gradient will happen slow
- Move mouse many seconds around making gradient to be seen

Actual result:
- after releasing the mouse button, Pinta will do in the background painting of the gradients and will look like frozen

Expected result:
- just the last gradient should be executed, all "intermediary" invisible gradients should not be processed

Note: to test this behavior:
- add in Linux build Console.Writeline("Start time: {0}". Environment.TickCount); at the start of OnMouseMvove from GradientTool, end of OnMouseMove add Console.Writeline("Start time: {0}". Environment.TickCount);,and in PintaCanvas.cs at the OnExposeEvent add another Console.WriteLine("Paint the widget");

Revision history for this message
ciplogic (ciprian-mustiata) wrote :

I attached a patch that may address the problem (certainly fixes on my machine, I don't know side effects on other actions).

In short the fixes are:
 Added an internal class (could be added to Utility class, as was added RangeEnumerator) that can store just one computation, and made one internal instance to compute that:
       ToCompute _toCompute = new ToCompute();
         class ToCompute
        {
            public delegate void ExecutedEvent();
            public ExecutedEvent Executed;

            public void AddEvent(ExecutedEvent ev)
            {
                Executed = null;
                 Executed += ev;
            }

            public void ComputeAll()
            {
                if(Executed!=null)
                    Executed();
                Executed = null;
             }
        }

Add the MotionEvents with AddEvent that will clear the previous motion event and will take all closures arguments using lambdas:
So previous code was:
MotionNotifyEvent += delegate (object sender, MotionNotifyEventArgs e) {
                 if (!PintaCore.Workspace.HasOpenDocuments)
And it was replaced with:
MotionNotifyEvent += delegate (object sender, MotionNotifyEventArgs e) {
                _toCompute.AddEvent( () => { //here is the lambda
                 if (!PintaCore.Workspace.HasOpenDocuments)
and after that is forcing a view paint:
               }; //end of lambda
                GdkWindow.Invalidate();

And at last, but not at least, added the motion event in invalidate method at start of it:
 So the previous code:

            base.OnExposeEvent (e);

            if (!PintaCore.Workspace.HasOpenDocuments)
                return true;

was replaced with:

            base.OnExposeEvent (e);

            _toCompute.ComputeAll();

            if (!PintaCore.Workspace.HasOpenDocuments)
                return true;

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers