Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 309912 - Wrong z-order in outline
Summary: Wrong z-order in outline
Status: RESOLVED FIXED
Alias: None
Product: GEF
Classification: Tools
Component: GEF-Legacy Draw2d (show other bugs)
Version: 3.5.2   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 3.10.0 (Mars) M2   Edit
Assignee: Alexander Nyßen CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-04-21 05:00 EDT by Carsten Reckord CLA
Modified: 2019-01-24 06:14 EST (History)
6 users (show)

See Also:


Attachments
Screenshot showing effect in outline (33.31 KB, image/png)
2010-04-21 05:02 EDT, Carsten Reckord CLA
no flags Details
Screenshot demonstrating the problem (61.36 KB, image/png)
2014-08-10 09:12 EDT, Alexander Nyßen CLA
no flags Details
Workbench screenshot (38.54 KB, image/png)
2015-01-08 04:42 EST, Florian Barbin CLA
no flags Details
309912-AddAsyncExecSleep.patch for comment 12 (862 bytes, patch)
2015-04-01 04:01 EDT, Laurent Redor CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Carsten Reckord CLA 2010-04-21 05:00:57 EDT
We have a Figure with the following (simplified) paint logic, which essentially adds a drop shadow by first filling a rectangle with the 
shadow and then painting over it:

public class RectangleWithShadow extends RectangleFigure
{
    @Override
    protected void fillShape(Graphics graphics)
    {
       drawShadow(graphics);

       super.fillShape(graphics);
    }

    private void drawShadow(Graphics graphics)
    {
       final int shadow = EditorConstants.SHADOW_WIDTH;
       final Rectangle bounds = getBounds();
       graphics.pushState();
       graphics.setBackgroundColor(ColorConstants.gray);
       graphics.setAlpha(140);
       graphics.setClip(new Rectangle(bounds.x, bounds.y, bounds.width+shadow, bounds.height+shadow));
       final Rectangle shadowRect = bounds.getCopy().translate(shadow, shadow);
       graphics.fillRectangle(shadowRect);
       graphics.popState();
    }
}

This works fine in the editor itself, however it causes strange effects in the outline view, where some or all of the shadow rectangle is 
displayed in front of the figure rather than behind it.
Comment 1 Carsten Reckord CLA 2010-04-21 05:02:15 EDT
Created attachment 165540 [details]
Screenshot showing effect in outline
Comment 2 Alexander Nyßen CLA 2014-08-10 09:12:09 EDT
I can reproduce this with the logic example, if I adjust the LEDFigure paint method as follows:

/**
	 * @see org.eclipse.draw2d.Figure#paintFigure(Graphics)
	 */
	protected void paintFigure(Graphics g) {
		Rectangle r = getBounds().getCopy();

		drawShadow(g);

		g.translate(r.getLocation());
		g.setBackgroundColor(LogicColorConstants.logicGreen);
		g.setForegroundColor(LogicColorConstants.connectorGreen);
		g.fillRectangle(0, 2, r.width, r.height - 4);
		int right = r.width - 1;
		g.drawLine(0, Y1, right, Y1);
		g.drawLine(0, Y1, 0, Y2);

		g.setForegroundColor(LogicColorConstants.connectorGreen);
		g.drawLine(0, Y2, right, Y2);
		g.drawLine(right, Y1, right, Y2);

		// Draw the gaps for the connectors
		g.setForegroundColor(ColorConstants.listBackground);
		for (int i = 0; i < 4; i++) {
			g.drawLine(GAP_CENTERS_X[i] - 2, Y1, GAP_CENTERS_X[i] + 3, Y1);
			g.drawLine(GAP_CENTERS_X[i] - 2, Y2, GAP_CENTERS_X[i] + 3, Y2);
		}

		// Draw the connectors
		g.setForegroundColor(LogicColorConstants.connectorGreen);
		g.setBackgroundColor(LogicColorConstants.connectorGreen);
		for (int i = 0; i < 4; i++) {
			connector.translate(GAP_CENTERS_X[i], 0);
			g.fillPolygon(connector);
			g.drawPolygon(connector);
			connector.translate(-GAP_CENTERS_X[i], 0);

			bottomConnector.translate(GAP_CENTERS_X[i], r.height - 1);
			g.fillPolygon(bottomConnector);
			g.drawPolygon(bottomConnector);
			bottomConnector.translate(-GAP_CENTERS_X[i], -r.height + 1);
		}

		// Draw the display
		g.setBackgroundColor(LogicColorConstants.logicHighlight);
		g.fillRectangle(displayHighlight);
		g.setBackgroundColor(DISPLAY_SHADOW);
		g.fillRectangle(displayShadow);
		g.setBackgroundColor(ColorConstants.black);
		g.fillRectangle(displayRectangle);

		// Draw the value
		g.setFont(DISPLAY_FONT);
		g.setForegroundColor(DISPLAY_TEXT);
		g.drawText(value, valuePoint);
	}

	private void drawShadow(Graphics graphics) {
		final int shadow = 10;
		final Rectangle bounds = getBounds();

		graphics.pushState();
		graphics.setClip(new Rectangle(bounds.x + shadow, bounds.y + shadow,
				bounds.width + shadow, bounds.height + shadow));
		graphics.setBackgroundColor(ColorConstants.gray);
		graphics.setAlpha(140);
		final Rectangle shadowRect = bounds.getCopy().translate(shadow, shadow);
		graphics.fillRectangle(shadowRect);
		graphics.popState();
	}
Comment 3 Alexander Nyßen CLA 2014-08-10 09:12:53 EDT
Created attachment 245855 [details]
Screenshot demonstrating the problem
Comment 4 Alexander Nyßen CLA 2014-08-14 13:32:55 EDT
I tracked it down to be related to the fact that the source figure is broken down into tiles within Thumbnail$ThumbnailUpdater. If I change ThumbnailUpdater#resetTileValues() to always use hTiles = vTiles = 1, I do not observe these effects. However, I have not identified the root cause of this yet.
Comment 5 Alexander Nyßen CLA 2014-08-14 13:45:47 EDT
It seems that the drop shadow is rendered twice, i.e. not properly clipped when rendering the thumbnail. By adding a "thumbnailGraphics.drawRectangle(rect);" in addition to the "thumbnailGraphics.fillRectangle(rect);" within ThumnailUpdater#run() that can be properly observed.
Comment 6 Alexander Nyßen CLA 2014-08-14 16:11:07 EDT
The cause for this seems to be as follows: ThumbnailUpdater sets the clipping rectangle for the tile it is going to update in one of its run. This clipping rectangle is used to constraint the painting of the source figure within this run, however, as the drop shadow pushes the graphics state and applies its own clip (which extends the figure bounds), it paints into a larger area than actually covered by the current tile clipping rectangle, thus overwriting parts of the image belonging to tiles that have already been rendered in earlier runs. 

There are two problems related to this (one is actually a concern for Thumbnail):

1) The fact that the figure actually paints outside of its bounds is a violation of the paint contract, so Thumbnail actually would not have to compensate for this.

2) Even if the clipping area used to draw the drop shadow is not set outside the clipping area of the source figure  the overpaint problem would still occur; this is a problem of Thumbnail because it does not prevent the figure from overpainting the tile clipping area.

We will probably have to used the wrapped in SWTGraphics (wrapped inside the thumbnailGraphics) to apply the clipping.
Comment 7 Alexander Nyßen CLA 2014-08-15 02:28:28 EDT
A proper solution that still preserves the tile-based rendering of the thumbnail would have to ensure that whatever clipping the user specifies within the sourceFigure's paint(Graphics), this never extends the area of the current tile that is updated.

This could be achieved by replacing the SWTGraphics that wraps around the thumbnailGC with a custom implementation that overwrites setClip(Rectangle) and setClip(Path) to intersect the passed in value with the current tile area. However, as SWT is not capable of intersecting with Paths (see bug #87776) this is only realizable for setClip(Rectangle) (or we would have to implement Path intersection ourselves). 

As such, there are IMHO two alternatives how to solve this:

1) Preserve the tile rendering by using a second image into which the sourceFigure renders and from which the current tile area is then copied to the thumbnailImage within run()
2) Drop the tile-based rendering and update the thumbnail image as a whole within run()
Comment 8 Alexander Nyßen CLA 2014-08-19 13:21:17 EDT
I performed the following changes:
- Added a tile image, into which the source figure now paints (instead of painting directly into the thumbnail image). The thumnail image is now udpated by copying the contents of this tile image into the thumbnail (at the specified tile area) in each run. This way, even if the source figure sets an own clip on the passed in Graphics inside paint(Graphics), already painted areas in the thumbnail image can never be corrupted (as we can control what gets copied).
- Fixed that the SWTGraphics that is constructed around the GC was never disposed.

Verified that the thumbnail within logic editor's outline now paints fine, even if the LEDFigure is tweaked as described. Resolving as fixed in 3.10.0M2.
Comment 9 Florian Barbin CLA 2015-01-08 04:42:21 EST
Created attachment 249779 [details]
Workbench screenshot
Comment 10 Florian Barbin CLA 2015-01-08 04:43:07 EST
Hello,

In the case of Sirius for Eclipse Mars, we recently launched our SWTBot test suite on this platform. We have an error raised during the execution of some of these tests. We do not reproduce this error manually:

java.lang.AssertionError: Error(s) raised during test : org.eclipse.sirius.tests.swtbot.ContainerCreationTest
. Log Plugin : org.eclipse.core.runtime
  . Error from plugin:org.eclipse.ui, message: Unhandled event loop exception, exception: org.eclipse.swt.SWTException: Failed to execute runnable (java.lang.IllegalArgumentException: Argument not valid)
(...)
Caused by: java.lang.IllegalArgumentException: Argument not valid
	at org.eclipse.swt.SWT.error(SWT.java:4446)
	at org.eclipse.swt.SWT.error(SWT.java:4380)
	at org.eclipse.swt.SWT.error(SWT.java:4351)
	at org.eclipse.swt.graphics.GC.drawImage(GC.java:870)
	at org.eclipse.draw2d.parts.Thumbnail$ThumbnailUpdater.run(Thumbnail.java:185)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:136)
	... 25 more

I placed a breakpoint on GC [line: 870] - drawImage(Image, int, int, int, int, int, int, int, int) and this error is raised because of a negative srcWidth passed during the call of this method, line 185 in org.eclipse.draw2d.parts.Thumbnail:

thumbnailGC.drawImage(tileImage, 0, 0, sx2 - sx1, sy2 - sy1, sx1, sy1, sx2 - sx1, sy2 - sy1);
The variable state:
sy1	738	
sy2	861	
sx1	1659	
sx2	1301

Do you have any idea about what could lead to this inconsistent state ?
I Joined a screen-shot of the workbench at the moment where this error is raised.

Regards,

Florian
Comment 11 Alexander Nyßen CLA 2015-01-08 04:51:06 EST
Not obviously. Could you try to strip-down the example so we have a chance to reproduce it?
Comment 12 Laurent Redor CLA 2015-04-01 04:00:23 EDT
Hi Alexander,

After Florian, I analyzed our problem on Sirius test. It's difficult to reproduce, as it is random. But after several debug sessions with one of our failing test (org.eclipse.sirius.tests.swtbot.ContainerCreationTest.testContainerCreationInDiagramWithScrollAndChangeZoom()), I noticed that this problem occurs when the zoom is changed in the middle of iteration on Thumbnail.ThumbnailUpdater.run(), that calls itself in asyncExec several times.

I reproduced this problem manually with org.eclipse.gef.examples.logic. This needs a change in ThumbnailUpdater to have time to change the zoom during the iteration. The patch with this update is linked to this issue: "309912-AddAsyncExecSleep.patch".

Steps to reproduce:
* Apply the patch to Thumbnail class
* Ensure you have the project org.eclipse.gef.examples.logic in your workspace
* Launch a runtime
* Create a simple project
* Create a fourBitAdder1.logic diagram (Examples/GEF (Graphical Editing Framework)/Logic Diagram --> Four-bit Adder Model)
* Display Error Log
* In the Outline click on "Show Overview" button
* Change the zoom quickly several times
--> An error appears in error log "Unhandled event loop exception"

Corresponding stack in the error log:

org.eclipse.swt.SWTException: Failed to execute runnable (java.lang.IllegalArgumentException: Argument not valid)
	at org.eclipse.swt.SWT.error(SWT.java:4481)
	at org.eclipse.swt.SWT.error(SWT.java:4396)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:139)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3790)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3428)
...
Caused by: java.lang.IllegalArgumentException: Argument not valid
	at org.eclipse.swt.SWT.error(SWT.java:4462)
	at org.eclipse.swt.SWT.error(SWT.java:4396)
	at org.eclipse.swt.SWT.error(SWT.java:4367)
	at org.eclipse.swt.graphics.GC.drawImage(GC.java:870)
	at org.eclipse.draw2d.parts.Thumbnail$ThumbnailUpdater.run(Thumbnail.java:185)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:136)
	... 24 more

I have not reopened this issue but I think it should be.
Comment 13 Laurent Redor CLA 2015-04-01 04:01:23 EDT
Created attachment 252071 [details]
309912-AddAsyncExecSleep.patch for comment 12
Comment 14 Laurent Redor CLA 2015-04-09 04:35:17 EDT
Hi Alexander,
Is there a chance that this regression (comment 12) be fixed for M7?
Best regards,
Laurent
Comment 15 Alexander Nyßen CLA 2015-04-09 08:08:07 EDT
(In reply to Laurent Redor from comment #14)
> Hi Alexander,
> Is there a chance that this regression (comment 12) be fixed for M7?
> Best regards,
> Laurent

Laurent, your proposed patch does not look as a reasonable fix to me. The problem seems to be merely circumvented by introducing some idle time before running the update. Who guarantees that 500ms are sufficient in all cases?

The only chance I see to resolve this in the Mars release timeframe is that we isolate the root cause for it soon and fix it properly before M7. Can you confirm whether the steps to reproduce you named in comment #12 cannot be used to reproduce it with earlier versions (i.e. the test regression is actually the same problem as the one you manually reproduce)?
Comment 16 Laurent Redor CLA 2015-04-09 08:38:03 EDT
I've not proposed a patch to solve the problem. The patch is only here to reproduce the problem more easily.

I will try to reproduce the problem with earlier versions (ie before commit d212bdd [1]) with an equivalent modification (ie add sleep(500)).

[1] http://git.eclipse.org/c/gef/org.eclipse.gef.git/commit/org.eclipse.draw2d/src/org/eclipse/draw2d/parts/Thumbnail.java?id=d212bdd4794f5866669e8bbd0425c31ee7632ae7
Comment 17 Alexander Nyßen CLA 2015-04-09 08:50:04 EDT
(In reply to Laurent Redor from comment #16)
> I've not proposed a patch to solve the problem. The patch is only here to
> reproduce the problem more easily.
> 
> I will try to reproduce the problem with earlier versions (ie before commit
> d212bdd [1]) with an equivalent modification (ie add sleep(500)).
> 
> [1]
> http://git.eclipse.org/c/gef/org.eclipse.gef.git/commit/org.eclipse.draw2d/
> src/org/eclipse/draw2d/parts/Thumbnail.
> java?id=d212bdd4794f5866669e8bbd0425c31ee7632ae7

Ok, thanks. That is, without the application of your patch, the issue is only reproducible within the test-setting?
Comment 18 Laurent Redor CLA 2015-04-09 08:57:43 EDT
(In reply to Alexander Nyßen from comment #17)
> Ok, thanks. That is, without the application of your patch, the issue is
> only reproducible within the test-setting?

Yes, this is the only way I found to reproduce easily the problem that we detected in our SWTBot tests since the commit d212bdd.
Comment 19 Laurent Redor CLA 2015-04-09 11:48:04 EDT
I confirm that before the commit d212bdd I cannot reproduce the problem (even with adding the same "sleep(500)" in the middle of iteration that reveals the problem).
Comment 20 Alexander Nyßen CLA 2015-04-09 12:30:39 EDT
That is really hard to reproduce, it only happens in very few occasions. 

From looking at the code, I would assume there is some race-condition when the thumbnail-image gets requested repeatedly. Could you please re-try after having made Thumbnail#getThumbnailImage() synchronized?
Comment 21 Laurent Redor CLA 2015-04-10 05:13:20 EDT
Same problem after having made Thumbnail#getThumbnailImage() synchronized

--> protected synchronized Image getThumbnailImage()
Comment 22 Alexander Nyßen CLA 2015-05-18 14:13:06 EDT
This also seems to have occurred outside your test scenario, as we have 4 occurrences that were reported via the Eclipse error reporting initiative. As the issue targeted in this bugzilla has been resolved, I am resolving it as fixed and have created #467528 to keep track of the ThumbnailUpdater problem. Please let's discuss anything further there.
Comment 23 Alexander Nyßen CLA 2015-05-18 14:18:38 EDT
(In reply to Alexander Nyßen from comment #22)
> This also seems to have occurred outside your test scenario, as we have 4
> occurrences that were reported via the Eclipse error reporting initiative.
> As the issue targeted in this bugzilla has been resolved, I am resolving it
> as fixed and have created #467528 to keep track of the ThumbnailUpdater
> problem. Please let's discuss anything further there.

Bug #467528.
Comment 24 Alexander Nyßen CLA 2015-05-18 15:33:24 EDT
Laurent, I added a patch to #467528. Could you please try it out and give me some feedback?
Comment 25 Laurent Redor CLA 2015-05-19 06:10:12 EDT
(In reply to Alexander Nyßen from comment #24)
> Laurent, I added a patch to #467528. Could you please try it out and give me
> some feedback?

The test org.eclipse.sirius.tests.swtbot.ContainerCreationTest.testContainerCreationInDiagramWithScrollAndChangeZoom() (see comment 12) is OK now with this patch. So I think your patch fix the problem.
Comment 26 Phil Beauvoir CLA 2019-01-24 06:14:30 EST
The thumbnail is not rendering on MacOs Mojave.

See https://github.com/archimatetool/archi/issues/401

I've been trying for a long time to devise a workaround. If I set MAX_NUMBER_OF_TILES to 1 the thumbnail is rendered correctly. However I feel there must be a better workaround. Can anyone help?