Hi,
Xerxes RÄnby wrote:
Hello, during bug testing of icedtea6 on various embedded and stationary
hardware i have stumbled across some rendering issues of java2d when
running under icedtea6.
Testcase:
http://java.sun.com/products/java-media/2D/samples/java2demo/Java2Demo.html
Sourcecode available from:
http://java.sun.com/products/java-media/2D/samples/java2demo/Java2D.zip
Attaching two patches and adding 2d-dev to cc list to get feedback on
the patches.
5. and 6. The last two issues are related. the specified joinType are
not applyed to lines that gets inserted using closePath.
public void drawDemo(int w, int h, Graphics2D g2) {
BasicStroke bs = new BasicStroke(bswidth,
BasicStroke.CAP_BUTT, joinType);
GeneralPath p = new GeneralPath();
p.moveTo(- w / 4.0f, - h / 12.0f);
p.lineTo(+ w / 4.0f, - h / 12.0f);
p.lineTo(- w / 6.0f, + h / 4.0f);
p.lineTo(+ 0.0f, - h / 4.0f);
p.lineTo(+ w / 6.0f, + h / 4.0f);
p.closePath(); //
<----- issue 6: this should have create a correct join. instead a round
join are allways used.
p.closePath(); //
<----- issue 5: this should have create a correct join. the rendering
of the start line should
Is the second closePath() supposed to do anything?
// have been updated when the second closepath was
instructed.
g2.translate(w/2, h/2);
g2.setColor(Color.black);
g2.draw(bs.createStrokedShape(p));
}
issue5-internal-joint.patch fixes the internal joint on the bottom right
corner of the star. The external round join issue is fixed by an
OpenJDK7 patch [1]. This has already been backported to IcedTea6.
issue6-extra-line-closing-joint.patch removes the extra line that is
drawn on the top left corner of the star, fixing that issue.
Each patch also includes a jtreg test for the bug.
The second attached picture without pink markings displays how the
testcase looks using Sun's properitary java2d for reference.
Cheers.
Xerxes
Cheers,
Omair
[1] http://hg.openjdk.java.net/jdk7/2d/jdk/rev/9318628e8eee
diff -r 9d89e1ada1c8 -r 100e6e0825ee HACKING
--- a/HACKING Wed Apr 29 16:01:07 2009 +0200
+++ b/HACKING Wed Apr 29 13:46:09 2009 -0400
@@ -93,6 +93,7 @@
* icedtea-jtreg-jrunscript.patch: Fix jrunscript test so it works with newer versions of rhino (by comparing the actual numbers).
* icedtea-ignore-unrecognized-options.patch: Add -XX:+IgnoreUnrecognizedVMOptions flag to hotspot (S6788376).
* icedtea-java2d-mitre-join.patch: Backport fix for mitre join decoration (S6812600).
+* icedtea-java2d-stroker-internal-joint.patch: Fix penultimate joint created by GeneralPath.closePath().
The following patches are only applied to OpenJDK6 in IcedTea6:
diff -r 9d89e1ada1c8 -r 100e6e0825ee Makefile.am
--- a/Makefile.am Wed Apr 29 16:01:07 2009 +0200
+++ b/Makefile.am Wed Apr 29 13:46:09 2009 -0400
@@ -678,6 +678,7 @@
patches/icedtea-network-unreachable.patch \
patches/icedtea-dnd-filelists.patch \
patches/icedtea-java2d-mitre-join.patch \
+ patches/icedtea-java2d-stroker-internal-joint.patch \
$(DISTRIBUTION_PATCHES)
stamps/extract.stamp: stamps/download.stamp
diff -r 9d89e1ada1c8 -r 100e6e0825ee patches/icedtea-java2d-stroker-internal-joint.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/icedtea-java2d-stroker-internal-joint.patch Wed Apr 29 13:46:09 2009 -0400
@@ -0,0 +1,98 @@
+--- openjdk/jdk/src/share/classes/sun/java2d/pisces/Stroker.java.orig 2009-04-29 13:30:24.000000000 -0400
++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Stroker.java 2009-04-29 13:31:05.000000000 -0400
+@@ -614,6 +614,8 @@
+ ROUND_JOIN_INTERNAL_THRESHOLD);
+ }
+
++ emitLineTo(x0, y0, !ccw);
++
+ emitLineTo(x0 + mx, y0 + my);
+ emitLineTo(sx0 + mx, sy0 + my);
+
+--- /dev/null 2009-04-15 13:37:55.776002308 -0400
++++ openjdk/jdk/test/sun/pisces/MiterInternalJointTest.java 2009-04-29 13:41:30.000000000 -0400
+@@ -0,0 +1,84 @@
++/*
++ * Copyright 2009 Red Hat, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++/*
++ @test
++ @summary Check that the penultimate joint created using
++ generalPath.closePath() is correct
++ @author Omair Majid <[email protected]>
++ @run main MiterInternalJointTest
++ */
++
++
++import java.awt.BasicStroke;
++import java.awt.Color;
++import java.awt.Graphics2D;
++import java.awt.geom.GeneralPath;
++import java.awt.image.BufferedImage;
++
++public class MiterInternalJointTest {
++
++ static final int WIDTH = 200;
++ static final int HEIGHT = 200;
++
++ static final int x0 = 50, y0 = 50;
++ static final int x1 = 150, y1 = 50;
++ static final int x2 = 100, y2 = 100;
++
++ private static BufferedImage createTestImage() {
++ final BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
++ BufferedImage.TYPE_INT_BGR);
++ Graphics2D g = image.createGraphics();
++
++ g.setColor(Color.BLACK);
++ g.fillRect(0, 0, WIDTH, HEIGHT);
++
++ float wideStrokeWidth = 20.0f;
++ BasicStroke wideStroke = new BasicStroke(wideStrokeWidth,
++ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, wideStrokeWidth);
++ float thinStrokeWidth = 3.0f;
++ BasicStroke thinStroke = new BasicStroke(thinStrokeWidth,
++ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, thinStrokeWidth);
++
++ g.setColor(Color.WHITE);
++ GeneralPath path = new GeneralPath();
++ path.moveTo(x0, y0);
++ path.lineTo(x1, y1);
++ path.lineTo(x2, y2);
++ path.closePath();
++ path.closePath();
++ g.setStroke(thinStroke);
++ g.draw(wideStroke.createStrokedShape(path));
++
++ return image;
++ }
++
++ public static void main(String[] args) {
++
++ BufferedImage testImage = createTestImage();
++
++ int color = testImage.getRGB(x2,y2);
++ System.out.println("Color seen: #" + Integer.toHexString(color));
++ if (color != Color.WHITE.getRGB()) {
++ throw new RuntimeException(
++ "Test Failed; expected to see a white vertex above the bottom of the triangle");
++ }
++
++ }
++}
diff -r 100e6e0825ee -r d8106a6bd266 HACKING
--- a/HACKING Wed Apr 29 13:46:09 2009 -0400
+++ b/HACKING Wed Apr 29 14:09:02 2009 -0400
@@ -94,6 +94,7 @@
* icedtea-ignore-unrecognized-options.patch: Add -XX:+IgnoreUnrecognizedVMOptions flag to hotspot (S6788376).
* icedtea-java2d-mitre-join.patch: Backport fix for mitre join decoration (S6812600).
* icedtea-java2d-stroker-internal-joint.patch: Fix penultimate joint created by GeneralPath.closePath().
+* icedtea-java2d-stroker-internal-close-joint.patch: Fix final joint created by GeneralPath.closePath().
The following patches are only applied to OpenJDK6 in IcedTea6:
diff -r 100e6e0825ee -r d8106a6bd266 Makefile.am
--- a/Makefile.am Wed Apr 29 13:46:09 2009 -0400
+++ b/Makefile.am Wed Apr 29 14:09:02 2009 -0400
@@ -679,6 +679,7 @@
patches/icedtea-dnd-filelists.patch \
patches/icedtea-java2d-mitre-join.patch \
patches/icedtea-java2d-stroker-internal-joint.patch \
+ patches/icedtea-java2d-stroker-internal-close-joint.patch \
$(DISTRIBUTION_PATCHES)
stamps/extract.stamp: stamps/download.stamp
diff -r 100e6e0825ee -r d8106a6bd266 patches/icedtea-java2d-stroker-internal-close-joint.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/icedtea-java2d-stroker-internal-close-joint.patch Wed Apr 29 14:09:02 2009 -0400
@@ -0,0 +1,96 @@
+--- openjdk/jdk/src/share/classes/sun/java2d/pisces/Stroker.java.orig 2009-04-29 14:01:43.000000000 -0400
++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Stroker.java 2009-04-29 14:00:03.000000000 -0400
+@@ -633,7 +633,7 @@
+ }
+
+ emitLineTo(sx0 + mx0, sy0 + my0);
+- emitLineTo(sx0 - mx0, sy0 - my0); // same as reverse[0], reverse[1]
++ emitMoveTo(sx0, sy0); // same as reverse[0], reverse[1]
+
+ // Draw final join on the inside
+ if (ccw) {
+--- /dev/null 2009-04-15 13:37:55.776002308 -0400
++++ openjdk/jdk/test/sun/pisces/MiterInternalCloseJointTest.java 2009-04-29 13:59:31.000000000 -0400
+@@ -0,0 +1,82 @@
++/*
++ * Copyright 2009 Red Hat, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++/*
++ @test
++ @summary Check that the final joint created using
++ generalPath.closePath() is correct
++ @author Omair Majid <[email protected]>
++ @run main MiterInternalCloseJointTest
++ */
++import java.awt.BasicStroke;
++import java.awt.Color;
++import java.awt.Graphics2D;
++import java.awt.geom.GeneralPath;
++import java.awt.image.BufferedImage;
++
++public class MiterInternalCloseJointTest {
++
++ static final int WIDTH = 200;
++ static final int HEIGHT = 200;
++
++ static final int x0 = 50, y0 = 50;
++ static final int x1 = 150, y1 = 50;
++ static final int x2 = 100, y2 = 100;
++
++ private static BufferedImage createTestImage() {
++ final BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
++ BufferedImage.TYPE_INT_BGR);
++ Graphics2D g = image.createGraphics();
++
++ g.setColor(Color.BLACK);
++ g.fillRect(0, 0, WIDTH, HEIGHT);
++
++ float wideStrokeWidth = 20.0f;
++ BasicStroke wideStroke = new BasicStroke(wideStrokeWidth,
++ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, wideStrokeWidth);
++ float thinStrokeWidth = 3.0f;
++ BasicStroke thinStroke = new BasicStroke(thinStrokeWidth,
++ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, thinStrokeWidth);
++
++ g.setColor(Color.WHITE);
++ GeneralPath path = new GeneralPath();
++ path.moveTo(x0, y0);
++ path.lineTo(x1, y1);
++ path.lineTo(x2, y2);
++ path.closePath();
++ path.closePath();
++ g.setStroke(thinStroke);
++ g.draw(wideStroke.createStrokedShape(path));
++
++ return image;
++ }
++
++ public static void main(String[] args) {
++
++ BufferedImage testImage = createTestImage();
++
++ int color = testImage.getRGB(x0,y0-5);
++ System.out.println("Color seen: #" + Integer.toHexString(color));
++ if (color == Color.WHITE.getRGB()) {
++ throw new RuntimeException(
++ "Test Failed; did not expected to see a white line at the start of the path");
++ }
++
++ }
++}