Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 340206

Summary: Recycling Transform when drawing paths on GC leads to inconsistencies.
Product: [Eclipse Project] Platform Reporter: Alexander Nyßen <nyssen>
Component: SWTAssignee: Platform-SWT-Inbox <platform-swt-inbox>
Status: RESOLVED INVALID QA Contact:
Severity: major    
Priority: P3    
Version: 3.7   
Target Milestone: ---   
Hardware: Macintosh   
OS: Mac OS X   
Whiteboard:
Attachments:
Description Flags
Screenshot showing the difference between the two snippets. none

Description Alexander Nyßen CLA 2011-03-16 14:09:18 EDT
Created attachment 191329 [details]
Screenshot showing the difference between the two snippets.

While trying to isolate a SWT-alone snippet to reproduce GEF bug #124506, I recognized that reusing a Transform object between different calls to GC#drawPath() leads to inconsistent painting.

Consider the two snippets added below, which differ only by reusing vs. recreating a Transform that is used on the GC. While the snippet recreating a Transform between succeeding calls to drawPath() seems to paint the paths as desired, the one that recycles the Transform "looses" two of the painted paths.

/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package swt.bugs;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

public class RecreateTransformSnippet {

	private static final Display display = new Display();

	private static final Color darkGreen = new Color(null, 0, 127, 0);
	private static final Color darkBlue = new Color(null, 0, 0, 127);
	private static final Color black = new Color(null, 0, 0, 0);

	private static Path path1;
	private static Path path2;
	private static Transform transform;

	public static void main(String[] args) {
		path1 = new Path(null);
		path1.moveTo(20, 5);
		path1.quadTo(40, 5, 50, 25);
		path1.quadTo(20, 25, 20, 45);
		path1.lineTo(0, 25);
		path1.close();

		path2 = new Path(null);
		path2.moveTo(15, 30);
		path2.cubicTo(50, 0, 50, 30, 20, 60);
		path2.close();

		final Shell shell = new Shell(display);
		shell.setText("Advanced Graphics");
		shell.addListener(SWT.Paint, new Listener() {
			public void handleEvent(Event event) {
				GC gc = event.gc;

				drawPaths(0, gc, SWT.ON, 3, SWT.LINE_SOLID, darkBlue, darkBlue);
				drawPaths(1, gc, SWT.OFF, 0, SWT.LINE_DOT, ColorConstants.red,
						darkBlue);
				drawPaths(2, gc, SWT.DEFAULT, 1, SWT.LINE_DOT, darkBlue,
						darkBlue);
				drawPaths(3, gc, SWT.DEFAULT, 2, SWT.LINE_DOT, darkGreen,
						darkBlue);
				drawPaths(4, gc, SWT.ON, 2, SWT.LINE_DASHDOTDOT, black,
						darkBlue);

			}
		});
		shell.setSize(200, 600);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}

		path1.dispose();
		path2.dispose();
		display.dispose();
	}

	protected static void drawPaths(int i, GC gc, int antialias, int lineWidth,
			int lineSolid, Color foreground, Color background) {
		gc.setAntialias(antialias);
		gc.setLineWidth(lineWidth);
		gc.setLineStyle(lineSolid);
		gc.setForeground(foreground);
		gc.setBackground(background);

		transform = new Transform(gc.getDevice());
		transform.translate(0, i * 100);
		gc.setTransform(transform);

		gc.drawPath(path1);
		gc.drawPath(path2);

		transform.dispose();
	}
}

/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package swt.bugs;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

public class RecycleTransformSnippet {

	private static final Display display = new Display();

	private static final Color darkGreen = new Color(null, 0, 127, 0);
	private static final Color darkBlue = new Color(null, 0, 0, 127);
	private static final Color black = new Color(null, 0, 0, 0);

	private static Path path1;
	private static Path path2;
	private static Transform transform;

	public static void main(String[] args) {
		path1 = new Path(null);
		path1.moveTo(20, 5);
		path1.quadTo(40, 5, 50, 25);
		path1.quadTo(20, 25, 20, 45);
		path1.lineTo(0, 25);
		path1.close();

		path2 = new Path(null);
		path2.moveTo(15, 30);
		path2.cubicTo(50, 0, 50, 30, 20, 60);
		path2.close();

		final Shell shell = new Shell(display);
		shell.setText("Advanced Graphics");
		shell.addListener(SWT.Paint, new Listener() {
			public void handleEvent(Event event) {
				GC gc = event.gc;

				transform = new Transform(gc.getDevice());

				drawPaths(0, gc, SWT.ON, 3, SWT.LINE_SOLID, darkBlue, darkBlue);
				drawPaths(1, gc, SWT.OFF, 0, SWT.LINE_DOT, ColorConstants.red,
						darkBlue);
				drawPaths(2, gc, SWT.DEFAULT, 1, SWT.LINE_DOT, darkBlue,
						darkBlue);
				drawPaths(3, gc, SWT.DEFAULT, 2, SWT.LINE_DOT, darkGreen,
						darkBlue);
				drawPaths(4, gc, SWT.ON, 2, SWT.LINE_DASHDOTDOT, black,
						darkBlue);

				transform.dispose();
			}
		});
		shell.setSize(200, 600);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}

		path1.dispose();
		path2.dispose();
		display.dispose();
	}

	protected static void drawPaths(int i, GC gc, int antialias, int lineWidth,
			int lineSolid, Color foreground, Color background) {
		gc.setAntialias(antialias);
		gc.setLineWidth(lineWidth);
		gc.setLineStyle(lineSolid);
		gc.setForeground(foreground);
		gc.setBackground(background);

		transform.translate(0, i * 100);
		gc.setTransform(transform);

		gc.drawPath(path1);
		gc.drawPath(path2);
	}
}
Comment 1 Alexander Nyßen CLA 2011-03-16 14:14:02 EDT
I am sorry, but I overlooked that the snippets still depend on Draw2d's ColorConstants.red. You may of course simply remove this by declaring another local color constant as follows:

private static final Color red = new Color(null, 255, 0, 0);
Comment 2 Alexander Nyßen CLA 2011-03-16 14:29:56 EDT
Forget about this one. In case the transform is not recycled, it should of course be:

transform.translate(0, 100);

instead of 

transform.translate(0, i*100);