Request for review: cleanup in preparation of the final java2d backport.
Denis Lila
dlila at redhat.com
Tue Mar 1 15:03:40 PST 2011
> and attach the patches. I can't really review a patch that isn't
> attached as I can't comment inline.
In the future, should I attach the diff, or just attach the
patches and put the ChangeLog diff in the e-mail?
> The patch you've separated as 'piscesMakefile.patch' should really be
> part
> of whichever patch needs it to make it work. It will need to be
> upstreamed
> to OpenJDK6 as one lump to avoid breaking the build.
Ok. That's what the attached patch does.
Is it ok to push?
ChangeLog:
+2011-03-01 Denis Lila <dlila at redhat.com>
+
+ * Makefile.am:
+ Removed patch (piscesMakefile.am).
+ * patches/openjdk/piscesMakefile.patch:
+ Removed. Its body was added to
+ 6967436-6967433-floating-pt-conversion.patch
+
> Why is the amalgamated patch being removed in the first changeset when
> it is the other changeset that replaces it? IMHO, 1 should just be:
>
> 2011-02-28 Denis Lila <dlila at redhat.com>
>
> * Makefile.am: Replaced patches/renderer-crossing.patch with
> patches/openjdk/6887494-NPE-in-pisces.patch.
> * patches/openjdk/6887494-NPE-in-pisces.patch:
> Added.
> * patches/renderer-crossing.patch:
> Removed. Replaced by 6887494-NPE-in-pisces.patch.
I agree that this isn't very pleasant, but I think doing what
you suggest above would might break the build since the NPE
patch came before the huge 3 changeset patch in openjdk7. Moreover,
the 3 changeset patch assumes that renderer-crossing.patch
has been applied, so at best we would need to do some complicated
reordering of the patch application to not break the build. It
might even be impossible to make it build without removing the
huge patch, so that's a lot of work for a patch that is removed
in the next changeset.
This is why I put everything in one big changeset when I first
proposed it...
> Why does the floating point conversion have two bug IDs?
Because the commit comment in openjdk7 has 2 bug IDs.
Thank you,
Denis.
----- Original Message -----
> On 11:19 Mon 28 Feb , Denis Lila wrote:
> > Hi.
> >
> > http://icedtea.classpath.org/~dlila/cleanupPart1v2.patch
> > http://icedtea.classpath.org/~dlila/cleanupPart2v2.patch
> >
> > and the attached patch are the revised patches (the only
> > thing that has changed is that now I'm removing patches
> > in the same changeset where I stop applying them).
> >
> > Ok to push?
> >
>
> Please include the ChangeLogs in the e-mail:
>
> 2011-02-28 Denis Lila <dlila at redhat.com>
>
> * Makefile.am: Replaced patches/renderer-crossing.patch with
> patches/openjdk/6887494-NPE-in-pisces.patch.
> No longer applying:
> patches/openjdk/6967436-6976265-6967434-pisces.patch
> patches/openjdk/6766342-AA-simple-shape-performance.patch
> * patches/openjdk/6887494-NPE-in-pisces.patch:
> Added.
> * patches/renderer-crossing.patch:
> Removed. Replaced by 6887494-NPE-in-pisces.patch.
> * patches/openjdk/6967436-6976265-6967434-pisces.patch:
> Removed. Will never be applied again.
>
> 2011-02-28 Denis Lila <dlila at redhat.com>
>
> * Makefile.am: Added patches
> patches/openjdk/6967434-bad-round-joins.patch,
> patches/openjdk/6967436-6967433-floating-pt-conversion.patch, and
> patches/openjdk/6976265-stroke-control.patch to replace
> patches/openjdk/6967436-6976265-6967434-pisces.patch, which
> was removed in the previous changeset.
> Added patches/piscesMakefile.patch to make the build work.
> No longer applying:
> patches/stroker-finish.patch,
> patches/java2d-stroker-internal-joint.patch, and
> patches/java2d-stroker-internal-close-joint.patch.
> Applying once again:
> patches/openjdk/6766342-AA-simple-shape-performance.patch
> * patches/openjdk/6967434-bad-round-joins.patch
> * patches/openjdk/6967436-6967433-floating-pt-conversion.patch
> * patches/openjdk/6976265-stroke-control.patch
> * patches/piscesMakefile.patch:
> Added.
> * patches/java2d-stroker-internal-close-joint.patch
> * patches/java2d-stroker-internal-joint.patch
> * patches/stroker-finish.patch:
> Obsolete and unused. Removed.
>
>
>
>
>
> > Regards,
> > Denis.
> >
> > ----- Original Message -----
> > > On 16:20 Thu 24 Feb , Denis Lila wrote:
> > > > And the attached file is the final part of the cleanup.
> > > > It just removes 3 patches that were obsoleted by the
> > > > previous changeset (if the previous changest is approved
> > > > for pushing, that is).
> > > >
> > >
> > > As I said in my previous reply, these can be removed with
> > > the patch that obsoletes them.
> > >
> > > But can you explain why they are obsolete?
> > >
> > > > Ok to push?
> > > >
> > > > Thank you,
> > > > Denis.
> > > >
> > > > ----- Original Message -----
> > > > > This:
> > > > > http://icedtea.classpath.org/~dlila/cleanupPart2.patch
> > > > > is the second part of the clean up. It removes
> > > > > patches/openjdk/6967436-6976265-6967434-pisces.patch
> > > > > (which we stopped applying in the previous
> > > > > cleanupPart1.patch),
> > > > > it adds the 3 replacement patches for it, it adds
> > > > > piscesMakefile.patch to make the build work, and it reapplies
> > > > > patches/openjdk/6766342-AA-simple-shape-performance.patch.
> > > > >
> > > > > It also stops applying 3 patches made obsolete by the 3 new
> > > > > patches (but doesn't remove them (should it?). I've left this
> > > > > for the next changeset).
> > > > >
> > > > > Ok to push (contingent on cleanupPart1.patch being ok)?
> > > > >
> > > > > Thank you,
> > > > > Denis.
> > > > >
> > > > > ----- Original Message -----
> > > > > > > The IcedTea version:
> > > > > > >
> > > > > > > 2008-10-27 Mark Wielaard <mark at klomp.org>
> > > > > > >
> > > > > > > * patches/icedtea-renderer-crossing.patch: New patch.
> > > > > > >
> > > > > > > predates the OpenJDK one:
> > > > > > >
> > > > > > > changeset: 1878:ccc36189f2a7
> > > > > > > user: rkennke
> > > > > > > date: Mon Oct 05 23:12:22 2009 +0200
> > > > > > >
> > > > > > > by just under a year. Roman may even have been using the
> > > > > > > IcedTea
> > > > > > > patch
> > > > > > > and not bothered
> > > > > > > to report to us.
> > > > > > >
> > > > > > > Anyway, good to be replaced in a separate changeset.
> > > > > >
> > > > > > The attached patch does the replacement of
> > > > > > patches/renderer-crossings.patch. I do this first because it
> > > > > > came
> > > > > > before any of the other things, and the other patches won't
> > > > > > apply
> > > > > > without it.
> > > > > >
> > > > > > Unfortunately, it also has to stop applying
> > > > > > patches/openjdk/6967436-6976265-6967434-pisces.patch
> > > > > > patches/openjdk/6766342-AA-simple-shape-performance.patch
> > > > > > If we don't stop applying the first of those, the patching
> > > > > > will
> > > > > > fail. It's not a good idea to apply the second because it
> > > > > > changes
> > > > > > a file that is also changed by
> > > > > > 6967436-6976265-6967434-pisces.patch.
> > > > > > So until the replacement for
> > > > > > 6967436-6976265-6967434-pisces.patch is
> > > > > > in, we shouldn't apply it.
> > > > > >
> > > > > > Ok to push?
> > > > > >
> > > > > > The next changeset after this will introduce the three
> > > > > > replacements
> > > > > > of
> > > > > > patches/openjdk/6967436-6976265-6967434-pisces.patch.
> > > > > >
> > > > > > Thank you.
> > > > > >
> > > > > > ----- Original Message -----
> > > > > > > On 12:45 Thu 24 Feb , Denis Lila wrote:
> > > > > > > > Hi.
> > > > > > > >
> > > > > > > > I want to push this patch:
> > > > > > > > http://icedtea.classpath.org/~dlila/hgCleanup.diff. I
> > > > > > > > tried
> > > > > > > > attaching it,
> > > > > > > > but it was too big and I cancelled the message.
> > > > > > > >
> > > > > > >
> > > > > > > Ok let's split this up and do them in individual
> > > > > > > changesets
> > > > > > > with
> > > > > > > individual
> > > > > > > ChangeLogs. It's much easier to track things that way if
> > > > > > > we're
> > > > > > > not
> > > > > > > mixing
> > > > > > > up orthogonal things.
> > > > > > >
> > > > > > > > The changes are:
> > > > > > > > patches/openjdk/6967436-6976265-6967434-pisces.patch was
> > > > > > > > replaced
> > > > > > > > by
> > > > > > > > 6967436-6967433-floating-pt-conversion.patch,
> > > > > > > > 6967434-bad-round-joins.patch,
> > > > > > > > and 6976265-stroke-control.patch. Each of the separate
> > > > > > > > patches
> > > > > > > > is
> > > > > > > > its own
> > > > > > > > changeset in openjdk7, so it was my mistake to begin
> > > > > > > > with to
> > > > > > > > lump
> > > > > > > > them
> > > > > > > > all into one backport. Splitting it up into the 3
> > > > > > > > patches
> > > > > > > > fixes
> > > > > > > > it.
> > > > > > > > Also
> > > > > > > > each of the three patches is an unmodified "hg export"
> > > > > > > > of
> > > > > > > > its
> > > > > > > > openjdk7
> > > > > > > > revision. What we had in
> > > > > > > > 6967436-6976265-6967434-pisces.patch
> > > > > > > > wasn't,
> > > > > > > > because I had to remove some of the @Override
> > > > > > > > annotations so
> > > > > > > > that
> > > > > > > > the
> > > > > > > > build wouldn't fail. The proper way to get around this
> > > > > > > > is by
> > > > > > > > changing
> > > > > > > > the source and target options to ecj, which is what
> > > > > > > > patches/piscesMakefile.patch does.
> > > > > > > >
> > > > > > >
> > > > > > > Ok so this is one patch: the three patch split + the
> > > > > > > piscesMakefile
> > > > > > > to
> > > > > > > make
> > > > > > > it work.
> > > > > > >
> > > > > > > > I replaced patches/renderer-crossing.patch with
> > > > > > > > patches/openjdk/6887494-NPE-in-pisces.patch. The former
> > > > > > > > adds
> > > > > > > > if (crossingIndices != null &&
> > > > > > > > crossingIndices.length >
> > > > > > > > DEFAULT_INDICES_SIZE) {
> > > > > > > > while the latter adds
> > > > > > > > if (crossingIndices != null &&
> > > > > > > > crossingIndices.length > DEFAULT_INDICES_SIZE)
> > > > > > > > {
> > > > > > > > so they do exactly the same thing, but the layout is
> > > > > > > > different.
> > > > > > > > What
> > > > > > > > I think
> > > > > > > > happened is that patches/renderer-crossing.patch was
> > > > > > > > added
> > > > > > > > to
> > > > > > > > icedtea before
> > > > > > > > openjdk7, and when it went into openjdk7 the newlines
> > > > > > > > were
> > > > > > > > changed.
> > > > > > > > So now
> > > > > > > > I'm replacing the patch with a proper backport.
> > > > > > > >
> > > > > > >
> > > > > >
> > > > > > >
> > > > > > > > The other changes are just removing obsolete patches,
> > > > > > > > and
> > > > > > > > they're
> > > > > > > > described
> > > > > > > > in the ChangeLog.
> > > > > > > >
> > > > > > >
> > > > > > > Again, separate changeset.
> > > > > > >
> > > > > > > > Thank you,
> > > > > > > > Denis.
> > > > > > >
> > > > > > > --
> > > > > > > Andrew :)
> > > > > > >
> > > > > > > Free Java Software Engineer
> > > > > > > Red Hat, Inc. (http://www.redhat.com)
> > > > > > >
> > > > > > > Support Free Java!
> > > > > > > Contribute to GNU Classpath and IcedTea
> > > > > > > http://www.gnu.org/software/classpath
> > > > > > > http://icedtea.classpath.org
> > > > > > > PGP Key: F5862A37 (https://keys.indymedia.org/)
> > > > > > > Fingerprint = EA30 D855 D50F 90CD F54D 0698 0713 C3ED F586
> > > > > > > 2A37
> > >
> > > > diff -r 5c0be31545ff ChangeLog
> > > > --- a/ChangeLog Thu Feb 24 15:30:47 2011 -0500
> > > > +++ b/ChangeLog Thu Feb 24 16:13:11 2011 -0500
> > > > @@ -1,3 +1,12 @@
> > > > +2011-02-24 Denis Lila <dlila at redhat.com>
> > > > +
> > > > + * patches/java2d-stroker-internal-close-joint.patch:
> > > > + Obsolete and unused. Removed.
> > > > + * patches/java2d-stroker-internal-joint.patch:
> > > > + Obsolete and unused. Removed.
> > > > + * patches/stroker-finish.patch:
> > > > + Obsolete and unused. Removed.
> > > > +
> > > > 2011-02-24 Denis Lila <dlila at redhat.com>
> > > >
> > > > * Makefile.am: Added patches
> > > > diff -r 5c0be31545ff
> > > > patches/java2d-stroker-internal-close-joint.patch
> > > > --- a/patches/java2d-stroker-internal-close-joint.patch Thu Feb
> > > > 24
> > > > 15:30:47 2011 -0500
> > > > +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
> > > > @@ -1,96 +0,0 @@
> > > > ----
> > > > 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 <omajid at redhat.com>
> > > > -+ @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");
> > > > -+ }
> > > > -+
> > > > -+ }
> > > > -+}
> > > > diff -r 5c0be31545ff patches/java2d-stroker-internal-joint.patch
> > > > --- a/patches/java2d-stroker-internal-joint.patch Thu Feb 24
> > > > 15:30:47 2011 -0500
> > > > +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
> > > > @@ -1,98 +0,0 @@
> > > > ----
> > > > 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 <omajid at redhat.com>
> > > > -+ @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 5c0be31545ff patches/stroker-finish.patch
> > > > --- a/patches/stroker-finish.patch Thu Feb 24 15:30:47 2011
> > > > -0500
> > > > +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
> > > > @@ -1,20 +0,0 @@
> > > > ----
> > > > openjdk6/jdk/src/share/classes/sun/java2d/pisces/Stroker.java
> > > > -+++
> > > > openjdk/jdk/src/share/classes/sun/java2d/pisces/Stroker.java
> > > > -@@ -695,7 +695,7 @@
> > > > - long ldx = (long)(px0 - x0);
> > > > - long ldy = (long)(py0 - y0);
> > > > - long llen = lineLength(ldx, ldy);
> > > > -- long s = (long)lineWidth2*65536/llen;
> > > > -+ long s = (llen == 0) ? 0 : (long)lineWidth2*65536/llen;
> > > > -
> > > > - int capx = x0 - (int)(ldx*s >> 16);
> > > > - int capy = y0 - (int)(ldy*s >> 16);
> > > > -@@ -717,7 +717,7 @@
> > > > - long ldx = (long)(sx1 - sx0);
> > > > - long ldy = (long)(sy1 - sy0);
> > > > - long llen = lineLength(ldx, ldy);
> > > > -- long s = (long)lineWidth2*65536/llen;
> > > > -+ long s = (llen == 0) ? 0 : (long)lineWidth2*65536/llen;
> > > > -
> > > > - int capx = sx0 - (int)(ldx*s >> 16);
> > > > - int capy = sy0 - (int)(ldy*s >> 16);
> > >
> > >
> > > --
> > > Andrew :)
> > >
> > > Free Java Software Engineer
> > > Red Hat, Inc. (http://www.redhat.com)
> > >
> > > Support Free Java!
> > > Contribute to GNU Classpath and IcedTea
> > > http://www.gnu.org/software/classpath
> > > http://icedtea.classpath.org
> > > PGP Key: F5862A37 (https://keys.indymedia.org/)
> > > Fingerprint = EA30 D855 D50F 90CD F54D 0698 0713 C3ED F586 2A37
>
> > # HG changeset patch
> > # User Denis Lila <dlila at redhat.com>
> > # Date 1298908401 18000
> > # Node ID 131dffed9cc9c611d59eff469e77c49001794cd1
> > # Parent 3328285d972281bb22a3810b84ea4fa0b17d0dff
> > Backport of S7016856
> >
> > diff -r 3328285d9722 -r 131dffed9cc9 ChangeLog
> > --- a/ChangeLog Mon Feb 28 10:42:50 2011 -0500
> > +++ b/ChangeLog Mon Feb 28 10:53:21 2011 -0500
> > @@ -1,3 +1,12 @@
> > +2011-02-28 Denis Lila <dlila at redhat.com>
> > +
> > + * Makefile.am:
> > + Added patch.
> > + * NEWS:
> > + Added backport entry.
> > + * patches/openjdk/7016856-pisces-performance.patch:
> > + Backport.
> > +
> > 2011-02-28 Denis Lila <dlila at redhat.com>
> >
> > * Makefile.am: Added patches
> > diff -r 3328285d9722 -r 131dffed9cc9 Makefile.am
> > --- a/Makefile.am Mon Feb 28 10:42:50 2011 -0500
> > +++ b/Makefile.am Mon Feb 28 10:53:21 2011 -0500
> > @@ -325,7 +325,8 @@
> > patches/openjdk/6967436-6967433-floating-pt-conversion.patch \
> > patches/openjdk/6976265-stroke-control.patch \
> > patches/openjdk/6967434-bad-round-joins.patch \
> > - patches/openjdk/6766342-AA-simple-shape-performance.patch
> > + patches/openjdk/6766342-AA-simple-shape-performance.patch \
> > + patches/openjdk/7016856-pisces-performance.patch
> >
> > if !WITH_ALT_HSBUILD
> > ICEDTEA_PATCHES += \
> > diff -r 3328285d9722 -r 131dffed9cc9 NEWS
> > --- a/NEWS Mon Feb 28 10:42:50 2011 -0500
> > +++ b/NEWS Mon Feb 28 10:53:21 2011 -0500
> > @@ -434,6 +434,7 @@
> > - S6444769:
> > java/awt/Insets/WindowWithWarningTest/WindowWithWarningTest.html
> > fails
> > - S6775317: Improve performance of non-AA transformed rectangles
> > and single wide lines in software pipelines
> > - S6766342: Improve performance of Ductus rasterizer
> > + - S7016856: fix dashing performance regression. Improve other
> > rendering performance.
> > * Bug fixes
> > - RH661505: JPEGs with sRGB IEC61966-2.1 color profiles have
> > wrong colors
> > - PR600: HS19 upgrade broke CACAO build on ARM
> > diff -r 3328285d9722 -r 131dffed9cc9
> > patches/openjdk/7016856-pisces-performance.patch
> > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
> > +++ b/patches/openjdk/7016856-pisces-performance.patch Mon Feb 28
> > 10:53:21 2011 -0500
> > @@ -0,0 +1,2116 @@
> > +# HG changeset patch
> > +# User dlila
> > +# Date 1297174969 18000
> > +# Node ID 5e624003e6225ec1ee92536bc356cba90043826c
> > +# Parent 21621a756b32028fde37935cd91330a8e194f96b
> > +7016856: dashing performance was reduced during latest changes to
> > the OpenJDK rasterizer
> > +Summary: Optimized dashing, rasterizing, and the flow of
> > transformed coordinates
> > +Reviewed-by: flar
> > +
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/Curve.java
> > +--- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Curve.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Curve.java Tue
> > Feb 08 09:22:49 2011 -0500
> > +@@ -27,7 +27,7 @@
> > +
> > + import java.util.Iterator;
> > +
> > +-class Curve {
> > ++final class Curve {
> > +
> > + float ax, ay, bx, by, cx, cy, dx, dy;
> > + float dax, day, dbx, dby;
> > +@@ -101,14 +101,6 @@
> > + return t * (t * day + dby) + cy;
> > + }
> > +
> > +- private float ddxat(float t) {
> > +- return 2 * dax * t + dbx;
> > +- }
> > +-
> > +- private float ddyat(float t) {
> > +- return 2 * day * t + dby;
> > +- }
> > +-
> > + int dxRoots(float[] roots, int off) {
> > + return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
> > + }
> > +@@ -131,17 +123,17 @@
> > + // finds points where the first and second derivative are
> > + // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0
> > (where
> > + // * is a dot product). Unfortunately, we have to solve a cubic.
> > +- private int perpendiculardfddf(float[] pts, int off, final float
> > err) {
> > ++ private int perpendiculardfddf(float[] pts, int off) {
> > + assert pts.length >= off + 4;
> > +
> > +- // these are the coefficients of g(t):
> > ++ // these are the coefficients of some multiple of g(t) (not g(t),
> > ++ // because the roots of a polynomial are not changed after
> > multiplication
> > ++ // by a constant, and this way we save a few multiplications).
> > + final float a = 2*(dax*dax + day*day);
> > + final float b = 3*(dax*dbx + day*dby);
> > + final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;
> > + final float d = dbx*cx + dby*cy;
> > +- // TODO: We might want to divide the polynomial by a to make the
> > +- // coefficients smaller. This won't change the roots.
> > +- return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f);
> > ++ return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
> > + }
> > +
> > + // Tries to find the roots of the function ROC(t)-w in [0, 1). It
> > uses
> > +@@ -161,7 +153,7 @@
> > + // no OOB exception, because by now off<=6, and roots.length >= 10
> > + assert off <= 6 && roots.length >= 10;
> > + int ret = off;
> > +- int numPerpdfddf = perpendiculardfddf(roots, off, err);
> > ++ int numPerpdfddf = perpendiculardfddf(roots, off);
> > + float t0 = 0, ft0 = ROCsq(t0) - w*w;
> > + roots[off + numPerpdfddf] = 1f; // always check interval end
> > points
> > + numPerpdfddf++;
> > +@@ -189,8 +181,9 @@
> > + // A slight modification of the false position algorithm on
> > wikipedia.
> > + // This only works for the ROCsq-x functions. It might be nice to
> > have
> > + // the function as an argument, but that would be awkward in
> > java6.
> > +- // It is something to consider for java7, depending on how
> > closures
> > +- // and function objects turn out. Same goes for the newton's
> > method
> > ++ // TODO: It is something to consider for java8 (or whenever
> > lambda
> > ++ // expressions make it into the language), depending on how
> > closures
> > ++ // and turn out. Same goes for the newton's method
> > + // algorithm in Helpers.java
> > + private float falsePositionROCsqMinusX(float x0, float x1,
> > + final float x, final float err)
> > +@@ -203,7 +196,7 @@
> > + for (int i = 0; i < iterLimit && Math.abs(t - s) > err *
> > Math.abs(t + s); i++) {
> > + r = (fs * t - ft * s) / (fs - ft);
> > + fr = ROCsq(r) - x;
> > +- if (fr * ft > 0) {// have the same sign
> > ++ if (sameSign(fr, ft)) {
> > + ft = fr; t = r;
> > + if (side < 0) {
> > + fs /= (1 << (-side));
> > +@@ -226,55 +219,65 @@
> > + return r;
> > + }
> > +
> > ++ private static boolean sameSign(double x, double y) {
> > ++ // another way is to test if x*y > 0. This is bad for small x, y.
> > ++ return (x < 0 && y < 0) || (x > 0 && y > 0);
> > ++ }
> > ++
> > + // returns the radius of curvature squared at t of this curve
> > + // see
> > http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
> > + private float ROCsq(final float t) {
> > +- final float dx = dxat(t);
> > +- final float dy = dyat(t);
> > +- final float ddx = ddxat(t);
> > +- final float ddy = ddyat(t);
> > ++ // dx=xat(t) and dy=yat(t). These calls have been inlined for
> > efficiency
> > ++ final float dx = t * (t * dax + dbx) + cx;
> > ++ final float dy = t * (t * day + dby) + cy;
> > ++ final float ddx = 2 * dax * t + dbx;
> > ++ final float ddy = 2 * day * t + dby;
> > + final float dx2dy2 = dx*dx + dy*dy;
> > + final float ddx2ddy2 = ddx*ddx + ddy*ddy;
> > + final float ddxdxddydy = ddx*dx + ddy*dy;
> > +- float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 -
> > ddxdxddydy*ddxdxddydy))*dx2dy2;
> > +- return ret;
> > ++ return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 -
> > ddxdxddydy*ddxdxddydy));
> > + }
> > +
> > +- // curve to be broken should be in pts[0]
> > +- // this will change the contents of both pts and Ts
> > ++ // curve to be broken should be in pts
> > ++ // this will change the contents of pts but not Ts
> > + // TODO: There's no reason for Ts to be an array. All we need is a
> > sequence
> > + // of t values at which to subdivide. An array statisfies this
> > condition,
> > + // but is unnecessarily restrictive. Ts should be an
> > Iterator<Float> instead.
> > + // Doing this will also make dashing easier, since we could easily
> > make
> > + // LengthIterator an Iterator<Float> and feed it to this function
> > to simplify
> > + // the loop in Dasher.somethingTo.
> > +- static Iterator<float[]> breakPtsAtTs(final float[][] pts, final
> > int type,
> > ++ static Iterator<Integer> breakPtsAtTs(final float[] pts, final
> > int type,
> > + final float[] Ts, final int numTs)
> > + {
> > +- assert pts.length >= 2 && pts[0].length >= 8 && numTs <=
> > Ts.length;
> > +- return new Iterator<float[]>() {
> > +- int nextIdx = 0;
> > ++ assert pts.length >= 2*type && numTs <= Ts.length;
> > ++ return new Iterator<Integer>() {
> > ++ // these prevent object creation and destruction during
> > autoboxing.
> > ++ // Because of this, the compiler should be able to completely
> > ++ // eliminate the boxing costs.
> > ++ final Integer i0 = 0;
> > ++ final Integer itype = type;
> > + int nextCurveIdx = 0;
> > ++ Integer curCurveOff = i0;
> > + float prevT = 0;
> > +
> > + @Override public boolean hasNext() {
> > + return nextCurveIdx < numTs + 1;
> > + }
> > +
> > +- @Override public float[] next() {
> > +- float[] ret;
> > ++ @Override public Integer next() {
> > ++ Integer ret;
> > + if (nextCurveIdx < numTs) {
> > + float curT = Ts[nextCurveIdx];
> > + float splitT = (curT - prevT) / (1 - prevT);
> > + Helpers.subdivideAt(splitT,
> > +- pts[nextIdx], 0,
> > +- pts[nextIdx], 0,
> > +- pts[1-nextIdx], 0, type);
> > +- updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs -
> > nextCurveIdx - 1);
> > +- ret = pts[nextIdx];
> > +- nextIdx = 1 - nextIdx;
> > ++ pts, curCurveOff,
> > ++ pts, 0,
> > ++ pts, type, type);
> > ++ prevT = curT;
> > ++ ret = i0;
> > ++ curCurveOff = itype;
> > + } else {
> > +- ret = pts[nextIdx];
> > ++ ret = curCurveOff;
> > + }
> > + nextCurveIdx++;
> > + return ret;
> > +@@ -283,12 +286,5 @@
> > + @Override public void remove() {}
> > + };
> > + }
> > +-
> > +- // precondition: ts[off]...ts[off+len-1] must all be greater than
> > t.
> > +- private static void updateTs(float[] ts, final float t, final int
> > off, final int len) {
> > +- for (int i = off; i < off + len; i++) {
> > +- ts[i] = (ts[i] - t) / (1 - t);
> > +- }
> > +- }
> > + }
> > +
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/Dasher.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Dasher.java Thu
> > Feb 03 19:15:30 2011 -0800
> > ++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Dasher.java Tue
> > Feb 08 09:22:49 2011 -0500
> > +@@ -38,7 +38,7 @@
> > + * semantics are unclear.
> > + *
> > + */
> > +-public class Dasher implements sun.awt.geom.PathConsumer2D {
> > ++final class Dasher implements sun.awt.geom.PathConsumer2D {
> > +
> > + private final PathConsumer2D out;
> > + private final float[] dash;
> > +@@ -169,7 +169,7 @@
> > + float dx = x1 - x0;
> > + float dy = y1 - y0;
> > +
> > +- float len = (float) Math.hypot(dx, dy);
> > ++ float len = (float) Math.sqrt(dx*dx + dy*dy);
> > +
> > + if (len == 0) {
> > + return;
> > +@@ -226,7 +226,7 @@
> > + return;
> > + }
> > + if (li == null) {
> > +- li = new LengthIterator(4, 0.0001f);
> > ++ li = new LengthIterator(4, 0.01f);
> > + }
> > + li.initializeIterationOnCurve(curCurvepts, type);
> > +
> > +@@ -237,9 +237,9 @@
> > + while ((t = li.next(leftInThisDashSegment)) < 1) {
> > + if (t != 0) {
> > + Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT),
> > +- curCurvepts, curCurveoff,
> > +- curCurvepts, 0,
> > +- curCurvepts, type, type);
> > ++ curCurvepts, curCurveoff,
> > ++ curCurvepts, 0,
> > ++ curCurvepts, type, type);
> > + lastSplitT = t;
> > + goTo(curCurvepts, 2, type);
> > + curCurveoff = type;
> > +@@ -307,6 +307,11 @@
> > + private int recLevel;
> > + private boolean done;
> > +
> > ++ // the lengths of the lines of the control polygon. Only its
> > first
> > ++ // curveType/2 - 1 elements are valid. This is an optimization.
> > See
> > ++ // next(float) for more detail.
> > ++ private float[] curLeafCtrlPolyLengths = new float[3];
> > ++
> > + public LengthIterator(int reclimit, float err) {
> > + this.limit = reclimit;
> > + this.minTincrement = 1f / (1 << limit);
> > +@@ -344,11 +349,52 @@
> > + this.lastSegLen = 0;
> > + }
> > +
> > ++ // 0 == false, 1 == true, -1 == invalid cached value.
> > ++ private int cachedHaveLowAcceleration = -1;
> > ++
> > ++ private boolean haveLowAcceleration(float err) {
> > ++ if (cachedHaveLowAcceleration == -1) {
> > ++ final float len1 = curLeafCtrlPolyLengths[0];
> > ++ final float len2 = curLeafCtrlPolyLengths[1];
> > ++ // the test below is equivalent to !within(len1/len2, 1, err).
> > ++ // It is using a multiplication instead of a division, so it
> > ++ // should be a bit faster.
> > ++ if (!Helpers.within(len1, len2, err*len2)) {
> > ++ cachedHaveLowAcceleration = 0;
> > ++ return false;
> > ++ }
> > ++ if (curveType == 8) {
> > ++ final float len3 = curLeafCtrlPolyLengths[2];
> > ++ // if len1 is close to 2 and 2 is close to 3, that probably
> > ++ // means 1 is close to 3 so the second part of this test might
> > ++ // not be needed, but it doesn't hurt to include it.
> > ++ if (!(Helpers.within(len2, len3, err*len3) &&
> > ++ Helpers.within(len1, len3, err*len3))) {
> > ++ cachedHaveLowAcceleration = 0;
> > ++ return false;
> > ++ }
> > ++ }
> > ++ cachedHaveLowAcceleration = 1;
> > ++ return true;
> > ++ }
> > ++
> > ++ return (cachedHaveLowAcceleration == 1);
> > ++ }
> > ++
> > ++ // we want to avoid allocations/gc so we keep this array so we
> > ++ // can put roots in it,
> > ++ private float[] nextRoots = new float[4];
> > ++
> > ++ // caches the coefficients of the current leaf in its flattened
> > ++ // form (see inside next() for what that means). The cache is
> > ++ // invalid when it's third element is negative, since in any
> > ++ // valid flattened curve, this would be >= 0.
> > ++ private float[] flatLeafCoefCache = new float[] {0, 0, -1, 0};
> > + // returns the t value where the remaining curve should be split
> > in
> > + // order for the left subdivided curve to have length len. If len
> > + // is >= than the length of the uniterated curve, it returns 1.
> > +- public float next(float len) {
> > +- float targetLength = lenAtLastSplit + len;
> > ++ public float next(final float len) {
> > ++ final float targetLength = lenAtLastSplit + len;
> > + while(lenAtNextT < targetLength) {
> > + if (done) {
> > + lastSegLen = lenAtNextT - lenAtLastSplit;
> > +@@ -357,8 +403,46 @@
> > + goToNextLeaf();
> > + }
> > + lenAtLastSplit = targetLength;
> > +- float t = binSearchForLen(lenAtLastSplit - lenAtLastT,
> > +- recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT,
> > ERR);
> > ++ final float leaflen = lenAtNextT - lenAtLastT;
> > ++ float t = (targetLength - lenAtLastT) / leaflen;
> > ++
> > ++ // cubicRootsInAB is a fairly expensive call, so we just don't do
> > it
> > ++ // if the acceleration in this section of the curve is small
> > enough.
> > ++ if (!haveLowAcceleration(0.05f)) {
> > ++ // We flatten the current leaf along the x axis, so that we're
> > ++ // left with a, b, c which define a 1D Bezier curve. We then
> > ++ // solve this to get the parameter of the original leaf that
> > ++ // gives us the desired length.
> > ++
> > ++ if (flatLeafCoefCache[2] < 0) {
> > ++ float x = 0+curLeafCtrlPolyLengths[0],
> > ++ y = x+curLeafCtrlPolyLengths[1];
> > ++ if (curveType == 8) {
> > ++ float z = y + curLeafCtrlPolyLengths[2];
> > ++ flatLeafCoefCache[0] = 3*(x - y) + z;
> > ++ flatLeafCoefCache[1] = 3*(y - 2*x);
> > ++ flatLeafCoefCache[2] = 3*x;
> > ++ flatLeafCoefCache[3] = -z;
> > ++ } else if (curveType == 6) {
> > ++ flatLeafCoefCache[0] = 0f;
> > ++ flatLeafCoefCache[1] = y - 2*x;
> > ++ flatLeafCoefCache[2] = 2*x;
> > ++ flatLeafCoefCache[3] = -y;
> > ++ }
> > ++ }
> > ++ float a = flatLeafCoefCache[0];
> > ++ float b = flatLeafCoefCache[1];
> > ++ float c = flatLeafCoefCache[2];
> > ++ float d = t*flatLeafCoefCache[3];
> > ++
> > ++ // we use cubicRootsInAB here, because we want only roots in 0,
> > 1,
> > ++ // and our quadratic root finder doesn't filter, so it's just a
> > ++ // matter of convenience.
> > ++ int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
> > ++ if (n == 1 && !Float.isNaN(nextRoots[0])) {
> > ++ t = nextRoots[0];
> > ++ }
> > ++ }
> > + // t is relative to the current leaf, so we must make it a valid
> > parameter
> > + // of the original curve.
> > + t = t * (nextT - lastT) + lastT;
> > +@@ -379,36 +463,6 @@
> > + return lastSegLen;
> > + }
> > +
> > +- // Returns t such that if leaf is subdivided at t the left
> > +- // curve will have length len. leafLen must be the length of
> > leaf.
> > +- private static Curve bsc = new Curve();
> > +- private static float binSearchForLen(float len, float[] leaf, int
> > type,
> > +- float leafLen, float err)
> > +- {
> > +- assert len <= leafLen;
> > +- bsc.set(leaf, type);
> > +- float errBound = err*len;
> > +- float left = 0, right = 1;
> > +- while (left < right) {
> > +- float m = (left + right) / 2;
> > +- if (m == left || m == right) {
> > +- return m;
> > +- }
> > +- float x = bsc.xat(m);
> > +- float y = bsc.yat(m);
> > +- float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y);
> > +- if (Math.abs(leftLen - len) < errBound) {
> > +- return m;
> > +- }
> > +- if (leftLen < len) {
> > +- left = m;
> > +- } else {
> > +- right = m;
> > +- }
> > +- }
> > +- return left;
> > +- }
> > +-
> > + // go to the next leaf (in an inorder traversal) in the recursion
> > tree
> > + // preconditions: must be on a leaf, and that leaf must not be the
> > root.
> > + private void goToNextLeaf() {
> > +@@ -437,6 +491,9 @@
> > + lenAtLastT = lenAtNextT;
> > + nextT += (1 << (limit - recLevel)) * minTincrement;
> > + lenAtNextT += len;
> > ++ // invalidate caches
> > ++ flatLeafCoefCache[2] = -1;
> > ++ cachedHaveLowAcceleration = -1;
> > + } else {
> > + Helpers.subdivide(recCurveStack[recLevel], 0,
> > + recCurveStack[recLevel+1], 0,
> > +@@ -450,11 +507,24 @@
> > + // this is a bit of a hack. It returns -1 if we're not on a leaf,
> > and
> > + // the length of the leaf if we are on a leaf.
> > + private float onLeaf() {
> > +- float polylen = Helpers.polyLineLength(recCurveStack[recLevel],
> > 0, curveType);
> > +- float linelen = Helpers.linelen(recCurveStack[recLevel][0],
> > recCurveStack[recLevel][1],
> > +- recCurveStack[recLevel][curveType - 2],
> > recCurveStack[recLevel][curveType - 1]);
> > +- return (polylen - linelen < ERR || recLevel == limit) ?
> > +- (polylen + linelen)/2 : -1;
> > ++ float[] curve = recCurveStack[recLevel];
> > ++ float polyLen = 0;
> > ++
> > ++ float x0 = curve[0], y0 = curve[1];
> > ++ for (int i = 2; i < curveType; i += 2) {
> > ++ final float x1 = curve[i], y1 = curve[i+1];
> > ++ final float len = Helpers.linelen(x0, y0, x1, y1);
> > ++ polyLen += len;
> > ++ curLeafCtrlPolyLengths[i/2 - 1] = len;
> > ++ x0 = x1;
> > ++ y0 = y1;
> > ++ }
> > ++
> > ++ final float lineLen = Helpers.linelen(curve[0], curve[1],
> > curve[curveType-2], curve[curveType-1]);
> > ++ if (polyLen - lineLen < ERR || recLevel == limit) {
> > ++ return (polyLen + lineLen)/2;
> > ++ }
> > ++ return -1;
> > + }
> > + }
> > +
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/Helpers.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Helpers.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Helpers.java
> > Tue Feb 08 09:22:49 2011 -0500
> > +@@ -26,6 +26,12 @@
> > + package sun.java2d.pisces;
> > +
> > + import java.util.Arrays;
> > ++import static java.lang.Math.PI;
> > ++import static java.lang.Math.cos;
> > ++import static java.lang.Math.sqrt;
> > ++import static java.lang.Math.cbrt;
> > ++import static java.lang.Math.acos;
> > ++
> > +
> > + final class Helpers {
> > + private Helpers() {
> > +@@ -75,100 +81,74 @@
> > + return ret - off;
> > + }
> > +
> > +- // find the roots of g(t) = a*t^3 + b*t^2 + c*t + d in [A,B)
> > +- // We will not use Cardano's method, since it is complicated and
> > +- // involves too many square and cubic roots. We will use Newton's
> > method.
> > +- // TODO: this should probably return ALL roots. Then the user can
> > do
> > +- // his own filtering of roots outside [A,B).
> > +- static int cubicRootsInAB(final float a, final float b,
> > +- final float c, final float d,
> > +- float[] pts, final int off, final float E,
> > ++ // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
> > ++ static int cubicRootsInAB(float d, float a, float b, float c,
> > ++ float[] pts, final int off,
> > + final float A, final float B)
> > + {
> > +- if (a == 0) {
> > +- return quadraticRoots(b, c, d, pts, off);
> > ++ if (d == 0) {
> > ++ int num = quadraticRoots(a, b, c, pts, off);
> > ++ return filterOutNotInAB(pts, off, num, A, B) - off;
> > + }
> > +- // the coefficients of g'(t). no dc variable because dc=c
> > +- // we use these to get the critical points of g(t), which
> > +- // we then use to chose starting points for Newton's method.
> > These
> > +- // should be very close to the actual roots.
> > +- final float da = 3 * a;
> > +- final float db = 2 * b;
> > +- int numCritPts = quadraticRoots(da, db, c, pts, off+1);
> > +- numCritPts = filterOutNotInAB(pts, off+1, numCritPts, A, B) - off
> > - 1;
> > +- // need them sorted.
> > +- if (numCritPts == 2 && pts[off+1] > pts[off+2]) {
> > +- float tmp = pts[off+1];
> > +- pts[off+1] = pts[off+2];
> > +- pts[off+2] = tmp;
> > ++ // From Graphics Gems:
> > ++ // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
> > ++ // (also from awt.geom.CubicCurve2D. But here we don't need as
> > ++ // much accuracy and we don't want to create arrays so we use
> > ++ // our own customized version).
> > ++
> > ++ /* normal form: x^3 + ax^2 + bx + c = 0 */
> > ++ a /= d;
> > ++ b /= d;
> > ++ c /= d;
> > ++
> > ++ // substitute x = y - A/3 to eliminate quadratic term:
> > ++ // x^3 +Px + Q = 0
> > ++ //
> > ++ // Since we actually need P/3 and Q/2 for all of the
> > ++ // calculations that follow, we will calculate
> > ++ // p = P/3
> > ++ // q = Q/2
> > ++ // instead and use those values for simplicity of the code.
> > ++ double sq_A = a * a;
> > ++ double p = 1.0/3 * (-1.0/3 * sq_A + b);
> > ++ double q = 1.0/2 * (2.0/27 * a * sq_A - 1.0/3 * a * b + c);
> > ++
> > ++ /* use Cardano's formula */
> > ++
> > ++ double cb_p = p * p * p;
> > ++ double D = q * q + cb_p;
> > ++
> > ++ int num;
> > ++ if (D < 0) {
> > ++ // see:
> > http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
> > ++ final double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
> > ++ final double t = 2 * sqrt(-p);
> > ++
> > ++ pts[ off+0 ] = (float)( t * cos(phi));
> > ++ pts[ off+1 ] = (float)(-t * cos(phi + PI / 3));
> > ++ pts[ off+2 ] = (float)(-t * cos(phi - PI / 3));
> > ++ num = 3;
> > ++ } else {
> > ++ final double sqrt_D = sqrt(D);
> > ++ final double u = cbrt(sqrt_D - q);
> > ++ final double v = - cbrt(sqrt_D + q);
> > ++
> > ++ pts[ off ] = (float)(u + v);
> > ++ num = 1;
> > ++
> > ++ if (within(D, 0, 1e-8)) {
> > ++ pts[off+1] = -(pts[off] / 2);
> > ++ num = 2;
> > ++ }
> > + }
> > +
> > +- int ret = off;
> > ++ final float sub = 1.0f/3 * a;
> > +
> > +- // we don't actually care much about the extrema themselves. We
> > +- // only use them to ensure that g(t) is monotonic in each
> > +- // interval [pts[i],pts[i+1] (for i in off...off+numCritPts+1).
> > +- // This will allow us to determine intervals containing exactly
> > +- // one root.
> > +- // The end points of the interval are always local extrema.
> > +- pts[off] = A;
> > +- pts[off + numCritPts + 1] = B;
> > +- numCritPts += 2;
> > ++ for (int i = 0; i < num; ++i) {
> > ++ pts[ off+i ] -= sub;
> > ++ }
> > +
> > +- float x0 = pts[off], fx0 = evalCubic(a, b, c, d, x0);
> > +- for (int i = off; i < off + numCritPts - 1; i++) {
> > +- float x1 = pts[i+1], fx1 = evalCubic(a, b, c, d, x1);
> > +- if (fx0 == 0f) {
> > +- pts[ret++] = x0;
> > +- } else if (fx1 * fx0 < 0f) { // have opposite signs
> > +- pts[ret++] = CubicNewton(a, b, c, d,
> > +- x0 + fx0 * (x1 - x0) / (fx0 - fx1), E);
> > +- }
> > +- x0 = x1;
> > +- fx0 = fx1;
> > +- }
> > +- return ret - off;
> > +- }
> > +-
> > +- // precondition: the polynomial to be evaluated must not be 0 at
> > x0.
> > +- static float CubicNewton(final float a, final float b,
> > +- final float c, final float d,
> > +- float x0, final float err)
> > +- {
> > +- // considering how this function is used, 10 should be more than
> > enough
> > +- final int itlimit = 10;
> > +- float fx0 = evalCubic(a, b, c, d, x0);
> > +- float x1;
> > +- int count = 0;
> > +- while(true) {
> > +- x1 = x0 - (fx0 / evalCubic(0, 3 * a, 2 * b, c, x0));
> > +- if (Math.abs(x1 - x0) < err * Math.abs(x1 + x0) || count ==
> > itlimit) {
> > +- break;
> > +- }
> > +- x0 = x1;
> > +- fx0 = evalCubic(a, b, c, d, x0);
> > +- count++;
> > +- }
> > +- return x1;
> > +- }
> > +-
> > +- // fills the input array with numbers 0, INC, 2*INC, ...
> > +- static void fillWithIdxes(final float[] data, final int[] idxes)
> > {
> > +- if (idxes.length > 0) {
> > +- idxes[0] = 0;
> > +- for (int i = 1; i < idxes.length; i++) {
> > +- idxes[i] = idxes[i-1] + (int)data[idxes[i-1]];
> > +- }
> > +- }
> > +- }
> > +-
> > +- static void fillWithIdxes(final int[] idxes, final int inc) {
> > +- if (idxes.length > 0) {
> > +- idxes[0] = 0;
> > +- for (int i = 1; i < idxes.length; i++) {
> > +- idxes[i] = idxes[i-1] + inc;
> > +- }
> > +- }
> > ++ return filterOutNotInAB(pts, off, num, A, B) - off;
> > + }
> > +
> > + // These use a hardcoded factor of 2 for increasing sizes. Perhaps
> > this
> > +@@ -182,6 +162,7 @@
> > + }
> > + return Arrays.copyOf(in, 2 * (cursize + numToAdd));
> > + }
> > ++
> > + static int[] widenArray(int[] in, final int cursize, final int
> > numToAdd) {
> > + if (in.length >= cursize + numToAdd) {
> > + return in;
> > +@@ -208,7 +189,7 @@
> > + {
> > + int ret = off;
> > + for (int i = off; i < off + len; i++) {
> > +- if (nums[i] > a && nums[i] < b) {
> > ++ if (nums[i] >= a && nums[i] < b) {
> > + nums[ret++] = nums[i];
> > + }
> > + }
> > +@@ -225,7 +206,9 @@
> > + }
> > +
> > + static float linelen(float x1, float y1, float x2, float y2) {
> > +- return (float)Math.hypot(x2 - x1, y2 - y1);
> > ++ final float dx = x2 - x1;
> > ++ final float dy = y2 - y1;
> > ++ return (float)Math.sqrt(dx*dx + dy*dy);
> > + }
> > +
> > + static void subdivide(float[] src, int srcoff, float[] left, int
> > leftoff,
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/PiscesCache.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesCache.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++
> > openjdk/jdk/src/share/classes/sun/java2d/pisces/PiscesCache.java Tue
> > Feb 08 09:22:49 2011 -0500
> > +@@ -32,7 +32,7 @@
> > + *
> > + * @see PiscesRenderer#render
> > + */
> > +-public final class PiscesCache {
> > ++final class PiscesCache {
> > +
> > + final int bboxX0, bboxY0, bboxX1, bboxY1;
> > +
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++
> > openjdk/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
> > Tue Feb 08 09:22:49 2011 -0500
> > +@@ -27,7 +27,6 @@
> > +
> > + import java.awt.Shape;
> > + import java.awt.BasicStroke;
> > +-import java.awt.geom.NoninvertibleTransformException;
> > + import java.awt.geom.Path2D;
> > + import java.awt.geom.AffineTransform;
> > + import java.awt.geom.PathIterator;
> > +@@ -250,7 +249,7 @@
> > + float dashphase,
> > + PathConsumer2D pc2d)
> > + {
> > +- // We use inat and outat so that in Stroker and Dasher we can
> > work only
> > ++ // We use strokerat and outat so that in Stroker and Dasher we
> > can work only
> > + // with the pre-transformation coordinates. This will repeat a lot
> > of
> > + // computations done in the path iterator, but the alternative is
> > to
> > + // work with transformed paths and compute untransformed
> > coordinates
> > +@@ -265,7 +264,7 @@
> > + // transformation after the path processing has been done.
> > + // We can't do this if normalization is on, because it isn't a
> > good
> > + // idea to normalize before the transformation is applied.
> > +- AffineTransform inat = null;
> > ++ AffineTransform strokerat = null;
> > + AffineTransform outat = null;
> > +
> > + PathIterator pi = null;
> > +@@ -284,9 +283,9 @@
> > + // again so, nothing can be drawn.
> > +
> > + // Every path needs an initial moveTo and a pathDone. If these
> > +- // aren't there this causes a SIGSEV in libawt.so (at the time
> > ++ // are not there this causes a SIGSEGV in libawt.so (at the time
> > + // of writing of this comment (September 16, 2010)). Actually,
> > +- // I'm not sure if the moveTo is necessary to avoid the SIGSEV
> > ++ // I am not sure if the moveTo is necessary to avoid the SIGSEGV
> > + // but the pathDone is definitely needed.
> > + pc2d.moveTo(0, 0);
> > + pc2d.pathDone();
> > +@@ -313,25 +312,32 @@
> > + if (normalize != NormMode.OFF) {
> > + pi = new NormalizingPathIterator(pi, normalize);
> > + }
> > +- // leave inat and outat null.
> > ++ // by now strokerat == null && outat == null. Input paths to
> > ++ // stroker (and maybe dasher) will have the full transform at
> > ++ // applied to them and nothing will happen to the output paths.
> > + } else {
> > +- // We only need the inverse if normalization is on. Otherwise
> > +- // we just don't transform the input paths, do all the stroking
> > +- // and then transform out output (instead of making PathIterator
> > +- // apply the transformation, us applying the inverse, and then
> > +- // us applying the transform again to our output).
> > +- outat = at;
> > + if (normalize != NormMode.OFF) {
> > +- try {
> > +- inat = outat.createInverse();
> > +- } catch (NoninvertibleTransformException e) {
> > +- // we made sure this can't happen
> > +- e.printStackTrace();
> > +- }
> > ++ strokerat = at;
> > + pi = src.getPathIterator(at);
> > + pi = new NormalizingPathIterator(pi, normalize);
> > ++ // by now strokerat == at && outat == null. Input paths to
> > ++ // stroker (and maybe dasher) will have the full transform at
> > ++ // applied to them, then they will be normalized, and then
> > ++ // the inverse of *only the non translation part of at* will
> > ++ // be applied to the normalized paths. This won't cause problems
> > ++ // in stroker, because, suppose at = T*A, where T is just the
> > ++ // translation part of at, and A is the rest. T*A has already
> > ++ // been applied to Stroker/Dasher's input. Then Ainv will be
> > ++ // applied. Ainv*T*A is not equal to T, but it is a translation,
> > ++ // which means that none of stroker's assumptions about its
> > ++ // input will be violated. After all this, A will be applied
> > ++ // to stroker's output.
> > + } else {
> > ++ outat = at;
> > + pi = src.getPathIterator(null);
> > ++ // outat == at && strokerat == null. This is because if no
> > ++ // normalization is done, we can just apply all our
> > ++ // transformations to stroker's output.
> > + }
> > + }
> > + } else {
> > +@@ -343,13 +349,17 @@
> > + }
> > + }
> > +
> > ++ // by now, at least one of outat and strokerat will be null.
> > Unless at is not
> > ++ // a constant multiple of an orthogonal transformation, they will
> > both be
> > ++ // null. In other cases, outat == at if normalization is off, and
> > if
> > ++ // normalization is on, strokerat == at.
> > + pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
> > ++ pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d,
> > strokerat);
> > + pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
> > + if (dashes != null) {
> > + pc2d = new Dasher(pc2d, dashes, dashphase);
> > + }
> > +- pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat);
> > +-
> > ++ pc2d =
> > TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d,
> > strokerat);
> > + pathTo(pi, pc2d);
> > + }
> > +
> > +@@ -588,9 +598,9 @@
> > + }
> > +
> > + Renderer r = new Renderer(3, 3,
> > +- clip.getLoX(), clip.getLoY(),
> > +- clip.getWidth(), clip.getHeight(),
> > +- PathIterator.WIND_EVEN_ODD);
> > ++ clip.getLoX(), clip.getLoY(),
> > ++ clip.getWidth(), clip.getHeight(),
> > ++ PathIterator.WIND_EVEN_ODD);
> > +
> > + r.moveTo((float) x, (float) y);
> > + r.lineTo((float) (x+dx1), (float) (y+dy1));
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++
> > openjdk/jdk/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java
> > Tue Feb 08 09:22:49 2011 -0500
> > +@@ -30,7 +30,7 @@
> > +
> > + import sun.java2d.pipe.AATileGenerator;
> > +
> > +-public final class PiscesTileGenerator implements AATileGenerator
> > {
> > ++final class PiscesTileGenerator implements AATileGenerator {
> > + public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
> > +
> > + // perhaps we should be using weak references here, but right now
> > +diff -r 21621a756b32 -r 5e624003e622
> > src/share/classes/sun/java2d/pisces/Renderer.java
> > +---
> > openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/Renderer.java
> > Thu Feb 03 19:15:30 2011 -0800
> > ++++ openjdk/jdk/src/share/classes/sun/java2d/pisces/Renderer.java
> > Tue Feb 08 09:22:49 2011 -0500
> > +@@ -25,12 +25,9 @@
> > +
> > + package sun.java2d.pisces;
> > +
> > +-import java.util.Arrays;
> > +-import java.util.Iterator;
> > +-
> > + import sun.awt.geom.PathConsumer2D;
> > +
> > +-public class Renderer implements PathConsumer2D {
> > ++final class Renderer implements PathConsumer2D {
> > +
> > + private class ScanlineIterator {
> > +
> > +@@ -39,115 +36,81 @@
> > + // crossing bounds. The bounds are not necessarily tight (the scan
> > line
> > + // at minY, for example, might have no crossings). The x bounds
> > will
> > + // be accumulated as crossings are computed.
> > +- private int minY, maxY;
> > ++ private final int maxY;
> > + private int nextY;
> > +
> > + // indices into the segment pointer lists. They indicate the
> > "active"
> > + // sublist in the segment lists (the portion of the list that
> > contains
> > + // all the segments that cross the next scan line).
> > +- private int elo, ehi;
> > +- private final int[] edgePtrs;
> > +- private int qlo, qhi;
> > +- private final int[] quadPtrs;
> > +- private int clo, chi;
> > +- private final int[] curvePtrs;
> > ++ private int edgeCount;
> > ++ private int[] edgePtrs;
> > +
> > + private static final int INIT_CROSSINGS_SIZE = 10;
> > +
> > + private ScanlineIterator() {
> > + crossings = new int[INIT_CROSSINGS_SIZE];
> > +-
> > +- edgePtrs = new int[numEdges];
> > +- Helpers.fillWithIdxes(edgePtrs, SIZEOF_EDGE);
> > +- qsort(edges, edgePtrs, YMIN, 0, numEdges - 1);
> > +-
> > +- quadPtrs = new int[numQuads];
> > +- Helpers.fillWithIdxes(quadPtrs, SIZEOF_QUAD);
> > +- qsort(quads, quadPtrs, YMIN, 0, numQuads - 1);
> > +-
> > +- curvePtrs = new int[numCurves];
> > +- Helpers.fillWithIdxes(curvePtrs, SIZEOF_CURVE);
> > +- qsort(curves, curvePtrs, YMIN, 0, numCurves - 1);
> > ++ edgePtrs = new int[INIT_CROSSINGS_SIZE];
> > +
> > + // We don't care if we clip some of the line off with ceil, since
> > + // no scan line crossings will be eliminated (in fact, the ceil is
> > + // the y of the first scan line crossing).
> > +- nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
> > +- maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
> > +-
> > +- for (elo = 0; elo < numEdges && edges[edgePtrs[elo]+YMAX] <=
> > minY; elo++)
> > +- ;
> > +- // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi]
> > (exclusive)
> > +- for (ehi = elo; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <=
> > minY; ehi++)
> > +- edgeSetCurY(edgePtrs[ehi], minY);// TODO: make minY a float to
> > avoid casts
> > +-
> > +- for (qlo = 0; qlo < numQuads && quads[quadPtrs[qlo]+YMAX] <=
> > minY; qlo++)
> > +- ;
> > +- for (qhi = qlo; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <=
> > minY; qhi++)
> > +- quadSetCurY(quadPtrs[qhi], minY);
> > +-
> > +- for (clo = 0; clo < numCurves && curves[curvePtrs[clo]+YMAX] <=
> > minY; clo++)
> > +- ;
> > +- for (chi = clo; chi < numCurves && curves[curvePtrs[chi]+YMIN] <=
> > minY; chi++)
> > +- curveSetCurY(curvePtrs[chi], minY);
> > ++ final int minY = getFirstScanLineCrossing();
> > ++ nextY = minY;
> > ++ maxY = getScanLineCrossingEnd()-1;
> > ++ edgeCount = 0;
> > + }
> > +
> > + private int next() {
> > +- // we go through the active lists and remove segments that don't
> > cross
> > +- // the nextY scanline.
> > +- int crossingIdx = 0;
> > +- for (int i = elo; i < ehi; i++) {
> > +- if (edges[edgePtrs[i]+YMAX] <= nextY) {
> > +- edgePtrs[i] = edgePtrs[elo++];
> > ++ int cury = nextY++;
> > ++ int bucket = cury - boundsMinY;
> > ++ int count = this.edgeCount;
> > ++ int ptrs[] = this.edgePtrs;
> > ++ int bucketcount = edgeBucketCounts[bucket];
> > ++ if ((bucketcount & 0x1) != 0) {
> > ++ int newCount = 0;
> > ++ for (int i = 0; i < count; i++) {
> > ++ int ecur = ptrs[i];
> > ++ if (edges[ecur+YMAX] > cury) {
> > ++ ptrs[newCount++] = ecur;
> > ++ }
> > + }
> > ++ count = newCount;
> > + }
> > +- for (int i = qlo; i < qhi; i++) {
> > +- if (quads[quadPtrs[i]+YMAX] <= nextY) {
> > +- quadPtrs[i] = quadPtrs[qlo++];
> > ++ ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1);
> > ++ for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur =
> > (int)edges[ecur+NEXT]) {
> > ++ ptrs[count++] = ecur;
> > ++ // REMIND: Adjust start Y if necessary
> > ++ }
> > ++ this.edgePtrs = ptrs;
> > ++ this.edgeCount = count;
> > ++// if ((count & 0x1) != 0) {
> > ++// System.out.println("ODD NUMBER OF EDGES!!!!");
> > ++// }
> > ++ int xings[] = this.crossings;
> > ++ if (xings.length < count) {
> > ++ this.crossings = xings = new int[ptrs.length];
> > ++ }
> > ++ for (int i = 0; i < count; i++) {
> > ++ int ecur = ptrs[i];
> > ++ float curx = edges[ecur+CURX];
> > ++ int cross = ((int) curx) << 1;
> > ++ edges[ecur+CURX] = curx + edges[ecur+SLOPE];
> > ++ if (edges[ecur+OR] > 0) {
> > ++ cross |= 1;
> > + }
> > ++ int j = i;
> > ++ while (--j >= 0) {
> > ++ int jcross = xings[j];
> > ++ if (jcross <= cross) {
> > ++ break;
> > ++ }
> > ++ xings[j+1] = jcross;
> > ++ ptrs[j+1] = ptrs[j];
> > ++ }
> > ++ xings[j+1] = cross;
> > ++ ptrs[j+1] = ecur;
> > + }
> > +- for (int i = clo; i < chi; i++) {
> > +- if (curves[curvePtrs[i]+YMAX] <= nextY) {
> > +- curvePtrs[i] = curvePtrs[clo++];
> > +- }
> > +- }
> > +-
> > +- crossings = Helpers.widenArray(crossings, 0,
> > ehi-elo+qhi-qlo+chi-clo);
> > +-
> > +- // Now every edge between lo and hi crosses nextY. Compute it's
> > +- // crossing and put it in the crossings array.
> > +- for (int i = elo; i < ehi; i++) {
> > +- int ptr = edgePtrs[i];
> > +- addCrossing(nextY, (int)edges[ptr+CURX], edges[ptr+OR],
> > crossingIdx);
> > +- edgeGoToNextY(ptr);
> > +- crossingIdx++;
> > +- }
> > +- for (int i = qlo; i < qhi; i++) {
> > +- int ptr = quadPtrs[i];
> > +- addCrossing(nextY, (int)quads[ptr+CURX], quads[ptr+OR],
> > crossingIdx);
> > +- quadGoToNextY(ptr);
> > +- crossingIdx++;
> > +- }
> > +- for (int i = clo; i < chi; i++) {
> > +- int ptr = curvePtrs[i];
> > +- addCrossing(nextY, (int)curves[ptr+CURX], curves[ptr+OR],
> > crossingIdx);
> > +- curveGoToNextY(ptr);
> > +- crossingIdx++;
> > +- }
> > +-
> > +- nextY++;
> > +- // Expand active lists to include new edges.
> > +- for (; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= nextY;
> > ehi++) {
> > +- edgeSetCurY(edgePtrs[ehi], nextY);
> > +- }
> > +- for (; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= nextY;
> > qhi++) {
> > +- quadSetCurY(quadPtrs[qhi], nextY);
> > +- }
> > +- for (; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= nextY;
> > chi++) {
> > +- curveSetCurY(curvePtrs[chi], nextY);
> > +- }
> > +- Arrays.sort(crossings, 0, crossingIdx);
> > +- return crossingIdx;
> > ++ return count;
> > + }
> > +
> > + private boolean hasNext() {
> > +@@ -157,51 +120,7 @@
> > + private int curY() {
> > + return nextY - 1;
> > + }
> > +-
> > +- private void addCrossing(int y, int x, float or, int idx) {
> > +- x <<= 1;
> > +- crossings[idx] = ((or > 0) ? (x | 0x1) : x);
> > +- }
> > + }
> > +- // quicksort implementation for sorting the edge indices
> > ("pointers")
> > +- // by increasing y0. first, last are indices into the "pointer"
> > array
> > +- // It sorts the pointer array from first (inclusive) to last
> > (inclusive)
> > +- private static void qsort(final float[] data, final int[] ptrs,
> > +- final int fieldForCmp, int first, int last)
> > +- {
> > +- if (last > first) {
> > +- int p = partition(data, ptrs, fieldForCmp, first, last);
> > +- if (first < p - 1) {
> > +- qsort(data, ptrs, fieldForCmp, first, p - 1);
> > +- }
> > +- if (p < last) {
> > +- qsort(data, ptrs, fieldForCmp, p, last);
> > +- }
> > +- }
> > +- }
> > +-
> > +- // i, j are indices into edgePtrs.
> > +- private static int partition(final float[] data, final int[]
> > ptrs,
> > +- final int fieldForCmp, int i, int j)
> > +- {
> > +- int pivotValFieldForCmp = ptrs[i]+fieldForCmp;
> > +- while (i <= j) {
> > +- // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C
> > +- while (data[ptrs[i]+fieldForCmp] < data[pivotValFieldForCmp])
> > +- i++;
> > +- while (data[ptrs[j]+fieldForCmp] > data[pivotValFieldForCmp])
> > +- j--;
> > +- if (i <= j) {
> > +- int tmp = ptrs[i];
> > +- ptrs[i] = ptrs[j];
> > +- ptrs[j] = tmp;
> > +- i++;
> > +- j--;
> > +- }
> > +- }
> > +- return i;
> > +- }
> > +-//============================================================================
> > +
> > +
> > +
> > //////////////////////////////////////////////////////////////////////////////
> > +@@ -209,269 +128,89 @@
> > +
> > //////////////////////////////////////////////////////////////////////////////
> > + // TODO(maybe): very tempting to use fixed point here. A lot of
> > opportunities
> > + // for shifts and just removing certain operations altogether.
> > +-// TODO: it might be worth it to make an EdgeList class. It would
> > probably
> > +-// clean things up a bit and not impact performance much.
> > +
> > + // common to all types of input path segments.
> > +- private static final int YMIN = 0;
> > +- private static final int YMAX = 1;
> > +- private static final int CURX = 2;
> > +- // this and OR are meant to be indeces into "int" fields, but
> > arrays must
> > ++ private static final int YMAX = 0;
> > ++ private static final int CURX = 1;
> > ++ // NEXT and OR are meant to be indices into "int" fields, but
> > arrays must
> > + // be homogenous, so every field is a float. However floats can
> > represent
> > + // exactly up to 26 bit ints, so we're ok.
> > +- private static final int CURY = 3;
> > +- private static final int OR = 4;
> > +-
> > +- // for straight lines only:
> > +- private static final int SLOPE = 5;
> > +-
> > +- // for quads and cubics:
> > +- private static final int X0 = 5;
> > +- private static final int Y0 = 6;
> > +- private static final int XL = 7;
> > +- private static final int COUNT = 8;
> > +- private static final int CURSLOPE = 9;
> > +- private static final int DX = 10;
> > +- private static final int DY = 11;
> > +- private static final int DDX = 12;
> > +- private static final int DDY = 13;
> > +-
> > +- // for cubics only
> > +- private static final int DDDX = 14;
> > +- private static final int DDDY = 15;
> > ++ private static final int OR = 2;
> > ++ private static final int SLOPE = 3;
> > ++ private static final int NEXT = 4;
> > +
> > + private float edgeMinY = Float.POSITIVE_INFINITY;
> > + private float edgeMaxY = Float.NEGATIVE_INFINITY;
> > + private float edgeMinX = Float.POSITIVE_INFINITY;
> > + private float edgeMaxX = Float.NEGATIVE_INFINITY;
> > +
> > +- private static final int SIZEOF_EDGE = 6;
> > ++ private static final int SIZEOF_EDGE = 5;
> > ++ // don't just set NULL to -1, because we want NULL+NEXT to be
> > negative.
> > ++ private static final int NULL = -SIZEOF_EDGE;
> > + private float[] edges = null;
> > ++ private int[] edgeBuckets = null;
> > ++ private int[] edgeBucketCounts = null; // 2*newedges + (1 if
> > pruning needed)
> > + private int numEdges;
> > +- // these are static because we need them to be usable from
> > ScanlineIterator
> > +- private void edgeSetCurY(final int idx, int y) {
> > +- edges[idx+CURX] += (y - edges[idx+CURY]) * edges[idx+SLOPE];
> > +- edges[idx+CURY] = y;
> > +- }
> > +- private void edgeGoToNextY(final int idx) {
> > +- edges[idx+CURY] += 1;
> > +- edges[idx+CURX] += edges[idx+SLOPE];
> > +- }
> > +-
> > +-
> > +- private static final int SIZEOF_QUAD = 14;
> > +- private float[] quads = null;
> > +- private int numQuads;
> > +- // This function should be called exactly once, to set the first
> > scanline
> > +- // of the curve. Before it is called, the curve should think its
> > first
> > +- // scanline is CEIL(YMIN).
> > +- private void quadSetCurY(final int idx, final int y) {
> > +- assert y < quads[idx+YMAX];
> > +- assert (quads[idx+CURY] > y);
> > +- assert (quads[idx+CURY] == Math.ceil(quads[idx+CURY]));
> > +-
> > +- while (quads[idx+CURY] < ((float)y)) {
> > +- quadGoToNextY(idx);
> > +- }
> > +- }
> > +- private void quadGoToNextY(final int idx) {
> > +- quads[idx+CURY] += 1;
> > +- // this will get overriden if the while executes.
> > +- quads[idx+CURX] += quads[idx+CURSLOPE];
> > +- int count = (int)quads[idx+COUNT];
> > +- // this loop should never execute more than once because our
> > +- // curve is monotonic in Y. Still we put it in because you can
> > +- // never be too sure when dealing with floating point.
> > +- while(quads[idx+CURY] >= quads[idx+Y0] && count > 0) {
> > +- float x0 = quads[idx+X0], y0 = quads[idx+Y0];
> > +- count = executeQuadAFDIteration(idx);
> > +- float x1 = quads[idx+X0], y1 = quads[idx+Y0];
> > +- // our quads are monotonic, so this shouldn't happen, but
> > +- // it is conceivable that for very flat quads with different
> > +- // y values at their endpoints AFD might give us a horizontal
> > +- // segment.
> > +- if (y1 == y0) {
> > +- continue;
> > +- }
> > +- quads[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
> > +- quads[idx+CURX] = x0 + (quads[idx+CURY] - y0) *
> > quads[idx+CURSLOPE];
> > +- }
> > +- }
> > +-
> > +-
> > +- private static final int SIZEOF_CURVE = 16;
> > +- private float[] curves = null;
> > +- private int numCurves;
> > +- private void curveSetCurY(final int idx, final int y) {
> > +- assert y < curves[idx+YMAX];
> > +- assert (curves[idx+CURY] > y);
> > +- assert (curves[idx+CURY] == Math.ceil(curves[idx+CURY]));
> > +-
> > +- while (curves[idx+CURY] < ((float)y)) {
> > +- curveGoToNextY(idx);
> > +- }
> > +- }
> > +- private void curveGoToNextY(final int idx) {
> > +- curves[idx+CURY] += 1;
> > +- // this will get overriden if the while executes.
> > +- curves[idx+CURX] += curves[idx+CURSLOPE];
> > +- int count = (int)curves[idx+COUNT];
> > +- // this loop should never execute more than once because our
> > +- // curve is monotonic in Y. Still we put it in because you can
> > +- // never be too sure when dealing with floating point.
> > +- while(curves[idx+CURY] >= curves[idx+Y0] && count > 0) {
> > +- float x0 = curves[idx+X0], y0 = curves[idx+Y0];
> > +- count = executeCurveAFDIteration(idx);
> > +- float x1 = curves[idx+X0], y1 = curves[idx+Y0];
> > +- // our curves are monotonic, so this shouldn't happen, but
> > +- // it is conceivable that for very flat curves with different
> > +- // y values at their endpoints AFD might give us a horizontal
> > +- // segment.
> > +- if (y1 == y0) {
> > +- continue;
> > +- }
> > +- curves[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
> > +- curves[idx+CURX] = x0 + (curves[idx+CURY] - y0) *
> > curves[idx+CURSLOPE];
> > +- }
> > +- }
> > +-
> > +
> > + private static final float DEC_BND = 20f;
> > + private static final float INC_BND = 8f;
> > ++
> > ++ // each bucket is a linked list. this method adds eptr to the
> > ++ // start "bucket"th linked list.
> > ++ private void addEdgeToBucket(final int eptr, final int bucket) {
> > ++ edges[eptr+NEXT] = edgeBuckets[bucket];
> > ++ edgeBuckets[bucket] = eptr;
> > ++ edgeBucketCounts[bucket] += 2;
> > ++ }
> > ++
> > + // Flattens using adaptive forward differencing. This only carries
> > out
> > + // one iteration of the AFD loop. All it does is update AFD
> > variables (i.e.
> > + // X0, Y0, D*[X|Y], COUNT; not variables used for computing
> > scanline crossings).
> > +- private int executeQuadAFDIteration(int idx) {
> > +- int count = (int)quads[idx+COUNT];
> > +- float ddx = quads[idx+DDX];
> > +- float ddy = quads[idx+DDY];
> > +- float dx = quads[idx+DX];
> > +- float dy = quads[idx+DY];
> > +-
> > +- while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
> > +- ddx = ddx / 4;
> > +- ddy = ddy / 4;
> > +- dx = (dx - ddx) / 2;
> > +- dy = (dy - ddy) / 2;
> > ++ private void quadBreakIntoLinesAndAdd(float x0, float y0,
> > ++ final Curve c,
> > ++ final float x2, final float y2) {
> > ++ final float QUAD_DEC_BND = 32;
> > ++ final int countlg = 4;
> > ++ int count = 1 << countlg;
> > ++ int countsq = count * count;
> > ++ float maxDD = Math.max(c.dbx / countsq, c.dby / countsq);
> > ++ while (maxDD > QUAD_DEC_BND) {
> > ++ maxDD /= 4;
> > + count <<= 1;
> > + }
> > +- // can only do this on even "count" values, because we must
> > divide count by 2
> > +- while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy)
> > <= INC_BND) {
> > +- dx = 2 * dx + ddx;
> > +- dy = 2 * dy + ddy;
> > +- ddx = 4 * ddx;
> > +- ddy = 4 * ddy;
> > +- count >>= 1;
> > ++
> > ++ countsq = count * count;
> > ++ final float ddx = c.dbx / countsq;
> > ++ final float ddy = c.dby / countsq;
> > ++ float dx = c.bx / countsq + c.cx / count;
> > ++ float dy = c.by / countsq + c.cy / count;
> > ++
> > ++ while (count-- > 1) {
> > ++ float x1 = x0 + dx;
> > ++ dx += ddx;
> > ++ float y1 = y0 + dy;
> > ++ dy += ddy;
> > ++ addLine(x0, y0, x1, y1);
> > ++ x0 = x1;
> > ++ y0 = y1;
> > + }
> > +- count--;
> > +- if (count > 0) {
> > +- quads[idx+X0] += dx;
> > +- dx += ddx;
> > +- quads[idx+Y0] += dy;
> > +- dy += ddy;
> > +- } else {
> > +- quads[idx+X0] = quads[idx+XL];
> > +- quads[idx+Y0] = quads[idx+YMAX];
> > +- }
> > +- quads[idx+COUNT] = count;
> > +- quads[idx+DDX] = ddx;
> > +- quads[idx+DDY] = ddy;
> > +- quads[idx+DX] = dx;
> > +- quads[idx+DY] = dy;
> > +- return count;
> > +- }
> > +- private int executeCurveAFDIteration(int idx) {
> > +- int count = (int)curves[idx+COUNT];
> > +- float ddx = curves[idx+DDX];
> > +- float ddy = curves[idx+DDY];
> > +- float dx = curves[idx+DX];
> > +- float dy = curves[idx+DY];
> > +- float dddx = curves[idx+DDDX];
> > +- float dddy = curves[idx+DDDY];
> > +-
> > +- while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
> > +- dddx /= 8;
> > +- dddy /= 8;
> > +- ddx = ddx/4 - dddx;
> > +- ddy = ddy/4 - dddy;
> > +- dx = (dx - ddx) / 2;
> > +- dy = (dy - ddy) / 2;
> > +- count <<= 1;
> > +- }
> > +- // can only do this on even "count" values, because we must
> > divide count by 2
> > +- while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy)
> > <= INC_BND) {
> > +- dx = 2 * dx + ddx;
> > +- dy = 2 * dy + ddy;
> > +- ddx = 4 * (ddx + dddx);
> > +- ddy = 4 * (ddy + dddy);
> > +- dddx = 8 * dddx;
> > +- dddy = 8 * dddy;
> > +- count >>= 1;
> > +- }
> > +- count--;
> > +- if (count > 0) {
> > +- curves[idx+X0] += dx;
> > +- dx += ddx;
> > +- ddx += dddx;
> > +- curves[idx+Y0] += dy;
> > +- dy += ddy;
> > +- ddy += dddy;
> > +- } else {
> > +- curves[idx+X0] = curves[idx+XL];
> > +- curves[idx+Y0] = curves[idx+YMAX];
> > +- }
> > +- curves[idx+COUNT] = count;
> > +- curves[idx+DDDX] = dddx;
> > +- curves[idx+DDDY] = dddy;
> > +- curves[idx+DDX] = ddx;
> > +- curves[idx+DDY] = ddy;
> > +- curves[idx+DX] = dx;
> > +- curves[idx+DY] = dy;
> > +- return count;
> > ++ addLine(x0, y0, x2, y2);
> > + }
> > +
> > +-
> > +- private void initLine(final int idx, float[] pts, int or) {
> > +- edges[idx+SLOPE] = (pts[2] - pts[0]) / (pts[3] - pts[1]);
> > +- edges[idx+CURX] = pts[0] + (edges[idx+CURY] - pts[1]) *
> > edges[idx+SLOPE];
> > +- }
> > +-
> > +- private void initQuad(final int idx, float[] points, int or) {
> > ++ // x0, y0 and x3,y3 are the endpoints of the curve. We could
> > compute these
> > ++ // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might
> > introduce
> > ++ // numerical errors, and our callers already have the exact
> > values.
> > ++ // Another alternative would be to pass all the control points,
> > and call c.set
> > ++ // here, but then too many numbers are passed around.
> > ++ private void curveBreakIntoLinesAndAdd(float x0, float y0,
> > ++ final Curve c,
> > ++ final float x3, final float y3) {
> > + final int countlg = 3;
> > +- final int count = 1 << countlg;
> > ++ int count = 1 << countlg;
> > +
> > + // the dx and dy refer to forward differencing variables, not the
> > last
> > + // coefficients of the "points" polynomial
> > +- final float ddx, ddy, dx, dy;
> > +- c.set(points, 6);
> > +-
> > +- ddx = c.dbx / (1 << (2 * countlg));
> > +- ddy = c.dby / (1 << (2 * countlg));
> > +- dx = c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
> > +- dy = c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
> > +-
> > +- quads[idx+DDX] = ddx;
> > +- quads[idx+DDY] = ddy;
> > +- quads[idx+DX] = dx;
> > +- quads[idx+DY] = dy;
> > +- quads[idx+COUNT] = count;
> > +- quads[idx+XL] = points[4];
> > +- quads[idx+X0] = points[0];
> > +- quads[idx+Y0] = points[1];
> > +- executeQuadAFDIteration(idx);
> > +- float x1 = quads[idx+X0], y1 = quads[idx+Y0];
> > +- quads[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
> > +- quads[idx+CURX] = points[0] + (quads[idx+CURY] -
> > points[1])*quads[idx+CURSLOPE];
> > +- }
> > +-
> > +- private void initCurve(final int idx, float[] points, int or) {
> > +- final int countlg = 3;
> > +- final int count = 1 << countlg;
> > +-
> > +- // the dx and dy refer to forward differencing variables, not the
> > last
> > +- // coefficients of the "points" polynomial
> > +- final float dddx, dddy, ddx, ddy, dx, dy;
> > +- c.set(points, 8);
> > ++ float dddx, dddy, ddx, ddy, dx, dy;
> > + dddx = 2f * c.dax / (1 << (3 * countlg));
> > + dddy = 2f * c.day / (1 << (3 * countlg));
> > +
> > +@@ -480,93 +219,100 @@
> > + dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) +
> > c.cx / (1 << countlg);
> > + dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) +
> > c.cy / (1 << countlg);
> > +
> > +- curves[idx+DDDX] = dddx;
> > +- curves[idx+DDDY] = dddy;
> > +- curves[idx+DDX] = ddx;
> > +- curves[idx+DDY] = ddy;
> > +- curves[idx+DX] = dx;
> > +- curves[idx+DY] = dy;
> > +- curves[idx+COUNT] = count;
> > +- curves[idx+XL] = points[6];
> > +- curves[idx+X0] = points[0];
> > +- curves[idx+Y0] = points[1];
> > +- executeCurveAFDIteration(idx);
> > +- float x1 = curves[idx+X0], y1 = curves[idx+Y0];
> > +- curves[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
> > +- curves[idx+CURX] = points[0] + (curves[idx+CURY] -
> > points[1])*curves[idx+CURSLOPE];
> > +- }
> > +-
> > +- private void addPathSegment(float[] pts, final int type, final
> > int or) {
> > +- int idx;
> > +- float[] addTo;
> > +- switch (type) {
> > +- case 4:
> > +- idx = numEdges * SIZEOF_EDGE;
> > +- addTo = edges = Helpers.widenArray(edges, numEdges*SIZEOF_EDGE,
> > SIZEOF_EDGE);
> > +- numEdges++;
> > +- break;
> > +- case 6:
> > +- idx = numQuads * SIZEOF_QUAD;
> > +- addTo = quads = Helpers.widenArray(quads, numQuads*SIZEOF_QUAD,
> > SIZEOF_QUAD);
> > +- numQuads++;
> > +- break;
> > +- case 8:
> > +- idx = numCurves * SIZEOF_CURVE;
> > +- addTo = curves = Helpers.widenArray(curves,
> > numCurves*SIZEOF_CURVE, SIZEOF_CURVE);
> > +- numCurves++;
> > +- break;
> > +- default:
> > +- throw new InternalError();
> > +- }
> > +- // set the common fields, except CURX, for which we must know the
> > kind
> > +- // of curve. NOTE: this must be done before the type specific
> > fields
> > +- // are initialized, because those depend on the common ones.
> > +- addTo[idx+YMIN] = pts[1];
> > +- addTo[idx+YMAX] = pts[type-1];
> > +- addTo[idx+OR] = or;
> > +- addTo[idx+CURY] = (float)Math.ceil(pts[1]);
> > +- switch (type) {
> > +- case 4:
> > +- initLine(idx, pts, or);
> > +- break;
> > +- case 6:
> > +- initQuad(idx, pts, or);
> > +- break;
> > +- case 8:
> > +- initCurve(idx, pts, or);
> > +- break;
> > +- default:
> > +- throw new InternalError();
> > ++ // we use x0, y0 to walk the line
> > ++ float x1 = x0, y1 = y0;
> > ++ while (count > 0) {
> > ++ while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
> > ++ dddx /= 8;
> > ++ dddy /= 8;
> > ++ ddx = ddx/4 - dddx;
> > ++ ddy = ddy/4 - dddy;
> > ++ dx = (dx - ddx) / 2;
> > ++ dy = (dy - ddy) / 2;
> > ++ count <<= 1;
> > ++ }
> > ++ // can only do this on even "count" values, because we must
> > divide count by 2
> > ++ while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy)
> > <= INC_BND) {
> > ++ dx = 2 * dx + ddx;
> > ++ dy = 2 * dy + ddy;
> > ++ ddx = 4 * (ddx + dddx);
> > ++ ddy = 4 * (ddy + dddy);
> > ++ dddx = 8 * dddx;
> > ++ dddy = 8 * dddy;
> > ++ count >>= 1;
> > ++ }
> > ++ count--;
> > ++ if (count > 0) {
> > ++ x1 += dx;
> > ++ dx += ddx;
> > ++ ddx += dddx;
> > ++ y1 += dy;
> > ++ dy += ddy;
> > ++ ddy += dddy;
> > ++ } else {
> > ++ x1 = x3;
> > ++ y1 = y3;
> > ++ }
> > ++ addLine(x0, y0, x1, y1);
> > ++ x0 = x1;
> > ++ y0 = y1;
> > + }
> > + }
> > +
> > +- // precondition: the curve in pts must be monotonic and
> > increasing in y.
> > +- private void somethingTo(float[] pts, final int type, final int
> > or) {
> > +- // NOTE: it's very important that we check for or >= 0 below (as
> > +- // opposed to or == 1, or or > 0, or anything else). That's
> > +- // because if we check for or==1, when the curve being added
> > +- // is a horizontal line, or will be 0 so or==1 will be false and
> > +- // x0 and y0 will be updated to pts[0] and pts[1] instead of
> > pts[type-2]
> > +- // and pts[type-1], which is the correct thing to do.
> > +- this.x0 = or >= 0 ? pts[type - 2] : pts[0];
> > +- this.y0 = or >= 0 ? pts[type - 1] : pts[1];
> > +-
> > +- float minY = pts[1], maxY = pts[type - 1];
> > +- if (Math.ceil(minY) >= Math.ceil(maxY) ||
> > +- Math.ceil(minY) >= boundsMaxY || maxY < boundsMinY)
> > +- {
> > ++ // Preconditions: y2 > y1 and the curve must cross some scanline
> > ++ // i.e.: y1 <= y < y2 for some y such that boundsMinY <= y <
> > boundsMaxY
> > ++ private void addLine(float x1, float y1, float x2, float y2) {
> > ++ float or = 1; // orientation of the line. 1 if y increases, 0
> > otherwise.
> > ++ if (y2 < y1) {
> > ++ or = y2; // no need to declare a temp variable. We have or.
> > ++ y2 = y1;
> > ++ y1 = or;
> > ++ or = x2;
> > ++ x2 = x1;
> > ++ x1 = or;
> > ++ or = 0;
> > ++ }
> > ++ final int firstCrossing = Math.max((int) Math.ceil(y1),
> > boundsMinY);
> > ++ final int lastCrossing = Math.min((int)Math.ceil(y2),
> > boundsMaxY);
> > ++ if (firstCrossing >= lastCrossing) {
> > + return;
> > + }
> > +
> > +- if (minY < edgeMinY) { edgeMinY = minY; }
> > +- if (maxY > edgeMaxY) { edgeMaxY = maxY; }
> > ++ if (y1 < edgeMinY) { edgeMinY = y1; }
> > ++ if (y2 > edgeMaxY) { edgeMaxY = y2; }
> > +
> > +- int minXidx = (pts[0] < pts[type-2] ? 0 : type - 2);
> > +- float minX = pts[minXidx];
> > +- float maxX = pts[type - 2 - minXidx];
> > +- if (minX < edgeMinX) { edgeMinX = minX; }
> > +- if (maxX > edgeMaxX) { edgeMaxX = maxX; }
> > +- addPathSegment(pts, type, or);
> > ++ final float slope = (x2 - x1) / (y2 - y1);
> > ++
> > ++ if (slope > 0) { // <==> x1 < x2
> > ++ if (x1 < edgeMinX) { edgeMinX = x1; }
> > ++ if (x2 > edgeMaxX) { edgeMaxX = x2; }
> > ++ } else {
> > ++ if (x2 < edgeMinX) { edgeMinX = x2; }
> > ++ if (x1 > edgeMaxX) { edgeMaxX = x1; }
> > ++ }
> > ++
> > ++ final int ptr = numEdges * SIZEOF_EDGE;
> > ++ edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE);
> > ++ numEdges++;
> > ++ edges[ptr+OR] = or;
> > ++ edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;
> > ++ edges[ptr+SLOPE] = slope;
> > ++ edges[ptr+YMAX] = y2;
> > ++ final int bucketIdx = firstCrossing - boundsMinY;
> > ++ addEdgeToBucket(ptr, bucketIdx);
> > ++ if (lastCrossing < boundsMaxY) {
> > ++ edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
> > ++ }
> > ++ }
> > ++
> > ++ // preconditions: should not be called before the last line has
> > been added
> > ++ // to the edge list (even though it will return a correct answer
> > at that
> > ++ // point in time, it's not meant to be used that way).
> > ++ private int getFirstScanLineCrossing() {
> > ++ return Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
> > ++ }
> > ++ private int getScanLineCrossingEnd() {
> > ++ return Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
> > + }
> > +
> > + // END EDGE LIST
> > +@@ -619,6 +365,10 @@
> > + this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;
> > + this.boundsMaxX = (pix_boundsX + pix_boundsWidth) *
> > SUBPIXEL_POSITIONS_X;
> > + this.boundsMaxY = (pix_boundsY + pix_boundsHeight) *
> > SUBPIXEL_POSITIONS_Y;
> > ++
> > ++ edgeBuckets = new int[boundsMaxY - boundsMinY];
> > ++ java.util.Arrays.fill(edgeBuckets, NULL);
> > ++ edgeBucketCounts = new int[edgeBuckets.length];
> > + }
> > +
> > + private float tosubpixx(float pix_x) {
> > +@@ -636,74 +386,34 @@
> > + this.x0 = tosubpixx(pix_x0);
> > + }
> > +
> > +- public void lineJoin() { /* do nothing */ }
> > +-
> > +- private final float[][] pts = new float[2][8];
> > +- private final float[] ts = new float[4];
> > +-
> > +- private static void invertPolyPoints(float[] pts, int off, int
> > type) {
> > +- for (int i = off, j = off + type - 2; i < j; i += 2, j -= 2) {
> > +- float tmp = pts[i];
> > +- pts[i] = pts[j];
> > +- pts[j] = tmp;
> > +- tmp = pts[i+1];
> > +- pts[i+1] = pts[j+1];
> > +- pts[j+1] = tmp;
> > +- }
> > +- }
> > +-
> > +- // return orientation before making the curve upright.
> > +- private static int makeMonotonicCurveUpright(float[] pts, int
> > off, int type) {
> > +- float y0 = pts[off + 1];
> > +- float y1 = pts[off + type - 1];
> > +- if (y0 > y1) {
> > +- invertPolyPoints(pts, off, type);
> > +- return -1;
> > +- } else if (y0 < y1) {
> > +- return 1;
> > +- }
> > +- return 0;
> > +- }
> > +-
> > + public void lineTo(float pix_x1, float pix_y1) {
> > +- pts[0][0] = x0; pts[0][1] = y0;
> > +- pts[0][2] = tosubpixx(pix_x1); pts[0][3] = tosubpixy(pix_y1);
> > +- int or = makeMonotonicCurveUpright(pts[0], 0, 4);
> > +- somethingTo(pts[0], 4, or);
> > ++ float x1 = tosubpixx(pix_x1);
> > ++ float y1 = tosubpixy(pix_y1);
> > ++ addLine(x0, y0, x1, y1);
> > ++ x0 = x1;
> > ++ y0 = y1;
> > + }
> > +
> > + Curve c = new Curve();
> > +- private void curveOrQuadTo(int type) {
> > +- c.set(pts[0], type);
> > +- int numTs = c.dxRoots(ts, 0);
> > +- numTs += c.dyRoots(ts, numTs);
> > +- numTs = Helpers.filterOutNotInAB(ts, 0, numTs, 0, 1);
> > +- Helpers.isort(ts, 0, numTs);
> > +-
> > +- Iterator<float[]> it = Curve.breakPtsAtTs(pts, type, ts, numTs);
> > +- while(it.hasNext()) {
> > +- float[] curCurve = it.next();
> > +- int or = makeMonotonicCurveUpright(curCurve, 0, type);
> > +- somethingTo(curCurve, type, or);
> > +- }
> > +- }
> > +-
> > + @Override public void curveTo(float x1, float y1,
> > + float x2, float y2,
> > + float x3, float y3)
> > + {
> > +- pts[0][0] = x0; pts[0][1] = y0;
> > +- pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
> > +- pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
> > +- pts[0][6] = tosubpixx(x3); pts[0][7] = tosubpixy(y3);
> > +- curveOrQuadTo(8);
> > ++ final float xe = tosubpixx(x3);
> > ++ final float ye = tosubpixy(y3);
> > ++ c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2),
> > tosubpixy(y2), xe, ye);
> > ++ curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
> > ++ x0 = xe;
> > ++ y0 = ye;
> > + }
> > +
> > + @Override public void quadTo(float x1, float y1, float x2, float
> > y2) {
> > +- pts[0][0] = x0; pts[0][1] = y0;
> > +- pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
> > +- pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
> > +- curveOrQuadTo(6);
> > ++ final float xe = tosubpixx(x2);
> > ++ final float ye = tosubpixy(y2);
> > ++ c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
> > ++ quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
> > ++ x0 = xe;
> > ++ y0 = ye;
> > + }
> > +
> > + public void closePath() {
> > +@@ -728,9 +438,9 @@
> > + // 0x1 if EVEN_ODD, all bits if NON_ZERO
> > + int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
> > +
> > +- // add 1 to better deal with the last pixel in a pixel row.
> > +- int width = pix_bboxx1 - pix_bboxx0 + 1;
> > +- int[] alpha = new int[width+1];
> > ++ // add 2 to better deal with the last pixel in a pixel row.
> > ++ int width = pix_bboxx1 - pix_bboxx0;
> > ++ int[] alpha = new int[width+2];
> > +
> > + int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;
> > + int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;
> > +@@ -766,7 +476,8 @@
> > + for (int i = 0; i < numCrossings; i++) {
> > + int curxo = crossings[i];
> > + int curx = curxo >> 1;
> > +- int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1;
> > ++ // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
> > ++ int crorientation = ((curxo & 0x1) << 1) -1;
> > + if ((sum & mask) != 0) {
> > + int x0 = Math.max(prev, bboxx0);
> > + int x1 = Math.min(curx, bboxx1);
> > +@@ -811,26 +522,26 @@
> > + }
> > +
> > + public void endRendering() {
> > +- final int bminx = boundsMinX >> SUBPIXEL_LG_POSITIONS_X;
> > +- final int bmaxx = boundsMaxX >> SUBPIXEL_LG_POSITIONS_X;
> > +- final int bminy = boundsMinY >> SUBPIXEL_LG_POSITIONS_Y;
> > +- final int bmaxy = boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y;
> > +- final int eminx = ((int)Math.floor(edgeMinX)) >>
> > SUBPIXEL_LG_POSITIONS_X;
> > +- final int emaxx = ((int)Math.ceil(edgeMaxX)) >>
> > SUBPIXEL_LG_POSITIONS_X;
> > +- final int eminy = ((int)Math.floor(edgeMinY)) >>
> > SUBPIXEL_LG_POSITIONS_Y;
> > +- final int emaxy = ((int)Math.ceil(edgeMaxY)) >>
> > SUBPIXEL_LG_POSITIONS_Y;
> > ++ int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX);
> > ++ int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX);
> > ++ int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY);
> > ++ int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY);
> > +
> > +- final int minX = Math.max(bminx, eminx);
> > +- final int maxX = Math.min(bmaxx, emaxx);
> > +- final int minY = Math.max(bminy, eminy);
> > +- final int maxY = Math.min(bmaxy, emaxy);
> > +- if (minX > maxX || minY > maxY) {
> > +- this.cache = new PiscesCache(bminx, bminy, bmaxx, bmaxy);
> > ++ int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
> > ++ int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >>
> > SUBPIXEL_LG_POSITIONS_X;
> > ++ int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
> > ++ int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >>
> > SUBPIXEL_LG_POSITIONS_Y;
> > ++
> > ++ if (pminX > pmaxX || pminY > pmaxY) {
> > ++ this.cache = new PiscesCache(boundsMinX >>
> > SUBPIXEL_LG_POSITIONS_X,
> > ++ boundsMinY >> SUBPIXEL_LG_POSITIONS_Y,
> > ++ boundsMaxX >> SUBPIXEL_LG_POSITIONS_X,
> > ++ boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y);
> > + return;
> > + }
> > +
> > +- this.cache = new PiscesCache(minX, minY, max
>
> [Message truncated]
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cleanupFixup.patch
Type: text/x-patch
Size: 2688 bytes
Desc: not available
Url : http://mail.openjdk.java.net/pipermail/distro-pkg-dev/attachments/20110301/0b104512/cleanupFixup.patch
More information about the distro-pkg-dev
mailing list