diff options
author | Simeon Andreev | 2018-03-01 10:49:59 +0000 |
---|---|---|
committer | Simeon Andreev | 2018-03-05 12:48:16 +0000 |
commit | e78489ee67784eb3704ba210de1effcb777e0126 (patch) | |
tree | 67ba42dbc5b0cc34d78e6c451d21856272584acc /tests | |
parent | fc1ec028ce6bb1b1df3db750ba0696e56156b5d4 (diff) | |
download | eclipse.platform.swt-e78489ee67784eb3704ba210de1effcb777e0126.tar.gz eclipse.platform.swt-e78489ee67784eb3704ba210de1effcb777e0126.tar.xz eclipse.platform.swt-e78489ee67784eb3704ba210de1effcb777e0126.zip |
Bug 531667 - [GTK3] Cannot draw Canvas with Control.print(GC)
Calling Canvas.pring(GC) results in no painted canvas on the GC. This is
caused by a regression from the fix for Bug 421127. Control.print
propagates the GC.handle as the last argument to Control.gtk_draw. With
changes for Bug 421127 in Control.gtk_draw, the handle is no longer
propagated to the actual onPaint event. The canvas (and canvas derived
widgets) therefore no longer paint themselves on the GC.
The problem which was fixed with the change in Control.gtk_draw, for Bug
421127, was that CTabFolder tabs were not painted. This is the case,
since GC.getClipping returned broken values with GTK 3.10 and above.
CTabFolder checks for intersection with the clipping region and doesn't
paint itself.
The clipping region was not computed properly, due to transformations
done with the device space transformation matrix provided by Cairo. From
GTK 3.10 onward, this matrix contains absolute coordinates of the
widget, with respect to the shell. This causes the method to
malfunction.
With the fix for Bug 421127, the Cairo matrix is no longer propagated.
As a result GC.getClipping only works with the identity matrix,
resulting in correct clipping.
This change reverts the change for Bug 421127 in Control.gtk_draw, for
GTK versions 3.14 and above. The GC.getClipping method is adapted to
first transform the local GC bounds to device space, before computing
the intersection with the clipping region. This is done for GTK 3.14 and
above.
We validated the change with GTK 3.14 and GTK 3.22. A bug snippet is
provided, show-casing clipping region and Cairo device space
transformation matrix.
Change-Id: Ib1d6bb866bcffa010a9e8f2a5e4296eba4e1d852
Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
Diffstat (limited to 'tests')
2 files changed, 301 insertions, 0 deletions
diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug421127_Clipping_is_wrong.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug421127_Clipping_is_wrong.java new file mode 100644 index 0000000000..949a49cecc --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug421127_Clipping_is_wrong.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2018 Simeon Andreev and others. All rights reserved. + * The contents of this file are made available under the terms + * of the GNU Lesser General Public License (LGPL) Version 2.1 that + * accompanies this distribution (lgpl-v21.txt). The LGPL is also + * available at http://www.gnu.org/licenses/lgpl.html. If the version + * of the LGPL at http://www.gnu.org is different to the version of + * the LGPL accompanying this distribution and there is any conflict + * between the two license versions, the terms of the LGPL accompanying + * this distribution shall govern. + * + * Contributors: + * Simeon Andreev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.swt.tests.gtk.snippets; + + +import java.util.Arrays; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.GCData; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.internal.cairo.Cairo; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + + +/** + * Description: Clipping computed by {@link GC#getClipping()} is wrong. + * Steps to reproduce: + * <ol> + * <li>Run the snippet.</li> + * </ol> + * Expected results: All canvases are green, indicating that their bounds intersect with the clipping provided by {@link GC#getClipping()}. + * Actual results: Only the top-left canvas is green, the rest of the canvases are red. + * Further notes: The clipping of {@link GC#getClipping()} is shown below each colored canvas. + */ +public class Bug421127_Clipping_is_wrong { + + public static void main (String [] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setSize(800, 800); + shell.setText("Display clipping bounds"); + shell.setLayout(new FillLayout()); + Composite main = new Composite(shell, SWT.NONE); + main.setLayout(new FillLayout()); + + /* + * Splits the shell into some composites. + * + * The composites each contain: + * a) A canvas, which paints red or green depending on whether it intersects with GC.getClipping. + * b) A text, which displays computed GC.getClipping and Cairo.cairo_get_matrix for the composite. + */ + Composite[] squares = showCaseClipping(main); + for (Composite square : squares) { + // comment in for a more complex example + //showCaseClipping(square); + square.layout(); + } + + shell.open (); + while (!shell.isDisposed ()) { + if (!display.readAndDispatch ()) display.sleep (); + } + display.dispose (); + } + + private static Composite[] showCaseClipping(Composite main) { + Composite composite = new Composite(main, SWT.NONE); + composite.setLayout(new FillLayout()); + + Composite left = new Composite(composite, SWT.NONE); + left.setLayout(new FillLayout(SWT.VERTICAL)); + Composite upperLeft = new Composite(left, SWT.BORDER); + Composite bottomLeft = new Composite(left, SWT.BORDER); + + Composite right = new Composite(composite, SWT.NONE); + right.setLayout(new FillLayout(SWT.VERTICAL)); + Composite upperRight = new Composite(right, SWT.BORDER); + Composite bottomRight = new Composite(right, SWT.BORDER); + + Composite[] squares = { upperLeft, bottomLeft, upperRight, bottomRight }; + for (Composite square : squares) { + square.setLayout(new FillLayout(SWT.VERTICAL)); + + final Canvas canvas = new Canvas(square, SWT.BORDER); + final Canvas text = new Canvas(square, SWT.BORDER); + + class PaintCanvas implements PaintListener { + @Override + public void paintControl(PaintEvent event) { + paintCanvas(event, canvas); + } + } + + class PrintClipping implements PaintListener { + @Override + public void paintControl(PaintEvent event) { + clippingText(event, text); + } + } + + canvas.addPaintListener(new PaintCanvas()); + text.addPaintListener(new PrintClipping()); + } + + return squares; + } + + private static void paintCanvas(PaintEvent event, Canvas canvas) { + GC gc = gc(event); + Rectangle bounds = canvas.getBounds(); + Rectangle clipping = gc.getClipping(); + Device device = gc.getDevice(); + int color = SWT.COLOR_GREEN; + String text = "CLIPPING AND BOUNDS INTERSECT"; + if (!clipping.intersects(bounds)) { + color = SWT.COLOR_RED; + text = "CLIPPING AND BOUNDS DON'T INTERSECT"; + } + gc.drawText(text, 0, 0); + gc.setAlpha(25); + gc.setBackground(device.getSystemColor(color)); + gc.fillRectangle(bounds); + gc.setAlpha(255); + } + + private static void clippingText(PaintEvent event, Canvas text) { + StringBuilder clipping = new StringBuilder(); + appendCairoMatrix(event, clipping); + clipping.append(System.lineSeparator()); + appendClippingRegion(event, clipping); + + GC gc = gc(event); + gc.drawText(clipping.toString(), 0, 0); + } + + private static void appendCairoMatrix(PaintEvent event, StringBuilder clipping) { + long cairo = cairo(event); + clipping.append("Cairo Matrix: "); + if (cairo != 0) { + double[] matrix = new double[6]; + Cairo.cairo_get_matrix(cairo, matrix); + clipping.append(Arrays.toString(matrix)); + } else { + clipping.append("--"); + } + } + + private static void appendClippingRegion(PaintEvent event, StringBuilder clipping) { + clipping.append("GC clipping: "); + Rectangle region = clipping(event); + if (region != null) { + clipping.append(region.x); + clipping.append(' '); + clipping.append(region.y); + clipping.append(' '); + clipping.append(region.width); + clipping.append(' '); + clipping.append(region.height); + } else { + clipping.append("--"); + } + } + + private static GC gc(PaintEvent event) { + if (event != null) { + return event.gc; + } + return null; + } + + private static Rectangle clipping(PaintEvent event) { + GC gc = gc(event); + if (gc != null) { + Rectangle region = gc.getClipping(); + return region; + } + return null; + } + + private static long cairo(PaintEvent event) { + GC gc = gc(event); + if (gc != null) { + if (event.gc != null) { + GCData data = event.gc.getGCData(); + if (data != null) { + return data.cairo; + } + } + } + return 0; + } + +} diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug531667_CanvasPrint_does_not_work.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug531667_CanvasPrint_does_not_work.java new file mode 100644 index 0000000000..97a6567e6f --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug531667_CanvasPrint_does_not_work.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2018 Simeon Andreev and others. All rights reserved. + * The contents of this file are made available under the terms + * of the GNU Lesser General Public License (LGPL) Version 2.1 that + * accompanies this distribution (lgpl-v21.txt). The LGPL is also + * available at http://www.gnu.org/licenses/lgpl.html. If the version + * of the LGPL at http://www.gnu.org is different to the version of + * the LGPL accompanying this distribution and there is any conflict + * between the two license versions, the terms of the LGPL accompanying + * this distribution shall govern. + * + * Contributors: + * Simeon Andreev - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.gtk.snippets; + +import java.io.File; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.ImageLoader; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * Description: {@link Canvas#print(GC)} does not print the canvas. + * Steps to reproduce: + * <ol> + * <li>Run the snippet.</li> + * <li>Examine the contents of {@code some_canvas.png}, absolute location is printed on the standard out.</li> + * </ol> + * Expected results: The image contains a white circle on a black background, similar to the canvas. + * Actual results: The image contains only black background. + */ +public class Bug531667_CanvasPrint_does_not_work { + + public static void main(String[] args) { + String filename = "some_canvas.png"; + + Display display = new Display(); + Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + shell.setSize(200, 200); + shell.setText("Bug 531667"); + + Composite composite = canvas(display, shell); + + shell.open(); + + snapshot(display, composite, filename); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } + + private static Composite canvas(Display display, Shell shell) { + Composite composite = new Composite(shell, SWT.NONE); + composite.setLayout(new FillLayout()); + Canvas canvas = new Canvas(composite, SWT.NONE); + canvas.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); + Color white = display.getSystemColor(SWT.COLOR_WHITE); + + canvas.addPaintListener(e -> { + Rectangle clientArea = canvas.getClientArea(); + e.gc.setBackground(white); + e.gc.fillOval(0, 0, clientArea.width, clientArea.height); + }); + return composite; + } + + private static void snapshot(Display display, Composite composite, String filename) { + Rectangle bounds = composite.getBounds(); + Image image = new Image(display, bounds); + GC gc = new GC(image); + composite.print(gc); + gc.dispose(); + + ImageLoader loader = new ImageLoader(); + loader.data = new ImageData[] { image.getImageData() }; + File output = new File(filename); + output.delete(); + loader.save(filename, SWT.IMAGE_PNG); + System.out.println("Image saved to: " + output.getAbsolutePath()); + } +} |