diff options
2 files changed, 177 insertions, 1 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java index 61386eda38..a841d7e07a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.swt.graphics; +import java.util.*; + import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.cairo.*; @@ -2054,7 +2056,53 @@ Rectangle getClippingInPixels() { if (clipRgn != 0) { /* Convert clipping to device space if needed */ if (data.clippingTransform != null && GTK.GTK_VERSION < OS.VERSION (3, 14, 0)) { - clipRgn = convertRgn(clipRgn, data.clippingTransform); + clipRgn = convertRgn(clipRgn, data.clippingTransform); + Cairo.cairo_region_intersect(rgn, clipRgn); + Cairo.cairo_region_destroy(clipRgn); + } else if (!Arrays.equals(data.clippingTransform, currentTransform) && GTK.GTK_VERSION >= OS.VERSION (3, 14, 0)) { + double[] clippingTransform; + if (currentTransform != null && data.clippingTransform == null) { + /* + * User actions in this case are: + * 1. Set clipping. + * 2. Set a transformation B. + * + * The clipping was specified before transformation B was set. + * So to convert it to the new space, we just invert the transformation B. + */ + clippingTransform = currentTransform.clone(); + Cairo.cairo_matrix_invert(clippingTransform); + } else if (currentTransform != null && data.clippingTransform != null) { + /* + * User actions in this case are: + * 1. Set a transformation A. + * 2. Set clipping. + * 3. Set a different transformation B. This is global and wipes out transformation A. + * + * Since step 3. wipes out transformation A, we must apply A on the clipping rectangle to have + * the correct clipping rectangle after transformation A is wiped. + * Then, we apply the inverted transformation B on the resulting clipping, + * to convert it to the new space (which results after applying B). + */ + clippingTransform = new double[6]; + double[] invertedCurrentTransform = currentTransform.clone(); + Cairo.cairo_matrix_invert(invertedCurrentTransform); + Cairo.cairo_matrix_multiply(clippingTransform, data.clippingTransform, invertedCurrentTransform); + } else { + /* + * User actions in this case are: + * 1. Set a transformation A. + * 2. Set clipping. + * 3. Wipe the transformation A (i.e. call GC.setTransformation(A)). + * + * We must apply transformation A on the clipping, to convert it to the new space. + */ + clippingTransform = data.clippingTransform.clone(); + } + long oldRgn = rgn; + rgn = convertRgn(rgn, clippingTransform); + Cairo.cairo_region_destroy(oldRgn); + clipRgn = convertRgn(clipRgn, clippingTransform); Cairo.cairo_region_intersect(rgn, clipRgn); Cairo.cairo_region_destroy(clipRgn); } else { @@ -3047,6 +3095,11 @@ void setClipping(long clipRgn) { if (GTK.GTK_VERSION < OS.VERSION (3, 14, 0)) { if (data.clippingTransform == null) data.clippingTransform = new double[6]; Cairo.cairo_get_matrix(cairo, data.clippingTransform); + } else if (currentTransform != null) { + // store the current transformation, to use it when the user requests clipping bounds + data.clippingTransform = currentTransform.clone(); + } else { + data.clippingTransform = null; } setCairoClip(data.damageRgn, clipRgn); } diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545226_GC_getClipping_is_wrong.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545226_GC_getClipping_is_wrong.java new file mode 100644 index 0000000000..290333b36d --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545226_GC_getClipping_is_wrong.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2019 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 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.Rectangle; +import org.eclipse.swt.graphics.Transform; +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 GC#getClipping()} returns wrong results when the user has set a transformation. Steps to reproduce: + * <ol> + * <li>Run the snippet.</li> + * <li>Observe {@link GC#getClipping()} states that the clipping starts at (10, 10), while it actually starts at (6, 18).</li> + * </ol> + * Expected results: The user specified and transformed clipping area is painted in dark red. + * The result of {@link GC#getClipping()} is painted in transparent red, the dark red area is bound by {@link GC#getClipping()}. + * The drawn text indicates the correct bounds of {@link GC#getClipping()} result. + * Actual results: Drawing is done correctly in regard of API, however + * the bounds returned by {@link GC#getClipping()} are incorrect, in particular the upper-left point is stated to be (10, 10). + */ +public class Bug545226_GC_getClipping_is_wrong { + + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setSize(600, 450); + shell.setText("Bug 545226 GC clipping is wrong"); + shell.setLayout(new FillLayout()); + Composite main = new Composite(shell, SWT.NONE); + main.setLayout(new FillLayout()); + + Composite[] squares = showCaseClipping(main); + for (Composite square : squares) { + 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, upperRight, bottomLeft, bottomRight }; + for (int i = 0; i < squares.length; ++i) { + Composite square = squares[i]; + square.setLayout(new FillLayout(SWT.VERTICAL)); + + Canvas canvas = new Canvas(square, SWT.BORDER); + PaintListener paint = new ShowCaseClipping(); + canvas.addPaintListener(paint); + } + + return squares; + } + + private static class ShowCaseClipping implements PaintListener { + + @Override + public void paintControl(PaintEvent event) { + GC gc = event.gc; + Device device = gc.getDevice(); + Rectangle originalClipping = gc.getClipping(); + + Transform transform = new Transform(device); + transform.translate(10.f, 10.f); + transform.scale(0.8f, 0.8f); + transform.rotate(5.f); + gc.setTransform(transform); + gc.setClipping(10, 10, 225, 150); + gc.setTransform(null); + + gc.setAlpha(155); + gc.setBackground(device.getSystemColor(SWT.COLOR_DARK_RED)); + gc.fillRectangle(originalClipping); + + Rectangle transformedClipping = gc.getClipping(); + gc.setClipping(gc.getClipping()); + gc.setBackground(device.getSystemColor(SWT.COLOR_DARK_RED)); + gc.setAlpha(75); + gc.fillRectangle(transformedClipping); + gc.setBackground(device.getSystemColor(SWT.COLOR_GRAY)); + gc.setAlpha(255); + gc.drawText(transformedClipping.toString(), 6, 18); + } + } +}
\ No newline at end of file |